[dart2js] new-rti: Implement type literals
Emit type literals in the constant pool using recipes. Change-Id: Ia5ea024b444596844e94afbe2a34ce33e8557604 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107454 Commit-Queue: Stephen Adams <sra@google.com> Reviewed-by: Mayank Patke <fishythefish@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
0256155b7a
commit
c6f708d2bf
@@ -16,6 +16,7 @@ import 'inferrer/abstract_value_domain.dart';
|
||||
import 'js_backend/native_data.dart' show NativeBasicData;
|
||||
import 'js_model/locals.dart';
|
||||
import 'kernel/dart2js_target.dart';
|
||||
import 'options.dart';
|
||||
import 'universe/selector.dart' show Selector;
|
||||
|
||||
/// The common elements and types in Dart.
|
||||
@@ -482,6 +483,7 @@ abstract class CommonElements {
|
||||
|
||||
FunctionEntity get findType;
|
||||
FunctionEntity get instanceType;
|
||||
FunctionEntity get typeLiteralMaker;
|
||||
FieldEntity get rtiAsField;
|
||||
FieldEntity get rtiCheckField;
|
||||
FieldEntity get rtiIsField;
|
||||
@@ -614,8 +616,9 @@ abstract class JCommonElements implements CommonElements {
|
||||
class CommonElementsImpl
|
||||
implements CommonElements, KCommonElements, JCommonElements {
|
||||
final ElementEnvironment _env;
|
||||
final CompilerOptions _options;
|
||||
|
||||
CommonElementsImpl(this._env);
|
||||
CommonElementsImpl(this._env, this._options);
|
||||
|
||||
ClassEntity _objectClass;
|
||||
@override
|
||||
@@ -1443,7 +1446,9 @@ class CommonElementsImpl
|
||||
ClassEntity _typeLiteralClass;
|
||||
@override
|
||||
ClassEntity get typeLiteralClass =>
|
||||
_typeLiteralClass ??= _findHelperClass('TypeImpl');
|
||||
_typeLiteralClass ??= _options.experimentNewRti
|
||||
? _findRtiClass('_Type')
|
||||
: _findHelperClass('TypeImpl');
|
||||
|
||||
ClassEntity _constMapLiteralClass;
|
||||
@override
|
||||
@@ -1730,8 +1735,9 @@ class CommonElementsImpl
|
||||
_findHelperFunction('throwNoSuchMethod');
|
||||
|
||||
@override
|
||||
FunctionEntity get createRuntimeType =>
|
||||
_findHelperFunction('createRuntimeType');
|
||||
FunctionEntity get createRuntimeType => _options.experimentNewRti
|
||||
? _findRtiFunction('_createRuntimeType')
|
||||
: _findHelperFunction('createRuntimeType');
|
||||
|
||||
@override
|
||||
FunctionEntity get fallThroughError =>
|
||||
@@ -1805,6 +1811,8 @@ class CommonElementsImpl
|
||||
|
||||
// From dart:_rti
|
||||
|
||||
ClassEntity _findRtiClass(String name) => _findClass(rtiLibrary, name);
|
||||
|
||||
FunctionEntity _findRtiFunction(String name) =>
|
||||
_findLibraryMember(rtiLibrary, name);
|
||||
|
||||
@@ -1817,6 +1825,11 @@ class CommonElementsImpl
|
||||
FunctionEntity get instanceType =>
|
||||
_instanceType ??= _findRtiFunction('instanceType');
|
||||
|
||||
FunctionEntity _typeLiteralMaker;
|
||||
@override
|
||||
FunctionEntity get typeLiteralMaker =>
|
||||
_typeLiteralMaker ??= _findRtiFunction('typeLiteral');
|
||||
|
||||
ClassEntity get _rtiImplClass => _findClass(rtiLibrary, 'Rti');
|
||||
FieldEntity _findRtiClassField(String name) =>
|
||||
_findClassMember(_rtiImplClass, name);
|
||||
|
||||
@@ -423,9 +423,12 @@ class BackendImpacts {
|
||||
BackendImpact _typeLiteral;
|
||||
|
||||
BackendImpact get typeLiteral {
|
||||
return _typeLiteral ??= new BackendImpact(
|
||||
instantiatedClasses: [_commonElements.typeLiteralClass],
|
||||
staticUses: [_commonElements.createRuntimeType]);
|
||||
return _typeLiteral ??= new BackendImpact(instantiatedClasses: [
|
||||
_commonElements.typeLiteralClass
|
||||
], staticUses: [
|
||||
_commonElements.createRuntimeType,
|
||||
if (_newRti) _commonElements.typeLiteralMaker,
|
||||
]);
|
||||
}
|
||||
|
||||
BackendImpact _stackTraceInCatch;
|
||||
|
||||
@@ -182,12 +182,10 @@ class CodegenEnqueuerListener extends EnqueuerListener {
|
||||
helper, helper.parameterStructure.callStructure));
|
||||
}
|
||||
if (type.element == _commonElements.typeLiteralClass) {
|
||||
// If we use a type literal in a constant, the compile time
|
||||
// constant emitter will generate a call to the createRuntimeType
|
||||
// helper so we register a use of that.
|
||||
FunctionEntity helper = _commonElements.createRuntimeType;
|
||||
impactBuilder.registerStaticUse(new StaticUse.staticInvoke(
|
||||
helper, helper.parameterStructure.callStructure));
|
||||
// If we use a type literal in a constant, the compile time constant
|
||||
// emitter will generate a call to a helper so we register the impact
|
||||
// that contains that call.
|
||||
_impacts.typeLiteral.registerImpact(impactBuilder, _elementEnvironment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,11 @@ import '../js/js.dart' as jsAst;
|
||||
import '../js/js.dart' show js;
|
||||
import '../js_backend/field_analysis.dart';
|
||||
import '../js_emitter/code_emitter_task.dart';
|
||||
import '../js_model/type_recipe.dart' show TypeExpressionRecipe;
|
||||
import '../options.dart';
|
||||
import 'field_analysis.dart' show JFieldAnalysis;
|
||||
import 'runtime_types.dart';
|
||||
import 'runtime_types_new.dart' show RecipeEncoder;
|
||||
|
||||
typedef jsAst.Expression _ConstantReferenceGenerator(ConstantValue constant);
|
||||
|
||||
@@ -205,6 +207,7 @@ class ConstantEmitter extends ModularConstantEmitter {
|
||||
final JElementEnvironment _elementEnvironment;
|
||||
final RuntimeTypesNeed _rtiNeed;
|
||||
final RuntimeTypesEncoder _rtiEncoder;
|
||||
final RecipeEncoder _rtiRecipeEncoder;
|
||||
final JFieldAnalysis _fieldAnalysis;
|
||||
final Emitter _emitter;
|
||||
final _ConstantReferenceGenerator _constantReferenceGenerator;
|
||||
@@ -219,6 +222,7 @@ class ConstantEmitter extends ModularConstantEmitter {
|
||||
this._elementEnvironment,
|
||||
this._rtiNeed,
|
||||
this._rtiEncoder,
|
||||
this._rtiRecipeEncoder,
|
||||
this._fieldAnalysis,
|
||||
this._emitter,
|
||||
this._constantReferenceGenerator,
|
||||
@@ -354,19 +358,34 @@ class ConstantEmitter extends ModularConstantEmitter {
|
||||
jsAst.Expression visitType(TypeConstantValue constant, [_]) {
|
||||
DartType type = constant.representedType.unaliased;
|
||||
|
||||
jsAst.Expression unexpected(TypeVariableType _variable) {
|
||||
TypeVariableType variable = _variable;
|
||||
throw failedAt(
|
||||
NO_LOCATION_SPANNABLE,
|
||||
"Unexpected type variable '${variable}'"
|
||||
" in constant '${constant.toDartText()}'");
|
||||
if (_options.experimentNewRti) {
|
||||
assert(!type.containsTypeVariables);
|
||||
|
||||
jsAst.Expression recipe = _rtiRecipeEncoder.encodeGroundRecipe(
|
||||
_emitter, TypeExpressionRecipe(type));
|
||||
|
||||
// Generate `typeLiteral(recipe)`.
|
||||
|
||||
// TODO(sra): `typeLiteral(r)` calls `createRuntimeType(findType(r))`.
|
||||
// Find a way to share the `findType` call with methods that also use the
|
||||
// type.
|
||||
return js('#(#)',
|
||||
[getHelperProperty(_commonElements.typeLiteralMaker), recipe]);
|
||||
} else {
|
||||
jsAst.Expression unexpected(TypeVariableType _variable) {
|
||||
TypeVariableType variable = _variable;
|
||||
throw failedAt(
|
||||
NO_LOCATION_SPANNABLE,
|
||||
"Unexpected type variable '${variable}'"
|
||||
" in constant '${constant.toDartText()}'");
|
||||
}
|
||||
|
||||
jsAst.Expression rti =
|
||||
_rtiEncoder.getTypeRepresentation(_emitter, type, unexpected);
|
||||
|
||||
return new jsAst.Call(
|
||||
getHelperProperty(_commonElements.createRuntimeType), [rti]);
|
||||
}
|
||||
|
||||
jsAst.Expression rti =
|
||||
_rtiEncoder.getTypeRepresentation(_emitter, type, unexpected);
|
||||
|
||||
return new jsAst.Call(
|
||||
getHelperProperty(_commonElements.createRuntimeType), [rti]);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -90,6 +90,7 @@ class CodeEmitterTask extends CompilerTask {
|
||||
namer,
|
||||
closedWorld,
|
||||
codegen.rtiEncoder,
|
||||
codegen.rtiRecipeEncoder,
|
||||
_backendStrategy.sourceInformationStrategy,
|
||||
this,
|
||||
_generateSourceMap);
|
||||
|
||||
@@ -17,6 +17,7 @@ import '../../js/js.dart' as js;
|
||||
import '../../js_backend/constant_emitter.dart';
|
||||
import '../../js_backend/namer.dart';
|
||||
import '../../js_backend/runtime_types.dart';
|
||||
import '../../js_backend/runtime_types_new.dart' show RecipeEncoder;
|
||||
import '../../options.dart';
|
||||
import '../../universe/codegen_world_builder.dart' show CodegenWorld;
|
||||
import '../../world.dart' show JClosedWorld;
|
||||
@@ -143,6 +144,7 @@ class EmitterImpl extends ModularEmitterBase implements Emitter {
|
||||
final DiagnosticReporter _reporter;
|
||||
final JClosedWorld _closedWorld;
|
||||
final RuntimeTypesEncoder _rtiEncoder;
|
||||
final RecipeEncoder _rtiRecipeEncoder;
|
||||
final CompilerTask _task;
|
||||
ModelEmitter _emitter;
|
||||
|
||||
@@ -157,6 +159,7 @@ class EmitterImpl extends ModularEmitterBase implements Emitter {
|
||||
Namer namer,
|
||||
this._closedWorld,
|
||||
this._rtiEncoder,
|
||||
this._rtiRecipeEncoder,
|
||||
SourceInformationStrategy sourceInformationStrategy,
|
||||
this._task,
|
||||
bool shouldGenerateSourceMap)
|
||||
@@ -172,6 +175,7 @@ class EmitterImpl extends ModularEmitterBase implements Emitter {
|
||||
this,
|
||||
sourceInformationStrategy,
|
||||
_rtiEncoder,
|
||||
_rtiRecipeEncoder,
|
||||
shouldGenerateSourceMap);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ import '../../js_backend/js_backend.dart'
|
||||
show Namer, ConstantEmitter, StringBackedName;
|
||||
import '../../js_backend/js_interop_analysis.dart' as jsInteropAnalysis;
|
||||
import '../../js_backend/runtime_types.dart';
|
||||
import '../../js_backend/runtime_types_new.dart' show RecipeEncoder;
|
||||
import '../../options.dart';
|
||||
import '../../universe/codegen_world_builder.dart' show CodegenWorld;
|
||||
import '../../world.dart';
|
||||
@@ -99,6 +100,7 @@ class ModelEmitter {
|
||||
this._emitter,
|
||||
this._sourceInformationStrategy,
|
||||
RuntimeTypesEncoder rtiEncoder,
|
||||
RecipeEncoder rtiRecipeEncoder,
|
||||
this._shouldGenerateSourceMap)
|
||||
: _constantOrdering = new ConstantOrdering(_closedWorld.sorter) {
|
||||
this._constantEmitter = new ConstantEmitter(
|
||||
@@ -107,6 +109,7 @@ class ModelEmitter {
|
||||
_closedWorld.elementEnvironment,
|
||||
_closedWorld.rtiNeed,
|
||||
rtiEncoder,
|
||||
rtiRecipeEncoder,
|
||||
_closedWorld.fieldAnalysis,
|
||||
_emitter,
|
||||
this.generateConstantReference,
|
||||
|
||||
@@ -138,7 +138,8 @@ class JsKernelToElementMap implements JsToElementMap, IrToElementMap {
|
||||
AnnotationsData annotations)
|
||||
: this.options = _elementMap.options {
|
||||
_elementEnvironment = new JsElementEnvironment(this);
|
||||
_commonElements = new CommonElementsImpl(_elementEnvironment);
|
||||
_commonElements =
|
||||
new CommonElementsImpl(_elementEnvironment, _elementMap.options);
|
||||
_constantEnvironment = new JsConstantEnvironment(this, environment);
|
||||
_typeConverter = new DartTypeConverter(this);
|
||||
_types = new KernelDartTypes(this);
|
||||
@@ -317,7 +318,7 @@ class JsKernelToElementMap implements JsToElementMap, IrToElementMap {
|
||||
JsKernelToElementMap.readFromDataSource(this.options, this.reporter,
|
||||
Environment environment, ir.Component component, DataSource source) {
|
||||
_elementEnvironment = new JsElementEnvironment(this);
|
||||
_commonElements = new CommonElementsImpl(_elementEnvironment);
|
||||
_commonElements = new CommonElementsImpl(_elementEnvironment, options);
|
||||
_constantEnvironment = new JsConstantEnvironment(this, environment);
|
||||
_typeConverter = new DartTypeConverter(this);
|
||||
_types = new KernelDartTypes(this);
|
||||
|
||||
@@ -122,7 +122,7 @@ class KernelToElementMapImpl implements KernelToElementMap, IrToElementMap {
|
||||
KernelToElementMapImpl(
|
||||
this.reporter, this._environment, this._frontendStrategy, this.options) {
|
||||
_elementEnvironment = new KernelElementEnvironment(this);
|
||||
_commonElements = new CommonElementsImpl(_elementEnvironment);
|
||||
_commonElements = new CommonElementsImpl(_elementEnvironment, options);
|
||||
_constantEnvironment = new KernelConstantEnvironment(this, _environment);
|
||||
_typeConverter = new DartTypeConverter(this);
|
||||
_types = new KernelDartTypes(this);
|
||||
|
||||
@@ -59,7 +59,7 @@ import 'dart:_js_names'
|
||||
unmangleGlobalNameIfPreservedAnyways,
|
||||
unmangleAllIdentifiersIfPreservedAnyways;
|
||||
|
||||
import 'dart:_rti' as newRti show getRuntimeType;
|
||||
import 'dart:_rti' as newRti show createRuntimeType, getRuntimeType;
|
||||
|
||||
part 'annotations.dart';
|
||||
part 'constant_map.dart';
|
||||
|
||||
@@ -87,7 +87,12 @@ class Rti {
|
||||
dynamic _precomputed4;
|
||||
|
||||
// The Type object corresponding to this Rti.
|
||||
Type _typeCache;
|
||||
Object _cachedRuntimeType;
|
||||
static _Type _getCachedRuntimeType(Rti rti) =>
|
||||
JS('_Type|Null', '#', rti._cachedRuntimeType);
|
||||
static void _setCachedRuntimeType(Rti rti, _Type type) {
|
||||
rti._cachedRuntimeType = type;
|
||||
}
|
||||
|
||||
/// The kind of Rti `this` is, one of the kindXXX constants below.
|
||||
///
|
||||
@@ -280,7 +285,43 @@ Rti _instanceTypeFromConstructor(constructor) {
|
||||
}
|
||||
|
||||
Type getRuntimeType(object) {
|
||||
throw UnimplementedError('getRuntimeType');
|
||||
Rti rti = instanceType(object);
|
||||
return _createRuntimeType(rti);
|
||||
}
|
||||
|
||||
/// Called from generated code.
|
||||
Type _createRuntimeType(Rti rti) {
|
||||
_Type type = Rti._getCachedRuntimeType(rti);
|
||||
if (type != null) return type;
|
||||
// TODO(https://github.com/dart-lang/language/issues/428) For NNBD transition,
|
||||
// canonicalization may be needed. It might be possible to generate a
|
||||
// star-free recipe from the canonical recipe and evaluate that.
|
||||
type = _Type(rti);
|
||||
Rti._setCachedRuntimeType(rti, type);
|
||||
return type;
|
||||
}
|
||||
|
||||
/// Called from generated code in the constant pool.
|
||||
Type typeLiteral(String recipe) {
|
||||
return _createRuntimeType(findType(recipe));
|
||||
}
|
||||
|
||||
/// Implementation of [Type] based on Rti.
|
||||
class _Type implements Type {
|
||||
final Rti _rti;
|
||||
int _hashCode;
|
||||
|
||||
_Type(this._rti);
|
||||
|
||||
int get hashCode => _hashCode ??= Rti._getCanonicalRecipe(_rti).hashCode;
|
||||
|
||||
@pragma('dart2js:noInline')
|
||||
bool operator ==(other) {
|
||||
return (other is _Type) && identical(_rti, other._rti);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => _rtiToString(_rti, null);
|
||||
}
|
||||
|
||||
/// Called from generated code.
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2019, 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.
|
||||
//
|
||||
// dart2jsOptions=--experiment-new-rti --no-minify
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
@pragma('dart2js:noInline')
|
||||
Type grab<T>() => T;
|
||||
|
||||
@pragma('dart2js:noInline')
|
||||
Type grabList<T>() => grab<List<T>>();
|
||||
|
||||
main() {
|
||||
Expect.equals('int', grab<int>().toString());
|
||||
Expect.identical(int, grab<int>());
|
||||
|
||||
Expect.equals('List<int>', grabList<int>().toString());
|
||||
|
||||
Expect.equals('List<dynamic>', (List).toString());
|
||||
}
|
||||
Reference in New Issue
Block a user