| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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). |
| OLD | NEW |