@@ -12,6 +12,7 @@ import 'package:path/path.dart' as p;
12
12
13
13
import '../compiler/js_names.dart' ;
14
14
import 'future_or_normalizer.dart' ;
15
+ import 'js_interop.dart' ;
15
16
import 'kernel_helpers.dart' ;
16
17
import 'type_environment.dart' ;
17
18
@@ -64,6 +65,9 @@ class TypeRecipeGenerator {
64
65
/// Returns a mapping of type hierarchies for all [InterfaceType] s that have
65
66
/// appeared in type recipes.
66
67
///
68
+ /// While the value of the returned map is typed as `Object` it is always
69
+ /// either a `String` or a `Map<String, List<String>>` .
70
+ ///
67
71
/// This mapping is intended to satisfy the requirements for the
68
72
/// `_Universe.addRules()` static method in the runtime rti library.
69
73
///
@@ -78,11 +82,35 @@ class TypeRecipeGenerator {
78
82
/// "type n": {...}}'
79
83
/// ```
80
84
///
85
+ /// Additionally, the runtime rti library supports a special forwarding rule
86
+ /// for non-static JavaScript interop types. With this format every interop
87
+ /// type simply forwards to `LegacyJavaScriptObject` where they all share
88
+ /// a single type rule
89
+ ///
90
+ /// ```
91
+ /// '{"interop type 0": "LegacyJavaScriptObject",
92
+ /// ...
93
+ /// "interop type n": "LegacyJavaScriptObject"}'
94
+ /// ```
95
+ ///
81
96
/// It is expected that this method is called at the end of a compilation
82
97
/// after all types used in the module have already been visited. Otherwise
83
98
/// the results will be incomplete.
84
- Map <String , Map <String , List <String >>> get visitedInterfaceTypeRules {
85
- var rules = < String , Map <String , List <String >>> {};
99
+ Map <String , Object > get liveInterfaceTypeRules {
100
+ // All non-static JavaScript interop classes should be mutual subtypes with
101
+ // 'LegacyJavaScriptObject`. To achieve this the rules are manually
102
+ // added here. There is special redirecting rule logic in the dart:_rti
103
+ // library for interop types because otherwise they would duplicate
104
+ // a lot of supertype information.
105
+ var legacyJavaScriptObjectRecipe = interfaceTypeRecipe (_coreTypes.index
106
+ .getClass ('dart:_interceptors' , 'LegacyJavaScriptObject' ));
107
+ var rules = < String , Object > {
108
+ for (var recipe in visitedJsInteropTypeRecipes)
109
+ recipe: legacyJavaScriptObjectRecipe
110
+ };
111
+
112
+ // All Dart types hierarchy rules are generated by exploring their class
113
+ // hierarchy.
86
114
for (var type in _recipeVisitor.visitedInterfaceTypes) {
87
115
var recipe = interfaceTypeRecipe (type.classNode);
88
116
var cls = type.classNode;
@@ -121,8 +149,30 @@ class TypeRecipeGenerator {
121
149
return rules;
122
150
}
123
151
152
+ /// Returns a mapping of type hierarchies for all [InterfaceType] s that have
153
+ /// appeared in type recipes.
154
+ Map <String , Object > get updateLegacyJavaScriptObjectRules {
155
+ var recipes = visitedJsInteropTypeRecipes;
156
+ return {
157
+ if (recipes.isNotEmpty)
158
+ interfaceTypeRecipe (_coreTypes.index
159
+ .getClass ('dart:_interceptors' , 'LegacyJavaScriptObject' )):
160
+ // Update type rules for `LegacyJavaScriptObject` to add all interop
161
+ // types in this module as a supertype.
162
+ {
163
+ for (var interopType in _recipeVisitor._visitedJsInteropTypes)
164
+ interfaceTypeRecipe (interopType.classNode): _recipeVisitor
165
+ ._listOfJSAnyType (interopType.typeArguments.length)
166
+ }
167
+ };
168
+ }
169
+
124
170
String interfaceTypeRecipe (Class node) =>
125
171
_recipeVisitor.interfaceTypeRecipe (node);
172
+
173
+ Iterable <String > get visitedJsInteropTypeRecipes =>
174
+ _recipeVisitor.visitedJsInteropTypes
175
+ .map ((type) => interfaceTypeRecipe (type.classNode));
126
176
}
127
177
128
178
/// A visitor to generate type recipe strings from a [DartType] .
@@ -155,6 +205,7 @@ class _TypeRecipeVisitor extends DartTypeVisitor<String> {
155
205
156
206
/// All of the [InterfaceType] s visited.
157
207
final _visitedInterfaceTypes = < InterfaceType > {};
208
+ final _visitedJsInteropTypes = < InterfaceType > {};
158
209
final CoreTypes _coreTypes;
159
210
160
211
_TypeRecipeVisitor (this ._typeEnvironment, this ._coreTypes);
@@ -176,6 +227,10 @@ class _TypeRecipeVisitor extends DartTypeVisitor<String> {
176
227
Iterable <InterfaceType > get visitedInterfaceTypes =>
177
228
Set .unmodifiable (_visitedInterfaceTypes);
178
229
230
+ /// The JavaScript interop types that have been visited.
231
+ Iterable <InterfaceType > get visitedJsInteropTypes =>
232
+ Set .unmodifiable (_visitedJsInteropTypes);
233
+
179
234
@override
180
235
String defaultDartType (DartType node) =>
181
236
throw UnimplementedError ('Unknown DartType: $node ' );
@@ -188,15 +243,17 @@ class _TypeRecipeVisitor extends DartTypeVisitor<String> {
188
243
189
244
@override
190
245
String visitInterfaceType (InterfaceType node) {
246
+ var cls = node.classNode;
191
247
addLiveType (node);
192
248
// Generate the interface type recipe.
193
- var recipeBuffer = StringBuffer (interfaceTypeRecipe (node.classNode ));
249
+ var recipeBuffer = StringBuffer (interfaceTypeRecipe (cls ));
194
250
// Generate the recipes for all type arguments.
195
251
if (node.typeArguments.isNotEmpty) {
252
+ var argumentRecipes = hasJSInteropAnnotation (cls)
253
+ ? _listOfJSAnyType (node.typeArguments.length)
254
+ : node.typeArguments.map ((typeArgument) => typeArgument.accept (this ));
196
255
recipeBuffer.write (Recipe .startTypeArgumentsString);
197
- recipeBuffer.writeAll (
198
- node.typeArguments.map ((typeArgument) => typeArgument.accept (this )),
199
- Recipe .separatorString);
256
+ recipeBuffer.writeAll (argumentRecipes, Recipe .separatorString);
200
257
recipeBuffer.write (Recipe .endTypeArgumentsString);
201
258
}
202
259
// Add nullability.
@@ -370,6 +427,17 @@ class _TypeRecipeVisitor extends DartTypeVisitor<String> {
370
427
}
371
428
}
372
429
430
+ /// Returns a list containing [n] copies of the "any" type recipe.
431
+ ///
432
+ /// Used exclusively for the type arguments of the older package:js style
433
+ /// JavaScript interop types when they are defined with generic type
434
+ /// parameters. These are not used to support the newer static JavaScript
435
+ /// interop.
436
+ List <String > _listOfJSAnyType (int n) => n == 0
437
+ ? const []
438
+ : List .filled (
439
+ n, '${Recipe .pushAnyExtensionString }${Recipe .extensionOpString }' );
440
+
373
441
/// Manually record [type] as being "live" without generating a recipe.
374
442
///
375
443
/// "Live" types here refer to the interface types that could potentially flow
@@ -393,7 +461,9 @@ class _TypeRecipeVisitor extends DartTypeVisitor<String> {
393
461
var cls = type.classNode;
394
462
var typeWithoutNullability =
395
463
cls.getThisType (_coreTypes, Nullability .nonNullable);
396
- _visitedInterfaceTypes.add (typeWithoutNullability);
464
+ hasJSInteropAnnotation (cls)
465
+ ? _visitedJsInteropTypes.add (typeWithoutNullability)
466
+ : _visitedInterfaceTypes.add (typeWithoutNullability);
397
467
}
398
468
}
399
469
}
0 commit comments