Chromium Code Reviews| 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. |