Chromium Code Reviews| Index: pkg/compiler/lib/src/cps_ir/cps_ir_builder_visitor.dart |
| diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_visitor.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_visitor.dart |
| index 0c504b75ee41f8910058b3452ccd535187ce0d05..47873c79ad2bcf8f9ffda9adeac21d040e1bb7ae 100644 |
| --- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_visitor.dart |
| +++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_visitor.dart |
| @@ -42,7 +42,9 @@ class IrBuilderTask extends CompilerTask { |
| return compiler.withCurrentElement(element, () { |
| SourceFile sourceFile = elementSourceFile(element); |
| IrBuilderVisitor builder = |
| - new IrBuilderVisitor(elementsMapping, compiler, sourceFile); |
| + compiler.backend is JavaScriptBackend |
| + ? new JsIrBuilderVisitor(elementsMapping, compiler, sourceFile) |
| + : new DartIrBuilderVisitor(elementsMapping, compiler, sourceFile); |
| return builder.buildExecutable(element); |
| }); |
| } |
| @@ -104,11 +106,10 @@ class _GetterElements { |
| * to the [builder] and return the last added statement for trees that represent |
| * an expression. |
| */ |
| -class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive> |
| +abstract class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive> |
| with IrBuilderMixin<ast.Node> { |
| final Compiler compiler; |
| final SourceFile sourceFile; |
| - ClosureClassMap closureMap; |
| // In SSA terms, join-point continuation parameters are the phis and the |
| // continuation invocation arguments are the corresponding phi inputs. To |
| @@ -132,119 +133,17 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive> |
| IrBuilderVisitor(TreeElements elements, this.compiler, this.sourceFile) |
| : super(elements); |
| - /// True if using the JavaScript backend; we use this to determine how |
| - /// closures should be translated. |
| - bool get isJavaScriptBackend => compiler.backend is JavaScriptBackend; |
| - |
| /** |
| * Builds the [ir.ExecutableDefinition] for an executable element. In case the |
| * function uses features that cannot be expressed in the IR, this element |
| * returns `null`. |
| */ |
| - ir.ExecutableDefinition buildExecutable(ExecutableElement element) { |
| - return nullIfGiveup(() { |
| - if (element is FieldElement) { |
| - return buildField(element); |
| - } else if (element is FunctionElement) { |
| - return buildFunction(element); |
| - } else { |
| - compiler.internalError(element, "Unexpected element type $element"); |
| - } |
| - }); |
| - } |
| + ir.ExecutableDefinition buildExecutable(ExecutableElement element); |
| - Map mapValues(Map map, dynamic fn(dynamic)) { |
| - Map result = {}; |
| - map.forEach((key,value) { |
| - result[key] = fn(value); |
| - }); |
| - return result; |
| - } |
| + ClosureScope getClosureScope(ast.Node node); |
| + ClosureEnvironment getClosureEnvironment(); |
| - // Converts closure.dart's CapturedVariable into a ClosureLocation. |
| - // There is a 1:1 corresponce between these; we do this because the IR builder |
| - // should not depend on synthetic elements. |
| - ClosureLocation getLocation(CapturedVariable v) { |
| - if (v is BoxFieldElement) { |
| - return new ClosureLocation(v.box, v); |
| - } else { |
| - ClosureFieldElement field = v; |
| - return new ClosureLocation(null, field); |
| - } |
| - } |
| - /// If the current function is a nested function with free variables (or a |
| - /// captured reference to `this`), this returns a [ClosureEnvironment] |
| - /// indicating how to access these. |
| - ClosureEnvironment getClosureEnvironment() { |
| - if (closureMap == null) return null; // dart2dart does not use closureMap. |
| - if (closureMap.closureElement == null) return null; |
| - return new ClosureEnvironment( |
| - closureMap.closureElement, |
| - closureMap.thisLocal, |
| - mapValues(closureMap.freeVariableMap, getLocation)); |
| - } |
| - |
| - /// If [node] has declarations for variables that should be boxed, this |
| - /// returns a [ClosureScope] naming a box to create, and enumerating the |
| - /// variables that should be stored in the box. |
| - /// |
| - /// Also see [ClosureScope]. |
| - ClosureScope getClosureScope(ast.Node node) { |
| - if (closureMap == null) return null; // dart2dart does not use closureMap. |
| - closurelib.ClosureScope scope = closureMap.capturingScopes[node]; |
| - if (scope == null) return null; |
| - // We translate a ClosureScope from closure.dart into IR builder's variant |
| - // because the IR builder should not depend on the synthetic elements |
| - // created in closure.dart. |
| - return new ClosureScope(scope.boxElement, |
| - mapValues(scope.capturedVariables, getLocation), |
| - scope.boxedLoopVariables); |
| - } |
| - |
| - IrBuilder makeIRBuilder(ast.Node node, ExecutableElement element) { |
| - if (isJavaScriptBackend) { |
| - closureMap = compiler.closureToClassMapper.computeClosureToClassMapping( |
| - element, |
| - node, |
| - elements); |
| - return new JsIrBuilder(compiler.backend.constantSystem, element); |
| - } else { |
| - DetectClosureVariables closures = new DetectClosureVariables(elements); |
| - if (!element.isSynthesized) { |
| - closures.visit(node); |
| - } |
| - return new DartIrBuilder(compiler.backend.constantSystem, |
| - element, |
| - closures); |
| - } |
| - } |
| - |
| - /// Returns a [ir.FieldDefinition] describing the initializer of [element]. |
| - ir.FieldDefinition buildField(FieldElement element) { |
| - assert(invariant(element, element.isImplementation)); |
| - ast.VariableDefinitions definitions = element.node; |
| - ast.Node fieldDefinition = |
| - definitions.definitions.nodes.first; |
| - if (definitions.modifiers.isConst) { |
| - // TODO(sigurdm): Just return const value. |
| - } |
| - assert(fieldDefinition != null); |
| - assert(elements[fieldDefinition] != null); |
| - |
| - IrBuilder builder = makeIRBuilder(fieldDefinition, element); |
| - |
| - return withBuilder(builder, () { |
| - builder.buildFieldInitializerHeader( |
| - closureScope: getClosureScope(fieldDefinition)); |
| - ir.Primitive initializer; |
| - if (fieldDefinition is ast.SendSet) { |
| - ast.SendSet sendSet = fieldDefinition; |
| - initializer = visit(sendSet.arguments.first); |
| - } |
| - return builder.makeFieldDefinition(initializer); |
| - }); |
| - } |
| ir.FunctionDefinition _makeFunctionBody(FunctionElement element, |
| ast.FunctionExpression node) { |
| @@ -352,28 +251,6 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive> |
| return result; |
| } |
| - ir.FunctionDefinition buildFunction(FunctionElement element) { |
| - assert(invariant(element, element.isImplementation)); |
| - ast.FunctionExpression node = element.node; |
| - |
| - Iterable<Entity> usedFromClosure; |
| - if (!element.isSynthesized) { |
| - assert(node != null); |
| - assert(elements[node] != null); |
| - } else { |
| - SynthesizedConstructorElementX constructor = element; |
| - if (!constructor.isDefaultConstructor) { |
| - giveup(null, 'cannot handle synthetic forwarding constructors'); |
| - } |
| - |
| - usedFromClosure = <Entity>[]; |
| - } |
| - |
| - IrBuilder builder = makeIRBuilder(node, element); |
| - |
| - return withBuilder(builder, () => _makeFunctionBody(element, node)); |
| - } |
| - |
| ir.Primitive visit(ast.Node node) => node.accept(this); |
| // ==== Statements ==== |
| @@ -1018,33 +895,6 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive> |
| return irBuilder.buildConstantLiteral(constant); |
| } |
| - /// Returns the backend-specific representation of an inner function. |
| - Object makeSubFunction(ast.FunctionExpression node) { |
| - if (isJavaScriptBackend) { |
| - ClosureClassMap innerMap = |
| - compiler.closureToClassMapper.getMappingForNestedFunction(node); |
| - ClosureClassElement closureClass = innerMap.closureClassElement; |
| - return closureClass; |
| - } else { |
| - FunctionElement element = elements[node]; |
| - assert(invariant(element, element.isImplementation)); |
| - |
| - IrBuilder builder = irBuilder.makeInnerFunctionBuilder(element); |
| - |
| - return withBuilder(builder, () => _makeFunctionBody(element, node)); |
| - } |
| - } |
| - |
| - ir.Primitive visitFunctionExpression(ast.FunctionExpression node) { |
| - return irBuilder.buildFunctionExpression(makeSubFunction(node)); |
| - } |
| - |
| - visitFunctionDeclaration(ast.FunctionDeclaration node) { |
| - LocalFunctionElement element = elements[node.function]; |
| - Object inner = makeSubFunction(node.function); |
| - irBuilder.declareLocalFunction(element, inner); |
| - } |
| - |
| ir.ExecutableDefinition nullIfGiveup(ir.ExecutableDefinition action()) { |
| try { |
| return action(); |
| @@ -1158,3 +1008,533 @@ class DetectClosureVariables extends ast.Visitor |
| currentFunction = oldFunction; |
| } |
| } |
| + |
| +/// IR builder specific to the Dart backend, coupled to the [DartIrBuilder]. |
| +class DartIrBuilderVisitor extends IrBuilderVisitor { |
| + /// Promote the type of [irBuilder] to [DartIrBuilder]. |
| + DartIrBuilder get irBuilder => super.irBuilder; |
| + |
| + DartIrBuilderVisitor(TreeElements elements, |
| + Compiler compiler, |
| + SourceFile sourceFile) |
| + : super(elements, compiler, sourceFile); |
| + |
| + DartIrBuilder makeIRBuilder(ast.Node node, ExecutableElement element) { |
| + DetectClosureVariables closures = new DetectClosureVariables(elements); |
| + if (!element.isSynthesized) { |
| + closures.visit(node); |
| + } |
| + return new DartIrBuilder(compiler.backend.constantSystem, |
| + element, |
| + closures); |
| + } |
| + |
| + /// Returns the backend-specific representation of an inner function. |
|
floitsch
2015/01/20 17:26:27
That comment can be updated.
|
| + ir.FunctionDefinition makeSubFunction(ast.FunctionExpression node) { |
| + FunctionElement element = elements[node]; |
| + assert(invariant(element, element.isImplementation)); |
| + |
| + IrBuilder builder = irBuilder.makeInnerFunctionBuilder(element); |
| + |
| + return withBuilder(builder, () => _makeFunctionBody(element, node)); |
| + } |
| + |
| + ir.Primitive visitFunctionExpression(ast.FunctionExpression node) { |
| + return irBuilder.buildFunctionExpression(makeSubFunction(node)); |
| + } |
| + |
| + visitFunctionDeclaration(ast.FunctionDeclaration node) { |
| + LocalFunctionElement element = elements[node.function]; |
| + Object inner = makeSubFunction(node.function); |
| + irBuilder.declareLocalFunction(element, inner); |
| + } |
| + |
| + ClosureScope getClosureScope(ast.Node node) => null; |
| + ClosureEnvironment getClosureEnvironment() => null; |
| + |
| + ir.ExecutableDefinition buildExecutable(ExecutableElement element) { |
| + return nullIfGiveup(() { |
| + if (element is FieldElement) { |
| + return buildField(element); |
| + } else if (element is FunctionElement) { |
| + return buildFunction(element); |
| + } else { |
| + compiler.internalError(element, "Unexpected element type $element"); |
| + } |
| + }); |
| + } |
| + |
| + /// Returns a [ir.FieldDefinition] describing the initializer of [element]. |
| + ir.FieldDefinition buildField(FieldElement element) { |
| + assert(invariant(element, element.isImplementation)); |
| + ast.VariableDefinitions definitions = element.node; |
| + ast.Node fieldDefinition = |
|
floitsch
2015/01/20 17:26:27
should fit on one line, no?
|
| + definitions.definitions.nodes.first; |
| + if (definitions.modifiers.isConst) { |
| + // TODO(sigurdm): Just return const value. |
| + } |
| + assert(fieldDefinition != null); |
| + assert(elements[fieldDefinition] != null); |
| + |
| + IrBuilder builder = makeIRBuilder(fieldDefinition, element); |
| + |
| + return withBuilder(builder, () { |
| + builder.buildFieldInitializerHeader( |
| + closureScope: getClosureScope(fieldDefinition)); |
| + ir.Primitive initializer; |
| + if (fieldDefinition is ast.SendSet) { |
| + ast.SendSet sendSet = fieldDefinition; |
| + initializer = visit(sendSet.arguments.first); |
| + } |
| + return builder.makeFieldDefinition(initializer); |
| + }); |
| + } |
| + |
| + ir.FunctionDefinition buildFunction(FunctionElement element) { |
| + assert(invariant(element, element.isImplementation)); |
| + ast.FunctionExpression node = element.node; |
| + |
| + if (!element.isSynthesized) { |
| + assert(node != null); |
| + assert(elements[node] != null); |
| + } else { |
| + SynthesizedConstructorElementX constructor = element; |
| + if (!constructor.isDefaultConstructor) { |
| + giveup(null, 'cannot handle synthetic forwarding constructors'); |
| + } |
| + } |
| + |
| + IrBuilder builder = makeIRBuilder(node, element); |
| + |
| + return withBuilder(builder, () => _makeFunctionBody(element, node)); |
| + } |
| +} |
| + |
| +/// IR builder specific to the JavaScript backend, coupled to the [JsIrBuilder]. |
| +class JsIrBuilderVisitor extends IrBuilderVisitor { |
| + /// Promote the type of [irBuilder] to [JsIrBuilder]. |
| + JsIrBuilder get irBuilder => super.irBuilder; |
| + ClosureClassMap closureMap; |
| + |
| + JsIrBuilderVisitor(TreeElements elements, |
| + Compiler compiler, |
| + SourceFile sourceFile) |
| + : super(elements, compiler, sourceFile); |
| + |
| + JsIrBuilder makeIRBuilder(ast.Node node, ExecutableElement element) { |
| + closureMap = compiler.closureToClassMapper.computeClosureToClassMapping( |
| + element, |
| + node, |
| + elements); |
| + return new JsIrBuilder(compiler.backend.constantSystem, element); |
| + } |
| + |
| + /// Returns the backend-specific representation of an inner function. |
|
floitsch
2015/01/20 17:26:27
Update comment.
|
| + ClosureClassElement makeSubFunction(ast.FunctionExpression node) { |
| + ClosureClassMap innerMap = |
| + compiler.closureToClassMapper.getMappingForNestedFunction(node); |
| + ClosureClassElement closureClass = innerMap.closureClassElement; |
| + return closureClass; |
| + } |
| + |
| + ir.Primitive visitFunctionExpression(ast.FunctionExpression node) { |
| + return irBuilder.buildFunctionExpression(makeSubFunction(node)); |
| + } |
| + |
| + visitFunctionDeclaration(ast.FunctionDeclaration node) { |
| + LocalFunctionElement element = elements[node.function]; |
| + Object inner = makeSubFunction(node.function); |
| + irBuilder.declareLocalFunction(element, inner); |
| + } |
| + |
| + Map mapValues(Map map, dynamic fn(dynamic)) { |
| + Map result = {}; |
| + map.forEach((key,value) { |
| + result[key] = fn(value); |
| + }); |
| + return result; |
| + } |
| + |
| + // Converts closure.dart's CapturedVariable into a ClosureLocation. |
|
floitsch
2015/01/20 17:26:27
Make a dartdoc.
|
| + // There is a 1:1 corresponce between these; we do this because the IR builder |
| + // should not depend on synthetic elements. |
| + ClosureLocation getLocation(CapturedVariable v) { |
| + if (v is BoxFieldElement) { |
| + return new ClosureLocation(v.box, v); |
| + } else { |
| + ClosureFieldElement field = v; |
| + return new ClosureLocation(null, field); |
| + } |
| + } |
| + |
| + /// If the current function is a nested function with free variables (or a |
| + /// captured reference to `this`), this returns a [ClosureEnvironment] |
| + /// indicating how to access these. |
| + ClosureEnvironment getClosureEnvironment() { |
| + if (closureMap.closureElement == null) return null; |
| + return new ClosureEnvironment( |
| + closureMap.closureElement, |
| + closureMap.thisLocal, |
| + mapValues(closureMap.freeVariableMap, getLocation)); |
| + } |
| + |
| + /// If [node] has declarations for variables that should be boxed, this |
| + /// returns a [ClosureScope] naming a box to create, and enumerating the |
| + /// variables that should be stored in the box. |
| + /// |
| + /// Also see [ClosureScope]. |
| + ClosureScope getClosureScope(ast.Node node) { |
| + closurelib.ClosureScope scope = closureMap.capturingScopes[node]; |
| + if (scope == null) return null; |
| + // We translate a ClosureScope from closure.dart into IR builder's variant |
| + // because the IR builder should not depend on the synthetic elements |
| + // created in closure.dart. |
| + return new ClosureScope(scope.boxElement, |
| + mapValues(scope.capturedVariables, getLocation), |
| + scope.boxedLoopVariables); |
| + } |
| + |
| + /// Returns the [ClosureScope] for any function, possibly different from the |
| + /// one currently being built. |
| + ClosureScope getFunctionScope(FunctionElement function) { |
| + ClosureClassMap map = |
| + compiler.closureToClassMapper.computeClosureToClassMapping( |
| + function, |
| + function.node, |
| + elements); |
| + closurelib.ClosureScope scope = map.capturingScopes[function.node]; |
| + if (scope == null) return null; |
| + return new ClosureScope(scope.boxElement, |
| + mapValues(scope.capturedVariables, getLocation), |
| + scope.boxedLoopVariables); |
| + } |
| + |
| + ir.ExecutableDefinition buildExecutable(ExecutableElement element) { |
| + return nullIfGiveup(() { |
| + switch (element.kind) { |
| + case ElementKind.GENERATIVE_CONSTRUCTOR: |
| + return buildConstructor(element); |
| + |
| + case ElementKind.GENERATIVE_CONSTRUCTOR_BODY: |
| + return buildConstructorBody(element); |
| + |
| + case ElementKind.FUNCTION: |
| + case ElementKind.GETTER: |
| + case ElementKind.SETTER: |
| + return buildFunction(element); |
| + |
| + default: |
| + compiler.internalError(element, "Unexpected element type $element"); |
| + } |
| + }); |
| + } |
| + |
| + /// During construction of a constructor factory, [fieldValues] maps fields |
|
floitsch
2015/01/20 17:26:27
Move it up to the locals.
|
| + /// to the primitive containing their initial value. |
| + Map<FieldElement, ir.Primitive> fieldValues = {}; |
|
floitsch
2015/01/20 17:26:27
type the map.
|
| + |
| + /// Builds the IR for a given constructor. The performs the following tasks: |
| + /// |
| + /// 1. Evaluate all own or inherited field initializers. |
| + /// 2. Create the object and assign its fields. |
| + /// 3. Call constructor body and super constructor bodies. |
| + /// 4. Return the created object. |
| + ir.FunctionDefinition buildConstructor(ConstructorElement constructor) { |
| + constructor = constructor.implementation; |
| + ClassElement classElement = constructor.enclosingClass.implementation; |
| + |
| + closureMap = compiler.closureToClassMapper.computeClosureToClassMapping( |
| + constructor, |
| + constructor.node, |
| + elements); |
| + ClosureScope closureScope = getClosureScope(constructor.node); |
| + |
| + JsIrBuilder builder = |
| + new JsIrBuilder(compiler.backend.constantSystem, constructor); |
| + |
| + return withBuilder(builder, () { |
| + // Setup parameters and create a box if anything is captured. |
| + List<ParameterElement> parameters = []; |
| + constructor.functionSignature.orderedForEachParameter(parameters.add); |
| + List<ir.Primitive> constructorParams = |
| + builder.buildFunctionHeader(parameters, closureScope: closureScope); |
| + |
| + // -- Step 1: evaluate field initializers --- |
| + // Evaluate all initializers on from field declarations. |
|
floitsch
2015/01/20 17:26:27
-on-
|
| + // For some reason, these should ALL be evaluated first, even before |
|
floitsch
2015/01/20 17:26:27
I'm pretty sure this is not true.
I tried it with
asgerf
2015/01/21 09:55:58
You are right. The SSA builder had me confused, an
|
| + // field initializer lists from super constructors. |
| + classElement.forEachInstanceField( |
| + (ClassElement classElement, FieldElement field) { |
| + if (compiler.elementHasCompileTimeError(field)) return; |
| + if (field.initializer != null) { |
| + fieldValues[field] = visit(field.initializer); |
| + } else { |
| + if (Elements.isNativeOrExtendsNative(classElement)) { |
| + // Native field is initialized elsewhere. |
| + } else { |
| + // Fields without an initializer default to null. |
| + // This value will be overwritten later if an initializer |
| + // is found on one of the constructors. |
| + fieldValues[field] = irBuilder.buildNullLiteral(); |
| + } |
| + } |
| + }, includeSuperAndInjectedMembers: true); |
| + |
| + // Evaluate field initializers in constructor and super constructors. |
| + List<ConstructorElement> constructorList = <ConstructorElement>[]; |
| + evaluateConstructorFieldInitializers(constructor, constructorList); |
| + |
| + // All parameters in all constructors are now bound in the environment. |
| + // BoxLocals for captured parameters are also in the environment. |
| + // The initial value of all fields are now bound in [fieldValues]. |
| + |
| + // --- Step 2: create the object --- |
| + // Get the initial field values in the canonical order. |
| + List<ir.Primitive> instanceArguments = <ir.Primitive>[]; |
| + classElement.forEachInstanceField( |
| + (ClassElement classElement, FieldElement field) { |
| + ir.Primitive value = fieldValues[field]; |
| + if (value != null) { |
| + instanceArguments.add(fieldValues[field]); |
| + } else { |
| + // Native fields are initialized elsewhere. |
| + } |
| + }, includeSuperAndInjectedMembers: true); |
| + ir.Primitive instance = |
| + new ir.CreateInstance(classElement, instanceArguments); |
| + irBuilder.add(new ir.LetPrim(instance)); |
| + |
| + // --- Step 3: call constructor bodies --- |
| + for (ConstructorElement target in constructorList) { |
| + ConstructorBodyElement bodyElement = getConstructorBody(target); |
| + if (bodyElement == null) continue; |
| + List<ir.Primitive> bodyArguments = <ir.Primitive>[]; |
| + for (Local param in getConstructorBodyParameters(bodyElement)) { |
| + bodyArguments.add(irBuilder.environment.lookup(param)); |
| + } |
| + irBuilder.buildInvokeDirectly(bodyElement, instance, bodyArguments); |
| + } |
| + |
| + // --- step 4: return the created object ---- |
| + irBuilder.buildReturn(instance); |
| + |
| + return irBuilder.makeFunctionDefinition([]); |
| + }); |
| + } |
| + |
| + /// Evaluates all field initializers on [constructor] and all constructors |
| + /// invoked through `this()` or `super()` ("superconstructors"). |
| + /// |
| + /// The resulting field values will be available in [fieldValues]. The values |
| + /// are not stored in any fields. |
| + /// |
| + /// This procedure assumes that the parameters to [constructor] are available |
| + /// in the IR builder's environment. |
| + /// |
| + /// The parameters to superconstructors are, however, assumed NOT to be in |
| + /// the environment, but will be put there by this procedure. |
| + /// |
| + /// All constructors will be added to [supers], with superconstructors first. |
| + void evaluateConstructorFieldInitializers(ConstructorElement constructor, |
| + List<ConstructorElement> supers) { |
| + // Evaluate initializing parameters, e.g. `Foo(this.x)`. |
| + constructor.functionSignature.orderedForEachParameter( |
| + (ParameterElement parameter) { |
| + if (parameter.isInitializingFormal) { |
| + InitializingFormalElement fieldParameter = parameter; |
| + fieldValues[fieldParameter.fieldElement] = |
| + irBuilder.buildLocalGet(parameter); |
| + } |
| + }); |
| + // Evaluate constructor initializers, e.g. `Foo() : x = 50`. |
| + ast.FunctionExpression node = constructor.node; |
| + bool hasConstructorCall = false; // Has this() or super() initializer? |
| + if (node != null && node.initializers != null) { |
| + for(ast.Node initializer in node.initializers) { |
| + if (initializer is ast.SendSet) { |
| + // Field initializer. |
| + FieldElement field = elements[initializer]; |
| + fieldValues[field] = visit(initializer.arguments.head); |
| + } else if (initializer is ast.Send) { |
| + // Super or this initializer. |
| + ConstructorElement target = elements[initializer].implementation; |
| + Selector selector = elements.getSelector(initializer); |
| + List<ir.Primitive> arguments = |
| + initializer.arguments.mapToList(visit); |
| + loadArguments(target, selector, arguments); |
| + evaluateConstructorFieldInitializers(target, supers); |
| + hasConstructorCall = true; |
| + } else { |
| + compiler.internalError(initializer, |
| + "Unexpected initializer type $initializer"); |
| + } |
| + } |
| + } |
| + // If no super() or this() was found, also call default superconstructor. |
| + ClassElement enclosingClass = constructor.enclosingClass.implementation; |
| + if (!hasConstructorCall && !enclosingClass.isObject) { |
| + ClassElement superClass = enclosingClass.superclass; |
| + Selector selector = |
| + new Selector.callDefaultConstructor(enclosingClass.library); |
| + FunctionElement target = superClass.lookupConstructor(selector); |
| + if (target == null) { |
| + compiler.internalError(superClass, "No default constructor available."); |
| + } |
| + evaluateConstructorFieldInitializers(target, supers); |
| + } |
| + // Add this constructor after the superconstructors. |
| + supers.add(constructor); |
| + } |
| + |
| + /// In preparation of inlining (part of) [target], the [arguments] are moved |
| + /// into the environment bindings for the corresponding parameters. |
| + /// |
| + /// Defaults for optional arguments are evaluated in order to ensure |
| + /// all parameters are available in the environment. |
| + void loadArguments(FunctionElement target, |
| + Selector selector, |
| + List<ir.Primitive> arguments) { |
| + target = target.implementation; |
| + FunctionSignature signature = target.functionSignature; |
| + |
| + // Establish a scope in case parameters are captured. |
| + ClosureScope scope = getFunctionScope(target); |
| + irBuilder._enterScope(scope); |
| + |
| + // Load required parameters |
| + int index = 0; |
| + signature.forEachRequiredParameter((ParameterElement param) { |
| + irBuilder.declareLocalVariable(param, initialValue: arguments[index]); |
| + ++index; |
| + }); |
| + |
| + // Load optional parameters, evaluating default values for omitted ones. |
| + signature.forEachOptionalParameter((ParameterElement param) { |
| + ir.Primitive value; |
| + // Load argument if provided. |
| + if (signature.optionalParametersAreNamed) { |
| + int translatedIndex = selector.namedArguments.indexOf(param.name); |
| + if (translatedIndex != -1) { |
| + value = arguments[translatedIndex]; |
| + } |
| + } else if (index < arguments.length) { |
| + value = arguments[index]; |
| + } |
| + // Load default if argument was not provided. |
| + if (value == null) { |
| + if (param.initializer != null) { |
| + value = visit(param.initializer); |
| + } else { |
| + value = irBuilder.buildNullLiteral(); |
| + } |
| + } |
| + irBuilder.declareLocalVariable(param, initialValue: value); |
| + ++index; |
| + }); |
| + } |
| + |
| + /** |
| + * Returns the constructor body associated with the given constructor or |
| + * creates a new constructor body, if none can be found. |
| + * |
| + * Returns [:null:] if the constructor does not have a body. |
|
floitsch
2015/01/20 17:26:27
`null`
|
| + */ |
| + ConstructorBodyElement getConstructorBody(FunctionElement constructor) { |
| + // TODO(asgerf): This is largely inherited from the SSA builder. |
| + // The ConstructorBodyElement has an invalid function signature, but we |
| + // cannot add a BoxLocal as parameter, because BoxLocal is not an element. |
| + // Instead of forging ParameterElements to forge a FunctionSignature, we |
| + // need a way to create backend methods without creating more fake elements. |
| + |
| + assert(constructor.isGenerativeConstructor); |
| + assert(invariant(constructor, constructor.isImplementation)); |
| + if (constructor.isSynthesized) return null; |
| + ast.FunctionExpression node = constructor.node; |
| + // If we know the body doesn't have any code, we don't generate it. |
| + if (!node.hasBody()) return null; |
| + if (node.hasEmptyBody()) return null; |
| + ClassElement classElement = constructor.enclosingClass; |
| + ConstructorBodyElement bodyElement; |
| + classElement.forEachBackendMember((Element backendMember) { |
| + if (backendMember.isGenerativeConstructorBody) { |
| + ConstructorBodyElement body = backendMember; |
| + if (body.constructor == constructor) { |
| + bodyElement = backendMember; |
| + } |
| + } |
| + }); |
| + if (bodyElement == null) { |
| + List<Local> parameters = getConstructorBodyParameters(constructor); |
| + |
| + bodyElement = new ConstructorBodyElementX(constructor); |
| + classElement.addBackendMember(bodyElement); |
| + |
| + if (constructor.isPatch) { |
| + // Create origin body element for patched constructors. |
| + ConstructorBodyElementX patch = bodyElement; |
| + ConstructorBodyElementX origin = |
| + new ConstructorBodyElementX(constructor.origin); |
| + origin.applyPatch(patch); |
| + classElement.origin.addBackendMember(bodyElement.origin); |
| + } |
| + } |
| + assert(bodyElement.isGenerativeConstructorBody); |
| + return bodyElement; |
| + } |
| + |
| + /// The list of parameters to send from the generative constructor |
| + /// to the generative constructor body. |
| + List<Local> getConstructorBodyParameters(FunctionElement constructor) { |
| + List<Local> parameters = <Local>[]; |
| + ClosureScope scope = getFunctionScope(constructor); |
| + if (scope != null) { |
| + parameters.add(scope.box); |
| + } |
| + constructor.functionSignature.orderedForEachParameter( |
| + (ParameterElement param) { |
| + if (scope != null && scope.capturedVariables.containsKey(param)) { |
| + // Do not pass this parameter; the box will carry its value. |
| + } else { |
| + parameters.add(param); |
| + } |
| + }); |
| + return parameters; |
| + } |
| + |
| + /// Builds the IR for the body of a constructor. |
| + /// |
| + /// This function is invoked from one or more "factory" constructors built by |
| + /// [buildConstructor]. |
| + ir.FunctionDefinition buildConstructorBody(ConstructorBodyElement body) { |
| + ConstructorElement constructor = body.constructor; |
| + ast.FunctionExpression node = constructor.node; |
| + closureMap = compiler.closureToClassMapper.computeClosureToClassMapping( |
| + constructor, |
| + node, |
| + elements); |
| + |
| + JsIrBuilder builder = new JsIrBuilder( |
| + compiler.backend.constantSystem, body); |
| + |
| + return withBuilder(builder, () { |
| + irBuilder.buildConstructorBodyHeader(getConstructorBodyParameters(body), |
| + getClosureScope(node)); |
| + visit(node.body); |
| + return irBuilder.makeFunctionDefinition([]); |
| + }); |
| + } |
| + |
| + ir.FunctionDefinition buildFunction(FunctionElement element) { |
| + assert(invariant(element, element.isImplementation)); |
| + ast.FunctionExpression node = element.node; |
| + |
| + assert(!element.isSynthesized); |
| + assert(node != null); |
| + assert(elements[node] != null); |
| + |
| + IrBuilder builder = makeIRBuilder(node, element); |
| + return withBuilder(builder, () => _makeFunctionBody(element, node)); |
| + } |
| + |
| +} |
| + |