[dart2js] Emit code for checks
Emit as-casts and type-checks.
The recipes and environments are dummy values, and there is no
propagation of type information, so the code is pretty terrible.
Change-Id: Iea8b7d7dddd4538187d88f12e9ed81439f74d0e1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106428
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Mayank Patke <fishythefish@google.com>
diff --git a/pkg/compiler/lib/src/common_elements.dart b/pkg/compiler/lib/src/common_elements.dart
index 36d7296..2778f65 100644
--- a/pkg/compiler/lib/src/common_elements.dart
+++ b/pkg/compiler/lib/src/common_elements.dart
@@ -99,6 +99,9 @@
LibraryEntity get foreignLibrary;
/// The dart:_internal library.
+ LibraryEntity get rtiLibrary;
+
+ /// The dart:_internal library.
LibraryEntity get internalLibrary;
/// The `NativeTypedData` class from dart:typed_data.
@@ -475,6 +478,15 @@
FunctionEntity get extractFunctionTypeObjectFromInternal;
+ // From dart:_rti
+
+ FunctionEntity get findType;
+ FieldEntity get rtiAsField;
+ FieldEntity get rtiCheckField;
+ FieldEntity get rtiIsField;
+ FunctionEntity get rtiEvalMethod;
+ FunctionEntity get rtiBindMethod;
+
// From dart:_internal
ClassEntity get symbolImplementationClass;
@@ -729,6 +741,11 @@
LibraryEntity get foreignLibrary =>
_foreignLibrary ??= _env.lookupLibrary(Uris.dart__foreign_helper);
+ LibraryEntity _rtiLibrary;
+ @override
+ LibraryEntity get rtiLibrary =>
+ _rtiLibrary ??= _env.lookupLibrary(Uris.dart__rti, required: true);
+
/// Reference to the internal library to lookup functions to always inline.
LibraryEntity _internalLibrary;
@override
@@ -1785,6 +1802,42 @@
cls.name.startsWith('Instantiation');
}
+ // From dart:_rti
+
+ FunctionEntity _findRtiFunction(String name) =>
+ _findLibraryMember(rtiLibrary, name);
+
+ FunctionEntity _findType;
+ @override
+ FunctionEntity get findType => _findType ??= _findRtiFunction('findType');
+
+ ClassEntity get _rtiImplClass => _findClass(rtiLibrary, 'Rti');
+ FieldEntity _findRtiClassField(String name) =>
+ _findClassMember(_rtiImplClass, name);
+
+ FieldEntity _rtiAsField;
+ @override
+ FieldEntity get rtiAsField => _rtiAsField ??= _findRtiClassField('_as');
+
+ FieldEntity _rtiIsField;
+ @override
+ FieldEntity get rtiIsField => _rtiIsField ??= _findRtiClassField('_is');
+
+ FieldEntity _rtiCheckField;
+ @override
+ FieldEntity get rtiCheckField =>
+ _rtiCheckField ??= _findRtiClassField('_check');
+
+ FunctionEntity _rtiEvalMethod;
+ @override
+ FunctionEntity get rtiEvalMethod =>
+ _rtiEvalMethod ??= _findClassMember(_rtiImplClass, '_eval');
+
+ FunctionEntity _rtiBindMethod;
+ @override
+ FunctionEntity get rtiBindMethod =>
+ _rtiBindMethod ??= _findClassMember(_rtiImplClass, '_bind');
+
// From dart:_internal
ClassEntity _symbolImplementationClass;
diff --git a/pkg/compiler/lib/src/js_backend/backend_impact.dart b/pkg/compiler/lib/src/js_backend/backend_impact.dart
index 256d8f1..9f683b7 100644
--- a/pkg/compiler/lib/src/js_backend/backend_impact.dart
+++ b/pkg/compiler/lib/src/js_backend/backend_impact.dart
@@ -90,8 +90,9 @@
/// The JavaScript backend dependencies for various features.
class BackendImpacts {
final CommonElements _commonElements;
+ final bool _newRti;
- BackendImpacts(this._commonElements);
+ BackendImpacts(this._commonElements, this._newRti);
BackendImpact _getRuntimeTypeArgument;
@@ -110,7 +111,7 @@
_commonElements.setRuntimeTypeInfo,
_commonElements.getRuntimeTypeInfo,
_commonElements.computeSignature,
- _commonElements.getRuntimeTypeArguments
+ _commonElements.getRuntimeTypeArguments,
], otherImpacts: [
listValues
]);
@@ -186,8 +187,9 @@
BackendImpact _asCheck;
BackendImpact get asCheck {
- return _asCheck ??=
- new BackendImpact(staticUses: [_commonElements.throwRuntimeError]);
+ return _asCheck ??= new BackendImpact(staticUses: [
+ _commonElements.throwRuntimeError,
+ ], otherImpacts: _newRti ? [usesNewRti] : []);
}
BackendImpact _throwNoSuchMethod;
@@ -759,4 +761,16 @@
], instantiatedClasses: [
_commonElements.getInstantiationClass(typeArgumentCount),
]);
+
+ BackendImpact _usesNewRti;
+
+ /// Backend impact for --experiment-new-rti.
+ BackendImpact get usesNewRti {
+ // TODO(sra): Can this be broken down into more selective impacts?
+ return _usesNewRti ??= BackendImpact(staticUses: [
+ _commonElements.findType,
+ _commonElements.rtiEvalMethod,
+ _commonElements.rtiBindMethod,
+ ]);
+ }
}
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index 136fe7f..2c0c686 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -237,7 +237,8 @@
globalInferenceResults, codegen, oneShotInterceptorData);
ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
CommonElements commonElements = closedWorld.commonElements;
- BackendImpacts impacts = new BackendImpacts(commonElements);
+ BackendImpacts impacts =
+ new BackendImpacts(commonElements, _compiler.options.experimentNewRti);
_customElementsCodegenAnalysis = new CustomElementsCodegenAnalysis(
commonElements, elementEnvironment, closedWorld.nativeData);
return new CodegenEnqueuer(
@@ -290,7 +291,8 @@
emitterTask.createEmitter(_namer, codegen, closedWorld);
// TODO(johnniwinther): Share the impact object created in
// createCodegenEnqueuer.
- BackendImpacts impacts = new BackendImpacts(closedWorld.commonElements);
+ BackendImpacts impacts = new BackendImpacts(
+ closedWorld.commonElements, _compiler.options.experimentNewRti);
_codegenImpactTransformer = new CodegenImpactTransformer(
_compiler.options,
diff --git a/pkg/compiler/lib/src/kernel/kernel_strategy.dart b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
index c5663d9..4ecf6d3 100644
--- a/pkg/compiler/lib/src/kernel/kernel_strategy.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
@@ -140,7 +140,8 @@
ResolutionEnqueuer createResolutionEnqueuer(
CompilerTask task, Compiler compiler) {
RuntimeTypesNeedBuilder rtiNeedBuilder = _createRuntimeTypesNeedBuilder();
- BackendImpacts impacts = new BackendImpacts(commonElements);
+ BackendImpacts impacts =
+ new BackendImpacts(commonElements, compiler.options.experimentNewRti);
_nativeResolutionEnqueuer = new NativeResolutionEnqueuer(
compiler.options,
elementEnvironment,
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 102f8bd..a80a7a6 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -696,6 +696,7 @@
if (instruction is HCheck) {
if (instruction is HTypeConversion ||
instruction is HPrimitiveCheck ||
+ instruction is HAsCheck ||
instruction is HBoolConversion) {
String inputName = variableNames.getName(instruction.checkedInput);
if (variableNames.getName(instruction) == inputName) {
@@ -3333,12 +3334,32 @@
@override
visitIsTest(HIsTest node) {
- throw UnimplementedError('SsaCodeGenerator.visitIsTest $node');
+ use(node.typeInput);
+ js.Expression first = pop();
+ use(node.checkedInput);
+ js.Expression second = pop();
+
+ FieldEntity field = _commonElements.rtiIsField;
+ js.Name name = _namer.instanceFieldPropertyName(field);
+
+ push(js.js('#.#(#)', [first, name, second]).withSourceInformation(
+ node.sourceInformation));
}
@override
visitAsCheck(HAsCheck node) {
- throw UnimplementedError('SsaCodeGenerator.visitAsCheck $node');
+ use(node.typeInput);
+ js.Expression first = pop();
+ use(node.checkedInput);
+ js.Expression second = pop();
+
+ FieldEntity field = node.isTypeError
+ ? _commonElements.rtiCheckField
+ : _commonElements.rtiAsField;
+ js.Name name = _namer.instanceFieldPropertyName(field);
+
+ push(js.js('#.#(#)', [first, name, second]).withSourceInformation(
+ node.sourceInformation));
}
@override
@@ -3348,16 +3369,56 @@
@override
visitLoadType(HLoadType node) {
- throw UnimplementedError('SsaCodeGenerator.visitLoadType $node');
+ FunctionEntity helperElement = _commonElements.findType;
+ _registry.registerStaticUse(
+ new StaticUse.staticInvoke(helperElement, CallStructure.ONE_ARG));
+ // TODO(sra): Encode recipe.
+ js.Expression recipe = js.string('${node.typeExpression}');
+ js.Expression helper = _emitter.staticFunctionAccess(helperElement);
+ push(js.js(r'#(#)', [helper, recipe]).withSourceInformation(
+ node.sourceInformation));
}
@override
visitTypeEval(HTypeEval node) {
- throw UnimplementedError('SsaCodeGenerator.visitTypeEval $node');
+ // Call `env._eval("recipe")`.
+ use(node.inputs[0]);
+ js.Expression environment = pop();
+ // TODO(sra): Encode recipe.
+ js.Expression recipe = js.string('${node.typeExpression}');
+
+ MemberEntity method = _commonElements.rtiEvalMethod;
+ Selector selector = Selector.fromElement(method);
+ js.Name methodLiteral = _namer.invocationName(selector);
+ push(js.js('#.#(#)', [
+ environment,
+ methodLiteral,
+ recipe
+ ]).withSourceInformation(node.sourceInformation));
+
+ _registry.registerStaticUse(
+ new StaticUse.directInvoke(method, selector.callStructure, null));
}
@override
visitTypeBind(HTypeBind node) {
- throw UnimplementedError('SsaCodeGenerator.visitTypeBind $node');
+ // Call `env1._bind(env2)`.
+ assert(node.inputs.length == 2);
+ use(node.inputs[0]);
+ js.Expression environment = pop();
+ use(node.inputs[1]);
+ js.Expression extensions = pop();
+
+ MemberEntity method = _commonElements.rtiEvalMethod;
+ Selector selector = Selector.fromElement(method);
+ js.Name methodLiteral = _namer.invocationName(selector);
+ push(js.js('#.#(#)', [
+ environment,
+ methodLiteral,
+ extensions
+ ]).withSourceInformation(node.sourceInformation));
+
+ _registry.registerStaticUse(
+ new StaticUse.directInvoke(method, selector.callStructure, null));
}
}
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index 8ffd0ed..c0ae4ec 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -755,6 +755,20 @@
}
@override
+ void visitAsCheck(HAsCheck instruction) {
+ // Type checks and cast checks compile to code that only use their input
+ // once, so we can safely visit them and try to merge the input.
+ visitInstruction(instruction);
+ }
+
+ @override
+ void visitTypeEval(HTypeEval instruction) {
+ // Type expressions compile to code that only use their input once, so we
+ // can safely visit them and try to merge the input.
+ visitInstruction(instruction);
+ }
+
+ @override
void visitPrimitiveCheck(HPrimitiveCheck instruction) {}
@override
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index d9c1357..ac8f779 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -4383,6 +4383,9 @@
HInstruction get checkedInput => inputs[1];
@override
+ bool isJsStatement() => false;
+
+ @override
accept(HVisitor visitor) => visitor.visitAsCheck(this);
@override
diff --git a/pkg/compiler/lib/src/ssa/type_builder.dart b/pkg/compiler/lib/src/ssa/type_builder.dart
index 36300b5..11c31ec 100644
--- a/pkg/compiler/lib/src/ssa/type_builder.dart
+++ b/pkg/compiler/lib/src/ssa/type_builder.dart
@@ -353,6 +353,10 @@
if (type == null) return original;
type = type.unaliased;
+ if (type.isDynamic) return original;
+ if (type.isVoid) return original;
+ if (type == _closedWorld.commonElements.objectType) return original;
+
HInstruction reifiedType =
analyzeTypeArgumentNewRti(type, builder.sourceElement);
if (type is InterfaceType) {
diff --git a/sdk/lib/_internal/js_runtime/lib/rti.dart b/sdk/lib/_internal/js_runtime/lib/rti.dart
index 1a12e60..0f6aa69 100644
--- a/sdk/lib/_internal/js_runtime/lib/rti.dart
+++ b/sdk/lib/_internal/js_runtime/lib/rti.dart
@@ -223,7 +223,7 @@
/// Evaluate a ground-term type.
/// Called from generated code.
-Rti rtiTypeEval(String recipe) {
+Rti findType(String recipe) {
_Universe.eval(_theUniverse(), recipe);
}