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 part of dart2js.ir_builder; | 5 part of dart2js.ir_builder; |
6 | 6 |
7 /** | 7 /** |
8 * This task iterates through all resolved elements and builds [ir.Node]s. The | 8 * This task iterates through all resolved elements and builds [ir.Node]s. The |
9 * nodes are stored in the [nodes] map and accessible through [hasIr] and | 9 * nodes are stored in the [nodes] map and accessible through [hasIr] and |
10 * [getIr]. | 10 * [getIr]. |
11 * | 11 * |
12 * The functionality of the IrNodes is added gradually, therefore elements might | 12 * The functionality of the IrNodes is added gradually, therefore elements might |
13 * have an IR or not, depending on the language features that are used. For | 13 * have an IR or not, depending on the language features that are used. For |
14 * elements that do have an IR, the tree [ast.Node]s and the [Token]s are not | 14 * elements that do have an IR, the tree [ast.Node]s and the [Token]s are not |
15 * used in the rest of the compilation. This is ensured by setting the element's | 15 * used in the rest of the compilation. This is ensured by setting the element's |
16 * cached tree to `null` and also breaking the token stream to crash future | 16 * cached tree to `null` and also breaking the token stream to crash future |
17 * attempts to parse. | 17 * attempts to parse. |
18 * | 18 * |
19 * The type inferrer works on either IR nodes or tree nodes. The IR nodes are | 19 * The type inferrer works on either IR nodes or tree nodes. The IR nodes are |
20 * then translated into the SSA form for optimizations and code generation. | 20 * then translated into the SSA form for optimizations and code generation. |
21 * Long-term, once the IR supports the full language, the backend can be | 21 * Long-term, once the IR supports the full language, the backend can be |
22 * re-implemented to work directly on the IR. | 22 * re-implemented to work directly on the IR. |
23 */ | 23 */ |
24 class IrBuilderTask extends CompilerTask { | 24 class IrBuilderTask extends CompilerTask { |
25 final Map<Element, ir.ExecutableDefinition> nodes = | 25 final Map<Element, ir.ExecutableDefinition> nodes = |
26 <Element, ir.ExecutableDefinition>{}; | 26 <Element, ir.ExecutableDefinition>{}; |
27 final bool generateSourceMap; | |
27 | 28 |
28 IrBuilderTask(Compiler compiler) : super(compiler); | 29 IrBuilderTask(Compiler compiler, {this.generateSourceMap: true}) |
30 : super(compiler); | |
29 | 31 |
30 String get name => 'IR builder'; | 32 String get name => 'IR builder'; |
31 | 33 |
32 bool hasIr(Element element) => nodes.containsKey(element.implementation); | 34 bool hasIr(Element element) => nodes.containsKey(element.implementation); |
33 | 35 |
34 ir.ExecutableDefinition getIr(ExecutableElement element) { | 36 ir.ExecutableDefinition getIr(ExecutableElement element) { |
35 return nodes[element.implementation]; | 37 return nodes[element.implementation]; |
36 } | 38 } |
37 | 39 |
38 ir.ExecutableDefinition buildNode(AstElement element) { | 40 ir.ExecutableDefinition buildNode(AstElement element) { |
39 if (!canBuild(element)) return null; | 41 if (!canBuild(element)) return null; |
42 | |
40 TreeElements elementsMapping = element.resolvedAst.elements; | 43 TreeElements elementsMapping = element.resolvedAst.elements; |
41 element = element.implementation; | 44 element = element.implementation; |
42 return compiler.withCurrentElement(element, () { | 45 return compiler.withCurrentElement(element, () { |
43 SourceFile sourceFile = elementSourceFile(element); | 46 SourceInformationBuilder sourceInformationBuilder = generateSourceMap |
47 ? new PositionSourceInformationBuilder(element) | |
48 : const SourceInformationBuilder(); | |
49 | |
44 IrBuilderVisitor builder = | 50 IrBuilderVisitor builder = |
45 compiler.backend is JavaScriptBackend | 51 compiler.backend is JavaScriptBackend |
46 ? new JsIrBuilderVisitor(elementsMapping, compiler, sourceFile) | 52 ? new JsIrBuilderVisitor( |
47 : new DartIrBuilderVisitor(elementsMapping, compiler, sourceFile); | 53 elementsMapping, compiler, sourceInformationBuilder) |
48 return builder.buildExecutable(element); | 54 : new DartIrBuilderVisitor( |
55 elementsMapping, compiler, sourceInformationBuilder); | |
56 ir.ExecutableDefinition definition = | |
57 builder.buildExecutable(element); | |
58 if (definition != null) { | |
59 nodes[element] = definition; | |
60 } | |
61 return definition; | |
49 }); | 62 }); |
50 } | 63 } |
51 | 64 |
52 void buildNodes() { | 65 void buildNodes() { |
53 measure(() { | 66 measure(() { |
54 Set<Element> resolved = compiler.enqueuer.resolution.resolvedElements; | 67 Set<Element> resolved = compiler.enqueuer.resolution.resolvedElements; |
55 resolved.forEach((AstElement element) { | 68 resolved.forEach(buildNode); |
56 ir.ExecutableDefinition definition = buildNode(element); | |
57 if (definition != null) { | |
58 nodes[element] = definition; | |
59 } | |
60 }); | |
61 }); | 69 }); |
62 } | 70 } |
63 | 71 |
64 bool canBuild(Element element) { | 72 bool canBuild(Element element) { |
65 if (element is TypedefElement) return false; | 73 if (element is TypedefElement) return false; |
66 if (element is FunctionElement) { | 74 if (element is FunctionElement) { |
67 // TODO(sigurdm): Support native functions for dart2js. | 75 // TODO(sigurdm): Support native functions for dart2js. |
68 assert(invariant(element, !element.isNative)); | 76 assert(invariant(element, !element.isNative)); |
69 | 77 |
70 if (element is ConstructorElement) { | 78 if (element is ConstructorElement) { |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
108 } | 116 } |
109 | 117 |
110 /** | 118 /** |
111 * A tree visitor that builds [IrNodes]. The visit methods add statements using | 119 * A tree visitor that builds [IrNodes]. The visit methods add statements using |
112 * to the [builder] and return the last added statement for trees that represent | 120 * to the [builder] and return the last added statement for trees that represent |
113 * an expression. | 121 * an expression. |
114 */ | 122 */ |
115 abstract class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive> | 123 abstract class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive> |
116 with IrBuilderMixin<ast.Node> { | 124 with IrBuilderMixin<ast.Node> { |
117 final Compiler compiler; | 125 final Compiler compiler; |
118 final SourceFile sourceFile; | 126 final SourceInformationBuilder sourceInformationBuilder; |
119 | 127 |
120 // In SSA terms, join-point continuation parameters are the phis and the | 128 // In SSA terms, join-point continuation parameters are the phis and the |
121 // continuation invocation arguments are the corresponding phi inputs. To | 129 // continuation invocation arguments are the corresponding phi inputs. To |
122 // support name introduction and renaming for source level variables, we use | 130 // support name introduction and renaming for source level variables, we use |
123 // nested (delimited) visitors for constructing subparts of the IR that will | 131 // nested (delimited) visitors for constructing subparts of the IR that will |
124 // need renaming. Each source variable is assigned an index. | 132 // need renaming. Each source variable is assigned an index. |
125 // | 133 // |
126 // Each nested visitor maintains a list of free variable uses in the body. | 134 // Each nested visitor maintains a list of free variable uses in the body. |
127 // These are implemented as a list of parameters, each with their own use | 135 // These are implemented as a list of parameters, each with their own use |
128 // list of references. When the delimited subexpression is plugged into the | 136 // list of references. When the delimited subexpression is plugged into the |
129 // surrounding context, the free occurrences can be captured or become free | 137 // surrounding context, the free occurrences can be captured or become free |
130 // occurrences in the next outer delimited subexpression. | 138 // occurrences in the next outer delimited subexpression. |
131 // | 139 // |
132 // Each nested visitor maintains a list that maps indexes of variables | 140 // Each nested visitor maintains a list that maps indexes of variables |
133 // assigned in the delimited subexpression to their reaching definition --- | 141 // assigned in the delimited subexpression to their reaching definition --- |
134 // that is, the definition in effect at the hole in 'current'. These are | 142 // that is, the definition in effect at the hole in 'current'. These are |
135 // used to determine if a join-point continuation needs to be passed | 143 // used to determine if a join-point continuation needs to be passed |
136 // arguments, and what the arguments are. | 144 // arguments, and what the arguments are. |
137 | 145 |
138 /// Construct a top-level visitor. | 146 /// Construct a top-level visitor. |
139 IrBuilderVisitor(TreeElements elements, this.compiler, this.sourceFile) | 147 IrBuilderVisitor(TreeElements elements, |
148 this.compiler, | |
149 this.sourceInformationBuilder) | |
140 : super(elements); | 150 : super(elements); |
141 | 151 |
142 /** | 152 /** |
143 * Builds the [ir.ExecutableDefinition] for an executable element. In case the | 153 * Builds the [ir.ExecutableDefinition] for an executable element. In case the |
144 * function uses features that cannot be expressed in the IR, this element | 154 * function uses features that cannot be expressed in the IR, this element |
145 * returns `null`. | 155 * returns `null`. |
146 */ | 156 */ |
147 ir.ExecutableDefinition buildExecutable(ExecutableElement element); | 157 ir.ExecutableDefinition buildExecutable(ExecutableElement element); |
148 | 158 |
149 ClosureScope getClosureScopeForNode(ast.Node node); | 159 ClosureScope getClosureScopeForNode(ast.Node node); |
(...skipping 496 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
646 irBuilder.buildDynamicInvocation(receiver, selector, arguments); | 656 irBuilder.buildDynamicInvocation(receiver, selector, arguments); |
647 } | 657 } |
648 } else if (element.isField || element.isGetter || element.isErroneous || | 658 } else if (element.isField || element.isGetter || element.isErroneous || |
649 element.isSetter) { | 659 element.isSetter) { |
650 // TODO(johnniwinther): Change handling of setter selectors. | 660 // TODO(johnniwinther): Change handling of setter selectors. |
651 // Access to a static field or getter (non-static case handled above). | 661 // Access to a static field or getter (non-static case handled above). |
652 // Even if there is only a setter, we compile as if it was a getter, | 662 // Even if there is only a setter, we compile as if it was a getter, |
653 // so the vm can fail at runtime. | 663 // so the vm can fail at runtime. |
654 assert(selector.kind == SelectorKind.GETTER || | 664 assert(selector.kind == SelectorKind.GETTER || |
655 selector.kind == SelectorKind.SETTER); | 665 selector.kind == SelectorKind.SETTER); |
656 result = irBuilder.buildStaticGet(element, selector); | 666 result = irBuilder.buildStaticGet(element, selector, |
667 sourceInformation: sourceInformationBuilder.buildGet(node)); | |
657 } else if (Elements.isStaticOrTopLevelFunction(element)) { | 668 } else if (Elements.isStaticOrTopLevelFunction(element)) { |
658 // Convert a top-level or static function to a function object. | 669 // Convert a top-level or static function to a function object. |
659 result = translateConstant(node); | 670 result = translateConstant(node); |
660 } else { | 671 } else { |
661 throw "Unexpected SendSet getter: $node, $element"; | 672 throw "Unexpected SendSet getter: $node, $element"; |
662 } | 673 } |
663 return new _GetterElements( | 674 return new _GetterElements( |
664 result: result,index: index, receiver: receiver); | 675 result: result,index: index, receiver: receiver); |
665 } | 676 } |
666 | 677 |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
745 return irBuilder.buildCallInvocation(receiver, call, arguments); | 756 return irBuilder.buildCallInvocation(receiver, call, arguments); |
746 } else if (selector.isGetter) { | 757 } else if (selector.isGetter) { |
747 // We are reading a static field or invoking a static getter. | 758 // We are reading a static field or invoking a static getter. |
748 return irBuilder.buildStaticGet(element, selector); | 759 return irBuilder.buildStaticGet(element, selector); |
749 } else { | 760 } else { |
750 // We are invoking a static method. | 761 // We are invoking a static method. |
751 assert(selector.isCall); | 762 assert(selector.isCall); |
752 assert(element is FunctionElement); | 763 assert(element is FunctionElement); |
753 List<ir.Primitive> arguments = node.arguments.mapToList(visit); | 764 List<ir.Primitive> arguments = node.arguments.mapToList(visit); |
754 arguments = normalizeStaticArguments(selector, element, arguments); | 765 arguments = normalizeStaticArguments(selector, element, arguments); |
755 return irBuilder.buildStaticInvocation(element, selector, arguments); | 766 return irBuilder.buildStaticInvocation(element, selector, arguments, |
767 sourceInformation: sourceInformationBuilder.buildCall(node)); | |
756 } | 768 } |
757 } | 769 } |
758 | 770 |
759 ir.Primitive visitSuperSend(ast.Send node) { | 771 ir.Primitive visitSuperSend(ast.Send node) { |
760 assert(irBuilder.isOpen); | 772 assert(irBuilder.isOpen); |
761 Selector selector = elements.getSelector(node); | 773 Selector selector = elements.getSelector(node); |
762 Element target = elements[node]; | 774 Element target = elements[node]; |
763 | 775 |
764 if (selector.isCall && (target.isGetter || target.isField)) { | 776 if (selector.isCall && (target.isGetter || target.isField)) { |
765 // We are invoking a field or getter as if it was a method, e.g: | 777 // We are invoking a field or getter as if it was a method, e.g: |
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1071 currentFunction = oldFunction; | 1083 currentFunction = oldFunction; |
1072 } | 1084 } |
1073 } | 1085 } |
1074 | 1086 |
1075 /// IR builder specific to the Dart backend, coupled to the [DartIrBuilder]. | 1087 /// IR builder specific to the Dart backend, coupled to the [DartIrBuilder]. |
1076 class DartIrBuilderVisitor extends IrBuilderVisitor { | 1088 class DartIrBuilderVisitor extends IrBuilderVisitor { |
1077 /// Promote the type of [irBuilder] to [DartIrBuilder]. | 1089 /// Promote the type of [irBuilder] to [DartIrBuilder]. |
1078 DartIrBuilder get irBuilder => super.irBuilder; | 1090 DartIrBuilder get irBuilder => super.irBuilder; |
1079 | 1091 |
1080 DartIrBuilderVisitor(TreeElements elements, | 1092 DartIrBuilderVisitor(TreeElements elements, |
1081 Compiler compiler, | 1093 Compiler compiler, |
1082 SourceFile sourceFile) | 1094 SourceInformationBuilder sourceInformationBuilder) |
1083 : super(elements, compiler, sourceFile); | 1095 : super(elements, compiler, sourceInformationBuilder); |
1084 | 1096 |
1085 DartIrBuilder makeIRBuilder(ast.Node node, ExecutableElement element) { | 1097 DartIrBuilder makeIRBuilder(ast.Node node, ExecutableElement element) { |
1086 DartCapturedVariables closures = new DartCapturedVariables(elements); | 1098 DartCapturedVariables closures = new DartCapturedVariables(elements); |
1087 if (!element.isSynthesized) { | 1099 if (!element.isSynthesized) { |
1088 closures.visit(node); | 1100 closures.visit(node); |
1089 } | 1101 } |
1090 return new DartIrBuilder(compiler.backend.constantSystem, | 1102 return new DartIrBuilder(compiler.backend.constantSystem, |
1091 element, | 1103 element, |
1092 closures); | 1104 closures); |
1093 } | 1105 } |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1198 /// Will be initialized upon entering the body of a function. | 1210 /// Will be initialized upon entering the body of a function. |
1199 /// It is computed by the [ClosureTranslator]. | 1211 /// It is computed by the [ClosureTranslator]. |
1200 ClosureClassMap closureMap; | 1212 ClosureClassMap closureMap; |
1201 | 1213 |
1202 /// During construction of a constructor factory, [fieldValues] maps fields | 1214 /// During construction of a constructor factory, [fieldValues] maps fields |
1203 /// to the primitive containing their initial value. | 1215 /// to the primitive containing their initial value. |
1204 Map<FieldElement, ir.Primitive> fieldValues = <FieldElement, ir.Primitive>{}; | 1216 Map<FieldElement, ir.Primitive> fieldValues = <FieldElement, ir.Primitive>{}; |
1205 | 1217 |
1206 JsIrBuilderVisitor(TreeElements elements, | 1218 JsIrBuilderVisitor(TreeElements elements, |
1207 Compiler compiler, | 1219 Compiler compiler, |
1208 SourceFile sourceFile) | 1220 SourceInformationBuilder sourceInformationBuilder) |
1209 : super(elements, compiler, sourceFile); | 1221 : super(elements, compiler, sourceInformationBuilder); |
1210 | 1222 |
1211 /// Builds the IR for creating an instance of the closure class corresponding | 1223 /// Builds the IR for creating an instance of the closure class corresponding |
1212 /// to the given nested function. | 1224 /// to the given nested function. |
1213 ClosureClassElement makeSubFunction(ast.FunctionExpression node) { | 1225 ClosureClassElement makeSubFunction(ast.FunctionExpression node) { |
1214 ClosureClassMap innerMap = | 1226 ClosureClassMap innerMap = |
1215 compiler.closureToClassMapper.getMappingForNestedFunction(node); | 1227 compiler.closureToClassMapper.getMappingForNestedFunction(node); |
1216 ClosureClassElement closureClass = innerMap.closureClassElement; | 1228 ClosureClassElement closureClass = innerMap.closureClassElement; |
1217 return closureClass; | 1229 return closureClass; |
1218 } | 1230 } |
1219 | 1231 |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1310 } | 1322 } |
1311 | 1323 |
1312 /// Builds the IR for an [expression] taken from a different [context]. | 1324 /// Builds the IR for an [expression] taken from a different [context]. |
1313 /// | 1325 /// |
1314 /// Such expressions need to be compiled with a different [sourceFile] and | 1326 /// Such expressions need to be compiled with a different [sourceFile] and |
1315 /// [elements] mapping. | 1327 /// [elements] mapping. |
1316 ir.Primitive inlineExpression(AstElement context, ast.Expression expression) { | 1328 ir.Primitive inlineExpression(AstElement context, ast.Expression expression) { |
1317 JsIrBuilderVisitor visitor = new JsIrBuilderVisitor( | 1329 JsIrBuilderVisitor visitor = new JsIrBuilderVisitor( |
1318 context.resolvedAst.elements, | 1330 context.resolvedAst.elements, |
1319 compiler, | 1331 compiler, |
1320 elementSourceFile(context)); | 1332 sourceInformationBuilder.forContext(context)); |
1321 return visitor.withBuilder(irBuilder, () => visitor.visit(expression)); | 1333 return visitor.withBuilder(irBuilder, () => visitor.visit(expression)); |
1322 } | 1334 } |
1323 | 1335 |
1324 /// Builds the IR for a constant taken from a different [context]. | 1336 /// Builds the IR for a constant taken from a different [context]. |
1325 /// | 1337 /// |
1326 /// Such constants need to be compiled with a different [sourceFile] and | 1338 /// Such constants need to be compiled with a different [sourceFile] and |
1327 /// [elements] mapping. | 1339 /// [elements] mapping. |
1328 ir.Primitive inlineConstant(AstElement context, ast.Expression exp) { | 1340 ir.Primitive inlineConstant(AstElement context, ast.Expression exp) { |
1329 JsIrBuilderVisitor visitor = new JsIrBuilderVisitor( | 1341 JsIrBuilderVisitor visitor = new JsIrBuilderVisitor( |
1330 context.resolvedAst.elements, | 1342 context.resolvedAst.elements, |
1331 compiler, | 1343 compiler, |
1332 elementSourceFile(context)); | 1344 sourceInformationBuilder.forContext(context)); |
1333 return visitor.withBuilder(irBuilder, () => visitor.translateConstant(exp)); | 1345 return visitor.withBuilder(irBuilder, () => visitor.translateConstant(exp)); |
1334 } | 1346 } |
1335 | 1347 |
1336 /// Builds the IR for a given constructor. | 1348 /// Builds the IR for a given constructor. |
1337 /// | 1349 /// |
1338 /// 1. Evaluates all own or inherited field initializers. | 1350 /// 1. Evaluates all own or inherited field initializers. |
1339 /// 2. Creates the object and assigns its fields. | 1351 /// 2. Creates the object and assigns its fields. |
1340 /// 3. Calls constructor body and super constructor bodies. | 1352 /// 3. Calls constructor body and super constructor bodies. |
1341 /// 4. Returns the created object. | 1353 /// 4. Returns the created object. |
1342 ir.FunctionDefinition buildConstructor(ConstructorElement constructor) { | 1354 ir.FunctionDefinition buildConstructor(ConstructorElement constructor) { |
(...skipping 361 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1704 for (int i=0; i < selector.positionalArgumentCount; i++) { | 1716 for (int i=0; i < selector.positionalArgumentCount; i++) { |
1705 result.add(arguments[i]); | 1717 result.add(arguments[i]); |
1706 } | 1718 } |
1707 for (String argName in selector.getOrderedNamedArguments()) { | 1719 for (String argName in selector.getOrderedNamedArguments()) { |
1708 int nameIndex = selector.namedArguments.indexOf(argName); | 1720 int nameIndex = selector.namedArguments.indexOf(argName); |
1709 int translatedIndex = selector.positionalArgumentCount + nameIndex; | 1721 int translatedIndex = selector.positionalArgumentCount + nameIndex; |
1710 result.add(arguments[translatedIndex]); | 1722 result.add(arguments[translatedIndex]); |
1711 } | 1723 } |
1712 return result; | 1724 return result; |
1713 } | 1725 } |
1714 | |
1715 } | 1726 } |
1716 | 1727 |
1728 /// Interface for generating [SourceInformation] for the CPS. | |
1729 class SourceInformationBuilder { | |
1730 const SourceInformationBuilder(); | |
1731 | |
1732 /// Create a [SourceInformationBuilder] for [element]. | |
1733 SourceInformationBuilder forContext(AstElement element) => this; | |
1734 | |
1735 /// Generate [SourceInformation] for the read access in [node]. | |
1736 SourceInformation buildGet(ast.Node node) => null; | |
floitsch
2015/02/24 19:58:12
The "build" reads redundant at the use-sites, but
| |
1737 | |
1738 /// Generate [SourceInformation] for the invocation in [node]. | |
1739 SourceInformation buildCall(ast.Node node) => null; | |
1740 } | |
1741 | |
1742 /// [SourceInformationBuilder] that generates [PositionSourceInformation]. | |
1743 class PositionSourceInformationBuilder implements SourceInformationBuilder { | |
1744 final SourceFile sourceFile; | |
1745 final String name; | |
1746 | |
1747 PositionSourceInformationBuilder(AstElement element) | |
1748 : sourceFile = element.compilationUnit.script.file, | |
1749 name = element.name; | |
1750 | |
1751 @override | |
1752 SourceInformation buildGet(ast.Node node) { | |
1753 return new PositionSourceInformation( | |
1754 new TokenSourceLocation(sourceFile, node.getBeginToken(), name)); | |
1755 } | |
1756 | |
1757 @override | |
1758 SourceInformation buildCall(ast.Node node) { | |
1759 return new PositionSourceInformation( | |
1760 new TokenSourceLocation(sourceFile, node.getBeginToken(), name)); | |
1761 } | |
1762 | |
1763 @override | |
1764 SourceInformationBuilder forContext(AstElement element) { | |
floitsch
2015/02/24 19:58:12
I'm not sure I like this. Is this just to avoid ha
Johnni Winther
2015/02/26 10:21:34
It is made to handle the choice of strategy at one
| |
1765 return new PositionSourceInformationBuilder(element); | |
1766 } | |
1767 } | |
OLD | NEW |