Skip to content

Commit 1ad3b1c

Browse files
committed
[ddc] Add support for new function types
Issue: #48585 Change-Id: I0a338e672467b8fec5158f5959da01fa100f3ae8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/266541 Reviewed-by: Anna Gringauze <[email protected]>
1 parent 52d70ed commit 1ad3b1c

File tree

7 files changed

+209
-45
lines changed

7 files changed

+209
-45
lines changed

pkg/dev_compiler/lib/src/kernel/compiler.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6113,8 +6113,9 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
61136113
js_ast.Expression _emitOperationForJsBuiltIn(JsBuiltin builtin) {
61146114
switch (builtin) {
61156115
case JsBuiltin.dartClosureConstructor:
6116-
// TODO(48585) How to get constructor for a Dart Function?
6117-
return _emitTopLevelName(_coreTypes.functionClass);
6116+
// TODO(48585) Is this safe or will it conflict with functions that
6117+
// enter the program through JS Interop?
6118+
return js.call('Function');
61186119
case JsBuiltin.dartObjectConstructor:
61196120
return _emitTopLevelName(_coreTypes.objectClass);
61206121
default:

pkg/dev_compiler/lib/src/kernel/type_recipe_generator.dart

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,15 @@ class _TypeRecipeVisitor extends DartTypeVisitor<String> {
4141
///
4242
/// Used to determine the indices for type variables.
4343
DDCTypeEnvironment _typeEnvironment;
44+
var _unboundTypeParameters = <String>[];
4445

4546
final CoreTypes _coreTypes;
4647

4748
_TypeRecipeVisitor(this._typeEnvironment, this._coreTypes);
4849

4950
@override
5051
String defaultDartType(DartType node) {
51-
return 'TODO';
52+
throw UnimplementedError('Unknown DartType: $node');
5253
}
5354

5455
@override
@@ -81,11 +82,91 @@ class _TypeRecipeVisitor extends DartTypeVisitor<String> {
8182
'${_nullabilityRecipe(node)}';
8283

8384
@override
84-
String visitFunctionType(FunctionType node) => defaultDartType(node);
85+
String visitFunctionType(FunctionType node) {
86+
var savedUnboundTypeParameters = _unboundTypeParameters;
87+
// Collect the type parameters introduced by this function type because they
88+
// might be used later in its definition.
89+
//
90+
// For example, the function type `T Function<T, S>(S)` introduces new type
91+
// parameter types and uses them in the parameter and return types.
92+
_unboundTypeParameters = [
93+
for (var parameter in node.typeParameters) parameter.name!,
94+
..._unboundTypeParameters
95+
];
96+
// Generate the return type recipe.
97+
var recipeBuffer = StringBuffer(node.returnType.accept(this));
98+
// Generate the method parameter recipes.
99+
recipeBuffer.write(Recipe.startFunctionArgumentsString);
100+
// Required positional parameters.
101+
var requiredParameterCount = node.requiredParameterCount;
102+
var positionalParameters = node.positionalParameters;
103+
for (var i = 0; i < requiredParameterCount; i++) {
104+
recipeBuffer.write(positionalParameters[i].accept(this));
105+
if (i < requiredParameterCount - 1) {
106+
recipeBuffer.write(Recipe.separatorString);
107+
}
108+
}
109+
var positionalCount = positionalParameters.length;
110+
// Optional positional parameters.
111+
if (positionalCount > requiredParameterCount) {
112+
recipeBuffer.write(Recipe.startOptionalGroupString);
113+
for (var i = requiredParameterCount; i < positionalCount; i++) {
114+
recipeBuffer.write(positionalParameters[i].accept(this));
115+
if (i < positionalCount - 1) {
116+
recipeBuffer.write(Recipe.separatorString);
117+
}
118+
}
119+
recipeBuffer.write(Recipe.endOptionalGroupString);
120+
}
121+
// Named parameters.
122+
var namedParameters = node.namedParameters;
123+
var namedCount = namedParameters.length;
124+
if (namedParameters.isNotEmpty) {
125+
recipeBuffer.write(Recipe.startNamedGroupString);
126+
for (var i = 0; i < namedCount; i++) {
127+
var named = namedParameters[i];
128+
recipeBuffer.write(named.name);
129+
recipeBuffer.write(named.isRequired
130+
? Recipe.requiredNameSeparatorString
131+
: Recipe.nameSeparatorString);
132+
recipeBuffer.write(named.type.accept(this));
133+
if (i < namedCount - 1) {
134+
recipeBuffer.write(Recipe.separatorString);
135+
}
136+
}
137+
recipeBuffer.write(Recipe.endNamedGroupString);
138+
}
139+
recipeBuffer.write(Recipe.endFunctionArgumentsString);
140+
// Generate type parameter recipes.
141+
var typeParameters = node.typeParameters;
142+
if (typeParameters.isNotEmpty) {
143+
recipeBuffer.write(Recipe.startTypeArgumentsString);
144+
recipeBuffer.writeAll(
145+
typeParameters.map((parameter) => parameter.bound.accept(this)),
146+
Recipe.separatorString);
147+
recipeBuffer.write(Recipe.endTypeArgumentsString);
148+
}
149+
_unboundTypeParameters = savedUnboundTypeParameters;
150+
// Add the function type nullability.
151+
recipeBuffer.write(_nullabilityRecipe(node));
152+
return recipeBuffer.toString();
153+
}
85154

86155
@override
87156
String visitTypeParameterType(TypeParameterType node) {
88-
var i = _typeEnvironment.recipeIndexOf(node.parameter);
157+
var i = _unboundTypeParameters.indexOf(node.parameter.name!);
158+
if (i >= 0) {
159+
return '$i'
160+
'${Recipe.genericFunctionTypeParameterIndexString}'
161+
'${_nullabilityRecipe(node)}';
162+
}
163+
i = _typeEnvironment.recipeIndexOf(node.parameter);
164+
if (i < 0) {
165+
throw UnsupportedError(
166+
'Type parameter $node was not found in the environment '
167+
'$_typeEnvironment or in the unbound parameters '
168+
'$_unboundTypeParameters.');
169+
}
89170
return '$i${_nullabilityRecipe(node)}';
90171
}
91172

sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,14 @@ bind(obj, name, method) {
5252
// TODO(jmesserly): canonicalize tearoffs.
5353
JS('', '#._boundObject = #', f, obj);
5454
JS('', '#._boundMethod = #', f, method);
55-
JS('', '#[#] = #', f, _runtimeType, getMethodType(getType(obj), name));
55+
JS(
56+
'',
57+
'#[#] = #',
58+
f,
59+
JS_GET_FLAG('NEW_RUNTIME_TYPES')
60+
? JS_GET_NAME(JsGetName.SIGNATURE_NAME)
61+
: _runtimeType,
62+
getMethodType(getType(obj), name));
5663
return f;
5764
}
5865

sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/rtti.dart

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@ part of dart._runtime;
4848
///
4949
/// `dart.fn(closure, type)` marks [closure] with the provided runtime [type].
5050
fn(closure, type) {
51-
JS('', '#[#] = #', closure, _runtimeType, type);
51+
if (JS_GET_FLAG('NEW_RUNTIME_TYPES')) {
52+
JS('', '#[#] = #', closure, JS_GET_NAME(JsGetName.SIGNATURE_NAME), type);
53+
} else {
54+
JS('', '#[#] = #', closure, _runtimeType, type);
55+
}
5256
return closure;
5357
}
5458

@@ -85,35 +89,64 @@ getFunctionType(obj) {
8589
/// different from the user-visible Type object returned by calling
8690
/// `runtimeType` on some Dart object.
8791
getReifiedType(obj) {
88-
switch (JS<String>('!', 'typeof #', obj)) {
89-
case "object":
90-
if (obj == null) return JS('', '#', Null);
91-
if (_jsInstanceOf(obj, Object)) {
92-
if (JS_GET_FLAG('NEW_RUNTIME_TYPES')) {
92+
if (JS_GET_FLAG('NEW_RUNTIME_TYPES')) {
93+
switch (JS<String>('!', 'typeof #', obj)) {
94+
case "object":
95+
if (obj == null) return typeRep<Null>();
96+
if (_jsInstanceOf(obj, Object) ||
97+
JS<bool>('!', 'Array.isArray(#)', obj)) {
98+
// The rti library can correctly extract the representation.
9399
return rti.instanceType(obj);
94-
} else {
100+
}
101+
// Otherwise assume this is a JS interop object.
102+
return typeRep<LegacyJavaScriptObject>();
103+
case "function":
104+
// Dart functions are tagged with a signature.
105+
var signature =
106+
JS('', '#[#]', obj, JS_GET_NAME(JsGetName.SIGNATURE_NAME));
107+
if (signature != null) return signature;
108+
// TODO(nshahan) Handle JS interop functions.
109+
throw "Unknown function type";
110+
case "undefined":
111+
return typeRep<Null>();
112+
case "number":
113+
return JS('', 'Math.floor(#) == # ? # : #', obj, obj, typeRep<int>(),
114+
typeRep<double>());
115+
case "boolean":
116+
return typeRep<bool>();
117+
case "string":
118+
return typeRep<String>();
119+
case "symbol":
120+
default:
121+
return typeRep<LegacyJavaScriptObject>();
122+
}
123+
} else {
124+
switch (JS<String>('!', 'typeof #', obj)) {
125+
case "object":
126+
if (obj == null) return JS('', '#', Null);
127+
if (_jsInstanceOf(obj, Object)) {
95128
return JS('', '#.constructor', obj);
96129
}
97-
}
98-
var result = JS('', '#[#]', obj, _extensionType);
99-
if (result == null) return typeRep<LegacyJavaScriptObject>();
100-
return result;
101-
case "function":
102-
// All Dart functions and callable classes must set _runtimeType
103-
var result = JS('', '#[#]', obj, _runtimeType);
104-
if (result != null) return result;
105-
return typeRep<LegacyJavaScriptObject>();
106-
case "undefined":
107-
return JS('', '#', Null);
108-
case "number":
109-
return JS('', 'Math.floor(#) == # ? # : #', obj, obj, int, double);
110-
case "boolean":
111-
return JS('', '#', bool);
112-
case "string":
113-
return JS('', '#', String);
114-
case "symbol":
115-
default:
116-
return typeRep<LegacyJavaScriptObject>();
130+
var result = JS('', '#[#]', obj, _extensionType);
131+
if (result == null) return typeRep<LegacyJavaScriptObject>();
132+
return result;
133+
case "function":
134+
// All Dart functions and callable classes must set _runtimeType
135+
var result = JS('', '#[#]', obj, _runtimeType);
136+
if (result != null) return result;
137+
return typeRep<LegacyJavaScriptObject>();
138+
case "undefined":
139+
return JS('', '#', Null);
140+
case "number":
141+
return JS('', 'Math.floor(#) == # ? # : #', obj, obj, int, double);
142+
case "boolean":
143+
return JS('', '#', bool);
144+
case "string":
145+
return JS('', '#', String);
146+
case "symbol":
147+
default:
148+
return typeRep<LegacyJavaScriptObject>();
149+
}
117150
}
118151
}
119152

sdk/lib/_internal/js_dev_runtime/private/foreign_helper.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,18 @@ external JS_BUILTIN(String typeDescription, JsBuiltin builtin,
299299
/// Returns the interceptor for [object].
300300
///
301301
// TODO(nshahan) Replace calls at compile time?
302-
Object getInterceptor(object) => object;
302+
Object getInterceptor(obj) {
303+
var classRef;
304+
if (obj == null) {
305+
classRef = JS_CLASS_REF(Null);
306+
} else if (JS<String>('!', 'typeof #', obj) == 'function') {
307+
var signature = JS('', '#[#]', obj, JS_GET_NAME(JsGetName.SIGNATURE_NAME));
308+
// Dart functions are always tagged with a signature.
309+
if (signature != null) classRef = JS_CLASS_REF(Function);
310+
}
311+
if (classRef == null) throw 'Unknown interceptor for object: ($obj)';
312+
return JS<Object>('!', '#.prototype', classRef);
313+
}
303314

304315
/// Returns the Rti object for the type for JavaScript arrays via JS-interop.
305316
///

sdk/lib/_internal/js_dev_runtime/private/interceptors.dart

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ library dart._interceptors;
77
import 'dart:collection';
88
import 'dart:_internal' hide Symbol;
99
import 'dart:_js_helper';
10-
import 'dart:_foreign_helper' show JS, JSExportName;
10+
import 'dart:_foreign_helper' show JS, JS_GET_FLAG, JSExportName;
1111
import 'dart:math' show Random, ln2;
12+
import 'dart:_rti' as rti show createRuntimeType, Rti;
1213
import 'dart:_runtime' as dart;
1314

1415
part 'js_array.dart';
@@ -217,13 +218,26 @@ class JSFunction extends Interceptor {
217218
return false;
218219
}
219220
for (var i = 0; i < typeArgCount; i++) {
220-
var typeArg = JS('!', '#[#]', typeArgs, i);
221-
var otherTypeArg = JS('!', '#[#]', otherTypeArgs, i);
222-
// TODO(nshahan) Replace wrapType() with a lighter weight legacy
223-
// erasure.
224-
if (JS<bool>('!', '# !== #', dart.wrapType(typeArg),
225-
dart.wrapType(otherTypeArg))) {
226-
return false;
221+
if (JS_GET_FLAG('NEW_RUNTIME_TYPES')) {
222+
var typeArg = JS<rti.Rti>('!', '#[#]', typeArgs, i);
223+
var otherTypeArg = JS<rti.Rti>('!', '#[#]', otherTypeArgs, i);
224+
if (dart.compileTimeFlag('soundNullSafety')) {
225+
if (typeArg != otherTypeArg) return false;
226+
} else {
227+
if (rti.Rti.getLegacyErasedRecipe(typeArg) !=
228+
rti.Rti.getLegacyErasedRecipe(otherTypeArg)) {
229+
return false;
230+
}
231+
}
232+
} else {
233+
var typeArg = JS('!', '#[#]', typeArgs, i);
234+
var otherTypeArg = JS('!', '#[#]', otherTypeArgs, i);
235+
// TODO(nshahan) Replace wrapType() with a lighter weight legacy
236+
// erasure.
237+
if (JS<bool>('!', '# !== #', dart.wrapType(typeArg),
238+
dart.wrapType(otherTypeArg))) {
239+
return false;
240+
}
227241
}
228242
}
229243
}
@@ -255,7 +269,9 @@ class JSFunction extends Interceptor {
255269
return (hash * 31 + identityHashCode(boundMethod)) & 0x1fffffff;
256270
}
257271

258-
get runtimeType => dart.wrapType(dart.getReifiedType(this));
272+
Type get runtimeType => JS_GET_FLAG('NEW_RUNTIME_TYPES')
273+
? rti.createRuntimeType(JS<rti.Rti>('!', '#', dart.getReifiedType(this)))
274+
: dart.wrapType(dart.getReifiedType(this));
259275
}
260276

261277
/// A class used for implementing `null` tear-offs.

sdk/lib/_internal/js_shared/lib/rti.dart

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,13 @@ class Rti {
364364
static void _setCanonicalRecipe(Rti rti, String s) {
365365
rti._canonicalRecipe = s;
366366
}
367+
368+
/// Returns the canonical recipe for [rti] with the all legacy type markers
369+
/// (* stars) erased.
370+
static String getLegacyErasedRecipe(Rti rti) {
371+
var s = _getCanonicalRecipe(rti);
372+
return JS('String', '#.replace(/\\*/g, "")', s);
373+
}
367374
}
368375

369376
class _FunctionParameters {
@@ -658,7 +665,15 @@ Rti? closureFunctionType(Object? closure) {
658665
if (JS('bool', 'typeof # == "number"', signature)) {
659666
return getTypeFromTypesTable(_Utils.asInt(signature));
660667
}
661-
return _Utils.asRti(JS('', '#[#]()', closure, signatureName));
668+
if (JS_GET_FLAG('DEV_COMPILER')) {
669+
// DDC attaches the evaluated Rti object as the signature because
670+
// attaching a function that evaluates to the signature breaks the current
671+
// const canonicalization for closures which assumes all properties of the
672+
// closure object are already themselves canonicalized.
673+
return _Utils.asRti(signature);
674+
} else {
675+
return _Utils.asRti(JS('', '#[#]()', closure, signatureName));
676+
}
662677
}
663678
return null;
664679
}
@@ -820,7 +835,7 @@ Type createRuntimeType(Rti rti) {
820835
return _Type(rti);
821836
} else {
822837
String recipe = Rti._getCanonicalRecipe(rti);
823-
String starErasedRecipe = JS('String', '#.replace(/\\*/g, "")', recipe);
838+
String starErasedRecipe = Rti.getLegacyErasedRecipe(rti);
824839
if (starErasedRecipe == recipe) {
825840
return _Type(rti);
826841
}

0 commit comments

Comments
 (0)