Handle dynamic/instance access in ir/impact.dart

Change-Id: Iaae339550bc5f693d592012e1798c9e64002f4ec
Reviewed-on: https://dart-review.googlesource.com/c/88965
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/ir/impact.dart b/pkg/compiler/lib/src/ir/impact.dart
index 99a364b..edd7503 100644
--- a/pkg/compiler/lib/src/ir/impact.dart
+++ b/pkg/compiler/lib/src/ir/impact.dart
@@ -2,6 +2,9 @@
 // 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:front_end/src/api_unstable/dart2js.dart'
+    show operatorFromString;
+
 import 'package:kernel/ast.dart' as ir;
 import 'package:kernel/class_hierarchy.dart' as ir;
 import 'package:kernel/type_environment.dart' as ir;
@@ -374,4 +377,119 @@
           node.target, node.arguments, getDeferredImport(node));
     }
   }
+
+  void registerLocalFunctionInvocation(
+      ir.FunctionDeclaration localFunction, ir.Arguments arguments);
+
+  void registerDynamicInvocation(ir.DartType receiverType,
+      ClassRelation relation, ir.Name name, ir.Arguments arguments);
+
+  void registerInstanceInvocation(ir.DartType receiverType,
+      ClassRelation relation, ir.Member target, ir.Arguments arguments);
+
+  void registerFunctionInvocation(
+      ir.DartType receiverType, ir.Arguments arguments);
+
+  @override
+  void handleMethodInvocation(
+      ir.MethodInvocation node,
+      ir.DartType receiverType,
+      ArgumentTypes argumentTypes,
+      ir.DartType returnType) {
+    ir.Expression receiver = node.receiver;
+    if (receiver is ir.VariableGet &&
+        receiver.variable.isFinal &&
+        receiver.variable.parent is ir.FunctionDeclaration) {
+      registerLocalFunctionInvocation(receiver.variable.parent, node.arguments);
+    } else {
+      ClassRelation relation = receiver is ir.ThisExpression
+          ? ClassRelation.thisExpression
+          : ClassRelation.subtype;
+
+      ir.Member interfaceTarget = node.interfaceTarget;
+      if (interfaceTarget == null) {
+        registerDynamicInvocation(
+            receiverType, relation, node.name, node.arguments);
+        // TODO(johnniwinther): Avoid treating a known function call as a
+        // dynamic call when CFE provides a way to distinguish the two.
+        if (operatorFromString(node.name.name) == null &&
+            receiverType is ir.DynamicType) {
+          // We might implicitly call a getter that returns a function.
+          registerFunctionInvocation(const ir.DynamicType(), node.arguments);
+        }
+      } else {
+        if (interfaceTarget is ir.Field ||
+            interfaceTarget is ir.Procedure &&
+                interfaceTarget.kind == ir.ProcedureKind.Getter) {
+          registerInstanceInvocation(
+              receiverType, relation, interfaceTarget, node.arguments);
+          registerFunctionInvocation(
+              interfaceTarget.getterType, node.arguments);
+        } else {
+          registerInstanceInvocation(
+              receiverType, relation, interfaceTarget, node.arguments);
+        }
+      }
+    }
+  }
+
+  @override
+  void handleDirectMethodInvocation(
+      ir.DirectMethodInvocation node,
+      ir.DartType receiverType,
+      ArgumentTypes argumentTypes,
+      ir.DartType returnType) {
+    registerInstanceInvocation(
+        receiverType, ClassRelation.exact, node.target, node.arguments);
+  }
+
+  void registerDynamicGet(
+      ir.DartType receiverType, ClassRelation relation, ir.Name name);
+
+  void registerInstanceGet(
+      ir.DartType receiverType, ClassRelation relation, ir.Member target);
+
+  @override
+  void handlePropertyGet(
+      ir.PropertyGet node, ir.DartType receiverType, ir.DartType resultType) {
+    ClassRelation relation = node.receiver is ir.ThisExpression
+        ? ClassRelation.thisExpression
+        : ClassRelation.subtype;
+    if (node.interfaceTarget != null) {
+      registerInstanceGet(receiverType, relation, node.interfaceTarget);
+    } else {
+      registerDynamicGet(receiverType, relation, node.name);
+    }
+  }
+
+  @override
+  void handleDirectPropertyGet(ir.DirectPropertyGet node,
+      ir.DartType receiverType, ir.DartType resultType) {
+    registerInstanceGet(receiverType, ClassRelation.exact, node.target);
+  }
+
+  void registerDynamicSet(
+      ir.DartType receiverType, ClassRelation relation, ir.Name name);
+
+  void registerInstanceSet(
+      ir.DartType receiverType, ClassRelation relation, ir.Member target);
+
+  @override
+  void handlePropertySet(
+      ir.PropertySet node, ir.DartType receiverType, ir.DartType valueType) {
+    ClassRelation relation = node.receiver is ir.ThisExpression
+        ? ClassRelation.thisExpression
+        : ClassRelation.subtype;
+    if (node.interfaceTarget != null) {
+      registerInstanceSet(receiverType, relation, node.interfaceTarget);
+    } else {
+      registerDynamicSet(receiverType, relation, node.name);
+    }
+  }
+
+  @override
+  void handleDirectPropertySet(ir.DirectPropertySet node,
+      ir.DartType receiverType, ir.DartType valueType) {
+    registerInstanceSet(receiverType, ClassRelation.exact, node.target);
+  }
 }
