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

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

Issue 9007053: await in frog: partial support for 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
« no previous file with comments | « corelib/src/implementation/future_implementation.dart ('k') | frog/minfrog » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 * A phase in the compilation process that processes the entire AST and 6 * A phase in the compilation process that processes the entire AST and
7 * desugars await expressions. 7 * desugars await expressions.
8 */ 8 */
9 awaitTransformation() { 9 awaitTransformation() {
10 _mainMethod =
11 world.findMainMethod(world.getOrAddLibrary(options.dartScript));
10 for (var lib in world.libraries.getValues()) { 12 for (var lib in world.libraries.getValues()) {
11 for (var type in lib.types.getValues()) { 13 for (var type in lib.types.getValues()) {
12 for (var member in type.members.getValues()) { 14 for (var member in type.members.getValues()) {
13 _process(member); 15 _process(member);
14 } 16 }
15 for (var member in type.constructors.getValues()) { 17 for (var member in type.constructors.getValues()) {
16 _process(member); 18 _process(member);
17 } 19 }
18 } 20 }
19 } 21 }
20 } 22 }
21 23
24 Member _mainMethod;
25
22 /** Transform a single member (method or property). */ 26 /** Transform a single member (method or property). */
23 _process(Member member) { 27 _process(Member member) {
24 if (member.isConstructor || member.isMethod) { 28 if (member.isConstructor || member.isMethod) {
25 _processFunction(member.definition); 29 _processFunction(member.definition);
26 } else if (member.isProperty) { 30 } else if (member.isProperty) {
27 PropertyMember p = member; 31 PropertyMember p = member;
28 if (p.getter != null) _process(p.getter); 32 if (p.getter != null) _process(p.getter);
29 if (p.setter != null) _process(p.setter); 33 if (p.setter != null) _process(p.setter);
30 } 34 }
31 } 35 }
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after
227 } 231 }
228 return awaitSeen; 232 return awaitSeen;
229 } 233 }
230 234
231 visitTryStatement(TryStatement node) { 235 visitTryStatement(TryStatement node) {
232 bool awaitSeen = (_visit(node.body)); 236 bool awaitSeen = (_visit(node.body));
233 if (_visitList(node.catches)) awaitSeen = true; 237 if (_visitList(node.catches)) awaitSeen = true;
234 if (_visit(node.finallyBlock)) { 238 if (_visit(node.finallyBlock)) {
235 awaitSeen = true; 239 awaitSeen = true;
236 } 240 }
237 if (awaitSeen) { 241 if (awaitSeen) haveAwait.add(node);
238 haveAwait.add(node);
239 _notSupportedStmt("try", node);
240 }
241 return awaitSeen; 242 return awaitSeen;
242 } 243 }
243 244
244 visitSwitchStatement(SwitchStatement node) { 245 visitSwitchStatement(SwitchStatement node) {
245 bool awaitSeen = node.test.visit(this); 246 bool awaitSeen = node.test.visit(this);
246 if (_visitList(node.cases)) awaitSeen = true; 247 if (_visitList(node.cases)) awaitSeen = true;
247 if (awaitSeen) { 248 if (awaitSeen) {
248 haveAwait.add(node); 249 haveAwait.add(node);
249 _notSupportedStmt("switch", node); 250 _notSupportedStmt("switch", node);
250 } 251 }
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after
509 * [AwaitChecker], nested functions have to be processed separately. 510 * [AwaitChecker], nested functions have to be processed separately.
510 */ 511 */
511 class AwaitProcessor implements TreeVisitor { 512 class AwaitProcessor implements TreeVisitor {
512 513
513 /** 514 /**
514 * Name of the variable introduced on asynchronous functions (of type 515 * Name of the variable introduced on asynchronous functions (of type
515 * [Completer] used to create a future of the function's result. 516 * [Completer] used to create a future of the function's result.
516 */ 517 */
517 // TODO(sigmund): fix frog to make it possible to switch to '_a:res'. The 518 // TODO(sigmund): fix frog to make it possible to switch to '_a:res'. The
518 // current mangling breaks across closure-boundaries. 519 // current mangling breaks across closure-boundaries.
519 static final _COMPLETER_NAME = '_a_res'; 520 static final _PREFIX = '_a_';
520 static final _THEN_PARAM = '_a_v'; 521 static final _COMPLETER_NAME = _PREFIX + 'res';
521 static final _IGNORED_THEN_PARAM = '_a_ignored_param'; 522 static final _THEN_PARAM = _PREFIX + 'v';
523 static final _EXCEPTION_HANDLER_PARAM = _PREFIX + 'e';
524 static final _IGNORED_THEN_PARAM = _PREFIX + 'ignored_param';
525 static final _CONTINUATION_PREFIX = _PREFIX + 'after_';
526
522 static final _COMPLETE_METHOD = 'complete'; 527 static final _COMPLETE_METHOD = 'complete';
523 static final _COMPLETE_EXCEPTION_METHOD = 'completeException'; 528 static final _COMPLETE_EXCEPTION_METHOD = 'completeException';
524 529
525 static final _CONTINUATION_PREFIX = '_a_after_';
526 static final _LOOP_CONTINUATION_PREFIX = '_a_';
527 530
528 /** The continuation when visiting a particular statement. */ 531 /** The continuation when visiting a particular statement. */
529 Queue<Statement> continuation; 532 Queue<Statement> continuation;
530 533
534 /** If not null, a closure to call when a future ends with an exception. */
535 Identifier currentExceptionHandler;
536
531 /** Counter to ensure created closure names are unique. */ 537 /** Counter to ensure created closure names are unique. */
532 int continuationClosures = 0; 538 int continuationClosures = 0;
533 539
534 /** Nodes containing await expressions (determined by [AwaitChecker]). */ 540 /** Nodes containing await expressions (determined by [AwaitChecker]). */
535 final NodeSet haveAwait; 541 final NodeSet haveAwait;
536 542
537 AwaitProcessor(this.haveAwait) : continuation = new Queue<Statement>(); 543 AwaitProcessor(this.haveAwait) : continuation = new Queue<Statement>();
538 544
539 visitVariableDefinition(VariableDefinition node) { 545 visitVariableDefinition(VariableDefinition node) {
540 if (!haveAwait.contains(node)) return node; 546 if (!haveAwait.contains(node)) return node;
(...skipping 19 matching lines...) Expand all
560 if (node.body is BlockStatement) { 566 if (node.body is BlockStatement) {
561 BlockStatement block = node.body; 567 BlockStatement block = node.body;
562 if (block.body.last() is! ReturnStatement) { 568 if (block.body.last() is! ReturnStatement) {
563 continuation.addFirst( 569 continuation.addFirst(
564 _callCompleter(new NullExpression(node.span), node.span)); 570 _callCompleter(new NullExpression(node.span), node.span));
565 } 571 }
566 } 572 }
567 573
568 Statement newBody = node.body.visit(this); 574 Statement newBody = node.body.visit(this);
569 // TODO(sigmund): extract type arg and put it in completer 575 // TODO(sigmund): extract type arg and put it in completer
570 final newList = [_declareCompleter(null, node.span)];
571 if (newBody is BlockStatement) {
572 BlockStatement block = newBody;
573 newList.addAll(block.body);
574 } else {
575 newList.add(newBody);
576 }
577 // We update the body in-place to make it easier to update nested functions 576 // We update the body in-place to make it easier to update nested functions
578 // without having to rewrite the containing function's AST. 577 // without having to rewrite the containing function's AST.
579 node.body = new BlockStatement(newList, newBody.span); 578 node.body = new BlockStatement([
579 _declareCompleter(null, node.span),
580 _wrapInTryCatch(newBody, node == _mainMethod.definition),
581 _returnFuture(node.span)], newBody.span);
580 return node; 582 return node;
581 } 583 }
582 584
583 visitReturnStatement(ReturnStatement node) { 585 visitReturnStatement(ReturnStatement node) {
584 continuation.clear(); 586 continuation.clear();
585 return _callCompleter(node.value, node.span); 587 return _callCompleter(node.value, node.span);
586 } 588 }
587 589
588 visitThrowStatement(ThrowStatement node) { 590 visitThrowStatement(ThrowStatement node) {
591 // instead of calling the exception handler here, we take a different
592 // approach and use throw directly. This helps make the try-catch
593 // transformation simpler.
589 continuation.clear(); 594 continuation.clear();
590 return _callCompleterException(node.value, node.span); 595 return node;
591 } 596 }
592 597
593 visitAssertStatement(AssertStatement node) { 598 visitAssertStatement(AssertStatement node) {
594 // TODO(sigmund): implement. This should be normalized into a conditional 599 // TODO(sigmund): implement. This should be normalized into a conditional
595 // and call completeException only when the assertion fails. 600 // and call completeException only when the assertion fails.
596 return node; 601 return node;
597 } 602 }
598 603
599 visitBreakStatement(BreakStatement node) { 604 visitBreakStatement(BreakStatement node) {
600 // TODO(sigmund): implement 605 // TODO(sigmund): implement
601 return node; 606 return node;
602 } 607 }
603 608
604 visitContinueStatement(ContinueStatement node) { 609 visitContinueStatement(ContinueStatement node) {
605 // TODO(sigmund): implement 610 // TODO(sigmund): implement
606 return node; 611 return node;
607 } 612 }
608 613
609 visitIfStatement(IfStatement node) { 614 visitIfStatement(IfStatement node) {
610 if (!haveAwait.contains(node)) return node; 615 if (!haveAwait.contains(node)) return node;
611 // TODO(sigmund): consider whether we should create this continuation 616 // TODO(sigmund): consider whether we should create this continuation
612 // closure when there are few statements following (e.g a simple expression 617 // closure when there are few statements following (e.g a simple expression
613 // statement, no loops, etc). 618 // statement, no loops, etc).
614 String afterIf = _newClosureName("if", false); 619 String afterIf = _newClosureName(_CONTINUATION_PREFIX + "_if");
615 Statement def = _makeContinuation(afterIf, node.span); 620 Statement def = _makeContinuation(afterIf, node.span);
616 621
617 final trueContinuation = new Queue(); 622 final trueContinuation = new Queue();
618 trueContinuation.addFirst(_callNoArg(afterIf, node.span)); 623 trueContinuation.addFirst(_callNoArg(afterIf, node.span));
619 continuation = trueContinuation; 624 continuation = trueContinuation;
620 Statement tRes = node.trueBranch.visit(this); 625 Statement tRes = node.trueBranch.visit(this);
621 626
622 Statement fRes = null; 627 Statement fRes = null;
623 if (node.falseBranch != null) { 628 if (node.falseBranch != null) {
624 final falseContinuation = new Queue(); 629 final falseContinuation = new Queue();
625 falseContinuation.addFirst(_callNoArg(afterIf, node.span)); 630 falseContinuation.addFirst(_callNoArg(afterIf, node.span));
626 continuation = falseContinuation; 631 continuation = falseContinuation;
627 fRes = node.falseBranch.visit(this); 632 fRes = node.falseBranch.visit(this);
628 continuation = new Queue(); 633 continuation = new Queue();
629 } else { 634 } else {
630 continuation = new Queue(); 635 continuation = new Queue();
631 continuation.addFirst(_callNoArg(afterIf, node.span)); 636 continuation.addFirst(_callNoArg(afterIf, node.span));
632 } 637 }
633 638
634 continuation.addFirst(new IfStatement(node.test, tRes, fRes, node.span)); 639 continuation.addFirst(new IfStatement(node.test, tRes, fRes, node.span));
635 return def; 640 return def;
636 } 641 }
637 642
638 visitWhileStatement(WhileStatement node) { 643 visitWhileStatement(WhileStatement node) {
639 if (!haveAwait.contains(node)) return node; 644 if (!haveAwait.contains(node)) return node;
640 645
641 String afterWhile = _newClosureName("while", false); 646 String afterWhile = _newClosureName(_CONTINUATION_PREFIX + "_while");
642 Statement def = _makeContinuation(afterWhile, node.span); 647 Statement def = _makeContinuation(afterWhile, node.span);
643 String repeatWhile = _newClosureName("while", true); 648 String repeatWhile = _newClosureName(_PREFIX + "_while");
644 649
645 final bodyContinuation = new Queue(); 650 final bodyContinuation = new Queue();
646 bodyContinuation.addFirst(_callNoArg(repeatWhile, node.span)); 651 bodyContinuation.addFirst(_callNoArg(repeatWhile, node.span));
647 continuation = bodyContinuation; 652 continuation = bodyContinuation;
648 Statement tRes = node.body.visit(this); 653 Statement body = node.body.visit(this);
649 654
650 continuation = new Queue(); 655 continuation = new Queue();
651 continuation.addFirst(_callNoArg(afterWhile, node.span)); 656 continuation.addFirst(_callNoArg(afterWhile, node.span));
652 continuation.addFirst(new IfStatement(node.test, tRes, null, node.span)); 657 continuation.addFirst(new IfStatement(node.test, body, null, node.span));
653 Statement defLoop = _makeContinuation(repeatWhile, node.span); 658 Statement defLoop = _makeContinuation(repeatWhile, node.span);
654 659
655 continuation = new Queue(); 660 continuation = new Queue();
656 continuation.addFirst(_callNoArg(repeatWhile, node.span)); 661 continuation.addFirst(_callNoArg(repeatWhile, node.span));
657 continuation.addFirst(defLoop); 662 continuation.addFirst(defLoop);
658 return def; 663 return def;
659 } 664 }
660 665
661 visitDoStatement(DoStatement node) { 666 visitDoStatement(DoStatement node) {
662 if (!haveAwait.contains(node)) return node; 667 if (!haveAwait.contains(node)) return node;
(...skipping 12 matching lines...) Expand all
675 visitForInStatement(ForInStatement node) { 680 visitForInStatement(ForInStatement node) {
676 if (!haveAwait.contains(node)) return node; 681 if (!haveAwait.contains(node)) return node;
677 // TODO(sigmund): implement 682 // TODO(sigmund): implement
678 // Note: this is harder than while loops because of dart's special semantics 683 // Note: this is harder than while loops because of dart's special semantics
679 // capturing the loop variable. 684 // capturing the loop variable.
680 return node; 685 return node;
681 } 686 }
682 687
683 visitTryStatement(TryStatement node) { 688 visitTryStatement(TryStatement node) {
684 if (!haveAwait.contains(node)) return node; 689 if (!haveAwait.contains(node)) return node;
685 // TODO(sigmund): implement 690 // TODO(sigmund): pending to do on try-catch blocks
686 return node; 691 // - consider when await shows in catch blocks, but not in the try block
692 // - support exceptions after the await
693 // - handle nested try blocks
694 // - support finally
695 // - consider throws within catch blocks, e.g:
696 // try { a } catch (E e1) { throw new E2(); } catch (E2 e2) { -- }
697
698 String afterTry = _newClosureName(_CONTINUATION_PREFIX + "_try");
699 Statement afterTryDef = _makeContinuation(afterTry, node.span);
700
701 // Transform the body first:
702 continuation = new Queue();
703 final exceptionHandlerName = new Identifier(
704 _newClosureName(_PREFIX + "exception_handler"), node.span);
705 currentExceptionHandler = exceptionHandlerName;
706 continuation.addFirst(_callNoArg(afterTry, node.span));
707 Statement body = node.body.visit(this);
708 currentExceptionHandler = null;
709
710 final defs = []; // closures for each catch block (avoid duplicating code).
711 final catches = []; // catch clauses of the transformed try-catch block
712
713 // Catch blocks are passed as an exception handler on de-sugared awaits:
714 // TODO(sigmund): add trace argument (library change in Future<T>)
715 final handlerArg = new Identifier(_EXCEPTION_HANDLER_PARAM, node.span);
716 final handlerBody = [];
717
718 // The exception handler is smaller when we encounter an untyped catch.
719 bool untypedCatch = false;
720
721 for (CatchNode n in node.catches) {
722 String fname = _newClosureName(_PREFIX + "catch");
723
724 // Code in transformed catch-block:
725 continuation = new Queue();
726 continuation.addFirst(_callNoArg(afterTry, node.span));
727 continuation.addFirst(n.body.visit(this));
728
729 defs.add(_makeCatchFunction(n, fname));
730 catches.add(new CatchNode(n.exception, n.trace,
731 new BlockStatement(
732 [_callCatchFunction(n, fname), _returnFuture(n.span)], n.span),
733 n.span));
734
735 // Code in exceptionHandler:
736 if (!untypedCatch) {
737 final exceptionHandlerCases = [
738 _callCatchFunctionHelper(fname, handlerArg, null, n.span),
739 _returnBoolean(n.span, true)];
740 if (n.exception.type == null) {
741 handlerBody.addAll(exceptionHandlerCases);
742 untypedCatch = true;
743 } else {
744 handlerBody.add(new IfStatement(
745 new IsExpression(true, handlerArg, n.exception.type, n.span),
746 new BlockStatement(exceptionHandlerCases, n.span),
747 null, n.span));
748 }
749 }
750 }
751
752 if (!untypedCatch) {
753 handlerBody.add(_returnBoolean(node.span, false));
754 }
755
756 final handlerDef = new FunctionDefinition([], null,
757 exceptionHandlerName, [new FormalNode(
758 false, false, null, handlerArg, null, node.span)],
759 null, null, new BlockStatement(handlerBody, node.span), node.span);
760
761 continuation = new Queue();
762 continuation.addAll(defs);
763 continuation.add(handlerDef);
764 continuation.add(new TryStatement(body,
765 catches, node.finallyBlock, node.span));
766 continuation.add(_callNoArg(afterTry, node.span));
767 return afterTryDef;
768 }
769
770 /**
771 * Create a closure representing the catch block. The closure takes either 1
772 * or 2 args, depending on whether [n] has a `trace` declaration.
773 */
774 Statement _makeCatchFunction(CatchNode n, String fname) {
775 if (n.trace == null) {
776 return _makeContinuation1(fname,
777 n.exception.type, n.exception.name, n.span);
778 } else {
779 return _makeContinuation2(fname,
780 n.exception.type, n.exception.name,
781 n.trace.type, n.trace.name, n.span);
782 }
783 }
784
785 /** Calls the catch function declared for [n] using [_makeCatchFunction]. */
786 Statement _callCatchFunction(CatchNode n, String fname) {
787 return _callCatchFunctionHelper(fname, n.exception.name,
788 n.trace == null ? null : n.trace.name, n.span);
789 }
790
791 /** Helper to call a catch function declared using [_makeCatchFunction]. */
792 Statement _callCatchFunctionHelper(
793 String fname, Identifier exception, Identifier trace, SourceSpan span) {
794 if (trace == null) {
795 return _call1Arg(fname,
796 new VarExpression(exception, exception.span), span);
797 } else {
798 return _call2Args(fname, new VarExpression(exception, exception.span),
799 new VarExpression(trace, trace.span), span);
800 }
687 } 801 }
688 802
689 visitSwitchStatement(SwitchStatement node) { 803 visitSwitchStatement(SwitchStatement node) {
690 if (!haveAwait.contains(node)) return node; 804 if (!haveAwait.contains(node)) return node;
691 // TODO(sigmund): implement 805 // TODO(sigmund): implement
692 return node; 806 return node;
693 } 807 }
694 808
695 visitBlockStatement(BlockStatement node) { 809 visitBlockStatement(BlockStatement node) {
696 if (!haveAwait.contains(node) && continuation.isEmpty()) return node; 810 if (!haveAwait.contains(node) && continuation.isEmpty()) return node;
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
752 return node; 866 return node;
753 } 867 }
754 868
755 visitCaseNode(CaseNode node) { 869 visitCaseNode(CaseNode node) {
756 if (!haveAwait.contains(node)) return node; 870 if (!haveAwait.contains(node)) return node;
757 return node; 871 return node;
758 } 872 }
759 873
760 /** 874 /**
761 * Converts an await expression into several statements: calling 875 * Converts an await expression into several statements: calling
762 * [:Future.then:] and propatating errors. 876 * [:Future.then:] and propatating errors. This implementation assumes that
877 * await calls are within blocks (after normalization).
763 */ 878 */
764 _desugarAwaitCall(AwaitExpression node, Identifier param) { 879 _desugarAwaitCall(AwaitExpression node, Identifier param) {
765 final thenMethod = new DotExpression(node.body,
766 new Identifier('then', node.span), node.span);
767 List<Statement> afterAwait = []; 880 List<Statement> afterAwait = [];
768 afterAwait.addAll(continuation); 881 afterAwait.addAll(continuation);
882 if (afterAwait[afterAwait.length - 1] is ReturnStatement) {
883 // The only reason there is a `return` is because there was another await
884 // and we introduced it. Such `return` is not needed in the callback.
885 afterAwait.length = afterAwait.length - 1;
886 }
887
888 // A lambda function that executes the continuation.
769 final thenArg = new LambdaExpression( 889 final thenArg = new LambdaExpression(
770 new FunctionDefinition([], null, null, 890 new FunctionDefinition([], null, null,
771 [new FormalNode( 891 [new FormalNode(
772 false, false, null /* infer type from body? */, 892 false, false, null /* infer type from body? */,
773 param, null, param.span) 893 param, null, param.span)
774 ], null, null, 894 ], null, null,
775 new BlockStatement(afterAwait, node.span), node.span), 895 new BlockStatement(afterAwait, node.span), node.span),
776 node.span); 896 node.span);
897
777 continuation.clear(); 898 continuation.clear();
778 // TODO(sigmund): insert in new continuation all additional statements that
779 // propagate errors.
780 // this assumes that the normalization ensures await calls are within blocks
781 continuation.addFirst(_returnFuture(node.span)); 899 continuation.addFirst(_returnFuture(node.span));
782 return new CallExpression(thenMethod, 900 // Within try-blocks, we also add an exception handler to propagate errors.
783 [new ArgumentNode(null, thenArg, node.span)], node.span); 901 if (currentExceptionHandler != null) {
902 continuation.addFirst(new ExpressionStatement(new CallExpression(
903 new DotExpression(node.body,
904 new Identifier('handleException', node.span), node.span),
905 [new ArgumentNode(null,
906 new VarExpression(currentExceptionHandler, node.span),
907 node.span)],
908 node.span), node.span));
909 }
910 return _callThen(node.body, thenArg, node.span);
784 } 911 }
785 912
913 /** Make the statement: [: future.then(arg); :] */
914 _callThen(Expression future, Expression arg, SourceSpan span) {
915 return new CallExpression(
916 new DotExpression(future, new Identifier('then', span), span),
917 [new ArgumentNode(null, arg, span)], span);
918 }
786 919
787 /** Make the statement: [: final Completer<T> v = new Completer<T>(); :]. */ 920 /** Make the statement: [: final Completer<T> v = new Completer<T>(); :]. */
788 _declareCompleter(Type argType, SourceSpan span) { 921 _declareCompleter(Type argType, SourceSpan span) {
789 final name = new Identifier('Completer', span); 922 final name = new Identifier('Completer', span);
790 final ctorName = new Identifier('', span); 923 final ctorName = new Identifier('', span);
791 var typeRef = new NameTypeReference(false, name, [ctorName], span); 924 var typeRef = new NameTypeReference(false, name, [ctorName], span);
792 var type = world.corelib.types['Completer']; 925 var type = world.corelib.types['Completer'];
793 if (argType != null) { 926 if (argType != null) {
794 typeRef = new GenericTypeReference(typeRef, 927 typeRef = new GenericTypeReference(typeRef,
795 new TypeReference(span, argType), 0, span); 928 new TypeReference(span, argType), 0, span);
796 typeRef.type = type.getOrMakeConcreteType([argType]); 929 typeRef.type = type.getOrMakeConcreteType([argType]);
797 } else { 930 } else {
798 typeRef.type = type; 931 typeRef.type = type;
799 } 932 }
800 final def = new VariableDefinition([new Token.fake(TokenKind.FINAL, span)], 933 final def = new VariableDefinition([new Token.fake(TokenKind.FINAL, span)],
801 typeRef, 934 typeRef,
802 [new Identifier(_COMPLETER_NAME, span)], 935 [new Identifier(_COMPLETER_NAME, span)],
803 [new NewExpression(false, typeRef, null, [], span)], 936 [new NewExpression(false, typeRef, null, [], span)],
804 span); 937 span);
805 return def; 938 return def;
806 } 939 }
807 940
941 /**
942 * Wrap [s] in a try-catch block that propagates errors through the future
943 * that is returned from the asynchronous function. If the function that we
944 * are generating is `main`, we add a noop listener on the resulting future.
945 * Without it, we would have to make it illegal to use `await` in main. This
946 * is because futures swallow exceptions if their values are never used.
947 */
948 Statement _wrapInTryCatch(Statement s, bool isMain) {
949 final ex = new Identifier("ex", s.span);
950 Statement catchStatement =
951 _callCompleterException(new VarExpression(ex, ex.span), s.span);
952 if (isMain) {
953 final future = new DotExpression(
954 new VarExpression(new Identifier(_COMPLETER_NAME, s.span), s.span),
955 new Identifier("future", s.span), s.span);
956 final noopHandler = new LambdaExpression(
957 new FunctionDefinition([], null, null,
958 [new FormalNode(false, false, null,
959 new Identifier(_IGNORED_THEN_PARAM, s.span), null, s.span)
960 ], null, null,
961 new BlockStatement([], s.span), s.span), s.span);
962 catchStatement = new BlockStatement([
963 // _a_res.future.then((_ignored_param) { });
964 new ExpressionStatement(
965 _callThen(future, noopHandler, s.span), s.span),
966 catchStatement], s.span);
967 }
968 return new TryStatement(s, [
969 new CatchNode(new DeclaredIdentifier(null, ex, ex.span), null,
970 catchStatement, s.span)], null, s.span);
971 }
972
808 /** Make the statement: [: _a$res.complete(value); :]. */ 973 /** Make the statement: [: _a$res.complete(value); :]. */
809 _callCompleter(Expression value, SourceSpan span) { 974 _callCompleter(Expression value, SourceSpan span) {
810 return _makeCall(_COMPLETER_NAME, _COMPLETE_METHOD, value, span); 975 return _callTarget1Arg(_COMPLETER_NAME, _COMPLETE_METHOD, value, span);
811 } 976 }
812 977
813 /** Make the statement: [: _a$res.completeException(value); :]. */ 978 /** Make the statement: [: _a$res.completeException(value); :]. */
814 _callCompleterException(Expression value, SourceSpan span) { 979 _callCompleterException(Expression value, SourceSpan span) {
815 return _makeCall(_COMPLETER_NAME, _COMPLETE_EXCEPTION_METHOD, value, span); 980 return _callTarget1Arg(
816 } 981 _COMPLETER_NAME, _COMPLETE_EXCEPTION_METHOD, value, span);
817
818 /** Make the statement: [: target.method(value); :]. */
819 _makeCall(String target, String method, Expression value, SourceSpan span) {
820 if (value == null) value = new NullExpression(span);
821 return new ExpressionStatement(new CallExpression(
822 new DotExpression(
823 new VarExpression(new Identifier(target, span), span),
824 new Identifier(method, span), span),
825 [new ArgumentNode(null, value, value.span)], span), span);
826 } 982 }
827 983
828 /** Make the statement: [: return _a$res.future; :]. */ 984 /** Make the statement: [: return _a$res.future; :]. */
829 _returnFuture(SourceSpan span) { 985 _returnFuture(SourceSpan span) {
830 return new ReturnStatement( 986 return new ReturnStatement(
831 new DotExpression( 987 new DotExpression(
832 new VarExpression(new Identifier(_COMPLETER_NAME, span), span), 988 new VarExpression(new Identifier(_COMPLETER_NAME, span), span),
833 new Identifier("future", span), span), span); 989 new Identifier("future", span), span), span);
834 } 990 }
835 991
836 /** Create a unique name for a continuation. */ 992 /** Create a unique name for a continuation. */
837 String _newClosureName(String name, bool isLoop) { 993 String _newClosureName(String name) {
838 String mName = (isLoop ? _LOOP_CONTINUATION_PREFIX : _CONTINUATION_PREFIX)
839 + '${name}_$continuationClosures';
840 continuationClosures++; 994 continuationClosures++;
841 return mName; 995 return '${name}_$continuationClosures';
996 }
997
998 /** Return the current continuation as a statement block for a method body. */
999 Statement _continuationAsBody(SourceSpan span) {
1000 if (continuation.length == 1 && continuation.first() is BlockStatement) {
1001 // No need to wrap a single block statement within a block statement:
1002 return continuation.first();
1003 } else {
1004 List<Statement> continuationBlock = [];
1005 continuationBlock.addAll(continuation);
1006 return new BlockStatement(continuationBlock, span);
1007 }
842 } 1008 }
843 1009
844 /** Create a closure that contains the continuation statements. */ 1010 /** Create a closure that contains the continuation statements. */
845 _makeContinuation(String mName, SourceSpan span) { 1011 FunctionDefinition _makeContinuation(String mName, SourceSpan span) {
846 List<Statement> continuationBlock = [];
847 continuationBlock.addAll(continuation);
848 return new FunctionDefinition([], null, 1012 return new FunctionDefinition([], null,
849 new Identifier(mName, span), [], null, null, 1013 new Identifier(mName, span), [], null, null,
850 new BlockStatement(continuationBlock, span), span); 1014 _continuationAsBody(span), span);
1015 }
1016
1017 /** Create a 1-arg closure that contains the continuation statements. */
1018 FunctionDefinition _makeContinuation1(String mName,
1019 TypeReference arg1Type, Identifier arg1Name, SourceSpan span) {
1020 return new FunctionDefinition([], null,
1021 new Identifier(mName, span),
1022 [new FormalNode(false, false, arg1Type, arg1Name, null, arg1Name.span)],
1023 null, null, _continuationAsBody(span), span);
1024 }
1025
1026 /** Create a 2-arg closure that contains the continuation statements. */
1027 FunctionDefinition _makeContinuation2(
1028 String mName, TypeReference arg1Type, Identifier arg1Name,
1029 TypeReference arg2Type, Identifier arg2Name, SourceSpan span) {
1030 return new FunctionDefinition([], null,
1031 new Identifier(mName, span),
1032 [new FormalNode(false, false, arg1Type, arg1Name, null, arg1Name.span),
1033 new FormalNode(false, false, arg2Type, arg2Name, null, arg2Name.span)],
1034 null, null, _continuationAsBody(span), span);
851 } 1035 }
852 1036
853 /** Make a statement invoking a function in scope. */ 1037 /** Make a statement invoking a function in scope. */
854 _callNoArg(String mName, SourceSpan span) { 1038 Statement _callNoArg(String mName, SourceSpan span) {
855 return new ExpressionStatement(new CallExpression( 1039 return new ExpressionStatement(new CallExpression(
856 new VarExpression(new Identifier(mName, span), span), [], span), span); 1040 new VarExpression(new Identifier(mName, span), span), [], span), span);
857 } 1041 }
1042
1043 /** Make the statement: [: target.method(value); :]. */
1044 Statement _callTarget1Arg(
1045 String target, String method, Expression value, SourceSpan span) {
1046 if (value == null) value = new NullExpression(span);
1047 return new ExpressionStatement(new CallExpression(
1048 new DotExpression(
1049 new VarExpression(new Identifier(target, span), span),
1050 new Identifier(method, span), span),
1051 [new ArgumentNode(null, value, value.span)], span), span);
1052 }
1053
1054 /** Make the statement: [: f(value); :]. */
1055 Statement _call1Arg(String f, Expression value, SourceSpan span) {
1056 if (value == null) value = new NullExpression(span);
1057 return new ExpressionStatement(new CallExpression(
1058 new VarExpression(new Identifier(f, span), span),
1059 [new ArgumentNode(null, value, value.span)], span), span);
1060 }
1061
1062 /** Make the statement: [: f(a, b); :]. */
1063 Statement _call2Args(String f, Expression a, Expression b,
1064 SourceSpan span) {
1065 if (a == null) a = new NullExpression(span);
1066 if (b == null) b = new NullExpression(span);
1067 return new ExpressionStatement(new CallExpression(
1068 new VarExpression(new Identifier(f, span), span),
1069 [new ArgumentNode(null, a, a.span),
1070 new ArgumentNode(null, b, b.span)], span), span);
1071 }
1072
1073 /** Make a return statement for a boolean value. */
1074 Statement _returnBoolean(SourceSpan span, value) {
1075 return new ReturnStatement(new LiteralExpression(value,
1076 new TypeReference(span, world.nonNullBool), "$value", span), span);
1077 }
858 } 1078 }
859 1079
860 // TODO(sigmund): create the following tests: 1080 // TODO(sigmund): create the following tests:
861 // - await within the body of getter or setter properties 1081 // - await within the body of getter or setter properties
862 // - await in each valid AST construct 1082 // - await in each valid AST construct
863 // - exceptions - make some of these fail and propagate errors. 1083 // - exceptions - make some of these fail and propagate errors.
864 // - methods with and without returns (are returns added implicitly) 1084 // - methods with and without returns (are returns added implicitly)
865 // - await within assignmetns, but not declarations (see what happens with 1085 // - await within assignmetns, but not declarations (see what happens with
866 // x = await t; 1086 // x = await t;
867 // x = await y; 1087 // x = await y;
868 // or 1088 // or
869 // x += await t; 1089 // x += await t;
870 // x += await y; 1090 // x += await y;
871 // (does it matter that ExpressionStatement will shadow the variable in the 1091 // (does it matter that ExpressionStatement will shadow the variable in the
872 // callback function to then?) 1092 // callback function to then?)
873 // - floating ifs/for loops, etc - make sure we don't need to normalize the ast 1093 // - floating ifs/for loops, etc - make sure we don't need to normalize the ast
874 // to disallow 'floating' ifs or to insert a block statement when returning from 1094 // to disallow 'floating' ifs or to insert a block statement when returning from
875 // ifs (rather than appending code to the existing continuation list). for 1095 // ifs (rather than appending code to the existing continuation list). for
876 // instance: 1096 // instance:
877 // if (a) if (b) await t; 2; 1097 // if (a) if (b) await t; 2;
878 // becomes: 1098 // becomes:
879 // if (a) { if (b) { await t; } } 2; 1099 // if (a) { if (b) { await t; } } 2;
880 // which becomes becomes: 1100 // which becomes becomes:
881 // _2() { 2; } if (a) { if (b) { t.then((_) { _2(); } } } _2(); 1101 // _2() { 2; } if (a) { if (b) { t.then((_) { _2(); } } } _2();
882 // (seems that 'a' doesn't need { }) 1102 // (seems that 'a' doesn't need { })
883 // - 'if/while' with only one statement in the continuation (check the 1103 // - 'if/while' with only one statement in the continuation (check the
884 // optimization that copies code rather than adding a continuation closure). 1104 // optimization that copies code rather than adding a continuation closure).
885 // - await not inside a block (could break error propagation if normalization is 1105 // - await not inside a block (could break error propagation if normalization is
886 // done incorrectly). 1106 // done incorrectly).
OLDNEW
« no previous file with comments | « corelib/src/implementation/future_implementation.dart ('k') | frog/minfrog » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698