Index: sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder.dart b/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder.dart |
index 9c7b87d16dc78eea7c15ed8a529c08013d44ac3b..6a2c566ff9469224862ea35b446dfe719ec7f4d2 100644 |
--- a/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder.dart |
+++ b/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder.dart |
@@ -16,106 +16,7 @@ import '../scanner/scannerlib.dart' show Token, isUserDefinableOperator; |
import '../universe/universe.dart' show SelectorKind; |
import 'cps_ir_nodes.dart' as ir; |
-/** |
- * 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.FunctionDefinition> nodes = |
- <Element, ir.FunctionDefinition>{}; |
- |
- IrBuilderTask(Compiler compiler) : super(compiler); |
- |
- String get name => 'IR builder'; |
- |
- bool hasIr(Element element) => nodes.containsKey(element.implementation); |
- |
- ir.FunctionDefinition getIr(Element element) => nodes[element.implementation]; |
- |
- void buildNodes({bool useNewBackend: false}) { |
- if (!irEnabled(useNewBackend: useNewBackend)) return; |
- measure(() { |
- Set<Element> resolved = compiler.enqueuer.resolution.resolvedElements; |
- resolved.forEach((AstElement element) { |
- if (canBuild(element)) { |
- TreeElements elementsMapping = element.resolvedAst.elements; |
- element = element.implementation; |
- compiler.withCurrentElement(element, () { |
- SourceFile sourceFile = elementSourceFile(element); |
- IrBuilderVisitor builder = |
- new IrBuilderVisitor(elementsMapping, compiler, sourceFile); |
- ir.FunctionDefinition function; |
- function = builder.buildFunction(element); |
- |
- if (function != null) { |
- nodes[element] = function; |
- compiler.tracer.traceCompilation(element.name, null); |
- compiler.tracer.traceGraph("IR Builder", function); |
- } |
- }); |
- } |
- }); |
- }); |
- } |
- |
- bool irEnabled({bool useNewBackend: false}) { |
- // TODO(sigurdm,kmillikin): Support checked-mode checks. |
- return (useNewBackend || const bool.fromEnvironment('USE_NEW_BACKEND')) && |
- compiler.backend is DartBackend && |
- !compiler.enableTypeAssertions && |
- !compiler.enableConcreteTypeInference; |
- } |
- |
- bool canBuild(Element element) { |
- FunctionElement function = element.asFunctionElement(); |
- // TODO(kmillikin,sigurdm): support lazy field initializers. |
- if (function == null) return false; |
- |
- if (!compiler.backend.shouldOutput(function)) return false; |
- |
- assert(invariant(element, !function.isNative)); |
- |
- // TODO(kmillikin,sigurdm): Support constructors. |
- if (function is ConstructorElement) return false; |
- |
- return true; |
- } |
- |
- bool get inCheckedMode { |
- bool result = false; |
- assert((result = true)); |
- return result; |
- } |
- |
- SourceFile elementSourceFile(Element element) { |
- if (element is FunctionElement) { |
- FunctionElement functionElement = element; |
- if (functionElement.patch != null) element = functionElement.patch; |
- } |
- return element.compilationUnit.script.file; |
- } |
-} |
- |
-class _GetterElements { |
- ir.Primitive result; |
- ir.Primitive index; |
- ir.Primitive receiver; |
- |
- _GetterElements({this.result, this.index, this.receiver}) ; |
-} |
+part 'cps_ir_builder_visitor.dart'; |
/// A mapping from variable elements to their compile-time values. |
/// |
@@ -595,1483 +496,3 @@ class IrBuilder { |
return false; |
} |
} |
- |
-/** |
- * 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. |
- */ |
-class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive> |
- with IrBuilderMixin { |
- final Compiler compiler; |
- final SourceFile sourceFile; |
- |
- // 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.sourceFile) |
- : super(elements); |
- |
- /** |
- * Builds the [ir.FunctionDefinition] for a function element. In case the |
- * function uses features that cannot be expressed in the IR, this function |
- * returns `null`. |
- */ |
- ir.FunctionDefinition buildFunction(FunctionElement functionElement) { |
- return nullIfGiveup(() => buildFunctionInternal(functionElement)); |
- } |
- |
- ir.FunctionDefinition buildFunctionInternal(FunctionElement element) { |
- assert(invariant(element, element.isImplementation)); |
- ast.FunctionExpression function = element.node; |
- assert(function != null); |
- assert(!function.modifiers.isExternal); |
- assert(elements[function] != null); |
- |
- DetectClosureVariables closureLocals = new DetectClosureVariables(elements); |
- closureLocals.visit(function); |
- |
- return withBuilder( |
- new IrBuilder(compiler.backend.constantSystem, |
- element, closureLocals.usedFromClosure), |
- () { |
- FunctionSignature signature = element.functionSignature; |
- signature.orderedForEachParameter((ParameterElement parameterElement) { |
- irBuilder.createParameter( |
- parameterElement, |
- isClosureVariable: isClosureVariable(parameterElement)); |
- }); |
- |
- List<ConstantExpression> defaults = new List<ConstantExpression>(); |
- signature.orderedOptionalParameters.forEach((ParameterElement element) { |
- defaults.add(getConstantForVariable(element)); |
- }); |
- |
- visit(function.body); |
- return irBuilder.buildFunctionDefinition(element, defaults); |
- }); |
- } |
- |
- ir.Primitive visit(ast.Node node) => node.accept(this); |
- |
- // ==== Statements ==== |
- // Build(Block(stamements), C) = C' |
- // where C' = statements.fold(Build, C) |
- ir.Primitive visitBlock(ast.Block node) { |
- assert(irBuilder.isOpen); |
- for (ast.Node n in node.statements.nodes) { |
- visit(n); |
- if (!irBuilder.isOpen) return null; |
- } |
- return null; |
- } |
- |
- 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; |
- } |
- |
- |
- /// Create a non-recursive join-point continuation. |
- /// |
- /// Given the environment length at the join point and a list of |
- /// jumps that should reach the join point, create a join-point |
- /// continuation. The join-point continuation has a parameter for each |
- /// variable that has different values reaching on different paths. |
- /// |
- /// The jumps are uninitialized [ir.InvokeContinuation] expressions. |
- /// They are filled in with the target continuation and appropriate |
- /// arguments. |
- /// |
- /// As a side effect, the environment of this builder is updated to include |
- /// the join-point continuation parameters. |
- ir.Continuation createJoin(int environmentLength, JumpCollector jumps) { |
- assert(jumps.length >= 2); |
- |
- // Compute which values are identical on all paths reaching the join. |
- // Handle the common case of a pair of contexts efficiently. |
- Environment first = jumps.environments[0]; |
- Environment second = jumps.environments[1]; |
- assert(environmentLength <= first.length); |
- assert(environmentLength <= second.length); |
- assert(first.sameDomain(environmentLength, second)); |
- // A running count of the join-point parameters. |
- int parameterCount = 0; |
- // The null elements of common correspond to required parameters of the |
- // join-point continuation. |
- List<ir.Primitive> common = |
- new List<ir.Primitive>.generate(environmentLength, |
- (i) { |
- ir.Primitive candidate = first[i]; |
- if (second[i] == candidate) { |
- return candidate; |
- } else { |
- ++parameterCount; |
- return null; |
- } |
- }); |
- // If there is already a parameter for each variable, the other |
- // environments do not need to be considered. |
- if (parameterCount < environmentLength) { |
- for (int i = 0; i < environmentLength; ++i) { |
- ir.Primitive candidate = common[i]; |
- if (candidate == null) continue; |
- for (Environment current in jumps.environments.skip(2)) { |
- assert(environmentLength <= current.length); |
- assert(first.sameDomain(environmentLength, current)); |
- if (candidate != current[i]) { |
- common[i] = null; |
- ++parameterCount; |
- break; |
- } |
- } |
- if (parameterCount >= environmentLength) break; |
- } |
- } |
- |
- // Create the join point continuation. |
- List<ir.Parameter> parameters = <ir.Parameter>[]; |
- parameters.length = parameterCount; |
- int index = 0; |
- for (int i = 0; i < environmentLength; ++i) { |
- if (common[i] == null) { |
- parameters[index++] = new ir.Parameter(first.index2variable[i]); |
- } |
- } |
- assert(index == parameterCount); |
- ir.Continuation join = new ir.Continuation(parameters); |
- |
- // Fill in all the continuation invocations. |
- for (int i = 0; i < jumps.length; ++i) { |
- Environment currentEnvironment = jumps.environments[i]; |
- ir.InvokeContinuation invoke = jumps.invocations[i]; |
- // Sharing this.environment with one of the invocations will not do |
- // the right thing (this.environment has already been mutated). |
- List<ir.Reference> arguments = <ir.Reference>[]; |
- arguments.length = parameterCount; |
- int index = 0; |
- for (int i = 0; i < environmentLength; ++i) { |
- if (common[i] == null) { |
- arguments[index++] = new ir.Reference(currentEnvironment[i]); |
- } |
- } |
- invoke.continuation = new ir.Reference(join); |
- invoke.arguments = arguments; |
- } |
- |
- // Mutate this.environment to be the environment at the join point. Do |
- // this after adding the continuation invocations, because this.environment |
- // might be collected by the jump collector and so the old environment |
- // values are needed for the continuation invocation. |
- // |
- // Iterate to environment.length because environmentLength includes values |
- // outside the environment which are 'phantom' variables used for the |
- // values of expressions like &&, ||, and ?:. |
- index = 0; |
- for (int i = 0; i < irBuilder.environment.length; ++i) { |
- if (common[i] == null) { |
- irBuilder.environment.index2value[i] = parameters[index++]; |
- } |
- } |
- |
- return join; |
- } |
- |
- /// Invoke a join-point continuation that contains arguments for all local |
- /// variables. |
- /// |
- /// Given the continuation and a list of uninitialized invocations, fill |
- /// in each invocation with the continuation and appropriate arguments. |
- void invokeFullJoin(ir.Continuation join, |
- JumpCollector jumps, |
- {recursive: false}) { |
- join.isRecursive = recursive; |
- for (int i = 0; i < jumps.length; ++i) { |
- Environment currentEnvironment = jumps.environments[i]; |
- ir.InvokeContinuation invoke = jumps.invocations[i]; |
- invoke.continuation = new ir.Reference(join); |
- invoke.arguments = new List<ir.Reference>.generate( |
- join.parameters.length, |
- (i) => new ir.Reference(currentEnvironment[i])); |
- invoke.isRecursive = recursive; |
- } |
- } |
- |
- ir.Primitive visitFor(ast.For node) { |
- assert(irBuilder.isOpen); |
- // TODO(kmillikin,sigurdm): Handle closure variables declared in a for-loop. |
- if (node.initializer is ast.VariableDefinitions) { |
- ast.VariableDefinitions definitions = node.initializer; |
- for (ast.Node definition in definitions.definitions.nodes) { |
- Element element = elements[definition]; |
- if (isClosureVariable(element)) { |
- return giveup(definition, 'Closure variable in for loop initializer'); |
- } |
- } |
- } |
- |
- // For loops use four named continuations: the entry to the condition, |
- // the entry to the body, the loop exit, and the loop successor (break). |
- // The CPS translation of |
- // [[for (initializer; condition; update) body; successor]] is: |
- // |
- // [[initializer]]; |
- // let cont loop(x, ...) = |
- // let prim cond = [[condition]] in |
- // let cont break() = [[successor]] in |
- // let cont exit() = break(v, ...) in |
- // let cont body() = |
- // let cont continue(x, ...) = [[update]]; loop(v, ...) in |
- // [[body]]; continue(v, ...) in |
- // branch cond (body, exit) in |
- // loop(v, ...) |
- // |
- // If there are no breaks in the body, the break continuation is inlined |
- // in the exit continuation (i.e., the translation of the successor |
- // statement occurs in the exit continuation). If there is only one |
- // invocation of the continue continuation (i.e., no continues in the |
- // body), the continue continuation is inlined in the body. |
- |
- if (node.initializer != null) visit(node.initializer); |
- |
- IrBuilder condBuilder = new IrBuilder.recursive(irBuilder); |
- ir.Primitive condition; |
- if (node.condition == null) { |
- // If the condition is empty then the body is entered unconditionally. |
- condition = irBuilder.makePrimConst( |
- irBuilder.state.constantSystem.createBool(true)); |
- condBuilder.add(new ir.LetPrim(condition)); |
- } else { |
- condition = withBuilder(condBuilder, () => visit(node.condition)); |
- } |
- |
- JumpTarget target = elements.getTargetDefinition(node); |
- JumpCollector breakCollector = new JumpCollector(target); |
- JumpCollector continueCollector = new JumpCollector(target); |
- irBuilder.state.breakCollectors.add(breakCollector); |
- irBuilder.state.continueCollectors.add(continueCollector); |
- |
- IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder); |
- withBuilder(bodyBuilder, () => visit(node.body)); |
- assert(irBuilder.state.breakCollectors.last == breakCollector); |
- assert(irBuilder.state.continueCollectors.last == continueCollector); |
- irBuilder.state.breakCollectors.removeLast(); |
- irBuilder.state.continueCollectors.removeLast(); |
- |
- // The binding of the continue continuation should occur as late as |
- // possible, that is, at the nearest common ancestor of all the continue |
- // sites in the body. However, that is difficult to compute here, so it |
- // is instead placed just outside the body of the body continuation. |
- bool hasContinues = !continueCollector.isEmpty; |
- IrBuilder updateBuilder = hasContinues |
- ? new IrBuilder.recursive(condBuilder) |
- : bodyBuilder; |
- for (ast.Node n in node.update) { |
- if (!updateBuilder.isOpen) break; |
- withBuilder(updateBuilder, () => visit(n)); |
- } |
- |
- // Create body entry and loop exit continuations and a branch to them. |
- ir.Continuation bodyContinuation = new ir.Continuation([]); |
- ir.Continuation exitContinuation = new ir.Continuation([]); |
- ir.LetCont branch = |
- new ir.LetCont(exitContinuation, |
- new ir.LetCont(bodyContinuation, |
- new ir.Branch(new ir.IsTrue(condition), |
- bodyContinuation, |
- exitContinuation))); |
- // If there are breaks in the body, then there must be a join-point |
- // continuation for the normal exit and the breaks. |
- bool hasBreaks = !breakCollector.isEmpty; |
- ir.LetCont letJoin; |
- if (hasBreaks) { |
- letJoin = new ir.LetCont(null, branch); |
- condBuilder.add(letJoin); |
- condBuilder._current = branch; |
- } else { |
- condBuilder.add(branch); |
- } |
- ir.Continuation continueContinuation; |
- if (hasContinues) { |
- // If there are continues in the body, we need a named continue |
- // continuation as a join point. |
- continueContinuation = new ir.Continuation(updateBuilder._parameters); |
- if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder); |
- invokeFullJoin(continueContinuation, continueCollector); |
- } |
- ir.Continuation loopContinuation = |
- new ir.Continuation(condBuilder._parameters); |
- if (updateBuilder.isOpen) { |
- JumpCollector backEdges = new JumpCollector(null); |
- backEdges.addJump(updateBuilder); |
- invokeFullJoin(loopContinuation, backEdges, recursive: true); |
- } |
- |
- // Fill in the body and possible continue continuation bodies. Do this |
- // only after it is guaranteed that they are not empty. |
- if (hasContinues) { |
- continueContinuation.body = updateBuilder._root; |
- bodyContinuation.body = |
- new ir.LetCont(continueContinuation, bodyBuilder._root); |
- } else { |
- bodyContinuation.body = bodyBuilder._root; |
- } |
- |
- loopContinuation.body = condBuilder._root; |
- irBuilder.add(new ir.LetCont(loopContinuation, |
- new ir.InvokeContinuation(loopContinuation, |
- irBuilder.environment.index2value))); |
- if (hasBreaks) { |
- irBuilder._current = branch; |
- irBuilder.environment = condBuilder.environment; |
- breakCollector.addJump(irBuilder); |
- letJoin.continuation = |
- createJoin(irBuilder.environment.length, breakCollector); |
- irBuilder._current = letJoin; |
- } else { |
- irBuilder._current = condBuilder._current; |
- irBuilder.environment = condBuilder.environment; |
- } |
- return null; |
- } |
- |
- ir.Primitive visitIf(ast.If node) { |
- assert(irBuilder.isOpen); |
- ir.Primitive condition = visit(node.condition); |
- |
- // The then and else parts are delimited. |
- IrBuilder thenBuilder = new IrBuilder.delimited(irBuilder); |
- IrBuilder elseBuilder = new IrBuilder.delimited(irBuilder); |
- withBuilder(thenBuilder, () => visit(node.thenPart)); |
- if (node.hasElsePart) { |
- withBuilder(elseBuilder, () => visit(node.elsePart)); |
- } |
- |
- // Build the term |
- // (Result =) let cont then() = [[thenPart]] in |
- // let cont else() = [[elsePart]] in |
- // if condition (then, else) |
- ir.Continuation thenContinuation = new ir.Continuation([]); |
- ir.Continuation elseContinuation = new ir.Continuation([]); |
- ir.Expression letElse = |
- new ir.LetCont(elseContinuation, |
- new ir.Branch(new ir.IsTrue(condition), |
- thenContinuation, |
- elseContinuation)); |
- ir.Expression letThen = new ir.LetCont(thenContinuation, letElse); |
- ir.Expression result = letThen; |
- |
- ir.Continuation joinContinuation; // Null if there is no join. |
- if (thenBuilder.isOpen && elseBuilder.isOpen) { |
- // There is a join-point continuation. Build the term |
- // 'let cont join(x, ...) = [] in Result' and plug invocations of the |
- // join-point continuation into the then and else continuations. |
- JumpCollector jumps = new JumpCollector(null); |
- jumps.addJump(thenBuilder); |
- jumps.addJump(elseBuilder); |
- joinContinuation = createJoin(irBuilder.environment.length, jumps); |
- result = new ir.LetCont(joinContinuation, result); |
- } |
- |
- // The then or else term root could be null, but not both. If there is |
- // a join then an InvokeContinuation was just added to both of them. If |
- // there is no join, then at least one of them is closed and thus has a |
- // non-null root by the definition of the predicate isClosed. In the |
- // case that one of them is null, it must be the only one that is open |
- // and thus contains the new hole in the context. This case is handled |
- // after the branch is plugged into the current hole. |
- thenContinuation.body = thenBuilder._root; |
- elseContinuation.body = elseBuilder._root; |
- |
- irBuilder.add(result); |
- if (joinContinuation == null) { |
- // At least one subexpression is closed. |
- if (thenBuilder.isOpen) { |
- irBuilder._current = |
- (thenBuilder._root == null) ? letThen : thenBuilder._current; |
- irBuilder.environment = thenBuilder.environment; |
- } else if (elseBuilder.isOpen) { |
- irBuilder._current = |
- (elseBuilder._root == null) ? letElse : elseBuilder._current; |
- irBuilder.environment = elseBuilder.environment; |
- } else { |
- irBuilder._current = null; |
- } |
- } |
- return null; |
- } |
- |
- ir.Primitive visitLabeledStatement(ast.LabeledStatement node) { |
- ast.Statement body = node.statement; |
- return body is ast.Loop |
- ? visit(body) |
- : giveup(node, 'labeled statement'); |
- } |
- |
- ir.Primitive visitWhile(ast.While node) { |
- assert(irBuilder.isOpen); |
- // While loops use four named continuations: the entry to the body, the |
- // loop exit, the loop back edge (continue), and the loop exit (break). |
- // The CPS translation of [[while (condition) body; successor]] is: |
- // |
- // let cont continue(x, ...) = |
- // let prim cond = [[condition]] in |
- // let cont break() = [[successor]] in |
- // let cont exit() = break(v, ...) in |
- // let cont body() = [[body]]; continue(v, ...) in |
- // branch cond (body, exit) in |
- // continue(v, ...) |
- // |
- // If there are no breaks in the body, the break continuation is inlined |
- // in the exit continuation (i.e., the translation of the successor |
- // statement occurs in the exit continuation). |
- |
- // The condition and body are delimited. |
- IrBuilder condBuilder = new IrBuilder.recursive(irBuilder); |
- ir.Primitive condition = |
- withBuilder(condBuilder, () => visit(node.condition)); |
- |
- JumpTarget target = elements.getTargetDefinition(node); |
- JumpCollector breakCollector = new JumpCollector(target); |
- JumpCollector continueCollector = new JumpCollector(target); |
- irBuilder.state.breakCollectors.add(breakCollector); |
- irBuilder.state.continueCollectors.add(continueCollector); |
- |
- IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder); |
- withBuilder(bodyBuilder, () => visit(node.body)); |
- assert(irBuilder.state.breakCollectors.last == breakCollector); |
- assert(irBuilder.state.continueCollectors.last == continueCollector); |
- irBuilder.state.breakCollectors.removeLast(); |
- irBuilder.state.continueCollectors.removeLast(); |
- |
- // Create body entry and loop exit continuations and a branch to them. |
- ir.Continuation bodyContinuation = new ir.Continuation([]); |
- ir.Continuation exitContinuation = new ir.Continuation([]); |
- ir.LetCont branch = |
- new ir.LetCont(exitContinuation, |
- new ir.LetCont(bodyContinuation, |
- new ir.Branch(new ir.IsTrue(condition), |
- bodyContinuation, |
- exitContinuation))); |
- // If there are breaks in the body, then there must be a join-point |
- // continuation for the normal exit and the breaks. |
- bool hasBreaks = !breakCollector.isEmpty; |
- ir.LetCont letJoin; |
- if (hasBreaks) { |
- letJoin = new ir.LetCont(null, branch); |
- condBuilder.add(letJoin); |
- condBuilder._current = branch; |
- } else { |
- condBuilder.add(branch); |
- } |
- ir.Continuation loopContinuation = |
- new ir.Continuation(condBuilder._parameters); |
- if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder); |
- invokeFullJoin(loopContinuation, continueCollector, recursive: true); |
- bodyContinuation.body = bodyBuilder._root; |
- |
- loopContinuation.body = condBuilder._root; |
- irBuilder.add(new ir.LetCont(loopContinuation, |
- new ir.InvokeContinuation(loopContinuation, |
- irBuilder.environment.index2value))); |
- if (hasBreaks) { |
- irBuilder._current = branch; |
- irBuilder.environment = condBuilder.environment; |
- breakCollector.addJump(irBuilder); |
- letJoin.continuation = |
- createJoin(irBuilder.environment.length, breakCollector); |
- irBuilder._current = letJoin; |
- } else { |
- irBuilder._current = condBuilder._current; |
- irBuilder.environment = condBuilder.environment; |
- } |
- return null; |
- } |
- |
- ir.Primitive visitForIn(ast.ForIn node) { |
- // The for-in loop |
- // |
- // for (a in e) s; |
- // |
- // Is compiled analogously to: |
- // |
- // a = e.iterator; |
- // while (a.moveNext()) { |
- // var n0 = a.current; |
- // s; |
- // } |
- |
- // The condition and body are delimited. |
- IrBuilder condBuilder = new IrBuilder.recursive(irBuilder); |
- |
- ir.Primitive expressionReceiver = visit(node.expression); |
- List<ir.Primitive> emptyArguments = new List<ir.Primitive>(); |
- |
- ir.Parameter iterator = new ir.Parameter(null); |
- ir.Continuation iteratorInvoked = new ir.Continuation([iterator]); |
- irBuilder.add(new ir.LetCont(iteratorInvoked, |
- new ir.InvokeMethod(expressionReceiver, |
- new Selector.getter("iterator", null), iteratorInvoked, |
- emptyArguments))); |
- |
- ir.Parameter condition = new ir.Parameter(null); |
- ir.Continuation moveNextInvoked = new ir.Continuation([condition]); |
- condBuilder.add(new ir.LetCont(moveNextInvoked, |
- new ir.InvokeMethod(iterator, |
- new Selector.call("moveNext", null, 0), |
- moveNextInvoked, emptyArguments))); |
- |
- JumpTarget target = elements.getTargetDefinition(node); |
- JumpCollector breakCollector = new JumpCollector(target); |
- JumpCollector continueCollector = new JumpCollector(target); |
- irBuilder.state.breakCollectors.add(breakCollector); |
- irBuilder.state.continueCollectors.add(continueCollector); |
- |
- IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder); |
- ast.Node identifier = node.declaredIdentifier; |
- Element variableElement = elements.getForInVariable(node); |
- Selector selector = elements.getSelector(identifier); |
- |
- // node.declaredIdentifier can be either an ast.VariableDefinitions |
- // (defining a new local variable) or a send designating some existing |
- // variable. |
- ast.Node declaredIdentifier = node.declaredIdentifier; |
- |
- if (declaredIdentifier is ast.VariableDefinitions) { |
- withBuilder(bodyBuilder, () => visit(declaredIdentifier)); |
- } |
- |
- ir.Parameter currentValue = new ir.Parameter(null); |
- ir.Continuation currentInvoked = new ir.Continuation([currentValue]); |
- bodyBuilder.add(new ir.LetCont(currentInvoked, |
- new ir.InvokeMethod(iterator, new Selector.getter("current", null), |
- currentInvoked, emptyArguments))); |
- if (Elements.isLocal(variableElement)) { |
- withBuilder(bodyBuilder, () => setLocal(variableElement, currentValue)); |
- } else if (Elements.isStaticOrTopLevel(variableElement)) { |
- withBuilder(bodyBuilder, |
- () => setStatic(variableElement, selector, currentValue)); |
- } else { |
- ir.Primitive receiver = |
- withBuilder(bodyBuilder, () => lookupThis()); |
- withBuilder(bodyBuilder, |
- () => setDynamic(null, receiver, selector, currentValue)); |
- } |
- |
- withBuilder(bodyBuilder, () => visit(node.body)); |
- assert(irBuilder.state.breakCollectors.last == breakCollector); |
- assert(irBuilder.state.continueCollectors.last == continueCollector); |
- irBuilder.state.breakCollectors.removeLast(); |
- irBuilder.state.continueCollectors.removeLast(); |
- |
- // Create body entry and loop exit continuations and a branch to them. |
- ir.Continuation bodyContinuation = new ir.Continuation([]); |
- ir.Continuation exitContinuation = new ir.Continuation([]); |
- ir.LetCont branch = |
- new ir.LetCont(exitContinuation, |
- new ir.LetCont(bodyContinuation, |
- new ir.Branch(new ir.IsTrue(condition), |
- bodyContinuation, |
- exitContinuation))); |
- // If there are breaks in the body, then there must be a join-point |
- // continuation for the normal exit and the breaks. |
- bool hasBreaks = !breakCollector.isEmpty; |
- ir.LetCont letJoin; |
- if (hasBreaks) { |
- letJoin = new ir.LetCont(null, branch); |
- condBuilder.add(letJoin); |
- condBuilder._current = branch; |
- } else { |
- condBuilder.add(branch); |
- } |
- ir.Continuation loopContinuation = |
- new ir.Continuation(condBuilder._parameters); |
- if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder); |
- invokeFullJoin(loopContinuation, continueCollector, recursive: true); |
- bodyContinuation.body = bodyBuilder._root; |
- |
- loopContinuation.body = condBuilder._root; |
- irBuilder.add(new ir.LetCont(loopContinuation, |
- new ir.InvokeContinuation(loopContinuation, |
- irBuilder.environment.index2value))); |
- if (hasBreaks) { |
- irBuilder._current = branch; |
- irBuilder.environment = condBuilder.environment; |
- breakCollector.addJump(irBuilder); |
- letJoin.continuation = |
- createJoin(irBuilder.environment.length, breakCollector); |
- irBuilder._current = letJoin; |
- } else { |
- irBuilder._current = condBuilder._current; |
- irBuilder.environment = condBuilder.environment; |
- } |
- return null; |
- } |
- |
- 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, |
- isClosureVariable: isClosureVariable(element)); |
- } |
- } |
- 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')); |
- if (node.expression == null) { |
- irBuilder.buildReturn(); |
- } else { |
- irBuilder.buildReturn(visit(node.expression)); |
- } |
- return null; |
- } |
- |
- // ==== Expressions ==== |
- ir.Primitive visitConditional(ast.Conditional node) { |
- assert(irBuilder.isOpen); |
- ir.Primitive condition = visit(node.condition); |
- |
- // The then and else expressions are delimited. |
- IrBuilder thenBuilder = new IrBuilder.delimited(irBuilder); |
- IrBuilder elseBuilder = new IrBuilder.delimited(irBuilder); |
- ir.Primitive thenValue = |
- withBuilder(thenBuilder, () => visit(node.thenExpression)); |
- ir.Primitive elseValue = |
- withBuilder(elseBuilder, () => visit(node.elseExpression)); |
- |
- // Treat the values of the subexpressions as named values in the |
- // environment, so they will be treated as arguments to the join-point |
- // continuation. |
- assert(irBuilder.environment.length == thenBuilder.environment.length); |
- assert(irBuilder.environment.length == elseBuilder.environment.length); |
- thenBuilder.environment.extend(null, thenValue); |
- elseBuilder.environment.extend(null, elseValue); |
- JumpCollector jumps = new JumpCollector(null); |
- jumps.addJump(thenBuilder); |
- jumps.addJump(elseBuilder); |
- ir.Continuation joinContinuation = |
- createJoin(irBuilder.environment.length + 1, jumps); |
- |
- // Build the term |
- // let cont join(x, ..., result) = [] in |
- // let cont then() = [[thenPart]]; join(v, ...) in |
- // let cont else() = [[elsePart]]; join(v, ...) in |
- // if condition (then, else) |
- ir.Continuation thenContinuation = new ir.Continuation([]); |
- ir.Continuation elseContinuation = new ir.Continuation([]); |
- thenContinuation.body = thenBuilder._root; |
- elseContinuation.body = elseBuilder._root; |
- irBuilder.add(new ir.LetCont(joinContinuation, |
- new ir.LetCont(thenContinuation, |
- new ir.LetCont(elseContinuation, |
- new ir.Branch(new ir.IsTrue(condition), |
- thenContinuation, |
- elseContinuation))))); |
- return (thenValue == elseValue) |
- ? thenValue |
- : joinContinuation.parameters.last; |
- } |
- |
- // 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) { |
- assert(irBuilder.isOpen); |
- if (node.isConst) { |
- return translateConstant(node); |
- } |
- List<ir.Primitive> values = node.elements.nodes.mapToList(visit); |
- GenericType type = elements.getType(node); |
- ir.Primitive result = new ir.LiteralList(type, values); |
- irBuilder.add(new ir.LetPrim(result)); |
- return result; |
- } |
- |
- ir.Primitive visitLiteralMap(ast.LiteralMap node) { |
- assert(irBuilder.isOpen); |
- if (node.isConst) { |
- return translateConstant(node); |
- } |
- List<ir.Primitive> keys = new List<ir.Primitive>(); |
- List<ir.Primitive> values = new List<ir.Primitive>(); |
- node.entries.nodes.forEach((ast.LiteralMapEntry node) { |
- keys.add(visit(node.key)); |
- values.add(visit(node.value)); |
- }); |
- GenericType type = elements.getType(node); |
- ir.Primitive result = new ir.LiteralMap(type, keys, values); |
- irBuilder.add(new ir.LetPrim(result)); |
- return result; |
- } |
- |
- ir.Primitive visitLiteralSymbol(ast.LiteralSymbol node) { |
- assert(irBuilder.isOpen); |
- return translateConstant(node); |
- } |
- |
- ir.Primitive visitIdentifier(ast.Identifier node) { |
- assert(irBuilder.isOpen); |
- // "this" is the only identifier that should be met by the visitor. |
- assert(node.isThis()); |
- return lookupThis(); |
- } |
- |
- 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; |
- } |
- |
- ir.Primitive lookupThis() { |
- ir.Primitive result = new ir.This(); |
- irBuilder.add(new ir.LetPrim(result)); |
- return result; |
- } |
- |
- // ==== Sends ==== |
- ir.Primitive visitAssert(ast.Send node) { |
- assert(irBuilder.isOpen); |
- return giveup(node, 'Assert'); |
- } |
- |
- ir.Primitive visitNamedArgument(ast.NamedArgument node) { |
- assert(irBuilder.isOpen); |
- return visit(node.expression); |
- } |
- |
- ir.Primitive translateClosureCall(ir.Primitive receiver, |
- Selector closureSelector, |
- ast.NodeList arguments) { |
- Selector namedCallSelector = new Selector(closureSelector.kind, |
- "call", |
- closureSelector.library, |
- closureSelector.argumentCount, |
- closureSelector.namedArguments); |
- List<ir.Primitive> args = arguments.nodes.mapToList(visit, growable:false); |
- return irBuilder.continueWithExpression( |
- (k) => new ir.InvokeMethod(receiver, namedCallSelector, k, args)); |
- } |
- |
- ir.Primitive visitClosureSend(ast.Send node) { |
- assert(irBuilder.isOpen); |
- Element element = elements[node]; |
- ir.Primitive closureTarget; |
- if (element == null) { |
- closureTarget = visit(node.selector); |
- } else if (isClosureVariable(element)) { |
- LocalElement local = element; |
- closureTarget = new ir.GetClosureVariable(local); |
- irBuilder.add(new ir.LetPrim(closureTarget)); |
- } else { |
- assert(Elements.isLocal(element)); |
- closureTarget = irBuilder.environment.lookup(element); |
- } |
- Selector closureSelector = elements.getSelector(node); |
- return translateClosureCall(closureTarget, closureSelector, |
- node.argumentsNode); |
- } |
- |
- /// If [node] is null, returns this. |
- /// If [node] is super, returns null (for special handling) |
- /// Otherwise visits [node] and returns the result. |
- ir.Primitive visitReceiver(ast.Expression node) { |
- if (node == null) return lookupThis(); |
- if (node.isSuper()) return null; |
- return visit(node); |
- } |
- |
- /// Makes an [InvokeMethod] unless [node.receiver.isSuper()], in that case |
- /// makes an [InvokeSuperMethod] ignoring [receiver]. |
- ir.Expression createDynamicInvoke(ast.Send node, |
- Selector selector, |
- ir.Definition receiver, |
- ir.Continuation k, |
- List<ir.Definition> arguments) { |
- return node != null && node.receiver != null && node.receiver.isSuper() |
- ? new ir.InvokeSuperMethod(selector, k, arguments) |
- : new ir.InvokeMethod(receiver, selector, k, arguments); |
- } |
- |
- ir.Primitive visitDynamicSend(ast.Send node) { |
- assert(irBuilder.isOpen); |
- Selector selector = elements.getSelector(node); |
- ir.Primitive receiver = visitReceiver(node.receiver); |
- List<ir.Primitive> arguments = new List<ir.Primitive>(); |
- for (ast.Node n in node.arguments) { |
- arguments.add(visit(n)); |
- } |
- return irBuilder.buildDynamicInvocation(receiver, selector, arguments); |
- } |
- |
- _GetterElements translateGetter(ast.Send node, Selector selector) { |
- Element element = elements[node]; |
- ir.Primitive result; |
- ir.Primitive receiver; |
- ir.Primitive index; |
- |
- if (element != null && element.isConst) { |
- // Reference to constant local, top-level or static field |
- result = translateConstant(node); |
- } else if (isClosureVariable(element)) { |
- LocalElement local = element; |
- result = new ir.GetClosureVariable(local); |
- irBuilder.add(new ir.LetPrim(result)); |
- } else if (Elements.isLocal(element)) { |
- // Reference to local variable |
- result = irBuilder.buildLocalGet(element); |
- } else if (element == null || |
- Elements.isInstanceField(element) || |
- Elements.isInstanceMethod(element) || |
- selector.isIndex || |
- // TODO(johnniwinther): clean up semantics of resolution. |
- node.isSuperCall) { |
- // Dynamic dispatch to a getter. Sometimes resolution will suggest a |
- // target element, but in these cases we must still emit a dynamic |
- // dispatch. The target element may be an instance method in case we are |
- // converting a method to a function object. |
- |
- receiver = visitReceiver(node.receiver); |
- List<ir.Primitive> arguments = new List<ir.Primitive>(); |
- if (selector.isIndex) { |
- index = visit(node.arguments.head); |
- arguments.add(index); |
- } |
- |
- assert(selector.kind == SelectorKind.GETTER || |
- selector.kind == SelectorKind.INDEX); |
- result = irBuilder.continueWithExpression( |
- (k) => createDynamicInvoke(node, selector, receiver, k, arguments)); |
- } else if (element.isField || element.isGetter || element.isErroneous || |
- element.isSetter) { |
- // TODO(johnniwinther): Change handling of setter selectors. |
- // Access to a static field or getter (non-static case handled above). |
- // Even if there is only a setter, we compile as if it was a getter, |
- // so the vm can fail at runtime. |
- assert(selector.kind == SelectorKind.GETTER || |
- selector.kind == SelectorKind.SETTER); |
- result = irBuilder.buildStaticGet(element, selector); |
- } else if (Elements.isStaticOrTopLevelFunction(element)) { |
- // Convert a top-level or static function to a function object. |
- result = translateConstant(node); |
- } else { |
- throw "Unexpected SendSet getter: $node, $element"; |
- } |
- return new _GetterElements( |
- result: result,index: index, receiver: receiver); |
- } |
- |
- ir.Primitive visitGetterSend(ast.Send node) { |
- assert(irBuilder.isOpen); |
- return translateGetter(node, elements.getSelector(node)).result; |
- |
- } |
- |
- ir.Primitive buildNegation(ir.Primitive condition) { |
- // ! e is translated as e ? false : true |
- |
- // Add a continuation parameter for the result of the expression. |
- ir.Parameter resultParameter = new ir.Parameter(null); |
- |
- ir.Continuation joinContinuation = new ir.Continuation([resultParameter]); |
- ir.Continuation thenContinuation = new ir.Continuation([]); |
- ir.Continuation elseContinuation = new ir.Continuation([]); |
- |
- ir.Constant trueConstant = irBuilder.makePrimConst( |
- irBuilder.state.constantSystem.createBool(true)); |
- ir.Constant falseConstant = irBuilder.makePrimConst( |
- irBuilder.state.constantSystem.createBool(false)); |
- |
- thenContinuation.body = new ir.LetPrim(falseConstant) |
- ..plug(new ir.InvokeContinuation(joinContinuation, [falseConstant])); |
- elseContinuation.body = new ir.LetPrim(trueConstant) |
- ..plug(new ir.InvokeContinuation(joinContinuation, [trueConstant])); |
- |
- irBuilder.add(new ir.LetCont(joinContinuation, |
- new ir.LetCont(thenContinuation, |
- new ir.LetCont(elseContinuation, |
- new ir.Branch(new ir.IsTrue(condition), |
- thenContinuation, |
- elseContinuation))))); |
- return resultParameter; |
- } |
- |
- ir.Primitive translateLogicalOperator(ast.Operator op, |
- ast.Expression left, |
- ast.Expression right) { |
- // e0 && e1 is translated as if e0 ? (e1 == true) : false. |
- // e0 || e1 is translated as if e0 ? true : (e1 == true). |
- // The translation must convert both e0 and e1 to booleans and handle |
- // local variable assignments in e1. |
- |
- ir.Primitive leftValue = visit(left); |
- IrBuilder rightBuilder = new IrBuilder.delimited(irBuilder); |
- ir.Primitive rightValue = |
- withBuilder(rightBuilder, () => visit(right)); |
- // A dummy empty target for the branch on the left subexpression branch. |
- // This enables using the same infrastructure for join-point continuations |
- // as in visitIf and visitConditional. It will hold a definition of the |
- // appropriate constant and an invocation of the join-point continuation. |
- IrBuilder emptyBuilder = new IrBuilder.delimited(irBuilder); |
- // Dummy empty targets for right true and right false. They hold |
- // definitions of the appropriate constant and an invocation of the |
- // join-point continuation. |
- IrBuilder rightTrueBuilder = new IrBuilder.delimited(rightBuilder); |
- IrBuilder rightFalseBuilder = new IrBuilder.delimited(rightBuilder); |
- |
- // If we don't evaluate the right subexpression, the value of the whole |
- // expression is this constant. |
- ir.Constant leftBool = emptyBuilder.makePrimConst( |
- emptyBuilder.state.constantSystem.createBool(op.source == '||')); |
- // If we do evaluate the right subexpression, the value of the expression |
- // is a true or false constant. |
- ir.Constant rightTrue = rightTrueBuilder.makePrimConst( |
- rightTrueBuilder.state.constantSystem.createBool(true)); |
- ir.Constant rightFalse = rightFalseBuilder.makePrimConst( |
- rightFalseBuilder.state.constantSystem.createBool(false)); |
- emptyBuilder.add(new ir.LetPrim(leftBool)); |
- rightTrueBuilder.add(new ir.LetPrim(rightTrue)); |
- rightFalseBuilder.add(new ir.LetPrim(rightFalse)); |
- |
- // Treat the result values as named values in the environment, so they |
- // will be treated as arguments to the join-point continuation. |
- assert(irBuilder.environment.length == emptyBuilder.environment.length); |
- assert(irBuilder.environment.length == rightTrueBuilder.environment.length); |
- assert(irBuilder.environment.length == |
- rightFalseBuilder.environment.length); |
- emptyBuilder.environment.extend(null, leftBool); |
- rightTrueBuilder.environment.extend(null, rightTrue); |
- rightFalseBuilder.environment.extend(null, rightFalse); |
- |
- // Wire up two continuations for the left subexpression, two continuations |
- // for the right subexpression, and a three-way join continuation. |
- JumpCollector jumps = new JumpCollector(null); |
- jumps.addJump(emptyBuilder); |
- jumps.addJump(rightTrueBuilder); |
- jumps.addJump(rightFalseBuilder); |
- ir.Continuation joinContinuation = |
- createJoin(irBuilder.environment.length + 1, jumps); |
- ir.Continuation leftTrueContinuation = new ir.Continuation([]); |
- ir.Continuation leftFalseContinuation = new ir.Continuation([]); |
- ir.Continuation rightTrueContinuation = new ir.Continuation([]); |
- ir.Continuation rightFalseContinuation = new ir.Continuation([]); |
- rightTrueContinuation.body = rightTrueBuilder._root; |
- rightFalseContinuation.body = rightFalseBuilder._root; |
- // The right subexpression has two continuations. |
- rightBuilder.add( |
- new ir.LetCont(rightTrueContinuation, |
- new ir.LetCont(rightFalseContinuation, |
- new ir.Branch(new ir.IsTrue(rightValue), |
- rightTrueContinuation, |
- rightFalseContinuation)))); |
- // Depending on the operator, the left subexpression's continuations are |
- // either the right subexpression or an invocation of the join-point |
- // continuation. |
- if (op.source == '&&') { |
- leftTrueContinuation.body = rightBuilder._root; |
- leftFalseContinuation.body = emptyBuilder._root; |
- } else { |
- leftTrueContinuation.body = emptyBuilder._root; |
- leftFalseContinuation.body = rightBuilder._root; |
- } |
- |
- irBuilder.add(new ir.LetCont(joinContinuation, |
- new ir.LetCont(leftTrueContinuation, |
- new ir.LetCont(leftFalseContinuation, |
- new ir.Branch(new ir.IsTrue(leftValue), |
- leftTrueContinuation, |
- leftFalseContinuation))))); |
- // There is always a join parameter for the result value, because it |
- // is different on at least two paths. |
- return joinContinuation.parameters.last; |
- } |
- |
- ir.Primitive visitOperatorSend(ast.Send node) { |
- assert(irBuilder.isOpen); |
- ast.Operator op = node.selector; |
- if (isUserDefinableOperator(op.source)) { |
- return visitDynamicSend(node); |
- } |
- if (op.source == '&&' || op.source == '||') { |
- assert(node.receiver != null); |
- assert(!node.arguments.isEmpty); |
- assert(node.arguments.tail.isEmpty); |
- return translateLogicalOperator(op, node.receiver, node.arguments.head); |
- } |
- if (op.source == "!") { |
- assert(node.receiver != null); |
- assert(node.arguments.isEmpty); |
- return buildNegation(visit(node.receiver)); |
- } |
- if (op.source == "!=") { |
- assert(node.receiver != null); |
- assert(!node.arguments.isEmpty); |
- assert(node.arguments.tail.isEmpty); |
- return buildNegation(visitDynamicSend(node)); |
- } |
- assert(invariant(node, op.source == "is" || op.source == "as", |
- message: "unexpected operator $op")); |
- DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast); |
- ir.Primitive receiver = visit(node.receiver); |
- ir.Primitive check = irBuilder.continueWithExpression( |
- (k) => new ir.TypeOperator(op.source, receiver, type, k)); |
- return node.isIsNotCheck ? buildNegation(check) : check; |
- } |
- |
- // Build(StaticSend(f, arguments), C) = C[C'[InvokeStatic(f, xs)]] |
- // where (C', xs) = arguments.fold(Build, C) |
- ir.Primitive visitStaticSend(ast.Send node) { |
- assert(irBuilder.isOpen); |
- Element element = elements[node]; |
- assert(!element.isConstructor); |
- // TODO(lry): support foreign functions. |
- if (element.isForeign(compiler.backend)) { |
- return giveup(node, 'StaticSend: foreign'); |
- } |
- |
- Selector selector = elements.getSelector(node); |
- |
- // TODO(lry): support default arguments, need support for locals. |
- List<ir.Definition> arguments = node.arguments.mapToList(visit, |
- growable:false); |
- return irBuilder.buildStaticInvocation(element, selector, arguments); |
- } |
- |
- |
- ir.Primitive visitSuperSend(ast.Send node) { |
- assert(irBuilder.isOpen); |
- if (node.isPropertyAccess) { |
- return visitGetterSend(node); |
- } else { |
- Selector selector = elements.getSelector(node); |
- List<ir.Primitive> arguments = new List<ir.Primitive>(); |
- for (ast.Node n in node.arguments) { |
- arguments.add(visit(n)); |
- } |
- return irBuilder.buildSuperInvocation(selector, arguments); |
- } |
- } |
- |
- visitTypePrefixSend(ast.Send node) { |
- compiler.internalError(node, "visitTypePrefixSend should not be called."); |
- } |
- |
- ir.Primitive visitTypeLiteralSend(ast.Send node) { |
- assert(irBuilder.isOpen); |
- // If the user is trying to invoke the type literal or variable, |
- // it must be treated as a function call. |
- if (node.argumentsNode != null) { |
- // TODO(sigurdm): Handle this to match proposed semantics of issue #19725. |
- return giveup(node, 'Type literal invoked as function'); |
- } |
- |
- DartType type = elements.getTypeLiteralType(node); |
- if (type is TypeVariableType) { |
- ir.Primitive prim = new ir.ReifyTypeVar(type.element); |
- irBuilder.add(new ir.LetPrim(prim)); |
- return prim; |
- } else { |
- return translateConstant(node); |
- } |
- } |
- |
- /// True if [element] is a local variable, local function, or parameter that |
- /// is accessed from an inner function. Recursive self-references in a local |
- /// function count as closure accesses. |
- /// |
- /// If `true`, [element] is a [LocalElement]. |
- bool isClosureVariable(Element element) { |
- return irBuilder.state.closureLocals.contains(element); |
- } |
- |
- void setLocal(Element element, ir.Primitive valueToStore) { |
- if (isClosureVariable(element)) { |
- LocalElement local = element; |
- irBuilder.add(new ir.SetClosureVariable(local, valueToStore)); |
- } else { |
- valueToStore.useElementAsHint(element); |
- irBuilder.environment.update(element, valueToStore); |
- } |
- } |
- |
- void setStatic(Element element, |
- Selector selector, |
- ir.Primitive valueToStore) { |
- assert(element.isErroneous || element.isField || element.isSetter); |
- irBuilder.continueWithExpression( |
- (k) => new ir.InvokeStatic(element, selector, k, [valueToStore])); |
- } |
- |
- void setDynamic(ast.Node node, |
- ir.Primitive receiver, Selector selector, |
- ir.Primitive valueToStore) { |
- List<ir.Definition> arguments = [valueToStore]; |
- irBuilder.continueWithExpression( |
- (k) => createDynamicInvoke(node, selector, receiver, k, arguments)); |
- } |
- |
- void setIndex(ast.Node node, |
- ir.Primitive receiver, |
- Selector selector, |
- ir.Primitive index, |
- ir.Primitive valueToStore) { |
- List<ir.Definition> arguments = [index, valueToStore]; |
- irBuilder.continueWithExpression( |
- (k) => createDynamicInvoke(node, selector, receiver, k, arguments)); |
- } |
- |
- ir.Primitive visitSendSet(ast.SendSet node) { |
- assert(irBuilder.isOpen); |
- Element element = elements[node]; |
- ast.Operator op = node.assignmentOperator; |
- // For complex operators, this is the result of getting (before assigning) |
- ir.Primitive originalValue; |
- // For []+= style operators, this saves the index. |
- ir.Primitive index; |
- ir.Primitive receiver; |
- // This is what gets assigned. |
- ir.Primitive valueToStore; |
- Selector selector = elements.getSelector(node); |
- Selector operatorSelector = |
- elements.getOperatorSelectorInComplexSendSet(node); |
- Selector getterSelector = |
- elements.getGetterSelectorInComplexSendSet(node); |
- assert( |
- // Indexing send-sets have an argument for the index. |
- (selector.isIndexSet ? 1 : 0) + |
- // Non-increment send-sets have one more argument. |
- (ast.Operator.INCREMENT_OPERATORS.contains(op.source) ? 0 : 1) |
- == node.argumentCount()); |
- |
- ast.Node getAssignArgument() { |
- assert(invariant(node, !node.arguments.isEmpty, |
- message: "argument expected")); |
- return selector.isIndexSet |
- ? node.arguments.tail.head |
- : node.arguments.head; |
- } |
- |
- // Get the value into valueToStore |
- if (op.source == "=") { |
- if (selector.isIndexSet) { |
- receiver = visitReceiver(node.receiver); |
- index = visit(node.arguments.head); |
- } else if (element == null || Elements.isInstanceField(element)) { |
- receiver = visitReceiver(node.receiver); |
- } |
- valueToStore = visit(getAssignArgument()); |
- } else { |
- // Get the original value into getter |
- assert(ast.Operator.COMPLEX_OPERATORS.contains(op.source)); |
- |
- _GetterElements getterResult = translateGetter(node, getterSelector); |
- index = getterResult.index; |
- receiver = getterResult.receiver; |
- originalValue = getterResult.result; |
- |
- // Do the modification of the value in getter. |
- ir.Primitive arg; |
- if (ast.Operator.INCREMENT_OPERATORS.contains(op.source)) { |
- arg = irBuilder.makePrimConst( |
- irBuilder.state.constantSystem.createInt(1)); |
- irBuilder.add(new ir.LetPrim(arg)); |
- } else { |
- arg = visit(getAssignArgument()); |
- } |
- valueToStore = new ir.Parameter(null); |
- ir.Continuation k = new ir.Continuation([valueToStore]); |
- ir.Expression invoke = |
- new ir.InvokeMethod(originalValue, operatorSelector, k, [arg]); |
- irBuilder.add(new ir.LetCont(k, invoke)); |
- } |
- |
- if (Elements.isLocal(element)) { |
- setLocal(element, valueToStore); |
- } else if ((!node.isSuperCall && Elements.isErroneousElement(element)) || |
- Elements.isStaticOrTopLevel(element)) { |
- setStatic(element, elements.getSelector(node), valueToStore); |
- } else { |
- // Setter or index-setter invocation |
- Selector selector = elements.getSelector(node); |
- assert(selector.kind == SelectorKind.SETTER || |
- selector.kind == SelectorKind.INDEX); |
- if (selector.isIndexSet) { |
- setIndex(node, receiver, selector, index, valueToStore); |
- } else { |
- setDynamic(node, receiver, selector, valueToStore); |
- } |
- } |
- |
- if (node.isPostfix) { |
- assert(originalValue != null); |
- return originalValue; |
- } else { |
- return valueToStore; |
- } |
- } |
- |
- ir.Primitive visitNewExpression(ast.NewExpression node) { |
- assert(irBuilder.isOpen); |
- if (node.isConst) { |
- return translateConstant(node); |
- } |
- FunctionElement element = elements[node.send]; |
- Selector selector = elements.getSelector(node.send); |
- ast.Node selectorNode = node.send.selector; |
- DartType type = elements.getType(node); |
- List<ir.Primitive> args = |
- node.send.arguments.mapToList(visit, growable:false); |
- return irBuilder.continueWithExpression( |
- (k) => new ir.InvokeConstructor(type, element,selector, k, args)); |
- } |
- |
- ir.Primitive visitStringJuxtaposition(ast.StringJuxtaposition node) { |
- assert(irBuilder.isOpen); |
- ir.Primitive first = visit(node.first); |
- ir.Primitive second = visit(node.second); |
- return irBuilder.continueWithExpression( |
- (k) => new ir.ConcatenateStrings(k, [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.continueWithExpression( |
- (k) => new ir.ConcatenateStrings(k, arguments)); |
- } |
- |
- ir.Primitive translateConstant(ast.Node node, [ConstantExpression constant]) { |
- assert(irBuilder.isOpen); |
- if (constant == null) { |
- constant = getConstantForNode(node); |
- } |
- ir.Primitive primitive = irBuilder.makeConst(constant); |
- irBuilder.add(new ir.LetPrim(primitive)); |
- return primitive; |
- } |
- |
- ir.FunctionDefinition makeSubFunction(ast.FunctionExpression node) { |
- // TODO(johnniwinther): Share the visitor. |
- return new IrBuilderVisitor(elements, compiler, sourceFile) |
- .buildFunctionInternal(elements[node]); |
- } |
- |
- ir.Primitive visitFunctionExpression(ast.FunctionExpression node) { |
- FunctionElement element = elements[node]; |
- ir.FunctionDefinition inner = makeSubFunction(node); |
- ir.CreateFunction prim = new ir.CreateFunction(inner); |
- irBuilder.add(new ir.LetPrim(prim)); |
- return prim; |
- } |
- |
- ir.Primitive visitFunctionDeclaration(ast.FunctionDeclaration node) { |
- LocalFunctionElement element = elements[node.function]; |
- ir.FunctionDefinition inner = makeSubFunction(node.function); |
- if (isClosureVariable(element)) { |
- irBuilder.add(new ir.DeclareFunction(element, inner)); |
- } else { |
- ir.CreateFunction prim = new ir.CreateFunction(inner); |
- irBuilder.add(new ir.LetPrim(prim)); |
- irBuilder.environment.extend(element, prim); |
- prim.useElementAsHint(element); |
- } |
- return null; |
- } |
- |
- static final String ABORT_IRNODE_BUILDER = "IrNode builder aborted"; |
- |
- dynamic giveup(ast.Node node, [String reason]) { |
- throw ABORT_IRNODE_BUILDER; |
- } |
- |
- ir.FunctionDefinition nullIfGiveup(ir.FunctionDefinition action()) { |
- try { |
- return action(); |
- } catch(e, tr) { |
- if (e == ABORT_IRNODE_BUILDER) { |
- return null; |
- } |
- rethrow; |
- } |
- } |
- |
- void internalError(String reason, {ast.Node node}) { |
- giveup(node); |
- } |
-} |
- |
-/// Classifies local variables and local functions as 'closure variables'. |
-/// A closure variable is one that is accessed from an inner function nested |
-/// one or more levels inside the one that declares it. |
-class DetectClosureVariables extends ast.Visitor { |
- final TreeElements elements; |
- DetectClosureVariables(this.elements); |
- |
- FunctionElement currentFunction; |
- Set<Local> usedFromClosure = new Set<Local>(); |
- Set<FunctionElement> recursiveFunctions = new Set<FunctionElement>(); |
- |
- bool isClosureVariable(Entity entity) => usedFromClosure.contains(entity); |
- |
- void markAsClosureVariable(Local local) { |
- usedFromClosure.add(local); |
- } |
- |
- visit(ast.Node node) => node.accept(this); |
- |
- visitNode(ast.Node node) { |
- node.visitChildren(this); |
- } |
- |
- visitSend(ast.Send node) { |
- Element element = elements[node]; |
- if (Elements.isLocal(element) && |
- !element.isConst && |
- element.enclosingElement != currentFunction) { |
- LocalElement local = element; |
- markAsClosureVariable(local); |
- } |
- node.visitChildren(this); |
- } |
- |
- visitFunctionExpression(ast.FunctionExpression node) { |
- FunctionElement oldFunction = currentFunction; |
- currentFunction = elements[node]; |
- visit(node.body); |
- currentFunction = oldFunction; |
- } |
- |
-} |