diff --git a/pkg/compiler/lib/src/kernel/element_map.dart b/pkg/compiler/lib/src/kernel/element_map.dart
index c28d34c..b6fe609 100644
--- a/pkg/compiler/lib/src/kernel/element_map.dart
+++ b/pkg/compiler/lib/src/kernel/element_map.dart
@@ -60,6 +60,10 @@
   /// access of [node].
   Selector getSelector(ir.Expression node);
 
+  /// Returns the [Selector] corresponding to the invocation of [name] with
+  /// [arguments].
+  Selector getInvocationSelector(ir.Name name, ir.Arguments arguments);
+
   /// Returns the [MemberEntity] corresponding to the member [node].
   MemberEntity getMember(ir.Member node);
 
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index 1d77e51..14ad0e0 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -774,7 +774,7 @@
       return getSetterSelector(node.name);
     }
     if (node is ir.InvocationExpression) {
-      return getInvocationSelector(node);
+      return getInvocationSelector(node.name, node.arguments);
     }
     throw failedAt(
         CURRENT_ELEMENT_SPANNABLE,
@@ -782,8 +782,8 @@
         "${node}");
   }
 
-  Selector getInvocationSelector(ir.InvocationExpression invocation) {
-    Name name = getName(invocation.name);
+  Selector getInvocationSelector(ir.Name irName, ir.Arguments arguments) {
+    Name name = getName(irName);
     SelectorKind kind;
     if (Selector.isOperatorName(name.text)) {
       if (name == Names.INDEX_NAME || name == Names.INDEX_SET_NAME) {
@@ -795,7 +795,7 @@
       kind = SelectorKind.CALL;
     }
 
-    CallStructure callStructure = getCallStructure(invocation.arguments);
+    CallStructure callStructure = getCallStructure(arguments);
     return new Selector(kind, name, callStructure);
   }
 
diff --git a/pkg/compiler/lib/src/kernel/kernel_impact.dart b/pkg/compiler/lib/src/kernel/kernel_impact.dart
index de88b6f..e8f6ee9 100644
--- a/pkg/compiler/lib/src/kernel/kernel_impact.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_impact.dart
@@ -2,9 +2,6 @@
 // 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:front_end/src/api_unstable/dart2js.dart'
-    show operatorFromString;
-
 import 'package:kernel/ast.dart' as ir;
 
 import '../common.dart';
@@ -54,6 +51,19 @@
   bool get inferEffectivelyFinalVariableTypes =>
       !_annotations.contains(PragmaAnnotation.disableFinal);
 
+  Object _computeReceiverConstraint(
+      ir.DartType receiverType, ClassRelation relation) {
+    if (receiverType is ir.InterfaceType) {
+      if (receiverType.classNode == typeEnvironment.futureOrClass) {
+        // CFE encodes FutureOr as an interface type!
+        return null;
+      }
+      return new StrongModeConstraint(commonElements, _nativeBasicData,
+          elementMap.getClass(receiverType.classNode), relation);
+    }
+    return null;
+  }
+
   @override
   void registerParameterCheck(ir.DartType irType) {
     DartType type = elementMap.getDartType(irType);
@@ -351,26 +361,6 @@
   }
 
   @override
-  void handleDirectMethodInvocation(
-      ir.DirectMethodInvocation node,
-      ir.DartType receiverType,
-      ArgumentTypes argumentTypes,
-      ir.DartType returnType) {
-    List<DartType> typeArguments = _getTypeArguments(node.arguments);
-    MemberEntity member = elementMap.getMember(node.target);
-    // TODO(johnniwinther): Restrict the dynamic use to only match the known
-    // target.
-    // TODO(johnniwinther): Restrict this to subclasses?
-    Object constraint = new StrongModeConstraint(
-        commonElements, _nativeBasicData, member.enclosingClass);
-    impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(
-        new Selector.call(
-            member.memberName, elementMap.getCallStructure(node.arguments)),
-        constraint,
-        typeArguments));
-  }
-
-  @override
   void handleSuperMethodInvocation(ir.SuperMethodInvocation node,
       ArgumentTypes argumentTypes, ir.DartType returnType) {
     // TODO(johnniwinther): Should we support this or always use the
@@ -396,15 +386,6 @@
   }
 
   @override
-  void handleDirectPropertyGet(ir.DirectPropertyGet node,
-      ir.DartType receiverType, ir.DartType resultType) {
-    // TODO(johnniwinther): Restrict the dynamic use to only match the known
-    // target.
-    impactBuilder.registerDynamicUse(new DynamicUse(
-        new Selector.getter(elementMap.getMember(node.target).memberName)));
-  }
-
-  @override
   void handleSuperPropertyGet(
       ir.SuperPropertyGet node, ir.DartType resultType) {
     handleSuperGet(node.name, node.interfaceTarget);
@@ -428,107 +409,92 @@
   }
 
   @override
-  void handleDirectPropertySet(ir.DirectPropertySet node,
-      ir.DartType receiverType, ir.DartType valueType) {
-    // TODO(johnniwinther): Restrict the dynamic use to only match the known
-    // target.
-    impactBuilder.registerDynamicUse(new DynamicUse(
-        new Selector.setter(elementMap.getMember(node.target).memberName)));
-  }
-
-  @override
   void handleSuperPropertySet(ir.SuperPropertySet node, ir.DartType valueType) {
     handleSuperSet(node.name, node.interfaceTarget, node.value);
   }
 
   @override
-  void handleMethodInvocation(
-      ir.MethodInvocation node,
-      ir.DartType receiverType,
-      ArgumentTypes argumentTypes,
-      ir.DartType returnType) {
-    Selector selector = elementMap.getSelector(node);
-    List<DartType> typeArguments = _getTypeArguments(node.arguments);
-    ir.Expression receiver = node.receiver;
-    if (receiver is ir.VariableGet &&
-        receiver.variable.isFinal &&
-        receiver.variable.parent is ir.FunctionDeclaration) {
-      Local localFunction =
-          elementMap.getLocalFunction(receiver.variable.parent);
-      // Invocation of a local function. No need for dynamic use, but
-      // we need to track the type arguments.
-      impactBuilder.registerStaticUse(new StaticUse.closureCall(
-          localFunction, selector.callStructure, typeArguments));
-      // TODO(johnniwinther): Yet, alas, we need the dynamic use for now. Remove
-      // this when kernel adds an `isFunctionCall` flag to
-      // [ir.MethodInvocation].
-      impactBuilder.registerDynamicUse(
-          new ConstrainedDynamicUse(selector, null, typeArguments));
-    } else {
-      ClassRelation relation = receiver is ir.ThisExpression
-          ? ClassRelation.thisExpression
-          : ClassRelation.subtype;
-      DartType receiverDartType = elementMap.getDartType(receiverType);
-      Object constraint;
-      if (receiverDartType is InterfaceType) {
-        constraint = new StrongModeConstraint(commonElements, _nativeBasicData,
-            receiverDartType.element, relation);
-      }
-      ir.Member interfaceTarget = node.interfaceTarget;
-      if (interfaceTarget == null) {
-        // TODO(johnniwinther): Avoid treating a known function call as a
-        // dynamic call when CFE provides a way to distinguish the two.
-        impactBuilder.registerDynamicUse(
-            new ConstrainedDynamicUse(selector, constraint, typeArguments));
-        if (operatorFromString(node.name.name) == null &&
-            receiverDartType.isDynamic) {
-          // We might implicitly call a getter that returns a function.
-          impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(
-              selector.toCallSelector(), null, typeArguments));
-        }
-      } else {
-        if (interfaceTarget is ir.Field ||
-            interfaceTarget is ir.Procedure &&
-                interfaceTarget.kind == ir.ProcedureKind.Getter) {
-          impactBuilder.registerDynamicUse(
-              new ConstrainedDynamicUse(selector, constraint, typeArguments));
-          // An `o.foo()` invocation is (potentially) an `o.foo.call()`
-          // invocation.
-          Object getterConstraint;
-          if (interfaceTarget != null) {
-            DartType receiverType =
-                elementMap.getDartType(interfaceTarget.getterType);
-            if (receiverType is InterfaceType) {
-              getterConstraint = new StrongModeConstraint(
-                  commonElements, _nativeBasicData, receiverType.element);
-            }
-          }
-
-          impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(
-              selector.toCallSelector(), getterConstraint, typeArguments));
-        } else {
-          impactBuilder.registerDynamicUse(
-              new ConstrainedDynamicUse(selector, constraint, typeArguments));
-        }
-      }
-    }
+  void registerLocalFunctionInvocation(
+      ir.FunctionDeclaration localFunction, ir.Arguments arguments) {
+    CallStructure callStructure = elementMap.getCallStructure(arguments);
+    List<DartType> typeArguments = _getTypeArguments(arguments);
+    // Invocation of a local function. No need for dynamic use, but
+    // we need to track the type arguments.
+    impactBuilder.registerStaticUse(new StaticUse.closureCall(
+        elementMap.getLocalFunction(localFunction),
+        callStructure,
+        typeArguments));
+    // TODO(johnniwinther): Yet, alas, we need the dynamic use for now. Remove
+    // this when kernel adds an `isFunctionCall` flag to
+    // [ir.MethodInvocation].
+    impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(
+        callStructure.callSelector, null, typeArguments));
   }
 
   @override
