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