@@ -41,6 +41,7 @@ import 'native_types.dart';
41
41
import 'nullable_inference.dart' ;
42
42
import 'property_model.dart' ;
43
43
import 'target.dart' show allowedNativeTest;
44
+ import 'type_environment.dart' ;
44
45
import 'type_recipe_generator.dart' ;
45
46
import 'type_table.dart' ;
46
47
@@ -131,6 +132,12 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
131
132
/// True when a class is emitting a deferred class hierarchy.
132
133
bool _emittingDeferredType = false ;
133
134
135
+ /// The current type environment of type parameters introduced to the scope
136
+ /// via generic classes and functions.
137
+ DDCTypeEnvironment _currentTypeEnvironment = const EmptyTypeEnvironment ();
138
+
139
+ final TypeRecipeGenerator _typeRecipeGenerator;
140
+
134
141
/// The current element being loaded.
135
142
/// We can use this to determine if we're loading top-level code or not:
136
143
///
@@ -188,6 +195,9 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
188
195
189
196
final _superHelpers = < String , js_ast.Method > {};
190
197
198
+ /// Cache for the results of calling [_typeParametersInHierarchy] .
199
+ final _typeParametersInHierarchyCache = < Class , bool > {};
200
+
191
201
// Compilation of Kernel's [BreakStatement].
192
202
//
193
203
// Kernel represents Dart's `break` and `continue` uniformly as
@@ -345,7 +355,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
345
355
_asyncStarImplClass = sdk.getClass ('dart:async' , '_AsyncStarImpl' ),
346
356
_assertInteropMethod = sdk.getTopLevelMember (
347
357
'dart:_runtime' , 'assertInterop' ) as Procedure ,
348
- _futureOrNormalizer = FutureOrNormalizer (_coreTypes);
358
+ _futureOrNormalizer = FutureOrNormalizer (_coreTypes),
359
+ _typeRecipeGenerator = TypeRecipeGenerator (_coreTypes);
349
360
350
361
@override
351
362
Library ? get currentLibrary => _currentLibrary;
@@ -682,6 +693,12 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
682
693
_currentClass = c;
683
694
_currentLibrary = c.enclosingLibrary;
684
695
_currentUri = c.fileUri;
696
+ var savedTypeEnvironment = _currentTypeEnvironment;
697
+ // When compiling the type heritage of the class we can't reference an rti
698
+ // object attached to an instance. Instead we construct a type environment
699
+ // manually when needed. Later we use the rti attached to an instance for
700
+ // a simpler representation within instance members of the class.
701
+ _currentTypeEnvironment = _currentTypeEnvironment.extend (c.typeParameters);
685
702
686
703
// Mixins are unrolled in _defineClass.
687
704
// If this class is annotated with `@JS`, then there is nothing to emit.
@@ -697,6 +714,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
697
714
_currentClass = savedClass;
698
715
_currentLibrary = savedLibrary;
699
716
_currentUri = savedUri;
717
+ _currentTypeEnvironment = savedTypeEnvironment;
700
718
}
701
719
702
720
/// To emit top-level classes, we sometimes need to reorder them.
@@ -759,7 +777,15 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
759
777
});
760
778
761
779
var jsCtors = _defineConstructors (c, className);
780
+ // Emitting class members in a class type environment results in a more
781
+ // succinct type representation when referencing class type arguments from
782
+ // instance members.
783
+ var savedTypeEnvironment = _currentTypeEnvironment;
784
+ if (c.typeParameters.isNotEmpty) {
785
+ _currentTypeEnvironment = ClassTypeEnvironment (c.typeParameters);
786
+ }
762
787
var jsMethods = _emitClassMethods (c);
788
+ _currentTypeEnvironment = savedTypeEnvironment;
763
789
764
790
_emitSuperHelperSymbols (body);
765
791
// Deferred supertypes must be evaluated lazily while emitting classes to
@@ -785,7 +811,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
785
811
786
812
// Attach caches on all canonicalized types.
787
813
if (_options.newRuntimeTypes) {
788
- var name = TypeRecipeGenerator .interfaceTypeRecipe (c);
814
+ var name = _typeRecipeGenerator .interfaceTypeRecipe (c);
789
815
body.add (runtimeStatement (
790
816
'addRtiResources(#, #)' , [className, js.string (name)]));
791
817
}
@@ -1687,6 +1713,18 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
1687
1713
// multiple constructors, but it needs to be balanced against readability.
1688
1714
body.add (_initializeFields (fields, node));
1689
1715
1716
+ // Instances of classes with type arguments need an rti object attached to
1717
+ // them since the type arguments could be instantiated differently for
1718
+ // each instance.
1719
+ if (_options.newRuntimeTypes && _typeParametersInHierarchy (cls)) {
1720
+ var type = cls.getThisType (_coreTypes, Nullability .nonNullable);
1721
+ // Only set the rti if there isn't one already. This avoids superclasses
1722
+ // from overwriting the value already set by subclass.
1723
+ var rtiProperty = propertyName (js_ast.FixedNames .rtiName);
1724
+ body.add (js.statement (
1725
+ 'this.# = this.# || #' , [rtiProperty, rtiProperty, _emitType (type)]));
1726
+ }
1727
+
1690
1728
// If no superinitializer is provided, an implicit superinitializer of the
1691
1729
// form `super()` is added at the end of the initializer list, unless the
1692
1730
// enclosing class is class Object.
@@ -1700,6 +1738,19 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
1700
1738
return body;
1701
1739
}
1702
1740
1741
+ /// Returns `true` if [cls] or any of its transitive super classes has
1742
+ /// generic type parameters.
1743
+ bool _typeParametersInHierarchy (Class ? cls) {
1744
+ if (cls == null ) return false ;
1745
+ var cachedResult = _typeParametersInHierarchyCache[cls];
1746
+ if (cachedResult != null ) return cachedResult;
1747
+ var hasTypeParameters = cls.typeParameters.isNotEmpty
1748
+ ? true
1749
+ : _typeParametersInHierarchy (cls.superclass);
1750
+ _typeParametersInHierarchyCache[cls] = hasTypeParameters;
1751
+ return hasTypeParameters;
1752
+ }
1753
+
1703
1754
js_ast.LiteralString _constructorName (String name) {
1704
1755
if (name == '' ) {
1705
1756
// Default constructors (factory or not) use `new` as their name.
@@ -2182,12 +2233,16 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
2182
2233
/// own type parameters, this will need to be changed to call
2183
2234
/// [_emitFunction] instead.
2184
2235
var name = node.name.text;
2236
+ var savedTypeEnvironment = _currentTypeEnvironment;
2237
+ _currentTypeEnvironment =
2238
+ _currentTypeEnvironment.extend (function.typeParameters);
2185
2239
var jsBody = _emitSyncFunctionBody (function, name);
2186
2240
var jsName = _constructorName (name);
2187
2241
memberNames[node] = jsName.valueWithoutQuotes;
2242
+ var jsParams = _emitParameters (function);
2243
+ _currentTypeEnvironment = savedTypeEnvironment;
2188
2244
2189
- return js_ast.Method (jsName, js_ast.Fun (_emitParameters (function), jsBody),
2190
- isStatic: true )
2245
+ return js_ast.Method (jsName, js_ast.Fun (jsParams, jsBody), isStatic: true )
2191
2246
..sourceInformation = _nodeEnd (node.fileEndOffset);
2192
2247
}
2193
2248
@@ -2945,10 +3000,79 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
2945
3000
/// Returns an expression that evaluates to the rti object from the dart:_rti
2946
3001
/// library that represents [type] .
2947
3002
js_ast.Expression _newEmitType (DartType type) {
3003
+ /// Returns an expression that evaluates a type [recipe] within the type
3004
+ /// [environment] .
3005
+ ///
3006
+ /// At runtime the expression will evaluate to an rti object.
3007
+ js_ast.Expression emitRtiEval (
3008
+ js_ast.Expression environment, String recipe) =>
3009
+ js.call ('#.#("$recipe ")' ,
3010
+ [environment, _emitMemberName ('_eval' , memberClass: rtiClass)]);
3011
+
3012
+ /// Returns an expression that binds a type [parameter] within the type
3013
+ /// [environment] .
3014
+ ///
3015
+ /// At runtime the expression will evaluate to an rti object that has been
3016
+ /// extended to include the provided [parameter] .
3017
+ js_ast.Expression emitRtiBind (
3018
+ js_ast.Expression environment, TypeParameter parameter) =>
3019
+ js.call ('#.#(#)' , [
3020
+ environment,
3021
+ _emitMemberName ('_bind' , memberClass: rtiClass),
3022
+ _emitTypeParameter (parameter)
3023
+ ]);
3024
+
3025
+ /// Returns an expression that evaluates a type [recipe] in a type
3026
+ /// [environment] resulting in an rti object.
3027
+ js_ast.Expression evalInEnvironment (
3028
+ DDCTypeEnvironment environment, String recipe) {
3029
+ if (environment is EmptyTypeEnvironment ) {
3030
+ return js.call ('#.findType("$recipe ")' , [emitLibraryName (rtiLibrary)]);
3031
+ } else if (environment is BindingTypeEnvironment ) {
3032
+ js_ast.Expression env;
3033
+ if (environment.isSingleTypeParameter) {
3034
+ // An environment with a single type parameter can be simplified to
3035
+ // just that parameter.
3036
+ env = _emitTypeParameter (environment.parameters.single);
3037
+ } else {
3038
+ var environmentTypes = environment.parameters;
3039
+ // Create a dummy interface type to "hold" type arguments.
3040
+ env = emitRtiEval (_emitTypeParameter (environmentTypes.first), '@<0>' );
3041
+ // Bind remaining type arguments.
3042
+ for (var i = 1 ; i < environmentTypes.length; i++ ) {
3043
+ env = emitRtiBind (env, environmentTypes[i]);
3044
+ }
3045
+ }
3046
+ return emitRtiEval (env, recipe);
3047
+ } else if (environment is ClassTypeEnvironment ) {
3048
+ // Class type environments are already constructed and attached to the
3049
+ // instance of a generic class.
3050
+ return js.call ('this.${js_ast .FixedNames .rtiName }' );
3051
+ } else if (environment is ExtendedClassTypeEnvironment ) {
3052
+ // A generic class instance already stores a reference to a type
3053
+ // containing all of its type arguments.
3054
+ var env = js.call ('this.${js_ast .FixedNames .rtiName }' );
3055
+ // Bind extra type parameters.
3056
+ for (var parameter in environment.extendedParameters) {
3057
+ env = emitRtiBind (env, parameter);
3058
+ }
3059
+ return emitRtiEval (env, recipe);
3060
+ } else {
3061
+ _typeCompilationError (type,
3062
+ 'Unexpected DDCTypeEnvironment type (${environment .runtimeType }).' );
3063
+ }
3064
+ }
3065
+
3066
+ // TODO(nshahan) Avoid calling _emitType when we actually want a
3067
+ // reference to an rti that already exists in scope.
3068
+ if (type is TypeParameterType && type.isPotentiallyNonNullable) {
3069
+ return _emitTypeParameterType (type, emitNullability: false );
3070
+ }
2948
3071
var normalizedType = _futureOrNormalizer.normalize (type);
2949
3072
try {
2950
- var recipe = normalizedType.accept (TypeRecipeGenerator ());
2951
- return js.call ('#.findType("$recipe ")' , [emitLibraryName (rtiLibrary)]);
3073
+ var result = _typeRecipeGenerator.recipeInEnvironment (
3074
+ normalizedType, _currentTypeEnvironment);
3075
+ return evalInEnvironment (result.requiredEnvironment, result.recipe);
2952
3076
} on UnsupportedError catch (e) {
2953
3077
_typeCompilationError (normalizedType, e.message ?? 'Unknown Error' );
2954
3078
}
@@ -3443,6 +3567,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
3443
3567
}
3444
3568
3445
3569
js_ast.Fun _emitFunction (FunctionNode f, String ? name) {
3570
+ var savedTypeEnvironment = _currentTypeEnvironment;
3571
+ _currentTypeEnvironment = _currentTypeEnvironment.extend (f.typeParameters);
3446
3572
// normal function (sync), vs (sync*, async, async*)
3447
3573
var isSync = f.asyncMarker == AsyncMarker .Sync ;
3448
3574
var formals = _emitParameters (f);
@@ -3462,6 +3588,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
3462
3588
: _emitGeneratorFunctionBody (f, name);
3463
3589
3464
3590
block = super .exitFunction (formals, block);
3591
+ _currentTypeEnvironment = savedTypeEnvironment;
3465
3592
return js_ast.Fun (formals, block);
3466
3593
}
3467
3594
@@ -5965,9 +6092,11 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
5965
6092
case JsGetName .RTI_NAME :
5966
6093
return js.string (js_ast.FixedNames .rtiName);
5967
6094
case JsGetName .FUTURE_CLASS_TYPE_NAME :
5968
- return js.string (js_ast.FixedNames .futureClassName);
6095
+ return js.string (
6096
+ _typeRecipeGenerator.interfaceTypeRecipe (_coreTypes.futureClass));
5969
6097
case JsGetName .LIST_CLASS_TYPE_NAME :
5970
- return js.string (js_ast.FixedNames .listClassName);
6098
+ return js.string (
6099
+ _typeRecipeGenerator.interfaceTypeRecipe (_coreTypes.listClass));
5971
6100
case JsGetName .RTI_FIELD_AS :
5972
6101
return _emitMemberName (js_ast.FixedNames .rtiAsField,
5973
6102
memberClass: rtiClass);
@@ -6907,12 +7036,16 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
6907
7036
return js_ast.Property (symbol, constant);
6908
7037
}
6909
7038
6910
- var type = _emitInterfaceType (
6911
- node. getType (_staticTypeContext) as InterfaceType ,
6912
- emitNullability: false );
6913
- var prototype = js.call ('#.prototype' , [type ]);
7039
+ var type = node. getType (_staticTypeContext);
7040
+ var classRef =
7041
+ _emitInterfaceType (type as InterfaceType , emitNullability: false );
7042
+ var prototype = js.call ('#.prototype' , [classRef ]);
6914
7043
var properties = [
6915
7044
js_ast.Property (propertyName ('__proto__' ), prototype),
7045
+ if (_options.newRuntimeTypes && type.typeArguments.isNotEmpty)
7046
+ // Generic interface type instances require a type information tag.
7047
+ js_ast.Property (
7048
+ propertyName (js_ast.FixedNames .rtiName), _emitType (type)),
6916
7049
for (var e in node.fieldValues.entries.toList ().reversed)
6917
7050
entryToProperty (e),
6918
7051
];
0 commit comments