Skip to content

Commit 52d70ed

Browse files
committed
[ddc] Add support for new generic interface types
- Introduce type environments in the compiler used for evaluating type parameters from generic classes and generic function types. Issue: #48585 Change-Id: Ib5641eb666527acc3b7f13a4a00dea34e0122b52 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/266540 Reviewed-by: Mark Zhou <[email protected]>
1 parent bc17724 commit 52d70ed

File tree

6 files changed

+409
-28
lines changed

6 files changed

+409
-28
lines changed

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ abstract class FixedNames {
1919
static const operatorIsPrefix = r'$is_';
2020
static const operatorSignature = r'$signature';
2121
static const rtiName = r'$ti';
22-
// TODO(48585) Fix this name.
23-
static const futureClassName = 'TODO';
24-
// TODO(48585) Fix this name.
25-
static const listClassName = 'TODO';
2622
static const rtiAsField = '_as';
2723
static const rtiIsField = '_is';
2824
}

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

Lines changed: 145 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import 'native_types.dart';
4141
import 'nullable_inference.dart';
4242
import 'property_model.dart';
4343
import 'target.dart' show allowedNativeTest;
44+
import 'type_environment.dart';
4445
import 'type_recipe_generator.dart';
4546
import 'type_table.dart';
4647

@@ -131,6 +132,12 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
131132
/// True when a class is emitting a deferred class hierarchy.
132133
bool _emittingDeferredType = false;
133134

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+
134141
/// The current element being loaded.
135142
/// We can use this to determine if we're loading top-level code or not:
136143
///
@@ -188,6 +195,9 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
188195

189196
final _superHelpers = <String, js_ast.Method>{};
190197

198+
/// Cache for the results of calling [_typeParametersInHierarchy].
199+
final _typeParametersInHierarchyCache = <Class, bool>{};
200+
191201
// Compilation of Kernel's [BreakStatement].
192202
//
193203
// Kernel represents Dart's `break` and `continue` uniformly as
@@ -345,7 +355,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
345355
_asyncStarImplClass = sdk.getClass('dart:async', '_AsyncStarImpl'),
346356
_assertInteropMethod = sdk.getTopLevelMember(
347357
'dart:_runtime', 'assertInterop') as Procedure,
348-
_futureOrNormalizer = FutureOrNormalizer(_coreTypes);
358+
_futureOrNormalizer = FutureOrNormalizer(_coreTypes),
359+
_typeRecipeGenerator = TypeRecipeGenerator(_coreTypes);
349360

350361
@override
351362
Library? get currentLibrary => _currentLibrary;
@@ -682,6 +693,12 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
682693
_currentClass = c;
683694
_currentLibrary = c.enclosingLibrary;
684695
_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);
685702

686703
// Mixins are unrolled in _defineClass.
687704
// If this class is annotated with `@JS`, then there is nothing to emit.
@@ -697,6 +714,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
697714
_currentClass = savedClass;
698715
_currentLibrary = savedLibrary;
699716
_currentUri = savedUri;
717+
_currentTypeEnvironment = savedTypeEnvironment;
700718
}
701719

702720
/// To emit top-level classes, we sometimes need to reorder them.
@@ -759,7 +777,15 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
759777
});
760778

761779
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+
}
762787
var jsMethods = _emitClassMethods(c);
788+
_currentTypeEnvironment = savedTypeEnvironment;
763789

764790
_emitSuperHelperSymbols(body);
765791
// Deferred supertypes must be evaluated lazily while emitting classes to
@@ -785,7 +811,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
785811

