Index: pkg/compiler/lib/src/ssa/builder_kernel.dart |
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart |
index 4665ee8a0ee8ef4ed4813bb152f1e7ecfd90b810..3b9bb36d1a167226ab52a7fd213bcf04571ce317 100644 |
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart |
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart |
@@ -9,15 +9,19 @@ import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem; |
import '../common/names.dart'; |
import '../common/tasks.dart' show CompilerTask; |
import '../compiler.dart'; |
+import '../constants/values.dart' show StringConstantValue; |
import '../dart_types.dart'; |
import '../elements/elements.dart'; |
import '../io/source_information.dart'; |
+import '../js/js.dart' as js; |
import '../js_backend/backend.dart' show JavaScriptBackend; |
import '../kernel/kernel.dart'; |
+import '../native/native.dart' as native; |
import '../resolution/tree_elements.dart'; |
import '../tree/dartstring.dart'; |
import '../types/masks.dart'; |
import '../universe/selector.dart'; |
+import '../universe/side_effects.dart' show SideEffects; |
import 'graph_builder.dart'; |
import 'kernel_ast_adapter.dart'; |
import 'kernel_string_builder.dart'; |
@@ -567,12 +571,16 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
@override |
void visitStaticGet(ir.StaticGet staticGet) { |
- var staticTarget = staticGet.target; |
+ ir.Member staticTarget = staticGet.target; |
if (staticTarget is ir.Procedure && |
staticTarget.kind == ir.ProcedureKind.Getter) { |
// Invoke the getter |
_pushStaticInvocation(staticTarget, const <HInstruction>[], |
astAdapter.returnTypeOf(staticTarget)); |
+ } else if (staticTarget is ir.Field && staticTarget.isConst) { |
+ assert(staticTarget.initializer != null); |
+ stack.add(graph.addConstant( |
+ astAdapter.getConstantFor(staticTarget.initializer), compiler)); |
} else { |
Element element = astAdapter.getElement(staticTarget).declaration; |
push(new HStatic(element, astAdapter.inferredTypeOf(staticTarget))); |
@@ -671,6 +679,10 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
@override |
void visitStaticInvocation(ir.StaticInvocation invocation) { |
ir.Procedure target = invocation.target; |
+ if (astAdapter.isInForeignLibrary(target)) { |
+ handleInvokeStaticForeign(invocation, target); |
+ return; |
+ } |
TypeMask typeMask = astAdapter.returnTypeOf(target); |
List<HInstruction> arguments = _visitArguments(invocation.arguments); |
@@ -678,6 +690,378 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
_pushStaticInvocation(target, arguments, typeMask); |
} |
+ void handleInvokeStaticForeign( |
+ ir.StaticInvocation invocation, ir.Procedure target) { |
+ String name = target.name.name; |
+ if (name == 'JS') { |
+ handleForeignJs(invocation); |
+ } else if (name == 'JS_CURRENT_ISOLATE_CONTEXT') { |
+ handleForeignJsCurrentIsolateContext(invocation); |
+ } else if (name == 'JS_CALL_IN_ISOLATE') { |
+ handleForeignJsCallInIsolate(invocation); |
+ } else if (name == 'DART_CLOSURE_TO_JS') { |
+ handleForeignDartClosureToJs(invocation, 'DART_CLOSURE_TO_JS'); |
+ } else if (name == 'RAW_DART_FUNCTION_REF') { |
+ handleForeignRawFunctionRef(invocation, 'RAW_DART_FUNCTION_REF'); |
+ } else if (name == 'JS_SET_STATIC_STATE') { |
+ handleForeignJsSetStaticState(invocation); |
+ } else if (name == 'JS_GET_STATIC_STATE') { |
+ handleForeignJsGetStaticState(invocation); |
+ } else if (name == 'JS_GET_NAME') { |
+ handleForeignJsGetName(invocation); |
+ } else if (name == 'JS_EMBEDDED_GLOBAL') { |
+ handleForeignJsEmbeddedGlobal(invocation); |
+ } else if (name == 'JS_BUILTIN') { |
+ handleForeignJsBuiltin(invocation); |
+ } else if (name == 'JS_GET_FLAG') { |
+ handleForeignJsGetFlag(invocation); |
+ } else if (name == 'JS_EFFECT') { |
+ stack.add(graph.addConstantNull(compiler)); |
+ } else if (name == 'JS_INTERCEPTOR_CONSTANT') { |
+ handleJsInterceptorConstant(invocation); |
+ } else if (name == 'JS_STRING_CONCAT') { |
+ handleJsStringConcat(invocation); |
+ } else { |
+ compiler.reporter.internalError( |
+ astAdapter.getNode(invocation), "Unknown foreign: ${name}"); |
+ } |
+ } |
+ |
+ bool _unexpectedForeignArguments( |
+ ir.StaticInvocation invocation, int minPositional, [int maxPositional]) { |
+ |
+ String pluralizeArguments(int count) { |
+ if (count == 0) return 'no arguments'; |
+ if (count == 1) return 'one argument'; |
+ if (count == 2) return 'two arguments'; |
+ return '$count arguments'; |
+ } |
+ String name() => invocation.target.name.name; |
+ |
+ |
+ ir.Arguments arguments = invocation.arguments; |
+ bool bad = false; |
+ if (arguments.types.isNotEmpty) { |
+ compiler.reporter.reportErrorMessage( |
+ astAdapter.getNode(invocation), |
+ MessageKind.GENERIC, |
+ {'text': "Error: '${name()}' does not take type arguments."}); |
+ bad = true; |
+ } |
+ if (arguments.positional.length < minPositional) { |
+ String phrase = pluralizeArguments(minPositional); |
+ if (maxPositional != minPositional) phrase = 'at least $phrase'; |
+ compiler.reporter.reportErrorMessage( |
+ astAdapter.getNode(invocation), |
+ MessageKind.GENERIC, |
+ {'text': "Error: Too few arguments. '${name()}' takes $phrase."}); |
+ bad = true; |
+ } |
+ if (maxPositional != null && arguments.positional.length > maxPositional) { |
+ String phrase = pluralizeArguments(maxPositional); |
+ if (maxPositional != minPositional) phrase = 'at most $phrase'; |
+ compiler.reporter.reportErrorMessage( |
+ astAdapter.getNode(invocation), |
+ MessageKind.GENERIC, |
+ {'text': "Error: Too many arguments. '${name()}' takes $phrase."}); |
+ bad = true; |
+ } |
+ if (arguments.named.isNotEmpty) { |
+ compiler.reporter.reportErrorMessage( |
+ astAdapter.getNode(invocation), |
+ MessageKind.GENERIC, |
+ {'text': "Error: '${name()}' does not take named arguments."}); |
+ bad = true; |
+ } |
+ return bad; |
+ } |
+ |
+ /// Returns the value of the string argument. The argument must evaluate to a |
+ /// constant. If there is an error, the error is reported and `null` is |
+ /// returned. |
+ String _foreignConstantStringArgument( |
+ ir.StaticInvocation invocation, int position, String methodName, |
+ [String adjective = '']) { |
+ ir.Expression argument = invocation.arguments.positional[position]; |
+ argument.accept(this); |
+ HInstruction instruction = pop(); |
+ |
+ if (!instruction.isConstantString()) { |
+ compiler.reporter.reportErrorMessage( |
+ astAdapter.getNode(argument), MessageKind.GENERIC, { |
+ 'text': "Error: Expected String constant as ${adjective}argument " |
+ "to '$methodName'." |
+ }); |
+ return null; |
+ } |
+ |
+ HConstant hConstant = instruction; |
+ StringConstantValue stringConstant = hConstant.constant; |
+ return stringConstant.primitiveValue.slowToString(); |
+ } |
+ |
+ // TODO(sra): Remove when handleInvokeStaticForeign fully implemented. |
+ void unhandledForeign(ir.StaticInvocation invocation) { |
+ ir.Procedure target = invocation.target; |
+ TypeMask typeMask = astAdapter.returnTypeOf(target); |
+ List<HInstruction> arguments = _visitArguments(invocation.arguments); |
+ _pushStaticInvocation(target, arguments, typeMask); |
+ } |
+ |
+ void handleForeignJsCurrentIsolateContext(ir.StaticInvocation invocation) { |
+ if (_unexpectedForeignArguments(invocation, 0, 0)) { |
+ stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
+ return; |
+ } |
+ |
+ if (!compiler.hasIsolateSupport) { |
+ // If the isolate library is not used, we just generate code |
+ // to fetch the static state. |
+ String name = backend.namer.staticStateHolder; |
+ push(new HForeignCode( |
+ js.js.parseForeignJS(name), backend.dynamicType, <HInstruction>[], |
+ nativeBehavior: native.NativeBehavior.DEPENDS_OTHER)); |
+ } else { |
+ // Call a helper method from the isolate library. The isolate library uses |
+ // its own isolate structure that encapsulates the isolate structure used |
+ // for binding to methods. |
+ ir.Procedure target = astAdapter.currentIsolate; |
+ Element element = helpers.currentIsolate; |
+ if (target == null) { |
+ compiler.reporter.internalError(node, 'Isolate library and compiler mismatch.'); |
+ } |
+ _pushStaticInvocation(target, <HInstruction>[], backend.dynamicType); |
+ } |
+ |
+ /* |
+ if (!node.arguments.isEmpty) { |
+ reporter.internalError( |
+ node, 'Too many arguments to JS_CURRENT_ISOLATE_CONTEXT.'); |
+ } |
+ |
+ if (!compiler.hasIsolateSupport) { |
+ // If the isolate library is not used, we just generate code |
+ // to fetch the static state. |
+ String name = backend.namer.staticStateHolder; |
+ push(new HForeignCode( |
+ js.js.parseForeignJS(name), backend.dynamicType, <HInstruction>[], |
+ nativeBehavior: native.NativeBehavior.DEPENDS_OTHER)); |
+ } else { |
+ // Call a helper method from the isolate library. The isolate |
+ // library uses its own isolate structure, that encapsulates |
+ // Leg's isolate. |
+ Element element = helpers.currentIsolate; |
+ if (element == null) { |
+ reporter.internalError(node, 'Isolate library and compiler mismatch.'); |
+ } |
+ pushInvokeStatic(null, element, [], typeMask: backend.dynamicType); |
+ } |
+ */ |
+ } |
+ |
+ void handleForeignJsCallInIsolate(ir.StaticInvocation invocation) { |
+ unhandledForeign(invocation); |
+ } |
+ |
+ void handleForeignDartClosureToJs( |
+ ir.StaticInvocation invocation, String name) { |
+ unhandledForeign(invocation); |
+ } |
+ |
+ void handleForeignRawFunctionRef( |
+ ir.StaticInvocation invocation, String name) { |
+ unhandledForeign(invocation); |
+ } |
+ |
+ void handleForeignJsSetStaticState(ir.StaticInvocation invocation) { |
+ if (_unexpectedForeignArguments(invocation, 0, 0)) { |
+ stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
+ return; |
+ } |
+ _visitArguments(invocation.arguments); |
+ String isolateName = backend.namer.staticStateHolder; |
+ SideEffects sideEffects = new SideEffects.empty(); |
+ sideEffects.setAllSideEffects(); |
+ push(new HForeignCode(js.js.parseForeignJS("$isolateName = #"), |
+ backend.dynamicType, <HInstruction>[pop()], |
+ nativeBehavior: native.NativeBehavior.CHANGES_OTHER, |
+ effects: sideEffects)); |
+ } |
+ |
+ void handleForeignJsGetStaticState(ir.StaticInvocation invocation) { |
+ if (_unexpectedForeignArguments(invocation, 0, 0)) { |
+ stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
+ return; |
+ } |
+ |
+ push(new HForeignCode(js.js.parseForeignJS(backend.namer.staticStateHolder), |
+ backend.dynamicType, <HInstruction>[], |
+ nativeBehavior: native.NativeBehavior.DEPENDS_OTHER)); |
+ } |
+ |
+ void handleForeignJsGetName(ir.StaticInvocation invocation) { |
+ if (_unexpectedForeignArguments(invocation, 1, 1)) { |
+ stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
+ return; |
+ } |
+ |
+ ir.Node argument = invocation.arguments.positional.first; |
+ argument.accept(this); |
+ HInstruction instruction = pop(); |
+ |
+ if (instruction is HConstant) { |
+ js.Name name = |
+ astAdapter.getNameForJsGetName(argument, instruction.constant); |
+ stack.add(graph.addConstantStringFromName(name, compiler)); |
+ return; |
+ } |
+ |
+ compiler.reporter.reportErrorMessage( |
+ astAdapter.getNode(argument), |
+ MessageKind.GENERIC, |
+ {'text': 'Error: Expected a JsGetName enum value.'}); |
+ stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
+ } |
+ |
+ void handleForeignJsEmbeddedGlobal(ir.StaticInvocation invocation) { |
+ if (_unexpectedForeignArguments(invocation, 2, 2)) { |
+ stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
+ return; |
+ } |
+ String globalName = _foreignConstantStringArgument( |
+ invocation, 1, 'JS_EMBEDDED_GLOBAL', 'second '); |
+ js.Template expr = js.js.expressionTemplateYielding( |
+ backend.emitter.generateEmbeddedGlobalAccess(globalName)); |
+ |
+ native.NativeBehavior nativeBehavior = |
+ astAdapter.getNativeBehavior(invocation); |
+ assert(invariant(astAdapter.getNode(invocation), nativeBehavior != null, |
+ message: "No NativeBehavior for $invocation")); |
+ |
+ TypeMask ssaType = astAdapter.typeFromNativeBehavior(nativeBehavior); |
+ push(new HForeignCode(expr, ssaType, const <HInstruction>[], |
+ nativeBehavior: nativeBehavior)); |
+ } |
+ |
+ void handleForeignJsBuiltin(ir.StaticInvocation invocation) { |
+ if (_unexpectedForeignArguments(invocation, 2)) { |
+ stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
+ return; |
+ } |
+ |
+ List<ir.Expression> arguments = invocation.arguments.positional; |
+ ir.Expression nameArgument = arguments[1]; |
+ |
+ nameArgument.accept(this); |
+ HInstruction instruction = pop(); |
+ |
+ js.Template template; |
+ if (instruction is HConstant) { |
+ template = astAdapter.getJsBuiltinTemplate(instruction.constant); |
+ } |
+ if (template == null) { |
+ compiler.reporter.reportErrorMessage( |
+ astAdapter.getNode(nameArgument), |
+ MessageKind.GENERIC, |
+ {'text': 'Error: Expected a JsBuiltin enum value.'}); |
+ stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
+ return; |
+ } |
+ |
+ List<HInstruction> inputs = <HInstruction>[]; |
+ for (ir.Expression argument in arguments.skip(2)) { |
+ argument.accept(this); |
+ inputs.add(pop()); |
+ } |
+ |
+ native.NativeBehavior nativeBehavior = |
+ astAdapter.getNativeBehavior(invocation); |
+ assert(invariant(astAdapter.getNode(invocation), nativeBehavior != null, |
+ message: "No NativeBehavior for $invocation")); |
+ |
+ TypeMask ssaType = astAdapter.typeFromNativeBehavior(nativeBehavior); |
+ push(new HForeignCode(template, ssaType, inputs, |
+ nativeBehavior: nativeBehavior)); |
+ } |
+ |
+ void handleForeignJsGetFlag(ir.StaticInvocation invocation) { |
+ if (_unexpectedForeignArguments(invocation, 1, 1)) { |
+ stack.add( |
+ graph.addConstantBool(false, compiler)); // Result expected on stack. |
+ return; |
+ } |
+ String name = _foreignConstantStringArgument(invocation, 0, 'JS_GET_FLAG'); |
+ bool value = false; |
+ switch (name) { |
+ case 'MUST_RETAIN_METADATA': |
+ value = backend.mustRetainMetadata; |
+ break; |
+ case 'USE_CONTENT_SECURITY_POLICY': |
+ value = compiler.options.useContentSecurityPolicy; |
+ break; |
+ default: |
+ compiler.reporter.reportErrorMessage( |
+ astAdapter.getNode(invocation), |
+ MessageKind.GENERIC, |
+ {'text': 'Error: Unknown internal flag "$name".'}); |
+ } |
+ stack.add(graph.addConstantBool(value, compiler)); |
+ } |
+ |
+ void handleJsInterceptorConstant(ir.StaticInvocation invocation) { |
+ unhandledForeign(invocation); |
+ } |
+ |
+ void handleForeignJs(ir.StaticInvocation invocation) { |
+ if (_unexpectedForeignArguments(invocation, 2)) { |
+ stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
+ return; |
+ } |
+ |
+ native.NativeBehavior nativeBehavior = |
+ astAdapter.getNativeBehavior(invocation); |
+ assert(invariant(astAdapter.getNode(invocation), nativeBehavior != null, |
+ message: "No NativeBehavior for $invocation")); |
+ |
+ List<HInstruction> inputs = <HInstruction>[]; |
+ for (ir.Expression argument in invocation.arguments.positional.skip(2)) { |
+ argument.accept(this); |
+ inputs.add(pop()); |
+ } |
+ |
+ if (nativeBehavior.codeTemplate.positionalArgumentCount != inputs.length) { |
+ compiler.reporter.reportErrorMessage( |
+ astAdapter.getNode(invocation), MessageKind.GENERIC, { |
+ 'text': 'Mismatch between number of placeholders' |
+ ' and number of arguments.' |
+ }); |
+ stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
+ return; |
+ } |
+ |
+ if (native.HasCapturedPlaceholders.check(nativeBehavior.codeTemplate.ast)) { |
+ compiler.reporter.reportErrorMessage( |
+ astAdapter.getNode(invocation), MessageKind.JS_PLACEHOLDER_CAPTURE); |
+ } |
+ |
+ TypeMask ssaType = astAdapter.typeFromNativeBehavior(nativeBehavior); |
+ |
+ SourceInformation sourceInformation = null; |
+ push(new HForeignCode(nativeBehavior.codeTemplate, ssaType, inputs, |
+ isStatement: !nativeBehavior.codeTemplate.isExpression, |
+ effects: nativeBehavior.sideEffects, |
+ nativeBehavior: nativeBehavior)..sourceInformation = sourceInformation); |
+ } |
+ |
+ void handleJsStringConcat(ir.StaticInvocation invocation) { |
+ if (_unexpectedForeignArguments(invocation, 2, 2)) { |
+ stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
+ return; |
+ } |
+ List<HInstruction> inputs = _visitArguments(invocation.arguments); |
+ push(new HStringConcat(inputs[0], inputs[1], backend.stringType)); |
+ } |
+ |
void _pushStaticInvocation( |
ir.Node target, List<HInstruction> arguments, TypeMask typeMask) { |
HInstruction instruction = new HInvokeStatic( |