Support noSuchMethod forwarders in dart2js
Closes #32750
Change-Id: Ib2f358b14ae5a079e3df8413013ecfc006f0e69b
Reviewed-on: https://dart-review.googlesource.com/60250
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/common_elements.dart b/pkg/compiler/lib/src/common_elements.dart
index 738bd388..c54b666 100644
--- a/pkg/compiler/lib/src/common_elements.dart
+++ b/pkg/compiler/lib/src/common_elements.dart
@@ -1091,6 +1091,15 @@
FunctionEntity get createInvocationMirror =>
_findHelperFunction('createInvocationMirror');
+ bool isCreateInvocationMirrorHelper(MemberEntity member) {
+ return member.isTopLevel &&
+ member.name == '_createInvocationMirror' &&
+ member.library == coreLibrary;
+ }
+
+ FunctionEntity get createUnmangledInvocationMirror =>
+ _findHelperFunction('createUnmangledInvocationMirror');
+
FunctionEntity get cyclicThrowHelper =>
_findHelperFunction("throwCyclicInit");
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index 6b6934a..a8164c0 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -409,7 +409,8 @@
compiler.backendStrategy.sourceInformationStrategy,
constantCompilerTask = new JavaScriptConstantTask(compiler) {
CommonElements commonElements = compiler.frontendStrategy.commonElements;
- _backendUsageBuilder = new BackendUsageBuilderImpl(commonElements);
+ _backendUsageBuilder =
+ new BackendUsageBuilderImpl(compiler.frontendStrategy);
_checkedModeHelpers = new CheckedModeHelpers();
emitter =
new CodeEmitterTask(compiler, generateSourceMap, useStartupEmitter);
diff --git a/pkg/compiler/lib/src/js_backend/backend_impact.dart b/pkg/compiler/lib/src/js_backend/backend_impact.dart
index 90bc21f..9adf701 100644
--- a/pkg/compiler/lib/src/js_backend/backend_impact.dart
+++ b/pkg/compiler/lib/src/js_backend/backend_impact.dart
@@ -720,9 +720,12 @@
BackendImpact _noSuchMethodSupport;
BackendImpact get noSuchMethodSupport {
- return _noSuchMethodSupport ??= new BackendImpact(
- staticUses: [_commonElements.createInvocationMirror],
- dynamicUses: [Selectors.noSuchMethod_]);
+ return _noSuchMethodSupport ??= new BackendImpact(staticUses: [
+ _commonElements.createInvocationMirror,
+ _commonElements.createUnmangledInvocationMirror
+ ], dynamicUses: [
+ Selectors.noSuchMethod_
+ ]);
}
BackendImpact _loadLibrary;
diff --git a/pkg/compiler/lib/src/js_backend/backend_usage.dart b/pkg/compiler/lib/src/js_backend/backend_usage.dart
index cce6611..3b7503c 100644
--- a/pkg/compiler/lib/src/js_backend/backend_usage.dart
+++ b/pkg/compiler/lib/src/js_backend/backend_usage.dart
@@ -6,6 +6,7 @@
import '../common_elements.dart';
import '../elements/entities.dart';
import '../elements/types.dart';
+import '../frontend_strategy.dart';
import '../universe/feature.dart';
import '../util/util.dart' show Setlet;
import 'backend_impact.dart';
@@ -87,7 +88,7 @@
}
class BackendUsageBuilderImpl implements BackendUsageBuilder {
- final CommonElements _commonElements;
+ FrontendStrategy _frontendStrategy;
// TODO(johnniwinther): Remove the need for these.
Setlet<FunctionEntity> _globalFunctionDependencies;
Setlet<ClassEntity> _globalClassDependencies;
@@ -118,43 +119,34 @@
/// `true` if `noSuchMethod` is used.
bool isNoSuchMethodUsed = false;
- BackendUsageBuilderImpl(this._commonElements);
+ BackendUsageBuilderImpl(this._frontendStrategy);
+
+ CommonElements get _commonElements => _frontendStrategy.commonElements;
@override
void registerBackendFunctionUse(FunctionEntity element) {
- assert(_isValidBackendUse(element),
+ assert(_isValidBackendUse(element, element.library),
failedAt(element, "Backend use of $element is not allowed."));
_helperFunctionsUsed.add(element);
}
@override
void registerBackendClassUse(ClassEntity element) {
- assert(_isValidBackendUse(element),
+ assert(_isValidBackendUse(element, element.library),
failedAt(element, "Backend use of $element is not allowed."));
_helperClassesUsed.add(element);
}
- bool _isValidBackendUse(Entity element) {
+ bool _isValidBackendUse(Entity element, LibraryEntity library) {
if (_isValidEntity(element)) return true;
- // TODO(redemption): Support these checks on kernel based elements:
- /*if (element is Element) {
- assert(element.isDeclaration,
- failedAt(element, "Backend use $element must be the declaration."));
- if (element.implementationLibrary.isPatch ||
- // Needed to detect deserialized injected elements, that is
- // element declared in patch files.
- (element.library.isPlatformLibrary &&
- element.sourcePosition.uri.path
- .contains('_internal/js_runtime/lib/')) ||
- element.library == _commonElements.jsHelperLibrary ||
- element.library == _commonElements.interceptorsLibrary) {
- // TODO(johnniwinther): We should be more precise about these.
- return true;
- } else {
- return false;
- }
- }*/
- return true;
+ SourceSpan span = _frontendStrategy.spanFromSpannable(element, element);
+ if (library.canonicalUri.scheme == 'dart' &&
+ span.uri.path.contains('_internal/js_runtime/lib/')) {
+ // TODO(johnniwinther): We should be more precise about these.
+ return true;
+ } else {
+ return false;
+ }
}
bool _isValidEntity(Entity element) {
diff --git a/pkg/compiler/lib/src/js_backend/resolution_listener.dart b/pkg/compiler/lib/src/js_backend/resolution_listener.dart
index 131da5b..c1b2cac 100644
--- a/pkg/compiler/lib/src/js_backend/resolution_listener.dart
+++ b/pkg/compiler/lib/src/js_backend/resolution_listener.dart
@@ -285,6 +285,10 @@
}
_backendUsage.registerUsedMember(member);
+ if (_commonElements.isCreateInvocationMirrorHelper(member)) {
+ _registerBackendImpact(worldImpact, _impacts.noSuchMethodSupport);
+ }
+
if (_elementEnvironment.isDeferredLoadLibraryGetter(member)) {
// TODO(sigurdm): Create a function registerLoadLibraryAccess.
if (!_isLoadLibraryFunctionResolved) {
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 462ba79..71a8c15 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -4,12 +4,13 @@
library compiler.src.kernel.dart2js_target;
-import 'package:kernel/kernel.dart';
+import 'package:kernel/ast.dart' as ir;
import 'package:kernel/core_types.dart';
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/target/targets.dart';
import '../native/native.dart' show maybeEnableNative;
+import '../universe/selector.dart';
/// A kernel [Target] to configure the Dart Front End for dart2js.
class Dart2jsTarget extends Target {
@@ -20,6 +21,8 @@
bool get strongMode => flags.strongMode;
+ bool get enableNoSuchMethodForwarders => flags.strongMode;
+
List<String> get extraRequiredLibraries => _requiredLibraries[name];
@override
@@ -40,23 +43,56 @@
@override
void performModularTransformationsOnLibraries(
- CoreTypes coreTypes, ClassHierarchy hierarchy, List<Library> libraries,
+ CoreTypes coreTypes, ClassHierarchy hierarchy, List<ir.Library> libraries,
{void logger(String msg)}) {}
@override
- void performGlobalTransformations(CoreTypes coreTypes, Component component,
+ void performGlobalTransformations(CoreTypes coreTypes, ir.Component component,
{void logger(String msg)}) {}
@override
- Expression instantiateInvocation(CoreTypes coreTypes, Expression receiver,
- String name, Arguments arguments, int offset, bool isSuper) {
- // TODO(sigmund): implement;
- return new InvalidExpression(null);
+ ir.Expression instantiateInvocation(
+ CoreTypes coreTypes,
+ ir.Expression receiver,
+ String name,
+ ir.Arguments arguments,
+ int offset,
+ bool isSuper) {
+ int kind;
+ if (name.startsWith('get:')) {
+ kind = Selector.invocationMirrorGetterKind;
+ name = name.substring(4);
+ } else if (name.startsWith('set:')) {
+ kind = Selector.invocationMirrorSetterKind;
+ name = name.substring(4);
+ } else {
+ kind = Selector.invocationMirrorMethodKind;
+ }
+ return new ir.StaticInvocation(
+ coreTypes.index
+ .getTopLevelMember('dart:core', '_createInvocationMirror'),
+ new ir.Arguments(<ir.Expression>[
+ new ir.StringLiteral(name)..fileOffset = offset,
+ new ir.ListLiteral(
+ arguments.types.map((t) => new ir.TypeLiteral(t)).toList()),
+ new ir.ListLiteral(arguments.positional)..fileOffset = offset,
+ new ir.MapLiteral(new List<ir.MapEntry>.from(
+ arguments.named.map((ir.NamedExpression arg) {
+ return new ir.MapEntry(
+ new ir.StringLiteral(arg.name)..fileOffset = arg.fileOffset,
+ arg.value)
+ ..fileOffset = arg.fileOffset;
+ })), keyType: coreTypes.stringClass.rawType)
+ ..isConst = (arguments.named.length == 0)
+ ..fileOffset = arguments.fileOffset,
+ new ir.IntLiteral(kind)..fileOffset = offset,
+ ]))
+ ..fileOffset = offset;
}
@override
- Expression instantiateNoSuchMethodError(CoreTypes coreTypes,
- Expression receiver, String name, Arguments arguments, int offset,
+ ir.Expression instantiateNoSuchMethodError(CoreTypes coreTypes,
+ ir.Expression receiver, String name, ir.Arguments arguments, int offset,
{bool isMethod: false,
bool isGetter: false,
bool isSetter: false,
@@ -68,7 +104,7 @@
bool isConstructor: false,
bool isTopLevel: false}) {
// TODO(sigmund): implement;
- return new InvalidExpression(null);
+ return new ir.InvalidExpression(null);
}
}
diff --git a/pkg/compiler/lib/src/kernel/element_map.dart b/pkg/compiler/lib/src/kernel/element_map.dart
index 99f481e..7be52d1 100644
--- a/pkg/compiler/lib/src/kernel/element_map.dart
+++ b/pkg/compiler/lib/src/kernel/element_map.dart
@@ -93,8 +93,11 @@
/// Returns the [Name] corresponding to [name].
Name getName(ir.Name name);
- /// Return `true` if [node] is the `dart:_foreign_helper` library.
- bool isForeignLibrary(ir.Library node);
+ /// Return `true` if [member] is a "foreign helper", that is, a member whose
+ /// semantics is defined synthetically and not through Dart code.
+ ///
+ /// Most foreign helpers are located in the `dart:_foreign_helper` library.
+ bool isForeignHelper(MemberEntity member);
/// Computes the [native.NativeBehavior] for a call to the [JS] function.
native.NativeBehavior getNativeBehaviorForJsCall(ir.StaticInvocation node);
diff --git a/pkg/compiler/lib/src/kernel/element_map_mixins.dart b/pkg/compiler/lib/src/kernel/element_map_mixins.dart
index d8c74cc..88c8a8c 100644
--- a/pkg/compiler/lib/src/kernel/element_map_mixins.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_mixins.dart
@@ -104,9 +104,13 @@
return new Selector.setter(name);
}
- /// Return `true` if [node] is the `dart:_foreign_helper` library.
- bool isForeignLibrary(ir.Library node) {
- return node.importUri == Uris.dart__foreign_helper;
+ /// Return `true` if [member] is a "foreign helper", that is, a member whose
+ /// semantics is defined synthetically and not through Dart code.
+ ///
+ /// Most foreign helpers are located in the `dart:_foreign_helper` library.
+ bool isForeignHelper(MemberEntity member) {
+ return member.library == commonElements.foreignLibrary ||
+ commonElements.isCreateInvocationMirrorHelper(member);
}
/// Looks up [typeName] for use in the spec-string of a `JS` call.
@@ -387,7 +391,7 @@
/// Compute the kind of foreign helper function called by [node], if any.
ForeignKind getForeignKind(ir.StaticInvocation node) {
- if (isForeignLibrary(node.target.enclosingLibrary)) {
+ if (isForeignHelper(getMember(node.target))) {
switch (node.target.name.name) {
case JavaScriptBackend.JS:
return ForeignKind.JS;
diff --git a/pkg/compiler/lib/src/kernel/env.dart b/pkg/compiler/lib/src/kernel/env.dart
index dcef78f..ec0511b 100644
--- a/pkg/compiler/lib/src/kernel/env.dart
+++ b/pkg/compiler/lib/src/kernel/env.dart
@@ -373,7 +373,8 @@
}
}
- void addProcedures(ir.Class c, {bool includeStatic}) {
+ void addProcedures(ir.Class c,
+ {bool includeStatic, bool includeNoSuchMethodForwarders}) {
for (ir.Procedure member in c.procedures) {
if (member.isForwardingStub && member.isAbstract) {
// Skip abstract forwarding stubs. These are never emitted but they
@@ -391,6 +392,9 @@
continue;
}
if (!includeStatic && member.isStatic) continue;
+ if (!includeNoSuchMethodForwarders && member.isNoSuchMethodForwarder) {
+ continue;
+ }
var name = member.name.name;
assert(!name.contains('#'));
if (member.kind == ir.ProcedureKind.Factory) {
@@ -424,13 +428,15 @@
if (cls.mixedInClass != null) {
elementMap.ensureClassMembers(cls.mixedInClass);
addFields(cls.mixedInClass.mixin, includeStatic: false);
- addProcedures(cls.mixedInClass.mixin, includeStatic: false);
+ addProcedures(cls.mixedInClass.mixin,
+ includeStatic: false, includeNoSuchMethodForwarders: false);
mergeSort(members, compare: orderByFileOffset);
mixinMemberCount = members.length;
}
addFields(cls, includeStatic: true);
addConstructors(cls);
- addProcedures(cls, includeStatic: true);
+ addProcedures(cls,
+ includeStatic: true, includeNoSuchMethodForwarders: true);
if (isUnnamedMixinApplication && _constructorMap.isEmpty) {
// Ensure that constructors are created for the superclass in case it
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 2ad67c8..fb04063 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -19,6 +19,7 @@
import '../dump_info.dart';
import '../elements/entities.dart';
import '../elements/jumps.dart';
+import '../elements/names.dart';
import '../elements/types.dart';
import '../io/source_information.dart';
import '../js/js.dart' as js;
@@ -3280,11 +3281,11 @@
ir.Procedure target = node.target;
SourceInformation sourceInformation =
_sourceInformationBuilder.buildCall(node, node);
- if (_elementMap.isForeignLibrary(target.enclosingLibrary)) {
- handleInvokeStaticForeign(node, target);
+ FunctionEntity function = _elementMap.getMember(target);
+ if (_elementMap.isForeignHelper(function)) {
+ handleInvokeStaticForeign(node, function);
return;
}
- FunctionEntity function = _elementMap.getMember(target);
if (options.strongMode &&
function == _commonElements.extractTypeArguments &&
@@ -3558,8 +3559,8 @@
}
void handleInvokeStaticForeign(
- ir.StaticInvocation invocation, ir.Procedure target) {
- String name = target.name.name;
+ ir.StaticInvocation invocation, MemberEntity member) {
+ String name = member.name;
if (name == 'JS') {
handleForeignJs(invocation);
} else if (name == 'DART_CLOSURE_TO_JS') {
@@ -3584,6 +3585,8 @@
handleJsInterceptorConstant(invocation);
} else if (name == 'JS_STRING_CONCAT') {
handleJsStringConcat(invocation);
+ } else if (name == '_createInvocationMirror') {
+ _handleCreateInvocationMirror(invocation);
} else {
reporter.internalError(
_elementMap.getSpannable(targetElement, invocation),
@@ -3591,6 +3594,114 @@
}
}
+ void _handleCreateInvocationMirror(ir.StaticInvocation invocation) {
+ ir.StringLiteral nameLiteral = invocation.arguments.positional[0];
+ String name = nameLiteral.value;
+
+ ir.ListLiteral typeArgumentsLiteral = invocation.arguments.positional[1];
+ List<DartType> typeArguments =
+ typeArgumentsLiteral.expressions.map((ir.Expression expression) {
+ ir.TypeLiteral typeLiteral = expression;
+ return _elementMap.getDartType(typeLiteral.type);
+ }).toList();
+
+ ir.ListLiteral positionalArgumentsLiteral =
+ invocation.arguments.positional[2];
+ ir.MapLiteral namedArgumentsLiteral = invocation.arguments.positional[3];
+ ir.IntLiteral kindLiteral = invocation.arguments.positional[4];
+
+ Name memberName = new Name(name, _currentFrame.member.library);
+ Selector selector;
+ switch (kindLiteral.value) {
+ case Selector.invocationMirrorGetterKind:
+ selector = new Selector.getter(memberName);
+ break;
+ case Selector.invocationMirrorSetterKind:
+ selector = new Selector.setter(memberName);
+ break;
+ case Selector.invocationMirrorMethodKind:
+ if (memberName == Names.INDEX_NAME) {
+ selector = new Selector.index();
+ } else if (memberName == Names.INDEX_SET_NAME) {
+ selector = new Selector.indexSet();
+ } else {
+ CallStructure callStructure = new CallStructure(
+ positionalArgumentsLiteral.expressions.length,
+ namedArgumentsLiteral.entries.map<String>((ir.MapEntry entry) {
+ ir.StringLiteral key = entry.key;
+ return key.value;
+ }).toList(),
+ typeArguments.length);
+ if (Selector.isOperatorName(name)) {
+ selector =
+ new Selector(SelectorKind.OPERATOR, memberName, callStructure);
+ } else {
+ selector = new Selector.call(memberName, callStructure);
+ }
+ }
+ break;
+ }
+
+ HConstant nameConstant = graph.addConstant(
+ closedWorld.constantSystem
+ .createSymbol(closedWorld.commonElements, name),
+ closedWorld);
+
+ List<HInstruction> arguments = <HInstruction>[];
+ for (ir.Expression argument in positionalArgumentsLiteral.expressions) {
+ argument.accept(this);
+ arguments.add(pop());
+ }
+ if (namedArgumentsLiteral.entries.isNotEmpty) {
+ Map<String, HInstruction> namedValues = <String, HInstruction>{};
+ for (ir.MapEntry entry in namedArgumentsLiteral.entries) {
+ ir.StringLiteral key = entry.key;
+ String name = key.value;
+ entry.value.accept(this);
+ namedValues[name] = pop();
+ }
+ for (String name in selector.callStructure.getOrderedNamedArguments()) {
+ arguments.add(namedValues[name]);
+ }
+ }
+
+ _addTypeArguments(arguments, typeArguments,
+ _sourceInformationBuilder.buildCall(invocation, invocation));
+
+ HInstruction argumentsInstruction = buildLiteralList(arguments);
+ add(argumentsInstruction);
+
+ List<HInstruction> argumentNames = <HInstruction>[];
+ for (String argumentName in selector.namedArguments) {
+ ConstantValue argumentNameConstant =
+ constantSystem.createString(argumentName);
+ argumentNames.add(graph.addConstant(argumentNameConstant, closedWorld));
+ }
+ HInstruction argumentNamesInstruction = buildLiteralList(argumentNames);
+ add(argumentNamesInstruction);
+
+ HInstruction typeArgumentCount =
+ graph.addConstantInt(typeArguments.length, closedWorld);
+
+ js.Name internalName = namer.invocationName(selector);
+
+ ConstantValue kindConstant =
+ constantSystem.createIntFromInt(selector.invocationMirrorKind);
+
+ _pushStaticInvocation(
+ _commonElements.createUnmangledInvocationMirror,
+ [
+ nameConstant,
+ graph.addConstantStringFromName(internalName, closedWorld),
+ graph.addConstant(kindConstant, closedWorld),
+ argumentsInstruction,
+ argumentNamesInstruction,
+ typeArgumentCount,
+ ],
+ abstractValueDomain.dynamicType,
+ const <DartType>[]);
+ }
+
bool _unexpectedForeignArguments(ir.StaticInvocation invocation,
{int minPositional, int maxPositional, int typeArgumentCount = 0}) {
String pluralizeArguments(int count, [String adjective = '']) {
diff --git a/pkg/compiler/lib/src/ssa/kernel_impact.dart b/pkg/compiler/lib/src/ssa/kernel_impact.dart
index 8e62ae0..b5cd0b2 100644
--- a/pkg/compiler/lib/src/ssa/kernel_impact.dart
+++ b/pkg/compiler/lib/src/ssa/kernel_impact.dart
@@ -142,9 +142,8 @@
handleSignature(constructor.function, checkReturnType: false);
visitNodes(constructor.initializers);
visitNode(constructor.function.body);
- if (constructor.isExternal &&
- !elementMap.isForeignLibrary(constructor.enclosingLibrary)) {
- MemberEntity member = elementMap.getMember(constructor);
+ MemberEntity member = elementMap.getMember(constructor);
+ if (constructor.isExternal && !elementMap.isForeignHelper(member)) {
bool isJsInterop = elementMap.nativeBasicData.isJsInteropMember(member);
impactBuilder.registerNativeData(elementMap
.getNativeBehaviorForMethod(constructor, isJsInterop: isJsInterop));
@@ -198,9 +197,8 @@
handleSignature(procedure.function);
visitNode(procedure.function.body);
handleAsyncMarker(procedure.function);
- if (procedure.isExternal &&
- !elementMap.isForeignLibrary(procedure.enclosingLibrary)) {
- MemberEntity member = elementMap.getMember(procedure);
+ MemberEntity member = elementMap.getMember(procedure);
+ if (procedure.isExternal && !elementMap.isForeignHelper(member)) {
bool isJsInterop = elementMap.nativeBasicData.isJsInteropMember(member);
impactBuilder.registerNativeData(elementMap
.getNativeBehaviorForMethod(procedure, isJsInterop: isJsInterop));
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 2258231..4451c74 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -978,7 +978,7 @@
HInstruction(this.inputs, this.instructionType)
: id = idCounter++,
usedBy = <HInstruction>[] {
- assert(inputs.every((e) => e != null));
+ assert(inputs.every((e) => e != null), "inputs: $inputs");
}
int get hashCode => id;
diff --git a/pkg/compiler/lib/src/universe/selector.dart b/pkg/compiler/lib/src/universe/selector.dart
index d4af193..34aaf06 100644
--- a/pkg/compiler/lib/src/universe/selector.dart
+++ b/pkg/compiler/lib/src/universe/selector.dart
@@ -208,15 +208,16 @@
*/
String get invocationMirrorMemberName => isSetter ? '$name=' : name;
+ static const int invocationMirrorMethodKind = 0;
+ static const int invocationMirrorGetterKind = 1;
+ static const int invocationMirrorSetterKind = 2;
+
int get invocationMirrorKind {
- const int METHOD = 0;
- const int GETTER = 1;
- const int SETTER = 2;
- int kind = METHOD;
+ int kind = invocationMirrorMethodKind;
if (isGetter) {
- kind = GETTER;
+ kind = invocationMirrorGetterKind;
} else if (isSetter) {
- kind = SETTER;
+ kind = invocationMirrorSetterKind;
}
return kind;
}
diff --git a/sdk/lib/_internal/js_runtime/lib/core_patch.dart b/sdk/lib/_internal/js_runtime/lib/core_patch.dart
index e590c9c..e27baf5 100644
--- a/sdk/lib/_internal/js_runtime/lib/core_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/core_patch.dart
@@ -2947,3 +2947,14 @@
return _reduce(resultDigits, resultUsed);
}
}
+
+/// Creates an invocation object used in noSuchMethod forwarding stubs.
+///
+/// The signature is hardwired to the kernel nodes generated in the
+/// `Dart2jsTarget` and read in the `KernelSsaGraphBuilder`.
+external Invocation _createInvocationMirror(
+ String memberName,
+ List typeArguments,
+ List positionalArguments,
+ Map<String, dynamic> namedArguments,
+ int kind);
diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
index fd9e485..9e2f105 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
@@ -251,9 +251,9 @@
}
createUnmangledInvocationMirror(
- Symbol symbol, internalName, kind, arguments, argumentNames) {
+ Symbol symbol, internalName, kind, arguments, argumentNames, types) {
return new JSInvocationMirror(
- symbol, internalName, kind, arguments, argumentNames, 0);
+ symbol, internalName, kind, arguments, argumentNames, types);
}
void throwInvalidReflectionError(String memberName) {
@@ -1258,8 +1258,13 @@
String selectorName =
'${JS_GET_NAME(JsGetName.CALL_PREFIX)}\$$argumentCount$names';
- return function.noSuchMethod(createUnmangledInvocationMirror(#call,
- selectorName, JSInvocationMirror.METHOD, arguments, namedArgumentList));
+ return function.noSuchMethod(createUnmangledInvocationMirror(
+ #call,
+ selectorName,
+ JSInvocationMirror.METHOD,
+ arguments,
+ namedArgumentList,
+ 0));
}
/**
diff --git a/tests/compiler/dart2js/helpers/element_lookup.dart b/tests/compiler/dart2js/helpers/element_lookup.dart
index 6d24abe..4823c94 100644
--- a/tests/compiler/dart2js/helpers/element_lookup.dart
+++ b/tests/compiler/dart2js/helpers/element_lookup.dart
@@ -22,12 +22,14 @@
}
MemberEntity findClassMember(
- JClosedWorld closedWorld, String className, String memberName) {
+ JClosedWorld closedWorld, String className, String memberName,
+ {bool required: true}) {
ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
ClassEntity cls = findClass(closedWorld, className);
assert(cls != null, "Class '$className' not found.");
MemberEntity member = elementEnvironment.lookupClassMember(cls, memberName);
- assert(member != null, "Member '$memberName' not found in $cls.");
+ assert(
+ !required || member != null, "Member '$memberName' not found in $cls.");
return member;
}
diff --git a/tests/compiler/dart2js/inference/data/no_such_method1.dart b/tests/compiler/dart2js/inference/data/no_such_method1.dart
index b4a1774..146ea37 100644
--- a/tests/compiler/dart2js/inference/data/no_such_method1.dart
+++ b/tests/compiler/dart2js/inference/data/no_such_method1.dart
@@ -10,7 +10,8 @@
/*element: B.:[exact=B]*/
class B extends A {
- foo();
+ /*strong.element: B.foo:[exact=JSUInt31]*/
+ /*strong.invoke: [subclass=B]*/ foo();
}
/*element: C.:[exact=C]*/
diff --git a/tests/compiler/dart2js/model/no_such_method_forwarders_test.dart b/tests/compiler/dart2js/model/no_such_method_forwarders_test.dart
new file mode 100644
index 0000000..7519cdd
--- /dev/null
+++ b/tests/compiler/dart2js/model/no_such_method_forwarders_test.dart
@@ -0,0 +1,180 @@
+// 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:async_helper/async_helper.dart';
+import 'package:compiler/src/commandline_options.dart';
+import 'package:compiler/src/compiler.dart';
+import 'package:compiler/src/elements/entities.dart';
+import 'package:compiler/src/elements/types.dart';
+import 'package:compiler/src/world.dart';
+import 'package:expect/expect.dart';
+import '../helpers/element_lookup.dart';
+import '../memory_compiler.dart';
+
+const String source = '''
+abstract class I<T> {
+ T method();
+}
+
+class A<T> implements I<T> {
+ noSuchMethod(_) => null;
+}
+
+class B<T> extends I<T> {
+ noSuchMethod(_) => null;
+}
+
+abstract class C1<T> implements I<T> {
+}
+
+class C2<T> extends C1<T> {
+ noSuchMethod(_) => null;
+}
+
+abstract class D1<T> implements I<T> {
+}
+
+abstract class D2<T> extends D1<T> {
+ noSuchMethod(_) => null;
+}
+
+class D3<T> extends D2<T> {
+}
+
+class E1<T> {
+ T method() => null;
+}
+
+abstract class E2<T> implements I<T> {
+ noSuchMethod(_) => null;
+}
+
+class E3<T> extends E1<T> with E2<T> {
+}
+
+class F1<T> {
+ T method() => null;
+}
+
+class F2<T> implements I<T> {
+ noSuchMethod(_) => null;
+}
+
+class F3<T> extends F1<T> with F2<T> {
+}
+
+abstract class G1<T> {
+ T method();
+}
+
+abstract class G2<T> implements I<T> {
+ noSuchMethod(_) => null;
+}
+
+class G3<T> extends G1<T> with G2<T> {
+}
+
+abstract class H1<T> {
+ T method();
+}
+
+abstract class H2<T> implements I<T> {
+ noSuchMethod(_) => null;
+}
+
+class H3<T> extends H1<T> with H2<T> {
+ method() => null;
+}
+
+main() {
+ new A();
+ new B();
+ new C2();
+ new D3();
+ new E1();
+ new E3();
+ new F1();
+ new F2();
+ new F3();
+ new G3();
+ new H3();
+}
+''';
+
+main() {
+ asyncTest(() async {
+ CompilationResult result = await runCompiler(
+ memorySourceFiles: {'main.dart': source}, options: [Flags.strongMode]);
+ Expect.isTrue(result.isSuccess);
+ Compiler compiler = result.compiler;
+ JClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
+
+ void check(String className,
+ {bool hasMethod, bool isAbstract: false, String declaringClass}) {
+ MemberEntity member =
+ findClassMember(closedWorld, className, 'method', required: false);
+ if (hasMethod) {
+ Expect.isNotNull(
+ member, "Missing member 'method' in class '$className'.");
+ Expect.equals(isAbstract, member.isAbstract,
+ "Unexpected abstract-ness on method $member.");
+ ClassEntity cls = findClass(closedWorld, declaringClass);
+ if (cls != member.enclosingClass) {
+ print("Unexpected declaring class $member. "
+ "Found ${member.enclosingClass}, expected $cls.");
+ }
+ Expect.equals(
+ cls,
+ member.enclosingClass,
+ "Unexpected declaring class $member. "
+ "Found ${member.enclosingClass}, expected $cls.");
+ DartType type;
+ if (member.isFunction) {
+ type =
+ closedWorld.elementEnvironment.getFunctionType(member).returnType;
+ } else if (member.isGetter) {
+ type =
+ closedWorld.elementEnvironment.getFunctionType(member).returnType;
+ } else if (member.isSetter) {
+ type = closedWorld.elementEnvironment
+ .getFunctionType(member)
+ .parameterTypes
+ .first;
+ }
+ Expect.isTrue(type is TypeVariableType,
+ "Unexpected member type for $member: $type");
+ TypeVariableType typeVariable = type;
+ Expect.equals(
+ cls,
+ typeVariable.element.typeDeclaration,
+ "Unexpected type declaration for $typeVariable for $member. "
+ "Expected $cls, found ${typeVariable.element.typeDeclaration}.");
+ } else {
+ Expect.isNull(member,
+ "Unexpected member 'method' in class '$className': $member.");
+ }
+ }
+
+ check('I', hasMethod: true, isAbstract: true, declaringClass: 'I');
+ check('A', hasMethod: true, declaringClass: 'A');
+ check('B', hasMethod: true, declaringClass: 'B');
+ check('C1', hasMethod: false);
+ check('C2', hasMethod: true, declaringClass: 'C2');
+ check('D1', hasMethod: false);
+ check('D2', hasMethod: false);
+ check('D3', hasMethod: true, declaringClass: 'D3');
+ check('E1', hasMethod: true, declaringClass: 'E1');
+ check('E2', hasMethod: false);
+ check('E3', hasMethod: true, declaringClass: 'E1');
+ check('F1', hasMethod: true, declaringClass: 'F1');
+ check('F2', hasMethod: true, declaringClass: 'F2');
+ check('F3', hasMethod: true, declaringClass: 'F1');
+ check('G1', hasMethod: true, isAbstract: true, declaringClass: 'G1');
+ check('G2', hasMethod: false);
+ check('G3', hasMethod: true, declaringClass: 'G3');
+ check('H1', hasMethod: true, isAbstract: true, declaringClass: 'H1');
+ check('H2', hasMethod: false);
+ check('H3', hasMethod: true, declaringClass: 'H3');
+ });
+}
diff --git a/tests/compiler/dart2js_extra/no_such_method_strong10_test.dart b/tests/compiler/dart2js_extra/no_such_method_strong10_test.dart
new file mode 100644
index 0000000..c09441f
--- /dev/null
+++ b/tests/compiler/dart2js_extra/no_such_method_strong10_test.dart
@@ -0,0 +1,31 @@
+// 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.
+
+// dart2jsOptions=--strong
+
+import 'package:expect/expect.dart';
+
+abstract class A {
+ m(x);
+}
+
+class B implements A {
+ noSuchMethod(Invocation i) {
+ print("nsm call: ${i.memberName}");
+ if (i.isGetter) {
+ throw (" - tearoff");
+ }
+ if (i.isMethod) {
+ print(" - method invocation");
+ return 42;
+ }
+ return 123;
+ }
+}
+
+void main() {
+ A x = new B();
+ var tearoff = x.m;
+ Expect.equals(42, tearoff(3));
+}
diff --git a/tests/compiler/dart2js_extra/no_such_method_strong1_test.dart b/tests/compiler/dart2js_extra/no_such_method_strong1_test.dart
new file mode 100644
index 0000000..62b6c60
--- /dev/null
+++ b/tests/compiler/dart2js_extra/no_such_method_strong1_test.dart
@@ -0,0 +1,30 @@
+// 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.
+
+// dart2jsOptions=--strong
+
+import 'package:expect/expect.dart';
+
+abstract class A {
+ m(x);
+}
+
+class B implements A {
+ noSuchMethod(Invocation i) {
+ print("nsm call: ${i.memberName}");
+ if (i.isGetter) {
+ throw (" - tearoff");
+ }
+ if (i.isMethod) {
+ print(" - method invocation");
+ return 42;
+ }
+ return 123;
+ }
+}
+
+void main() {
+ A x = new B();
+ Expect.equals(42, x.m(3));
+}
diff --git a/tests/compiler/dart2js_extra/no_such_method_strong2_test.dart b/tests/compiler/dart2js_extra/no_such_method_strong2_test.dart
new file mode 100644
index 0000000..e42f388
--- /dev/null
+++ b/tests/compiler/dart2js_extra/no_such_method_strong2_test.dart
@@ -0,0 +1,22 @@
+// 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.
+
+// dart2jsOptions=--strong
+
+import 'package:expect/expect.dart';
+
+abstract class A {
+ m(x);
+}
+
+class B implements A {
+ noSuchMethod(Invocation i) {
+ return i.memberName;
+ }
+}
+
+void main() {
+ A x = new B();
+ Expect.equals(#m, x.m(3));
+}
diff --git a/tests/compiler/dart2js_extra/no_such_method_strong3_test.dart b/tests/compiler/dart2js_extra/no_such_method_strong3_test.dart
new file mode 100644
index 0000000..5353d7b
--- /dev/null
+++ b/tests/compiler/dart2js_extra/no_such_method_strong3_test.dart
@@ -0,0 +1,22 @@
+// 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.
+
+// dart2jsOptions=--strong
+
+import 'package:expect/expect.dart';
+
+abstract class A {
+ m(x, y);
+}
+
+class B implements A {
+ noSuchMethod(Invocation i) {
+ return '${i.positionalArguments[0]},${i.positionalArguments[1]}';
+ }
+}
+
+void main() {
+ A x = new B();
+ Expect.equals('3,4', x.m(3, 4));
+}
diff --git a/tests/compiler/dart2js_extra/no_such_method_strong4_test.dart b/tests/compiler/dart2js_extra/no_such_method_strong4_test.dart
new file mode 100644
index 0000000..1ec9499
--- /dev/null
+++ b/tests/compiler/dart2js_extra/no_such_method_strong4_test.dart
@@ -0,0 +1,23 @@
+// 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.
+
+// dart2jsOptions=--strong
+
+import 'package:expect/expect.dart';
+
+abstract class A {
+ m({a, b});
+}
+
+class B implements A {
+ noSuchMethod(Invocation i) {
+ return '${i.namedArguments[#a]},${i.namedArguments[#b]}';
+ }
+}
+
+void main() {
+ A x = new B();
+ Expect.equals('3,4', x.m(a: 3, b: 4));
+ Expect.equals('3,4', x.m(b: 4, a: 3));
+}
diff --git a/tests/compiler/dart2js_extra/no_such_method_strong5_test.dart b/tests/compiler/dart2js_extra/no_such_method_strong5_test.dart
new file mode 100644
index 0000000..94f1531
--- /dev/null
+++ b/tests/compiler/dart2js_extra/no_such_method_strong5_test.dart
@@ -0,0 +1,22 @@
+// 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.
+
+// dart2jsOptions=--strong
+
+import 'package:expect/expect.dart';
+
+abstract class A {
+ m<X, Y>();
+}
+
+class B implements A {
+ noSuchMethod(Invocation i) {
+ return [i.typeArguments[0], i.typeArguments[1]];
+ }
+}
+
+void main() {
+ A x = new B();
+ Expect.listEquals([int, String], x.m<int, String>());
+}
diff --git a/tests/compiler/dart2js_extra/no_such_method_strong6_test.dart b/tests/compiler/dart2js_extra/no_such_method_strong6_test.dart
new file mode 100644
index 0000000..0018b85
--- /dev/null
+++ b/tests/compiler/dart2js_extra/no_such_method_strong6_test.dart
@@ -0,0 +1,28 @@
+// 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.
+
+// dart2jsOptions=--strong
+
+import 'package:expect/expect.dart';
+
+abstract class A {
+ m();
+}
+
+class B implements A {
+ noSuchMethod(Invocation i) {
+ return 42;
+ }
+}
+
+class C extends B {
+ noSuchMethod(Invocation i) => 87;
+
+ method() => super.m();
+}
+
+void main() {
+ C x = new C();
+ Expect.equals(87, x.method());
+}
diff --git a/tests/compiler/dart2js_extra/no_such_method_strong7_test.dart b/tests/compiler/dart2js_extra/no_such_method_strong7_test.dart
new file mode 100644
index 0000000..7f3925f
--- /dev/null
+++ b/tests/compiler/dart2js_extra/no_such_method_strong7_test.dart
@@ -0,0 +1,22 @@
+// 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.
+
+// dart2jsOptions=--strong
+
+import 'package:expect/expect.dart';
+
+abstract class A {
+ get m;
+}
+
+class B implements A {
+ noSuchMethod(Invocation i) {
+ return i.isGetter ? 42 : 87;
+ }
+}
+
+void main() {
+ A x = new B();
+ Expect.equals(42, x.m);
+}
diff --git a/tests/compiler/dart2js_extra/no_such_method_strong8_test.dart b/tests/compiler/dart2js_extra/no_such_method_strong8_test.dart
new file mode 100644
index 0000000..a98447b
--- /dev/null
+++ b/tests/compiler/dart2js_extra/no_such_method_strong8_test.dart
@@ -0,0 +1,22 @@
+// 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.
+
+// dart2jsOptions=--strong
+
+import 'package:expect/expect.dart';
+
+abstract class A {
+ set m(_);
+}
+
+class B implements A {
+ noSuchMethod(Invocation i) {
+ return i.isSetter ? i.positionalArguments[0] : 87;
+ }
+}
+
+void main() {
+ A x = new B();
+ Expect.equals(42, x.m = 42);
+}
diff --git a/tests/compiler/dart2js_extra/no_such_method_strong9_test.dart b/tests/compiler/dart2js_extra/no_such_method_strong9_test.dart
new file mode 100644
index 0000000..25ccc7e
--- /dev/null
+++ b/tests/compiler/dart2js_extra/no_such_method_strong9_test.dart
@@ -0,0 +1,30 @@
+// 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.
+
+// dart2jsOptions=--strong
+
+import 'package:expect/expect.dart';
+
+abstract class A {
+ m();
+}
+
+abstract class B implements A {
+ noSuchMethod(Invocation i) {
+ return 42;
+ }
+}
+
+class C extends B {}
+
+class D extends C {
+ noSuchMethod(Invocation i) => 87;
+
+ method() => super.m();
+}
+
+void main() {
+ D x = new D();
+ Expect.equals(87, x.method());
+}
diff --git a/tests/language_2/language_2_dart2js.status b/tests/language_2/language_2_dart2js.status
index 01b658e..ef32963 100644
--- a/tests/language_2/language_2_dart2js.status
+++ b/tests/language_2/language_2_dart2js.status
@@ -760,8 +760,6 @@
no_main_test/01: CompileTimeError
no_such_method_mock_test: RuntimeError
nosuchmethod_forwarding/nosuchmethod_forwarding_arguments_test: RuntimeError
-nosuchmethod_forwarding/nosuchmethod_forwarding_test/05: RuntimeError
-nosuchmethod_forwarding/nosuchmethod_forwarding_test/06: RuntimeError
null_no_such_method_test: CompileTimeError
number_identity2_test: RuntimeError
numbers_test: RuntimeError, OK # non JS number semantics
@@ -800,12 +798,7 @@
string_split_test: CompileTimeError
string_supertype_checked_test: CompileTimeError
super_bound_closure_test/none: CompileTimeError
-super_no_such_method1_test: CompileTimeError
-super_no_such_method2_test: CompileTimeError
-super_no_such_method3_test: CompileTimeError
-super_no_such_method4_test: CompileTimeError
-super_no_such_method5_test: CompileTimeError
-super_operator_index5_test: CompileTimeError
+super_call4_test/01: MissingCompileTimeError
super_operator_index6_test: CompileTimeError
super_operator_index7_test: CompileTimeError
super_operator_index8_test: CompileTimeError
@@ -1206,8 +1199,6 @@
no_main_test/01: CompileTimeError
no_such_method_mock_test: RuntimeError
nosuchmethod_forwarding/nosuchmethod_forwarding_arguments_test: RuntimeError
-nosuchmethod_forwarding/nosuchmethod_forwarding_test/05: RuntimeError
-nosuchmethod_forwarding/nosuchmethod_forwarding_test/06: RuntimeError
null_no_such_method_test: CompileTimeError
number_identity2_test: RuntimeError
numbers_test: RuntimeError, OK # non JS number semantics
@@ -1264,12 +1255,7 @@
string_split_test: CompileTimeError
string_supertype_checked_test: CompileTimeError
super_bound_closure_test/none: CompileTimeError
-super_no_such_method1_test: CompileTimeError
-super_no_such_method2_test: CompileTimeError
-super_no_such_method3_test: CompileTimeError
-super_no_such_method4_test: CompileTimeError
-super_no_such_method5_test: CompileTimeError
-super_operator_index5_test: CompileTimeError
+super_call4_test/01: MissingCompileTimeError
super_operator_index6_test: CompileTimeError
super_operator_index7_test: CompileTimeError
super_operator_index8_test: CompileTimeError
@@ -1721,8 +1707,6 @@
no_such_method_native_test: RuntimeError
no_such_method_test: RuntimeError
nosuchmethod_forwarding/nosuchmethod_forwarding_arguments_test: RuntimeError
-nosuchmethod_forwarding/nosuchmethod_forwarding_test/05: RuntimeError
-nosuchmethod_forwarding/nosuchmethod_forwarding_test/06: RuntimeError
null_no_such_method_test: CompileTimeError
number_identity2_test: RuntimeError
numbers_test: RuntimeError, OK # non JS number semantics
@@ -1779,12 +1763,7 @@
string_split_test: CompileTimeError
string_supertype_checked_test: CompileTimeError
super_bound_closure_test/none: CompileTimeError
-super_no_such_method1_test: CompileTimeError
-super_no_such_method2_test: CompileTimeError
-super_no_such_method3_test: CompileTimeError
-super_no_such_method4_test: CompileTimeError
-super_no_such_method5_test: CompileTimeError
-super_operator_index5_test: CompileTimeError
+super_call4_test/01: MissingCompileTimeError
super_operator_index6_test: CompileTimeError
super_operator_index7_test: CompileTimeError
super_operator_index8_test: CompileTimeError
@@ -1871,7 +1850,6 @@
const_constructor3_test/04: MissingCompileTimeError # OK - Subtype check uses JS number semantics.
covariant_subtyping_test: Crash
ct_const_test: RuntimeError
-nosuchmethod_forwarding/nosuchmethod_forwarding_partial_instantiation_test: RuntimeError
[ $compiler == dart2js && $fasta && !$strong ]
*: SkipByDesign