Index: pkg/compiler/lib/src/ssa/builder.dart |
diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart |
index e24af1149cbc83888b475d375c66d39cc9cffed0..bb00a2640caf363cc5a45d4592002585afbc3269 100644 |
--- a/pkg/compiler/lib/src/ssa/builder.dart |
+++ b/pkg/compiler/lib/src/ssa/builder.dart |
@@ -1339,6 +1339,11 @@ class SsaBuilder extends ast.Visitor |
// enqueued. |
backend.registerStaticUse(element, compiler.enqueuer.codegen); |
+ if (element.isJsInterop && !element.isFactoryConstructor) { |
+ // We only inline factory JavaScript interop constructors. |
+ return false; |
+ } |
+ |
// Ensure that [element] is an implementation element. |
element = element.implementation; |
@@ -1369,6 +1374,8 @@ class SsaBuilder extends ast.Visitor |
} |
} |
+ if (element.isJsInterop) return false; |
+ |
// Don't inline operator== methods if the parameter can be null. |
if (element.name == '==') { |
if (element.enclosingClass != compiler.objectClass |
@@ -1547,6 +1554,13 @@ class SsaBuilder extends ast.Visitor |
}); |
} |
+ /** |
+ * Return null so it is simple to remove the optional parameters completely |
+ * from interop methods to match JavaScript semantics for ommitted arguments. |
+ */ |
+ HInstruction handleConstantForOptionalParameterJsInterop(Element parameter) => |
+ null; |
+ |
HInstruction handleConstantForOptionalParameter(Element parameter) { |
ConstantValue constantValue = |
backend.constants.getConstantValueForVariable(parameter); |
@@ -1634,10 +1648,19 @@ class SsaBuilder extends ast.Visitor |
graph.calledInLoop = compiler.world.isCalledInLoop(functionElement); |
ast.FunctionExpression function = functionElement.node; |
assert(function != null); |
- assert(invariant(functionElement, !function.modifiers.isExternal)); |
assert(elements.getFunctionDefinition(function) != null); |
openFunction(functionElement, function); |
String name = functionElement.name; |
+ if (functionElement.isJsInterop) { |
+ push(invokeJsInteropFunction(functionElement, parameters.values.toList(), |
+ sourceInformationBuilder.buildGeneric(function))); |
+ var value = pop(); |
+ closeAndGotoExit(new HReturn(value, |
+ sourceInformationBuilder.buildReturn(functionElement.node))); |
+ return closeFunction(); |
+ } |
+ assert(invariant(functionElement, !function.modifiers.isExternal)); |
+ |
// If [functionElement] is `operator==` we explicitely add a null check at |
// the beginning of the method. This is to avoid having call sites do the |
// null check. |
@@ -1842,6 +1865,7 @@ class SsaBuilder extends ast.Visitor |
*/ |
void visitInlinedFunction(FunctionElement function) { |
potentiallyCheckInlinedParameterTypes(function); |
+ |
if (function.isGenerativeConstructor) { |
buildFactory(function); |
} else { |
@@ -2145,7 +2169,8 @@ class SsaBuilder extends ast.Visitor |
ClassElement classElement = |
functionElement.enclosingClass.implementation; |
bool isNativeUpgradeFactory = |
- Elements.isNativeOrExtendsNative(classElement); |
+ Elements.isNativeOrExtendsNative(classElement) |
+ && !classElement.isJsInterop; |
ast.FunctionExpression function = functionElement.node; |
// Note that constructors (like any other static function) do not need |
// to deal with optional arguments. It is the callers job to provide all |
@@ -3956,7 +3981,9 @@ class SsaBuilder extends ast.Visitor |
arguments, |
element, |
compileArgument, |
- handleConstantForOptionalParameter); |
+ element.isJsInterop ? |
+ handleConstantForOptionalParameterJsInterop : |
+ handleConstantForOptionalParameter); |
} |
void addGenericSendArgumentsToList(Link<ast.Node> link, List<HInstruction> list) { |
@@ -5101,7 +5128,8 @@ class SsaBuilder extends ast.Visitor |
var inputs = <HInstruction>[]; |
if (constructor.isGenerativeConstructor && |
- Elements.isNativeOrExtendsNative(constructor.enclosingClass)) { |
+ Elements.isNativeOrExtendsNative(constructor.enclosingClass) && |
+ !constructor.isJsInterop) { |
// Native class generative constructors take a pre-constructed object. |
inputs.add(graph.addConstantNull(compiler)); |
} |
@@ -5818,6 +5846,96 @@ class SsaBuilder extends ast.Visitor |
} |
} |
+ HForeignCode invokeJsInteropFunction(Element element, |
+ List<HInstruction> arguments, |
+ SourceInformation sourceInformation) { |
+ assert(element.isJsInterop); |
+ nativeEmitter.nativeMethods.add(element); |
+ String templateString; |
+ |
+ if (element.isFactoryConstructor) { |
+ // Treat factory constructors as syntactic sugar for creating object |
+ // literals. |
+ ConstructorElement constructor = element; |
+ FunctionSignature params = constructor.functionSignature; |
+ int i = 0; |
+ int positions = 0; |
+ var filteredArguments = <HInstruction>[]; |
+ var parameterNameMap = new Map<String, js.Expression>(); |
+ params.orderedForEachParameter((ParameterElement parameter) { |
+ // TODO(jacobr): throw if parameter names do not match names of property |
+ // names in the class. |
+ assert (parameter.isNamed); |
+ if (!parameter.isNamed) { |
+ reporter.reportErrorMessage( |
+ parameter, MessageKind.GENERIC, |
+ {'text': 'All arguments to external constructors of JavaScript ' |
+ 'interop classes must be named as these constructors ' |
+ 'are syntactic sugar for object literals.'}); |
+ } |
+ HInstruction argument = arguments[i]; |
+ if (argument != null) { |
+ filteredArguments.add(argument); |
+ parameterNameMap[parameter.name] = |
+ new js.InterpolatedExpression(positions++); |
+ } |
+ i++; |
+ }); |
+ var codeTemplate = new js.Template(null, |
+ js.objectLiteral(parameterNameMap)); |
+ |
+ var nativeBehavior = new native.NativeBehavior() |
+ ..codeTemplate = codeTemplate; |
+ return new HForeignCode( |
+ codeTemplate, |
+ backend.dynamicType, filteredArguments, |
+ nativeBehavior: nativeBehavior) |
+ ..sourceInformation = sourceInformation; |
+ } |
+ var target = new HForeignCode(js.js.parseForeignJS( |
+ "${backend.namer.fixedBackendPath(element)}." |
+ "${element.fixedBackendName}"), |
+ backend.dynamicType, |
+ <HInstruction>[]); |
+ add(target); |
+ // Strip off trailing arguments that were not specified. |
+ // we could assert that the trailing arguments are all null. |
+ // TODO(jacobr): rewrite named arguments to an object literal matching |
+ // the factory constructor case. |
+ arguments = arguments.where((arg) => arg != null).toList(); |
+ var inputs = <HInstruction>[target]..addAll(arguments); |
+ |
+ js.Template codeTemplate; |
+ if (element.isGetter) { |
+ codeTemplate = js.js.parseForeignJS("#"); |
+ } else if (element.isSetter) { |
+ codeTemplate = js.js.parseForeignJS("# = #"); |
+ } else { |
+ var argsStub = <String>[]; |
+ for (int i = 0; i < arguments.length; i++) { |
+ argsStub.add('#'); |
+ } |
+ if (element.isConstructor) { |
+ codeTemplate = js.js.parseForeignJS("new #(${argsStub.join(",")})"); |
+ } else { |
+ codeTemplate = js.js.parseForeignJS("#(${argsStub.join(",")})"); |
+ } |
+ } |
+ |
+ var nativeBehavior = new native.NativeBehavior() |
+ ..codeTemplate = codeTemplate |
+ ..typesReturned.add( |
+ backend.jsJavaScriptObjectClass.thisType) |
+ ..typesInstantiated.add( |
+ backend.jsJavaScriptObjectClass.thisType) |
+ ..sideEffects.setAllSideEffects(); |
+ return new HForeignCode( |
+ codeTemplate, |
+ backend.dynamicType, inputs, |
+ nativeBehavior: nativeBehavior) |
+ ..sourceInformation = sourceInformation; |
+ } |
+ |
void pushInvokeStatic(ast.Node location, |
Element element, |
List<HInstruction> arguments, |
@@ -5836,16 +5954,22 @@ class SsaBuilder extends ast.Visitor |
} |
bool targetCanThrow = !compiler.world.getCannotThrow(element); |
// TODO(5346): Try to avoid the need for calling [declaration] before |
- // creating an [HInvokeStatic]. |
- HInvokeStatic instruction = new HInvokeStatic( |
- element.declaration, arguments, typeMask, |
- targetCanThrow: targetCanThrow) |
- ..sourceInformation = sourceInformation; |
- if (!currentInlinedInstantiations.isEmpty) { |
- instruction.instantiatedTypes = new List<DartType>.from( |
- currentInlinedInstantiations); |
+ var instruction; |
+ if (element.isJsInterop) { |
+ instruction = invokeJsInteropFunction(element, arguments, |
+ sourceInformation); |
+ } else { |
+ // creating an [HInvokeStatic]. |
+ instruction = new HInvokeStatic( |
+ element.declaration, arguments, typeMask, |
+ targetCanThrow: targetCanThrow) |
+ ..sourceInformation = sourceInformation; |
+ if (!currentInlinedInstantiations.isEmpty) { |
+ instruction.instantiatedTypes = new List<DartType>.from( |
+ currentInlinedInstantiations); |
+ } |
+ instruction.sideEffects = compiler.world.getSideEffectsOfElement(element); |
} |
- instruction.sideEffects = compiler.world.getSideEffectsOfElement(element); |
if (location == null) { |
push(instruction); |
} else { |