| 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(
|
|
|