| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library dart2js.ir_builder_task; |
| |
| import '../closure.dart' as closurelib; |
| import '../closure.dart' hide ClosureScope; |
| import '../constants/expressions.dart'; |
| import '../dart_types.dart'; |
| import '../dart2jslib.dart'; |
| import '../elements/elements.dart'; |
| import '../elements/modelx.dart' show SynthesizedConstructorElementX, |
| ConstructorBodyElementX, FunctionSignatureX; |
| import '../io/source_file.dart'; |
| import '../io/source_information.dart'; |
| import '../js_backend/js_backend.dart' show JavaScriptBackend; |
| import '../resolution/semantic_visitor.dart'; |
| import '../resolution/operators.dart' as op; |
| import '../scanner/scannerlib.dart' show Token, isUserDefinableOperator; |
| import '../tree/tree.dart' as ast; |
| import '../universe/universe.dart' show SelectorKind; |
| import 'cps_ir_nodes.dart' as ir; |
| import 'cps_ir_builder.dart'; |
| |
| /** |
| * This task iterates through all resolved elements and builds [ir.Node]s. The |
| * nodes are stored in the [nodes] map and accessible through [hasIr] and |
| * [getIr]. |
| * |
| * The functionality of the IrNodes is added gradually, therefore elements might |
| * have an IR or not, depending on the language features that are used. For |
| * elements that do have an IR, the tree [ast.Node]s and the [Token]s are not |
| * used in the rest of the compilation. This is ensured by setting the element's |
| * cached tree to `null` and also breaking the token stream to crash future |
| * attempts to parse. |
| * |
| * The type inferrer works on either IR nodes or tree nodes. The IR nodes are |
| * then translated into the SSA form for optimizations and code generation. |
| * Long-term, once the IR supports the full language, the backend can be |
| * re-implemented to work directly on the IR. |
| */ |
| class IrBuilderTask extends CompilerTask { |
| final Map<Element, ir.ExecutableDefinition> nodes = |
| <Element, ir.ExecutableDefinition>{}; |
| final bool generateSourceMap; |
| |
| IrBuilderTask(Compiler compiler, {this.generateSourceMap: true}) |
| : super(compiler); |
| |
| String get name => 'IR builder'; |
| |
| bool hasIr(Element element) => nodes.containsKey(element.implementation); |
| |
| ir.ExecutableDefinition getIr(ExecutableElement element) { |
| return nodes[element.implementation]; |
| } |
| |
| ir.ExecutableDefinition buildNode(AstElement element) { |
| if (!canBuild(element)) return null; |
| |
| TreeElements elementsMapping = element.resolvedAst.elements; |
| element = element.implementation; |
| return compiler.withCurrentElement(element, () { |
| SourceInformationBuilder sourceInformationBuilder = generateSourceMap |
| ? new PositionSourceInformationBuilder(element) |
| : const SourceInformationBuilder(); |
| |
| IrBuilderVisitor builder = |
| compiler.backend is JavaScriptBackend |
| ? new JsIrBuilderVisitor( |
| elementsMapping, compiler, sourceInformationBuilder) |
| : new DartIrBuilderVisitor( |
| elementsMapping, compiler, sourceInformationBuilder); |
| ir.ExecutableDefinition definition = |
| builder.buildExecutable(element); |
| if (definition != null) { |
| nodes[element] = definition; |
| } |
| return definition; |
| }); |
| } |
| |
| void buildNodes() { |
| measure(() { |
| Set<Element> resolved = compiler.enqueuer.resolution.resolvedElements; |
| resolved.forEach(buildNode); |
| }); |
| } |
| |
| bool canBuild(Element element) { |
| if (element is TypedefElement) return false; |
| if (element is FunctionElement) { |
| // TODO(sigurdm): Support native functions for dart2js. |
| assert(invariant(element, !element.isNative)); |
| |
| if (element is ConstructorElement) { |
| if (!element.isGenerativeConstructor) { |
| // TODO(kmillikin,sigurdm): Support constructors. |
| return false; |
| } |
| if (element.isSynthesized) { |
| // Do generate CPS for synthetic constructors. |
| return true; |
| } |
| } |
| } else if (element is! FieldElement) { |
| compiler.internalError(element, "Unexpected element type $element"); |
| } |
| return compiler.backend.shouldOutput(element); |
| } |
| |
| bool get inCheckedMode { |
| bool result = false; |
| assert((result = true)); |
| return result; |
| } |
| |
| } |
| |
| /** |
| * A tree visitor that builds [IrNodes]. The visit methods add statements using |
| * to the [builder] and return the last added statement for trees that represent |
| * an expression. |
| */ |
| abstract class IrBuilderVisitor extends SemanticVisitor<ir.Primitive, dynamic> |
| with IrBuilderMixin<ast.Node>, |
| BaseImplementationOfStaticsMixin<ir.Primitive, dynamic>, |
| BaseImplementationOfLocalsMixin<ir.Primitive, dynamic>, |
| BaseImplementationOfDynamicsMixin<ir.Primitive, dynamic>, |
| BaseImplementationOfConstantsMixin<ir.Primitive, dynamic>, |
| BaseImplementationOfSuperIncDecsMixin<ir.Primitive, dynamic>, |
| ErrorBulkMixin<ir.Primitive, dynamic> |
| implements SemanticSendVisitor<ir.Primitive, dynamic> { |
| final Compiler compiler; |
| final SourceInformationBuilder sourceInformationBuilder; |
| |
| // In SSA terms, join-point continuation parameters are the phis and the |
| // continuation invocation arguments are the corresponding phi inputs. To |
| // support name introduction and renaming for source level variables, we use |
| // nested (delimited) visitors for constructing subparts of the IR that will |
| // need renaming. Each source variable is assigned an index. |
| // |
| // Each nested visitor maintains a list of free variable uses in the body. |
| // These are implemented as a list of parameters, each with their own use |
| // list of references. When the delimited subexpression is plugged into the |
| // surrounding context, the free occurrences can be captured or become free |
| // occurrences in the next outer delimited subexpression. |
| // |
| // Each nested visitor maintains a list that maps indexes of variables |
| // assigned in the delimited subexpression to their reaching definition --- |
| // that is, the definition in effect at the hole in 'current'. These are |
| // used to determine if a join-point continuation needs to be passed |
| // arguments, and what the arguments are. |
| |
| /// Construct a top-level visitor. |
| IrBuilderVisitor(TreeElements elements, |
| this.compiler, |
| this.sourceInformationBuilder) |
| : super(elements); |
| |
| @override |
| bulkHandleNode(ast.Node node, String message) => giveup(node, message); |
| |
| @override |
| ir.Primitive apply(ast.Node node, _) => node.accept(this); |
| |
| @override |
| SemanticSendVisitor get sendVisitor => this; |
| |
| /** |
| * 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); |
| |
| ClosureScope getClosureScopeForNode(ast.Node node); |
| ClosureEnvironment getClosureEnvironment(); |
| |
| /// Normalizes the argument list to a static invocation (i.e. where the target |
| /// element is known). |
| /// |
| /// For the JS backend, inserts default arguments and normalizes order of |
| /// named arguments. |
| /// |
| /// For the Dart backend, returns [arguments]. |
| List<ir.Primitive> normalizeStaticArguments( |
| Selector selector, |
| FunctionElement target, |
| List<ir.Primitive> arguments); |
| |
| /// Normalizes the argument list of a dynamic invocation (i.e. where the |
| /// target element is unknown). |
| /// |
| /// For the JS backend, normalizes order of named arguments. |
| /// |
| /// For the Dart backend, returns [arguments]. |
| List<ir.Primitive> normalizeDynamicArguments( |
| Selector selector, |
| List<ir.Primitive> arguments); |
| |
| ir.FunctionDefinition _makeFunctionBody(FunctionElement element, |
| ast.FunctionExpression node) { |
| FunctionSignature signature = element.functionSignature; |
| List<ParameterElement> parameters = []; |
| signature.orderedForEachParameter(parameters.add); |
| |
| irBuilder.buildFunctionHeader(parameters, |
| closureScope: getClosureScopeForNode(node), |
| env: getClosureEnvironment()); |
| |
| List<ConstantExpression> defaults = new List<ConstantExpression>(); |
| signature.orderedOptionalParameters.forEach((ParameterElement element) { |
| defaults.add(getConstantForVariable(element)); |
| }); |
| |
| List<ir.Initializer> initializers; |
| if (element.isSynthesized) { |
| assert(element is ConstructorElement); |
| return irBuilder.makeConstructorDefinition(const <ConstantExpression>[], |
| const <ir.Initializer>[]); |
| } else if (element.isGenerativeConstructor) { |
| if (element.isExternal) { |
| return irBuilder.makeAbstractConstructorDefinition(defaults); |
| } else { |
| initializers = buildConstructorInitializers(node, element); |
| visit(node.body); |
| return irBuilder.makeConstructorDefinition(defaults, initializers); |
| } |
| } else { |
| visit(node.body); |
| return irBuilder.makeFunctionDefinition(defaults); |
| } |
| } |
| |
| List<ir.Initializer> buildConstructorInitializers( |
| ast.FunctionExpression function, ConstructorElement element) { |
| List<ir.Initializer> result = <ir.Initializer>[]; |
| FunctionSignature signature = element.functionSignature; |
| |
| void tryAddInitializingFormal(ParameterElement parameterElement) { |
| if (parameterElement.isInitializingFormal) { |
| InitializingFormalElement initializingFormal = parameterElement; |
| withBuilder(irBuilder.makeInitializerBuilder(), () { |
| ir.Primitive value = irBuilder.buildLocalGet(parameterElement); |
| result.add(irBuilder.makeFieldInitializer( |
| initializingFormal.fieldElement, |
| irBuilder.makeRunnableBody(value))); |
| }); |
| } |
| } |
| |
| // TODO(sigurdm): Preserve initializing formals as initializing formals. |
| signature.orderedForEachParameter(tryAddInitializingFormal); |
| |
| if (function.initializers == null) return result; |
| bool explicitSuperInitializer = false; |
| for(ast.Node initializer in function.initializers) { |
| if (initializer is ast.SendSet) { |
| // Field initializer. |
| FieldElement field = elements[initializer]; |
| withBuilder(irBuilder.makeInitializerBuilder(), () { |
| ir.Primitive value = visit(initializer.arguments.head); |
| ir.RunnableBody body = irBuilder.makeRunnableBody(value); |
| result.add(irBuilder.makeFieldInitializer(field, body)); |
| }); |
| } else if (initializer is ast.Send) { |
| // Super or this initializer. |
| if (ast.Initializers.isConstructorRedirect(initializer)) { |
| giveup(initializer, "constructor redirect (this) initializer"); |
| } |
| ConstructorElement constructor = elements[initializer].implementation; |
| Selector selector = elements.getSelector(initializer); |
| List<ir.RunnableBody> arguments = |
| initializer.arguments.mapToList((ast.Node argument) { |
| return withBuilder(irBuilder.makeInitializerBuilder(), () { |
| ir.Primitive value = visit(argument); |
| return irBuilder.makeRunnableBody(value); |
| }); |
| }); |
| result.add(irBuilder.makeSuperInitializer(constructor, |
| arguments, |
| selector)); |
| explicitSuperInitializer = true; |
| } else { |
| compiler.internalError(initializer, |
| "Unexpected initializer type $initializer"); |
| } |
| |
| } |
| if (!explicitSuperInitializer) { |
| // No super initializer found. Try to find the default constructor if |
| // the class is not Object. |
| ClassElement enclosingClass = element.enclosingClass; |
| if (!enclosingClass.isObject) { |
| ClassElement superClass = enclosingClass.superclass; |
| FunctionElement target = superClass.lookupDefaultConstructor(); |
| if (target == null) { |
| compiler.internalError(superClass, |
| "No default constructor available."); |
| } |
| Selector selector = new Selector.callDefaultConstructor(); |
| result.add(irBuilder.makeSuperInitializer(target, |
| <ir.RunnableBody>[], |
| selector)); |
| } |
| } |
| return result; |
| } |
| |
| ir.Primitive visit(ast.Node node) => node.accept(this); |
| |
| // ## Statements ## |
| visitBlock(ast.Block node) { |
| irBuilder.buildBlock(node.statements.nodes, build); |
| } |
| |
| ir.Primitive visitBreakStatement(ast.BreakStatement node) { |
| if (!irBuilder.buildBreak(elements.getTargetOf(node))) { |
| compiler.internalError(node, "'break' target not found"); |
| } |
| return null; |
| } |
| |
| ir.Primitive visitContinueStatement(ast.ContinueStatement node) { |
| if (!irBuilder.buildContinue(elements.getTargetOf(node))) { |
| compiler.internalError(node, "'continue' target not found"); |
| } |
| return null; |
| } |
| |
| // Build(EmptyStatement, C) = C |
| ir.Primitive visitEmptyStatement(ast.EmptyStatement node) { |
| assert(irBuilder.isOpen); |
| return null; |
| } |
| |
| // Build(ExpressionStatement(e), C) = C' |
| // where (C', _) = Build(e, C) |
| ir.Primitive visitExpressionStatement(ast.ExpressionStatement node) { |
| assert(irBuilder.isOpen); |
| visit(node.expression); |
| return null; |
| } |
| |
| visitFor(ast.For node) { |
| List<LocalElement> loopVariables = <LocalElement>[]; |
| if (node.initializer is ast.VariableDefinitions) { |
| ast.VariableDefinitions definitions = node.initializer; |
| for (ast.Node node in definitions.definitions.nodes) { |
| LocalElement loopVariable = elements[node]; |
| loopVariables.add(loopVariable); |
| } |
| } |
| |
| JumpTarget target = elements.getTargetDefinition(node); |
| irBuilder.buildFor( |
| buildInitializer: subbuild(node.initializer), |
| buildCondition: subbuild(node.condition), |
| buildBody: subbuild(node.body), |
| buildUpdate: subbuildSequence(node.update), |
| closureScope: getClosureScopeForNode(node), |
| loopVariables: loopVariables, |
| target: target); |
| } |
| |
| visitIf(ast.If node) { |
| irBuilder.buildIf( |
| build(node.condition), |
| subbuild(node.thenPart), |
| subbuild(node.elsePart)); |
| } |
| |
| visitLabeledStatement(ast.LabeledStatement node) { |
| ast.Statement body = node.statement; |
| if (body is ast.Loop) { |
| visit(body); |
| } else { |
| JumpTarget target = elements.getTargetDefinition(body); |
| irBuilder.buildLabeledStatement( |
| buildBody: subbuild(body), |
| target: target); |
| } |
| } |
| |
| visitDoWhile(ast.DoWhile node) { |
| irBuilder.buildDoWhile( |
| buildBody: subbuild(node.body), |
| buildCondition: subbuild(node.condition), |
| target: elements.getTargetDefinition(node), |
| closureScope: getClosureScopeForNode(node)); |
| } |
| |
| visitWhile(ast.While node) { |
| irBuilder.buildWhile( |
| buildCondition: subbuild(node.condition), |
| buildBody: subbuild(node.body), |
| target: elements.getTargetDefinition(node), |
| closureScope: getClosureScopeForNode(node)); |
| } |
| |
| visitForIn(ast.ForIn node) { |
| // [node.declaredIdentifier] can be either an [ast.VariableDefinitions] |
| // (defining a new local variable) or a send designating some existing |
| // variable. |
| ast.Node identifier = node.declaredIdentifier; |
| ast.VariableDefinitions variableDeclaration = |
| identifier.asVariableDefinitions(); |
| Element variableElement = elements.getForInVariable(node); |
| Selector selector = elements.getSelector(identifier); |
| |
| irBuilder.buildForIn( |
| buildExpression: subbuild(node.expression), |
| buildVariableDeclaration: subbuild(variableDeclaration), |
| variableElement: variableElement, |
| variableSelector: selector, |
| buildBody: subbuild(node.body), |
| target: elements.getTargetDefinition(node), |
| closureScope: getClosureScopeForNode(node)); |
| } |
| |
| ir.Primitive visitVariableDefinitions(ast.VariableDefinitions node) { |
| assert(irBuilder.isOpen); |
| if (node.modifiers.isConst) { |
| for (ast.SendSet definition in node.definitions.nodes) { |
| assert(!definition.arguments.isEmpty); |
| assert(definition.arguments.tail.isEmpty); |
| VariableElement element = elements[definition]; |
| ConstantExpression value = getConstantForVariable(element); |
| irBuilder.declareLocalConstant(element, value); |
| } |
| } else { |
| for (ast.Node definition in node.definitions.nodes) { |
| Element element = elements[definition]; |
| ir.Primitive initialValue; |
| // Definitions are either SendSets if there is an initializer, or |
| // Identifiers if there is no initializer. |
| if (definition is ast.SendSet) { |
| assert(!definition.arguments.isEmpty); |
| assert(definition.arguments.tail.isEmpty); |
| initialValue = visit(definition.arguments.head); |
| } else { |
| assert(definition is ast.Identifier); |
| } |
| irBuilder.declareLocalVariable(element, initialValue: initialValue); |
| } |
| } |
| return null; |
| } |
| |
| // Build(Return(e), C) = C'[InvokeContinuation(return, x)] |
| // where (C', x) = Build(e, C) |
| // |
| // Return without a subexpression is translated as if it were return null. |
| ir.Primitive visitReturn(ast.Return node) { |
| assert(irBuilder.isOpen); |
| assert(invariant(node, node.beginToken.value != 'native')); |
| irBuilder.buildReturn(build(node.expression)); |
| return null; |
| } |
| |
| visitTryStatement(ast.TryStatement node) { |
| // Try/catch is not yet implemented in the JS backend. |
| if (this.irBuilder.tryStatements == null) { |
| return giveup(node, 'try/catch in the JS backend'); |
| } |
| // Multiple catch blocks are not yet implemented. |
| if (node.catchBlocks.isEmpty || |
| node.catchBlocks.nodes.tail == null) { |
| return giveup(node, 'not exactly one catch block'); |
| } |
| // 'on T' catch blocks are not yet implemented. |
| if ((node.catchBlocks.nodes.head as ast.CatchBlock).onKeyword != null) { |
| return giveup(node, '"on T" catch block'); |
| } |
| // Finally blocks are not yet implemented. |
| if (node.finallyBlock != null) { |
| return giveup(node, 'try/finally'); |
| } |
| |
| List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[]; |
| for (ast.CatchBlock catchClause in node.catchBlocks.nodes) { |
| assert(catchClause.exception != null); |
| LocalVariableElement exceptionVariable = elements[catchClause.exception]; |
| LocalVariableElement stackTraceVariable; |
| if (catchClause.trace != null) { |
| stackTraceVariable = elements[catchClause.trace]; |
| } |
| catchClauseInfos.add(new CatchClauseInfo( |
| exceptionVariable: exceptionVariable, |
| stackTraceVariable: stackTraceVariable, |
| buildCatchBlock: subbuild(catchClause.block))); |
| } |
| |
| irBuilder.buildTry( |
| tryStatementInfo: irBuilder.tryStatements[node], |
| buildTryBlock: subbuild(node.tryBlock), |
| catchClauseInfos: catchClauseInfos); |
| } |
| |
| // ## Expressions ## |
| ir.Primitive visitConditional(ast.Conditional node) { |
| return irBuilder.buildConditional( |
| build(node.condition), |
| subbuild(node.thenExpression), |
| subbuild(node.elseExpression)); |
| } |
| |
| // For all simple literals: |
| // Build(Literal(c), C) = C[let val x = Constant(c) in [], x] |
| ir.Primitive visitLiteralBool(ast.LiteralBool node) { |
| assert(irBuilder.isOpen); |
| return translateConstant(node); |
| } |
| |
| ir.Primitive visitLiteralDouble(ast.LiteralDouble node) { |
| assert(irBuilder.isOpen); |
| return translateConstant(node); |
| } |
| |
| ir.Primitive visitLiteralInt(ast.LiteralInt node) { |
| assert(irBuilder.isOpen); |
| return translateConstant(node); |
| } |
| |
| ir.Primitive visitLiteralNull(ast.LiteralNull node) { |
| assert(irBuilder.isOpen); |
| return translateConstant(node); |
| } |
| |
| ir.Primitive visitLiteralString(ast.LiteralString node) { |
| assert(irBuilder.isOpen); |
| return translateConstant(node); |
| } |
| |
| ConstantExpression getConstantForNode(ast.Node node) { |
| ConstantExpression constant = |
| compiler.backend.constantCompilerTask.compileNode(node, elements); |
| assert(invariant(node, constant != null, |
| message: 'No constant computed for $node')); |
| return constant; |
| } |
| |
| ConstantExpression getConstantForVariable(VariableElement element) { |
| ConstantExpression constant = |
| compiler.backend.constants.getConstantForVariable(element); |
| assert(invariant(element, constant != null, |
| message: 'No constant computed for $element')); |
| return constant; |
| } |
| |
| ir.Primitive visitLiteralList(ast.LiteralList node) { |
| if (node.isConst) { |
| return translateConstant(node); |
| } |
| List<ir.Primitive> values = node.elements.nodes.mapToList(visit); |
| InterfaceType type = elements.getType(node); |
| return irBuilder.buildListLiteral(type, values); |
| } |
| |
| ir.Primitive visitLiteralMap(ast.LiteralMap node) { |
| if (node.isConst) { |
| return translateConstant(node); |
| } |
| InterfaceType type = elements.getType(node); |
| return irBuilder.buildMapLiteral( |
| type, |
| node.entries.nodes.map((e) => e.key), |
| node.entries.nodes.map((e) => e.value), |
| build); |
| } |
| |
| ir.Primitive visitLiteralSymbol(ast.LiteralSymbol node) { |
| assert(irBuilder.isOpen); |
| return translateConstant(node); |
| } |
| |
| ir.Primitive visitIdentifier(ast.Identifier node) { |
| // "this" is the only identifier that should be met by the visitor. |
| assert(node.isThis()); |
| return irBuilder.buildThis(); |
| } |
| |
| ir.Primitive visitParenthesizedExpression( |
| ast.ParenthesizedExpression node) { |
| assert(irBuilder.isOpen); |
| return visit(node.expression); |
| } |
| |
| // Stores the result of visiting a CascadeReceiver, so we can return it from |
| // its enclosing Cascade. |
| ir.Primitive _currentCascadeReceiver; |
| |
| ir.Primitive visitCascadeReceiver(ast.CascadeReceiver node) { |
| assert(irBuilder.isOpen); |
| return _currentCascadeReceiver = visit(node.expression); |
| } |
| |
| ir.Primitive visitCascade(ast.Cascade node) { |
| assert(irBuilder.isOpen); |
| var oldCascadeReceiver = _currentCascadeReceiver; |
| // Throw away the result of visiting the expression. |
| // Instead we return the result of visiting the CascadeReceiver. |
| this.visit(node.expression); |
| ir.Primitive receiver = _currentCascadeReceiver; |
| _currentCascadeReceiver = oldCascadeReceiver; |
| return receiver; |
| } |
| |
| // ## Sends ## |
| @override |
| ir.Primitive visitAssert( |
| ast.Send node, |
| ast.Node condition, |
| _) { |
| assert(irBuilder.isOpen); |
| return giveup(node, 'Assert'); |
| } |
| |
| ir.Primitive visitNamedArgument(ast.NamedArgument node) { |
| assert(irBuilder.isOpen); |
| return visit(node.expression); |
| } |
| |
| @override |
| ir.Primitive visitExpressionInvoke(ast.Send node, |
| ast.Node expression, |
| ast.NodeList arguments, |
| Selector selector, _) { |
| ir.Primitive receiver = visit(expression); |
| List<ir.Primitive> arguments = node.arguments.mapToList(visit); |
| arguments = normalizeDynamicArguments(selector, arguments); |
| return irBuilder.buildCallInvocation(receiver, selector, arguments); |
| } |
| |
| /// Returns `true` if [node] is a super call. |
| // TODO(johnniwinther): Remove the need for this. |
| bool isSuperCall(ast.Send node) { |
| return node != null && node.receiver != null && node.receiver.isSuper(); |
| } |
| |
| @override |
| ir.Primitive handleConstantGet(ast.Send node, |
| ConstantExpression constant, _) { |
| return irBuilder.buildConstantLiteral(constant); |
| } |
| |
| /// If [node] is null, returns this. |
| /// Otherwise visits [node] and returns the result. |
| ir.Primitive translateReceiver(ast.Expression node) { |
| return node != null ? visit(node) : irBuilder.buildThis(); |
| } |
| |
| @override |
| ir.Primitive handleDynamicGet( |
| ast.Send node, |
| ast.Node receiver, |
| Selector selector, |
| _) { |
| return irBuilder.buildDynamicGet( |
| translateReceiver(receiver), |
| selector); |
| } |
| |
| @override |
| ir.Primitive visitDynamicTypeLiteralGet( |
| ast.Send node, |
| ConstantExpression constant, |
| _) { |
| return irBuilder.buildConstantLiteral(constant); |
| } |
| |
| @override |
| ir.Primitive handleLocalGet( |
| ast.Send node, |
| LocalElement element, |
| _) { |
| if (element.isConst) { |
| return translateConstant(node); |
| } |
| return irBuilder.buildLocalGet(element); |
| } |
| |
| @override |
| ir.Primitive handleStaticFieldGet( |
| ast.Send node, |
| FieldElement field, |
| _) { |
| if (field.isConst) { |
| return translateConstant(node); |
| } |
| return irBuilder.buildStaticGet(field, |
| sourceInformation: sourceInformationBuilder.buildGet(node)); |
| } |
| |
| @override |
| ir.Primitive handleStaticFunctionGet( |
| ast.Send node, |
| MethodElement function, |
| _) { |
| // TODO(karlklose): support foreign functions. |
| if (function.isForeign(compiler.backend)) { |
| return giveup(node, 'handleStaticFunctionGet: foreign: $function'); |
| } |
| return translateConstant(node); |
| } |
| |
| @override |
| ir.Primitive handleStaticGetterGet( |
| ast.Send node, |
| FunctionElement getter, |
| _) { |
| return irBuilder.buildStaticInvocation(getter, |
| new Selector.getter(getter.name, getter.library), const []); |
| } |
| |
| @override |
| ir.Primitive visitSuperFieldGet( |
| ast.Send node, |
| FieldElement field, |
| _) { |
| return irBuilder.buildSuperGet(field); |
| } |
| |
| @override |
| ir.Primitive visitSuperGetterGet( |
| ast.Send node, |
| FunctionElement getter, |
| _) { |
| return irBuilder.buildSuperGet(getter); |
| } |
| |
| @override |
| ir.Primitive visitSuperMethodGet( |
| ast.Send node, |
| MethodElement method, |
| _) { |
| return irBuilder.buildSuperGet(method); |
| } |
| |
| @override |
| ir.Primitive visitThisGet(ast.Identifier node, _) { |
| return irBuilder.buildThis(); |
| } |
| |
| ir.Primitive translateTypeVariableTypeLiteral(TypeVariableElement element) { |
| return buildReifyTypeVariable(irBuilder.buildThis(), element.type); |
| } |
| |
| @override |
| ir.Primitive visitTypeVariableTypeLiteralGet(ast.Send node, |
| TypeVariableElement element, _) { |
| return translateTypeVariableTypeLiteral(element); |
| } |
| |
| ir.Primitive translateLogicalOperator(ast.Expression left, |
| ast.Expression right, |
| {bool isLazyOr}) { |
| ir.Primitive leftValue = visit(left); |
| |
| ir.Primitive buildRightValue(IrBuilder rightBuilder) { |
| return withBuilder(rightBuilder, () => visit(right)); |
| } |
| |
| return irBuilder.buildLogicalOperator( |
| leftValue, buildRightValue, isLazyOr: isLazyOr); |
| } |
| |
| @override |
| ir.Primitive visitLogicalAnd( |
| ast.Send node, ast.Node left, ast.Node right, _) { |
| return translateLogicalOperator(left, right, isLazyOr: false); |
| } |
| |
| @override |
| ir.Primitive visitLogicalOr( |
| ast.Send node, ast.Node left, ast.Node right, _) { |
| return translateLogicalOperator(left, right, isLazyOr: true); |
| } |
| |
| @override |
| ir.Primitive visitAs( |
| ast.Send node, |
| ast.Node expression, |
| DartType type, |
| _) { |
| ir.Primitive receiver = visit(expression); |
| return irBuilder.buildTypeOperator(receiver, type, isTypeTest: false); |
| } |
| |
| @override |
| ir.Primitive visitIs( |
| ast.Send node, |
| ast.Node expression, |
| DartType type, |
| _) { |
| ir.Primitive receiver = visit(expression); |
| return irBuilder.buildTypeOperator( |
| receiver, type, |
| isTypeTest: true, |
| isNotCheck: false); |
| } |
| |
| @override |
| ir.Primitive visitIsNot(ast.Send node, |
| ast.Node expression, DartType type, _) { |
| ir.Primitive receiver = visit(expression); |
| return irBuilder.buildTypeOperator( |
| receiver, type, |
| isTypeTest: true, |
| isNotCheck: true); |
| } |
| |
| ir.Primitive translateBinary(ast.Node left, |
| op.BinaryOperator operator, |
| ast.Node right) { |
| Selector selector = new Selector.binaryOperator(operator.selectorName); |
| ir.Primitive receiver = visit(left); |
| List<ir.Primitive> arguments = <ir.Primitive>[visit(right)]; |
| arguments = normalizeDynamicArguments(selector, arguments); |
| return irBuilder.buildDynamicInvocation(receiver, selector, arguments); |
| } |
| |
| @override |
| ir.Primitive visitBinary(ast.Send node, |
| ast.Node left, |
| op.BinaryOperator operator, |
| ast.Node right, _) { |
| return translateBinary(left, operator, right); |
| } |
| |
| @override |
| ir.Primitive visitIndex(ast.Send node, |
| ast.Node receiver, |
| ast.Node index, _) { |
| Selector selector = new Selector.index(); |
| ir.Primitive target = visit(receiver); |
| List<ir.Primitive> arguments = <ir.Primitive>[visit(index)]; |
| arguments = normalizeDynamicArguments(selector, arguments); |
| return irBuilder.buildDynamicInvocation(target, selector, arguments); |
| } |
| |
| ir.Primitive translateSuperBinary(FunctionElement function, |
| op.BinaryOperator operator, |
| ast.Node argument) { |
| Selector selector = new Selector.binaryOperator(operator.selectorName); |
| List<ir.Primitive> arguments = <ir.Primitive>[visit(argument)]; |
| arguments = normalizeDynamicArguments(selector, arguments); |
| return irBuilder.buildSuperInvocation(function, selector, arguments); |
| } |
| |
| @override |
| ir.Primitive visitSuperBinary( |
| ast.Send node, |
| FunctionElement function, |
| op.BinaryOperator operator, |
| ast.Node argument, |
| _) { |
| return translateSuperBinary(function, operator, argument); |
| } |
| |
| @override |
| ir.Primitive visitSuperIndex( |
| ast.Send node, |
| FunctionElement function, |
| ast.Node index, |
| _) { |
| Selector selector = new Selector.index(); |
| List<ir.Primitive> arguments = <ir.Primitive>[visit(index)]; |
| arguments = normalizeDynamicArguments(selector, arguments); |
| return irBuilder.buildSuperInvocation(function, selector, arguments); |
| } |
| |
| @override |
| ir.Primitive visitEquals( |
| ast.Send node, |
| ast.Node left, |
| ast.Node right, |
| _) { |
| return translateBinary(left, op.BinaryOperator.EQ, right); |
| } |
| |
| @override |
| ir.Primitive visitSuperEquals( |
| ast.Send node, |
| FunctionElement function, |
| ast.Node argument, |
| _) { |
| return translateSuperBinary(function, op.BinaryOperator.EQ, argument); |
| } |
| |
| @override |
| ir.Primitive visitNot( |
| ast.Send node, |
| ast.Node expression, |
| _) { |
| return irBuilder.buildNegation(visit(expression)); |
| } |
| |
| @override |
| ir.Primitive visitNotEquals( |
| ast.Send node, |
| ast.Node left, |
| ast.Node right, |
| _) { |
| return irBuilder.buildNegation( |
| translateBinary(left, op.BinaryOperator.NOT_EQ, right)); |
| } |
| |
| @override |
| ir.Primitive visitSuperNotEquals( |
| ast.Send node, |
| FunctionElement function, |
| ast.Node argument, |
| _) { |
| return irBuilder.buildNegation( |
| translateSuperBinary(function, op.BinaryOperator.NOT_EQ, argument)); |
| } |
| |
| @override |
| ir.Primitive visitUnary(ast.Send node, |
| op.UnaryOperator operator, ast.Node expression, _) { |
| // TODO(johnniwinther): Clean up the creation of selectors. |
| Selector selector = |
| new Selector(SelectorKind.OPERATOR, operator.selectorName, null, 0); |
| ir.Primitive receiver = translateReceiver(expression); |
| return irBuilder.buildDynamicInvocation(receiver, selector, const []); |
| } |
| |
| @override |
| ir.Primitive visitSuperUnary( |
| ast.Send node, |
| op.UnaryOperator operator, |
| FunctionElement function, |
| _) { |
| // TODO(johnniwinther): Clean up the creation of selectors. |
| Selector selector = |
| new Selector(SelectorKind.OPERATOR, operator.selectorName, null, 0); |
| return irBuilder.buildSuperInvocation(function, selector, const []); |
| } |
| |
| // TODO(johnniwinther): Handle this in the [IrBuilder] to ensure the correct |
| // semantic correlation between arguments and invocation. |
| List<ir.Primitive> translateDynamicArguments(ast.NodeList nodeList, |
| Selector selector) { |
| List<ir.Primitive> arguments = nodeList.nodes.mapToList(visit); |
| return normalizeDynamicArguments(selector, arguments); |
| } |
| |
| // TODO(johnniwinther): Handle this in the [IrBuilder] to ensure the correct |
| // semantic correlation between arguments and invocation. |
| List<ir.Primitive> translateStaticArguments(ast.NodeList nodeList, |
| Element element, |
| Selector selector) { |
| List<ir.Primitive> arguments = nodeList.nodes.mapToList(visit); |
| return normalizeStaticArguments(selector, element, arguments); |
| } |
| |
| ir.Primitive translateCallInvoke(ir.Primitive target, |
| ast.NodeList arguments, |
| Selector selector) { |
| |
| return irBuilder.buildCallInvocation(target, selector, |
| translateDynamicArguments(arguments, selector)); |
| } |
| |
| ir.Primitive translateConstantInvoke(ConstantExpression constant, |
| ast.NodeList arguments, |
| Selector selector) { |
| return translateCallInvoke( |
| irBuilder.buildConstantLiteral(constant), |
| arguments, |
| selector); |
| } |
| |
| @override |
| ir.Primitive handleConstantInvoke( |
| ast.Send node, |
| ConstantExpression constant, |
| ast.NodeList arguments, |
| Selector selector, |
| _) { |
| return translateConstantInvoke(constant, arguments, selector); |
| } |
| |
| @override |
| ir.Primitive handleDynamicInvoke( |
| ast.Send node, |
| ast.Node receiver, |
| ast.NodeList arguments, |
| Selector selector, |
| _) { |
| return irBuilder.buildDynamicInvocation( |
| translateReceiver(receiver), selector, |
| translateDynamicArguments(arguments, selector)); |
| } |
| |
| ir.Primitive handleLocalInvoke( |
| ast.Send node, |
| LocalElement element, |
| ast.NodeList arguments, |
| Selector selector, |
| _) { |
| return irBuilder.buildLocalInvocation(element, selector, |
| translateDynamicArguments(arguments, selector)); |
| } |
| |
| @override |
| ir.Primitive handleStaticFieldInvoke( |
| ast.Send node, |
| FieldElement field, |
| ast.NodeList arguments, |
| Selector selector, |
| _) { |
| return translateCallInvoke( |
| irBuilder.buildStaticGet(field), |
| arguments, selector); |
| } |
| |
| @override |
| ir.Primitive handleStaticFunctionInvoke( |
| ast.Send node, |
| MethodElement function, |
| ast.NodeList arguments, |
| Selector selector, |
| _) { |
| // TODO(karlklose): support foreign functions. |
| if (function.isForeign(compiler.backend)) { |
| return giveup(node, 'handleStaticFunctionInvoke: foreign: $function'); |
| } |
| return irBuilder.buildStaticInvocation(function, selector, |
| translateStaticArguments(arguments, function, selector), |
| sourceInformation: sourceInformationBuilder.buildCall(node)); |
| } |
| |
| @override |
| ir.Primitive handleStaticGetterInvoke( |
| ast.Send node, |
| FunctionElement getter, |
| ast.NodeList arguments, |
| Selector selector, |
| _) { |
| return translateCallInvoke( |
| irBuilder.buildStaticGet(getter), |
| arguments, selector); |
| } |
| |
| @override |
| ir.Primitive visitSuperFieldInvoke( |
| ast.Send node, |
| FieldElement field, |
| ast.NodeList arguments, |
| Selector selector, |
| _) { |
| return translateCallInvoke( |
| irBuilder.buildSuperGet(field), |
| arguments, selector); |
| } |
| |
| @override |
| ir.Primitive visitSuperGetterInvoke( |
| ast.Send node, |
| FunctionElement getter, |
| ast.NodeList arguments, |
| Selector selector, |
| _) { |
| return translateCallInvoke( |
| irBuilder.buildSuperGet(getter), |
| arguments, selector); |
| } |
| |
| @override |
| ir.Primitive visitSuperMethodInvoke( |
| ast.Send node, |
| MethodElement method, |
| ast.NodeList arguments, |
| Selector selector, |
| _) { |
| return irBuilder.buildSuperInvocation(method, selector, |
| translateDynamicArguments(arguments, selector)); |
| } |
| |
| @override |
| ir.Primitive visitThisInvoke( |
| ast.Send node, |
| ast.NodeList arguments, |
| Selector selector, |
| _) { |
| return translateCallInvoke(irBuilder.buildThis(), arguments, selector); |
| } |
| |
| @override |
| ir.Primitive visitTypeVariableTypeLiteralInvoke( |
| ast.Send node, |
| TypeVariableElement element, |
| ast.NodeList arguments, |
| Selector selector, |
| _) { |
| return translateCallInvoke( |
| translateTypeVariableTypeLiteral(element), |
| arguments, |
| selector); |
| } |
| |
| @override |
| ir.Primitive visitTypedefTypeLiteralInvoke( |
| ast.Send node, |
| TypeConstantExpression constant, |
| ast.NodeList arguments, |
| Selector selector, _) { |
| return translateConstantInvoke(constant, arguments, selector); |
| } |
| |
| // TODO(johnniwinther): This should be a method on [IrBuilder]. |
| ir.Primitive buildReifyTypeVariable(ir.Primitive target, |
| TypeVariableType variable); |
| |
| @override |
| ir.Primitive visitIndexSet( |
| ast.SendSet node, |
| ast.Node receiver, |
| ast.Node index, |
| ast.Node rhs, |
| _) { |
| return irBuilder.buildDynamicIndexSet( |
| visit(receiver), visit(index), visit(rhs)); |
| } |
| |
| @override |
| ir.Primitive visitSuperIndexSet( |
| ast.SendSet node, |
| FunctionElement function, |
| ast.Node index, |
| ast.Node rhs, |
| _) { |
| return irBuilder.buildSuperIndexSet(function, visit(index), visit(rhs)); |
| } |
| |
| @override |
| ir.Primitive visitCompoundIndexSet( |
| ast.SendSet node, |
| ast.Node receiver, |
| ast.Node index, |
| op.AssignmentOperator operator, |
| ast.Node rhs, |
| _) { |
| ir.Primitive target = visit(receiver); |
| ir.Primitive indexValue = visit(index); |
| return translateCompound( |
| getValue: () { |
| Selector selector = new Selector.index(); |
| List<ir.Primitive> arguments = <ir.Primitive>[indexValue]; |
| arguments = normalizeDynamicArguments(selector, arguments); |
| return irBuilder.buildDynamicInvocation(target, selector, arguments); |
| }, |
| operator: operator, |
| rhs: rhs, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildDynamicIndexSet(target, indexValue, result); |
| }); |
| } |
| |
| @override |
| ir.Primitive visitSuperCompoundIndexSet( |
| ast.SendSet node, |
| FunctionElement getter, |
| FunctionElement setter, |
| ast.Node index, |
| op.AssignmentOperator operator, |
| ast.Node rhs, |
| _) { |
| ir.Primitive indexValue = visit(index); |
| return translateCompound( |
| getValue: () { |
| Selector selector = new Selector.index(); |
| List<ir.Primitive> arguments = <ir.Primitive>[indexValue]; |
| arguments = normalizeDynamicArguments(selector, arguments); |
| return irBuilder.buildSuperInvocation(getter, selector, arguments); |
| }, |
| operator: operator, |
| rhs: rhs, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildSuperIndexSet(setter, indexValue, result); |
| }); |
| } |
| |
| ir.Primitive translatePrefixPostfix( |
| {ir.Primitive getValue(), |
| op.IncDecOperator operator, |
| void setValue(ir.Primitive value), |
| bool isPrefix}) { |
| ir.Primitive value = getValue(); |
| Selector operatorSelector = |
| new Selector.binaryOperator(operator.selectorName); |
| List<ir.Primitive> arguments = |
| <ir.Primitive>[irBuilder.buildIntegerLiteral(1)]; |
| arguments = normalizeDynamicArguments(operatorSelector, arguments); |
| ir.Primitive result = |
| irBuilder.buildDynamicInvocation(value, operatorSelector, arguments); |
| setValue(result); |
| return isPrefix ? result : value; |
| } |
| |
| ir.Primitive translateCompound( |
| {ir.Primitive getValue(), |
| op.AssignmentOperator operator, |
| ast.Node rhs, |
| void setValue(ir.Primitive value)}) { |
| ir.Primitive value = getValue(); |
| Selector operatorSelector = |
| new Selector.binaryOperator(operator.selectorName); |
| List<ir.Primitive> arguments = <ir.Primitive>[visit(rhs)]; |
| arguments = normalizeDynamicArguments(operatorSelector, arguments); |
| ir.Primitive result = |
| irBuilder.buildDynamicInvocation(value, operatorSelector, arguments); |
| setValue(result); |
| return result; |
| } |
| |
| @override |
| ir.Primitive handleDynamicCompound( |
| ast.Send node, |
| ast.Node receiver, |
| op.AssignmentOperator operator, |
| ast.Node rhs, |
| Selector getterSelector, |
| Selector setterSelector, |
| _) { |
| ir.Primitive target = translateReceiver(receiver); |
| return translateCompound( |
| getValue: () => irBuilder.buildDynamicGet(target, getterSelector), |
| operator: operator, |
| rhs: rhs, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildDynamicSet(target, setterSelector, result); |
| }); |
| } |
| |
| @override |
| ir.Primitive handleDynamicPostfixPrefix( |
| ast.Send node, |
| ast.Node receiver, |
| op.IncDecOperator operator, |
| Selector getterSelector, |
| Selector setterSelector, |
| arg, |
| {bool isPrefix}) { |
| ir.Primitive target = translateReceiver(receiver); |
| return translatePrefixPostfix( |
| getValue: () => irBuilder.buildDynamicGet(target, getterSelector), |
| operator: operator, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildDynamicSet(target, setterSelector, result); |
| }, |
| isPrefix: isPrefix); |
| } |
| |
| @override |
| ir.Primitive handleDynamicSet( |
| ast.SendSet node, |
| ast.Node receiver, |
| Selector selector, |
| ast.Node rhs, |
| _) { |
| return irBuilder.buildDynamicSet( |
| translateReceiver(receiver), |
| selector, |
| visit(rhs)); |
| } |
| |
| @override |
| ir.Primitive handleLocalCompound( |
| ast.Send node, |
| LocalElement element, |
| op.AssignmentOperator operator, |
| ast.Node rhs, |
| _) { |
| return translateCompound( |
| getValue: () => irBuilder.buildLocalGet(element), |
| operator: operator, |
| rhs: rhs, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildLocalSet(element, result); |
| }); |
| } |
| |
| @override |
| ir.Primitive handleLocalPostfixPrefix( |
| ast.Send node, |
| LocalElement element, |
| op.IncDecOperator operator, |
| arg, |
| {bool isPrefix}) { |
| return translatePrefixPostfix( |
| getValue: () => irBuilder.buildLocalGet(element), |
| operator: operator, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildLocalSet(element, result); |
| }, |
| isPrefix: isPrefix); |
| } |
| |
| @override |
| ir.Primitive handleLocalSet( |
| ast.SendSet node, |
| LocalElement element, |
| ast.Node rhs, |
| _) { |
| return irBuilder.buildLocalSet(element, visit(rhs)); |
| } |
| |
| @override |
| ir.Primitive handleStaticFieldCompound( |
| ast.Send node, |
| FieldElement field, |
| op.AssignmentOperator operator, |
| ast.Node rhs, |
| _) { |
| return translateCompound( |
| getValue: () => irBuilder.buildStaticGet(field), |
| operator: operator, |
| rhs: rhs, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildStaticSet(field, result); |
| }); |
| } |
| |
| @override |
| ir.Primitive handleStaticFieldPostfixPrefix( |
| ast.Send node, |
| FieldElement field, |
| op.IncDecOperator operator, |
| arg, |
| {bool isPrefix}) { |
| return translatePrefixPostfix( |
| getValue: () => irBuilder.buildStaticGet(field), |
| operator: operator, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildStaticSet(field, result); |
| }, |
| isPrefix: isPrefix); |
| } |
| |
| @override |
| ir.Primitive handleStaticFieldSet( |
| ast.SendSet node, |
| FieldElement field, |
| ast.Node rhs, |
| _) { |
| return irBuilder.buildStaticSet(field, visit(rhs)); |
| } |
| |
| @override |
| ir.Primitive visitSuperFieldSet( |
| ast.SendSet node, |
| FieldElement field, |
| ast.Node rhs, |
| _) { |
| return irBuilder.buildSuperSet(field, visit(rhs)); |
| } |
| |
| @override |
| ir.Primitive visitSuperSetterSet( |
| ast.SendSet node, |
| FunctionElement setter, |
| ast.Node rhs, |
| _) { |
| return irBuilder.buildSuperSet(setter, visit(rhs)); |
| } |
| |
| @override |
| ir.Primitive handleStaticGetterSetterCompound( |
| ast.Send node, |
| FunctionElement getter, |
| FunctionElement setter, |
| op.AssignmentOperator operator, |
| ast.Node rhs, |
| _) { |
| return translateCompound( |
| getValue: () => irBuilder.buildStaticGet(getter), |
| operator: operator, |
| rhs: rhs, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildStaticSet(setter, result); |
| }); |
| } |
| |
| @override |
| ir.Primitive handleSuperFieldFieldPostfixPrefix( |
| ast.Send node, |
| FieldElement readField, |
| FieldElement writtenField, |
| op.IncDecOperator operator, |
| arg, |
| {bool isPrefix}) { |
| return translatePrefixPostfix( |
| getValue: () => irBuilder.buildSuperGet(readField), |
| operator: operator, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildSuperSet(writtenField, result); |
| }, |
| isPrefix: isPrefix); |
| } |
| |
| @override |
| ir.Primitive handleSuperFieldSetterPostfixPrefix( |
| ast.Send node, |
| FieldElement field, |
| FunctionElement setter, |
| op.IncDecOperator operator, |
| arg, |
| {bool isPrefix}) { |
| return translatePrefixPostfix( |
| getValue: () => irBuilder.buildSuperGet(field), |
| operator: operator, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildSuperSet(setter, result); |
| }, |
| isPrefix: isPrefix); |
| } |
| |
| @override |
| ir.Primitive handleSuperGetterFieldPostfixPrefix( |
| ast.Send node, |
| FunctionElement getter, |
| FieldElement field, |
| op.IncDecOperator operator, |
| arg, |
| {bool isPrefix}) { |
| return translatePrefixPostfix( |
| getValue: () => irBuilder.buildSuperGet(getter), |
| operator: operator, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildSuperSet(field, result); |
| }, |
| isPrefix: isPrefix); |
| } |
| |
| @override |
| ir.Primitive handleSuperGetterSetterPostfixPrefix( |
| ast.Send node, |
| FunctionElement getter, |
| FunctionElement setter, |
| op.IncDecOperator operator, |
| arg, |
| {bool isPrefix}) { |
| return translatePrefixPostfix( |
| getValue: () => irBuilder.buildSuperGet(getter), |
| operator: operator, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildSuperSet(setter, result); |
| }, |
| isPrefix: isPrefix); |
| } |
| |
| @override |
| ir.Primitive handleSuperMethodSetterPostfixPrefix( |
| ast.Send node, |
| FunctionElement method, |
| FunctionElement setter, |
| op.IncDecOperator operator, |
| arg, |
| {bool isPrefix}) { |
| return translatePrefixPostfix( |
| getValue: () => irBuilder.buildSuperGet(method), |
| operator: operator, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildSuperSet(setter, result); |
| }, |
| isPrefix: isPrefix); |
| } |
| |
| @override |
| ir.Primitive handleStaticGetterSetterPostfixPrefix( |
| ast.Send node, |
| FunctionElement getter, |
| FunctionElement setter, |
| op.IncDecOperator operator, |
| arg, |
| {bool isPrefix}) { |
| return translatePrefixPostfix( |
| getValue: () => irBuilder.buildStaticGet(getter), |
| operator: operator, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildStaticSet(setter, result); |
| }, |
| isPrefix: isPrefix); |
| } |
| |
| @override |
| ir.Primitive handleStaticMethodSetterCompound( |
| ast.Send node, |
| FunctionElement method, |
| FunctionElement setter, |
| op.AssignmentOperator operator, |
| ast.Node rhs, |
| _) { |
| return translateCompound( |
| getValue: () => irBuilder.buildStaticGet(method), |
| operator: operator, |
| rhs: rhs, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildStaticSet(setter, result); |
| }); |
| } |
| |
| @override |
| ir.Primitive handleStaticMethodSetterPostfixPrefix( |
| ast.Send node, |
| FunctionElement getter, |
| FunctionElement setter, |
| op.IncDecOperator operator, |
| arg, |
| {bool isPrefix}) { |
| return translatePrefixPostfix( |
| getValue: () => irBuilder.buildStaticGet(getter), |
| operator: operator, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildStaticSet(setter, result); |
| }, |
| isPrefix: isPrefix); |
| } |
| |
| @override |
| ir.Primitive handleDynamicIndexPostfixPrefix( |
| ast.Send node, |
| ast.Node receiver, |
| ast.Node index, |
| op.IncDecOperator operator, |
| arg, |
| {bool isPrefix}) { |
| ir.Primitive target = visit(receiver); |
| ir.Primitive indexValue = visit(index); |
| return translatePrefixPostfix( |
| getValue: () { |
| Selector selector = new Selector.index(); |
| List<ir.Primitive> arguments = <ir.Primitive>[indexValue]; |
| arguments = normalizeDynamicArguments(selector, arguments); |
| return irBuilder.buildDynamicInvocation(target, selector, arguments); |
| }, |
| operator: operator, |
| setValue: (ir.Primitive result) { |
| Selector selector = new Selector.indexSet(); |
| List<ir.Primitive> arguments = <ir.Primitive>[indexValue, result]; |
| arguments = normalizeDynamicArguments(selector, arguments); |
| irBuilder.buildDynamicInvocation(target, selector, arguments); |
| }, |
| isPrefix: isPrefix); |
| } |
| |
| @override |
| ir.Primitive handleSuperIndexPostfixPrefix( |
| ast.Send node, |
| FunctionElement indexFunction, |
| FunctionElement indexSetFunction, |
| ast.Node index, |
| op.IncDecOperator operator, |
| arg, |
| {bool isPrefix}) { |
| ir.Primitive indexValue = visit(index); |
| return translatePrefixPostfix( |
| getValue: () { |
| Selector selector = new Selector.index(); |
| List<ir.Primitive> arguments = <ir.Primitive>[indexValue]; |
| arguments = normalizeDynamicArguments(selector, arguments); |
| return irBuilder.buildSuperInvocation( |
| indexFunction, selector, arguments); |
| }, |
| operator: operator, |
| setValue: (ir.Primitive result) { |
| Selector selector = new Selector.indexSet(); |
| List<ir.Primitive> arguments = <ir.Primitive>[indexValue, result]; |
| arguments = normalizeDynamicArguments(selector, arguments); |
| irBuilder.buildSuperInvocation( |
| indexSetFunction, selector, arguments); |
| }, |
| isPrefix: isPrefix); |
| } |
| |
| @override |
| ir.Primitive handleStaticSetterSet( |
| ast.SendSet node, |
| FunctionElement setter, |
| ast.Node rhs, |
| _) { |
| return irBuilder.buildStaticSet(setter, visit(rhs)); |
| } |
| |
| @override |
| ir.Primitive visitSuperFieldCompound( |
| ast.Send node, |
| FieldElement field, |
| op.AssignmentOperator operator, |
| ast.Node rhs, |
| _) { |
| return translateCompound( |
| getValue: () => irBuilder.buildSuperGet(field), |
| operator: operator, |
| rhs: rhs, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildSuperSet(field, result); |
| }); |
| } |
| |
| @override |
| ir.Primitive visitSuperFieldFieldPostfix( |
| ast.Send node, |
| FieldElement readField, |
| FieldElement writtenField, |
| op.IncDecOperator operator, |
| _) { |
| return translatePrefixPostfix( |
| getValue: () => irBuilder.buildSuperGet(readField), |
| operator: operator, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildSuperSet(writtenField, result); |
| }, |
| isPrefix: false); |
| } |
| |
| @override |
| ir.Primitive visitSuperFieldFieldPrefix( |
| ast.Send node, |
| FieldElement readField, |
| FieldElement writtenField, |
| op.IncDecOperator operator, |
| _) { |
| return translatePrefixPostfix( |
| getValue: () => irBuilder.buildSuperGet(readField), |
| operator: operator, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildSuperSet(writtenField, result); |
| }, |
| isPrefix: true); |
| } |
| |
| @override |
| ir.Primitive visitSuperFieldSetterCompound( |
| ast.Send node, |
| FieldElement field, |
| FunctionElement setter, |
| op.AssignmentOperator operator, |
| ast.Node rhs, |
| _) { |
| return translateCompound( |
| getValue: () => irBuilder.buildSuperGet(field), |
| operator: operator, |
| rhs: rhs, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildSuperSet(setter, result); |
| }); |
| } |
| |
| @override |
| ir.Primitive visitSuperGetterFieldCompound( |
| ast.Send node, |
| FunctionElement getter, |
| FieldElement field, |
| op.AssignmentOperator operator, |
| ast.Node rhs, |
| _) { |
| return translateCompound( |
| getValue: () => irBuilder.buildSuperGet(getter), |
| operator: operator, |
| rhs: rhs, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildSuperSet(field, result); |
| }); |
| } |
| |
| @override |
| ir.Primitive visitSuperGetterSetterCompound( |
| ast.Send node, |
| FunctionElement getter, |
| FunctionElement setter, |
| op.AssignmentOperator operator, |
| ast.Node rhs, |
| _) { |
| return translateCompound( |
| getValue: () => irBuilder.buildSuperGet(getter), |
| operator: operator, |
| rhs: rhs, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildSuperSet(setter, result); |
| }); |
| } |
| |
| @override |
| ir.Primitive visitSuperMethodSetterCompound( |
| ast.Send node, |
| FunctionElement method, |
| FunctionElement setter, |
| op.AssignmentOperator operator, |
| ast.Node rhs, |
| _) { |
| return translateCompound( |
| getValue: () => irBuilder.buildSuperGet(method), |
| operator: operator, |
| rhs: rhs, |
| setValue: (ir.Primitive result) { |
| irBuilder.buildSuperSet(setter, result); |
| }); |
| } |
| |
| ir.Primitive visitNewExpression(ast.NewExpression node) { |
| if (node.isConst) { |
| return translateConstant(node); |
| } |
| FunctionElement element = elements[node.send]; |
| Selector selector = elements.getSelector(node.send); |
| DartType type = elements.getType(node); |
| ast.Node selectorNode = node.send.selector; |
| List<ir.Primitive> arguments = |
| node.send.arguments.mapToList(visit, growable:false); |
| arguments = normalizeStaticArguments(selector, element, arguments); |
| return irBuilder.buildConstructorInvocation( |
| element, selector, type, arguments); |
| } |
| |
| ir.Primitive visitStringJuxtaposition(ast.StringJuxtaposition node) { |
| assert(irBuilder.isOpen); |
| ir.Primitive first = visit(node.first); |
| ir.Primitive second = visit(node.second); |
| return irBuilder.buildStringConcatenation([first, second]); |
| } |
| |
| ir.Primitive visitStringInterpolation(ast.StringInterpolation node) { |
| assert(irBuilder.isOpen); |
| List<ir.Primitive> arguments = []; |
| arguments.add(visitLiteralString(node.string)); |
| var it = node.parts.iterator; |
| while (it.moveNext()) { |
| ast.StringInterpolationPart part = it.current; |
| arguments.add(visit(part.expression)); |
| arguments.add(visitLiteralString(part.string)); |
| } |
| return irBuilder.buildStringConcatenation(arguments); |
| } |
| |
| ir.Primitive translateConstant(ast.Node node) { |
| assert(irBuilder.isOpen); |
| return irBuilder.buildConstantLiteral(getConstantForNode(node)); |
| } |
| |
| ir.ExecutableDefinition nullIfGiveup(ir.ExecutableDefinition action()) { |
| try { |
| return action(); |
| } catch(e, tr) { |
| if (e == ABORT_IRNODE_BUILDER) { |
| return null; |
| } |
| rethrow; |
| } |
| } |
| |
| void internalError(ast.Node node, String message) { |
| giveup(node); |
| } |
| |
| @override |
| visitNode(ast.Node node) { |
| internalError(node, "Unhandled node"); |
| } |
| } |
| |
| final String ABORT_IRNODE_BUILDER = "IrNode builder aborted"; |
| |
| dynamic giveup(ast.Node node, [String reason]) { |
| throw ABORT_IRNODE_BUILDER; |
| } |
| |
| /// Classifies local variables and local functions as captured, if they |
| /// are accessed from within a nested function. |
| /// |
| /// This class is specific to the [DartIrBuilder], in that it gives up if it |
| /// sees a feature that is currently unsupport by that builder. In particular, |
| /// loop variables captured in a for-loop initializer, condition, or update |
| /// expression are unsupported. |
| class DartCapturedVariables extends ast.Visitor { |
| final TreeElements elements; |
| DartCapturedVariables(this.elements); |
| |
| FunctionElement currentFunction; |
| bool insideInitializer = false; |
| Set<Local> capturedVariables = new Set<Local>(); |
| |
| Map<ast.TryStatement, TryStatementInfo> tryStatements = |
| <ast.TryStatement, TryStatementInfo>{}; |
| |
| List<TryStatementInfo> tryNestingStack = <TryStatementInfo>[]; |
| bool get inTryStatement => tryNestingStack.isNotEmpty; |
| |
| void markAsCaptured(Local local) { |
| capturedVariables.add(local); |
| } |
| |
| visit(ast.Node node) => node.accept(this); |
| |
| visitNode(ast.Node node) { |
| node.visitChildren(this); |
| } |
| |
| visitFor(ast.For node) { |
| if (node.initializer != null) visit(node.initializer); |
| if (node.condition != null) visit(node.condition); |
| if (node.update != null) visit(node.update); |
| |
| // Give up if a variable was captured outside of the loop body. |
| if (node.initializer is ast.VariableDefinitions) { |
| ast.VariableDefinitions definitions = node.initializer; |
| for (ast.Node node in definitions.definitions.nodes) { |
| LocalElement loopVariable = elements[node]; |
| if (capturedVariables.contains(loopVariable)) { |
| return giveup(node, 'For-loop variable captured in loop header'); |
| } |
| } |
| } |
| |
| if (node.body != null) visit(node.body); |
| } |
| |
| void handleSend(ast.Send node) { |
| Element element = elements[node]; |
| if (Elements.isLocal(element) && |
| !element.isConst && |
| element.enclosingElement != currentFunction) { |
| LocalElement local = element; |
| markAsCaptured(local); |
| } |
| } |
| |
| visitSend(ast.Send node) { |
| handleSend(node); |
| node.visitChildren(this); |
| } |
| |
| visitSendSet(ast.SendSet node) { |
| handleSend(node); |
| Element element = elements[node]; |
| if (Elements.isLocal(element)) { |
| LocalElement local = element; |
| if (insideInitializer) { |
| assert(local.isParameter); |
| // Initializers in an initializer-list can communicate via parameters. |
| // If a parameter is stored in an initializer list we box it. |
| // TODO(sigurdm): Fix this. |
| // Though these variables do not outlive the activation of the |
| // function, they still need to be boxed. As a simplification, we |
| // treat them as if they are captured by a closure (i.e., they do |
| // outlive the activation of the function). |
| markAsCaptured(local); |
| } else if (inTryStatement) { |
| assert(local.isParameter || local.isVariable); |
| // Search for the position of the try block containing the variable |
| // declaration, or -1 if it is declared outside the outermost try. |
| int i = tryNestingStack.length - 1; |
| while (i >= 0 && !tryNestingStack[i].declared.contains(local)) { |
| --i; |
| } |
| // If there is a next inner try, then the variable should be boxed on |
| // entry to it. |
| if (i + 1 < tryNestingStack.length) { |
| tryNestingStack[i + 1].boxedOnEntry.add(local); |
| } |
| } |
| } |
| node.visitChildren(this); |
| } |
| |
| visitFunctionExpression(ast.FunctionExpression node) { |
| FunctionElement oldFunction = currentFunction; |
| currentFunction = elements[node]; |
| if (currentFunction.asyncMarker != AsyncMarker.SYNC) { |
| giveup(node, "cannot handle async/sync*/async* functions"); |
| } |
| if (node.initializers != null) { |
| insideInitializer = true; |
| visit(node.initializers); |
| insideInitializer = false; |
| } |
| visit(node.body); |
| currentFunction = oldFunction; |
| } |
| |
| visitTryStatement(ast.TryStatement node) { |
| TryStatementInfo info = new TryStatementInfo(); |
| tryStatements[node] = info; |
| tryNestingStack.add(info); |
| visit(node.tryBlock); |
| assert(tryNestingStack.last == info); |
| tryNestingStack.removeLast(); |
| |
| visit(node.catchBlocks); |
| if (node.finallyBlock != null) visit(node.finallyBlock); |
| } |
| |
| visitVariableDefinitions(ast.VariableDefinitions node) { |
| if (inTryStatement) { |
| for (ast.Node definition in node.definitions.nodes) { |
| LocalVariableElement local = elements[definition]; |
| assert(local != null); |
| // In the closure conversion pass we check for isInitializingFormal, |
| // but I'm not sure it can arise. |
| assert(!local.isInitializingFormal); |
| tryNestingStack.last.declared.add(local); |
| } |
| } |
| node.visitChildren(this); |
| } |
| } |
| |
| /// 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, |
| SourceInformationBuilder sourceInformationBuilder) |
| : super(elements, compiler, sourceInformationBuilder); |
| |
| DartIrBuilder makeIRBuilder(ast.Node node, ExecutableElement element) { |
| DartCapturedVariables closures = new DartCapturedVariables(elements); |
| if (!element.isSynthesized) { |
| closures.visit(node); |
| } |
| return new DartIrBuilder(compiler.backend.constantSystem, |
| element, |
| closures); |
| } |
| |
| /// Recursively builds the IR for the given nested function. |
| 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 getClosureScopeForNode(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 = 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: getClosureScopeForNode(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.asyncMarker != AsyncMarker.SYNC) { |
| giveup(null, 'cannot handle async-await'); |
| } |
| |
| 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)); |
| } |
| |
| List<ir.Primitive> normalizeStaticArguments( |
| Selector selector, |
| FunctionElement target, |
| List<ir.Primitive> arguments) { |
| return arguments; |
| } |
| |
| List<ir.Primitive> normalizeDynamicArguments( |
| Selector selector, |
| List<ir.Primitive> arguments) { |
| return arguments; |
| } |
| |
| @override |
| ir.Primitive buildReifyTypeVariable(ir.Primitive target, |
| TypeVariableType variable) { |
| assert(target == irBuilder.state.enclosingMethodThisParameter); |
| ir.Primitive prim = new ir.ReifyTypeVar(variable.element); |
| irBuilder.add(new ir.LetPrim(prim)); |
| return prim; |
| } |
| } |
| |
| /// 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; |
| |
| /// Result of closure conversion for the current body of code. |
| /// |
| /// Will be initialized upon entering the body of a function. |
| /// It is computed by the [ClosureTranslator]. |
| ClosureClassMap closureMap; |
| |
| /// During construction of a constructor factory, [fieldValues] maps fields |
| /// to the primitive containing their initial value. |
| Map<FieldElement, ir.Primitive> fieldValues = <FieldElement, ir.Primitive>{}; |
| |
| JsIrBuilderVisitor(TreeElements elements, |
| Compiler compiler, |
| SourceInformationBuilder sourceInformationBuilder) |
| : super(elements, compiler, sourceInformationBuilder); |
| |
| /// Builds the IR for creating an instance of the closure class corresponding |
| /// to the given nested function. |
| 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. |
| /// 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`), 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, |
| /// returns a [ClosureScope] naming a box to create, and enumerating the |
| /// variables that should be stored in the box. |
| /// |
| /// Also see [ClosureScope]. |
| ClosureScope getClosureScopeForNode(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 getClosureScopeForFunction(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"); |
| } |
| }); |
| } |
| |
| /// Builds the IR for an [expression] taken from a different [context]. |
| /// |
| /// Such expressions need to be compiled with a different [sourceFile] and |
| /// [elements] mapping. |
| ir.Primitive inlineExpression(AstElement context, ast.Expression expression) { |
| JsIrBuilderVisitor visitor = new JsIrBuilderVisitor( |
| context.resolvedAst.elements, |
| compiler, |
| sourceInformationBuilder.forContext(context)); |
| return visitor.withBuilder(irBuilder, () => visitor.visit(expression)); |
| } |
| |
| /// Builds the IR for a constant taken from a different [context]. |
| /// |
| /// Such constants need to be compiled with a different [sourceFile] and |
| /// [elements] mapping. |
| ir.Primitive inlineConstant(AstElement context, ast.Expression exp) { |
| JsIrBuilderVisitor visitor = new JsIrBuilderVisitor( |
| context.resolvedAst.elements, |
| compiler, |
| sourceInformationBuilder.forContext(context)); |
| return visitor.withBuilder(irBuilder, () => visitor.translateConstant(exp)); |
| } |
| |
| /// Builds the IR for a given constructor. |
| /// |
| /// 1. Evaluates all own or inherited field initializers. |
| /// 2. Creates the object and assigns its fields. |
| /// 3. Calls constructor body and super constructor bodies. |
| /// 4. Returns the created object. |
| ir.FunctionDefinition buildConstructor(ConstructorElement constructor) { |
| constructor = constructor.implementation; |
| ClassElement classElement = constructor.enclosingClass.implementation; |
| |
| 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); |
| builder.buildFunctionHeader(parameters, |
| closureScope: getClosureScopeForFunction(constructor)); |
| |
| // -- Step 1: evaluate field initializers --- |
| // 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 c, FieldElement field) { |
| ir.Primitive value = fieldValues[field]; |
| if (value != null) { |
| instanceArguments.add(fieldValues[field]); |
| } else { |
| assert(Elements.isNativeOrExtendsNative(c)); |
| // 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; // Skip if constructor has no body. |
| 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 declaration-site field initializers. |
| ClassElement enclosingClass = constructor.enclosingClass.implementation; |
| enclosingClass.forEachInstanceField((ClassElement c, FieldElement field) { |
| if (field.initializer != null) { |
| fieldValues[field] = inlineExpression(field, field.initializer); |
| } else { |
| if (Elements.isNativeOrExtendsNative(c)) { |
| // Native field is initialized elsewhere. |
| } else { |
| // Fields without an initializer default to null. |
| // This value will be overwritten below if an initializer is found. |
| fieldValues[field] = irBuilder.buildNullLiteral(); |
| } |
| } |
| }); |
| // 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] = |
| inlineExpression(constructor, 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. |
| if (!hasConstructorCall && !enclosingClass.isObject) { |
| ClassElement superClass = enclosingClass.superclass; |
| FunctionElement target = superClass.lookupDefaultConstructor(); |
| 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 = getClosureScopeForFunction(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 nameIndex = selector.namedArguments.indexOf(param.name); |
| if (nameIndex != -1) { |
| int translatedIndex = selector.positionalArgumentCount + nameIndex; |
| 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 = inlineExpression(target, 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. |
| */ |
| 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) { |
| 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. |
| /// |
| /// Boxed parameters are not in the list, instead, a [BoxLocal] is passed |
| /// containing the boxed parameters. |
| /// |
| /// For example, given the following constructor, |
| /// |
| /// Foo(x, y) : field = (() => ++x) { print(x + y) } |
| /// |
| /// the argument `x` would be replaced by a [BoxLocal]: |
| /// |
| /// Foo_body(box0, y) { print(box0.x + y) } |
| /// |
| List<Local> getConstructorBodyParameters(ConstructorBodyElement body) { |
| List<Local> parameters = <Local>[]; |
| ClosureScope scope = getClosureScopeForFunction(body.constructor); |
| if (scope != null) { |
| parameters.add(scope.box); |
| } |
| body.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), |
| getClosureScopeForNode(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); |
| |
| closureMap = compiler.closureToClassMapper.computeClosureToClassMapping( |
| element, |
| node, |
| elements); |
| IrBuilder builder = |
| new JsIrBuilder(compiler.backend.constantSystem, element); |
| return withBuilder(builder, () => _makeFunctionBody(element, node)); |
| } |
| |
| /// Creates a primitive for the default value of [parameter]. |
| ir.Primitive translateDefaultValue(ParameterElement parameter) { |
| if (parameter.initializer == null) { |
| return irBuilder.buildNullLiteral(); |
| } else { |
| return inlineConstant(parameter.executableContext, parameter.initializer); |
| } |
| } |
| |
| /// Inserts default arguments and normalizes order of named arguments. |
| List<ir.Primitive> normalizeStaticArguments( |
| Selector selector, |
| FunctionElement target, |
| List<ir.Primitive> arguments) { |
| target = target.implementation; |
| FunctionSignature signature = target.functionSignature; |
| if (!signature.optionalParametersAreNamed && |
| signature.parameterCount == arguments.length) { |
| // Optimization: don't copy the argument list for trivial cases. |
| return arguments; |
| } |
| |
| List<ir.Primitive> result = <ir.Primitive>[]; |
| int i = 0; |
| signature.forEachRequiredParameter((ParameterElement element) { |
| result.add(arguments[i]); |
| ++i; |
| }); |
| |
| if (!signature.optionalParametersAreNamed) { |
| signature.forEachOptionalParameter((ParameterElement element) { |
| if (i < arguments.length) { |
| result.add(arguments[i]); |
| ++i; |
| } else { |
| result.add(translateDefaultValue(element)); |
| } |
| }); |
| } else { |
| int offset = i; |
| // Iterate over the optional parameters of the signature, and try to |
| // find them in [compiledNamedArguments]. If found, we use the |
| // value in the temporary list, otherwise the default value. |
| signature.orderedOptionalParameters.forEach((ParameterElement element) { |
| int nameIndex = selector.namedArguments.indexOf(element.name); |
| if (nameIndex != -1) { |
| int translatedIndex = offset + nameIndex; |
| result.add(arguments[translatedIndex]); |
| } else { |
| result.add(translateDefaultValue(element)); |
| } |
| }); |
| } |
| return result; |
| } |
| |
| /// Normalizes order of named arguments. |
| List<ir.Primitive> normalizeDynamicArguments( |
| Selector selector, |
| List<ir.Primitive> arguments) { |
| assert(arguments.length == selector.argumentCount); |
| // Optimization: don't copy the argument list for trivial cases. |
| if (selector.namedArguments.isEmpty) return arguments; |
| List<ir.Primitive> result = <ir.Primitive>[]; |
| for (int i=0; i < selector.positionalArgumentCount; i++) { |
| result.add(arguments[i]); |
| } |
| for (String argName in selector.getOrderedNamedArguments()) { |
| int nameIndex = selector.namedArguments.indexOf(argName); |
| int translatedIndex = selector.positionalArgumentCount + nameIndex; |
| result.add(arguments[translatedIndex]); |
| } |
| return result; |
| } |
| |
| @override |
| ir.Primitive buildReifyTypeVariable(ir.Primitive target, |
| TypeVariableType variable) { |
| ir.Primitive typeArgument = new ir.ReadTypeVariable(variable, target); |
| irBuilder.add(new ir.LetPrim(typeArgument)); |
| ir.Primitive type = new ir.ReifyRuntimeType(typeArgument); |
| irBuilder.add(new ir.LetPrim(type)); |
| return type; |
| } |
| } |
| |
| /// Interface for generating [SourceInformation] for the CPS. |
| class SourceInformationBuilder { |
| const SourceInformationBuilder(); |
| |
| /// Create a [SourceInformationBuilder] for [element]. |
| SourceInformationBuilder forContext(AstElement element) => this; |
| |
| /// Generate [SourceInformation] for the read access in [node]. |
| SourceInformation buildGet(ast.Node node) => null; |
| |
| /// Generate [SourceInformation] for the invocation in [node]. |
| SourceInformation buildCall(ast.Node node) => null; |
| } |
| |
| /// [SourceInformationBuilder] that generates [PositionSourceInformation]. |
| class PositionSourceInformationBuilder implements SourceInformationBuilder { |
| final SourceFile sourceFile; |
| final String name; |
| |
| PositionSourceInformationBuilder(AstElement element) |
| : sourceFile = element.compilationUnit.script.file, |
| name = element.name; |
| |
| @override |
| SourceInformation buildGet(ast.Node node) { |
| return new PositionSourceInformation( |
| new TokenSourceLocation(sourceFile, node.getBeginToken(), name)); |
| } |
| |
| @override |
| SourceInformation buildCall(ast.Node node) { |
| return new PositionSourceInformation( |
| new TokenSourceLocation(sourceFile, node.getBeginToken(), name)); |
| } |
| |
| @override |
| SourceInformationBuilder forContext(AstElement element) { |
| return new PositionSourceInformationBuilder(element); |
| } |
| } |