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

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

Issue 10548047: Remove frog from the repository. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Move test and update apidoc.gyp. Created 8 years, 6 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
« no previous file with comments | « frog/await/samples/mintmaker/mintmaker.dart ('k') | frog/block_scope.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
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
8 * only needs to recurse on statements and not on expressions. Like in
9 * [AwaitChecker], nested functions have to be processed separately.
10 */
11 class AwaitProcessor implements TreeVisitor {
12
13 /**
14 * Name of the variable introduced on asynchronous functions (of type
15 * [Completer] used to create a future of the function's result.
16 */
17 // TODO(sigmund): fix frog to make it possible to switch to '_a:res'. The
18 // mangling code currently breaks across closure-boundaries.
19 static final _PREFIX = '_a_';
20 static final _COMPLETER_NAME = _PREFIX + 'res';
21 static final _THEN_PARAM = _PREFIX + 'v';
22 static final _EXCEPTION_HANDLER_PARAM = _PREFIX + 'e';
23 static final _IGNORED_THEN_PARAM = _PREFIX + 'ignored_param';
24 static final _CONTINUATION_PREFIX = _PREFIX + 'after_';
25
26 static final _COMPLETE_METHOD = 'complete';
27 static final _COMPLETE_EXCEPTION_METHOD = 'completeException';
28
29
30 /** The continuation when visiting a particular statement. */
31 Queue<Statement> continuation;
32
33 /** Closures to call when a future throws an exception (in reverse order). */
34 List<Identifier> exceptionHandlers;
35
36 /** All try-catch blocks enclosing the current statement. */
37 List<TryStatement> enclosingTrys;
38
39 /** Counter to ensure created closure names are unique. */
40 int continuationClosures = 0;
41
42 /** Nodes containing await expressions (determined by [AwaitChecker]). */
43 final NodeSet haveAwait;
44
45 AwaitProcessor(this.haveAwait)
46 : continuation = new Queue<Statement>(),
47 exceptionHandlers = [],
48 enclosingTrys = [];
49
50 visitVariableDefinition(VariableDefinition node) {
51 if (!haveAwait.contains(node)) return node;
52 final values = node.values;
53 // After normalization, variable declarations with await can only occur at
54 // the top-level and there is no other declaration.
55 assert(values != null && values.length == 1
56 && values[0] is AwaitExpression);
57 final param = new Identifier(_THEN_PARAM, node.span);
58 // T x = await t; ... becomes:
59 // t.then((v) { T x = v; ... }
60 continuation.addFirst(new VariableDefinition(
61 node.modifiers, node.type, node.names,
62 [new VarExpression(param, node.span)], node.span));
63 return new ExpressionStatement(
64 _desugarAwaitCall(values[0], param), node.span);
65 }
66
67 visitFunctionDefinition(FunctionDefinition node) {
68 if (!haveAwait.contains(node)) return node;
69 // TODO(sigmund): consider making this part of the normalizer
70 // make implicit return explicit:
71 if (node.body is BlockStatement) {
72 BlockStatement block = node.body;
73 if (block.body.last() is! ReturnStatement) {
74 continuation.addFirst(
75 _callCompleter(_makeNull(node.span), node.span));
76 }
77 }
78
79 Statement newBody = node.body.visit(this);
80 // TODO(sigmund): extract type arg and put it in completer
81 // We update the body in-place to make it easier to update nested functions
82 // without having to rewrite the containing function's AST.
83 node.body = new BlockStatement([
84 _declareCompleter(null, node.span),
85 _wrapInTryCatch(newBody, node == _mainMethod.definition),
86 _returnFuture(node.span)], newBody.span);
87 return node;
88 }
89
90 visitReturnStatement(ReturnStatement node) {
91 continuation.clear();
92 return _callCompleter(node.value, node.span);
93 }
94
95 visitThrowStatement(ThrowStatement node) {
96 // instead of calling the exception handler here, we take a different
97 // approach and use throw directly. This helps make the try-catch
98 // transformation simpler.
99 continuation.clear();
100 return node;
101 }
102
103 visitAssertStatement(AssertStatement node) {
104 return node;
105 }
106
107 visitBreakStatement(BreakStatement node) {
108 // TODO(sigmund): implement
109 return node;
110 }
111
112 visitContinueStatement(ContinueStatement node) {
113 // TODO(sigmund): implement
114 return node;
115 }
116
117 visitIfStatement(IfStatement node) {
118 if (!haveAwait.contains(node)) return node;
119 // TODO(sigmund): consider whether we should create this continuation
120 // closure when there are few statements following (e.g a simple expression
121 // statement, no loops, etc).
122 String afterIf = _newClosureName(_CONTINUATION_PREFIX + "_if");
123 Statement def = _makeContinuation(afterIf, node.span);
124
125 final trueContinuation = new Queue();
126 trueContinuation.addFirst(_callNoArg(afterIf, node.span));
127 continuation = trueContinuation;
128 Statement tRes = node.trueBranch.visit(this);
129
130 Statement fRes = null;
131 if (node.falseBranch != null) {
132 final falseContinuation = new Queue();
133 falseContinuation.addFirst(_callNoArg(afterIf, node.span));
134 continuation = falseContinuation;
135 fRes = node.falseBranch.visit(this);
136 continuation = new Queue();
137 } else {
138 continuation = new Queue();
139 continuation.addFirst(_callNoArg(afterIf, node.span));
140 }
141
142 continuation.addFirst(new IfStatement(node.test, tRes, fRes, node.span));
143 return def;
144 }
145
146 visitWhileStatement(WhileStatement node) {
147 if (!haveAwait.contains(node)) return node;
148
149 String afterWhile = _newClosureName(_CONTINUATION_PREFIX + "_while");
150 Statement def = _makeContinuation(afterWhile, node.span);
151 String repeatWhile = _newClosureName(_PREFIX + "_while");
152
153 final bodyContinuation = new Queue();
154 bodyContinuation.addFirst(_callNoArg(repeatWhile, node.span));
155 continuation = bodyContinuation;
156 Statement body = node.body.visit(this);
157
158 continuation = new Queue();
159 continuation.addFirst(_callNoArg(afterWhile, node.span));
160 continuation.addFirst(new IfStatement(node.test, body, null, node.span));
161 Statement defLoop = _makeContinuation(repeatWhile, node.span);
162
163 continuation = new Queue();
164 continuation.addFirst(_callNoArg(repeatWhile, node.span));
165 continuation.addFirst(defLoop);
166 return def;
167 }
168
169 visitDoStatement(DoStatement node) {
170 if (!haveAwait.contains(node)) return node;
171 // TODO(sigmund): implement
172 return node;
173 }
174
175 visitForStatement(ForStatement node) {
176 if (!haveAwait.contains(node)) return node;
177 // TODO(sigmund): implement
178 // Note: this is harder than while loops because of dart's special semantics
179 // capturing the loop variable.
180 return node;
181 }
182
183 visitForInStatement(ForInStatement node) {
184 if (!haveAwait.contains(node)) return node;
185 // TODO(sigmund): implement
186 // Note: this is harder than while loops because of dart's special semantics
187 // capturing the loop variable.
188 return node;
189 }
190
191 visitTryStatement(TryStatement node) {
192 if (!haveAwait.contains(node)) return node;
193 // TODO(sigmund): pending to do on try-catch blocks
194 // - consider when await shows in catch blocks, but not in the try block
195 // - support exceptions after the await
196 // - handle nested try blocks
197 // - support finally
198 // - consider throws within catch blocks, e.g:
199 // try { a } catch (E e1) { throw new E2(); } catch (E2 e2) { -- }
200
201 String afterTry = _newClosureName(_CONTINUATION_PREFIX + "_try");
202 Statement afterTryDef = _makeContinuation(afterTry, node.span);
203
204 final defs = []; // closures for each catch block (avoid duplicating code).
205 final catches = []; // catch clauses of the transformed try-catch block
206
207 // Catch blocks are passed as an exception handler on de-sugared awaits:
208 final handlerName = new Identifier(
209 _newClosureName(_PREFIX + "exception_handler"), node.span);
210 // TODO(sigmund): add trace argument (library change in Future<T>)
211 final handlerArg = new Identifier(_EXCEPTION_HANDLER_PARAM, node.span);
212 final handlerBody = [];
213
214 // Transform each catch-block internally.
215 bool untypedCatch = false; // When true, the exception handler is smaller.
216 for (CatchNode n in node.catches) {
217 String fname = _newClosureName(_PREFIX + "catch");
218
219 // Code in transformed catch-block:
220 continuation = new Queue();
221 continuation.addFirst(_callNoArg(afterTry, node.span));
222 continuation.addFirst(n.body.visit(this));
223
224 defs.add(_makeCatchFunction(n, fname));
225 catches.add(new CatchNode(n.exception, n.trace,
226 new BlockStatement(
227 [_callCatchFunction(n, fname), _returnFuture(n.span)], n.span),
228 n.span));
229
230 // Code in exception handler:
231 if (!untypedCatch) {
232 final exceptionHandlerCases = [
233 _callCatchFunctionHelper(fname, handlerArg, null, n.span),
234 _returnBoolean(n.span, true)];
235 if (n.exception.type == null) {
236 handlerBody.addAll(exceptionHandlerCases);
237 untypedCatch = true;
238 } else {
239 handlerBody.add(new IfStatement(
240 new IsExpression(true,
241 new VarExpression(handlerArg, n.span),
242 n.exception.type, n.span),
243 new BlockStatement(exceptionHandlerCases, n.span),
244 null, n.span));
245 }
246 }
247 }
248
249 if (!untypedCatch) {
250 handlerBody.add(_returnBoolean(node.span, false));
251 }
252
253 // Declare the exception handler
254 final handlerDef = new FunctionDefinition([], null, handlerName,
255 [new FormalNode(false, false, null, handlerArg, null, node.span)],
256 null, null, new BlockStatement(handlerBody, node.span), node.span);
257
258 // Transform the try body, tracking the enclosing try blocks and
259 // exception handlers.
260 enclosingTrys.addLast(new TryStatement(
261 null /* this is replaced with code in [_desugarAwaitCall] */,
262 catches, node.finallyBlock, node.span));
263 exceptionHandlers.addLast(handlerName);
264 continuation = new Queue();
265 continuation.addFirst(_callNoArg(afterTry, node.span));
266 Statement body = node.body.visit(this);
267 exceptionHandlers.removeLast();
268 enclosingTrys.removeLast();
269
270 continuation = new Queue();
271 continuation.addAll(defs);
272 continuation.add(handlerDef);
273 continuation.add(new TryStatement(body,
274 catches, node.finallyBlock, node.span));
275 continuation.add(_callNoArg(afterTry, node.span));
276 return afterTryDef;
277 }
278
279 /**
280 * Create a closure representing the catch block. The closure takes either 1
281 * or 2 args, depending on whether [n] has a `trace` declaration.
282 */
283 Statement _makeCatchFunction(CatchNode n, String fname) {
284 if (n.trace == null) {
285 return _makeContinuation1(fname,
286 n.exception.type, n.exception.name, n.span);
287 } else {
288 return _makeContinuation2(fname,
289 n.exception.type, n.exception.name,
290 n.trace.type, n.trace.name, n.span);
291 }
292 }
293
294 /** Calls the catch function declared for [n] using [_makeCatchFunction]. */
295 Statement _callCatchFunction(CatchNode n, String fname) {
296 return _callCatchFunctionHelper(fname, n.exception.name,
297 n.trace == null ? null : n.trace.name, n.span);
298 }
299
300 /** Helper to call a catch function declared using [_makeCatchFunction]. */
301 Statement _callCatchFunctionHelper(
302 String fname, Identifier exception, Identifier trace, SourceSpan span) {
303 if (trace == null) {
304 return _call1Arg(fname,
305 new VarExpression(exception, exception.span), span);
306 } else {
307 return _call2Args(fname, new VarExpression(exception, exception.span),
308 new VarExpression(trace, trace.span), span);
309 }
310 }
311
312 visitSwitchStatement(SwitchStatement node) {
313 if (!haveAwait.contains(node)) return node;
314 // TODO(sigmund): implement
315 return node;
316 }
317
318 visitBlockStatement(BlockStatement node) {
319 if (!haveAwait.contains(node) && continuation.isEmpty()) return node;
320 // TODO(sigmund): test also when !continuation.isEmpty();
321 for (int i = node.body.length - 1; i >= 0; i--) {
322 final res = node.body[i].visit(this);
323 // Note: we can't inline [res] in the line below because [continuation]
324 // might be redefined and we want to use the latest reference after we
325 // finish visiting [node.body].
326 continuation.addFirst(res);
327 }
328
329 List<Statement> newBody = [];
330 newBody.addAll(continuation);
331 continuation.clear();
332 return new BlockStatement(newBody, node.span);
333 }
334
335 visitLabeledStatement(LabeledStatement node) {
336 if (!haveAwait.contains(node)) return node;
337 // TODO(sigmund): implement
338 return node;
339 }
340
341 visitExpressionStatement(ExpressionStatement node) {
342 if (!haveAwait.contains(node)) return node;
343 // After normalization, expression statements with await can only occur at
344 // the top-level or as the rhs of simple assignments (= but not +=).
345 if (node.body is AwaitExpression) {
346 return new ExpressionStatement(
347 _desugarAwaitCall(node.body,
348 new Identifier(_IGNORED_THEN_PARAM, node.span)), node.span);
349 } else {
350 assert(node.body is BinaryExpression);
351 BinaryExpression bin = node.body;
352 assert(bin.op.kind == TokenKind.ASSIGN);
353 assert(bin.y is AwaitExpression);
354 final param = new Identifier(_THEN_PARAM, node.span);
355 continuation.addFirst(new ExpressionStatement(
356 new BinaryExpression(
357 bin.op, bin.x, new VarExpression(param, node.span), node.span),
358 node.span));
359 return new ExpressionStatement(
360 _desugarAwaitCall(bin.y, param), node.span);
361 }
362 }
363
364 visitEmptyStatement(EmptyStatement node) {
365 if (!haveAwait.contains(node)) return node;
366 return node;
367 }
368
369 visitArgumentNode(ArgumentNode node) {
370 if (!haveAwait.contains(node)) return node;
371 return node;
372 }
373
374 visitCatchNode(CatchNode node) {
375 // shouldn't reach here.
376 world.fatal("'catch' should be handled with the enclosing try statement",
377 node.span);
378 }
379
380 visitCaseNode(CaseNode node) {
381 if (!haveAwait.contains(node)) return node;
382 return node;
383 }
384
385 /**
386 * Converts an await expression into several statements: calling
387 * [:Future.then:] and propatating errors. This implementation assumes that
388 * await calls are within blocks (after normalization).
389 */
390 CallExpression _desugarAwaitCall(AwaitExpression node, Identifier param) {
391 List<Statement> afterAwait = [];
392 afterAwait.addAll(continuation);
393 if (afterAwait.last() is ReturnStatement) {
394 // The only reason there is a `return` is because there was another await
395 // and we introduced it. Such `return` is not needed in the callback.
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);
404 }
405
406 // A lambda function that executes the continuation.
407 final thenArg = new LambdaExpression(
408 new FunctionDefinition([], null, null,
409 [new FormalNode(
410 false, false, null /* infer type from body? */,
411 param, null, param.span)
412 ], null, null,
413 thenBody, node.span),
414 node.span);
415
416 continuation.clear();
417 continuation.addFirst(_returnFuture(node.span));
418 // Within try-blocks, we also add an exception handler to propagate errors.
419 for (final handlerName in exceptionHandlers) {
420 continuation.addFirst(new ExpressionStatement(new CallExpression(
421 new DotExpression(node.body,
422 new Identifier('handleException', node.span), node.span),
423 [new ArgumentNode(null,
424 new VarExpression(handlerName, node.span),
425 node.span)],
426 node.span), node.span));
427 }
428 return _callThen(node.body, thenArg, node.span);
429 }
430
431 /** Make the call expression: [: future.then(arg); :] */
432 CallExpression _callThen(Expression future, Expression arg, SourceSpan span) {
433 return new CallExpression(
434 new DotExpression(future, new Identifier('then', span), span),
435 [new ArgumentNode(null, arg, span)], span);
436 }
437
438 /** Make the statement: [: final Completer<T> v = new Completer<T>(); :]. */
439 VariableDefinition _declareCompleter(Type argType, SourceSpan span) {
440 final name = new Identifier('Completer', span);
441 final ctorName = new Identifier('', span);
442 var typeRef = new NameTypeReference(false, name, [ctorName], span);
443 var type = world.corelib.types['Completer'];
444 if (argType != null) {
445 typeRef = new GenericTypeReference(typeRef,
446 new TypeReference(span, argType), 0, span);
447 typeRef.type = type.getOrMakeConcreteType([argType]);
448 } else {
449 typeRef.type = type;
450 }
451 final def = new VariableDefinition([new Token.fake(TokenKind.FINAL, span)],
452 typeRef,
453 [new Identifier(_COMPLETER_NAME, span)],
454 [new NewExpression(false, typeRef, null, [], span)],
455 span);
456 return def;
457 }
458
459 /**
460 * Wrap [s] in a try-catch block that propagates errors through the future
461 * that is returned from the asynchronous function. If the function that we
462 * are generating is `main`, we add a noop listener on the resulting future.
463 * Without it, we would have to make it illegal to use `await` in main. This
464 * is because futures swallow exceptions if their values are never used.
465 */
466 Statement _wrapInTryCatch(Statement s, bool isMain) {
467 final ex = new Identifier("ex", s.span);
468 Statement catchStatement =
469 _callCompleterException(new VarExpression(ex, ex.span), s.span);
470 if (isMain) {
471 final future = new DotExpression(
472 new VarExpression(new Identifier(_COMPLETER_NAME, s.span), s.span),
473 new Identifier("future", s.span), s.span);
474 final noopHandler = new LambdaExpression(
475 new FunctionDefinition([], null, null,
476 [new FormalNode(false, false, null,
477 new Identifier(_IGNORED_THEN_PARAM, s.span), null, s.span)
478 ], null, null,
479 new BlockStatement([], s.span), s.span), s.span);
480 catchStatement = new BlockStatement([
481 // _a_res.future.then((_ignored_param) { });
482 new ExpressionStatement(
483 _callThen(future, noopHandler, s.span), s.span),
484 catchStatement], s.span);
485 }
486 return new TryStatement(s, [
487 new CatchNode(new DeclaredIdentifier(null, ex, ex.span), null,
488 catchStatement, s.span)], null, s.span);
489 }
490
491 /** Make the statement: [: _a$res.complete(value); :]. */
492 _callCompleter(Expression value, SourceSpan span) {
493 return _callTarget1Arg(_COMPLETER_NAME, _COMPLETE_METHOD, value, span);
494 }
495
496 /** Make the statement: [: _a$res.completeException(value); :]. */
497 _callCompleterException(Expression value, SourceSpan span) {
498 return _callTarget1Arg(
499 _COMPLETER_NAME, _COMPLETE_EXCEPTION_METHOD, value, span);
500 }
501
502 /** Make the statement: [: return _a$res.future; :]. */
503 _returnFuture(SourceSpan span) {
504 return new ReturnStatement(
505 new DotExpression(
506 new VarExpression(new Identifier(_COMPLETER_NAME, span), span),
507 new Identifier("future", span), span), span);
508 }
509
510 /** Create a unique name for a continuation. */
511 String _newClosureName(String name) {
512 continuationClosures++;
513 return '${name}_$continuationClosures';
514 }
515
516 /** Return the current continuation as a statement block for a method body. */
517 Statement _continuationAsBody(SourceSpan span) {
518 if (continuation.length == 1 && continuation.first() is BlockStatement) {
519 // No need to wrap a single block statement within a block statement:
520 return continuation.first();
521 } else {
522 List<Statement> continuationBlock = [];
523 continuationBlock.addAll(continuation);
524 return new BlockStatement(continuationBlock, span);
525 }
526 }
527
528 /** Create a closure that contains the continuation statements. */
529 FunctionDefinition _makeContinuation(String mName, SourceSpan span) {
530 return new FunctionDefinition([], null,
531 new Identifier(mName, span), [], null, null,
532 _continuationAsBody(span), span);
533 }
534
535 /** Create a 1-arg closure that contains the continuation statements. */
536 FunctionDefinition _makeContinuation1(String mName,
537 TypeReference arg1Type, Identifier arg1Name, SourceSpan span) {
538 return new FunctionDefinition([], null,
539 new Identifier(mName, span),
540 [new FormalNode(false, false, arg1Type, arg1Name, null, arg1Name.span)],
541 null, null, _continuationAsBody(span), span);
542 }
543
544 /** Create a 2-arg closure that contains the continuation statements. */
545 FunctionDefinition _makeContinuation2(
546 String mName, TypeReference arg1Type, Identifier arg1Name,
547 TypeReference arg2Type, Identifier arg2Name, SourceSpan span) {
548 return new FunctionDefinition([], null,
549 new Identifier(mName, span),
550 [new FormalNode(false, false, arg1Type, arg1Name, null, arg1Name.span),
551 new FormalNode(false, false, arg2Type, arg2Name, null, arg2Name.span)],
552 null, null, _continuationAsBody(span), span);
553 }
554
555 /** Make a statement invoking a function in scope. */
556 Statement _callNoArg(String mName, SourceSpan span) {
557 return new ExpressionStatement(new CallExpression(
558 new VarExpression(new Identifier(mName, span), span), [], span), span);
559 }
560
561 /** Make the statement: [: target.method(value); :]. */
562 Statement _callTarget1Arg(
563 String target, String method, Expression value, SourceSpan span) {
564 if (value == null) value = _makeNull(span);
565 return new ExpressionStatement(new CallExpression(
566 new DotExpression(
567 new VarExpression(new Identifier(target, span), span),
568 new Identifier(method, span), span),
569 [new ArgumentNode(null, value, value.span)], span), span);
570 }
571
572 /** Make the statement: [: f(value); :]. */
573 Statement _call1Arg(String f, Expression value, SourceSpan span) {
574 if (value == null) value = _makeNull(span);
575 return new ExpressionStatement(new CallExpression(
576 new VarExpression(new Identifier(f, span), span),
577 [new ArgumentNode(null, value, value.span)], span), span);
578 }
579
580 /** Make the statement: [: f(a, b); :]. */
581 Statement _call2Args(String f, Expression a, Expression b,
582 SourceSpan span) {
583 if (a == null) a = _makeNull(span);
584 if (b == null) b = _makeNull(span);
585 return new ExpressionStatement(new CallExpression(
586 new VarExpression(new Identifier(f, span), span),
587 [new ArgumentNode(null, a, a.span),
588 new ArgumentNode(null, b, b.span)], span), span);
589 }
590
591 /** Make a return statement for a boolean value. */
592 Statement _returnBoolean(SourceSpan span, bool value) {
593 return new ReturnStatement(
594 new LiteralExpression(Value.fromBool(value, span), span), span);
595 }
596
597 Expression _makeNull(SourceSpan span) {
598 return new LiteralExpression(Value.fromNull(span), span);
599 }
600 }
601
602 // TODO(sigmund): create the following tests:
603 // - await within the body of getter or setter properties
604 // - await in each valid AST construct
605 // - exceptions - make some of these fail and propagate errors.
606 // - methods with and without returns (are returns added implicitly)
607 // - await within assignmetns, but not declarations (see what happens with
608 // x = await t;
609 // x = await y;
610 // or
611 // x += await t;
612 // x += await y;
613 // (does it matter that ExpressionStatement will shadow the variable in the
614 // callback function to then?)
615 // - floating ifs/for loops, etc - make sure we don't need to normalize the ast
616 // to disallow 'floating' ifs or to insert a block statement when returning from
617 // ifs (rather than appending code to the existing continuation list). for
618 // instance:
619 // if (a) if (b) await t; 2;
620 // becomes:
621 // if (a) { if (b) { await t; } } 2;
622 // which becomes becomes:
623 // _2() { 2; } if (a) { if (b) { t.then((_) { _2(); } } } _2();
624 // (seems that 'a' doesn't need { })
625 // - 'if/while' with only one statement in the continuation (check the
626 // optimization that copies code rather than adding a continuation closure).
627 // - await not inside a block (could break error propagation if normalization is
628 // done incorrectly).
OLDNEW
« no previous file with comments | « frog/await/samples/mintmaker/mintmaker.dart ('k') | frog/block_scope.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698