Chromium Code Reviews| 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..66a7d8d350305a3f1289894394c18a26cd54dd46 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,359 @@ 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, String name, int minPositional, |
| + [int maxPositional]) { |
| + 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; |
| + } |
| + String pluralize(int count, String singular, String plural) { |
| + if (count == 0) return 'no $plural'; |
| + if (count == 1) return 'one $singular'; |
| + if (count == 2) return 'two $plural'; |
| + return '$count ${plural}s'; |
|
Harry Terkelsen
2016/10/12 16:42:59
should be '$count $plural'
sra1
2016/10/13 19:02:01
Done.
|
| + } |
| + |
| + if (arguments.positional.length < minPositional) { |
| + String phrase = pluralize(minPositional, 'argument', 'arguments'); |
| + 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 = pluralize(maxPositional, 'argument', 'arguments'); |
| + 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(); |
| + } |
| + |
| + dynamic _foreignConstantEnumArgument( |
|
Harry Terkelsen
2016/10/12 16:42:59
this seems unused?
sra1
2016/10/13 19:02:01
Removed
|
| + ir.StaticInvocation invocation, int position, String methodName, |
| + [String adjective = '']) { |
| + ir.Expression argument = invocation.arguments.positional[position]; |
| + argument.accept(this); |
| + HInstruction instruction = pop(); |
| + |
| + if (!instruction.isConstant()) { |
| + compiler.reporter.reportErrorMessage( |
| + astAdapter.getNode(argument), MessageKind.GENERIC, { |
| + 'text': "Error: Expected String constant as ${adjective}argument " |
|
Harry Terkelsen
2016/10/12 16:42:59
should it be 'Expected enum constant ...'?
sra1
2016/10/13 19:02:01
Acknowledged.
|
| + "to '$methodName'." |
| + }); |
| + return null; |
| + } |
| + |
| + HConstant hConstant = instruction; |
| + StringConstantValue stringConstant = hConstant.constant; |
|
Harry Terkelsen
2016/10/12 16:42:59
should this be an EnumConstantValue?
sra1
2016/10/13 19:02:01
Acknowledged.
|
| + 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) { |
| + unhandledForeign(invocation); |
| + } |
| + |
| + 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, 'JS_SET_STATIC_STATE', 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, 'JS_GET_STATIC_STATE', 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, 'JS_GET_NAME', 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, 'JS_EMBEDDED_GLOBAL', 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, 'JS_BUILTIN', 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, 'JS_GET_FLAG', 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, 'JS', 2)) { |
| + stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
| + return; |
| + } |
| + |
| + native.NativeBehavior nativeBehavior = |
| + astAdapter.getNativeBehavior(invocation); |
| + assert(nativeBehavior != null); |
|
Harry Terkelsen
2016/10/12 16:42:59
remove this assert since it makes the next one unr
sra1
2016/10/13 19:02:01
Done.
|
| + 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; |
| + if (nativeBehavior.codeTemplate.isExpression) { |
|
Harry Terkelsen
2016/10/12 16:42:58
maybe rewrite this entire if statement as:
push(n
sra1
2016/10/13 19:02:01
Done.
|
| + push(new HForeignCode(nativeBehavior.codeTemplate, ssaType, inputs, |
| + effects: nativeBehavior.sideEffects, nativeBehavior: nativeBehavior) |
| + ..sourceInformation = sourceInformation); |
| + } else { |
| + push(new HForeignCode(nativeBehavior.codeTemplate, ssaType, inputs, |
| + isStatement: true, |
| + effects: nativeBehavior.sideEffects, |
| + nativeBehavior: nativeBehavior) |
| + ..sourceInformation = sourceInformation); |
| + } |
| + } |
| + |
| + void handleJsStringConcat(ir.StaticInvocation invocation) { |
| + if (_unexpectedForeignArguments(invocation, 'JS_STRING_CONCAT', 2)) { |
| + stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
| + return; |
| + } |
| + List<HInstruction> inputs = _visitArguments(invocation.arguments); |
| + assert(inputs.length == 2); |
| + push(new HStringConcat(inputs[0], inputs[1], backend.stringType)); |
| + } |
| + |
| void _pushStaticInvocation( |
| ir.Node target, List<HInstruction> arguments, TypeMask typeMask) { |
| HInstruction instruction = new HInvokeStatic( |