| Index: pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
|
| diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
|
| index 3bfeed456686a7f73bb206602e37e4d153e6f6f2..88373676a4111b53ab6a16f33315c9ffc173020b 100644
|
| --- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
|
| +++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
|
| @@ -20,6 +20,17 @@ import '../tree/tree.dart' as ast;
|
| import '../universe/universe.dart' show SelectorKind, CallStructure;
|
| import 'cps_ir_nodes.dart' as ir;
|
| import 'cps_ir_builder.dart';
|
| +import '../native/native.dart' show NativeBehavior;
|
| +
|
| +// TODO(karlklose): remove.
|
| +import '../js/js.dart' as js show js, Template, Expression;
|
| +import '../ssa/ssa.dart' show TypeMaskFactory;
|
| +import '../types/types.dart' show TypeMask;
|
| +import '../util/util.dart';
|
| +
|
| +import 'package:_internal/compiler/js_lib/shared/embedded_names.dart'
|
| + show JsBuiltin, JsGetName;
|
| +import '../constants/values.dart';
|
|
|
| typedef void IrBuilderCallback(Element element, ir.FunctionDefinition irNode);
|
|
|
| @@ -997,15 +1008,7 @@ abstract class IrBuilderVisitor extends ast.Visitor<ir.Primitive>
|
| MethodElement function,
|
| ast.NodeList arguments,
|
| CallStructure callStructure,
|
| - _) {
|
| - // TODO(karlklose): support foreign functions.
|
| - if (compiler.backend.isForeign(function)) {
|
| - return giveup(node, 'handleStaticFunctionInvoke: foreign: $function');
|
| - }
|
| - return irBuilder.buildStaticFunctionInvocation(function, callStructure,
|
| - translateStaticArguments(arguments, function, callStructure),
|
| - sourceInformation: sourceInformationBuilder.buildCall(node));
|
| - }
|
| + _);
|
|
|
| @override
|
| ir.Primitive handleStaticFunctionIncompatibleInvoke(
|
| @@ -1864,7 +1867,7 @@ abstract class IrBuilderVisitor extends ast.Visitor<ir.Primitive>
|
| }
|
| }
|
|
|
| - void internalError(ast.Node node, String message) {
|
| + internalError(ast.Node node, String message) {
|
| giveup(node, message);
|
| }
|
|
|
| @@ -2059,6 +2062,10 @@ class GlobalProgramInformation {
|
| ClassElement get nullClass => _compiler.nullClass;
|
|
|
| DartType unaliasType(DartType type) => type.unalias(_compiler);
|
| +
|
| + TypeMask getTypeMaskForForeign(NativeBehavior behavior) {
|
| + return TypeMaskFactory.fromNativeBehavior(behavior, _compiler);
|
| + }
|
| }
|
|
|
| /// IR builder specific to the JavaScript backend, coupled to the [JsIrBuilder].
|
| @@ -2810,6 +2817,232 @@ class JsIrBuilderVisitor extends IrBuilderVisitor {
|
| return irBuilder.buildStaticFieldGet(field, src);
|
| }
|
| }
|
| +
|
| + /// Build code to handle foreign code, that is, native JavaScript code, or
|
| + /// builtin values and operations of the backend.
|
| + ir.Primitive handleForeignCode(ast.Send node,
|
| + MethodElement function,
|
| + ast.NodeList argumentList,
|
| + CallStructure callStructure) {
|
| +
|
| + void validateArgumentCount({int minimum, int exactly}) {
|
| + assert((minimum == null) != (exactly == null));
|
| + int count = 0;
|
| + int maximum;
|
| + if (exactly != null) {
|
| + minimum = exactly;
|
| + maximum = exactly;
|
| + }
|
| + for (ast.Node argument in argumentList) {
|
| + count++;
|
| + if (maximum != null && count > maximum) {
|
| + internalError(argument, 'Additional argument.');
|
| + }
|
| + }
|
| + if (count < minimum) {
|
| + internalError(node, 'Expected at least $minimum arguments.');
|
| + }
|
| + }
|
| +
|
| + /// Call a helper method from the isolate library. The isolate library uses
|
| + /// its own isolate structure, that encapsulates dart2js's isolate.
|
| + ir.Primitive buildIsolateHelperInvocation(String helperName,
|
| + CallStructure callStructure) {
|
| + Element element = backend.isolateHelperLibrary.find(helperName);
|
| + if (element == null) {
|
| + compiler.internalError(node,
|
| + 'Isolate library and compiler mismatch.');
|
| + }
|
| + List<ir.Primitive> arguments = translateStaticArguments(argumentList,
|
| + element, CallStructure.TWO_ARGS);
|
| + return irBuilder.buildStaticFunctionInvocation(element,
|
| + CallStructure.TWO_ARGS, arguments,
|
| + sourceInformation: sourceInformationBuilder.buildCall(node));
|
| + }
|
| +
|
| + /// Lookup the value of the enum described by [node].
|
| + getEnumValue(ast.Node node, EnumClassElement enumClass, List values) {
|
| + Element element = elements[node];
|
| + if (element is! FieldElement || element.enclosingClass != enumClass) {
|
| + internalError(node, 'expected a JsBuiltin enum value');
|
| + }
|
| +
|
| + int index = enumClass.enumValues.indexOf(element);
|
| + return values[index];
|
| + }
|
| +
|
| + /// Returns the String the node evaluates to, or throws an error if the
|
| + /// result is not a string constant.
|
| + String expectStringConstant(ast.Node node) {
|
| + ir.Primitive nameValue = visit(node);
|
| + if (nameValue is ir.Constant && nameValue.value.isString) {
|
| + StringConstantValue constantValue = nameValue.value;
|
| + return constantValue.primitiveValue.slowToString();
|
| + } else {
|
| + return internalError(node, 'expected a literal string');
|
| + }
|
| + }
|
| +
|
| + Link<ast.Node> argumentNodes = argumentList.nodes;
|
| + NativeBehavior behavior =
|
| + compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node);
|
| + switch (function.name) {
|
| + case 'JS':
|
| + validateArgumentCount(minimum: 2);
|
| + // The first two arguments are the type and the foreign code template,
|
| + // which already have been analyzed by the resolver and can be retrieved
|
| + // using [NativeBehavior]. We can ignore these arguments in the backend.
|
| + List<ir.Primitive> arguments =
|
| + argumentNodes.skip(2).mapToList(visit, growable: false);
|
| + return irBuilder.buildForeignCode(behavior.codeTemplate, arguments,
|
| + behavior);
|
| +
|
| + case 'DART_CLOSURE_TO_JS':
|
| + // TODO(ahe): This should probably take care to wrap the closure in
|
| + // another closure that saves the current isolate.
|
| + case 'RAW_DART_FUNCTION_REF':
|
| + validateArgumentCount(exactly: 1);
|
| +
|
| + ast.Node argument = node.arguments.single;
|
| + FunctionElement closure = elements[argument].implementation;
|
| + if (!Elements.isStaticOrTopLevelFunction(closure)) {
|
| + internalError(argument,
|
| + 'only static or toplevel function supported');
|
| + }
|
| + if (closure.functionSignature.hasOptionalParameters) {
|
| + internalError(argument,
|
| + 'closures with optional parameters not supported');
|
| + }
|
| + return irBuilder.buildForeignCode(
|
| + js.js.expressionTemplateYielding(
|
| + backend.emitter.staticFunctionAccess(function)),
|
| + <ir.Primitive>[],
|
| + NativeBehavior.PURE,
|
| + dependency: closure);
|
| +
|
| + case 'JS_BUILTIN':
|
| + // The first argument is a description of the type and effect of the
|
| + // builtin, which has already been analyzed in the frontend. The second
|
| + // argument must be a [JsBuiltin] value. All other arguments are
|
| + // values used by the JavaScript template that is associated with the
|
| + // builtin.
|
| + validateArgumentCount(minimum: 2);
|
| +
|
| + ast.Node builtin = argumentNodes.tail.head;
|
| + JsBuiltin value = getEnumValue(argumentNodes.tail.head,
|
| + backend.jsBuiltinEnum, JsBuiltin.values);
|
| + js.Template template = backend.emitter.builtinTemplateFor(value);
|
| + List<ir.Primitive> arguments =
|
| + argumentNodes.skip(2).mapToList(visit, growable: false);
|
| + return irBuilder.buildForeignCode(template, arguments, behavior);
|
| +
|
| + case 'JS_EMBEDDED_GLOBAL':
|
| + validateArgumentCount(exactly: 2);
|
| +
|
| + String name = expectStringConstant(argumentNodes.tail.head);
|
| + js.Expression access =
|
| + backend.emitter.generateEmbeddedGlobalAccess(name);
|
| + js.Template template = js.js.expressionTemplateYielding(access);
|
| + return irBuilder.buildForeignCode(template, <ir.Primitive>[], behavior);
|
| +
|
| + case 'JS_INTERCEPTOR_CONSTANT':
|
| + validateArgumentCount(exactly: 1);
|
| +
|
| + ast.Node argument = argumentNodes.head;
|
| + ir.Primitive argumentValue = visit(argument);
|
| + if (argumentValue is ir.Constant && argumentValue.value.isType) {
|
| + TypeConstantValue constant = argumentValue.value;
|
| + ConstantValue interceptorValue =
|
| + new InterceptorConstantValue(constant.representedType);
|
| + return irBuilder.buildConstant(argumentValue.expression,
|
| + interceptorValue);
|
| + } else {
|
| + internalError(argument, 'expected Type as argument');
|
| + }
|
| + break;
|
| +
|
| + case 'JS_EFFECT':
|
| + return irBuilder.buildNullConstant();
|
| +
|
| + case 'JS_GET_NAME':
|
| + validateArgumentCount(exactly: 1);
|
| +
|
| + ast.Node argument = argumentNodes.head;
|
| + JsGetName id = getEnumValue(argument, backend.jsGetNameEnum,
|
| + JsGetName.values);
|
| + String name = backend.namer.getNameForJsGetName(argument, id);
|
| + return irBuilder.buildStringConstant(name);
|
| +
|
| + case 'JS_GET_FLAG':
|
| + validateArgumentCount(exactly: 1);
|
| +
|
| + String name = expectStringConstant(argumentNodes.first);
|
| + bool value = false;
|
| + switch (name) {
|
| + case 'MUST_RETAIN_METADATA':
|
| + value = backend.mustRetainMetadata;
|
| + break;
|
| + case 'USE_CONTENT_SECURITY_POLICY':
|
| + value = compiler.useContentSecurityPolicy;
|
| + break;
|
| + default:
|
| + internalError(node, 'Unknown internal flag "$name".');
|
| + }
|
| + return irBuilder.buildBooleanConstant(value);
|
| +
|
| + case 'JS_STRING_CONCAT':
|
| + validateArgumentCount(exactly: 2);
|
| + List<ir.Primitive> arguments = argumentNodes.mapToList(visit);
|
| + return irBuilder.buildStringConcatenation(arguments);
|
| +
|
| + case 'JS_CURRENT_ISOLATE_CONTEXT':
|
| + validateArgumentCount(exactly: 0);
|
| +
|
| + if (!compiler.hasIsolateSupport) {
|
| + // If the isolate library is not used, we just generate code
|
| + // to fetch the current isolate.
|
| + String name = backend.namer.currentIsolate;
|
| + return irBuilder.buildForeignCode(js.js.parseForeignJS(name),
|
| + const <ir.Primitive>[], NativeBehavior.PURE);
|
| + } else {
|
| + return buildIsolateHelperInvocation('_currentIsolate',
|
| + CallStructure.NO_ARGS);
|
| + }
|
| + break;
|
| +
|
| + case 'JS_CALL_IN_ISOLATE':
|
| + validateArgumentCount(exactly: 2);
|
| +
|
| + if (!compiler.hasIsolateSupport) {
|
| + ir.Primitive closure = visit(argumentNodes.tail.head);
|
| + return irBuilder.buildCallInvocation(closure, CallStructure.NO_ARGS,
|
| + const <ir.Primitive>[]);
|
| + } else {
|
| + return buildIsolateHelperInvocation('_callInIsolate',
|
| + CallStructure.TWO_ARGS);
|
| + }
|
| + break;
|
| +
|
| + default:
|
| + giveup(node, 'unplemented native construct: ${function.name}');
|
| + break;
|
| + }
|
| + }
|
| +
|
| + @override
|
| + ir.Primitive handleStaticFunctionInvoke(ast.Send node,
|
| + MethodElement function,
|
| + ast.NodeList argumentList,
|
| + CallStructure callStructure,
|
| + _) {
|
| + if (compiler.backend.isForeign(function)) {
|
| + return handleForeignCode(node, function, argumentList, callStructure);
|
| + } else {
|
| + return irBuilder.buildStaticFunctionInvocation(function, callStructure,
|
| + translateStaticArguments(argumentList, function, callStructure),
|
| + sourceInformation: sourceInformationBuilder.buildCall(node));
|
| + }
|
| + }
|
| }
|
|
|
| /// Perform simple post-processing on the initial CPS-translated root term.
|
|
|