| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 library dart2js.ir_builder; | 5 library dart2js.ir_builder; |
| 6 | 6 |
| 7 import '../constants/expressions.dart'; | 7 import '../constants/expressions.dart'; |
| 8 import '../constants/values.dart' show PrimitiveConstantValue; | 8 import '../constants/values.dart' show PrimitiveConstantValue; |
| 9 import '../dart_backend/dart_backend.dart' show DartBackend; | 9 import '../dart_backend/dart_backend.dart' show DartBackend; |
| 10 import '../dart_types.dart'; | 10 import '../dart_types.dart'; |
| 11 import '../dart2jslib.dart'; | 11 import '../dart2jslib.dart'; |
| 12 import '../elements/elements.dart'; | 12 import '../elements/elements.dart'; |
| 13 import '../source_file.dart'; | 13 import '../source_file.dart'; |
| 14 import '../tree/tree.dart' as ast; | 14 import '../tree/tree.dart' as ast; |
| 15 import '../scanner/scannerlib.dart' show Token, isUserDefinableOperator; | 15 import '../scanner/scannerlib.dart' show Token, isUserDefinableOperator; |
| 16 import '../universe/universe.dart' show SelectorKind; | 16 import '../universe/universe.dart' show SelectorKind; |
| 17 import 'cps_ir_nodes.dart' as ir; | 17 import 'cps_ir_nodes.dart' as ir; |
| 18 | 18 |
| 19 /** | 19 part 'cps_ir_builder_visitor.dart'; |
| 20 * This task iterates through all resolved elements and builds [ir.Node]s. The | |
| 21 * nodes are stored in the [nodes] map and accessible through [hasIr] and | |
| 22 * [getIr]. | |
| 23 * | |
| 24 * The functionality of the IrNodes is added gradually, therefore elements might | |
| 25 * have an IR or not, depending on the language features that are used. For | |
| 26 * elements that do have an IR, the tree [ast.Node]s and the [Token]s are not | |
| 27 * used in the rest of the compilation. This is ensured by setting the element's | |
| 28 * cached tree to `null` and also breaking the token stream to crash future | |
| 29 * attempts to parse. | |
| 30 * | |
| 31 * The type inferrer works on either IR nodes or tree nodes. The IR nodes are | |
| 32 * then translated into the SSA form for optimizations and code generation. | |
| 33 * Long-term, once the IR supports the full language, the backend can be | |
| 34 * re-implemented to work directly on the IR. | |
| 35 */ | |
| 36 class IrBuilderTask extends CompilerTask { | |
| 37 final Map<Element, ir.FunctionDefinition> nodes = | |
| 38 <Element, ir.FunctionDefinition>{}; | |
| 39 | |
| 40 IrBuilderTask(Compiler compiler) : super(compiler); | |
| 41 | |
| 42 String get name => 'IR builder'; | |
| 43 | |
| 44 bool hasIr(Element element) => nodes.containsKey(element.implementation); | |
| 45 | |
| 46 ir.FunctionDefinition getIr(Element element) => nodes[element.implementation]; | |
| 47 | |
| 48 void buildNodes({bool useNewBackend: false}) { | |
| 49 if (!irEnabled(useNewBackend: useNewBackend)) return; | |
| 50 measure(() { | |
| 51 Set<Element> resolved = compiler.enqueuer.resolution.resolvedElements; | |
| 52 resolved.forEach((AstElement element) { | |
| 53 if (canBuild(element)) { | |
| 54 TreeElements elementsMapping = element.resolvedAst.elements; | |
| 55 element = element.implementation; | |
| 56 compiler.withCurrentElement(element, () { | |
| 57 SourceFile sourceFile = elementSourceFile(element); | |
| 58 IrBuilderVisitor builder = | |
| 59 new IrBuilderVisitor(elementsMapping, compiler, sourceFile); | |
| 60 ir.FunctionDefinition function; | |
| 61 function = builder.buildFunction(element); | |
| 62 | |
| 63 if (function != null) { | |
| 64 nodes[element] = function; | |
| 65 compiler.tracer.traceCompilation(element.name, null); | |
| 66 compiler.tracer.traceGraph("IR Builder", function); | |
| 67 } | |
| 68 }); | |
| 69 } | |
| 70 }); | |
| 71 }); | |
| 72 } | |
| 73 | |
| 74 bool irEnabled({bool useNewBackend: false}) { | |
| 75 // TODO(sigurdm,kmillikin): Support checked-mode checks. | |
| 76 return (useNewBackend || const bool.fromEnvironment('USE_NEW_BACKEND')) && | |
| 77 compiler.backend is DartBackend && | |
| 78 !compiler.enableTypeAssertions && | |
| 79 !compiler.enableConcreteTypeInference; | |
| 80 } | |
| 81 | |
| 82 bool canBuild(Element element) { | |
| 83 FunctionElement function = element.asFunctionElement(); | |
| 84 // TODO(kmillikin,sigurdm): support lazy field initializers. | |
| 85 if (function == null) return false; | |
| 86 | |
| 87 if (!compiler.backend.shouldOutput(function)) return false; | |
| 88 | |
| 89 assert(invariant(element, !function.isNative)); | |
| 90 | |
| 91 // TODO(kmillikin,sigurdm): Support constructors. | |
| 92 if (function is ConstructorElement) return false; | |
| 93 | |
| 94 return true; | |
| 95 } | |
| 96 | |
| 97 bool get inCheckedMode { | |
| 98 bool result = false; | |
| 99 assert((result = true)); | |
| 100 return result; | |
| 101 } | |
| 102 | |
| 103 SourceFile elementSourceFile(Element element) { | |
| 104 if (element is FunctionElement) { | |
| 105 FunctionElement functionElement = element; | |
| 106 if (functionElement.patch != null) element = functionElement.patch; | |
| 107 } | |
| 108 return element.compilationUnit.script.file; | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 class _GetterElements { | |
| 113 ir.Primitive result; | |
| 114 ir.Primitive index; | |
| 115 ir.Primitive receiver; | |
| 116 | |
| 117 _GetterElements({this.result, this.index, this.receiver}) ; | |
| 118 } | |
| 119 | 20 |
| 120 /// A mapping from variable elements to their compile-time values. | 21 /// A mapping from variable elements to their compile-time values. |
| 121 /// | 22 /// |
| 122 /// Map elements denoted by parameters and local variables to the | 23 /// Map elements denoted by parameters and local variables to the |
| 123 /// [ir.Primitive] that is their value. Parameters and locals are | 24 /// [ir.Primitive] that is their value. Parameters and locals are |
| 124 /// assigned indexes which can be used to refer to them. | 25 /// assigned indexes which can be used to refer to them. |
| 125 class Environment { | 26 class Environment { |
| 126 /// A map from elements to their environment index. | 27 /// A map from elements to their environment index. |
| 127 final Map<Element, int> variable2index; | 28 final Map<Element, int> variable2index; |
| 128 | 29 |
| (...skipping 459 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 588 assert(isOpen); | 489 assert(isOpen); |
| 589 for (JumpCollector collector in collectors) { | 490 for (JumpCollector collector in collectors) { |
| 590 if (target == collector.target) { | 491 if (target == collector.target) { |
| 591 collector.addJump(this); | 492 collector.addJump(this); |
| 592 return true; | 493 return true; |
| 593 } | 494 } |
| 594 } | 495 } |
| 595 return false; | 496 return false; |
| 596 } | 497 } |
| 597 } | 498 } |
| 598 | |
| 599 /** | |
| 600 * A tree visitor that builds [IrNodes]. The visit methods add statements using | |
| 601 * to the [builder] and return the last added statement for trees that represent | |
| 602 * an expression. | |
| 603 */ | |
| 604 class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive> | |
| 605 with IrBuilderMixin { | |
| 606 final Compiler compiler; | |
| 607 final SourceFile sourceFile; | |
| 608 | |
| 609 // In SSA terms, join-point continuation parameters are the phis and the | |
| 610 // continuation invocation arguments are the corresponding phi inputs. To | |
| 611 // support name introduction and renaming for source level variables, we use | |
| 612 // nested (delimited) visitors for constructing subparts of the IR that will | |
| 613 // need renaming. Each source variable is assigned an index. | |
| 614 // | |
| 615 // Each nested visitor maintains a list of free variable uses in the body. | |
| 616 // These are implemented as a list of parameters, each with their own use | |
| 617 // list of references. When the delimited subexpression is plugged into the | |
| 618 // surrounding context, the free occurrences can be captured or become free | |
| 619 // occurrences in the next outer delimited subexpression. | |
| 620 // | |
| 621 // Each nested visitor maintains a list that maps indexes of variables | |
| 622 // assigned in the delimited subexpression to their reaching definition --- | |
| 623 // that is, the definition in effect at the hole in 'current'. These are | |
| 624 // used to determine if a join-point continuation needs to be passed | |
| 625 // arguments, and what the arguments are. | |
| 626 | |
| 627 /// Construct a top-level visitor. | |
| 628 IrBuilderVisitor(TreeElements elements, this.compiler, this.sourceFile) | |
| 629 : super(elements); | |
| 630 | |
| 631 /** | |
| 632 * Builds the [ir.FunctionDefinition] for a function element. In case the | |
| 633 * function uses features that cannot be expressed in the IR, this function | |
| 634 * returns `null`. | |
| 635 */ | |
| 636 ir.FunctionDefinition buildFunction(FunctionElement functionElement) { | |
| 637 return nullIfGiveup(() => buildFunctionInternal(functionElement)); | |
| 638 } | |
| 639 | |
| 640 ir.FunctionDefinition buildFunctionInternal(FunctionElement element) { | |
| 641 assert(invariant(element, element.isImplementation)); | |
| 642 ast.FunctionExpression function = element.node; | |
| 643 assert(function != null); | |
| 644 assert(!function.modifiers.isExternal); | |
| 645 assert(elements[function] != null); | |
| 646 | |
| 647 DetectClosureVariables closureLocals = new DetectClosureVariables(elements); | |
| 648 closureLocals.visit(function); | |
| 649 | |
| 650 return withBuilder( | |
| 651 new IrBuilder(compiler.backend.constantSystem, | |
| 652 element, closureLocals.usedFromClosure), | |
| 653 () { | |
| 654 FunctionSignature signature = element.functionSignature; | |
| 655 signature.orderedForEachParameter((ParameterElement parameterElement) { | |
| 656 irBuilder.createParameter( | |
| 657 parameterElement, | |
| 658 isClosureVariable: isClosureVariable(parameterElement)); | |
| 659 }); | |
| 660 | |
| 661 List<ConstantExpression> defaults = new List<ConstantExpression>(); | |
| 662 signature.orderedOptionalParameters.forEach((ParameterElement element) { | |
| 663 defaults.add(getConstantForVariable(element)); | |
| 664 }); | |
| 665 | |
| 666 visit(function.body); | |
| 667 return irBuilder.buildFunctionDefinition(element, defaults); | |
| 668 }); | |
| 669 } | |
| 670 | |
| 671 ir.Primitive visit(ast.Node node) => node.accept(this); | |
| 672 | |
| 673 // ==== Statements ==== | |
| 674 // Build(Block(stamements), C) = C' | |
| 675 // where C' = statements.fold(Build, C) | |
| 676 ir.Primitive visitBlock(ast.Block node) { | |
| 677 assert(irBuilder.isOpen); | |
| 678 for (ast.Node n in node.statements.nodes) { | |
| 679 visit(n); | |
| 680 if (!irBuilder.isOpen) return null; | |
| 681 } | |
| 682 return null; | |
| 683 } | |
| 684 | |
| 685 ir.Primitive visitBreakStatement(ast.BreakStatement node) { | |
| 686 if (!irBuilder.buildBreak(elements.getTargetOf(node))) { | |
| 687 compiler.internalError(node, "'break' target not found"); | |
| 688 } | |
| 689 return null; | |
| 690 } | |
| 691 | |
| 692 ir.Primitive visitContinueStatement(ast.ContinueStatement node) { | |
| 693 if (!irBuilder.buildContinue(elements.getTargetOf(node))) { | |
| 694 compiler.internalError(node, "'continue' target not found"); | |
| 695 } | |
| 696 return null; | |
| 697 } | |
| 698 | |
| 699 // Build(EmptyStatement, C) = C | |
| 700 ir.Primitive visitEmptyStatement(ast.EmptyStatement node) { | |
| 701 assert(irBuilder.isOpen); | |
| 702 return null; | |
| 703 } | |
| 704 | |
| 705 // Build(ExpressionStatement(e), C) = C' | |
| 706 // where (C', _) = Build(e, C) | |
| 707 ir.Primitive visitExpressionStatement(ast.ExpressionStatement node) { | |
| 708 assert(irBuilder.isOpen); | |
| 709 visit(node.expression); | |
| 710 return null; | |
| 711 } | |
| 712 | |
| 713 | |
| 714 /// Create a non-recursive join-point continuation. | |
| 715 /// | |
| 716 /// Given the environment length at the join point and a list of | |
| 717 /// jumps that should reach the join point, create a join-point | |
| 718 /// continuation. The join-point continuation has a parameter for each | |
| 719 /// variable that has different values reaching on different paths. | |
| 720 /// | |
| 721 /// The jumps are uninitialized [ir.InvokeContinuation] expressions. | |
| 722 /// They are filled in with the target continuation and appropriate | |
| 723 /// arguments. | |
| 724 /// | |
| 725 /// As a side effect, the environment of this builder is updated to include | |
| 726 /// the join-point continuation parameters. | |
| 727 ir.Continuation createJoin(int environmentLength, JumpCollector jumps) { | |
| 728 assert(jumps.length >= 2); | |
| 729 | |
| 730 // Compute which values are identical on all paths reaching the join. | |
| 731 // Handle the common case of a pair of contexts efficiently. | |
| 732 Environment first = jumps.environments[0]; | |
| 733 Environment second = jumps.environments[1]; | |
| 734 assert(environmentLength <= first.length); | |
| 735 assert(environmentLength <= second.length); | |
| 736 assert(first.sameDomain(environmentLength, second)); | |
| 737 // A running count of the join-point parameters. | |
| 738 int parameterCount = 0; | |
| 739 // The null elements of common correspond to required parameters of the | |
| 740 // join-point continuation. | |
| 741 List<ir.Primitive> common = | |
| 742 new List<ir.Primitive>.generate(environmentLength, | |
| 743 (i) { | |
| 744 ir.Primitive candidate = first[i]; | |
| 745 if (second[i] == candidate) { | |
| 746 return candidate; | |
| 747 } else { | |
| 748 ++parameterCount; | |
| 749 return null; | |
| 750 } | |
| 751 }); | |
| 752 // If there is already a parameter for each variable, the other | |
| 753 // environments do not need to be considered. | |
| 754 if (parameterCount < environmentLength) { | |
| 755 for (int i = 0; i < environmentLength; ++i) { | |
| 756 ir.Primitive candidate = common[i]; | |
| 757 if (candidate == null) continue; | |
| 758 for (Environment current in jumps.environments.skip(2)) { | |
| 759 assert(environmentLength <= current.length); | |
| 760 assert(first.sameDomain(environmentLength, current)); | |
| 761 if (candidate != current[i]) { | |
| 762 common[i] = null; | |
| 763 ++parameterCount; | |
| 764 break; | |
| 765 } | |
| 766 } | |
| 767 if (parameterCount >= environmentLength) break; | |
| 768 } | |
| 769 } | |
| 770 | |
| 771 // Create the join point continuation. | |
| 772 List<ir.Parameter> parameters = <ir.Parameter>[]; | |
| 773 parameters.length = parameterCount; | |
| 774 int index = 0; | |
| 775 for (int i = 0; i < environmentLength; ++i) { | |
| 776 if (common[i] == null) { | |
| 777 parameters[index++] = new ir.Parameter(first.index2variable[i]); | |
| 778 } | |
| 779 } | |
| 780 assert(index == parameterCount); | |
| 781 ir.Continuation join = new ir.Continuation(parameters); | |
| 782 | |
| 783 // Fill in all the continuation invocations. | |
| 784 for (int i = 0; i < jumps.length; ++i) { | |
| 785 Environment currentEnvironment = jumps.environments[i]; | |
| 786 ir.InvokeContinuation invoke = jumps.invocations[i]; | |
| 787 // Sharing this.environment with one of the invocations will not do | |
| 788 // the right thing (this.environment has already been mutated). | |
| 789 List<ir.Reference> arguments = <ir.Reference>[]; | |
| 790 arguments.length = parameterCount; | |
| 791 int index = 0; | |
| 792 for (int i = 0; i < environmentLength; ++i) { | |
| 793 if (common[i] == null) { | |
| 794 arguments[index++] = new ir.Reference(currentEnvironment[i]); | |
| 795 } | |
| 796 } | |
| 797 invoke.continuation = new ir.Reference(join); | |
| 798 invoke.arguments = arguments; | |
| 799 } | |
| 800 | |
| 801 // Mutate this.environment to be the environment at the join point. Do | |
| 802 // this after adding the continuation invocations, because this.environment | |
| 803 // might be collected by the jump collector and so the old environment | |
| 804 // values are needed for the continuation invocation. | |
| 805 // | |
| 806 // Iterate to environment.length because environmentLength includes values | |
| 807 // outside the environment which are 'phantom' variables used for the | |
| 808 // values of expressions like &&, ||, and ?:. | |
| 809 index = 0; | |
| 810 for (int i = 0; i < irBuilder.environment.length; ++i) { | |
| 811 if (common[i] == null) { | |
| 812 irBuilder.environment.index2value[i] = parameters[index++]; | |
| 813 } | |
| 814 } | |
| 815 | |
| 816 return join; | |
| 817 } | |
| 818 | |
| 819 /// Invoke a join-point continuation that contains arguments for all local | |
| 820 /// variables. | |
| 821 /// | |
| 822 /// Given the continuation and a list of uninitialized invocations, fill | |
| 823 /// in each invocation with the continuation and appropriate arguments. | |
| 824 void invokeFullJoin(ir.Continuation join, | |
| 825 JumpCollector jumps, | |
| 826 {recursive: false}) { | |
| 827 join.isRecursive = recursive; | |
| 828 for (int i = 0; i < jumps.length; ++i) { | |
| 829 Environment currentEnvironment = jumps.environments[i]; | |
| 830 ir.InvokeContinuation invoke = jumps.invocations[i]; | |
| 831 invoke.continuation = new ir.Reference(join); | |
| 832 invoke.arguments = new List<ir.Reference>.generate( | |
| 833 join.parameters.length, | |
| 834 (i) => new ir.Reference(currentEnvironment[i])); | |
| 835 invoke.isRecursive = recursive; | |
| 836 } | |
| 837 } | |
| 838 | |
| 839 ir.Primitive visitFor(ast.For node) { | |
| 840 assert(irBuilder.isOpen); | |
| 841 // TODO(kmillikin,sigurdm): Handle closure variables declared in a for-loop. | |
| 842 if (node.initializer is ast.VariableDefinitions) { | |
| 843 ast.VariableDefinitions definitions = node.initializer; | |
| 844 for (ast.Node definition in definitions.definitions.nodes) { | |
| 845 Element element = elements[definition]; | |
| 846 if (isClosureVariable(element)) { | |
| 847 return giveup(definition, 'Closure variable in for loop initializer'); | |
| 848 } | |
| 849 } | |
| 850 } | |
| 851 | |
| 852 // For loops use four named continuations: the entry to the condition, | |
| 853 // the entry to the body, the loop exit, and the loop successor (break). | |
| 854 // The CPS translation of | |
| 855 // [[for (initializer; condition; update) body; successor]] is: | |
| 856 // | |
| 857 // [[initializer]]; | |
| 858 // let cont loop(x, ...) = | |
| 859 // let prim cond = [[condition]] in | |
| 860 // let cont break() = [[successor]] in | |
| 861 // let cont exit() = break(v, ...) in | |
| 862 // let cont body() = | |
| 863 // let cont continue(x, ...) = [[update]]; loop(v, ...) in | |
| 864 // [[body]]; continue(v, ...) in | |
| 865 // branch cond (body, exit) in | |
| 866 // loop(v, ...) | |
| 867 // | |
| 868 // If there are no breaks in the body, the break continuation is inlined | |
| 869 // in the exit continuation (i.e., the translation of the successor | |
| 870 // statement occurs in the exit continuation). If there is only one | |
| 871 // invocation of the continue continuation (i.e., no continues in the | |
| 872 // body), the continue continuation is inlined in the body. | |
| 873 | |
| 874 if (node.initializer != null) visit(node.initializer); | |
| 875 | |
| 876 IrBuilder condBuilder = new IrBuilder.recursive(irBuilder); | |
| 877 ir.Primitive condition; | |
| 878 if (node.condition == null) { | |
| 879 // If the condition is empty then the body is entered unconditionally. | |
| 880 condition = irBuilder.makePrimConst( | |
| 881 irBuilder.state.constantSystem.createBool(true)); | |
| 882 condBuilder.add(new ir.LetPrim(condition)); | |
| 883 } else { | |
| 884 condition = withBuilder(condBuilder, () => visit(node.condition)); | |
| 885 } | |
| 886 | |
| 887 JumpTarget target = elements.getTargetDefinition(node); | |
| 888 JumpCollector breakCollector = new JumpCollector(target); | |
| 889 JumpCollector continueCollector = new JumpCollector(target); | |
| 890 irBuilder.state.breakCollectors.add(breakCollector); | |
| 891 irBuilder.state.continueCollectors.add(continueCollector); | |
| 892 | |
| 893 IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder); | |
| 894 withBuilder(bodyBuilder, () => visit(node.body)); | |
| 895 assert(irBuilder.state.breakCollectors.last == breakCollector); | |
| 896 assert(irBuilder.state.continueCollectors.last == continueCollector); | |
| 897 irBuilder.state.breakCollectors.removeLast(); | |
| 898 irBuilder.state.continueCollectors.removeLast(); | |
| 899 | |
| 900 // The binding of the continue continuation should occur as late as | |
| 901 // possible, that is, at the nearest common ancestor of all the continue | |
| 902 // sites in the body. However, that is difficult to compute here, so it | |
| 903 // is instead placed just outside the body of the body continuation. | |
| 904 bool hasContinues = !continueCollector.isEmpty; | |
| 905 IrBuilder updateBuilder = hasContinues | |
| 906 ? new IrBuilder.recursive(condBuilder) | |
| 907 : bodyBuilder; | |
| 908 for (ast.Node n in node.update) { | |
| 909 if (!updateBuilder.isOpen) break; | |
| 910 withBuilder(updateBuilder, () => visit(n)); | |
| 911 } | |
| 912 | |
| 913 // Create body entry and loop exit continuations and a branch to them. | |
| 914 ir.Continuation bodyContinuation = new ir.Continuation([]); | |
| 915 ir.Continuation exitContinuation = new ir.Continuation([]); | |
| 916 ir.LetCont branch = | |
| 917 new ir.LetCont(exitContinuation, | |
| 918 new ir.LetCont(bodyContinuation, | |
| 919 new ir.Branch(new ir.IsTrue(condition), | |
| 920 bodyContinuation, | |
| 921 exitContinuation))); | |
| 922 // If there are breaks in the body, then there must be a join-point | |
| 923 // continuation for the normal exit and the breaks. | |
| 924 bool hasBreaks = !breakCollector.isEmpty; | |
| 925 ir.LetCont letJoin; | |
| 926 if (hasBreaks) { | |
| 927 letJoin = new ir.LetCont(null, branch); | |
| 928 condBuilder.add(letJoin); | |
| 929 condBuilder._current = branch; | |
| 930 } else { | |
| 931 condBuilder.add(branch); | |
| 932 } | |
| 933 ir.Continuation continueContinuation; | |
| 934 if (hasContinues) { | |
| 935 // If there are continues in the body, we need a named continue | |
| 936 // continuation as a join point. | |
| 937 continueContinuation = new ir.Continuation(updateBuilder._parameters); | |
| 938 if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder); | |
| 939 invokeFullJoin(continueContinuation, continueCollector); | |
| 940 } | |
| 941 ir.Continuation loopContinuation = | |
| 942 new ir.Continuation(condBuilder._parameters); | |
| 943 if (updateBuilder.isOpen) { | |
| 944 JumpCollector backEdges = new JumpCollector(null); | |
| 945 backEdges.addJump(updateBuilder); | |
| 946 invokeFullJoin(loopContinuation, backEdges, recursive: true); | |
| 947 } | |
| 948 | |
| 949 // Fill in the body and possible continue continuation bodies. Do this | |
| 950 // only after it is guaranteed that they are not empty. | |
| 951 if (hasContinues) { | |
| 952 continueContinuation.body = updateBuilder._root; | |
| 953 bodyContinuation.body = | |
| 954 new ir.LetCont(continueContinuation, bodyBuilder._root); | |
| 955 } else { | |
| 956 bodyContinuation.body = bodyBuilder._root; | |
| 957 } | |
| 958 | |
| 959 loopContinuation.body = condBuilder._root; | |
| 960 irBuilder.add(new ir.LetCont(loopContinuation, | |
| 961 new ir.InvokeContinuation(loopContinuation, | |
| 962 irBuilder.environment.index2value))); | |
| 963 if (hasBreaks) { | |
| 964 irBuilder._current = branch; | |
| 965 irBuilder.environment = condBuilder.environment; | |
| 966 breakCollector.addJump(irBuilder); | |
| 967 letJoin.continuation = | |
| 968 createJoin(irBuilder.environment.length, breakCollector); | |
| 969 irBuilder._current = letJoin; | |
| 970 } else { | |
| 971 irBuilder._current = condBuilder._current; | |
| 972 irBuilder.environment = condBuilder.environment; | |
| 973 } | |
| 974 return null; | |
| 975 } | |
| 976 | |
| 977 ir.Primitive visitIf(ast.If node) { | |
| 978 assert(irBuilder.isOpen); | |
| 979 ir.Primitive condition = visit(node.condition); | |
| 980 | |
| 981 // The then and else parts are delimited. | |
| 982 IrBuilder thenBuilder = new IrBuilder.delimited(irBuilder); | |
| 983 IrBuilder elseBuilder = new IrBuilder.delimited(irBuilder); | |
| 984 withBuilder(thenBuilder, () => visit(node.thenPart)); | |
| 985 if (node.hasElsePart) { | |
| 986 withBuilder(elseBuilder, () => visit(node.elsePart)); | |
| 987 } | |
| 988 | |
| 989 // Build the term | |
| 990 // (Result =) let cont then() = [[thenPart]] in | |
| 991 // let cont else() = [[elsePart]] in | |
| 992 // if condition (then, else) | |
| 993 ir.Continuation thenContinuation = new ir.Continuation([]); | |
| 994 ir.Continuation elseContinuation = new ir.Continuation([]); | |
| 995 ir.Expression letElse = | |
| 996 new ir.LetCont(elseContinuation, | |
| 997 new ir.Branch(new ir.IsTrue(condition), | |
| 998 thenContinuation, | |
| 999 elseContinuation)); | |
| 1000 ir.Expression letThen = new ir.LetCont(thenContinuation, letElse); | |
| 1001 ir.Expression result = letThen; | |
| 1002 | |
| 1003 ir.Continuation joinContinuation; // Null if there is no join. | |
| 1004 if (thenBuilder.isOpen && elseBuilder.isOpen) { | |
| 1005 // There is a join-point continuation. Build the term | |
| 1006 // 'let cont join(x, ...) = [] in Result' and plug invocations of the | |
| 1007 // join-point continuation into the then and else continuations. | |
| 1008 JumpCollector jumps = new JumpCollector(null); | |
| 1009 jumps.addJump(thenBuilder); | |
| 1010 jumps.addJump(elseBuilder); | |
| 1011 joinContinuation = createJoin(irBuilder.environment.length, jumps); | |
| 1012 result = new ir.LetCont(joinContinuation, result); | |
| 1013 } | |
| 1014 | |
| 1015 // The then or else term root could be null, but not both. If there is | |
| 1016 // a join then an InvokeContinuation was just added to both of them. If | |
| 1017 // there is no join, then at least one of them is closed and thus has a | |
| 1018 // non-null root by the definition of the predicate isClosed. In the | |
| 1019 // case that one of them is null, it must be the only one that is open | |
| 1020 // and thus contains the new hole in the context. This case is handled | |
| 1021 // after the branch is plugged into the current hole. | |
| 1022 thenContinuation.body = thenBuilder._root; | |
| 1023 elseContinuation.body = elseBuilder._root; | |
| 1024 | |
| 1025 irBuilder.add(result); | |
| 1026 if (joinContinuation == null) { | |
| 1027 // At least one subexpression is closed. | |
| 1028 if (thenBuilder.isOpen) { | |
| 1029 irBuilder._current = | |
| 1030 (thenBuilder._root == null) ? letThen : thenBuilder._current; | |
| 1031 irBuilder.environment = thenBuilder.environment; | |
| 1032 } else if (elseBuilder.isOpen) { | |
| 1033 irBuilder._current = | |
| 1034 (elseBuilder._root == null) ? letElse : elseBuilder._current; | |
| 1035 irBuilder.environment = elseBuilder.environment; | |
| 1036 } else { | |
| 1037 irBuilder._current = null; | |
| 1038 } | |
| 1039 } | |
| 1040 return null; | |
| 1041 } | |
| 1042 | |
| 1043 ir.Primitive visitLabeledStatement(ast.LabeledStatement node) { | |
| 1044 ast.Statement body = node.statement; | |
| 1045 return body is ast.Loop | |
| 1046 ? visit(body) | |
| 1047 : giveup(node, 'labeled statement'); | |
| 1048 } | |
| 1049 | |
| 1050 ir.Primitive visitWhile(ast.While node) { | |
| 1051 assert(irBuilder.isOpen); | |
| 1052 // While loops use four named continuations: the entry to the body, the | |
| 1053 // loop exit, the loop back edge (continue), and the loop exit (break). | |
| 1054 // The CPS translation of [[while (condition) body; successor]] is: | |
| 1055 // | |
| 1056 // let cont continue(x, ...) = | |
| 1057 // let prim cond = [[condition]] in | |
| 1058 // let cont break() = [[successor]] in | |
| 1059 // let cont exit() = break(v, ...) in | |
| 1060 // let cont body() = [[body]]; continue(v, ...) in | |
| 1061 // branch cond (body, exit) in | |
| 1062 // continue(v, ...) | |
| 1063 // | |
| 1064 // If there are no breaks in the body, the break continuation is inlined | |
| 1065 // in the exit continuation (i.e., the translation of the successor | |
| 1066 // statement occurs in the exit continuation). | |
| 1067 | |
| 1068 // The condition and body are delimited. | |
| 1069 IrBuilder condBuilder = new IrBuilder.recursive(irBuilder); | |
| 1070 ir.Primitive condition = | |
| 1071 withBuilder(condBuilder, () => visit(node.condition)); | |
| 1072 | |
| 1073 JumpTarget target = elements.getTargetDefinition(node); | |
| 1074 JumpCollector breakCollector = new JumpCollector(target); | |
| 1075 JumpCollector continueCollector = new JumpCollector(target); | |
| 1076 irBuilder.state.breakCollectors.add(breakCollector); | |
| 1077 irBuilder.state.continueCollectors.add(continueCollector); | |
| 1078 | |
| 1079 IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder); | |
| 1080 withBuilder(bodyBuilder, () => visit(node.body)); | |
| 1081 assert(irBuilder.state.breakCollectors.last == breakCollector); | |
| 1082 assert(irBuilder.state.continueCollectors.last == continueCollector); | |
| 1083 irBuilder.state.breakCollectors.removeLast(); | |
| 1084 irBuilder.state.continueCollectors.removeLast(); | |
| 1085 | |
| 1086 // Create body entry and loop exit continuations and a branch to them. | |
| 1087 ir.Continuation bodyContinuation = new ir.Continuation([]); | |
| 1088 ir.Continuation exitContinuation = new ir.Continuation([]); | |
| 1089 ir.LetCont branch = | |
| 1090 new ir.LetCont(exitContinuation, | |
| 1091 new ir.LetCont(bodyContinuation, | |
| 1092 new ir.Branch(new ir.IsTrue(condition), | |
| 1093 bodyContinuation, | |
| 1094 exitContinuation))); | |
| 1095 // If there are breaks in the body, then there must be a join-point | |
| 1096 // continuation for the normal exit and the breaks. | |
| 1097 bool hasBreaks = !breakCollector.isEmpty; | |
| 1098 ir.LetCont letJoin; | |
| 1099 if (hasBreaks) { | |
| 1100 letJoin = new ir.LetCont(null, branch); | |
| 1101 condBuilder.add(letJoin); | |
| 1102 condBuilder._current = branch; | |
| 1103 } else { | |
| 1104 condBuilder.add(branch); | |
| 1105 } | |
| 1106 ir.Continuation loopContinuation = | |
| 1107 new ir.Continuation(condBuilder._parameters); | |
| 1108 if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder); | |
| 1109 invokeFullJoin(loopContinuation, continueCollector, recursive: true); | |
| 1110 bodyContinuation.body = bodyBuilder._root; | |
| 1111 | |
| 1112 loopContinuation.body = condBuilder._root; | |
| 1113 irBuilder.add(new ir.LetCont(loopContinuation, | |
| 1114 new ir.InvokeContinuation(loopContinuation, | |
| 1115 irBuilder.environment.index2value))); | |
| 1116 if (hasBreaks) { | |
| 1117 irBuilder._current = branch; | |
| 1118 irBuilder.environment = condBuilder.environment; | |
| 1119 breakCollector.addJump(irBuilder); | |
| 1120 letJoin.continuation = | |
| 1121 createJoin(irBuilder.environment.length, breakCollector); | |
| 1122 irBuilder._current = letJoin; | |
| 1123 } else { | |
| 1124 irBuilder._current = condBuilder._current; | |
| 1125 irBuilder.environment = condBuilder.environment; | |
| 1126 } | |
| 1127 return null; | |
| 1128 } | |
| 1129 | |
| 1130 ir.Primitive visitForIn(ast.ForIn node) { | |
| 1131 // The for-in loop | |
| 1132 // | |
| 1133 // for (a in e) s; | |
| 1134 // | |
| 1135 // Is compiled analogously to: | |
| 1136 // | |
| 1137 // a = e.iterator; | |
| 1138 // while (a.moveNext()) { | |
| 1139 // var n0 = a.current; | |
| 1140 // s; | |
| 1141 // } | |
| 1142 | |
| 1143 // The condition and body are delimited. | |
| 1144 IrBuilder condBuilder = new IrBuilder.recursive(irBuilder); | |
| 1145 | |
| 1146 ir.Primitive expressionReceiver = visit(node.expression); | |
| 1147 List<ir.Primitive> emptyArguments = new List<ir.Primitive>(); | |
| 1148 | |
| 1149 ir.Parameter iterator = new ir.Parameter(null); | |
| 1150 ir.Continuation iteratorInvoked = new ir.Continuation([iterator]); | |
| 1151 irBuilder.add(new ir.LetCont(iteratorInvoked, | |
| 1152 new ir.InvokeMethod(expressionReceiver, | |
| 1153 new Selector.getter("iterator", null), iteratorInvoked, | |
| 1154 emptyArguments))); | |
| 1155 | |
| 1156 ir.Parameter condition = new ir.Parameter(null); | |
| 1157 ir.Continuation moveNextInvoked = new ir.Continuation([condition]); | |
| 1158 condBuilder.add(new ir.LetCont(moveNextInvoked, | |
| 1159 new ir.InvokeMethod(iterator, | |
| 1160 new Selector.call("moveNext", null, 0), | |
| 1161 moveNextInvoked, emptyArguments))); | |
| 1162 | |
| 1163 JumpTarget target = elements.getTargetDefinition(node); | |
| 1164 JumpCollector breakCollector = new JumpCollector(target); | |
| 1165 JumpCollector continueCollector = new JumpCollector(target); | |
| 1166 irBuilder.state.breakCollectors.add(breakCollector); | |
| 1167 irBuilder.state.continueCollectors.add(continueCollector); | |
| 1168 | |
| 1169 IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder); | |
| 1170 ast.Node identifier = node.declaredIdentifier; | |
| 1171 Element variableElement = elements.getForInVariable(node); | |
| 1172 Selector selector = elements.getSelector(identifier); | |
| 1173 | |
| 1174 // node.declaredIdentifier can be either an ast.VariableDefinitions | |
| 1175 // (defining a new local variable) or a send designating some existing | |
| 1176 // variable. | |
| 1177 ast.Node declaredIdentifier = node.declaredIdentifier; | |
| 1178 | |
| 1179 if (declaredIdentifier is ast.VariableDefinitions) { | |
| 1180 withBuilder(bodyBuilder, () => visit(declaredIdentifier)); | |
| 1181 } | |
| 1182 | |
| 1183 ir.Parameter currentValue = new ir.Parameter(null); | |
| 1184 ir.Continuation currentInvoked = new ir.Continuation([currentValue]); | |
| 1185 bodyBuilder.add(new ir.LetCont(currentInvoked, | |
| 1186 new ir.InvokeMethod(iterator, new Selector.getter("current", null), | |
| 1187 currentInvoked, emptyArguments))); | |
| 1188 if (Elements.isLocal(variableElement)) { | |
| 1189 withBuilder(bodyBuilder, () => setLocal(variableElement, currentValue)); | |
| 1190 } else if (Elements.isStaticOrTopLevel(variableElement)) { | |
| 1191 withBuilder(bodyBuilder, | |
| 1192 () => setStatic(variableElement, selector, currentValue)); | |
| 1193 } else { | |
| 1194 ir.Primitive receiver = | |
| 1195 withBuilder(bodyBuilder, () => lookupThis()); | |
| 1196 withBuilder(bodyBuilder, | |
| 1197 () => setDynamic(null, receiver, selector, currentValue)); | |
| 1198 } | |
| 1199 | |
| 1200 withBuilder(bodyBuilder, () => visit(node.body)); | |
| 1201 assert(irBuilder.state.breakCollectors.last == breakCollector); | |
| 1202 assert(irBuilder.state.continueCollectors.last == continueCollector); | |
| 1203 irBuilder.state.breakCollectors.removeLast(); | |
| 1204 irBuilder.state.continueCollectors.removeLast(); | |
| 1205 | |
| 1206 // Create body entry and loop exit continuations and a branch to them. | |
| 1207 ir.Continuation bodyContinuation = new ir.Continuation([]); | |
| 1208 ir.Continuation exitContinuation = new ir.Continuation([]); | |
| 1209 ir.LetCont branch = | |
| 1210 new ir.LetCont(exitContinuation, | |
| 1211 new ir.LetCont(bodyContinuation, | |
| 1212 new ir.Branch(new ir.IsTrue(condition), | |
| 1213 bodyContinuation, | |
| 1214 exitContinuation))); | |
| 1215 // If there are breaks in the body, then there must be a join-point | |
| 1216 // continuation for the normal exit and the breaks. | |
| 1217 bool hasBreaks = !breakCollector.isEmpty; | |
| 1218 ir.LetCont letJoin; | |
| 1219 if (hasBreaks) { | |
| 1220 letJoin = new ir.LetCont(null, branch); | |
| 1221 condBuilder.add(letJoin); | |
| 1222 condBuilder._current = branch; | |
| 1223 } else { | |
| 1224 condBuilder.add(branch); | |
| 1225 } | |
| 1226 ir.Continuation loopContinuation = | |
| 1227 new ir.Continuation(condBuilder._parameters); | |
| 1228 if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder); | |
| 1229 invokeFullJoin(loopContinuation, continueCollector, recursive: true); | |
| 1230 bodyContinuation.body = bodyBuilder._root; | |
| 1231 | |
| 1232 loopContinuation.body = condBuilder._root; | |
| 1233 irBuilder.add(new ir.LetCont(loopContinuation, | |
| 1234 new ir.InvokeContinuation(loopContinuation, | |
| 1235 irBuilder.environment.index2value))); | |
| 1236 if (hasBreaks) { | |
| 1237 irBuilder._current = branch; | |
| 1238 irBuilder.environment = condBuilder.environment; | |
| 1239 breakCollector.addJump(irBuilder); | |
| 1240 letJoin.continuation = | |
| 1241 createJoin(irBuilder.environment.length, breakCollector); | |
| 1242 irBuilder._current = letJoin; | |
| 1243 } else { | |
| 1244 irBuilder._current = condBuilder._current; | |
| 1245 irBuilder.environment = condBuilder.environment; | |
| 1246 } | |
| 1247 return null; | |
| 1248 } | |
| 1249 | |
| 1250 ir.Primitive visitVariableDefinitions(ast.VariableDefinitions node) { | |
| 1251 assert(irBuilder.isOpen); | |
| 1252 if (node.modifiers.isConst) { | |
| 1253 for (ast.SendSet definition in node.definitions.nodes) { | |
| 1254 assert(!definition.arguments.isEmpty); | |
| 1255 assert(definition.arguments.tail.isEmpty); | |
| 1256 VariableElement element = elements[definition]; | |
| 1257 ConstantExpression value = getConstantForVariable(element); | |
| 1258 irBuilder.declareLocalConstant(element, value); | |
| 1259 } | |
| 1260 } else { | |
| 1261 for (ast.Node definition in node.definitions.nodes) { | |
| 1262 Element element = elements[definition]; | |
| 1263 ir.Primitive initialValue; | |
| 1264 // Definitions are either SendSets if there is an initializer, or | |
| 1265 // Identifiers if there is no initializer. | |
| 1266 if (definition is ast.SendSet) { | |
| 1267 assert(!definition.arguments.isEmpty); | |
| 1268 assert(definition.arguments.tail.isEmpty); | |
| 1269 initialValue = visit(definition.arguments.head); | |
| 1270 } else { | |
| 1271 assert(definition is ast.Identifier); | |
| 1272 } | |
| 1273 irBuilder.declareLocalVariable(element, | |
| 1274 initialValue: initialValue, | |
| 1275 isClosureVariable: isClosureVariable(element)); | |
| 1276 } | |
| 1277 } | |
| 1278 return null; | |
| 1279 } | |
| 1280 | |
| 1281 // Build(Return(e), C) = C'[InvokeContinuation(return, x)] | |
| 1282 // where (C', x) = Build(e, C) | |
| 1283 // | |
| 1284 // Return without a subexpression is translated as if it were return null. | |
| 1285 ir.Primitive visitReturn(ast.Return node) { | |
| 1286 assert(irBuilder.isOpen); | |
| 1287 assert(invariant(node, node.beginToken.value != 'native')); | |
| 1288 if (node.expression == null) { | |
| 1289 irBuilder.buildReturn(); | |
| 1290 } else { | |
| 1291 irBuilder.buildReturn(visit(node.expression)); | |
| 1292 } | |
| 1293 return null; | |
| 1294 } | |
| 1295 | |
| 1296 // ==== Expressions ==== | |
| 1297 ir.Primitive visitConditional(ast.Conditional node) { | |
| 1298 assert(irBuilder.isOpen); | |
| 1299 ir.Primitive condition = visit(node.condition); | |
| 1300 | |
| 1301 // The then and else expressions are delimited. | |
| 1302 IrBuilder thenBuilder = new IrBuilder.delimited(irBuilder); | |
| 1303 IrBuilder elseBuilder = new IrBuilder.delimited(irBuilder); | |
| 1304 ir.Primitive thenValue = | |
| 1305 withBuilder(thenBuilder, () => visit(node.thenExpression)); | |
| 1306 ir.Primitive elseValue = | |
| 1307 withBuilder(elseBuilder, () => visit(node.elseExpression)); | |
| 1308 | |
| 1309 // Treat the values of the subexpressions as named values in the | |
| 1310 // environment, so they will be treated as arguments to the join-point | |
| 1311 // continuation. | |
| 1312 assert(irBuilder.environment.length == thenBuilder.environment.length); | |
| 1313 assert(irBuilder.environment.length == elseBuilder.environment.length); | |
| 1314 thenBuilder.environment.extend(null, thenValue); | |
| 1315 elseBuilder.environment.extend(null, elseValue); | |
| 1316 JumpCollector jumps = new JumpCollector(null); | |
| 1317 jumps.addJump(thenBuilder); | |
| 1318 jumps.addJump(elseBuilder); | |
| 1319 ir.Continuation joinContinuation = | |
| 1320 createJoin(irBuilder.environment.length + 1, jumps); | |
| 1321 | |
| 1322 // Build the term | |
| 1323 // let cont join(x, ..., result) = [] in | |
| 1324 // let cont then() = [[thenPart]]; join(v, ...) in | |
| 1325 // let cont else() = [[elsePart]]; join(v, ...) in | |
| 1326 // if condition (then, else) | |
| 1327 ir.Continuation thenContinuation = new ir.Continuation([]); | |
| 1328 ir.Continuation elseContinuation = new ir.Continuation([]); | |
| 1329 thenContinuation.body = thenBuilder._root; | |
| 1330 elseContinuation.body = elseBuilder._root; | |
| 1331 irBuilder.add(new ir.LetCont(joinContinuation, | |
| 1332 new ir.LetCont(thenContinuation, | |
| 1333 new ir.LetCont(elseContinuation, | |
| 1334 new ir.Branch(new ir.IsTrue(condition), | |
| 1335 thenContinuation, | |
| 1336 elseContinuation))))); | |
| 1337 return (thenValue == elseValue) | |
| 1338 ? thenValue | |
| 1339 : joinContinuation.parameters.last; | |
| 1340 } | |
| 1341 | |
| 1342 // For all simple literals: | |
| 1343 // Build(Literal(c), C) = C[let val x = Constant(c) in [], x] | |
| 1344 ir.Primitive visitLiteralBool(ast.LiteralBool node) { | |
| 1345 assert(irBuilder.isOpen); | |
| 1346 return translateConstant(node); | |
| 1347 } | |
| 1348 | |
| 1349 ir.Primitive visitLiteralDouble(ast.LiteralDouble node) { | |
| 1350 assert(irBuilder.isOpen); | |
| 1351 return translateConstant(node); | |
| 1352 } | |
| 1353 | |
| 1354 ir.Primitive visitLiteralInt(ast.LiteralInt node) { | |
| 1355 assert(irBuilder.isOpen); | |
| 1356 return translateConstant(node); | |
| 1357 } | |
| 1358 | |
| 1359 ir.Primitive visitLiteralNull(ast.LiteralNull node) { | |
| 1360 assert(irBuilder.isOpen); | |
| 1361 return translateConstant(node); | |
| 1362 } | |
| 1363 | |
| 1364 ir.Primitive visitLiteralString(ast.LiteralString node) { | |
| 1365 assert(irBuilder.isOpen); | |
| 1366 return translateConstant(node); | |
| 1367 } | |
| 1368 | |
| 1369 ConstantExpression getConstantForNode(ast.Node node) { | |
| 1370 ConstantExpression constant = | |
| 1371 compiler.backend.constantCompilerTask.compileNode(node, elements); | |
| 1372 assert(invariant(node, constant != null, | |
| 1373 message: 'No constant computed for $node')); | |
| 1374 return constant; | |
| 1375 } | |
| 1376 | |
| 1377 ConstantExpression getConstantForVariable(VariableElement element) { | |
| 1378 ConstantExpression constant = | |
| 1379 compiler.backend.constants.getConstantForVariable(element); | |
| 1380 assert(invariant(element, constant != null, | |
| 1381 message: 'No constant computed for $element')); | |
| 1382 return constant; | |
| 1383 } | |
| 1384 | |
| 1385 ir.Primitive visitLiteralList(ast.LiteralList node) { | |
| 1386 assert(irBuilder.isOpen); | |
| 1387 if (node.isConst) { | |
| 1388 return translateConstant(node); | |
| 1389 } | |
| 1390 List<ir.Primitive> values = node.elements.nodes.mapToList(visit); | |
| 1391 GenericType type = elements.getType(node); | |
| 1392 ir.Primitive result = new ir.LiteralList(type, values); | |
| 1393 irBuilder.add(new ir.LetPrim(result)); | |
| 1394 return result; | |
| 1395 } | |
| 1396 | |
| 1397 ir.Primitive visitLiteralMap(ast.LiteralMap node) { | |
| 1398 assert(irBuilder.isOpen); | |
| 1399 if (node.isConst) { | |
| 1400 return translateConstant(node); | |
| 1401 } | |
| 1402 List<ir.Primitive> keys = new List<ir.Primitive>(); | |
| 1403 List<ir.Primitive> values = new List<ir.Primitive>(); | |
| 1404 node.entries.nodes.forEach((ast.LiteralMapEntry node) { | |
| 1405 keys.add(visit(node.key)); | |
| 1406 values.add(visit(node.value)); | |
| 1407 }); | |
| 1408 GenericType type = elements.getType(node); | |
| 1409 ir.Primitive result = new ir.LiteralMap(type, keys, values); | |
| 1410 irBuilder.add(new ir.LetPrim(result)); | |
| 1411 return result; | |
| 1412 } | |
| 1413 | |
| 1414 ir.Primitive visitLiteralSymbol(ast.LiteralSymbol node) { | |
| 1415 assert(irBuilder.isOpen); | |
| 1416 return translateConstant(node); | |
| 1417 } | |
| 1418 | |
| 1419 ir.Primitive visitIdentifier(ast.Identifier node) { | |
| 1420 assert(irBuilder.isOpen); | |
| 1421 // "this" is the only identifier that should be met by the visitor. | |
| 1422 assert(node.isThis()); | |
| 1423 return lookupThis(); | |
| 1424 } | |
| 1425 | |
| 1426 ir.Primitive visitParenthesizedExpression( | |
| 1427 ast.ParenthesizedExpression node) { | |
| 1428 assert(irBuilder.isOpen); | |
| 1429 return visit(node.expression); | |
| 1430 } | |
| 1431 | |
| 1432 // Stores the result of visiting a CascadeReceiver, so we can return it from | |
| 1433 // its enclosing Cascade. | |
| 1434 ir.Primitive _currentCascadeReceiver; | |
| 1435 | |
| 1436 ir.Primitive visitCascadeReceiver(ast.CascadeReceiver node) { | |
| 1437 assert(irBuilder.isOpen); | |
| 1438 return _currentCascadeReceiver = visit(node.expression); | |
| 1439 } | |
| 1440 | |
| 1441 ir.Primitive visitCascade(ast.Cascade node) { | |
| 1442 assert(irBuilder.isOpen); | |
| 1443 var oldCascadeReceiver = _currentCascadeReceiver; | |
| 1444 // Throw away the result of visiting the expression. | |
| 1445 // Instead we return the result of visiting the CascadeReceiver. | |
| 1446 this.visit(node.expression); | |
| 1447 ir.Primitive receiver = _currentCascadeReceiver; | |
| 1448 _currentCascadeReceiver = oldCascadeReceiver; | |
| 1449 return receiver; | |
| 1450 } | |
| 1451 | |
| 1452 ir.Primitive lookupThis() { | |
| 1453 ir.Primitive result = new ir.This(); | |
| 1454 irBuilder.add(new ir.LetPrim(result)); | |
| 1455 return result; | |
| 1456 } | |
| 1457 | |
| 1458 // ==== Sends ==== | |
| 1459 ir.Primitive visitAssert(ast.Send node) { | |
| 1460 assert(irBuilder.isOpen); | |
| 1461 return giveup(node, 'Assert'); | |
| 1462 } | |
| 1463 | |
| 1464 ir.Primitive visitNamedArgument(ast.NamedArgument node) { | |
| 1465 assert(irBuilder.isOpen); | |
| 1466 return visit(node.expression); | |
| 1467 } | |
| 1468 | |
| 1469 ir.Primitive translateClosureCall(ir.Primitive receiver, | |
| 1470 Selector closureSelector, | |
| 1471 ast.NodeList arguments) { | |
| 1472 Selector namedCallSelector = new Selector(closureSelector.kind, | |
| 1473 "call", | |
| 1474 closureSelector.library, | |
| 1475 closureSelector.argumentCount, | |
| 1476 closureSelector.namedArguments); | |
| 1477 List<ir.Primitive> args = arguments.nodes.mapToList(visit, growable:false); | |
| 1478 return irBuilder.continueWithExpression( | |
| 1479 (k) => new ir.InvokeMethod(receiver, namedCallSelector, k, args)); | |
| 1480 } | |
| 1481 | |
| 1482 ir.Primitive visitClosureSend(ast.Send node) { | |
| 1483 assert(irBuilder.isOpen); | |
| 1484 Element element = elements[node]; | |
| 1485 ir.Primitive closureTarget; | |
| 1486 if (element == null) { | |
| 1487 closureTarget = visit(node.selector); | |
| 1488 } else if (isClosureVariable(element)) { | |
| 1489 LocalElement local = element; | |
| 1490 closureTarget = new ir.GetClosureVariable(local); | |
| 1491 irBuilder.add(new ir.LetPrim(closureTarget)); | |
| 1492 } else { | |
| 1493 assert(Elements.isLocal(element)); | |
| 1494 closureTarget = irBuilder.environment.lookup(element); | |
| 1495 } | |
| 1496 Selector closureSelector = elements.getSelector(node); | |
| 1497 return translateClosureCall(closureTarget, closureSelector, | |
| 1498 node.argumentsNode); | |
| 1499 } | |
| 1500 | |
| 1501 /// If [node] is null, returns this. | |
| 1502 /// If [node] is super, returns null (for special handling) | |
| 1503 /// Otherwise visits [node] and returns the result. | |
| 1504 ir.Primitive visitReceiver(ast.Expression node) { | |
| 1505 if (node == null) return lookupThis(); | |
| 1506 if (node.isSuper()) return null; | |
| 1507 return visit(node); | |
| 1508 } | |
| 1509 | |
| 1510 /// Makes an [InvokeMethod] unless [node.receiver.isSuper()], in that case | |
| 1511 /// makes an [InvokeSuperMethod] ignoring [receiver]. | |
| 1512 ir.Expression createDynamicInvoke(ast.Send node, | |
| 1513 Selector selector, | |
| 1514 ir.Definition receiver, | |
| 1515 ir.Continuation k, | |
| 1516 List<ir.Definition> arguments) { | |
| 1517 return node != null && node.receiver != null && node.receiver.isSuper() | |
| 1518 ? new ir.InvokeSuperMethod(selector, k, arguments) | |
| 1519 : new ir.InvokeMethod(receiver, selector, k, arguments); | |
| 1520 } | |
| 1521 | |
| 1522 ir.Primitive visitDynamicSend(ast.Send node) { | |
| 1523 assert(irBuilder.isOpen); | |
| 1524 Selector selector = elements.getSelector(node); | |
| 1525 ir.Primitive receiver = visitReceiver(node.receiver); | |
| 1526 List<ir.Primitive> arguments = new List<ir.Primitive>(); | |
| 1527 for (ast.Node n in node.arguments) { | |
| 1528 arguments.add(visit(n)); | |
| 1529 } | |
| 1530 return irBuilder.buildDynamicInvocation(receiver, selector, arguments); | |
| 1531 } | |
| 1532 | |
| 1533 _GetterElements translateGetter(ast.Send node, Selector selector) { | |
| 1534 Element element = elements[node]; | |
| 1535 ir.Primitive result; | |
| 1536 ir.Primitive receiver; | |
| 1537 ir.Primitive index; | |
| 1538 | |
| 1539 if (element != null && element.isConst) { | |
| 1540 // Reference to constant local, top-level or static field | |
| 1541 result = translateConstant(node); | |
| 1542 } else if (isClosureVariable(element)) { | |
| 1543 LocalElement local = element; | |
| 1544 result = new ir.GetClosureVariable(local); | |
| 1545 irBuilder.add(new ir.LetPrim(result)); | |
| 1546 } else if (Elements.isLocal(element)) { | |
| 1547 // Reference to local variable | |
| 1548 result = irBuilder.buildLocalGet(element); | |
| 1549 } else if (element == null || | |
| 1550 Elements.isInstanceField(element) || | |
| 1551 Elements.isInstanceMethod(element) || | |
| 1552 selector.isIndex || | |
| 1553 // TODO(johnniwinther): clean up semantics of resolution. | |
| 1554 node.isSuperCall) { | |
| 1555 // Dynamic dispatch to a getter. Sometimes resolution will suggest a | |
| 1556 // target element, but in these cases we must still emit a dynamic | |
| 1557 // dispatch. The target element may be an instance method in case we are | |
| 1558 // converting a method to a function object. | |
| 1559 | |
| 1560 receiver = visitReceiver(node.receiver); | |
| 1561 List<ir.Primitive> arguments = new List<ir.Primitive>(); | |
| 1562 if (selector.isIndex) { | |
| 1563 index = visit(node.arguments.head); | |
| 1564 arguments.add(index); | |
| 1565 } | |
| 1566 | |
| 1567 assert(selector.kind == SelectorKind.GETTER || | |
| 1568 selector.kind == SelectorKind.INDEX); | |
| 1569 result = irBuilder.continueWithExpression( | |
| 1570 (k) => createDynamicInvoke(node, selector, receiver, k, arguments)); | |
| 1571 } else if (element.isField || element.isGetter || element.isErroneous || | |
| 1572 element.isSetter) { | |
| 1573 // TODO(johnniwinther): Change handling of setter selectors. | |
| 1574 // Access to a static field or getter (non-static case handled above). | |
| 1575 // Even if there is only a setter, we compile as if it was a getter, | |
| 1576 // so the vm can fail at runtime. | |
| 1577 assert(selector.kind == SelectorKind.GETTER || | |
| 1578 selector.kind == SelectorKind.SETTER); | |
| 1579 result = irBuilder.buildStaticGet(element, selector); | |
| 1580 } else if (Elements.isStaticOrTopLevelFunction(element)) { | |
| 1581 // Convert a top-level or static function to a function object. | |
| 1582 result = translateConstant(node); | |
| 1583 } else { | |
| 1584 throw "Unexpected SendSet getter: $node, $element"; | |
| 1585 } | |
| 1586 return new _GetterElements( | |
| 1587 result: result,index: index, receiver: receiver); | |
| 1588 } | |
| 1589 | |
| 1590 ir.Primitive visitGetterSend(ast.Send node) { | |
| 1591 assert(irBuilder.isOpen); | |
| 1592 return translateGetter(node, elements.getSelector(node)).result; | |
| 1593 | |
| 1594 } | |
| 1595 | |
| 1596 ir.Primitive buildNegation(ir.Primitive condition) { | |
| 1597 // ! e is translated as e ? false : true | |
| 1598 | |
| 1599 // Add a continuation parameter for the result of the expression. | |
| 1600 ir.Parameter resultParameter = new ir.Parameter(null); | |
| 1601 | |
| 1602 ir.Continuation joinContinuation = new ir.Continuation([resultParameter]); | |
| 1603 ir.Continuation thenContinuation = new ir.Continuation([]); | |
| 1604 ir.Continuation elseContinuation = new ir.Continuation([]); | |
| 1605 | |
| 1606 ir.Constant trueConstant = irBuilder.makePrimConst( | |
| 1607 irBuilder.state.constantSystem.createBool(true)); | |
| 1608 ir.Constant falseConstant = irBuilder.makePrimConst( | |
| 1609 irBuilder.state.constantSystem.createBool(false)); | |
| 1610 | |
| 1611 thenContinuation.body = new ir.LetPrim(falseConstant) | |
| 1612 ..plug(new ir.InvokeContinuation(joinContinuation, [falseConstant])); | |
| 1613 elseContinuation.body = new ir.LetPrim(trueConstant) | |
| 1614 ..plug(new ir.InvokeContinuation(joinContinuation, [trueConstant])); | |
| 1615 | |
| 1616 irBuilder.add(new ir.LetCont(joinContinuation, | |
| 1617 new ir.LetCont(thenContinuation, | |
| 1618 new ir.LetCont(elseContinuation, | |
| 1619 new ir.Branch(new ir.IsTrue(condition), | |
| 1620 thenContinuation, | |
| 1621 elseContinuation))))); | |
| 1622 return resultParameter; | |
| 1623 } | |
| 1624 | |
| 1625 ir.Primitive translateLogicalOperator(ast.Operator op, | |
| 1626 ast.Expression left, | |
| 1627 ast.Expression right) { | |
| 1628 // e0 && e1 is translated as if e0 ? (e1 == true) : false. | |
| 1629 // e0 || e1 is translated as if e0 ? true : (e1 == true). | |
| 1630 // The translation must convert both e0 and e1 to booleans and handle | |
| 1631 // local variable assignments in e1. | |
| 1632 | |
| 1633 ir.Primitive leftValue = visit(left); | |
| 1634 IrBuilder rightBuilder = new IrBuilder.delimited(irBuilder); | |
| 1635 ir.Primitive rightValue = | |
| 1636 withBuilder(rightBuilder, () => visit(right)); | |
| 1637 // A dummy empty target for the branch on the left subexpression branch. | |
| 1638 // This enables using the same infrastructure for join-point continuations | |
| 1639 // as in visitIf and visitConditional. It will hold a definition of the | |
| 1640 // appropriate constant and an invocation of the join-point continuation. | |
| 1641 IrBuilder emptyBuilder = new IrBuilder.delimited(irBuilder); | |
| 1642 // Dummy empty targets for right true and right false. They hold | |
| 1643 // definitions of the appropriate constant and an invocation of the | |
| 1644 // join-point continuation. | |
| 1645 IrBuilder rightTrueBuilder = new IrBuilder.delimited(rightBuilder); | |
| 1646 IrBuilder rightFalseBuilder = new IrBuilder.delimited(rightBuilder); | |
| 1647 | |
| 1648 // If we don't evaluate the right subexpression, the value of the whole | |
| 1649 // expression is this constant. | |
| 1650 ir.Constant leftBool = emptyBuilder.makePrimConst( | |
| 1651 emptyBuilder.state.constantSystem.createBool(op.source == '||')); | |
| 1652 // If we do evaluate the right subexpression, the value of the expression | |
| 1653 // is a true or false constant. | |
| 1654 ir.Constant rightTrue = rightTrueBuilder.makePrimConst( | |
| 1655 rightTrueBuilder.state.constantSystem.createBool(true)); | |
| 1656 ir.Constant rightFalse = rightFalseBuilder.makePrimConst( | |
| 1657 rightFalseBuilder.state.constantSystem.createBool(false)); | |
| 1658 emptyBuilder.add(new ir.LetPrim(leftBool)); | |
| 1659 rightTrueBuilder.add(new ir.LetPrim(rightTrue)); | |
| 1660 rightFalseBuilder.add(new ir.LetPrim(rightFalse)); | |
| 1661 | |
| 1662 // Treat the result values as named values in the environment, so they | |
| 1663 // will be treated as arguments to the join-point continuation. | |
| 1664 assert(irBuilder.environment.length == emptyBuilder.environment.length); | |
| 1665 assert(irBuilder.environment.length == rightTrueBuilder.environment.length); | |
| 1666 assert(irBuilder.environment.length == | |
| 1667 rightFalseBuilder.environment.length); | |
| 1668 emptyBuilder.environment.extend(null, leftBool); | |
| 1669 rightTrueBuilder.environment.extend(null, rightTrue); | |
| 1670 rightFalseBuilder.environment.extend(null, rightFalse); | |
| 1671 | |
| 1672 // Wire up two continuations for the left subexpression, two continuations | |
| 1673 // for the right subexpression, and a three-way join continuation. | |
| 1674 JumpCollector jumps = new JumpCollector(null); | |
| 1675 jumps.addJump(emptyBuilder); | |
| 1676 jumps.addJump(rightTrueBuilder); | |
| 1677 jumps.addJump(rightFalseBuilder); | |
| 1678 ir.Continuation joinContinuation = | |
| 1679 createJoin(irBuilder.environment.length + 1, jumps); | |
| 1680 ir.Continuation leftTrueContinuation = new ir.Continuation([]); | |
| 1681 ir.Continuation leftFalseContinuation = new ir.Continuation([]); | |
| 1682 ir.Continuation rightTrueContinuation = new ir.Continuation([]); | |
| 1683 ir.Continuation rightFalseContinuation = new ir.Continuation([]); | |
| 1684 rightTrueContinuation.body = rightTrueBuilder._root; | |
| 1685 rightFalseContinuation.body = rightFalseBuilder._root; | |
| 1686 // The right subexpression has two continuations. | |
| 1687 rightBuilder.add( | |
| 1688 new ir.LetCont(rightTrueContinuation, | |
| 1689 new ir.LetCont(rightFalseContinuation, | |
| 1690 new ir.Branch(new ir.IsTrue(rightValue), | |
| 1691 rightTrueContinuation, | |
| 1692 rightFalseContinuation)))); | |
| 1693 // Depending on the operator, the left subexpression's continuations are | |
| 1694 // either the right subexpression or an invocation of the join-point | |
| 1695 // continuation. | |
| 1696 if (op.source == '&&') { | |
| 1697 leftTrueContinuation.body = rightBuilder._root; | |
| 1698 leftFalseContinuation.body = emptyBuilder._root; | |
| 1699 } else { | |
| 1700 leftTrueContinuation.body = emptyBuilder._root; | |
| 1701 leftFalseContinuation.body = rightBuilder._root; | |
| 1702 } | |
| 1703 | |
| 1704 irBuilder.add(new ir.LetCont(joinContinuation, | |
| 1705 new ir.LetCont(leftTrueContinuation, | |
| 1706 new ir.LetCont(leftFalseContinuation, | |
| 1707 new ir.Branch(new ir.IsTrue(leftValue), | |
| 1708 leftTrueContinuation, | |
| 1709 leftFalseContinuation))))); | |
| 1710 // There is always a join parameter for the result value, because it | |
| 1711 // is different on at least two paths. | |
| 1712 return joinContinuation.parameters.last; | |
| 1713 } | |
| 1714 | |
| 1715 ir.Primitive visitOperatorSend(ast.Send node) { | |
| 1716 assert(irBuilder.isOpen); | |
| 1717 ast.Operator op = node.selector; | |
| 1718 if (isUserDefinableOperator(op.source)) { | |
| 1719 return visitDynamicSend(node); | |
| 1720 } | |
| 1721 if (op.source == '&&' || op.source == '||') { | |
| 1722 assert(node.receiver != null); | |
| 1723 assert(!node.arguments.isEmpty); | |
| 1724 assert(node.arguments.tail.isEmpty); | |
| 1725 return translateLogicalOperator(op, node.receiver, node.arguments.head); | |
| 1726 } | |
| 1727 if (op.source == "!") { | |
| 1728 assert(node.receiver != null); | |
| 1729 assert(node.arguments.isEmpty); | |
| 1730 return buildNegation(visit(node.receiver)); | |
| 1731 } | |
| 1732 if (op.source == "!=") { | |
| 1733 assert(node.receiver != null); | |
| 1734 assert(!node.arguments.isEmpty); | |
| 1735 assert(node.arguments.tail.isEmpty); | |
| 1736 return buildNegation(visitDynamicSend(node)); | |
| 1737 } | |
| 1738 assert(invariant(node, op.source == "is" || op.source == "as", | |
| 1739 message: "unexpected operator $op")); | |
| 1740 DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast); | |
| 1741 ir.Primitive receiver = visit(node.receiver); | |
| 1742 ir.Primitive check = irBuilder.continueWithExpression( | |
| 1743 (k) => new ir.TypeOperator(op.source, receiver, type, k)); | |
| 1744 return node.isIsNotCheck ? buildNegation(check) : check; | |
| 1745 } | |
| 1746 | |
| 1747 // Build(StaticSend(f, arguments), C) = C[C'[InvokeStatic(f, xs)]] | |
| 1748 // where (C', xs) = arguments.fold(Build, C) | |
| 1749 ir.Primitive visitStaticSend(ast.Send node) { | |
| 1750 assert(irBuilder.isOpen); | |
| 1751 Element element = elements[node]; | |
| 1752 assert(!element.isConstructor); | |
| 1753 // TODO(lry): support foreign functions. | |
| 1754 if (element.isForeign(compiler.backend)) { | |
| 1755 return giveup(node, 'StaticSend: foreign'); | |
| 1756 } | |
| 1757 | |
| 1758 Selector selector = elements.getSelector(node); | |
| 1759 | |
| 1760 // TODO(lry): support default arguments, need support for locals. | |
| 1761 List<ir.Definition> arguments = node.arguments.mapToList(visit, | |
| 1762 growable:false); | |
| 1763 return irBuilder.buildStaticInvocation(element, selector, arguments); | |
| 1764 } | |
| 1765 | |
| 1766 | |
| 1767 ir.Primitive visitSuperSend(ast.Send node) { | |
| 1768 assert(irBuilder.isOpen); | |
| 1769 if (node.isPropertyAccess) { | |
| 1770 return visitGetterSend(node); | |
| 1771 } else { | |
| 1772 Selector selector = elements.getSelector(node); | |
| 1773 List<ir.Primitive> arguments = new List<ir.Primitive>(); | |
| 1774 for (ast.Node n in node.arguments) { | |
| 1775 arguments.add(visit(n)); | |
| 1776 } | |
| 1777 return irBuilder.buildSuperInvocation(selector, arguments); | |
| 1778 } | |
| 1779 } | |
| 1780 | |
| 1781 visitTypePrefixSend(ast.Send node) { | |
| 1782 compiler.internalError(node, "visitTypePrefixSend should not be called."); | |
| 1783 } | |
| 1784 | |
| 1785 ir.Primitive visitTypeLiteralSend(ast.Send node) { | |
| 1786 assert(irBuilder.isOpen); | |
| 1787 // If the user is trying to invoke the type literal or variable, | |
| 1788 // it must be treated as a function call. | |
| 1789 if (node.argumentsNode != null) { | |
| 1790 // TODO(sigurdm): Handle this to match proposed semantics of issue #19725. | |
| 1791 return giveup(node, 'Type literal invoked as function'); | |
| 1792 } | |
| 1793 | |
| 1794 DartType type = elements.getTypeLiteralType(node); | |
| 1795 if (type is TypeVariableType) { | |
| 1796 ir.Primitive prim = new ir.ReifyTypeVar(type.element); | |
| 1797 irBuilder.add(new ir.LetPrim(prim)); | |
| 1798 return prim; | |
| 1799 } else { | |
| 1800 return translateConstant(node); | |
| 1801 } | |
| 1802 } | |
| 1803 | |
| 1804 /// True if [element] is a local variable, local function, or parameter that | |
| 1805 /// is accessed from an inner function. Recursive self-references in a local | |
| 1806 /// function count as closure accesses. | |
| 1807 /// | |
| 1808 /// If `true`, [element] is a [LocalElement]. | |
| 1809 bool isClosureVariable(Element element) { | |
| 1810 return irBuilder.state.closureLocals.contains(element); | |
| 1811 } | |
| 1812 | |
| 1813 void setLocal(Element element, ir.Primitive valueToStore) { | |
| 1814 if (isClosureVariable(element)) { | |
| 1815 LocalElement local = element; | |
| 1816 irBuilder.add(new ir.SetClosureVariable(local, valueToStore)); | |
| 1817 } else { | |
| 1818 valueToStore.useElementAsHint(element); | |
| 1819 irBuilder.environment.update(element, valueToStore); | |
| 1820 } | |
| 1821 } | |
| 1822 | |
| 1823 void setStatic(Element element, | |
| 1824 Selector selector, | |
| 1825 ir.Primitive valueToStore) { | |
| 1826 assert(element.isErroneous || element.isField || element.isSetter); | |
| 1827 irBuilder.continueWithExpression( | |
| 1828 (k) => new ir.InvokeStatic(element, selector, k, [valueToStore])); | |
| 1829 } | |
| 1830 | |
| 1831 void setDynamic(ast.Node node, | |
| 1832 ir.Primitive receiver, Selector selector, | |
| 1833 ir.Primitive valueToStore) { | |
| 1834 List<ir.Definition> arguments = [valueToStore]; | |
| 1835 irBuilder.continueWithExpression( | |
| 1836 (k) => createDynamicInvoke(node, selector, receiver, k, arguments)); | |
| 1837 } | |
| 1838 | |
| 1839 void setIndex(ast.Node node, | |
| 1840 ir.Primitive receiver, | |
| 1841 Selector selector, | |
| 1842 ir.Primitive index, | |
| 1843 ir.Primitive valueToStore) { | |
| 1844 List<ir.Definition> arguments = [index, valueToStore]; | |
| 1845 irBuilder.continueWithExpression( | |
| 1846 (k) => createDynamicInvoke(node, selector, receiver, k, arguments)); | |
| 1847 } | |
| 1848 | |
| 1849 ir.Primitive visitSendSet(ast.SendSet node) { | |
| 1850 assert(irBuilder.isOpen); | |
| 1851 Element element = elements[node]; | |
| 1852 ast.Operator op = node.assignmentOperator; | |
| 1853 // For complex operators, this is the result of getting (before assigning) | |
| 1854 ir.Primitive originalValue; | |
| 1855 // For []+= style operators, this saves the index. | |
| 1856 ir.Primitive index; | |
| 1857 ir.Primitive receiver; | |
| 1858 // This is what gets assigned. | |
| 1859 ir.Primitive valueToStore; | |
| 1860 Selector selector = elements.getSelector(node); | |
| 1861 Selector operatorSelector = | |
| 1862 elements.getOperatorSelectorInComplexSendSet(node); | |
| 1863 Selector getterSelector = | |
| 1864 elements.getGetterSelectorInComplexSendSet(node); | |
| 1865 assert( | |
| 1866 // Indexing send-sets have an argument for the index. | |
| 1867 (selector.isIndexSet ? 1 : 0) + | |
| 1868 // Non-increment send-sets have one more argument. | |
| 1869 (ast.Operator.INCREMENT_OPERATORS.contains(op.source) ? 0 : 1) | |
| 1870 == node.argumentCount()); | |
| 1871 | |
| 1872 ast.Node getAssignArgument() { | |
| 1873 assert(invariant(node, !node.arguments.isEmpty, | |
| 1874 message: "argument expected")); | |
| 1875 return selector.isIndexSet | |
| 1876 ? node.arguments.tail.head | |
| 1877 : node.arguments.head; | |
| 1878 } | |
| 1879 | |
| 1880 // Get the value into valueToStore | |
| 1881 if (op.source == "=") { | |
| 1882 if (selector.isIndexSet) { | |
| 1883 receiver = visitReceiver(node.receiver); | |
| 1884 index = visit(node.arguments.head); | |
| 1885 } else if (element == null || Elements.isInstanceField(element)) { | |
| 1886 receiver = visitReceiver(node.receiver); | |
| 1887 } | |
| 1888 valueToStore = visit(getAssignArgument()); | |
| 1889 } else { | |
| 1890 // Get the original value into getter | |
| 1891 assert(ast.Operator.COMPLEX_OPERATORS.contains(op.source)); | |
| 1892 | |
| 1893 _GetterElements getterResult = translateGetter(node, getterSelector); | |
| 1894 index = getterResult.index; | |
| 1895 receiver = getterResult.receiver; | |
| 1896 originalValue = getterResult.result; | |
| 1897 | |
| 1898 // Do the modification of the value in getter. | |
| 1899 ir.Primitive arg; | |
| 1900 if (ast.Operator.INCREMENT_OPERATORS.contains(op.source)) { | |
| 1901 arg = irBuilder.makePrimConst( | |
| 1902 irBuilder.state.constantSystem.createInt(1)); | |
| 1903 irBuilder.add(new ir.LetPrim(arg)); | |
| 1904 } else { | |
| 1905 arg = visit(getAssignArgument()); | |
| 1906 } | |
| 1907 valueToStore = new ir.Parameter(null); | |
| 1908 ir.Continuation k = new ir.Continuation([valueToStore]); | |
| 1909 ir.Expression invoke = | |
| 1910 new ir.InvokeMethod(originalValue, operatorSelector, k, [arg]); | |
| 1911 irBuilder.add(new ir.LetCont(k, invoke)); | |
| 1912 } | |
| 1913 | |
| 1914 if (Elements.isLocal(element)) { | |
| 1915 setLocal(element, valueToStore); | |
| 1916 } else if ((!node.isSuperCall && Elements.isErroneousElement(element)) || | |
| 1917 Elements.isStaticOrTopLevel(element)) { | |
| 1918 setStatic(element, elements.getSelector(node), valueToStore); | |
| 1919 } else { | |
| 1920 // Setter or index-setter invocation | |
| 1921 Selector selector = elements.getSelector(node); | |
| 1922 assert(selector.kind == SelectorKind.SETTER || | |
| 1923 selector.kind == SelectorKind.INDEX); | |
| 1924 if (selector.isIndexSet) { | |
| 1925 setIndex(node, receiver, selector, index, valueToStore); | |
| 1926 } else { | |
| 1927 setDynamic(node, receiver, selector, valueToStore); | |
| 1928 } | |
| 1929 } | |
| 1930 | |
| 1931 if (node.isPostfix) { | |
| 1932 assert(originalValue != null); | |
| 1933 return originalValue; | |
| 1934 } else { | |
| 1935 return valueToStore; | |
| 1936 } | |
| 1937 } | |
| 1938 | |
| 1939 ir.Primitive visitNewExpression(ast.NewExpression node) { | |
| 1940 assert(irBuilder.isOpen); | |
| 1941 if (node.isConst) { | |
| 1942 return translateConstant(node); | |
| 1943 } | |
| 1944 FunctionElement element = elements[node.send]; | |
| 1945 Selector selector = elements.getSelector(node.send); | |
| 1946 ast.Node selectorNode = node.send.selector; | |
| 1947 DartType type = elements.getType(node); | |
| 1948 List<ir.Primitive> args = | |
| 1949 node.send.arguments.mapToList(visit, growable:false); | |
| 1950 return irBuilder.continueWithExpression( | |
| 1951 (k) => new ir.InvokeConstructor(type, element,selector, k, args)); | |
| 1952 } | |
| 1953 | |
| 1954 ir.Primitive visitStringJuxtaposition(ast.StringJuxtaposition node) { | |
| 1955 assert(irBuilder.isOpen); | |
| 1956 ir.Primitive first = visit(node.first); | |
| 1957 ir.Primitive second = visit(node.second); | |
| 1958 return irBuilder.continueWithExpression( | |
| 1959 (k) => new ir.ConcatenateStrings(k, [first, second])); | |
| 1960 } | |
| 1961 | |
| 1962 ir.Primitive visitStringInterpolation(ast.StringInterpolation node) { | |
| 1963 assert(irBuilder.isOpen); | |
| 1964 List<ir.Primitive> arguments = []; | |
| 1965 arguments.add(visitLiteralString(node.string)); | |
| 1966 var it = node.parts.iterator; | |
| 1967 while (it.moveNext()) { | |
| 1968 ast.StringInterpolationPart part = it.current; | |
| 1969 arguments.add(visit(part.expression)); | |
| 1970 arguments.add(visitLiteralString(part.string)); | |
| 1971 } | |
| 1972 return irBuilder.continueWithExpression( | |
| 1973 (k) => new ir.ConcatenateStrings(k, arguments)); | |
| 1974 } | |
| 1975 | |
| 1976 ir.Primitive translateConstant(ast.Node node, [ConstantExpression constant]) { | |
| 1977 assert(irBuilder.isOpen); | |
| 1978 if (constant == null) { | |
| 1979 constant = getConstantForNode(node); | |
| 1980 } | |
| 1981 ir.Primitive primitive = irBuilder.makeConst(constant); | |
| 1982 irBuilder.add(new ir.LetPrim(primitive)); | |
| 1983 return primitive; | |
| 1984 } | |
| 1985 | |
| 1986 ir.FunctionDefinition makeSubFunction(ast.FunctionExpression node) { | |
| 1987 // TODO(johnniwinther): Share the visitor. | |
| 1988 return new IrBuilderVisitor(elements, compiler, sourceFile) | |
| 1989 .buildFunctionInternal(elements[node]); | |
| 1990 } | |
| 1991 | |
| 1992 ir.Primitive visitFunctionExpression(ast.FunctionExpression node) { | |
| 1993 FunctionElement element = elements[node]; | |
| 1994 ir.FunctionDefinition inner = makeSubFunction(node); | |
| 1995 ir.CreateFunction prim = new ir.CreateFunction(inner); | |
| 1996 irBuilder.add(new ir.LetPrim(prim)); | |
| 1997 return prim; | |
| 1998 } | |
| 1999 | |
| 2000 ir.Primitive visitFunctionDeclaration(ast.FunctionDeclaration node) { | |
| 2001 LocalFunctionElement element = elements[node.function]; | |
| 2002 ir.FunctionDefinition inner = makeSubFunction(node.function); | |
| 2003 if (isClosureVariable(element)) { | |
| 2004 irBuilder.add(new ir.DeclareFunction(element, inner)); | |
| 2005 } else { | |
| 2006 ir.CreateFunction prim = new ir.CreateFunction(inner); | |
| 2007 irBuilder.add(new ir.LetPrim(prim)); | |
| 2008 irBuilder.environment.extend(element, prim); | |
| 2009 prim.useElementAsHint(element); | |
| 2010 } | |
| 2011 return null; | |
| 2012 } | |
| 2013 | |
| 2014 static final String ABORT_IRNODE_BUILDER = "IrNode builder aborted"; | |
| 2015 | |
| 2016 dynamic giveup(ast.Node node, [String reason]) { | |
| 2017 throw ABORT_IRNODE_BUILDER; | |
| 2018 } | |
| 2019 | |
| 2020 ir.FunctionDefinition nullIfGiveup(ir.FunctionDefinition action()) { | |
| 2021 try { | |
| 2022 return action(); | |
| 2023 } catch(e, tr) { | |
| 2024 if (e == ABORT_IRNODE_BUILDER) { | |
| 2025 return null; | |
| 2026 } | |
| 2027 rethrow; | |
| 2028 } | |
| 2029 } | |
| 2030 | |
| 2031 void internalError(String reason, {ast.Node node}) { | |
| 2032 giveup(node); | |
| 2033 } | |
| 2034 } | |
| 2035 | |
| 2036 /// Classifies local variables and local functions as 'closure variables'. | |
| 2037 /// A closure variable is one that is accessed from an inner function nested | |
| 2038 /// one or more levels inside the one that declares it. | |
| 2039 class DetectClosureVariables extends ast.Visitor { | |
| 2040 final TreeElements elements; | |
| 2041 DetectClosureVariables(this.elements); | |
| 2042 | |
| 2043 FunctionElement currentFunction; | |
| 2044 Set<Local> usedFromClosure = new Set<Local>(); | |
| 2045 Set<FunctionElement> recursiveFunctions = new Set<FunctionElement>(); | |
| 2046 | |
| 2047 bool isClosureVariable(Entity entity) => usedFromClosure.contains(entity); | |
| 2048 | |
| 2049 void markAsClosureVariable(Local local) { | |
| 2050 usedFromClosure.add(local); | |
| 2051 } | |
| 2052 | |
| 2053 visit(ast.Node node) => node.accept(this); | |
| 2054 | |
| 2055 visitNode(ast.Node node) { | |
| 2056 node.visitChildren(this); | |
| 2057 } | |
| 2058 | |
| 2059 visitSend(ast.Send node) { | |
| 2060 Element element = elements[node]; | |
| 2061 if (Elements.isLocal(element) && | |
| 2062 !element.isConst && | |
| 2063 element.enclosingElement != currentFunction) { | |
| 2064 LocalElement local = element; | |
| 2065 markAsClosureVariable(local); | |
| 2066 } | |
| 2067 node.visitChildren(this); | |
| 2068 } | |
| 2069 | |
| 2070 visitFunctionExpression(ast.FunctionExpression node) { | |
| 2071 FunctionElement oldFunction = currentFunction; | |
| 2072 currentFunction = elements[node]; | |
| 2073 visit(node.body); | |
| 2074 currentFunction = oldFunction; | |
| 2075 } | |
| 2076 | |
| 2077 } | |
| OLD | NEW |