[dart2js] Remove more entry checks

Remove checks on generator bodies, constructor bodies and constructor
factories, as these are all called from statically resolved contexts.

Add a TargetChecks type to convey which checks are required.

Change-Id: Ib354956d039078a5d1428de4706b7427401b08d9
Reviewed-on: https://dart-review.googlesource.com/58681
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index fcb0672..079f4c0 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -37,6 +37,7 @@
 import '../universe/call_structure.dart';
 import '../universe/selector.dart';
 import '../universe/side_effects.dart' show SideEffects;
+import '../universe/target_checks.dart' show TargetChecks;
 import '../universe/use.dart'
     show ConstantUse, ConstrainedDynamicUse, StaticUse;
 import '../universe/world_builder.dart' show CodegenWorldBuilder;
@@ -323,7 +324,7 @@
   void buildField(ir.Field node) {
     _inLazyInitializerExpression = node.isStatic;
     FieldEntity field = _elementMap.getMember(node);
-    openFunction(field);
+    openFunction(field, checks: TargetChecks.none);
     if (node.isInstanceMember && options.enableTypeAssertions) {
       HInstruction thisInstruction = localsHandler.readThis(
           sourceInformation: _sourceInformationBuilder.buildGet(node));
@@ -471,7 +472,8 @@
     ClassEntity cls = constructor.enclosingClass;
 
     if (_inliningStack.isEmpty) {
-      openFunction(constructor, node.function);
+      openFunction(constructor,
+          functionNode: node.function, checks: TargetChecks.none);
     }
 
     // [fieldValues] accumulates the field initializer values, which may be
@@ -961,8 +963,8 @@
 
   /// Builds generative constructor body.
   void buildConstructorBody(ir.Constructor constructor) {
-    openFunction(
-        _elementMap.getConstructorBody(constructor), constructor.function);
+    openFunction(_elementMap.getConstructorBody(constructor),
+        functionNode: constructor.function, checks: TargetChecks.none);
     constructor.function.body.accept(this);
     closeFunction();
   }
@@ -976,7 +978,11 @@
       return;
     }
 
-    openFunction(function, functionNode);
+    // TODO(sra): Static methods with no tear-off can be generated with no
+    // checks.
+    // TODO(sra): Instance methods can be generated with reduced checks if
+    // called only from non-dynamic call-sites.
+    openFunction(function, functionNode: functionNode);
 
     // If [functionNode] is `operator==` we explicitly add a null check at the
     // beginning of the method. This is to avoid having call sites do the null
@@ -1033,7 +1039,7 @@
   /// per-invocation checks and the body, which is later transformed, contains
   /// the re-entrant 'state machine' code.
   void buildGenerator(FunctionEntity function, ir.FunctionNode functionNode) {
-    openFunction(function, functionNode);
+    openFunction(function, functionNode: functionNode);
 
     // Prepare to tail-call the body.
 
@@ -1097,7 +1103,7 @@
   void buildGeneratorBody(
       JGeneratorBody function, ir.FunctionNode functionNode) {
     FunctionEntity entry = function.function;
-    openFunction(entry, functionNode);
+    openFunction(entry, functionNode: functionNode, checks: TargetChecks.none);
     graph.needsAsyncRewrite = true;
     if (!function.elementType.containsFreeTypeVariables) {
       // We can generate the element type in place
@@ -1118,13 +1124,16 @@
     return true;
   }
 
-  void _potentiallyAddFunctionParameterTypeChecks(ir.FunctionNode function) {
+  void _potentiallyAddFunctionParameterTypeChecks(
+      ir.FunctionNode function, TargetChecks targetChecks) {
     // Put the type checks in the first successor of the entry,
     // because that is where the type guards will also be inserted.
     // This way we ensure that a type guard will dominate the type
     // check.
 
-    checkTypeVariableBounds(targetElement);
+    if (targetChecks.checkTypeParameters) {
+      checkTypeVariableBounds(targetElement);
+    }
 
     MemberDefinition definition =
         _elementMap.getMemberDefinition(targetElement);
@@ -1140,9 +1149,22 @@
         return;
       }
       HInstruction newParameter = localsHandler.directLocals[local];
+      DartType type = _getDartTypeIfValid(variable.type);
 
-      newParameter = typeBuilder.potentiallyCheckOrTrustTypeOfParameter(
-          newParameter, _getDartTypeIfValid(variable.type));
+      if (options.strongMode) {
+        if (targetChecks.checkAllParameters ||
+            (targetChecks.checkCovariantParameters &&
+                (variable.isGenericCovariantImpl || variable.isCovariant))) {
+          newParameter = typeBuilder.potentiallyCheckOrTrustTypeOfParameter(
+              newParameter, type);
+        } else {
+          newParameter = typeBuilder.trustTypeOfParameter(newParameter, type);
+        }
+      } else {
+        newParameter = typeBuilder.potentiallyCheckOrTrustTypeOfParameter(
+            newParameter, type);
+      }
+
       localsHandler.directLocals[local] = newParameter;
     }
 
@@ -1179,7 +1201,7 @@
     // TODO(johnniwinther): Non-js-interop external functions should
     // throw a runtime error.
     assert(functionNode.body == null);
-    openFunction(function, functionNode);
+    openFunction(function, functionNode: functionNode);
 
     if (closedWorld.nativeData.isNativeMember(targetElement)) {
       nativeEmitter.nativeMethods.add(targetElement);
@@ -1259,7 +1281,11 @@
     }
   }
 
-  void openFunction(MemberEntity member, [ir.FunctionNode functionNode]) {
+  void openFunction(MemberEntity member,
+      {ir.FunctionNode functionNode, TargetChecks checks}) {
+    // TODO(sra): Pass from all sites.
+    checks ??= TargetChecks.dynamicChecks;
+
     Map<Local, AbstractValue> parameterMap = <Local, AbstractValue>{};
     if (functionNode != null) {
       void handleParameter(ir.VariableDeclaration node) {
@@ -1293,7 +1319,7 @@
     _addFunctionTypeVariablesIfNeeded(member);
 
     if (functionNode != null) {
-      _potentiallyAddFunctionParameterTypeChecks(functionNode);
+      _potentiallyAddFunctionParameterTypeChecks(functionNode, checks);
     }
     _insertTraceCall(member);
     _insertCoverageCall(member);
diff --git a/pkg/compiler/lib/src/universe/target_checks.dart b/pkg/compiler/lib/src/universe/target_checks.dart
new file mode 100644
index 0000000..6d4ac79
--- /dev/null
+++ b/pkg/compiler/lib/src/universe/target_checks.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2018, 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.
+
+library dart2js.target_checks;
+
+/// A summary of the checks required when entering a target method.
+///
+/// The target checks are used to annotate call sites with the checking required
+/// from that call site.
+///
+/// The target can either perform worst-case checks over all call sites, or can
+/// be generated as multiple entry points for different TargetChecks.
+///
+/// The TargetChecks at a call site can be refined by analysis.  For example,
+/// the generic covariant check for writing into a List might not be required
+/// when copying values from a List allocated with the same type variable value.
+///
+/// The TargetChecks at a call site can be used to inform optimizations, for
+/// example, only lowering to simpler instructions when generic covariant check
+/// is required.
+///
+/// Unsound modes can be implemented in a scoped manner by using a TargetChecks
+/// that has fewer checks than required (or no checks) in the unsound region.
+class TargetChecks {
+  // Typical of direct static call sites.
+  // Typical of static method targets with no tear-offs.
+  static final TargetChecks none = const TargetChecks._(false, false, false);
+
+  // Typical of closure calls and dynamic calls.
+  static final TargetChecks dynamicChecks =
+      const TargetChecks._(true, true, true);
+
+  // Typical of method calls.
+  static final TargetChecks covariantChecks =
+      const TargetChecks._(false, true, false);
+
+  // TODO(sra): This can be more fine-grained, talking about individual
+  // parameters.
+  final bool _checkOtherParameters;
+  final bool _checkCovariantParameters;
+  final bool _checkTypeParameters;
+  const TargetChecks._(
+    this._checkOtherParameters,
+    this._checkCovariantParameters,
+    this._checkTypeParameters,
+  );
+
+  bool get checkTypeParameters => _checkTypeParameters;
+  bool get checkCovariantParameters => _checkCovariantParameters;
+  bool get checkAllParameters => _checkOtherParameters;
+}
diff --git a/tests/compiler/dart2js/rti/emission/constructor_argument_static_strong.dart b/tests/compiler/dart2js/rti/emission/constructor_argument_static_strong.dart
new file mode 100644
index 0000000..37dfefe
--- /dev/null
+++ b/tests/compiler/dart2js/rti/emission/constructor_argument_static_strong.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2018, 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.
+
+import 'package:meta/dart2js.dart';
+
+/*class: A1:checks=[],instance*/
+class A1 {}
+
+// Constructor calls are always statically invoked, so there is no checks at the
+// entry and the `Test1` constructor does not cause any checks.
+/*class: B1:checks=[],instance*/
+class B1 implements A1 {}
+
+/*class: Test1:checks=[],instance*/
+class Test1 {
+  A1 x;
+  @noInline
+  Test1(this.x);
+}
+
+/*class: A2:checks=[],instance*/
+class A2 {}
+
+// Constructor bodies are always statically resolved, so there is no checks at
+// the entry and the `Test2` constructor body does not cause any checks.
+/*class: B2:checks=[],instance*/
+class B2 implements A2 {}
+
+/*class: Test2:checks=[]*/
+abstract class Test2 {
+  @noInline
+  Test2(A2 x) {
+    print(x);
+  }
+}
+
+/*class: Use:checks=[],instance*/
+class Use extends Test2 {
+  Use.A2() : super(new A2());
+  Use.B2() : super(new B2());
+}
+
+main() {
+  new Test1(new A1());
+  new Test1(new B1());
+  new Test1(null);
+
+  new Use.A2();
+  new Use.B2();
+}
diff --git a/tests/compiler/dart2js/rti/emission/static_argument_strong.dart b/tests/compiler/dart2js/rti/emission/static_argument_strong.dart
new file mode 100644
index 0000000..7bdc190
--- /dev/null
+++ b/tests/compiler/dart2js/rti/emission/static_argument_strong.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2018, 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.
+
+import 'package:meta/dart2js.dart';
+
+/*class: I1:checkedInstance*/
+class I1 {}
+
+/*class: I2:checkedInstance,checkedTypeArgument,checks=[],typeArgument*/
+class I2 {}
+
+// TODO(32954): Exclude $isI1 because foo is only called directly.
+/*class: A:checks=[$isI1,$isI2],instance*/
+class A implements I1, I2 {}
+
+// TODO(32954): Exclude $isI1 because foo is only called directly.
+/*class: B:checks=[$isI1,$isI2],instance*/
+class B implements I1, I2 {}
+
+@noInline
+void foo(I1 x) {}
+
+@noInline
+void bar(I2 x) {}
+
+main() {
+  dynamic f = bar;
+
+  foo(new A());
+  foo(new B());
+  f(new A());
+  f(new B());
+}