[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);
 }