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 1ab44e7146e0fd59f4417f5c15b9484bb12c4b25..6669204f210a1011f13afe70829caa2db39005d8 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 |
@@ -21,6 +21,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); |
@@ -994,15 +1005,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( |
@@ -1861,7 +1864,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); |
} |
@@ -2054,6 +2057,10 @@ class GlobalProgramInformation { |
FunctionElement get throwTypeErrorHelper => _backend.getThrowTypeError(); |
ClassElement get nullClass => _compiler.nullClass; |
+ |
+ TypeMask getTypeMaskForForeign(NativeBehavior behavior) { |
+ return TypeMaskFactory.fromNativeBehavior(behavior, _compiler); |
+ } |
} |
/// IR builder specific to the JavaScript backend, coupled to the [JsIrBuilder]. |
@@ -2805,6 +2812,233 @@ 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 handleForeignSend(ast.Send node, |
+ MethodElement function, |
+ Link<ast.Node> argumentNodes, |
+ CallStructure callStructure) { |
+ |
+ void validateArgumentCount({int minimum, int exactly}) { |
+ assert((minimum == null) != (exactly == null)); |
+ int count = 0; |
+ if (exactly != null) { |
+ minimum = exactly; |
+ } |
+ for (Link<ast.Node> arguments = argumentNodes; |
Kevin Millikin (Google)
2015/06/16 11:23:10
I'd make argumentNodes a NodeList (to avoid exposi
karlklose
2015/06/18 09:38:14
Done.
|
+ !arguments.isEmpty; |
+ arguments = arguments.tail) { |
+ count++; |
+ if (exactly != null && count > exactly) { |
+ internalError(arguments.head, 'Additional argument.'); |
+ } |
+ } |
+ if (minimum != null && count < minimum) { |
Kevin Millikin (Google)
2015/06/16 11:23:10
minimum should always be non-null.
karlklose
2015/06/18 09:38:14
Done.
|
+ 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, |
+ Link<ast.Node> argumentNodes) { |
Kevin Millikin (Google)
2015/06/16 11:23:10
argumentNodes is unused in this function.
karlklose
2015/06/18 09:38:14
Done.
|
+ Element element = backend.isolateHelperLibrary.find(helperName); |
+ if (element == null) { |
+ compiler.internalError(node, |
+ 'Isolate library and compiler mismatch.'); |
+ } |
+ List<ir.Primitive> arguments = translateStaticArguments( |
+ node.argumentsNode, element, CallStructure.TWO_ARGS); |
Kevin Millikin (Google)
2015/06/16 11:23:10
This uses the arguments NodeList off the Send. It
karlklose
2015/06/18 09:38:14
Done.
|
+ 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'); |
+ } |
+ } |
+ |
+ 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, argumentNodes); |
+ } |
+ 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, argumentNodes); |
+ } |
+ 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 handleForeignSend(node, function, argumentList.nodes, |
Kevin Millikin (Google)
2015/06/16 11:23:10
"Send" seems weird here. handleForeignCode?
karlklose
2015/06/18 09:38:14
Done.
|
+ 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. |