Fix receiver type for js-interop access
This includes the unknown potential targets of access to js-interop
members. Since we don't know actual classes implementing the js-interop
classes we just assume it could be any of them.
Change-Id: I4d91ab673fa8221eb701b34e9c32fd16e5a1c381
Reviewed-on: https://dart-review.googlesource.com/74980
Reviewed-by: Stephen Adams <sra@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/ssa/kernel_impact.dart b/pkg/compiler/lib/src/ssa/kernel_impact.dart
index a1d6c47..c88565b 100644
--- a/pkg/compiler/lib/src/ssa/kernel_impact.dart
+++ b/pkg/compiler/lib/src/ssa/kernel_impact.dart
@@ -14,6 +14,7 @@
import '../elements/entities.dart';
import '../elements/types.dart';
import '../ir/util.dart';
+import '../js_backend/native_data.dart';
import '../kernel/element_map.dart';
import '../kernel/runtime_type_analysis.dart';
import '../options.dart';
@@ -58,6 +59,8 @@
CommonElements get commonElements => elementMap.commonElements;
+ NativeBasicData get _nativeBasicData => elementMap.nativeBasicData;
+
/// Add a checked-mode type use of [type] if it is not `dynamic`.
DartType checkType(ir.DartType irType, TypeUseKind kind) {
DartType type = elementMap.getDartType(irType);
@@ -117,7 +120,7 @@
if (field.isInstanceMember &&
elementMap.isNativeClass(field.enclosingClass)) {
MemberEntity member = elementMap.getMember(field);
- bool isJsInterop = elementMap.nativeBasicData.isJsInteropMember(member);
+ bool isJsInterop = _nativeBasicData.isJsInteropMember(member);
impactBuilder.registerNativeData(elementMap
.getNativeBehaviorForFieldLoad(field, isJsInterop: isJsInterop));
impactBuilder
@@ -132,7 +135,7 @@
visitNode(constructor.function.body);
MemberEntity member = elementMap.getMember(constructor);
if (constructor.isExternal && !commonElements.isForeignHelper(member)) {
- bool isJsInterop = elementMap.nativeBasicData.isJsInteropMember(member);
+ bool isJsInterop = _nativeBasicData.isJsInteropMember(member);
impactBuilder.registerNativeData(elementMap
.getNativeBehaviorForMethod(constructor, isJsInterop: isJsInterop));
}
@@ -187,7 +190,7 @@
handleAsyncMarker(procedure.function);
MemberEntity member = elementMap.getMember(procedure);
if (procedure.isExternal && !commonElements.isForeignHelper(member)) {
- bool isJsInterop = elementMap.nativeBasicData.isJsInteropMember(member);
+ bool isJsInterop = _nativeBasicData.isJsInteropMember(member);
impactBuilder.registerNativeData(elementMap
.getNativeBehaviorForMethod(procedure, isJsInterop: isJsInterop));
}
@@ -493,7 +496,8 @@
// TODO(johnniwinther): Restrict the dynamic use to only match the known
// target.
// TODO(johnniwinther): Restrict this to subclasses?
- Object constraint = new StrongModeConstraint(member.enclosingClass);
+ Object constraint = new StrongModeConstraint(
+ commonElements, _nativeBasicData, member.enclosingClass);
impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(
new Selector.call(
member.memberName, elementMap.getCallStructure(node.arguments)),
@@ -606,7 +610,8 @@
Object constraint;
DartType receiverType = elementMap.getStaticType(node.receiver);
if (receiverType is InterfaceType) {
- constraint = new StrongModeConstraint(receiverType.element);
+ constraint = new StrongModeConstraint(
+ commonElements, _nativeBasicData, receiverType.element);
}
if (interfaceTarget is ir.Field ||
@@ -621,7 +626,8 @@
DartType receiverType =
elementMap.getDartType(interfaceTarget.getterType);
if (receiverType is InterfaceType) {
- getterConstraint = new StrongModeConstraint(receiverType.element);
+ getterConstraint = new StrongModeConstraint(
+ commonElements, _nativeBasicData, receiverType.element);
}
}
@@ -641,7 +647,8 @@
Object constraint;
DartType receiverType = elementMap.getStaticType(node.receiver);
if (receiverType is InterfaceType) {
- constraint = new StrongModeConstraint(receiverType.element);
+ constraint = new StrongModeConstraint(
+ commonElements, _nativeBasicData, receiverType.element);
}
impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(
new Selector.getter(elementMap.getName(node.name)),
@@ -680,7 +687,8 @@
Object constraint;
DartType receiverType = elementMap.getStaticType(node.receiver);
if (receiverType is InterfaceType) {
- constraint = new StrongModeConstraint(receiverType.element);
+ constraint = new StrongModeConstraint(
+ commonElements, _nativeBasicData, receiverType.element);
}
impactBuilder.registerDynamicUse(new ConstrainedDynamicUse(
new Selector.setter(elementMap.getName(node.name)),
diff --git a/pkg/compiler/lib/src/universe/world_builder.dart b/pkg/compiler/lib/src/universe/world_builder.dart
index 03f3fcb..5107b28 100644
--- a/pkg/compiler/lib/src/universe/world_builder.dart
+++ b/pkg/compiler/lib/src/universe/world_builder.dart
@@ -192,7 +192,17 @@
class StrongModeConstraint {
final ClassEntity cls;
- const StrongModeConstraint(this.cls);
+ factory StrongModeConstraint(CommonElements commonElements,
+ NativeBasicData nativeBasicData, ClassEntity cls) {
+ if (nativeBasicData.isJsInteropClass(cls)) {
+ // We can not tell js-interop classes apart, so we just assume the
+ // receiver could be any js-interop class.
+ cls = commonElements.jsJavaScriptObjectClass;
+ }
+ return new StrongModeConstraint.internal(cls);
+ }
+
+ const StrongModeConstraint.internal(this.cls);
bool needsNoSuchMethodHandling(Selector selector, World world) => true;
diff --git a/tests/compiler/dart2js/impact/data/jsinterop.dart b/tests/compiler/dart2js/impact/data/jsinterop.dart
index a2db31e..b36d583 100644
--- a/tests/compiler/dart2js/impact/data/jsinterop.dart
+++ b/tests/compiler/dart2js/impact/data/jsinterop.dart
@@ -44,7 +44,7 @@
}
/*strong.element: testJsInteropClass:
- dynamic=[JsInteropClass.method(0)],
+ dynamic=[JavaScriptObject.method(0)],
static=[JsInteropClass.(0)]
*/
testJsInteropClass() => new JsInteropClass().method();
@@ -76,7 +76,7 @@
}
/*strong.element: testOptionalGenericFunctionTypeArgument:
- dynamic=[GenericClass.method(0)],
+ dynamic=[JavaScriptObject.method(0)],
static=[GenericClass.(0)]
*/
testOptionalGenericFunctionTypeArgument() => new GenericClass().method();
diff --git a/tests/compiler/dart2js/model/enqueuer_test.dart b/tests/compiler/dart2js/model/enqueuer_test.dart
index 56d652d..60c3fe6 100644
--- a/tests/compiler/dart2js/model/enqueuer_test.dart
+++ b/tests/compiler/dart2js/model/enqueuer_test.dart
@@ -250,7 +250,8 @@
checkInvariant(enqueuer, elementEnvironment);
Object createConstraint(ClassEntity cls) {
- return new StrongModeConstraint(cls);
+ return new StrongModeConstraint(compiler.frontendStrategy.commonElements,
+ compiler.frontendStrategy.nativeBasicData, cls);
}
for (Impact impact in impacts) {
diff --git a/tests/compiler/dart2js_extra/js_interop_implements_test.dart b/tests/compiler/dart2js_extra/js_interop_implements_test.dart
new file mode 100644
index 0000000..a4c8a72
--- /dev/null
+++ b/tests/compiler/dart2js_extra/js_interop_implements_test.dart
@@ -0,0 +1,37 @@
+// 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.
+
+// Test that methods implemented (not extended) in js-interop classes are still
+// considered live.
+
+@JS()
+library anonymous_js_test;
+
+import 'package:js/js.dart';
+
+@JS()
+@anonymous
+abstract class A {
+ external factory A();
+ external String get a;
+ external set a(String a);
+}
+
+@JS()
+@anonymous
+abstract class B implements A {
+ external factory B();
+ external String get b;
+ external set b(String b);
+}
+
+void main() {
+ final b = B();
+ // This setter is missing if we don't assume the receiver could be an
+ // unknown but concrete implementation of A.
+ b.a = 'Hi';
+ b.b = 'Hello';
+ if (b.a != 'Hi') throw 'b.a';
+ if (b.b != 'Hello') throw 'b.b';
+}