786812
// Attach caches on all canonicalized types.
787813
if (_options.newRuntimeTypes) {
788-
var name = TypeRecipeGenerator.interfaceTypeRecipe(c);
814+
var name = _typeRecipeGenerator.interfaceTypeRecipe(c);
789815
body.add(runtimeStatement(
790816
'addRtiResources(#, #)', [className, js.string(name)]));
791817
}
@@ -1687,6 +1713,18 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
16871713
// multiple constructors, but it needs to be balanced against readability.
16881714
body.add(_initializeFields(fields, node));
16891715

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+
16901728
// If no superinitializer is provided, an implicit superinitializer of the
16911729
// form `super()` is added at the end of the initializer list, unless the
16921730
// enclosing class is class Object.
@@ -1700,6 +1738,19 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
17001738
return body;
17011739
}
17021740

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+
17031754
js_ast.LiteralString _constructorName(String name) {
17041755
if (name == '') {
17051756
// Default constructors (factory or not) use `new` as their name.
@@ -2182,12 +2233,16 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
21822233
/// own type parameters, this will need to be changed to call
21832234
/// [_emitFunction] instead.
21842235
var name = node.name.text;
2236+
var savedTypeEnvironment = _currentTypeEnvironment;
2237+
_currentTypeEnvironment =
2238+
_currentTypeEnvironment.extend(function.typeParameters);
21852239
var jsBody = _emitSyncFunctionBody(function, name);
21862240
var jsName = _constructorName(name);
21872241
memberNames[node] = jsName.valueWithoutQuotes;
2242+
var jsParams = _emitParameters(function);
2243+
_currentTypeEnvironment = savedTypeEnvironment;
21882244

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)
21912246
..sourceInformation = _nodeEnd(node.fileEndOffset);
21922247
}
21932248

@@ -2945,10 +3000,79 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
29453000
/// Returns an expression that evaluates to the rti object from the dart:_rti
29463001
/// library that represents [type].
29473002
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+
}
29483071
var normalizedType = _futureOrNormalizer.normalize(type);
29493072
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);
29523076
} on UnsupportedError catch (e) {
29533077
_typeCompilationError(normalizedType, e.message ?? 'Unknown Error');
29543078
}
@@ -3443,6 +3567,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
34433567
}
34443568

34453569
js_ast.Fun _emitFunction(FunctionNode f, String? name) {
3570+
var savedTypeEnvironment = _currentTypeEnvironment;
3571+
_currentTypeEnvironment = _currentTypeEnvironment.extend(f.typeParameters);
34463572
// normal function (sync), vs (sync*, async, async*)
34473573
var isSync = f.asyncMarker == AsyncMarker.Sync;
34483574
var formals = _emitParameters(f);
@@ -3462,6 +3588,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
34623588
: _emitGeneratorFunctionBody(f, name);
34633589

34643590
block = super.exitFunction(formals, block);
3591+
_currentTypeEnvironment = savedTypeEnvironment;
34653592
return js_ast.Fun(formals, block);
34663593
}
34673594

@@ -5965,9 +6092,11 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
59656092
case JsGetName.RTI_NAME:
59666093
return js.string(js_ast.FixedNames.rtiName);
59676094
case JsGetName.FUTURE_CLASS_TYPE_NAME:
5968-
return js.string(js_ast.FixedNames.futureClassName);
6095+
return js.string(
6096+
_typeRecipeGenerator.interfaceTypeRecipe(_coreTypes.futureClass));
59696097
case JsGetName.LIST_CLASS_TYPE_NAME:
5970-
return js.string(js_ast.FixedNames.listClassName);
6098+
return js.string(
6099+
_typeRecipeGenerator.interfaceTypeRecipe(_coreTypes.listClass));
59716100
case JsGetName.RTI_FIELD_AS:
59726101
return _emitMemberName(js_ast.FixedNames.rtiAsField,
59736102
memberClass: rtiClass);
@@ -6907,12 +7036,16 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
69077036
return js_ast.Property(symbol, constant);
69087037
}
69097038

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]);
69147043
var properties = [
69157044
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)),
69167049
for (var e in node.fieldValues.entries.toList().reversed)
69177050
entryToProperty(e),
69187051
];

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,3 +371,12 @@ bool _isNativeMarkerAnnotation(Expression annotation) {
371371

372372
bool _isDartInternal(Uri uri) =>
373373
uri.isScheme('dart') && uri.path == '_internal';
374+
375+
/// Collects all `TypeParameter`s from the `TypeParameterType`s present in the
376+
/// visited `DartType`.
377+
class TypeParameterTypeFinder extends RecursiveVisitor<void> {
378+
final found = <TypeParameter>{};
379+
@override
380+
void visitTypeParameterType(TypeParameterType node) =>
381+
found.add(node.parameter);
382+
}

0 commit comments

Comments
 (0)