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 |