diff --git a/pkg/compiler/lib/src/common_elements.dart b/pkg/compiler/lib/src/common_elements.dart index b9c18036d64..4cc4debcdff 100644 --- a/pkg/compiler/lib/src/common_elements.dart +++ b/pkg/compiler/lib/src/common_elements.dart @@ -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); diff --git a/pkg/compiler/lib/src/js_backend/backend_impact.dart b/pkg/compiler/lib/src/js_backend/backend_impact.dart index 3edb0179839..a51d05ee9c1 100644 --- a/pkg/compiler/lib/src/js_backend/backend_impact.dart +++ b/pkg/compiler/lib/src/js_backend/backend_impact.dart @@ -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; diff --git a/pkg/compiler/lib/src/js_backend/codegen_listener.dart b/pkg/compiler/lib/src/js_backend/codegen_listener.dart index 2306ad68570..c77257b731a 100644 --- a/pkg/compiler/lib/src/js_backend/codegen_listener.dart +++ b/pkg/compiler/lib/src/js_backend/codegen_listener.dart @@ -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); } } } diff --git a/pkg/compiler/lib/src/js_backend/constant_emitter.dart b/pkg/compiler/lib/src/js_backend/constant_emitter.dart index 06ec78421a4..943b3932e42 100644 --- a/pkg/compiler/lib/src/js_backend/constant_emitter.dart +++ b/pkg/compiler/lib/src/js_backend/constant_emitter.dart @@ -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 diff --git a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart index 5d3408b52ea..62e9ab0eefd 100644 --- a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart +++ b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart @@ -90,6 +90,7 @@ class CodeEmitterTask extends CompilerTask { namer, closedWorld, codegen.rtiEncoder, + codegen.rtiRecipeEncoder, _backendStrategy.sourceInformationStrategy, this, _generateSourceMap); diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart index d5ad70d3a9a..8752178c070 100644 --- a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart +++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart @@ -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); } diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart index fd8db9b9bdf..e5643a5b29b 100644 --- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart +++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart @@ -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, diff --git a/pkg/compiler/lib/src/js_model/element_map_impl.dart b/pkg/compiler/lib/src/js_model/element_map_impl.dart index 15e56a5f9c6..692c95e59a3 100644 --- a/pkg/compiler/lib/src/js_model/element_map_impl.dart +++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart @@ -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); diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart index ce5aed90336..eba6bd9a3bf 100644 --- a/pkg/compiler/lib/src/kernel/element_map_impl.dart +++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart @@ -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); diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart index beee35bc6b1..654019c2e6f 100644 --- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart +++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart @@ -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'; diff --git a/sdk/lib/_internal/js_runtime/lib/rti.dart b/sdk/lib/_internal/js_runtime/lib/rti.dart index a501e650abe..260c57307f7 100644 --- a/sdk/lib/_internal/js_runtime/lib/rti.dart +++ b/sdk/lib/_internal/js_runtime/lib/rti.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. diff --git a/tests/compiler/dart2js_extra/rti/runtime_type_2_test.dart b/tests/compiler/dart2js_extra/rti/runtime_type_2_test.dart new file mode 100644 index 00000000000..82c0535774c --- /dev/null +++ b/tests/compiler/dart2js_extra/rti/runtime_type_2_test.dart @@ -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; + +@pragma('dart2js:noInline') +Type grabList() => grab>(); + +main() { + Expect.equals('int', grab().toString()); + Expect.identical(int, grab()); + + Expect.equals('List', grabList().toString()); + + Expect.equals('List', (List).toString()); +}