[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: https://github.com/dart-lang/sdk/issues/48585
Change-Id: Ib5641eb666527acc3b7f13a4a00dea34e0122b52
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/266540
Reviewed-by: Mark Zhou <markzipan@google.com>
diff --git a/pkg/dev_compiler/lib/src/compiler/js_names.dart b/pkg/dev_compiler/lib/src/compiler/js_names.dart
index 3c48dc8..97921d9 100644
--- a/pkg/dev_compiler/lib/src/compiler/js_names.dart
+++ b/pkg/dev_compiler/lib/src/compiler/js_names.dart
@@ -19,10 +19,6 @@
static const operatorIsPrefix = r'$is_';
static const operatorSignature = r'$signature';
static const rtiName = r'$ti';
- // TODO(48585) Fix this name.
- static const futureClassName = 'TODO';
- // TODO(48585) Fix this name.
- static const listClassName = 'TODO';
static const rtiAsField = '_as';
static const rtiIsField = '_is';
}
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index b9a3646..7827185 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -41,6 +41,7 @@
import 'nullable_inference.dart';
import 'property_model.dart';
import 'target.dart' show allowedNativeTest;
+import 'type_environment.dart';
import 'type_recipe_generator.dart';
import 'type_table.dart';
@@ -131,6 +132,12 @@
/// True when a class is emitting a deferred class hierarchy.
bool _emittingDeferredType = false;
+ /// The current type environment of type parameters introduced to the scope
+ /// via generic classes and functions.
+ DDCTypeEnvironment _currentTypeEnvironment = const EmptyTypeEnvironment();
+
+ final TypeRecipeGenerator _typeRecipeGenerator;
+
/// The current element being loaded.
/// We can use this to determine if we're loading top-level code or not:
///
@@ -188,6 +195,9 @@
final _superHelpers = <String, js_ast.Method>{};
+ /// Cache for the results of calling [_typeParametersInHierarchy].
+ final _typeParametersInHierarchyCache = <Class, bool>{};
+
// Compilation of Kernel's [BreakStatement].
//
// Kernel represents Dart's `break` and `continue` uniformly as
@@ -345,7 +355,8 @@
_asyncStarImplClass = sdk.getClass('dart:async', '_AsyncStarImpl'),
_assertInteropMethod = sdk.getTopLevelMember(
'dart:_runtime', 'assertInterop') as Procedure,
- _futureOrNormalizer = FutureOrNormalizer(_coreTypes);
+ _futureOrNormalizer = FutureOrNormalizer(_coreTypes),
+ _typeRecipeGenerator = TypeRecipeGenerator(_coreTypes);
@override
Library? get currentLibrary => _currentLibrary;
@@ -682,6 +693,12 @@
_currentClass = c;
_currentLibrary = c.enclosingLibrary;
_currentUri = c.fileUri;
+ var savedTypeEnvironment = _currentTypeEnvironment;
+ // When compiling the type heritage of the class we can't reference an rti
+ // object attached to an instance. Instead we construct a type environment
+ // manually when needed. Later we use the rti attached to an instance for
+ // a simpler representation within instance members of the class.
+ _currentTypeEnvironment = _currentTypeEnvironment.extend(c.typeParameters);
// Mixins are unrolled in _defineClass.
// If this class is annotated with `@JS`, then there is nothing to emit.
@@ -697,6 +714,7 @@
_currentClass = savedClass;
_currentLibrary = savedLibrary;
_currentUri = savedUri;
+ _currentTypeEnvironment = savedTypeEnvironment;
}
/// To emit top-level classes, we sometimes need to reorder them.
@@ -759,7 +777,15 @@
});
var jsCtors = _defineConstructors(c, className);
+ // Emitting class members in a class type environment results in a more
+ // succinct type representation when referencing class type arguments from
+ // instance members.
+ var savedTypeEnvironment = _currentTypeEnvironment;
+ if (c.typeParameters.isNotEmpty) {
+ _currentTypeEnvironment = ClassTypeEnvironment(c.typeParameters);
+ }
var jsMethods = _emitClassMethods(c);
+ _currentTypeEnvironment = savedTypeEnvironment;
_emitSuperHelperSymbols(body);
// Deferred supertypes must be evaluated lazily while emitting classes to
@@ -785,7 +811,7 @@
// Attach caches on all canonicalized types.
if (_options.newRuntimeTypes) {
- var name = TypeRecipeGenerator.interfaceTypeRecipe(c);
+ var name = _typeRecipeGenerator.interfaceTypeRecipe(c);
body.add(runtimeStatement(
'addRtiResources(#, #)', [className, js.string(name)]));
}
@@ -1687,6 +1713,18 @@
// multiple constructors, but it needs to be balanced against readability.
body.add(_initializeFields(fields, node));
+ // Instances of classes with type arguments need an rti object attached to
+ // them since the type arguments could be instantiated differently for
+ // each instance.
+ if (_options.newRuntimeTypes && _typeParametersInHierarchy(cls)) {
+ var type = cls.getThisType(_coreTypes, Nullability.nonNullable);
+ // Only set the rti if there isn't one already. This avoids superclasses
+ // from overwriting the value already set by subclass.
+ var rtiProperty = propertyName(js_ast.FixedNames.rtiName);
+ body.add(js.statement(
+ 'this.# = this.# || #', [rtiProperty, rtiProperty, _emitType(type)]));
+ }
+
// If no superinitializer is provided, an implicit superinitializer of the
// form `super()` is added at the end of the initializer list, unless the
// enclosing class is class Object.
@@ -1700,6 +1738,19 @@
return body;
}
+ /// Returns `true` if [cls] or any of its transitive super classes has
+ /// generic type parameters.
+ bool _typeParametersInHierarchy(Class? cls) {
+ if (cls == null) return false;
+ var cachedResult = _typeParametersInHierarchyCache[cls];
+ if (cachedResult != null) return cachedResult;
+ var hasTypeParameters = cls.typeParameters.isNotEmpty
+ ? true
+ : _typeParametersInHierarchy(cls.superclass);
+ _typeParametersInHierarchyCache[cls] = hasTypeParameters;
+ return hasTypeParameters;
+ }
+
js_ast.LiteralString _constructorName(String name) {
if (name == '') {
// Default constructors (factory or not) use `new` as their name.
@@ -2182,12 +2233,16 @@
/// own type parameters, this will need to be changed to call
/// [_emitFunction] instead.
var name = node.name.text;
+ var savedTypeEnvironment = _currentTypeEnvironment;
+ _currentTypeEnvironment =
+ _currentTypeEnvironment.extend(function.typeParameters);
var jsBody = _emitSyncFunctionBody(function, name);
var jsName = _constructorName(name);
memberNames[node] = jsName.valueWithoutQuotes;
+ var jsParams = _emitParameters(function);
+ _currentTypeEnvironment = savedTypeEnvironment;
- return js_ast.Method(jsName, js_ast.Fun(_emitParameters(function), jsBody),
- isStatic: true)
+ return js_ast.Method(jsName, js_ast.Fun(jsParams, jsBody), isStatic: true)
..sourceInformation = _nodeEnd(node.fileEndOffset);
}
@@ -2945,10 +3000,79 @@
/// Returns an expression that evaluates to the rti object from the dart:_rti
/// library that represents [type].
js_ast.Expression _newEmitType(DartType type) {
+ /// Returns an expression that evaluates a type [recipe] within the type
+ /// [environment].
+ ///
+ /// At runtime the expression will evaluate to an rti object.
+ js_ast.Expression emitRtiEval(
+ js_ast.Expression environment, String recipe) =>
+ js.call('#.#("$recipe")',
+ [environment, _emitMemberName('_eval', memberClass: rtiClass)]);
+
+ /// Returns an expression that binds a type [parameter] within the type
+ /// [environment].
+ ///
+ /// At runtime the expression will evaluate to an rti object that has been
+ /// extended to include the provided [parameter].
+ js_ast.Expression emitRtiBind(
+ js_ast.Expression environment, TypeParameter parameter) =>
+ js.call('#.#(#)', [
+ environment,
+ _emitMemberName('_bind', memberClass: rtiClass),
+ _emitTypeParameter(parameter)
+ ]);
+
+ /// Returns an expression that evaluates a type [recipe] in a type
+ /// [environment] resulting in an rti object.
+ js_ast.Expression evalInEnvironment(
+ DDCTypeEnvironment environment, String recipe) {
+ if (environment is EmptyTypeEnvironment) {
+ return js.call('#.findType("$recipe")', [emitLibraryName(rtiLibrary)]);
+ } else if (environment is BindingTypeEnvironment) {
+ js_ast.Expression env;
+ if (environment.isSingleTypeParameter) {
+ // An environment with a single type parameter can be simplified to
+ // just that parameter.
+ env = _emitTypeParameter(environment.parameters.single);
+ } else {
+ var environmentTypes = environment.parameters;
+ // Create a dummy interface type to "hold" type arguments.
+ env = emitRtiEval(_emitTypeParameter(environmentTypes.first), '@<0>');
+ // Bind remaining type arguments.
+ for (var i = 1; i < environmentTypes.length; i++) {
+ env = emitRtiBind(env, environmentTypes[i]);
+ }
+ }
+ return emitRtiEval(env, recipe);
+ } else if (environment is ClassTypeEnvironment) {
+ // Class type environments are already constructed and attached to the
+ // instance of a generic class.
+ return js.call('this.${js_ast.FixedNames.rtiName}');
+ } else if (environment is ExtendedClassTypeEnvironment) {
+ // A generic class instance already stores a reference to a type
+ // containing all of its type arguments.
+ var env = js.call('this.${js_ast.FixedNames.rtiName}');
+ // Bind extra type parameters.
+ for (var parameter in environment.extendedParameters) {
+ env = emitRtiBind(env, parameter);
+ }
+ return emitRtiEval(env, recipe);
+ } else {
+ _typeCompilationError(type,
+ 'Unexpected DDCTypeEnvironment type (${environment.runtimeType}).');
+ }
+ }
+
+ // TODO(nshahan) Avoid calling _emitType when we actually want a
+ // reference to an rti that already exists in scope.
+ if (type is TypeParameterType && type.isPotentiallyNonNullable) {
+ return _emitTypeParameterType(type, emitNullability: false);
+ }
var normalizedType = _futureOrNormalizer.normalize(type);
try {
- var recipe = normalizedType.accept(TypeRecipeGenerator());
- return js.call('#.findType("$recipe")', [emitLibraryName(rtiLibrary)]);
+ var result = _typeRecipeGenerator.recipeInEnvironment(
+ normalizedType, _currentTypeEnvironment);
+ return evalInEnvironment(result.requiredEnvironment, result.recipe);
} on UnsupportedError catch (e) {
_typeCompilationError(normalizedType, e.message ?? 'Unknown Error');
}
@@ -3443,6 +3567,8 @@
}
js_ast.Fun _emitFunction(FunctionNode f, String? name) {
+ var savedTypeEnvironment = _currentTypeEnvironment;
+ _currentTypeEnvironment = _currentTypeEnvironment.extend(f.typeParameters);
// normal function (sync), vs (sync*, async, async*)
var isSync = f.asyncMarker == AsyncMarker.Sync;
var formals = _emitParameters(f);
@@ -3462,6 +3588,7 @@
: _emitGeneratorFunctionBody(f, name);
block = super.exitFunction(formals, block);
+ _currentTypeEnvironment = savedTypeEnvironment;
return js_ast.Fun(formals, block);
}
@@ -5965,9 +6092,11 @@
case JsGetName.RTI_NAME:
return js.string(js_ast.FixedNames.rtiName);
case JsGetName.FUTURE_CLASS_TYPE_NAME:
- return js.string(js_ast.FixedNames.futureClassName);
+ return js.string(
+ _typeRecipeGenerator.interfaceTypeRecipe(_coreTypes.futureClass));
case JsGetName.LIST_CLASS_TYPE_NAME:
- return js.string(js_ast.FixedNames.listClassName);
+ return js.string(
+ _typeRecipeGenerator.interfaceTypeRecipe(_coreTypes.listClass));
case JsGetName.RTI_FIELD_AS:
return _emitMemberName(js_ast.FixedNames.rtiAsField,
memberClass: rtiClass);
@@ -6907,12 +7036,16 @@
return js_ast.Property(symbol, constant);
}
- var type = _emitInterfaceType(
- node.getType(_staticTypeContext) as InterfaceType,
- emitNullability: false);
- var prototype = js.call('#.prototype', [type]);
+ var type = node.getType(_staticTypeContext);
+ var classRef =
+ _emitInterfaceType(type as InterfaceType, emitNullability: false);
+ var prototype = js.call('#.prototype', [classRef]);
var properties = [
js_ast.Property(propertyName('__proto__'), prototype),
+ if (_options.newRuntimeTypes && type.typeArguments.isNotEmpty)
+ // Generic interface type instances require a type information tag.
+ js_ast.Property(
+ propertyName(js_ast.FixedNames.rtiName), _emitType(type)),
for (var e in node.fieldValues.entries.toList().reversed)
entryToProperty(e),
];
diff --git a/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart b/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart
index 76a4b61..a97f328 100644
--- a/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart
+++ b/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart
@@ -371,3 +371,12 @@
bool _isDartInternal(Uri uri) =>
uri.isScheme('dart') && uri.path == '_internal';
+
+/// Collects all `TypeParameter`s from the `TypeParameterType`s present in the
+/// visited `DartType`.
+class TypeParameterTypeFinder extends RecursiveVisitor<void> {
+ final found = <TypeParameter>{};
+ @override
+ void visitTypeParameterType(TypeParameterType node) =>
+ found.add(node.parameter);
+}
diff --git a/pkg/dev_compiler/lib/src/kernel/type_environment.dart b/pkg/dev_compiler/lib/src/kernel/type_environment.dart
new file mode 100644
index 0000000..c8d5fd5
--- /dev/null
+++ b/pkg/dev_compiler/lib/src/kernel/type_environment.dart
@@ -0,0 +1,178 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:collection';
+
+import 'package:kernel/ast.dart';
+
+/// Type environments made of type parameters.
+///
+/// At runtime, type recipes are evaluated in an environment to produce an rti.
+/// These compile time representations are used to calculate the indices of type
+/// parameters in type recipes.
+abstract class DDCTypeEnvironment {
+ /// Creates a new environment by adding [parameters] to those already in this
+ /// environment.
+ DDCTypeEnvironment extend(List<TypeParameter> parameters);
+
+ /// Reduces this environment down to an environment that will contain
+ /// [requiredTypes] and be output in a compact representation.
+ DDCTypeEnvironment prune(Iterable<TypeParameter> requiredParameters);
+
+ /// Returns the index of [parameter] in this environment for use in a type
+ /// recipe or a negative value if the parameter was not found.
+ int recipeIndexOf(TypeParameter parameter);
+}
+
+/// An empty environment that signals no type parameters are present or needed.
+///
+/// Facilitates building other environments.
+class EmptyTypeEnvironment implements DDCTypeEnvironment {
+ const EmptyTypeEnvironment();
+
+ @override
+ DDCTypeEnvironment extend(List<TypeParameter> parameters) =>
+ parameters.isEmpty ? this : BindingTypeEnvironment(parameters);
+
+ @override
+ DDCTypeEnvironment prune(Iterable<TypeParameter> requiredParameters) => this;
+
+ @override
+ int recipeIndexOf(TypeParameter parameter) => -1;
+}
+
+/// A type environment introduced by a class with one or more generic type
+/// parameters.
+class ClassTypeEnvironment implements DDCTypeEnvironment {
+ final List<TypeParameter> _typeParameters;
+ ClassTypeEnvironment(this._typeParameters);
+
+ @override
+ DDCTypeEnvironment extend(List<TypeParameter> parameters) =>
+ parameters.isEmpty
+ ? this
+ : ExtendedClassTypeEnvironment(this, [...parameters]);
+
+ @override
+ DDCTypeEnvironment prune(Iterable<TypeParameter> requiredParameters) =>
+ // If any parameters are required, the class type environment already
+ // exists and a reference to it is suitably compact.
+ requiredParameters.any(_typeParameters.contains)
+ ? this
+ : const EmptyTypeEnvironment();
+
+ @override
+ int recipeIndexOf(TypeParameter parameter) {
+ var i = _typeParameters.indexOf(parameter);
+ if (i < 0) return i;
+ // Index for class type parameters is one based. Zero refers to the full
+ // class rti with all type arguments.
+ return i + 1;
+ }
+}
+
+/// A type environment containing multiple type parameters.
+class BindingTypeEnvironment implements DDCTypeEnvironment {
+ final List<TypeParameter> _typeParameters;
+
+ BindingTypeEnvironment(this._typeParameters);
+
+ @override
+ DDCTypeEnvironment extend(List<TypeParameter> parameters) =>
+ parameters.isEmpty
+ ? this
+ : BindingTypeEnvironment([
+ // Place new parameters first so they can effectively shadow
+ // parameters already in the environment.
+ ...parameters, ..._typeParameters
+ ]);
+
+ @override
+ DDCTypeEnvironment prune(Iterable<TypeParameter> requiredParameters) {
+ var foundParameters = requiredParameters.where(_typeParameters.contains);
+ if (foundParameters.isEmpty) return const EmptyTypeEnvironment();
+ if (foundParameters.length == _typeParameters.length) return this;
+ return BindingTypeEnvironment(foundParameters.toList());
+ }
+
+ @override
+ int recipeIndexOf(TypeParameter parameter) {
+ var i = _typeParameters.indexOf(parameter);
+ if (i < 0) return i;
+ // Environments containing a single parameter can have a more compact
+ // representation with a zero based index.
+ if (isSingleTypeParameter) return i;
+ return i + 1;
+ }
+
+ /// The type parameters in this environment.
+ List<TypeParameter> get parameters => UnmodifiableListView(_typeParameters);
+
+ /// Returns `true` if this environment only contains a single type parameter.
+ bool get isSingleTypeParameter => _typeParameters.length == 1;
+}
+
+/// A type environment based on one introduced by a generic class but extended
+/// with additional parameters from methods with generic type parameters.
+class ExtendedClassTypeEnvironment implements DDCTypeEnvironment {
+ final ClassTypeEnvironment _classEnvironment;
+ final List<TypeParameter> _typeParameters;
+
+ ExtendedClassTypeEnvironment(this._classEnvironment, this._typeParameters);
+
+ @override
+ DDCTypeEnvironment extend(List<TypeParameter> parameters) =>
+ parameters.isEmpty
+ ? this
+ : ExtendedClassTypeEnvironment(_classEnvironment, [
+ // Place new parameters first so they can effectively shadow
+ // parameters already in the environment.
+ ...parameters, ..._typeParameters
+ ]);
+
+ @override
+ DDCTypeEnvironment prune(Iterable<TypeParameter> requiredParameters) {
+ var classEnvironmentNeeded =
+ requiredParameters.any(_classEnvironment._typeParameters.contains);
+ var additionalParameters =
+ requiredParameters.where(_typeParameters.contains);
+ if (additionalParameters.isEmpty) {
+ return classEnvironmentNeeded
+ // Simply using the class environment has a compact representation
+ // and is already constructed.
+ ? _classEnvironment
+ // No type parameters are needed from this environment.
+ : const EmptyTypeEnvironment();
+ }
+ if (!classEnvironmentNeeded) {
+ // None of the parameters are needed from the class environment.
+ return BindingTypeEnvironment(additionalParameters.toList());
+ }
+ // This is already the exact environment needed.
+ if (additionalParameters.length == _typeParameters.length) return this;
+ // An extended class environment with fewer additional parameters is needed.
+ return ExtendedClassTypeEnvironment(
+ _classEnvironment, additionalParameters.toList());
+ }
+
+ @override
+ int recipeIndexOf(TypeParameter parameter) {
+ // Search in the extended type parameters first. They can shadow parameters
+ // from the class.
+ var i = _typeParameters.indexOf(parameter);
+ // Type parameter was found and should be a one based index. Zero refers to
+ // the original class rti that is the basis on this environment.
+ if (i >= 0) return i + 1;
+ i = _classEnvironment.recipeIndexOf(parameter);
+ // The type parameter bindings with the closest scope have the lowest
+ // indices. Since the parameter was found in the class binding the index
+ // must be offset by the number of extended parameters.
+ if (i > 0) return i + _typeParameters.length;
+ // Type parameter not found.
+ return -1;
+ }
+
+ List<TypeParameter> get extendedParameters =>
+ UnmodifiableListView(_typeParameters);
+}
diff --git a/pkg/dev_compiler/lib/src/kernel/type_recipe_generator.dart b/pkg/dev_compiler/lib/src/kernel/type_recipe_generator.dart
index 719b6c7..c633ba1 100644
--- a/pkg/dev_compiler/lib/src/kernel/type_recipe_generator.dart
+++ b/pkg/dev_compiler/lib/src/kernel/type_recipe_generator.dart
@@ -4,15 +4,48 @@
import 'package:js_shared/synced/recipe_syntax.dart' show Recipe;
import 'package:kernel/ast.dart';
+import 'package:kernel/core_types.dart';
import 'package:path/path.dart' as p;
import '../compiler/js_names.dart';
+import 'kernel_helpers.dart';
+import 'type_environment.dart';
+
+class TypeRecipeGenerator {
+ final _TypeRecipeVisitor _recipeVisitor;
+
+ TypeRecipeGenerator(CoreTypes coreTypes)
+ : _recipeVisitor =
+ _TypeRecipeVisitor(const EmptyTypeEnvironment(), coreTypes);
+
+ GeneratedRecipe recipeInEnvironment(
+ DartType type, DDCTypeEnvironment environment) {
+ var typeParameterFinder = TypeParameterTypeFinder();
+ type.accept(typeParameterFinder);
+ _recipeVisitor._typeEnvironment =
+ environment.prune(typeParameterFinder.found);
+ return GeneratedRecipe(
+ type.accept(_recipeVisitor), _recipeVisitor._typeEnvironment);
+ }
+
+ String interfaceTypeRecipe(Class node) =>
+ _recipeVisitor._interfaceTypeRecipe(node);
+}
/// A visitor to generate type recipe strings from a [DartType].
///
/// The recipes are used by the 'dart:_rti' library while running the compiled
/// application.
-class TypeRecipeGenerator extends DartTypeVisitor<String> {
+class _TypeRecipeVisitor extends DartTypeVisitor<String> {
+ /// The type environment to evaluate recipes in.
+ ///
+ /// Used to determine the indices for type variables.
+ DDCTypeEnvironment _typeEnvironment;
+
+ final CoreTypes _coreTypes;
+
+ _TypeRecipeVisitor(this._typeEnvironment, this._coreTypes);
+
@override
String defaultDartType(DartType node) {
return 'TODO';
@@ -26,11 +59,19 @@
@override
String visitInterfaceType(InterfaceType node) {
- var recipe = interfaceTypeRecipe(node.classNode);
- if (node.typeArguments.isEmpty) {
- return '$recipe${_nullabilityRecipe(node)}';
+ // Generate the interface type recipe.
+ var recipeBuffer = StringBuffer(_interfaceTypeRecipe(node.classNode));
+ // Generate the recipes for all type arguments.
+ if (node.typeArguments.isNotEmpty) {
+ recipeBuffer.write(Recipe.startTypeArgumentsString);
+ recipeBuffer.writeAll(
+ node.typeArguments.map((typeArgument) => typeArgument.accept(this)),
+ Recipe.separatorString);
+ recipeBuffer.write(Recipe.endTypeArgumentsString);
}
- return 'TODO';
+ // Add nullability.
+ recipeBuffer.write(_nullabilityRecipe(node));
+ return recipeBuffer.toString();
}
@override
@@ -43,8 +84,10 @@
String visitFunctionType(FunctionType node) => defaultDartType(node);
@override
- String visitTypeParameterType(TypeParameterType node) =>
- defaultDartType(node);
+ String visitTypeParameterType(TypeParameterType node) {
+ var i = _typeEnvironment.recipeIndexOf(node.parameter);
+ return '$i${_nullabilityRecipe(node)}';
+ }
@override
String visitTypedefType(TypedefType node) => defaultDartType(node);
@@ -59,7 +102,8 @@
'${_nullabilityRecipe(node)}';
@override
- String visitNullType(NullType node) => defaultDartType(node);
+ String visitNullType(NullType node) =>
+ _interfaceTypeRecipe(_coreTypes.deprecatedNullClass);
@override
String visitExtensionType(ExtensionType node) => defaultDartType(node);
@@ -69,14 +113,14 @@
visitTypeParameterType(node.left);
/// Returns the recipe for the interface type introduced by [cls].
- static String interfaceTypeRecipe(Class cls) {
+ String _interfaceTypeRecipe(Class cls) {
var path = p.withoutExtension(cls.enclosingLibrary.importUri.path);
var library = pathToJSIdentifier(path);
return '$library${Recipe.librarySeparatorString}${cls.name}';
}
/// Returns the recipe representation of [nullability].
- static String _nullabilityRecipe(DartType type) {
+ String _nullabilityRecipe(DartType type) {
switch (type.declaredNullability) {
case Nullability.undetermined:
if (type is TypeParameterType) {
@@ -96,3 +140,23 @@
}
}
}
+
+/// Packages the type recipe and the environment needed to evaluate it at
+/// runtime.
+///
+/// Returned from [TypeRecipeGenerator.recipeInEnvironment].
+class GeneratedRecipe {
+ /// A type recipe that can be used to evaluate a type at runtime in the
+ /// [requiredEnvironment].
+ ///
+ /// For use with the dart:_rti library.
+ final String recipe;
+
+ /// The environment required to properly evaluate [recipe] within.
+ ///
+ /// This environment has been reduced down to be represented compactly with
+ /// only the necessary type parameters.
+ final DDCTypeEnvironment requiredEnvironment;
+
+ GeneratedRecipe(this.recipe, this.requiredEnvironment);
+}
diff --git a/sdk/lib/_internal/js_dev_runtime/private/foreign_helper.dart b/sdk/lib/_internal/js_dev_runtime/private/foreign_helper.dart
index 8eef711..a6d1bec 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/foreign_helper.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/foreign_helper.dart
@@ -4,6 +4,7 @@
library dart._foreign_helper;
+import 'dart:_interceptors' show JSArray;
import 'dart:_js_helper' show notNull;
import 'dart:_js_shared_embedded_names' show JsBuiltin, JsGetName;
import 'dart:_rti' show Rti;
@@ -298,12 +299,12 @@
/// Returns the interceptor for [object].
///
// TODO(nshahan) Replace calls at compile time?
-external getInterceptor(object);
+Object getInterceptor(object) => object;
/// Returns the Rti object for the type for JavaScript arrays via JS-interop.
///
// TODO(nshahan) Replace calls at compile time?
-external Object getJSArrayInteropRti();
+Object getJSArrayInteropRti() => TYPE_REF<JSArray>();
/// Returns a raw reference to the JavaScript function which implements
/// [function].