Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(423)

Side by Side Diff: frog/await/transformation.dart

Issue 9006053: frog await: allow errors after the await within try-catch blocks. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: '' Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /** 5 /**
6 * Desugarizes all await calls in a single function. This visitor assumes that 6 * Desugarizes all await calls in a single function. This visitor assumes that
7 * the tree is already normalized with [AwaitNormalizer]. For this reason it 7 * the tree is already normalized with [AwaitNormalizer]. For this reason it
8 * only needs to recurse on statements and not on expressions. Like in 8 * only needs to recurse on statements and not on expressions. Like in
9 * [AwaitChecker], nested functions have to be processed separately. 9 * [AwaitChecker], nested functions have to be processed separately.
10 */ 10 */
(...skipping 12 matching lines...) Expand all
23 static final _IGNORED_THEN_PARAM = _PREFIX + 'ignored_param'; 23 static final _IGNORED_THEN_PARAM = _PREFIX + 'ignored_param';
24 static final _CONTINUATION_PREFIX = _PREFIX + 'after_'; 24 static final _CONTINUATION_PREFIX = _PREFIX + 'after_';
25 25
26 static final _COMPLETE_METHOD = 'complete'; 26 static final _COMPLETE_METHOD = 'complete';
27 static final _COMPLETE_EXCEPTION_METHOD = 'completeException'; 27 static final _COMPLETE_EXCEPTION_METHOD = 'completeException';
28 28
29 29
30 /** The continuation when visiting a particular statement. */ 30 /** The continuation when visiting a particular statement. */
31 Queue<Statement> continuation; 31 Queue<Statement> continuation;
32 32
33 /** If not null, a closure to call when a future ends with an exception. */ 33 /** Closures to call when a future throws an exception (in reverse order). */
34 Identifier currentExceptionHandler; 34 List<Identifier> exceptionHandlers;
35
36 /** All try-catch blocks enclosing the current statement. */
37 List<TryStatement> enclosingTrys;
35 38
36 /** Counter to ensure created closure names are unique. */ 39 /** Counter to ensure created closure names are unique. */
37 int continuationClosures = 0; 40 int continuationClosures = 0;
38 41
39 /** Nodes containing await expressions (determined by [AwaitChecker]). */ 42 /** Nodes containing await expressions (determined by [AwaitChecker]). */
40 final NodeSet haveAwait; 43 final NodeSet haveAwait;
41 44
42 AwaitProcessor(this.haveAwait) : continuation = new Queue<Statement>(); 45 AwaitProcessor(this.haveAwait)
46 : continuation = new Queue<Statement>(),
47 exceptionHandlers = [],
48 enclosingTrys = [];
43 49
44 visitVariableDefinition(VariableDefinition node) { 50 visitVariableDefinition(VariableDefinition node) {
45 if (!haveAwait.contains(node)) return node; 51 if (!haveAwait.contains(node)) return node;
46 final values = node.values; 52 final values = node.values;
47 // After normalization, variable declarations with await can only occur at 53 // After normalization, variable declarations with await can only occur at
48 // the top-level and there is no other declaration. 54 // the top-level and there is no other declaration.
49 assert(values != null && values.length == 1 55 assert(values != null && values.length == 1
50 && values[0] is AwaitExpression); 56 && values[0] is AwaitExpression);
51 final param = new Identifier(_THEN_PARAM, node.span); 57 final param = new Identifier(_THEN_PARAM, node.span);
52 // T x = await t; ... becomes: 58 // T x = await t; ... becomes:
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 // - consider when await shows in catch blocks, but not in the try block 196 // - consider when await shows in catch blocks, but not in the try block
191 // - support exceptions after the await 197 // - support exceptions after the await
192 // - handle nested try blocks 198 // - handle nested try blocks
193 // - support finally 199 // - support finally
194 // - consider throws within catch blocks, e.g: 200 // - consider throws within catch blocks, e.g:
195 // try { a } catch (E e1) { throw new E2(); } catch (E2 e2) { -- } 201 // try { a } catch (E e1) { throw new E2(); } catch (E2 e2) { -- }
196 202
197 String afterTry = _newClosureName(_CONTINUATION_PREFIX + "_try"); 203 String afterTry = _newClosureName(_CONTINUATION_PREFIX + "_try");
198 Statement afterTryDef = _makeContinuation(afterTry, node.span); 204 Statement afterTryDef = _makeContinuation(afterTry, node.span);
199 205
200 // Transform the body first:
201 continuation = new Queue();
202 final exceptionHandlerName = new Identifier(
203 _newClosureName(_PREFIX + "exception_handler"), node.span);
204 currentExceptionHandler = exceptionHandlerName;
205 continuation.addFirst(_callNoArg(afterTry, node.span));
206 Statement body = node.body.visit(this);
207 currentExceptionHandler = null;
208
209 final defs = []; // closures for each catch block (avoid duplicating code). 206 final defs = []; // closures for each catch block (avoid duplicating code).
210 final catches = []; // catch clauses of the transformed try-catch block 207 final catches = []; // catch clauses of the transformed try-catch block
211 208
212 // Catch blocks are passed as an exception handler on de-sugared awaits: 209 // Catch blocks are passed as an exception handler on de-sugared awaits:
210 final handlerName = new Identifier(
211 _newClosureName(_PREFIX + "exception_handler"), node.span);
213 // TODO(sigmund): add trace argument (library change in Future<T>) 212 // TODO(sigmund): add trace argument (library change in Future<T>)
214 final handlerArg = new Identifier(_EXCEPTION_HANDLER_PARAM, node.span); 213 final handlerArg = new Identifier(_EXCEPTION_HANDLER_PARAM, node.span);
215 final handlerBody = []; 214 final handlerBody = [];
216 215
217 // The exception handler is smaller when we encounter an untyped catch. 216 // Transform each catch-block internally.
218 bool untypedCatch = false; 217 bool untypedCatch = false; // When true, the exception handler is smaller.
219
220 for (CatchNode n in node.catches) { 218 for (CatchNode n in node.catches) {
221 String fname = _newClosureName(_PREFIX + "catch"); 219 String fname = _newClosureName(_PREFIX + "catch");
222 220
223 // Code in transformed catch-block: 221 // Code in transformed catch-block:
224 continuation = new Queue(); 222 continuation = new Queue();
225 continuation.addFirst(_callNoArg(afterTry, node.span)); 223 continuation.addFirst(_callNoArg(afterTry, node.span));
226 continuation.addFirst(n.body.visit(this)); 224 continuation.addFirst(n.body.visit(this));
227 225
228 defs.add(_makeCatchFunction(n, fname)); 226 defs.add(_makeCatchFunction(n, fname));
229 catches.add(new CatchNode(n.exception, n.trace, 227 catches.add(new CatchNode(n.exception, n.trace,
230 new BlockStatement( 228 new BlockStatement(
231 [_callCatchFunction(n, fname), _returnFuture(n.span)], n.span), 229 [_callCatchFunction(n, fname), _returnFuture(n.span)], n.span),
232 n.span)); 230 n.span));
233 231
234 // Code in exceptionHandler: 232 // Code in exception handler:
235 if (!untypedCatch) { 233 if (!untypedCatch) {
236 final exceptionHandlerCases = [ 234 final exceptionHandlerCases = [
237 _callCatchFunctionHelper(fname, handlerArg, null, n.span), 235 _callCatchFunctionHelper(fname, handlerArg, null, n.span),
238 _returnBoolean(n.span, true)]; 236 _returnBoolean(n.span, true)];
239 if (n.exception.type == null) { 237 if (n.exception.type == null) {
240 handlerBody.addAll(exceptionHandlerCases); 238 handlerBody.addAll(exceptionHandlerCases);
241 untypedCatch = true; 239 untypedCatch = true;
242 } else { 240 } else {
243 handlerBody.add(new IfStatement( 241 handlerBody.add(new IfStatement(
244 new IsExpression(true, handlerArg, n.exception.type, n.span), 242 new IsExpression(true,
243 new VarExpression(handlerArg, n.span),
244 n.exception.type, n.span),
245 new BlockStatement(exceptionHandlerCases, n.span), 245 new BlockStatement(exceptionHandlerCases, n.span),
246 null, n.span)); 246 null, n.span));
247 } 247 }
248 } 248 }
249 } 249 }
250 250
251 if (!untypedCatch) { 251 if (!untypedCatch) {
252 handlerBody.add(_returnBoolean(node.span, false)); 252 handlerBody.add(_returnBoolean(node.span, false));
253 } 253 }
254 254
255 final handlerDef = new FunctionDefinition([], null, 255 // Declare the exception handler
256 exceptionHandlerName, [new FormalNode( 256 final handlerDef = new FunctionDefinition([], null, handlerName,
257 false, false, null, handlerArg, null, node.span)], 257 [new FormalNode(false, false, null, handlerArg, null, node.span)],
258 null, null, new BlockStatement(handlerBody, node.span), node.span); 258 null, null, new BlockStatement(handlerBody, node.span), node.span);
259 259
260 // Transform the try body, tracking the enclosing try blocks and
261 // exception handlers.
262 enclosingTrys.addLast(new TryStatement(
263 null /* this is replaced with code in [_desugarAwaitCall] */,
264 catches, node.finallyBlock, node.span));
265 exceptionHandlers.addLast(handlerName);
266 continuation = new Queue();
267 continuation.addFirst(_callNoArg(afterTry, node.span));
268 Statement body = node.body.visit(this);
269 exceptionHandlers.removeLast();
270 enclosingTrys.removeLast();
271
260 continuation = new Queue(); 272 continuation = new Queue();
261 continuation.addAll(defs); 273 continuation.addAll(defs);
262 continuation.add(handlerDef); 274 continuation.add(handlerDef);
263 continuation.add(new TryStatement(body, 275 continuation.add(new TryStatement(body,
264 catches, node.finallyBlock, node.span)); 276 catches, node.finallyBlock, node.span));
265 continuation.add(_callNoArg(afterTry, node.span)); 277 continuation.add(_callNoArg(afterTry, node.span));
266 return afterTryDef; 278 return afterTryDef;
267 } 279 }
268 280
269 /** 281 /**
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
371 } 383 }
372 384
373 /** 385 /**
374 * Converts an await expression into several statements: calling 386 * Converts an await expression into several statements: calling
375 * [:Future.then:] and propatating errors. This implementation assumes that 387 * [:Future.then:] and propatating errors. This implementation assumes that
376 * await calls are within blocks (after normalization). 388 * await calls are within blocks (after normalization).
377 */ 389 */
378 _desugarAwaitCall(AwaitExpression node, Identifier param) { 390 _desugarAwaitCall(AwaitExpression node, Identifier param) {
379 List<Statement> afterAwait = []; 391 List<Statement> afterAwait = [];
380 afterAwait.addAll(continuation); 392 afterAwait.addAll(continuation);
381 if (afterAwait[afterAwait.length - 1] is ReturnStatement) { 393 if (afterAwait.last() is ReturnStatement) {
382 // The only reason there is a `return` is because there was another await 394 // The only reason there is a `return` is because there was another await
383 // and we introduced it. Such `return` is not needed in the callback. 395 // and we introduced it. Such `return` is not needed in the callback.
384 afterAwait.length = afterAwait.length - 1; 396 afterAwait.removeLast();
397 }
398 // Within try-blocks, we wrap the continuation in a try-catch block:
399 Statement thenBody = new BlockStatement(afterAwait, node.span);
400 for (int i = enclosingTrys.length - 1; i >= 0; i--) {
401 final templateTry = enclosingTrys[i];
402 thenBody = new TryStatement(thenBody,
403 templateTry.catches, templateTry.finallyBlock, templateTry.span);
385 } 404 }
386 405
387 // A lambda function that executes the continuation. 406 // A lambda function that executes the continuation.
388 final thenArg = new LambdaExpression( 407 final thenArg = new LambdaExpression(
389 new FunctionDefinition([], null, null, 408 new FunctionDefinition([], null, null,
390 [new FormalNode( 409 [new FormalNode(
391 false, false, null /* infer type from body? */, 410 false, false, null /* infer type from body? */,
392 param, null, param.span) 411 param, null, param.span)
393 ], null, null, 412 ], null, null,
394 new BlockStatement(afterAwait, node.span), node.span), 413 thenBody, node.span),
395 node.span); 414 node.span);
396 415
397 continuation.clear(); 416 continuation.clear();
398 continuation.addFirst(_returnFuture(node.span)); 417 continuation.addFirst(_returnFuture(node.span));
399 // Within try-blocks, we also add an exception handler to propagate errors. 418 // Within try-blocks, we also add an exception handler to propagate errors.
400 if (currentExceptionHandler != null) { 419 for (final handlerName in exceptionHandlers) {
401 continuation.addFirst(new ExpressionStatement(new CallExpression( 420 continuation.addFirst(new ExpressionStatement(new CallExpression(
402 new DotExpression(node.body, 421 new DotExpression(node.body,
403 new Identifier('handleException', node.span), node.span), 422 new Identifier('handleException', node.span), node.span),
404 [new ArgumentNode(null, 423 [new ArgumentNode(null,
405 new VarExpression(currentExceptionHandler, node.span), 424 new VarExpression(handlerName, node.span),
406 node.span)], 425 node.span)],
407 node.span), node.span)); 426 node.span), node.span));
408 } 427 }
409 return _callThen(node.body, thenArg, node.span); 428 return _callThen(node.body, thenArg, node.span);
410 } 429 }
411 430
412 /** Make the statement: [: future.then(arg); :] */ 431 /** Make the statement: [: future.then(arg); :] */
413 _callThen(Expression future, Expression arg, SourceSpan span) { 432 _callThen(Expression future, Expression arg, SourceSpan span) {
414 return new CallExpression( 433 return new CallExpression(
415 new DotExpression(future, new Identifier('then', span), span), 434 new DotExpression(future, new Identifier('then', span), span),
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after
596 // if (a) if (b) await t; 2; 615 // if (a) if (b) await t; 2;
597 // becomes: 616 // becomes:
598 // if (a) { if (b) { await t; } } 2; 617 // if (a) { if (b) { await t; } } 2;
599 // which becomes becomes: 618 // which becomes becomes:
600 // _2() { 2; } if (a) { if (b) { t.then((_) { _2(); } } } _2(); 619 // _2() { 2; } if (a) { if (b) { t.then((_) { _2(); } } } _2();
601 // (seems that 'a' doesn't need { }) 620 // (seems that 'a' doesn't need { })
602 // - 'if/while' with only one statement in the continuation (check the 621 // - 'if/while' with only one statement in the continuation (check the
603 // optimization that copies code rather than adding a continuation closure). 622 // optimization that copies code rather than adding a continuation closure).
604 // - await not inside a block (could break error propagation if normalization is 623 // - await not inside a block (could break error propagation if normalization is
605 // done incorrectly). 624 // done incorrectly).
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698