[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:
Stephen Adams
2019-06-27 16:39:36 +00:00
committed by commit-bot@chromium.org
parent 0256155b7a
commit c6f708d2bf
12 changed files with 136 additions and 31 deletions
+17 -4
View File
@@ -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';
+43 -2
View File
@@ -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());
}