OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library ir_builder; | |
6 | |
7 import 'ir_nodes.dart'; | |
8 import '../elements/elements.dart'; | |
9 import '../dart2jslib.dart'; | |
10 import '../source_file.dart'; | |
11 import '../tree/tree.dart'; | |
12 import '../scanner/scannerlib.dart' show Token; | |
13 import '../js_backend/js_backend.dart' show JavaScriptBackend; | |
14 | |
15 /** | |
16 * This task iterates through all resolved elements and builds [IrNode]s. The | |
17 * nodes are stored in the [nodes] map and accessible through [Element.irNode] | |
18 * and [Element.hasIrNode]. | |
19 * | |
20 * The functionality of the IrNodes is added gradually, therefore elements might | |
21 * have an IR or not, depending on the language features that are used. For | |
22 * elements that do have an IR, the tree [Node]s and the [Token]s are not used | |
23 * in the rest of the compilation. This is ensured by setting the element's | |
24 * cached tree to [:null:] and also breaking the token stream to crash future | |
25 * attempts to parse. | |
26 * | |
27 * The type inferrer works on either IR nodes or tree nodes. The IR nodes are | |
28 * then translated into the SSA form for optimizations and code generation. | |
29 * Long-term, once the IR supports the full language, the backend can be | |
30 * re-implemented to work directly on the IR. | |
31 */ | |
32 class IrBuilderTask extends CompilerTask { | |
33 final Map<Element, IrNode> nodes = new Map<Element, IrNode>(); | |
34 | |
35 IrBuilderTask(Compiler compiler) : super(compiler); | |
36 | |
37 String get name => 'IR builder'; | |
38 | |
39 void buildNodes() { | |
40 // TODO(lry): support checked-mode checks. | |
41 if (compiler.enableTypeAssertions) return; | |
42 | |
43 Map<Element, TreeElements> resolved = | |
44 compiler.enqueuer.resolution.resolvedElements; | |
45 resolved.forEach((Element element, TreeElements elementsMapping) { | |
46 if (canBuild(element)) { | |
47 element = element.implementation; | |
48 | |
49 SourceFile sourceFile = elementSourceFile(element); | |
50 IrNodeBuilderVisitor visitor = | |
51 new IrNodeBuilderVisitor(elementsMapping, compiler, sourceFile); | |
52 IrNode irNode; | |
53 ElementKind kind = element.kind; | |
54 if (kind == ElementKind.GENERATIVE_CONSTRUCTOR) { | |
55 // TODO(lry): build ir for constructors. | |
56 } else if (kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY || | |
57 kind == ElementKind.FUNCTION || | |
58 kind == ElementKind.GETTER || | |
59 kind == ElementKind.SETTER) { | |
60 irNode = visitor.buildMethod(element); | |
61 } else if (kind == ElementKind.FIELD) { | |
62 // TODO(lry): build ir for lazy initializers of static fields. | |
63 } else { | |
64 compiler.internalErrorOnElement(element, | |
65 'unexpected element kind $kind'); | |
66 } | |
67 | |
68 if (irNode != null) { | |
69 nodes[element] = irNode; | |
70 unlinkTreeAndToken(element); | |
71 } | |
72 } | |
73 }); | |
74 } | |
75 | |
76 bool canBuild(Element element) { | |
77 FunctionElement function = element.asFunctionElement(); | |
78 if (function == null) return false; | |
79 | |
80 // TODO(lry): support functions with parameters. | |
81 FunctionSignature signature = function.computeSignature(compiler); | |
82 if (signature.parameterCount > 0) return false; | |
83 | |
84 if (compiler.backend is JavaScriptBackend) { | |
85 JavaScriptBackend backend = compiler.backend; | |
86 if (backend.isInterceptedMethod(element)) return false; | |
karlklose
2013/11/22 08:01:06
Consider storing compiler.backend in a field. (of
lukas
2013/11/22 11:51:34
Cleaned up as discussed offline. For now we abort
| |
87 } | |
88 | |
89 return true; | |
90 } | |
91 | |
92 void unlinkTreeAndToken(element) { | |
93 // TODO(lry): Make the dart backend generate code from IR nodes. | |
94 if (compiler.backend is JavaScriptBackend) { | |
95 element.beginToken.next = null; | |
96 element.cachedNode = null; | |
97 } | |
98 } | |
99 | |
100 SourceFile elementSourceFile(Element element) { | |
101 if (element is FunctionElement) { | |
102 FunctionElement functionElement = element; | |
103 if (functionElement.patch != null) element = functionElement.patch; | |
104 } | |
105 return element.getCompilationUnit().script.file; | |
106 } | |
107 } | |
108 | |
109 /** | |
110 * A tree visitor that builds [IrNodes]. The visit methods add statements using | |
111 * to the [builder] and return the last added statement for trees that represent | |
112 * an expression. | |
113 */ | |
114 class IrNodeBuilderVisitor extends ResolvedVisitor<IrNode> { | |
115 final SourceFile sourceFile; | |
116 | |
117 IrNodeBuilderVisitor( | |
118 TreeElements elements, | |
119 Compiler compiler, | |
120 this.sourceFile) | |
121 : super(elements, compiler); | |
122 | |
123 IrBuilder builder; | |
124 | |
125 /** | |
126 * Builds the [IrFunction] for a function element. In case the function | |
127 * uses features that cannot be expressed in the IR, this function returns | |
128 * [:null:]. | |
129 */ | |
130 IrFunction buildMethod(FunctionElement functionElement) { | |
131 return nullIfGiveup(() => buildMethodInternal(functionElement)); | |
132 } | |
133 | |
134 IrFunction buildMethodInternal(FunctionElement functionElement) { | |
135 assert(invariant(functionElement, functionElement.isImplementation)); | |
136 FunctionExpression function = functionElement.parseNode(compiler); | |
137 assert(function != null); | |
138 assert(!function.modifiers.isExternal()); | |
139 assert(elements[function] != null); | |
140 | |
141 int endPosition = function.getEndToken().charOffset; | |
142 int namePosition = elements[function].position().charOffset; | |
143 IrFunction result = new IrFunction( | |
144 nodePosition(function), endPosition, namePosition, <IrNode>[]); | |
145 builder = new IrBuilder(this); | |
146 builder.enterBlock(); | |
147 if (function.hasBody()) { | |
148 function.body.accept(this); | |
149 ensureReturn(function); | |
150 result.statements | |
151 ..addAll(builder.constants.values) | |
152 ..addAll(builder.statements); | |
153 } | |
154 builder.exitBlock(); | |
155 return result; | |
156 } | |
157 | |
158 ConstantSystem get constantSystem => compiler.backend.constantSystem; | |
159 | |
160 /* int | PositionWithIdentifierName */ nodePosition(Node node) { | |
161 Token token = node.getBeginToken(); | |
162 if (token.isIdentifier()) { | |
163 return new PositionWithIdentifierName(token.charOffset, token.value); | |
164 } else { | |
165 return token.charOffset; | |
166 } | |
167 } | |
168 | |
169 /** | |
170 * Add an explicit [:return null:] for functions that don't have a return | |
171 * statement on each branch. This includes functions with an empty body, | |
172 * such as [:foo(){ }:]. | |
173 */ | |
174 void ensureReturn(FunctionExpression node) { | |
175 if (builder.returnOnAllBranches) return; | |
176 IrConstant nullValue = | |
177 builder.addConstant(constantSystem.createNull(), node); | |
178 builder.addStatement(new IrReturn(nodePosition(node), nullValue)); | |
179 } | |
180 | |
181 IrNode visitBlock(Block node) { | |
182 for (Node n in node.statements.nodes) { | |
183 n.accept(this); | |
184 } | |
185 } | |
186 | |
187 IrNode visitReturn(Return node) { | |
188 IrNode value; | |
189 if (node.expression == null) { | |
190 if (node.beginToken.value == 'native') return giveup(); | |
191 value = builder.addConstant(constantSystem.createNull(), node); | |
192 } else { | |
193 value = node.expression.accept(this); | |
194 } | |
195 builder.branchReturns(); | |
196 return builder.addStatement(new IrReturn(nodePosition(node), value)); | |
197 } | |
198 | |
199 IrConstant visitLiteralBool(LiteralBool node) => | |
200 builder.addConstant(constantSystem.createBool(node.value), node); | |
201 | |
202 IrConstant visitLiteralDouble(LiteralDouble node) => | |
203 builder.addConstant(constantSystem.createDouble(node.value), node); | |
204 | |
205 IrConstant visitLiteralInt(LiteralInt node) => | |
206 builder.addConstant(constantSystem.createInt(node.value), node); | |
207 | |
208 IrConstant visitLiteralString(LiteralString node) => | |
209 builder.addConstant( | |
210 constantSystem.createString(node.dartString, node), node); | |
211 | |
212 IrConstant visitLiteralNull(LiteralNull node) => | |
213 builder.addConstant(constantSystem.createNull(), node); | |
214 | |
215 // TODO(lry): other literals. | |
216 // IrNode visitLiteralList(LiteralList node) => visitExpression(node); | |
217 // IrNode visitLiteralMap(LiteralMap node) => visitExpression(node); | |
218 // IrNode visitLiteralMapEntry(LiteralMapEntry node) => visitNode(node); | |
219 // IrNode visitLiteralSymbol(LiteralSymbol node) => visitExpression(node); | |
220 | |
221 IrNode visitAssert(Send node) { | |
222 return giveup(); | |
223 } | |
224 | |
225 IrNode visitClosureSend(Send node) { | |
226 return giveup(); | |
227 } | |
228 | |
229 IrNode visitDynamicSend(Send node) { | |
230 return giveup(); | |
231 } | |
232 | |
233 IrNode visitGetterSend(Send node) { | |
234 return giveup(); | |
235 } | |
236 | |
237 IrNode visitOperatorSend(Send node) { | |
238 return giveup(); | |
239 } | |
240 | |
241 IrNode visitStaticSend(Send node) { | |
242 return giveup(); | |
243 } | |
244 | |
245 IrNode visitSuperSend(Send node) { | |
246 return giveup(); | |
247 } | |
248 | |
249 IrNode visitTypeReferenceSend(Send node) { | |
250 return giveup(); | |
251 } | |
252 | |
253 static final String ABORT_IRNODE_BUILDER = "IrNode builder aborted"; | |
254 | |
255 IrNode giveup() => throw ABORT_IRNODE_BUILDER; | |
256 | |
257 IrNode nullIfGiveup(IrNode action()) { | |
258 try { | |
259 return action(); | |
260 } catch(e) { | |
261 if (e == ABORT_IRNODE_BUILDER) return null; | |
262 rethrow; | |
263 } | |
264 } | |
265 | |
266 void internalError(String reason, {Node node}) { | |
267 giveup(); | |
268 } | |
269 } | |
270 | |
271 class IrBuilder { | |
272 final IrNodeBuilderVisitor visitor; | |
273 IrBuilder(this.visitor); | |
274 | |
275 // Statements lists for nested blocks. | |
276 List<List<IrNode>> statementsList = <List<IrNode>>[]; | |
277 List<IrNode> get statements => statementsList.last; | |
278 | |
279 // TODO(lry): Need to fix this once we actually have branching. Probably | |
280 // this will be handled by the LocalsHandler, not here. | |
281 List<bool> returnOnAllBranchesList = <bool>[]; | |
282 bool get returnOnAllBranches => returnOnAllBranchesList.last; | |
283 void branchReturns() { | |
284 returnOnAllBranchesList[returnOnAllBranchesList.length - 1] = true; | |
285 } | |
286 | |
287 Map<Constant, IrConstant> constants = <Constant, IrConstant>{}; | |
288 | |
289 IrConstant addConstant(Constant value, Node node) { | |
290 return constants.putIfAbsent( | |
291 value, () => new IrConstant(visitor.nodePosition(node), value)); | |
292 } | |
293 | |
294 IrNode addStatement(IrNode statement) { | |
295 statements.add(statement); | |
296 return statement; | |
297 } | |
298 | |
299 void enterBlock() { | |
300 statementsList.add(<IrNode>[]); | |
301 returnOnAllBranchesList.add(false); | |
302 } | |
303 | |
304 void exitBlock() { | |
305 statementsList.removeLast(); | |
306 returnOnAllBranchesList.removeLast(); | |
307 } | |
308 } | |
OLD | NEW |