Chromium Code Reviews| Index: pkg/compiler/lib/src/js/rewrite_async.dart |
| diff --git a/pkg/compiler/lib/src/js/rewrite_async.dart b/pkg/compiler/lib/src/js/rewrite_async.dart |
| index 656b1af9d9bdff50e11d41d8c8b1ef0961d644d1..71de2911e53d9afde7205ce9a6183b7b720225cb 100644 |
| --- a/pkg/compiler/lib/src/js/rewrite_async.dart |
| +++ b/pkg/compiler/lib/src/js/rewrite_async.dart |
| @@ -4,13 +4,15 @@ |
| library rewrite_async; |
| -// TODO(sigurdm): Throws in catch-handlers are handled wrong. |
| // TODO(sigurdm): Avoid using variables in templates. It could blow up memory |
| // use. |
| import "dart:math" show max; |
| import 'dart:collection'; |
| +import 'package:_internal/compiler/js_lib/shared/async_await_error_codes.dart' |
|
floitsch
2015/02/13 15:30:06
I think we import it relative for embedded names.
sigurdm
2015/02/17 08:43:10
At least in js_backend/js_backend.dart it is impor
|
| + as error_codes; |
|
floitsch
2015/02/13 15:30:07
not 100% about the naming convention of imports. W
sigurdm
2015/02/17 08:43:10
The style guide says lower_case_underscores.
|
| + |
| import "js.dart" as js; |
| import '../util/util.dart'; |
| @@ -38,7 +40,9 @@ class AsyncRewriter extends js.NodeVisitor { |
| Map<js.Node, int> continueLabels = new Map<js.Node, int>(); |
| Map<js.Node, int> breakLabels = new Map<js.Node, int>(); |
| Map<js.Node, int> finallyLabels = new Map<js.Node, int>(); |
| + Map<js.Node, int> handlerLabels = new Map<js.Node, int>(); |
| int exitLabel; |
| + int rethrowLabel; |
| // A stack of all enclosing jump targets (including the function for |
| // representing the target of a return, and all enclosing try-blocks that have |
| @@ -86,13 +90,17 @@ class AsyncRewriter extends js.NodeVisitor { |
| /// } |
| /// } |
| /// |
| - /// It is a parameter to the [helperName] function, so that [thenHelper] and |
| - /// [streamHelper] can call [helperName] with the result of an awaited Future. |
| + /// It is a parameter to the [bodyName] function, so that [asyncHelper] and |
| + /// [streamHelper] can call [bodyName] with the result of an awaited Future. |
| String resultName; |
| + /// A parameter to the [bodyName] function. Indicating if we are in success |
| + /// or error case. |
| + String errorCodeName; |
| + |
| /// The name of the inner function that is scheduled to do each await/yield, |
| /// and called to do a new iteration for sync*. |
| - String helperName; |
| + String bodyName; |
| /// The Completer that will finish an async function. |
| /// |
| @@ -121,9 +129,16 @@ class AsyncRewriter extends js.NodeVisitor { |
| /// the last. |
| String nextName; |
| + /// The stack of labels of finally blocks to assign to [nextName] if the |
| + /// async* [StreamSubscription] was canceled during a yield. |
| + String yieldNextName; |
|
floitsch
2015/02/13 15:30:06
nextWhenCanceledName ?
sigurdm
2015/02/17 08:43:09
Better, Donde
|
| + |
| /// The current returned value (a finally block may overwrite it). |
| String returnValueName; |
| + /// The thrown error (it might need to be rethrown after a finally block). |
|
floitsch
2015/02/13 15:30:06
currentErrorName ?
/// If we are in the process o
sigurdm
2015/02/17 08:43:09
Done.
|
| + String storedErrorName; |
| + |
| /// The label of the outer loop. |
| /// |
| /// Used if there are untransformed loops containing break or continues to |
| @@ -131,7 +146,7 @@ class AsyncRewriter extends js.NodeVisitor { |
| String outerLabelName; |
| /// If javascript `this` is used, it is accessed via this variable, in the |
| - /// [helperName] function. |
| + /// [bodyName] function. |
| String selfName; |
| // These expressions are hooks for communicating with the runtime. |
| @@ -141,7 +156,7 @@ class AsyncRewriter extends js.NodeVisitor { |
| /// For an await it is called with: |
| /// |
| /// - The value to await |
| - /// - The [helperName] |
| + /// - The [bodyName] |
| /// - The [completerName] |
| /// - A JavaScript function that is executed if the future completed with |
| /// an error. That function is responsible for executing the right error |
| @@ -153,7 +168,7 @@ class AsyncRewriter extends js.NodeVisitor { |
| /// - null |
| /// - The [completerName] |
| /// - null. |
| - final js.Expression thenHelper; |
| + final js.Expression asyncHelper; |
| /// The function called by an async* function to simulate an await, yield or |
| /// yield*. |
| @@ -162,7 +177,7 @@ class AsyncRewriter extends js.NodeVisitor { |
| /// |
| /// - The value to await/yieldExpression(value to yield)/ |
| /// yieldStarExpression(stream to yield) |
| - /// - The [helperName] |
| + /// - The [bodyName] |
|
floitsch
2015/02/13 15:30:06
The body function ([bodyName]). ?
Otherwise it so
sigurdm
2015/02/17 08:43:10
Done.
|
| /// - The [controllerName] |
| /// - A JavaScript function that is executed if the future completed with |
| /// an error. That function is responsible for executing the right error |
| @@ -192,7 +207,7 @@ class AsyncRewriter extends js.NodeVisitor { |
| final js.Expression streamOfController; |
| /// Contructor creating the Iterable for a sync* method. Called with |
| - /// [helperName]. |
| + /// [bodyName]. |
| final js.Expression newIterable; |
| /// A JS Expression that creates a marker showing that iteration is over. |
| @@ -235,7 +250,7 @@ class AsyncRewriter extends js.NodeVisitor { |
| AsyncRewriter(this.diagnosticListener, |
| spannable, |
| - {this.thenHelper, |
| + {this.asyncHelper, |
| this.streamHelper, |
| this.streamOfController, |
| this.newCompleter, |
| @@ -263,14 +278,17 @@ class AsyncRewriter extends js.NodeVisitor { |
| // To avoid name collisions with existing names, the fresh names are |
| // generated after the analysis. |
| resultName = freshName("result"); |
| + errorCodeName = freshName("errorCode"); |
| completerName = freshName("completer"); |
| controllerName = freshName("controller"); |
| - helperName = freshName("helper"); |
| + bodyName = freshName("body"); |
| gotoName = freshName("goto"); |
| handlerName = freshName("handler"); |
| errorName = freshName("error"); |
| nextName = freshName("next"); |
| + yieldNextName = freshName("yieldNext"); |
| returnValueName = freshName("returnValue"); |
| + storedErrorName = freshName("storedError"); |
| outerLabelName = freshName("outer"); |
| selfName = freshName("self"); |
| @@ -279,7 +297,7 @@ class AsyncRewriter extends js.NodeVisitor { |
| js.Expression get currentErrorHandler { |
| return errorHandlerLabels.isEmpty |
| - ? new js.LiteralNull() |
| + ? js.number(rethrowLabel) |
| : js.number(errorHandlerLabels.last); |
| } |
| @@ -542,7 +560,7 @@ class AsyncRewriter extends js.NodeVisitor { |
| /// through all the enclosing finally blocks). The jump to here is made in |
| /// [visitReturn]. |
| /// |
| - /// Returning from an async method calls the [thenHelper] with the result. |
| + /// Returning from an async method calls the [asyncHelper] with the result. |
| /// (the result might have been stored in [returnValueName] by some finally |
| /// block). |
| /// |
| @@ -561,16 +579,17 @@ class AsyncRewriter extends js.NodeVisitor { |
| String returnValue = |
| analysis.hasExplicitReturns ? returnValueName : "null"; |
| addStatement(js.js.statement( |
| - "return #thenHelper($returnValue, null, $completerName, null)", { |
| - "thenHelper": thenHelper |
| - })); |
| + "return #thenHelper($returnValue, ${error_codes.SUCCESS}, " |
|
floitsch
2015/02/13 15:30:06
Don't use "$" if you don't need to.
This is a val
sigurdm
2015/02/17 08:43:10
Done.
|
| + "$completerName, null)", |
| + {"thenHelper": asyncHelper})); |
| break; |
| case const js.AsyncModifier.syncStar(): |
| addStatement(new js.Return(new js.Call(endOfIteration, []))); |
| break; |
| case const js.AsyncModifier.asyncStar(): |
| addStatement(js.js.statement( |
| - "return #streamHelper(null, null, $controllerName, null)", { |
| + "return #streamHelper(null, ${error_codes.SUCCESS}, " |
|
floitsch
2015/02/13 15:30:06
ditto.
sigurdm
2015/02/17 08:43:10
Done.
|
| + "$controllerName)", { |
| "streamHelper": streamHelper |
| })); |
| break; |
| @@ -578,9 +597,16 @@ class AsyncRewriter extends js.NodeVisitor { |
| diagnosticListener.internalError( |
| spannable, "Internal error, unexpected asyncmodifier $async"); |
| } |
| + if (isAsync || isAsyncStar) { |
| + beginLabel(rethrowLabel); |
| + addStatement(js.js.statement( |
| + "return #thenHelper($storedErrorName, ${error_codes.ERROR}, " |
|
floitsch
2015/02/13 15:30:06
ditto.
sigurdm
2015/02/17 08:43:09
Done.
|
| + "${isAsync ? completerName : controllerName})", |
| + {"thenHelper": isAsync ? asyncHelper : streamHelper})); |
| + } |
| } |
| - /// The initial call to [thenHelper]/[streamHelper]. |
| + /// The initial call to [asyncHelper]/[streamHelper]. |
| /// |
| /// There is no value to await/yield, so the first argument is `null` and |
| /// also the errorCallback is `null`. |
| @@ -590,8 +616,8 @@ class AsyncRewriter extends js.NodeVisitor { |
| js.Statement generateInitializer() { |
| if (isAsync) { |
| return js.js.statement( |
| - "return #thenHelper(null, $helperName, $completerName, null);", { |
| - "thenHelper": thenHelper |
| + "return #asyncHelper(null, $bodyName, $completerName, null);", { |
| + "asyncHelper": asyncHelper |
| }); |
| } else if (isAsyncStar) { |
| return js.js.statement( |
| @@ -607,17 +633,17 @@ class AsyncRewriter extends js.NodeVisitor { |
| /// Rewrites an async/sync*/async* function to a normal Javascript function. |
| /// |
| /// The control flow is flattened by simulating 'goto' using a switch in a |
| - /// loop and a state variable [gotoName] inside a helper-function that can be |
| - /// called back by [thenHelper]/[streamHelper]/the [Iterator]. |
| + /// loop and a state variable [gotoName] inside a nested function [bodyName] |
| + /// that can be called back by [asyncHelper]/[asyncStarHelper]/the [Iterator]. |
| /// |
| /// Local variables are hoisted outside the helper. |
| /// |
| /// Awaits in async/async* are translated to code that remembers the current |
| /// location (so the function can resume from where it was) followed by a call |
| - /// to the [thenHelper]. The helper sets up the waiting for the awaited value |
| + /// to the [asyncHelper]. The helper sets up the waiting for the awaited value |
| /// and returns a future which is immediately returned by the translated |
| /// await. |
| - /// Yields in async* are translated to a call to the [streamHelper]. They, |
| + /// Yields in async* are translated to a call to the [asyncStarHelper]. They, |
| /// too, need to be prepared to be interrupted in case the stream is paused or |
| /// canceled. (Currently we always suspend - this is different from the spec, |
| /// see `streamHelper` in `js_helper.dart`). |
| @@ -635,7 +661,7 @@ class AsyncRewriter extends js.NodeVisitor { |
| /// return bar(p); |
| /// } |
| /// |
| - /// Becomes: |
| + /// Becomes (without error handling): |
| /// |
| /// function(x, y, z) { |
| /// var goto = 0, returnValue, completer = new Completer(), p; |
| @@ -644,17 +670,17 @@ class AsyncRewriter extends js.NodeVisitor { |
| /// switch (goto) { |
| /// case 0: |
| /// goto = 1 // Remember where to continue when the future succeeds. |
| - /// return thenHelper(foo(), helper, completer, null); |
| + /// return thenHelper(foo(), helper, completer); |
| /// case 1: |
| /// p = result; |
| /// returnValue = bar(p); |
| /// goto = 2; |
| /// break; |
| /// case 2: |
| - /// return thenHelper(returnValue, null, completer, null) |
| + /// return thenHelper(returnValue, null, completer) |
| /// } |
| /// } |
| - /// return thenHelper(null, helper, completer, null); |
| + /// return thenHelper(null, helper, completer); |
| /// } |
| /// } |
| /// |
| @@ -689,11 +715,16 @@ class AsyncRewriter extends js.NodeVisitor { |
| /// var goto = 0; |
| /// var returnValue; |
| /// var completer = new Completer(); |
| - /// var handler = null; |
| + /// var handler = 8; // Outside try-blocks go to the rethrow label. |
|
floitsch
2015/02/13 15:30:06
Handler is slightly weird, because we also use it
sigurdm
2015/02/17 08:43:10
Acknowledged. I mentioned it in visitTry.
|
| /// var p; |
| + /// var storedError; |
| /// // The result can be either the result of an awaited future, or an |
| /// // error if the future completed with an error. |
| - /// function helper(result) { |
| + /// function helper(errorCode, result) { |
| + /// if (errorCode == 1) { |
| + /// storedError = result; |
| + /// goto = handler; |
| + /// } |
| /// while (true) { |
| /// try { |
| /// switch (goto) { |
| @@ -718,7 +749,9 @@ class AsyncRewriter extends js.NodeVisitor { |
| /// goto = 5; // finally handler for outer try. |
| /// break; |
| /// case 4: // catch handler for outer try. |
| - /// e = result; |
| + /// handler = 5; // If we throw in this handler, do the finally |
|
floitsch
2015/02/13 15:30:06
?
// If the handler throws, do the finally ...
//
floitsch
2015/02/13 15:30:07
?
// If the handler throws, do the finally ...
//
sigurdm
2015/02/17 08:43:10
Acknowledged.
sigurdm
2015/02/17 08:43:10
Done.
|
| + /// next = [8] // And rethrow. |
| + /// e = storedError; |
| /// handle(e); |
| /// // Fall through to finally. |
| /// case 5: // finally handler for outer try. |
| @@ -728,14 +761,16 @@ class AsyncRewriter extends js.NodeVisitor { |
| /// break; |
| /// case 6: // Exiting outer try. |
| /// case 7: // return |
| - /// return thenHelper(returnValue, null, completer, null); |
| + /// return thenHelper(returnValue, 0, completer); |
| + /// case 8: // Rethrow |
| + /// return thenHelper(storedError, 1, completer); |
| /// } |
| /// } catch (error) { |
| - /// result = error; |
| + /// storedError = error; |
| /// goto = handler; |
| /// } |
| /// } |
| - /// return thenHelper(null, helper, completer, null); |
| + /// return thenHelper(null, helper, completer); |
| /// } |
| /// } |
| /// |
| @@ -748,6 +783,8 @@ class AsyncRewriter extends js.NodeVisitor { |
| // [visitDartYield]. |
| exitLabel = |
| analysis.hasExplicitReturns || isAsyncStar ? newLabel("return") : null; |
| + rethrowLabel = newLabel("rethrow"); |
| + handlerLabels[node] = rethrowLabel; |
| js.Statement body = node.body; |
| targetsAndTries.add(node); |
| visitStatement(body); |
| @@ -762,17 +799,13 @@ class AsyncRewriter extends js.NodeVisitor { |
| if (hasJumpThoughOuterLabel) { |
| helperBody = js.js.statement("$outerLabelName: #", [helperBody]); |
| } |
| - if (hasTryBlocks) { |
| - helperBody = js.js.statement(""" |
| - try { |
| - #body |
| - } catch ($errorName){ |
| - if ($handlerName === null) |
| - throw $errorName; |
| - $resultName = $errorName; |
| - $gotoName = $handlerName; |
| - }""", {"body": helperBody}); |
| - } |
| + helperBody = js.js.statement(""" |
| + try { |
| + #body |
| + } catch ($errorName){ |
| + $storedErrorName = $errorName; |
| + $gotoName = $handlerName; |
| + }""", {"body": helperBody}); |
| List<js.VariableInitialization> inits = <js.VariableInitialization>[]; |
| js.VariableInitialization makeInit(String name, js.Expression initValue) { |
| @@ -785,17 +818,19 @@ class AsyncRewriter extends js.NodeVisitor { |
| inits.add(makeInit(completerName, new js.New(newCompleter, []))); |
| } else if (isAsyncStar) { |
| inits.add(makeInit(controllerName, |
| - new js.Call(newController, [new js.VariableUse(helperName)]))); |
| - } |
| - if (hasTryBlocks) { |
| - inits.add(makeInit(handlerName, new js.LiteralNull())); |
| + new js.Call(newController, [new js.VariableUse(bodyName)]))); |
| } |
| + inits.add(makeInit(handlerName, js.number(rethrowLabel))); |
| + inits.add(makeInit(storedErrorName, null)); |
| if (hasJumpThroughFinally) { |
| inits.add(makeInit(nextName, null)); |
| } |
| if (analysis.hasExplicitReturns && isAsync) { |
| inits.add(makeInit(returnValueName, null)); |
| } |
| + if (isSyncStar) { |
| + inits.add(makeInit(resultName, null)); |
| + } |
| if (analysis.hasThis && !isSyncStar) { |
| // Sync* functions must remember `this` on the level of the outer |
| // function. |
| @@ -814,7 +849,7 @@ class AsyncRewriter extends js.NodeVisitor { |
| var $selfName = this; |
| return new #newIterable(function () { |
| #varDecl; |
| - return function $helperName($resultName) { |
| + return function $bodyName() { |
| while (true) |
| #helperBody; |
| }; |
| @@ -831,15 +866,30 @@ class AsyncRewriter extends js.NodeVisitor { |
| return js.js(""" |
| function (#params) { |
| #varDecl; |
| - function $helperName($resultName) { |
| + function $bodyName($errorCodeName, $resultName) { |
| + if (#hasYield) |
| + switch ($errorCodeName) { |
| + case ${error_codes.STREAM_WAS_CANCELED}: |
| + $nextName = $yieldNextName; |
| + // fallthrough |
|
floitsch
2015/02/13 15:30:06
Why fall through?
Is it to avoid repeating the $go
sigurdm
2015/02/17 08:43:09
Only for the handler. I changed the code, so it do
|
| + case ${error_codes.ERROR}: |
| + $storedErrorName = $resultName; |
| + $gotoName = $handlerName; |
| + } |
| + else |
| + if ($errorCodeName == ${error_codes.ERROR}) { |
| + $storedErrorName = $resultName; |
| + $gotoName = $handlerName; |
| + } |
| while (true) |
| #helperBody; |
| } |
| #init; |
| }""", { |
| "params": node.params, |
| - "helperBody": helperBody, |
| "varDecl": varDecl, |
| + "hasYield": analysis.hasYield, |
| + "helperBody": helperBody, |
| "init": generateInitializer() |
| }); |
| } |
| @@ -888,7 +938,7 @@ class AsyncRewriter extends js.NodeVisitor { |
| } |
| } |
| - /// An await is translated to a call to [thenHelper]/[streamHelper]. |
| + /// An await is translated to a call to [asyncHelper]/[streamHelper]. |
| /// |
| /// See the comments of [visitFun] for an example. |
| @override |
| @@ -897,23 +947,13 @@ class AsyncRewriter extends js.NodeVisitor { |
| int afterAwait = newLabel("returning from await."); |
| withExpression(node.expression, (js.Expression value) { |
| addStatement(setGotoVariable(afterAwait)); |
| - js.Expression errorCallback = errorHandlerLabels.isEmpty |
| - ? new js.LiteralNull() |
| - : js.js(""" |
| - function($errorName) { |
| - $gotoName = #currentHandler; |
| - $helperName($errorName); |
| - }""", {"currentHandler": currentErrorHandler}); |
| - |
| addStatement(js.js.statement(""" |
| return #thenHelper(#value, |
| - $helperName, |
| - ${isAsync ? completerName : controllerName}, |
| - #errorCallback); |
| + $bodyName, |
| + ${isAsync ? completerName : controllerName}); |
| """, { |
| - "thenHelper": isAsync ? thenHelper : streamHelper, |
| + "thenHelper": isAsync ? asyncHelper : streamHelper, |
|
floitsch
2015/02/13 15:30:07
rename "thenHelper" to "asyncHelper" ?
sigurdm
2015/02/17 08:43:10
Done.
|
| "value": value, |
| - "errorCallback": errorCallback |
| })); |
| }, store: false); |
| beginLabel(afterAwait); |
| @@ -1523,12 +1563,30 @@ class AsyncRewriter extends js.NodeVisitor { |
| }, store: false); |
| } |
| - setErrorHandler() { |
| + setErrorHandler([int errorHandler]) { |
| addExpressionStatement(new js.Assignment( |
| - new js.VariableUse(handlerName), currentErrorHandler)); |
| + new js.VariableUse(handlerName), |
| + errorHandler == null ? currentErrorHandler : js.number(errorHandler))); |
| } |
| - @override |
| + List<int> _finalliesAndEnclosingHandler() { |
| + List<int> result = new List<int>(); |
| + for (int i = targetsAndTries.length - 1; i >= 0; i--) { |
| + js.Node node = targetsAndTries[i]; |
| + int handlerLabel = handlerLabels[node]; |
| + if (handlerLabel != null) { |
| + result.add(handlerLabel); |
| + break; |
| + } |
| + if (node is js.Try) { |
| + assert(node.finallyPart != null); |
| + result.add(finallyLabels[node]); |
| + } |
| + } |
| + return result.reversed.toList(); |
| + } |
| + |
| + /// See the comments of [visitFun] for more explanation. |
| void visitTry(js.Try node) { |
| if (!shouldTransform(node)) { |
| js.Block body = translateInBlock(node.body); |
| @@ -1542,6 +1600,7 @@ class AsyncRewriter extends js.NodeVisitor { |
| addStatement(new js.Try(body, catchPart, finallyPart)); |
| return; |
| } |
| + |
| hasTryBlocks = true; |
| int handlerLabel = newLabel("catch"); |
| int finallyLabel = newLabel("finally"); |
| @@ -1567,7 +1626,18 @@ class AsyncRewriter extends js.NodeVisitor { |
| } |
| beginLabel(handlerLabel); |
| if (node.catchPart != null) { |
| - setErrorHandler(); |
| + if (node.finallyPart == null) { |
| + setErrorHandler(); |
| + } else { |
| + // If an error is thrown in the catch-part, and there is a finally |
| + // block, goto that. |
| + // And from that finally block, go to all enclosing finally blocks until |
|
floitsch
2015/02/13 15:30:06
If the catch-part throws visit the finally block f
sigurdm
2015/02/17 08:43:09
Done.
|
| + // the first enclosing catch-handler, and go to those. |
| + List<int> finallies = _finalliesAndEnclosingHandler(); |
| + setErrorHandler(finallies.removeLast()); |
| + addStatement(js.js.statement("$nextName = #;", |
| + [new js.ArrayInitializer(finallies.map(js.number).toList())])); |
| + } |
| // The catch declaration name can shadow outer variables, so a fresh name |
| // is needed to avoid collisions. See Ecma 262, 3rd edition, |
| // section 12.14. |
| @@ -1576,10 +1646,12 @@ class AsyncRewriter extends js.NodeVisitor { |
| variableRenamings |
| .add(new Pair(node.catchPart.declaration.name, errorRename)); |
| addExpressionStatement(new js.Assignment( |
| - new js.VariableUse(errorRename), new js.VariableUse(resultName))); |
| + new js.VariableUse(errorRename), |
| + new js.VariableUse(storedErrorName))); |
| visitStatement(node.catchPart.body); |
| variableRenamings.removeLast(); |
| } |
| + |
| if (node.finallyPart != null) { |
| targetsAndTries.removeLast(); |
| setErrorHandler(); |
| @@ -1683,10 +1755,8 @@ class AsyncRewriter extends js.NodeVisitor { |
| /// |
| /// yield/yield* in an async* function is translated much like the `await` is |
| /// translated in [visitAwait], only the object is wrapped in a |
| - /// [yieldExpression]/[yieldStarExpression] to let [streamHelper] distinguish. |
| - /// |
| - /// Because there is no Future that can fail (as there is in await) null is |
| - /// passed as the errorCallback. |
| + /// [yieldExpression]/[yieldStarExpression] to let [asyncStarHelper] |
| + /// distinguish. |
|
floitsch
2015/02/13 15:30:06
distinguish them.
sigurdm
2015/02/17 08:43:09
Done.
|
| void addAsyncYield(js.DartYield node, js.Expression expression) { |
| assert(isAsyncStar); |
| // Find all the finally blocks that should be performed if the stream is |
| @@ -1696,23 +1766,16 @@ class AsyncRewriter extends js.NodeVisitor { |
| enclosingFinallyLabels.addAll(targetsAndTries |
| .where((js.Node node) => node is js.Try) |
| .map((js.Try node) => finallyLabels[node])); |
| - int destinationOnCancel = enclosingFinallyLabels.removeLast(); |
| - js.ArrayInitializer finallyListInitializer = new js.ArrayInitializer( |
| - enclosingFinallyLabels.map(js.number).toList()); |
| + setErrorHandler(enclosingFinallyLabels.removeLast()); |
|
sigurdm
2015/02/17 08:43:09
I realized that I forget to reset the error-handle
|
| + addStatement(js.js.statement("$yieldNextName = #", |
| + [new js.ArrayInitializer(enclosingFinallyLabels.map(js.number) |
| + .toList())])); |
| addStatement(js.js.statement(""" |
| return #streamHelper(#yieldExpression(#expression), |
| - $helperName, $controllerName, function () { |
| - if (#notEmptyFinallyList) |
| - $nextName = #finallyList; |
| - $gotoName = #destinationOnCancel; |
| - $helperName(); |
| - });""", { |
| + $bodyName, $controllerName);""", { |
| "streamHelper": streamHelper, |
| "yieldExpression": node.hasStar ? yieldStarExpression : yieldExpression, |
| "expression": expression, |
| - "notEmptyFinallyList": enclosingFinallyLabels.isNotEmpty, |
| - "finallyList": finallyListInitializer, |
| - "destinationOnCancel": js.number(destinationOnCancel) |
| })); |
| } |
| @@ -1753,6 +1816,8 @@ class PreTranslationAnalysis extends js.NodeVisitor<bool> { |
| bool hasThis = false; |
| + bool hasYield = false; |
| + |
| // The function currently being analyzed. |
| js.Fun currentFunction; |
| @@ -2143,6 +2208,7 @@ class PreTranslationAnalysis extends js.NodeVisitor<bool> { |
| @override |
| bool visitDartYield(js.DartYield node) { |
| + hasYield = true; |
| visit(node.expression); |
| return true; |
| } |