-  void handlePropertyGet(
-      ir.PropertyGet node, ir.DartType receiverType, ir.DartType resultType) {
-    Object constraint;
-    DartType receiverDartType = elementMap.getDartType(receiverType);
-    if (receiverDartType is InterfaceType) {
-      ClassRelation relation = node.receiver is ir.ThisExpression
-          ? ClassRelation.thisExpression
-          : ClassRelation.subtype;
-      constraint = new StrongModeConstraint(
-          commonElements, _nativeBasicData, receiverDartType.element, relation);
-    }
+  void registerDynamicInvocation(ir.DartType receiverType,
+      ClassRelation relation, ir.Name name, ir.Arguments arguments) {
+    Selector selector = elementMap.getInvocationSelector(name, arguments);
+    List<DartType> typeArguments = _getTypeArguments(arguments);
+    impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(selector,
+        _computeReceiverConstraint(receiverType, relation), typeArguments));
+  }
+
+  @override
+  void registerFunctionInvocation(
+      ir.DartType receiverType, ir.Arguments arguments) {
+    CallStructure callStructure = elementMap.getCallStructure(arguments);
+    List<DartType> typeArguments = _getTypeArguments(arguments);
     impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(
-        new Selector.getter(elementMap.getName(node.name)),
-        constraint, const <DartType>[]));
+        callStructure.callSelector,
+        _computeReceiverConstraint(receiverType, ClassRelation.subtype),
+        typeArguments));
+  }
+
+  @override
+  void registerInstanceInvocation(ir.DartType receiverType,
+      ClassRelation relation, ir.Member target, ir.Arguments arguments) {
+    List<DartType> typeArguments = _getTypeArguments(arguments);
+    impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(
+        elementMap.getInvocationSelector(target.name, arguments),
+        _computeReceiverConstraint(receiverType, relation),
+        typeArguments));
+  }
+
+  @override
+  void registerDynamicGet(
+      ir.DartType receiverType, ClassRelation relation, ir.Name name) {
+    impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(
+        new Selector.getter(elementMap.getName(name)),
+        _computeReceiverConstraint(receiverType, relation),
+        const <DartType>[]));
+  }
+
+  @override
+  void registerInstanceGet(
+      ir.DartType receiverType, ClassRelation relation, ir.Member target) {
+    impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(
+        new Selector.getter(elementMap.getName(target.name)),
+        _computeReceiverConstraint(receiverType, relation),
+        const <DartType>[]));
+  }
+
+  @override
+  void registerDynamicSet(
+      ir.DartType receiverType, ClassRelation relation, ir.Name name) {
+    impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(
+        new Selector.setter(elementMap.getName(name)),
+        _computeReceiverConstraint(receiverType, relation),
+        const <DartType>[]));
+  }
+
+  @override
+  void registerInstanceSet(
+      ir.DartType receiverType, ClassRelation relation, ir.Member target) {
+    impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(
+        new Selector.setter(elementMap.getName(target.name)),
+        _computeReceiverConstraint(receiverType, relation),
+        const <DartType>[]));
   }
 
   void handleRuntimeTypeUse(ir.PropertyGet node, RuntimeTypeUseKind kind,
@@ -562,23 +528,6 @@
   }
 
   @override
