Chromium Code Reviews| Index: pkg/compiler/lib/src/ssa/builder.dart |
| diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart |
| index 43798cd7243067a82a320d3321048a8e36a8730b..490222d444074caa29876ecf4e40a61bc65a1cd4 100644 |
| --- a/pkg/compiler/lib/src/ssa/builder.dart |
| +++ b/pkg/compiler/lib/src/ssa/builder.dart |
| @@ -19,7 +19,51 @@ class SsaFunctionCompiler implements FunctionCompiler { |
| js.Fun compile(CodegenWorkItem work) { |
| HGraph graph = builder.build(work); |
| optimizer.optimize(work, graph); |
| - return generator.generateCode(work, graph); |
| + Element element = work.element; |
| + js.Expression result = generator.generateCode(work, graph); |
| + if (element is FunctionElement) { |
| + JavaScriptBackend backend = builder.backend; |
| + AsyncRewriter rewriter = null; |
| + if (element.asyncMarker == AsyncMarker.ASYNC) { |
| + rewriter = new AsyncRewriter( |
| + backend.compiler, |
| + backend.compiler.currentElement, |
| + thenHelper: |
| + backend.emitter.staticFunctionAccess(backend.getThenHelper()), |
| + newController: new js.New(backend.emitter.staticFunctionAccess( |
| + backend.getCompleterConstructor()), []), |
| + safeVariableName: backend.namer.safeVariableName); |
| + } else if (element.asyncMarker == AsyncMarker.SYNC_STAR) { |
| + rewriter = new AsyncRewriter( |
| + backend.compiler, |
| + backend.compiler.currentElement, |
| + endOfIteration: backend.emitter.staticFunctionAccess( |
| + backend.getEndOfIteration()), |
| + newIterable: backend.emitter.staticFunctionAccess( |
| + backend.getSyncStarIterableConstructor()), |
| + yieldStarExpression: backend.emitter.staticFunctionAccess( |
| + backend.getYieldStar()), |
| + safeVariableName: backend.namer.safeVariableName); |
| + } |
| + else if (element.asyncMarker == AsyncMarker.ASYNC_STAR) { |
| + rewriter = new AsyncRewriter( |
| + backend.compiler, |
| + backend.compiler.currentElement, |
| + thenHelper: backend.emitter.staticFunctionAccess( |
| + backend.getStreamHelper()), |
| + newController: new js.Call(backend.emitter.staticFunctionAccess( |
| + backend.getMakeController()), []), |
| + safeVariableName: backend.namer.safeVariableName, |
| + yieldExpression: backend.emitter.staticFunctionAccess( |
| + backend.getYieldSingle()), |
| + yieldStarExpression: backend.emitter.staticFunctionAccess( |
| + backend.getYieldStar())); |
| + } |
| + if (rewriter != null) { |
| + result = rewriter.rewrite(result); |
| + } |
| + } |
| + return result; |
| } |
| Iterable<CompilerTask> get tasks { |
| @@ -362,7 +406,7 @@ class LocalsHandler { |
| bool isAccessedDirectly(Local local) { |
| assert(local != null); |
| return !redirectionMapping.containsKey(local) |
| - && !closureData.usedVariablesInTry.contains(local); |
| + && !closureData.variablesUsedInTryOrGenerator.contains(local); |
| } |
| bool isStoredInClosureField(Local local) { |
| @@ -379,8 +423,8 @@ class LocalsHandler { |
| return redirectionMapping.containsKey(local); |
| } |
| - bool isUsedInTry(Local local) { |
| - return closureData.usedVariablesInTry.contains(local); |
| + bool isUsedInTryOrGenerator(Local local) { |
| + return closureData.variablesUsedInTryOrGenerator.contains(local); |
| } |
| /** |
| @@ -422,7 +466,7 @@ class LocalsHandler { |
| builder.add(lookup); |
| return lookup; |
| } else { |
| - assert(isUsedInTry(local)); |
| + assert(isUsedInTryOrGenerator(local)); |
| HLocalValue localValue = getLocal(local); |
| HInstruction instruction = new HLocalGet( |
| local, localValue, builder.backend.dynamicType); |
| @@ -478,7 +522,7 @@ class LocalsHandler { |
| HInstruction box = readLocal(redirect.box); |
| builder.add(new HFieldSet(redirect, box, value)); |
| } else { |
| - assert(isUsedInTry(local)); |
| + assert(isUsedInTryOrGenerator(local)); |
| HLocalValue localValue = getLocal(local); |
| builder.add(new HLocalSet(local, localValue, value)); |
| } |
| @@ -5164,14 +5208,17 @@ class SsaBuilder extends ResolvedVisitor { |
| } |
| visitYield(ast.Yield node) { |
| - // Dummy implementation. |
| visit(node.expression); |
| - pop(); |
| + HInstruction yielded = pop(); |
| + add(new HYield(yielded, node.hasStar)); |
| } |
| visitAwait(ast.Await node) { |
| - // Dummy implementation. |
| visit(node.expression); |
| + HInstruction awaited = pop(); |
| + // TODO(herhut): Improve this type. |
| + push(new HAwait(awaited, new TypeMask.subclass(compiler.objectClass, |
| + compiler.world))); |
| } |
| visitTypeAnnotation(ast.TypeAnnotation node) { |
| @@ -5315,7 +5362,78 @@ class SsaBuilder extends ResolvedVisitor { |
| return new JumpHandler(this, element); |
| } |
| + buildAsyncForIn(ast.ForIn node) { |
| + assert(node.isAsync); |
| + // The async-for is implemented with a StreamIterator. |
| + HInstruction streamIterator; |
| + |
| + visit(node.expression); |
| + HInstruction expression = pop(); |
| + pushInvokeStatic(node, |
| + backend.getStreamIteratorConstructor(), |
| + [expression, graph.addConstantNull(compiler)]); |
| + streamIterator = pop(); |
| + |
| + void buildInitializer() {} |
| + |
| + HInstruction buildCondition() { |
| + Selector selector = elements.getMoveNextSelector(node); |
| + pushInvokeDynamic(node, selector, [streamIterator]); |
| + HInstruction future = pop(); |
| + push(new HAwait(future, new TypeMask.subclass(compiler.objectClass, |
| + compiler.world))); |
| + return popBoolified(); |
| + } |
| + void buildBody() { |
| + Selector call = elements.getCurrentSelector(node); |
| + pushInvokeDynamic(node, call, [streamIterator]); |
| + |
| + ast.Node identifier = node.declaredIdentifier; |
| + Element variable = elements.getForInVariable(node); |
| + Selector selector = elements.getSelector(identifier); |
| + |
| + HInstruction value = pop(); |
| + if (identifier.asSend() != null |
| + && Elements.isInstanceSend(identifier, elements)) { |
| + HInstruction receiver = generateInstanceSendReceiver(identifier); |
| + assert(receiver != null); |
| + generateInstanceSetterWithCompiledReceiver( |
| + null, |
| + receiver, |
| + value, |
| + selector: selector, |
| + location: identifier); |
| + } else { |
| + generateNonInstanceSetter( |
| + null, variable, value, location: identifier); |
| + } |
| + pop(); // Pop the value pushed by the setter call. |
| + |
| + visit(node.body); |
| + } |
| + |
| + void buildUpdate() {}; |
| + |
| + buildProtectedByFinally(() { |
| + handleLoop(node, |
| + buildInitializer, |
| + buildCondition, |
| + buildUpdate, |
| + buildBody); |
| + }, () { |
| + pushInvokeDynamic(node, new Selector.call("cancel", null, 0), |
| + [streamIterator]); |
| + push(new HAwait(pop(), new TypeMask.subclass(compiler.objectClass, |
| + compiler.world))); |
| + pop(); |
| + }); |
| + } |
| + |
| visitForIn(ast.ForIn node) { |
| + if (node.isAsync) { |
| + return buildAsyncForIn(node); |
| + } |
| + |
| // Generate a structure equivalent to: |
| // Iterator<E> $iter = <iterable>.iterator; |
| // while ($iter.moveNext()) { |
| @@ -5851,6 +5969,96 @@ class SsaBuilder extends ResolvedVisitor { |
| compiler.internalError(node, 'SsaFromAstMixin.visitCaseMatch.'); |
| } |
| + /// Calls [buildTry] inside a synthetic try block with [buildFinally] in the |
| + /// finally block. |
| + void buildProtectedByFinally(void buildTry(), void buildFinally()) { |
| + // Save the current locals. The finally block must not reuse the existing |
| + // locals handler. None of the variables |
| + // that have been defined in the body-block will be used, but for |
| + // loops we will add (unnecessary) phis that will reference the body |
| + // variables. This makes it look as if the variables were used |
| + // in a non-dominated block. |
| + LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); |
| + HBasicBlock enterBlock = openNewBlock(); |
| + HTry tryInstruction = new HTry(); |
| + close(tryInstruction); |
| + bool oldInTryStatement = inTryStatement; |
| + inTryStatement = true; |
| + |
| + HBasicBlock startTryBlock; |
| + HBasicBlock endTryBlock; |
| + HBasicBlock startFinallyBlock; |
| + HBasicBlock endFinallyBlock; |
| + |
| + startTryBlock = graph.addNewBlock(); |
| + open(startTryBlock); |
| + buildTry(); |
| + // We use a [HExitTry] instead of a [HGoto] for the try block |
| + // because it will have two successors: the join block, and |
| + // the finally block. |
| + if (!isAborted()) endTryBlock = close(new HExitTry()); |
| + SubGraph bodyGraph = new SubGraph(startTryBlock, lastOpenedBlock); |
| + |
| + SubGraph finallyGraph = null; |
| + |
| + localsHandler = new LocalsHandler.from(savedLocals); |
| + startFinallyBlock = graph.addNewBlock(); |
| + open(startFinallyBlock); |
| + buildFinally(); |
| + if (!isAborted()) endFinallyBlock = close(new HGoto()); |
| + tryInstruction.finallyBlock = startFinallyBlock; |
| + finallyGraph = new SubGraph(startFinallyBlock, lastOpenedBlock); |
| + |
| + HBasicBlock exitBlock = graph.addNewBlock(); |
| + |
| + void addExitTrySuccessor(HBasicBlock successor) { |
| + // Iterate over all blocks created inside this try/catch, and |
| + // attach successor information to blocks that end with |
| + // [HExitTry]. |
| + for (int i = startTryBlock.id; i < successor.id; i++) { |
| + HBasicBlock block = graph.blocks[i]; |
| + var last = block.last; |
| + if (last is HExitTry) { |
| + block.addSuccessor(successor); |
| + } |
| + } |
| + } |
| + |
| + // Setup all successors. The entry block that contains the [HTry] |
| + // has 1) the body 2) the finally, and 4) the exit |
| + // blocks as successors. |
| + enterBlock.addSuccessor(startTryBlock); |
| + enterBlock.addSuccessor(startFinallyBlock); |
| + enterBlock.addSuccessor(exitBlock); |
| + |
| + // The body has the finally block as successor. |
| + if (endTryBlock != null) { |
| + endTryBlock.addSuccessor(startFinallyBlock); |
| + endTryBlock.addSuccessor(exitBlock); |
| + } |
| + |
| + // The finally block has the exit block as successor. |
| + endFinallyBlock.addSuccessor(exitBlock); |
| + |
| + // If a block inside try/catch aborts (eg with a return statement), |
| + // we explicitely mark this block a predecessor of the catch |
| + // block and the finally block. |
| + addExitTrySuccessor(startFinallyBlock); |
| + |
| + // Use the locals handler not altered by the catch and finally |
| + // blocks. |
| + localsHandler = savedLocals; |
| + open(exitBlock); |
| + enterBlock.setBlockFlow( |
| + new HTryBlockInformation( |
| + wrapStatementGraph(bodyGraph), |
| + null, |
|
floitsch
2015/02/04 12:31:28
add comment what this is.
sigurdm
2015/02/05 14:06:04
Done.
|
| + null, // No catchGraph |
| + wrapStatementGraph(finallyGraph)), |
| + exitBlock); |
| + inTryStatement = oldInTryStatement; |
| + } |
| + |
| visitTryStatement(ast.TryStatement node) { |
| // Save the current locals. The catch block and the finally block |
| // must not reuse the existing locals handler. None of the variables |
| @@ -6220,6 +6428,7 @@ class InlineWeeder extends ast.Visitor { |
| {bool allowLoops: false}) { |
| InlineWeeder weeder = |
| new InlineWeeder(maxInliningNodes, useMaxInliningNodes, allowLoops); |
| + if (functionExpression.asyncModifier != null) return false; |
| weeder.visit(functionExpression.initializers); |
| weeder.visit(functionExpression.body); |
| return !weeder.tooDifficult; |