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