-  void handlePropertySet(
-      ir.PropertySet node, ir.DartType receiverType, ir.DartType valueType) {
-    Object constraint;
-    DartType receiverDartType = elementMap.getDartType(receiverType);
-    if (receiverDartType is InterfaceType) {
-      ClassRelation relation = node.receiver is ir.ThisExpression
-          ? ClassRelation.thisExpression
-          : ClassRelation.subtype;
-      constraint = new StrongModeConstraint(
-          commonElements, _nativeBasicData, receiverDartType.element, relation);
-    }
-    impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(
-        new Selector.setter(elementMap.getName(node.name)),
-        constraint, const <DartType>[]));
-  }
-
-  @override
   void registerAssert({bool withMessage}) {
     impactBuilder.registerFeature(
         withMessage ? Feature.ASSERT_WITH_MESSAGE : Feature.ASSERT);
diff --git a/tests/compiler/dart2js/impact/data/future_or.dart b/tests/compiler/dart2js/impact/data/future_or.dart
new file mode 100644
index 0000000..c119fbe
--- /dev/null
+++ b/tests/compiler/dart2js/impact/data/future_or.dart
@@ -0,0 +1,17 @@
+// 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.
+
+import "dart:async";
+
+/*element: main:
+ dynamic=[runtimeType],
+ runtimeType=[unknown:FutureOr<int>],
+ static=[Future.value(1),assertIsSubtype,print(1),throwTypeError],
+ type=[inst:JSDouble,inst:JSInt,inst:JSNumber,inst:JSPositiveInt,inst:JSUInt31,inst:JSUInt32]
+*/
+@pragma('dart2js:disableFinal')
+void main() {
+  FutureOr<int> i = new Future<int>.value(0);
+  print(i.runtimeType);
+}
diff --git a/tests/compiler/dart2js_extra/35341_test.dart b/tests/compiler/dart2js_extra/35341_test.dart
index 027182f..80310f0 100644
--- a/tests/compiler/dart2js_extra/35341_test.dart
+++ b/tests/compiler/dart2js_extra/35341_test.dart
@@ -8,7 +8,6 @@
 
 @pragma('dart2js:disableFinal')
 void main() {
-  FutureOr<int> i = 0;
-  i = new Future<int>.value(0);
+  FutureOr<int> i = new Future<int>.value(0);
   print(i.runtimeType);
 }