OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, 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 class Interceptors { | |
6 Compiler compiler; | |
7 Interceptors(Compiler this.compiler); | |
8 | |
9 SourceString mapOperatorToMethodName(Operator op) { | |
10 String name = op.source.stringValue; | |
11 if (name === '+') return const SourceString('add'); | |
12 if (name === '-') return const SourceString('sub'); | |
13 if (name === '*') return const SourceString('mul'); | |
14 if (name === '/') return const SourceString('div'); | |
15 if (name === '~/') return const SourceString('tdiv'); | |
16 if (name === '%') return const SourceString('mod'); | |
17 if (name === '<<') return const SourceString('shl'); | |
18 if (name === '>>') return const SourceString('shr'); | |
19 if (name === '|') return const SourceString('or'); | |
20 if (name === '&') return const SourceString('and'); | |
21 if (name === '^') return const SourceString('xor'); | |
22 if (name === '<') return const SourceString('lt'); | |
23 if (name === '<=') return const SourceString('le'); | |
24 if (name === '>') return const SourceString('gt'); | |
25 if (name === '>=') return const SourceString('ge'); | |
26 if (name === '==') return const SourceString('eq'); | |
27 if (name === '!=') return const SourceString('eq'); | |
28 if (name === '===') return const SourceString('eqq'); | |
29 if (name === '!==') return const SourceString('eqq'); | |
30 if (name === '+=') return const SourceString('add'); | |
31 if (name === '-=') return const SourceString('sub'); | |
32 if (name === '*=') return const SourceString('mul'); | |
33 if (name === '/=') return const SourceString('div'); | |
34 if (name === '~/=') return const SourceString('tdiv'); | |
35 if (name === '%=') return const SourceString('mod'); | |
36 if (name === '<<=') return const SourceString('shl'); | |
37 if (name === '>>=') return const SourceString('shr'); | |
38 if (name === '|=') return const SourceString('or'); | |
39 if (name === '&=') return const SourceString('and'); | |
40 if (name === '^=') return const SourceString('xor'); | |
41 if (name === '++') return const SourceString('add'); | |
42 if (name === '--') return const SourceString('sub'); | |
43 compiler.unimplemented('Unknown operator', node: op); | |
44 } | |
45 | |
46 Element getStaticInterceptor(SourceString name, int parameters) { | |
47 String mangledName = "builtin\$${name.slowToString()}\$${parameters}"; | |
48 Element result = compiler.findHelper(new SourceString(mangledName)); | |
49 return result; | |
50 } | |
51 | |
52 Element getStaticGetInterceptor(SourceString name) { | |
53 String mangledName = "builtin\$get\$${name.slowToString()}"; | |
54 Element result = compiler.findHelper(new SourceString(mangledName)); | |
55 return result; | |
56 } | |
57 | |
58 Element getStaticSetInterceptor(SourceString name) { | |
59 String mangledName = "builtin\$set\$${name.slowToString()}"; | |
60 Element result = compiler.findHelper(new SourceString(mangledName)); | |
61 return result; | |
62 } | |
63 | |
64 Element getOperatorInterceptor(Operator op) { | |
65 SourceString name = mapOperatorToMethodName(op); | |
66 Element result = compiler.findHelper(name); | |
67 return result; | |
68 } | |
69 | |
70 Element getPrefixOperatorInterceptor(Operator op) { | |
71 String name = op.source.stringValue; | |
72 if (name === '~') { | |
73 return compiler.findHelper(const SourceString('not')); | |
74 } | |
75 if (name === '-') { | |
76 return compiler.findHelper(const SourceString('neg')); | |
77 } | |
78 compiler.unimplemented('Unknown operator', node: op); | |
79 } | |
80 | |
81 Element getIndexInterceptor() { | |
82 return compiler.findHelper(const SourceString('index')); | |
83 } | |
84 | |
85 Element getIndexAssignmentInterceptor() { | |
86 return compiler.findHelper(const SourceString('indexSet')); | |
87 } | |
88 | |
89 Element getEqualsNullInterceptor() { | |
90 return compiler.findHelper(const SourceString('eqNull')); | |
91 } | |
92 | |
93 Element getExceptionUnwrapper() { | |
94 return compiler.findHelper(const SourceString('unwrapException')); | |
95 } | |
96 | |
97 Element getClosureConverter() { | |
98 return compiler.findHelper(const SourceString('convertDartClosureToJS')); | |
99 } | |
100 | |
101 Element getTraceFromException() { | |
102 return compiler.findHelper(const SourceString('getTraceFromException')); | |
103 } | |
104 | |
105 Element getEqualsInterceptor() { | |
106 return compiler.findHelper(const SourceString('eq')); | |
107 } | |
108 | |
109 Element getMapMaker() { | |
110 return compiler.findHelper(const SourceString('makeLiteralMap')); | |
111 } | |
112 } | |
113 | |
114 class SsaBuilderTask extends CompilerTask { | |
115 final Interceptors interceptors; | |
116 final Map<Node, ClosureData> closureDataCache; | |
117 | |
118 String get name() => 'SSA builder'; | |
119 | |
120 SsaBuilderTask(Compiler compiler) | |
121 : interceptors = new Interceptors(compiler), | |
122 closureDataCache = new HashMap<Node, ClosureData>(), | |
123 super(compiler); | |
124 | |
125 HGraph build(WorkItem work) { | |
126 return measure(() { | |
127 FunctionElement element = work.element; | |
128 HInstruction.idCounter = 0; | |
129 SsaBuilder builder = new SsaBuilder(compiler, work); | |
130 HGraph graph; | |
131 switch (element.kind) { | |
132 case ElementKind.GENERATIVE_CONSTRUCTOR: | |
133 graph = compileConstructor(builder, work); | |
134 break; | |
135 case ElementKind.GENERATIVE_CONSTRUCTOR_BODY: | |
136 case ElementKind.FUNCTION: | |
137 case ElementKind.GETTER: | |
138 case ElementKind.SETTER: | |
139 graph = builder.buildMethod(work.element); | |
140 break; | |
141 } | |
142 assert(graph.isValid()); | |
143 if (compiler.tracer.enabled) { | |
144 String name; | |
145 if (element.enclosingElement !== null && | |
146 element.enclosingElement.kind == ElementKind.CLASS) { | |
147 String className = element.enclosingElement.name.slowToString(); | |
148 String memberName = element.name.slowToString(); | |
149 name = "$className.$memberName"; | |
150 if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { | |
151 name = "$name (body)"; | |
152 } | |
153 } else { | |
154 name = "${element.name.slowToString()}"; | |
155 } | |
156 compiler.tracer.traceCompilation(name); | |
157 compiler.tracer.traceGraph('builder', graph); | |
158 } | |
159 return graph; | |
160 }); | |
161 } | |
162 | |
163 HGraph compileConstructor(SsaBuilder builder, WorkItem work) { | |
164 // The body of the constructor will be generated in a separate function. | |
165 final ClassElement classElement = work.element.enclosingElement; | |
166 return builder.buildFactory(classElement, work.element); | |
167 } | |
168 } | |
169 | |
170 /** | |
171 * Keeps track of locals (including parameters and phis) when building. The | |
172 * 'this' reference is treated as parameter and hence handled by this class, | |
173 * too. | |
174 */ | |
175 class LocalsHandler { | |
176 /** | |
177 * The values of locals that can be directly accessed (without redirections | |
178 * to boxes or closure-fields). | |
179 */ | |
180 Map<Element, HInstruction> directLocals; | |
181 Map<Element, Element> redirectionMapping; | |
182 SsaBuilder builder; | |
183 ClosureData closureData; | |
184 | |
185 LocalsHandler(this.builder) | |
186 : directLocals = new Map<Element, HInstruction>(), | |
187 redirectionMapping = new Map<Element, Element>(); | |
188 | |
189 /** | |
190 * Creates a new [LocalsHandler] based on [other]. We only need to | |
191 * copy the [directLocals], since the other fields can be shared | |
192 * throughout the AST visit. | |
193 */ | |
194 LocalsHandler.from(LocalsHandler other) | |
195 : directLocals = new Map<Element, HInstruction>.from(other.directLocals), | |
196 redirectionMapping = other.redirectionMapping, | |
197 builder = other.builder, | |
198 closureData = other.closureData; | |
199 | |
200 /** | |
201 * Redirects accesses from element [from] to element [to]. The [to] element | |
202 * must be a boxed variable or a variable that is stored in a closure-field. | |
203 */ | |
204 void redirectElement(Element from, Element to) { | |
205 assert(redirectionMapping[from] === null); | |
206 redirectionMapping[from] = to; | |
207 assert(isStoredInClosureField(from) || isBoxed(from)); | |
208 } | |
209 | |
210 HInstruction createBox() { | |
211 // TODO(floitsch): Clean up this hack. Should we create a box-object by | |
212 // just creating an empty object literal? | |
213 HInstruction box = new HForeign(const LiteralDartString("{}"), | |
214 const LiteralDartString('Object'), | |
215 <HInstruction>[]); | |
216 builder.add(box); | |
217 return box; | |
218 } | |
219 | |
220 /** | |
221 * If the scope (function or loop) [node] has captured variables then this | |
222 * method creates a box and sets up the redirections. | |
223 */ | |
224 void enterScope(Node node) { | |
225 // See if any variable in the top-scope of the function is captured. If yes | |
226 // we need to create a box-object. | |
227 ClosureScope scopeData = closureData.capturingScopes[node]; | |
228 if (scopeData !== null) { | |
229 // The scope has captured variables. Create a box. | |
230 // TODO(floitsch): Clean up this hack. Should we create a box-object by | |
231 // just creating an empty object literal? | |
232 HInstruction box = createBox(); | |
233 // Add the box to the known locals. | |
234 directLocals[scopeData.boxElement] = box; | |
235 // Make sure that accesses to the boxed locals go into the box. We also | |
236 // need to make sure that parameters are copied into the box if necessary. | |
237 scopeData.capturedVariableMapping.forEach((Element from, Element to) { | |
238 // The [from] can only be a parameter for function-scopes and not | |
239 // loop scopes. | |
240 if (from.kind == ElementKind.PARAMETER) { | |
241 // Store the captured parameter in the box. Get the current value | |
242 // before we put the redirection in place. | |
243 HInstruction instruction = readLocal(from); | |
244 redirectElement(from, to); | |
245 // Now that the redirection is set up, the update to the local will | |
246 // write the parameter value into the box. | |
247 updateLocal(from, instruction); | |
248 } else { | |
249 redirectElement(from, to); | |
250 } | |
251 }); | |
252 } | |
253 } | |
254 | |
255 /** | |
256 * Replaces the current box with a new box and copies over the given list | |
257 * of elements from the old box into the new box. | |
258 */ | |
259 void updateCaptureBox(Element boxElement, List<Element> toBeCopiedElements) { | |
260 // Create a new box and copy over the values from the old box into the | |
261 // new one. | |
262 HInstruction oldBox = readLocal(boxElement); | |
263 HInstruction newBox = createBox(); | |
264 for (Element boxedVariable in toBeCopiedElements) { | |
265 // [readLocal] uses the [boxElement] to find its box. By replacing it | |
266 // behind its back we can still get to the old values. | |
267 updateLocal(boxElement, oldBox); | |
268 HInstruction oldValue = readLocal(boxedVariable); | |
269 updateLocal(boxElement, newBox); | |
270 updateLocal(boxedVariable, oldValue); | |
271 } | |
272 updateLocal(boxElement, newBox); | |
273 } | |
274 | |
275 void startFunction(FunctionElement function, | |
276 FunctionExpression node) { | |
277 | |
278 ClosureTranslator translator = | |
279 new ClosureTranslator(builder.compiler, builder.elements); | |
280 closureData = translator.translate(node); | |
281 | |
282 FunctionParameters params = function.computeParameters(builder.compiler); | |
283 params.forEachParameter((Element element) { | |
284 HParameterValue parameter = new HParameterValue(element); | |
285 builder.add(parameter); | |
286 directLocals[element] = parameter; | |
287 }); | |
288 if (closureData.thisElement !== null) { | |
289 // Once closures have been mapped to classes their instance members might | |
290 // not have any thisElement if the closure was created inside a static | |
291 // context. | |
292 assert(function.isInstanceMember() || function.isGenerativeConstructor()); | |
293 // We have to introduce 'this' before we enter the scope, since it might | |
294 // need to be copied into a box (if it is captured). This is similar | |
295 // to all other parameters that are introduced. | |
296 HInstruction thisInstruction = new HThis(); | |
297 builder.add(thisInstruction); | |
298 directLocals[closureData.thisElement] = thisInstruction; | |
299 } | |
300 | |
301 enterScope(node); | |
302 | |
303 // If the freeVariableMapping is not empty, then this function was a | |
304 // nested closure that captures variables. Redirect the captured | |
305 // variables to fields in the closure. | |
306 closureData.freeVariableMapping.forEach((Element from, Element to) { | |
307 redirectElement(from, to); | |
308 }); | |
309 if (closureData.isClosure()) { | |
310 // Inside closure redirect references to itself to [:this:]. | |
311 HInstruction thisInstruction = new HThis(); | |
312 builder.add(thisInstruction); | |
313 updateLocal(closureData.closureElement, thisInstruction); | |
314 } | |
315 } | |
316 | |
317 bool hasValueForDirectLocal(Element element) { | |
318 assert(element !== null); | |
319 assert(isAccessedDirectly(element)); | |
320 return directLocals[element] !== null; | |
321 } | |
322 | |
323 /** | |
324 * Returns true if the local can be accessed directly. Boxed variables or | |
325 * captured variables that are stored in the closure-field return [false]. | |
326 */ | |
327 bool isAccessedDirectly(Element element) { | |
328 assert(element !== null); | |
329 return redirectionMapping[element] === null | |
330 && !closureData.usedVariablesInTry.contains(element); | |
331 } | |
332 | |
333 bool isStoredInClosureField(Element element) { | |
334 assert(element !== null); | |
335 if (isAccessedDirectly(element)) return false; | |
336 Element redirectTarget = redirectionMapping[element]; | |
337 if (redirectTarget == null) return false; | |
338 if (redirectTarget.enclosingElement.kind == ElementKind.CLASS) { | |
339 assert(redirectTarget is ClosureFieldElement); | |
340 return true; | |
341 } | |
342 return false; | |
343 } | |
344 | |
345 bool isBoxed(Element element) { | |
346 if (isAccessedDirectly(element)) return false; | |
347 if (isStoredInClosureField(element)) return false; | |
348 return redirectionMapping[element] !== null; | |
349 } | |
350 | |
351 bool isUsedInTry(Element element) { | |
352 return closureData.usedVariablesInTry.contains(element); | |
353 } | |
354 | |
355 /** | |
356 * Returns an [HInstruction] for the given element. If the element is | |
357 * boxed or stored in a closure then the method generates code to retrieve | |
358 * the value. | |
359 */ | |
360 HInstruction readLocal(Element element) { | |
361 if (isAccessedDirectly(element)) { | |
362 if (directLocals[element] == null) { | |
363 builder.compiler.internalError("Cannot find value $element", | |
364 element: element); | |
365 } | |
366 return directLocals[element]; | |
367 } else if (isStoredInClosureField(element)) { | |
368 Element redirect = redirectionMapping[element]; | |
369 // We must not use the [LocalsHandler.readThis()] since that could | |
370 // point to a captured this which would be stored in a closure-field | |
371 // itself. | |
372 HInstruction receiver = new HThis(); | |
373 builder.add(receiver); | |
374 HInstruction fieldGet = new HFieldGet(redirect, receiver); | |
375 builder.add(fieldGet); | |
376 return fieldGet; | |
377 } else if (isBoxed(element)) { | |
378 Element redirect = redirectionMapping[element]; | |
379 // In the function that declares the captured variable the box is | |
380 // accessed as direct local. Inside the nested closure the box is | |
381 // accessed through a closure-field. | |
382 // Calling [readLocal] makes sure we generate the correct code to get | |
383 // the box. | |
384 assert(redirect.enclosingElement.kind == ElementKind.VARIABLE); | |
385 HInstruction box = readLocal(redirect.enclosingElement); | |
386 HInstruction lookup = new HFieldGet(redirect, box); | |
387 builder.add(lookup); | |
388 return lookup; | |
389 } else { | |
390 assert(isUsedInTry(element)); | |
391 HInstruction variable = new HFieldGet.fromActivation(element); | |
392 builder.add(variable); | |
393 return variable; | |
394 } | |
395 } | |
396 | |
397 HInstruction readThis() { | |
398 return readLocal(closureData.thisElement); | |
399 } | |
400 | |
401 /** | |
402 * Sets the [element] to [value]. If the element is boxed or stored in a | |
403 * closure then the method generates code to set the value. | |
404 */ | |
405 void updateLocal(Element element, HInstruction value) { | |
406 if (isAccessedDirectly(element)) { | |
407 directLocals[element] = value; | |
408 } else if (isStoredInClosureField(element)) { | |
409 Element redirect = redirectionMapping[element]; | |
410 // We must not use the [LocalsHandler.readThis()] since that could | |
411 // point to a captured this which would be stored in a closure-field | |
412 // itself. | |
413 HInstruction receiver = new HThis(); | |
414 builder.add(receiver); | |
415 builder.add(new HFieldSet(redirect, receiver, value)); | |
416 } else if (isBoxed(element)) { | |
417 Element redirect = redirectionMapping[element]; | |
418 // The box itself could be captured, or be local. A local variable that | |
419 // is captured will be boxed, but the box itself will be a local. | |
420 // Inside the closure the box is stored in a closure-field and cannot | |
421 // be accessed directly. | |
422 assert(redirect.enclosingElement.kind == ElementKind.VARIABLE); | |
423 HInstruction box = readLocal(redirect.enclosingElement); | |
424 builder.add(new HFieldSet(redirect, box, value)); | |
425 } else { | |
426 assert(isUsedInTry(element)); | |
427 builder.add(new HFieldSet.fromActivation(element,value)); | |
428 } | |
429 } | |
430 | |
431 /** | |
432 * This function must be called before visiting any children of the loop. In | |
433 * particular it needs to be called before executing the initializers. | |
434 * | |
435 * The [LocalsHandler] will make the boxes and updates at the right moment. | |
436 * The builder just needs to call [enterLoopBody] and [enterLoopUpdates] (for | |
437 * [For] loops) at the correct places. For phi-handling [beginLoopHeader] and | |
438 * [endLoop] must also be called. | |
439 * | |
440 * The correct place for the box depends on the given loop. In most cases | |
441 * the box will be created when entering the loop-body: while, do-while, and | |
442 * for-in (assuming the call to [:next:] is inside the body) can always be | |
443 * constructed this way. | |
444 * | |
445 * Things are slightly more complicated for [For] loops. If no declared | |
446 * loop variable is boxed then the loop-body approach works here too. If a | |
447 * loop-variable is boxed we need to introduce a new box for the | |
448 * loop-variable before we enter the initializer so that the initializer | |
449 * writes the values into the box. In any case we need to create the box | |
450 * before the condition since the condition could box the variable. | |
451 * Since the first box is created outside the actual loop we have a second | |
452 * location where a box is created: just before the updates. This is | |
453 * necessary since updates are considered to be part of the next iteration | |
454 * (and can again capture variables). | |
455 * | |
456 * For example the following Dart code prints 1 3 -- 3 4. | |
457 * | |
458 * var fs = []; | |
459 * for (var i = 0; i < 3; (f() { fs.add(f); print(i); i++; })()) { | |
460 * i++; | |
461 * } | |
462 * print("--"); | |
463 * for (var i = 0; i < 2; i++) fs[i](); | |
464 * | |
465 * We solve this by emitting the following code (only for [For] loops): | |
466 * <Create box> <== move the first box creation outside the loop. | |
467 * <initializer>; | |
468 * loop-entry: | |
469 * if (!<condition>) goto loop-exit; | |
470 * <body> | |
471 * <update box> // create a new box and copy the captured loop-variables. | |
472 * <updates> | |
473 * goto loop-entry; | |
474 * loop-exit: | |
475 */ | |
476 void startLoop(Node node) { | |
477 ClosureScope scopeData = closureData.capturingScopes[node]; | |
478 if (scopeData == null) return; | |
479 if (scopeData.hasBoxedLoopVariables()) { | |
480 // If there are boxed loop variables then we set up the box and | |
481 // redirections already now. This way the initializer can write its | |
482 // values into the box. | |
483 // For other loops the box will be created when entering the body. | |
484 enterScope(node); | |
485 } | |
486 } | |
487 | |
488 void beginLoopHeader(Node node, HBasicBlock loopEntry) { | |
489 // Create a copy because we modify the map while iterating over | |
490 // it. | |
491 Map<Element, HInstruction> saved = | |
492 new Map<Element, HInstruction>.from(directLocals); | |
493 | |
494 // Create phis for all elements in the definitions environment. | |
495 saved.forEach((Element element, HInstruction instruction) { | |
496 // We know 'this' cannot be modified. | |
497 if (element !== closureData.thisElement) { | |
498 HPhi phi = new HPhi.singleInput(element, instruction); | |
499 loopEntry.addPhi(phi); | |
500 directLocals[element] = phi; | |
501 } else { | |
502 directLocals[element] = instruction; | |
503 } | |
504 }); | |
505 } | |
506 | |
507 void enterLoopBody(Node node) { | |
508 ClosureScope scopeData = closureData.capturingScopes[node]; | |
509 if (scopeData == null) return; | |
510 // If there are no declared boxed loop variables then we did not create the | |
511 // box before the initializer and we have to create the box now. | |
512 if (!scopeData.hasBoxedLoopVariables()) { | |
513 enterScope(node); | |
514 } | |
515 } | |
516 | |
517 void enterLoopUpdates(Loop node) { | |
518 // If there are declared boxed loop variables then the updates might have | |
519 // access to the box and we must switch to a new box before executing the | |
520 // updates. | |
521 // In all other cases a new box will be created when entering the body of | |
522 // the next iteration. | |
523 ClosureScope scopeData = closureData.capturingScopes[node]; | |
524 if (scopeData == null) return; | |
525 if (scopeData.hasBoxedLoopVariables()) { | |
526 updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables); | |
527 } | |
528 } | |
529 | |
530 void endLoop(HBasicBlock loopEntry) { | |
531 loopEntry.forEachPhi((HPhi phi) { | |
532 Element element = phi.element; | |
533 HInstruction postLoopDefinition = directLocals[element]; | |
534 phi.addInput(postLoopDefinition); | |
535 }); | |
536 } | |
537 | |
538 /** | |
539 * Merge [otherLocals] into this locals handler, creating phi-nodes when | |
540 * there is a conflict. | |
541 * If a phi node is necessary, it will use the otherLocals instruction as the | |
542 * first input, and this handler's instruction as the second. | |
543 * NOTICE: This means that the predecessor corresponding to [otherLocals] | |
544 * should be the first predecessor of the current block, and the one | |
545 * corresponding to this locals handler should be the second. | |
546 */ | |
547 void mergeWith(LocalsHandler otherLocals, HBasicBlock joinBlock) { | |
548 // If an element is in one map but not the other we can safely | |
549 // ignore it. It means that a variable was declared in the | |
550 // block. Since variable declarations are scoped the declared | |
551 // variable cannot be alive outside the block. Note: this is only | |
552 // true for nodes where we do joins. | |
553 Map<Element, HInstruction> joinedLocals = new Map<Element, HInstruction>(); | |
554 otherLocals.directLocals.forEach((element, instruction) { | |
555 // We know 'this' cannot be modified. | |
556 if (element === closureData.thisElement) { | |
557 assert(directLocals[element] == instruction); | |
558 joinedLocals[element] = instruction; | |
559 } else { | |
560 HInstruction mine = directLocals[element]; | |
561 if (mine === null) return; | |
562 if (instruction === mine) { | |
563 joinedLocals[element] = instruction; | |
564 } else { | |
565 HInstruction phi = | |
566 new HPhi.manyInputs(element, <HInstruction>[instruction, mine]); | |
567 joinBlock.addPhi(phi); | |
568 joinedLocals[element] = phi; | |
569 } | |
570 } | |
571 }); | |
572 directLocals = joinedLocals; | |
573 } | |
574 | |
575 /** | |
576 * The current localsHandler is not used for its values, only for its | |
577 * declared variables. This is a way to exclude local values from the | |
578 * result when they are no longer in scope. | |
579 * Returns the new LocalsHandler to use (may not be [this]). | |
580 */ | |
581 LocalsHandler mergeMultiple(List<LocalsHandler> locals, | |
582 HBasicBlock joinBlock) { | |
583 assert(locals.length > 0); | |
584 if (locals.length == 1) return locals[0]; | |
585 Map<Element, HInstruction> joinedLocals = new Map<Element,HInstruction>(); | |
586 HInstruction thisValue = null; | |
587 directLocals.forEach((Element element, HInstruction instruction) { | |
588 if (element !== closureData.thisElement) { | |
589 HPhi phi = new HPhi.noInputs(element); | |
590 joinedLocals[element] = phi; | |
591 joinBlock.addPhi(phi); | |
592 } else { | |
593 // We know that "this" never changes, if it's there. | |
594 // Save it for later. While merging, there is no phi for "this", | |
595 // so we don't have to special case it in the merge loop. | |
596 thisValue = instruction; | |
597 } | |
598 }); | |
599 for (LocalsHandler local in locals) { | |
600 local.directLocals.forEach((Element element, HInstruction instruction) { | |
601 HPhi phi = joinedLocals[element]; | |
602 if (phi !== null) { | |
603 phi.addInput(instruction); | |
604 } | |
605 }); | |
606 } | |
607 if (thisValue !== null) { | |
608 // If there was a "this" for the scope, add it to the new locals. | |
609 joinedLocals[closureData.thisElement] = thisValue; | |
610 } | |
611 directLocals = joinedLocals; | |
612 return this; | |
613 } | |
614 } | |
615 | |
616 | |
617 // Represents a single break/continue instruction. | |
618 class JumpHandlerEntry { | |
619 final HGoto jumpInstruction; | |
620 final LocalsHandler locals; | |
621 bool isBreak() => jumpInstruction is HBreak; | |
622 bool isContinue() => jumpInstruction is HContinue; | |
623 JumpHandlerEntry(this.jumpInstruction, this.locals); | |
624 } | |
625 | |
626 | |
627 interface JumpHandler default JumpHandlerImpl { | |
628 JumpHandler(SsaBuilder builder, TargetElement target); | |
629 void generateBreak([LabelElement label]); | |
630 void generateContinue([LabelElement label]); | |
631 void forEachBreak(void action(HBreak instruction, LocalsHandler locals)); | |
632 void forEachContinue(void action(HBreak instruction, LocalsHandler locals)); | |
633 void close(); | |
634 List<LabelElement> labels(); | |
635 } | |
636 | |
637 // Insert break handler used to avoid null checks when a target isn't | |
638 // used as the target of a break, and therefore doesn't need a break | |
639 // handler associated with it. | |
640 class NullJumpHandler implements JumpHandler { | |
641 const NullJumpHandler(); | |
642 void generateBreak([LabelElement label]) { unreachable(); } | |
643 void generateContinue([LabelElement label]) { unreachable(); } | |
644 void forEachBreak(Function ignored) { } | |
645 void forEachContinue(Function ignored) { } | |
646 void close() { } | |
647 List<LabelElement> labels() => const <LabelElement>[]; | |
648 } | |
649 | |
650 // Records breaks until a target block is available. | |
651 // Breaks are always forward jumps. | |
652 // Continues in loops are implemented as breaks of the body. | |
653 // Continues in switches is currently not handled. | |
654 class JumpHandlerImpl implements JumpHandler { | |
655 final SsaBuilder builder; | |
656 final TargetElement target; | |
657 final List<JumpHandlerEntry> jumps; | |
658 | |
659 JumpHandlerImpl(SsaBuilder builder, this.target) | |
660 : this.builder = builder, | |
661 jumps = <JumpHandlerEntry>[] { | |
662 assert(builder.jumpTargets[target] === null); | |
663 builder.jumpTargets[target] = this; | |
664 } | |
665 | |
666 void generateBreak([LabelElement label]) { | |
667 HInstruction breakInstruction; | |
668 if (label === null) { | |
669 breakInstruction = new HBreak(target); | |
670 } else { | |
671 breakInstruction = new HBreak.toLabel(label); | |
672 } | |
673 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); | |
674 builder.close(breakInstruction); | |
675 jumps.add(new JumpHandlerEntry(breakInstruction, locals)); | |
676 } | |
677 | |
678 void generateContinue([LabelElement label]) { | |
679 HInstruction continueInstruction; | |
680 if (label === null) { | |
681 continueInstruction = new HContinue(target); | |
682 } else { | |
683 continueInstruction = new HContinue.toLabel(label); | |
684 } | |
685 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); | |
686 builder.close(continueInstruction); | |
687 jumps.add(new JumpHandlerEntry(continueInstruction, locals)); | |
688 } | |
689 | |
690 void forEachBreak(Function action) { | |
691 for (JumpHandlerEntry entry in jumps) { | |
692 if (entry.isBreak()) action(entry.jumpInstruction, entry.locals); | |
693 } | |
694 } | |
695 | |
696 void forEachContinue(Function action) { | |
697 for (JumpHandlerEntry entry in jumps) { | |
698 if (entry.isContinue()) action(entry.jumpInstruction, entry.locals); | |
699 } | |
700 } | |
701 | |
702 void close() { | |
703 // The mapping from TargetElement to JumpHandler is no longer needed. | |
704 builder.jumpTargets.remove(target); | |
705 } | |
706 | |
707 List<LabelElement> labels() { | |
708 List<LabelElement> result = null; | |
709 for (LabelElement element in target.labels) { | |
710 if (result === null) result = <LabelElement>[]; | |
711 result.add(element); | |
712 } | |
713 return (result === null) ? const <LabelElement>[] : result; | |
714 } | |
715 } | |
716 | |
717 class SsaBuilder implements Visitor { | |
718 final Compiler compiler; | |
719 TreeElements elements; | |
720 final Interceptors interceptors; | |
721 final WorkItem work; | |
722 bool methodInterceptionEnabled; | |
723 HGraph graph; | |
724 LocalsHandler localsHandler; | |
725 HInstruction rethrowableException; | |
726 | |
727 Map<TargetElement, JumpHandler> jumpTargets; | |
728 | |
729 // We build the Ssa graph by simulating a stack machine. | |
730 List<HInstruction> stack; | |
731 | |
732 // The current block to add instructions to. Might be null, if we are | |
733 // visiting dead code. | |
734 HBasicBlock current; | |
735 // The most recently opened block. Has the same value as [current] while | |
736 // the block is open, but unlike [current], it isn't cleared when the current | |
737 // block is closed. | |
738 HBasicBlock lastOpenedBlock; | |
739 | |
740 LibraryElement get currentLibrary() => work.element.getLibrary(); | |
741 | |
742 SsaBuilder(Compiler compiler, WorkItem work) | |
743 : this.compiler = compiler, | |
744 this.work = work, | |
745 interceptors = compiler.builder.interceptors, | |
746 methodInterceptionEnabled = true, | |
747 elements = work.resolutionTree, | |
748 graph = new HGraph(), | |
749 stack = new List<HInstruction>(), | |
750 jumpTargets = new Map<TargetElement, JumpHandler>() { | |
751 localsHandler = new LocalsHandler(this); | |
752 } | |
753 | |
754 void disableMethodInterception() { | |
755 assert(methodInterceptionEnabled); | |
756 methodInterceptionEnabled = false; | |
757 } | |
758 | |
759 void enableMethodInterception() { | |
760 assert(!methodInterceptionEnabled); | |
761 methodInterceptionEnabled = true; | |
762 } | |
763 | |
764 HGraph buildMethod(FunctionElement functionElement) { | |
765 FunctionExpression function = functionElement.parseNode(compiler); | |
766 openFunction(functionElement, function); | |
767 function.body.accept(this); | |
768 return closeFunction(); | |
769 } | |
770 | |
771 /** | |
772 * Returns the constructor body associated with the given constructor or | |
773 * creates a new constructor body, if none can be found. | |
774 */ | |
775 ConstructorBodyElement getConstructorBody(ClassElement classElement, | |
776 FunctionElement constructor) { | |
777 assert(constructor.kind === ElementKind.GENERATIVE_CONSTRUCTOR); | |
778 ConstructorBodyElement bodyElement; | |
779 for (Link<Element> backendMembers = classElement.backendMembers; | |
780 !backendMembers.isEmpty(); | |
781 backendMembers = backendMembers.tail) { | |
782 Element backendMember = backendMembers.head; | |
783 if (backendMember.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { | |
784 ConstructorBodyElement body = backendMember; | |
785 if (body.constructor == constructor) { | |
786 bodyElement = backendMember; | |
787 break; | |
788 } | |
789 } | |
790 } | |
791 if (bodyElement === null) { | |
792 bodyElement = new ConstructorBodyElement(constructor); | |
793 TreeElements treeElements = | |
794 compiler.resolver.resolveMethodElement(constructor); | |
795 compiler.enqueue(new WorkItem.toCodegen(bodyElement, treeElements)); | |
796 classElement.backendMembers = | |
797 classElement.backendMembers.prepend(bodyElement); | |
798 } | |
799 assert(bodyElement.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY); | |
800 return bodyElement; | |
801 } | |
802 | |
803 /** | |
804 * Run through the initializers and inline all field initializers. Recursively | |
805 * inlines super initializers. | |
806 * | |
807 * The constructors of the inlined initializers is added to [constructors] | |
808 * with sub constructors having a lower index than super constructors. | |
809 */ | |
810 void inlineInitializers(FunctionElement constructor, | |
811 List<FunctionElement> constructors, | |
812 Map<Element, HInstruction> fieldValues) { | |
813 TreeElements oldElements = elements; | |
814 constructors.addLast(constructor); | |
815 bool initializedSuper = false; | |
816 elements = compiler.resolver.resolveMethodElement(constructor); | |
817 FunctionExpression functionNode = constructor.parseNode(compiler); | |
818 | |
819 if (functionNode.initializers !== null) { | |
820 Link<Node> initializers = functionNode.initializers.nodes; | |
821 for (Link<Node> link = initializers; !link.isEmpty(); link = link.tail) { | |
822 assert(link.head is Send); | |
823 if (link.head is !SendSet) { | |
824 // A super initializer or constructor redirection. | |
825 Send call = link.head; | |
826 assert(Initializers.isSuperConstructorCall(call) || | |
827 Initializers.isConstructorRedirect(call)); | |
828 FunctionElement nextConstructor = elements[call]; | |
829 // Visit arguments and map the corresponding parameter value to | |
830 // the resulting HInstruction value. | |
831 List<HInstruction> arguments = new List<HInstruction>(); | |
832 addStaticSendArgumentsToList(call, nextConstructor, arguments); | |
833 int index = 0; | |
834 FunctionParameters parameters = | |
835 nextConstructor.computeParameters(compiler); | |
836 parameters.forEachParameter((Element parameter) { | |
837 HInstruction argument = arguments[index++]; | |
838 localsHandler.updateLocal(parameter, argument); | |
839 // Don't forget to update the field, if the parameter is of the | |
840 // form [:this.x:]. | |
841 if (parameter.kind == ElementKind.FIELD_PARAMETER) { | |
842 FieldParameterElement fieldParameterElement = parameter; | |
843 fieldValues[fieldParameterElement.fieldElement] = argument; | |
844 } | |
845 }); | |
846 inlineInitializers(nextConstructor, constructors, fieldValues); | |
847 initializedSuper = true; | |
848 } else { | |
849 // A field initializer. | |
850 SendSet init = link.head; | |
851 Link<Node> arguments = init.arguments; | |
852 assert(!arguments.isEmpty() && arguments.tail.isEmpty()); | |
853 visit(arguments.head); | |
854 fieldValues[elements[init]] = pop(); | |
855 } | |
856 } | |
857 } | |
858 | |
859 if (!initializedSuper) { | |
860 // No super initializer found. Try to find the default constructor if | |
861 // the class is not Object. | |
862 ClassElement enclosingClass = constructor.enclosingElement; | |
863 ClassElement superClass = enclosingClass.superclass; | |
864 if (enclosingClass != compiler.objectClass) { | |
865 assert(superClass !== null); | |
866 assert(superClass.isResolved); | |
867 FunctionElement nextConstructor = | |
868 superClass.lookupConstructor(superClass.name); | |
869 if (nextConstructor === null) { | |
870 compiler.internalError("no default constructor available"); | |
871 } | |
872 inlineInitializers(nextConstructor, constructors, fieldValues); | |
873 } | |
874 } | |
875 | |
876 elements = oldElements; | |
877 } | |
878 | |
879 /** | |
880 * Build the factory function corresponding to the constructor | |
881 * [functionElement]: | |
882 * - Initialize fields with the values of the field initializers of the | |
883 * current constructor and super constructors or constructors redirected | |
884 * to, starting from the current constructor. | |
885 * - Call the the constructor bodies, starting from the constructor(s) in the | |
886 * super class(es). | |
887 */ | |
888 HGraph buildFactory(ClassElement classElement, | |
889 FunctionElement functionElement) { | |
890 FunctionExpression function = functionElement.parseNode(compiler); | |
891 // Note that constructors (like any other static function) do not need | |
892 // to deal with optional arguments. It is the callers job to provide all | |
893 // arguments as if they were positional. | |
894 | |
895 // The initializer list could contain closures. | |
896 openFunction(functionElement, function); | |
897 | |
898 Map<Element, HInstruction> fieldValues = new Map<Element, HInstruction>(); | |
899 FunctionParameters parameters = functionElement.computeParameters(compiler); | |
900 parameters.forEachParameter((Element element) { | |
901 if (element.kind == ElementKind.FIELD_PARAMETER) { | |
902 // If the [element] is a field-parameter (such as [:this.x:] then | |
903 // initialize the field element with its value. | |
904 FieldParameterElement fieldParameterElement = element; | |
905 HInstruction parameterValue = localsHandler.readLocal(element); | |
906 fieldValues[fieldParameterElement.fieldElement] = parameterValue; | |
907 } | |
908 }); | |
909 | |
910 final Map<FunctionElement, TreeElements> constructorElements = | |
911 compiler.resolver.constructorElements; | |
912 List<FunctionElement> constructors = new List<FunctionElement>(); | |
913 | |
914 // Analyze the constructor and all referenced constructors and collect | |
915 // initializers and constructor bodies. | |
916 inlineInitializers(functionElement, constructors, fieldValues); | |
917 | |
918 // Call the JavaScript constructor with the fields as argument. | |
919 // TODO(floitsch,karlklose): move this code to ClassElement and share with | |
920 // the emitter. | |
921 List<HInstruction> constructorArguments = <HInstruction>[]; | |
922 ClassElement element = classElement; | |
923 while (element != null) { | |
924 for (Element member in element.members) { | |
925 if (member.isInstanceMember() && member.kind == ElementKind.FIELD) { | |
926 HInstruction value = fieldValues[member]; | |
927 if (value === null) { | |
928 // The field has no value in the initializer list. Initialize it | |
929 // with the declaration-site constant (if any). | |
930 Constant fieldValue = | |
931 compiler.constantHandler.compileVariable(member); | |
932 value = graph.addConstant(fieldValue); | |
933 } | |
934 constructorArguments.add(value); | |
935 } | |
936 } | |
937 element = element.superclass; | |
938 } | |
939 HForeignNew newObject = new HForeignNew(classElement, constructorArguments); | |
940 add(newObject); | |
941 // Generate calls to the constructor bodies. | |
942 for (int index = constructors.length - 1; index >= 0; index--) { | |
943 FunctionElement constructor = constructors[index]; | |
944 // TODO(floitsch): find better way to detect that constructor body is | |
945 // empty. | |
946 if (constructor is SynthesizedConstructorElement) continue; | |
947 ConstructorBodyElement body = getConstructorBody(classElement, | |
948 constructor); | |
949 List bodyCallInputs = <HInstruction>[]; | |
950 bodyCallInputs.add(newObject); | |
951 body.functionParameters.forEachParameter((parameter) { | |
952 bodyCallInputs.add(localsHandler.readLocal(parameter)); | |
953 }); | |
954 // TODO(ahe): The constructor name is statically resolved. See | |
955 // SsaCodeGenerator.visitInvokeDynamicMethod. Is there a cleaner | |
956 // way to do this? | |
957 SourceString methodName = new SourceString(compiler.namer.getName(body)); | |
958 add(new HInvokeDynamicMethod(null, methodName, bodyCallInputs)); | |
959 } | |
960 close(new HReturn(newObject)).addSuccessor(graph.exit); | |
961 return closeFunction(); | |
962 } | |
963 | |
964 void openFunction(FunctionElement functionElement, | |
965 FunctionExpression node) { | |
966 HBasicBlock block = graph.addNewBlock(); | |
967 open(graph.entry); | |
968 | |
969 localsHandler.startFunction(functionElement, node); | |
970 close(new HGoto()).addSuccessor(block); | |
971 | |
972 open(block); | |
973 } | |
974 | |
975 HGraph closeFunction() { | |
976 // TODO(kasperl): Make this goto an implicit return. | |
977 if (!isAborted()) close(new HGoto()).addSuccessor(graph.exit); | |
978 graph.finalize(); | |
979 return graph; | |
980 } | |
981 | |
982 HBasicBlock addNewBlock() { | |
983 HBasicBlock block = graph.addNewBlock(); | |
984 // If adding a new block during building of an expression, it is due to | |
985 // conditional expressions or short-circuit logical operators. | |
986 return block; | |
987 } | |
988 | |
989 void open(HBasicBlock block) { | |
990 block.open(); | |
991 current = block; | |
992 lastOpenedBlock = block; | |
993 } | |
994 | |
995 HBasicBlock close(HControlFlow end) { | |
996 HBasicBlock result = current; | |
997 current.close(end); | |
998 current = null; | |
999 return result; | |
1000 } | |
1001 | |
1002 void goto(HBasicBlock from, HBasicBlock to) { | |
1003 from.close(new HGoto()); | |
1004 from.addSuccessor(to); | |
1005 } | |
1006 | |
1007 bool isAborted() { | |
1008 return current === null; | |
1009 } | |
1010 | |
1011 void add(HInstruction instruction) { | |
1012 current.add(instruction); | |
1013 } | |
1014 | |
1015 void push(HInstruction instruction) { | |
1016 add(instruction); | |
1017 stack.add(instruction); | |
1018 } | |
1019 | |
1020 HInstruction pop() { | |
1021 return stack.removeLast(); | |
1022 } | |
1023 | |
1024 HBoolify popBoolified() { | |
1025 HBoolify boolified = new HBoolify(pop()); | |
1026 add(boolified); | |
1027 return boolified; | |
1028 } | |
1029 | |
1030 void visit(Node node) { | |
1031 if (node !== null) node.accept(this); | |
1032 } | |
1033 | |
1034 visitBlock(Block node) { | |
1035 for (Link<Node> link = node.statements.nodes; | |
1036 !link.isEmpty(); | |
1037 link = link.tail) { | |
1038 visit(link.head); | |
1039 if (isAborted()) { | |
1040 // The block has been aborted by a return or a throw. | |
1041 if (!stack.isEmpty()) compiler.cancel('non-empty instruction stack'); | |
1042 return; | |
1043 } | |
1044 } | |
1045 assert(!current.isClosed()); | |
1046 if (!stack.isEmpty()) compiler.cancel('non-empty instruction stack'); | |
1047 } | |
1048 | |
1049 visitClassNode(ClassNode node) { | |
1050 unreachable(); | |
1051 } | |
1052 | |
1053 visitExpressionStatement(ExpressionStatement node) { | |
1054 visit(node.expression); | |
1055 pop(); | |
1056 } | |
1057 | |
1058 /** | |
1059 * Creates a new loop-header block. The previous [current] block | |
1060 * is closed with an [HGoto] and replaced by the newly created block. | |
1061 * Also notifies the locals handler that we're entering a loop. | |
1062 */ | |
1063 JumpHandler beginLoopHeader(Node node) { | |
1064 assert(!isAborted()); | |
1065 HBasicBlock previousBlock = close(new HGoto()); | |
1066 | |
1067 JumpHandler jumpHandler = createJumpHandler(node); | |
1068 HBasicBlock loopEntry = graph.addNewLoopHeaderBlock(jumpHandler.labels()); | |
1069 previousBlock.addSuccessor(loopEntry); | |
1070 open(loopEntry); | |
1071 | |
1072 localsHandler.beginLoopHeader(node, loopEntry); | |
1073 return jumpHandler; | |
1074 } | |
1075 | |
1076 /** | |
1077 * Ends the loop: | |
1078 * - creates a new block and adds it as successor to the [branchBlock]. | |
1079 * - opens the new block (setting as [current]). | |
1080 * - notifies the locals handler that we're exiting a loop. | |
1081 */ | |
1082 void endLoop(HBasicBlock loopEntry, | |
1083 HBasicBlock branchBlock, | |
1084 JumpHandler jumpHandler, | |
1085 LocalsHandler savedLocals) { | |
1086 HBasicBlock loopExitBlock = addNewBlock(); | |
1087 assert(branchBlock.successors.length == 1); | |
1088 List<LocalsHandler> breakLocals = <LocalsHandler>[]; | |
1089 jumpHandler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) { | |
1090 breakInstruction.block.addSuccessor(loopExitBlock); | |
1091 breakLocals.add(locals); | |
1092 }); | |
1093 branchBlock.addSuccessor(loopExitBlock); | |
1094 open(loopExitBlock); | |
1095 localsHandler.endLoop(loopEntry); | |
1096 if (!breakLocals.isEmpty()) { | |
1097 breakLocals.add(savedLocals); | |
1098 localsHandler = savedLocals.mergeMultiple(breakLocals, loopExitBlock); | |
1099 } else { | |
1100 localsHandler = savedLocals; | |
1101 } | |
1102 } | |
1103 | |
1104 // For while loops, initializer and update are null. | |
1105 // The condition function must return a boolean result. | |
1106 // None of the functions must leave anything on the stack. | |
1107 handleLoop(Node loop, | |
1108 void initialize(), | |
1109 HInstruction condition(), | |
1110 void update(), | |
1111 void body()) { | |
1112 // Generate: | |
1113 // <initializer> | |
1114 // loop-entry: | |
1115 // if (!<condition>) goto loop-exit; | |
1116 // <body> | |
1117 // <updates> | |
1118 // goto loop-entry; | |
1119 // loop-exit: | |
1120 | |
1121 localsHandler.startLoop(loop); | |
1122 | |
1123 // The initializer. | |
1124 initialize(); | |
1125 assert(!isAborted()); | |
1126 | |
1127 JumpHandler jumpHandler = beginLoopHeader(loop); | |
1128 HBasicBlock conditionBlock = current; | |
1129 | |
1130 HInstruction conditionInstruction = condition(); | |
1131 HBasicBlock conditionExitBlock = | |
1132 close(new HLoopBranch(conditionInstruction)); | |
1133 | |
1134 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
1135 | |
1136 // The body. | |
1137 HBasicBlock beginBodyBlock = addNewBlock(); | |
1138 conditionExitBlock.addSuccessor(beginBodyBlock); | |
1139 open(beginBodyBlock); | |
1140 | |
1141 localsHandler.enterLoopBody(loop); | |
1142 hackAroundPossiblyAbortingBody(loop, body); | |
1143 | |
1144 SubGraph bodyGraph = new SubGraph(beginBodyBlock, current); | |
1145 HBasicBlock bodyBlock = close(new HGoto()); | |
1146 | |
1147 // Update. | |
1148 // We create an update block, even when we are in a while loop. There the | |
1149 // update block is the jump-target for continue statements. We could avoid | |
1150 // the creation if there is no continue, but for now we always create it. | |
1151 HBasicBlock updateBlock = addNewBlock(); | |
1152 | |
1153 List<LocalsHandler> continueLocals = <LocalsHandler>[]; | |
1154 jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) { | |
1155 instruction.block.addSuccessor(updateBlock); | |
1156 continueLocals.add(locals); | |
1157 }); | |
1158 bodyBlock.addSuccessor(updateBlock); | |
1159 continueLocals.add(localsHandler); | |
1160 | |
1161 open(updateBlock); | |
1162 | |
1163 localsHandler = localsHandler.mergeMultiple(continueLocals, updateBlock); | |
1164 | |
1165 HLabeledBlockInformation labelInfo; | |
1166 List<LabelElement> labels = jumpHandler.labels(); | |
1167 TargetElement target = elements[loop]; | |
1168 if (!labels.isEmpty()) { | |
1169 beginBodyBlock.labeledBlockInformation = | |
1170 new HLabeledBlockInformation(bodyGraph, updateBlock, | |
1171 jumpHandler.labels(), isContinue: true); | |
1172 } else if (target !== null && target.isContinueTarget) { | |
1173 beginBodyBlock.labeledBlockInformation = | |
1174 new HLabeledBlockInformation.implicit(bodyGraph, updateBlock, | |
1175 target, isContinue: true); | |
1176 } | |
1177 | |
1178 localsHandler.enterLoopUpdates(loop); | |
1179 | |
1180 update(); | |
1181 | |
1182 updateBlock = close(new HGoto()); | |
1183 // The back-edge completing the cycle. | |
1184 updateBlock.addSuccessor(conditionBlock); | |
1185 conditionBlock.postProcessLoopHeader(); | |
1186 | |
1187 endLoop(conditionBlock, conditionExitBlock, jumpHandler, savedLocals); | |
1188 } | |
1189 | |
1190 visitFor(For node) { | |
1191 assert(node.body !== null); | |
1192 void buildInitializer() { | |
1193 if (node.initializer === null) return; | |
1194 Node initializer = node.initializer; | |
1195 if (initializer !== null) { | |
1196 visit(initializer); | |
1197 if (initializer.asExpression() !== null) { | |
1198 pop(); | |
1199 } | |
1200 } | |
1201 } | |
1202 HInstruction buildCondition() { | |
1203 if (node.condition === null) { | |
1204 return graph.addConstantBool(true); | |
1205 } | |
1206 visit(node.condition); | |
1207 return popBoolified(); | |
1208 } | |
1209 void buildUpdate() { | |
1210 for (Expression expression in node.update) { | |
1211 visit(expression); | |
1212 assert(!isAborted()); | |
1213 // The result of the update instruction isn't used, and can just | |
1214 // be dropped. | |
1215 HInstruction updateInstruction = pop(); | |
1216 } | |
1217 } | |
1218 void buildBody() { | |
1219 visit(node.body); | |
1220 } | |
1221 handleLoop(node, buildInitializer, buildCondition, buildUpdate, buildBody); | |
1222 } | |
1223 | |
1224 visitWhile(While node) { | |
1225 HInstruction buildCondition() { | |
1226 visit(node.condition); | |
1227 return popBoolified(); | |
1228 } | |
1229 handleLoop(node, | |
1230 () {}, | |
1231 buildCondition, | |
1232 () {}, | |
1233 () { visit(node.body); }); | |
1234 } | |
1235 | |
1236 visitDoWhile(DoWhile node) { | |
1237 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
1238 localsHandler.startLoop(node); | |
1239 JumpHandler jumpHandler = beginLoopHeader(node); | |
1240 HBasicBlock loopEntryBlock = current; | |
1241 HBasicBlock bodyEntryBlock = current; | |
1242 TargetElement target = elements[node]; | |
1243 bool hasContinues = target !== null && target.isContinueTarget; | |
1244 if (hasContinues) { | |
1245 // Add extra block to hang labels on. | |
1246 // It doesn't currently work if they are on the same block as the | |
1247 // HLoopInfo. The handling of HLabeledBlockInformation will visit a | |
1248 // SubGraph that starts at the same block again, so the HLoopInfo is | |
1249 // either handled twice, or it's handled after the labeled block info, | |
1250 // both of which generate the wrong code. | |
1251 // Using a separate block is just a simple workaround. | |
1252 bodyEntryBlock = graph.addNewBlock(); | |
1253 goto(current, bodyEntryBlock); | |
1254 open(bodyEntryBlock); | |
1255 } | |
1256 localsHandler.enterLoopBody(node); | |
1257 hackAroundPossiblyAbortingBody(node, () { visit(node.body); }); | |
1258 | |
1259 // If there are no continues we could avoid the creation of the condition | |
1260 // block. This could also lead to a block having multiple entries and exits. | |
1261 HBasicBlock bodyExitBlock = close(new HGoto()); | |
1262 HBasicBlock conditionBlock = addNewBlock(); | |
1263 | |
1264 List<LocalsHandler> continueLocals = <LocalsHandler>[]; | |
1265 jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) { | |
1266 instruction.block.addSuccessor(conditionBlock); | |
1267 continueLocals.add(locals); | |
1268 }); | |
1269 bodyExitBlock.addSuccessor(conditionBlock); | |
1270 if (!continueLocals.isEmpty()) { | |
1271 continueLocals.add(localsHandler); | |
1272 localsHandler = savedLocals.mergeMultiple(continueLocals, conditionBlock); | |
1273 SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock); | |
1274 List<LabelElement> labels = jumpHandler.labels(); | |
1275 if (!labels.isEmpty()) { | |
1276 bodyEntryBlock.labeledBlockInformation = | |
1277 new HLabeledBlockInformation(bodyGraph, | |
1278 conditionBlock, | |
1279 labels, | |
1280 isContinue: true); | |
1281 } else { | |
1282 bodyEntryBlock.labeledBlockInformation = | |
1283 new HLabeledBlockInformation.implicit(bodyGraph, | |
1284 conditionBlock, | |
1285 target, | |
1286 isContinue: true); | |
1287 } | |
1288 } | |
1289 open(conditionBlock); | |
1290 | |
1291 visit(node.condition); | |
1292 assert(!isAborted()); | |
1293 conditionBlock = close(new HLoopBranch(popBoolified(), | |
1294 HLoopBranch.DO_WHILE_LOOP)); | |
1295 | |
1296 conditionBlock.addSuccessor(loopEntryBlock); // The back-edge. | |
1297 loopEntryBlock.postProcessLoopHeader(); | |
1298 | |
1299 endLoop(loopEntryBlock, conditionBlock, jumpHandler, localsHandler); | |
1300 jumpHandler.close(); | |
1301 } | |
1302 | |
1303 visitFunctionExpression(FunctionExpression node) { | |
1304 ClosureData nestedClosureData = compiler.builder.closureDataCache[node]; | |
1305 if (nestedClosureData === null) { | |
1306 // TODO(floitsch): we can only assume that the reason for not having a | |
1307 // closure data here is, because the function is inside an initializer. | |
1308 compiler.unimplemented("Closures inside initializers", node: node); | |
1309 } | |
1310 assert(nestedClosureData !== null); | |
1311 assert(nestedClosureData.closureClassElement !== null); | |
1312 ClassElement closureClassElement = | |
1313 nestedClosureData.closureClassElement; | |
1314 FunctionElement callElement = nestedClosureData.callElement; | |
1315 compiler.enqueue(new WorkItem.toCodegen(callElement, elements)); | |
1316 compiler.registerInstantiatedClass(closureClassElement); | |
1317 assert(closureClassElement.members.isEmpty()); | |
1318 | |
1319 List<HInstruction> capturedVariables = <HInstruction>[]; | |
1320 for (Element member in closureClassElement.backendMembers) { | |
1321 // The backendMembers also contains the call method(s). We are only | |
1322 // interested in the fields. | |
1323 if (member.kind == ElementKind.FIELD) { | |
1324 Element capturedLocal = nestedClosureData.capturedFieldMapping[member]; | |
1325 assert(capturedLocal != null); | |
1326 capturedVariables.add(localsHandler.readLocal(capturedLocal)); | |
1327 } | |
1328 } | |
1329 | |
1330 push(new HForeignNew(closureClassElement, capturedVariables)); | |
1331 } | |
1332 | |
1333 visitFunctionDeclaration(FunctionDeclaration node) { | |
1334 visit(node.function); | |
1335 localsHandler.updateLocal(elements[node], pop()); | |
1336 } | |
1337 | |
1338 visitIdentifier(Identifier node) { | |
1339 if (node.isThis()) { | |
1340 stack.add(localsHandler.readThis()); | |
1341 } else { | |
1342 compiler.internalError("SsaBuilder.visitIdentifier on non-this", | |
1343 node: node); | |
1344 } | |
1345 } | |
1346 | |
1347 visitIf(If node) { | |
1348 visit(node.condition); | |
1349 Function visitElse; | |
1350 if (node.elsePart != null) { | |
1351 visitElse = () { | |
1352 visit(node.elsePart); | |
1353 }; | |
1354 } | |
1355 handleIf(() => visit(node.thenPart), visitElse); | |
1356 } | |
1357 | |
1358 void handleIf(void visitThen(), void visitElse()) { | |
1359 bool hasElse = visitElse != null; | |
1360 HIf condition = new HIf(popBoolified(), hasElse); | |
1361 HBasicBlock conditionBlock = close(condition); | |
1362 | |
1363 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
1364 | |
1365 // The then part. | |
1366 HBasicBlock thenBlock = addNewBlock(); | |
1367 conditionBlock.addSuccessor(thenBlock); | |
1368 open(thenBlock); | |
1369 visitThen(); | |
1370 SubGraph thenGraph = new SubGraph(thenBlock, lastOpenedBlock); | |
1371 thenBlock = current; | |
1372 | |
1373 // Reset the locals state to the state after the condition and keep the | |
1374 // current state in [thenLocals]. | |
1375 LocalsHandler thenLocals = localsHandler; | |
1376 | |
1377 // Now the else part. | |
1378 localsHandler = savedLocals; | |
1379 HBasicBlock elseBlock = null; | |
1380 SubGraph elseGraph = null; | |
1381 if (hasElse) { | |
1382 elseBlock = addNewBlock(); | |
1383 conditionBlock.addSuccessor(elseBlock); | |
1384 open(elseBlock); | |
1385 visitElse(); | |
1386 elseGraph = new SubGraph(elseBlock, lastOpenedBlock); | |
1387 elseBlock = current; | |
1388 } | |
1389 | |
1390 HBasicBlock joinBlock = null; | |
1391 if (thenBlock !== null || elseBlock !== null || !hasElse) { | |
1392 joinBlock = addNewBlock(); | |
1393 if (thenBlock !== null) goto(thenBlock, joinBlock); | |
1394 if (elseBlock !== null) goto(elseBlock, joinBlock); | |
1395 else if (!hasElse) conditionBlock.addSuccessor(joinBlock); | |
1396 // If the join block has two predecessors we have to merge the | |
1397 // locals. The current locals is what either the | |
1398 // condition or the else block left us with, so we merge that | |
1399 // with the set of locals we got after visiting the then | |
1400 // part of the if. | |
1401 open(joinBlock); | |
1402 if (joinBlock.predecessors.length == 2) { | |
1403 localsHandler.mergeWith(thenLocals, joinBlock); | |
1404 } else if (thenBlock !== null) { | |
1405 // The only predecessor is the then branch. | |
1406 localsHandler = thenLocals; | |
1407 } | |
1408 } | |
1409 condition.blockInformation = | |
1410 new HIfBlockInformation(condition, thenGraph, elseGraph, joinBlock); | |
1411 } | |
1412 | |
1413 void visitLogicalAndOr(Send node, Operator op) { | |
1414 handleLogicalAndOr(() { visit(node.receiver); }, | |
1415 () { visit(node.argumentsNode); }, | |
1416 isAnd: (const SourceString("&&") == op.source)); | |
1417 } | |
1418 | |
1419 | |
1420 void handleLogicalAndOr(void left(), void right(), [bool isAnd = true]) { | |
1421 // x && y is transformed into: | |
1422 // t0 = boolify(x); | |
1423 // if (t0) t1 = boolify(y); | |
1424 // result = phi(t0, t1); | |
1425 // | |
1426 // x || y is transformed into: | |
1427 // t0 = boolify(x); | |
1428 // if (not(t0)) t1 = boolify(y); | |
1429 // result = phi(t0, t1); | |
1430 left(); | |
1431 HInstruction boolifiedLeft = popBoolified(); | |
1432 HInstruction condition; | |
1433 if (isAnd) { | |
1434 condition = boolifiedLeft; | |
1435 } else { | |
1436 condition = new HNot(boolifiedLeft); | |
1437 add(condition); | |
1438 } | |
1439 HIf branch = new HIf(condition, false); | |
1440 HBasicBlock leftBlock = close(branch); | |
1441 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
1442 | |
1443 HBasicBlock rightBlock = addNewBlock(); | |
1444 leftBlock.addSuccessor(rightBlock); | |
1445 open(rightBlock); | |
1446 | |
1447 right(); | |
1448 HInstruction boolifiedRight = popBoolified(); | |
1449 SubGraph rightGraph = new SubGraph(rightBlock, current); | |
1450 rightBlock = close(new HGoto()); | |
1451 | |
1452 HBasicBlock joinBlock = addNewBlock(); | |
1453 leftBlock.addSuccessor(joinBlock); | |
1454 rightBlock.addSuccessor(joinBlock); | |
1455 open(joinBlock); | |
1456 | |
1457 branch.blockInformation = | |
1458 new HIfBlockInformation(branch, rightGraph, null, joinBlock); | |
1459 | |
1460 localsHandler.mergeWith(savedLocals, joinBlock); | |
1461 HPhi result = new HPhi.manyInputs(null, | |
1462 <HInstruction>[boolifiedLeft, boolifiedRight]); | |
1463 joinBlock.addPhi(result); | |
1464 stack.add(result); | |
1465 } | |
1466 | |
1467 void visitLogicalNot(Send node) { | |
1468 assert(node.argumentsNode is Prefix); | |
1469 visit(node.receiver); | |
1470 HNot not = new HNot(popBoolified()); | |
1471 push(not); | |
1472 } | |
1473 | |
1474 void visitUnary(Send node, Operator op) { | |
1475 assert(node.argumentsNode is Prefix); | |
1476 visit(node.receiver); | |
1477 assert(op.token.kind !== PLUS_TOKEN); | |
1478 HInstruction operand = pop(); | |
1479 | |
1480 HInstruction target = | |
1481 new HStatic(interceptors.getPrefixOperatorInterceptor(op)); | |
1482 add(target); | |
1483 HInvokeUnary result; | |
1484 switch (op.source.stringValue) { | |
1485 case "-": result = new HNegate(target, operand); break; | |
1486 case "~": result = new HBitNot(target, operand); break; | |
1487 default: unreachable(); | |
1488 } | |
1489 // See if we can constant-fold right away. This avoids rewrites later on. | |
1490 if (operand is HConstant) { | |
1491 HConstant constant = operand; | |
1492 Constant folded = result.operation.fold(constant.constant); | |
1493 if (folded !== null) { | |
1494 stack.add(graph.addConstant(folded)); | |
1495 return; | |
1496 } | |
1497 } | |
1498 push(result); | |
1499 } | |
1500 | |
1501 void visitBinary(HInstruction left, Operator op, HInstruction right) { | |
1502 Element element = interceptors.getOperatorInterceptor(op); | |
1503 assert(element != null); | |
1504 HInstruction target = new HStatic(element); | |
1505 add(target); | |
1506 switch (op.source.stringValue) { | |
1507 case "+": | |
1508 case "++": | |
1509 case "+=": | |
1510 push(new HAdd(target, left, right)); | |
1511 break; | |
1512 case "-": | |
1513 case "--": | |
1514 case "-=": | |
1515 push(new HSubtract(target, left, right)); | |
1516 break; | |
1517 case "*": | |
1518 case "*=": | |
1519 push(new HMultiply(target, left, right)); | |
1520 break; | |
1521 case "/": | |
1522 case "/=": | |
1523 push(new HDivide(target, left, right)); | |
1524 break; | |
1525 case "~/": | |
1526 case "~/=": | |
1527 push(new HTruncatingDivide(target, left, right)); | |
1528 break; | |
1529 case "%": | |
1530 case "%=": | |
1531 push(new HModulo(target, left, right)); | |
1532 break; | |
1533 case "<<": | |
1534 case "<<=": | |
1535 push(new HShiftLeft(target, left, right)); | |
1536 break; | |
1537 case ">>": | |
1538 case ">>=": | |
1539 push(new HShiftRight(target, left, right)); | |
1540 break; | |
1541 case "|": | |
1542 case "|=": | |
1543 push(new HBitOr(target, left, right)); | |
1544 break; | |
1545 case "&": | |
1546 case "&=": | |
1547 push(new HBitAnd(target, left, right)); | |
1548 break; | |
1549 case "^": | |
1550 case "^=": | |
1551 push(new HBitXor(target, left, right)); | |
1552 break; | |
1553 case "==": | |
1554 push(new HEquals(target, left, right)); | |
1555 break; | |
1556 case "===": | |
1557 push(new HIdentity(target, left, right)); | |
1558 break; | |
1559 case "!==": | |
1560 HIdentity eq = new HIdentity(target, left, right); | |
1561 add(eq); | |
1562 push(new HNot(eq)); | |
1563 break; | |
1564 case "<": | |
1565 push(new HLess(target, left, right)); | |
1566 break; | |
1567 case "<=": | |
1568 push(new HLessEqual(target, left, right)); | |
1569 break; | |
1570 case ">": | |
1571 push(new HGreater(target, left, right)); | |
1572 break; | |
1573 case ">=": | |
1574 push(new HGreaterEqual(target, left, right)); | |
1575 break; | |
1576 case "!=": | |
1577 HEquals eq = new HEquals(target, left, right); | |
1578 add(eq); | |
1579 HBoolify bl = new HBoolify(eq); | |
1580 add(bl); | |
1581 push(new HNot(bl)); | |
1582 break; | |
1583 default: compiler.unimplemented("SsaBuilder.visitBinary"); | |
1584 } | |
1585 } | |
1586 | |
1587 void generateGetter(Send send, Element element) { | |
1588 Selector selector = elements.getSelector(send); | |
1589 if (Elements.isStaticOrTopLevelField(element)) { | |
1590 push(new HStatic(element)); | |
1591 if (element.kind == ElementKind.GETTER) { | |
1592 push(new HInvokeStatic(selector, <HInstruction>[pop()])); | |
1593 } | |
1594 } else if (Elements.isInstanceSend(send, elements)) { | |
1595 HInstruction receiver; | |
1596 if (send.receiver == null) { | |
1597 receiver = localsHandler.readThis(); | |
1598 } else { | |
1599 visit(send.receiver); | |
1600 receiver = pop(); | |
1601 } | |
1602 SourceString getterName = send.selector.asIdentifier().source; | |
1603 Element staticInterceptor = null; | |
1604 if (methodInterceptionEnabled) { | |
1605 staticInterceptor = interceptors.getStaticGetInterceptor(getterName); | |
1606 } | |
1607 if (staticInterceptor != null) { | |
1608 HStatic target = new HStatic(staticInterceptor); | |
1609 add(target); | |
1610 List<HInstruction> inputs = <HInstruction>[target, receiver]; | |
1611 push(new HInvokeInterceptor(selector, getterName, true, inputs)); | |
1612 } else { | |
1613 push(new HInvokeDynamicGetter(selector, null, getterName, receiver)); | |
1614 } | |
1615 } else if (Elements.isStaticOrTopLevelFunction(element)) { | |
1616 push(new HStatic(element)); | |
1617 compiler.registerGetOfStaticFunction(element); | |
1618 } else { | |
1619 stack.add(localsHandler.readLocal(element)); | |
1620 } | |
1621 } | |
1622 | |
1623 void generateSetter(SendSet send, Element element, HInstruction value) { | |
1624 Selector selector = elements.getSelector(send); | |
1625 if (Elements.isStaticOrTopLevelField(element)) { | |
1626 if (element.kind == ElementKind.SETTER) { | |
1627 HStatic target = new HStatic(element); | |
1628 add(target); | |
1629 add(new HInvokeStatic(selector, <HInstruction>[target, value])); | |
1630 } else { | |
1631 add(new HStaticStore(element, value)); | |
1632 } | |
1633 stack.add(value); | |
1634 } else if (element === null || Elements.isInstanceField(element)) { | |
1635 SourceString dartSetterName = send.selector.asIdentifier().source; | |
1636 HInstruction receiver; | |
1637 if (send.receiver == null) { | |
1638 receiver = localsHandler.readThis(); | |
1639 } else { | |
1640 visit(send.receiver); | |
1641 receiver = pop(); | |
1642 } | |
1643 Element staticInterceptor = null; | |
1644 if (methodInterceptionEnabled) { | |
1645 staticInterceptor = | |
1646 interceptors.getStaticSetInterceptor(dartSetterName); | |
1647 } | |
1648 if (staticInterceptor != null) { | |
1649 HStatic target = new HStatic(staticInterceptor); | |
1650 add(target); | |
1651 List<HInstruction> inputs = <HInstruction>[target, receiver, value]; | |
1652 add(new HInvokeInterceptor(selector, dartSetterName, false, inputs)); | |
1653 } else { | |
1654 add(new HInvokeDynamicSetter(selector, null, dartSetterName, | |
1655 receiver, value)); | |
1656 } | |
1657 stack.add(value); | |
1658 } else { | |
1659 localsHandler.updateLocal(element, value); | |
1660 stack.add(value); | |
1661 } | |
1662 } | |
1663 | |
1664 visitOperatorSend(node) { | |
1665 assert(node.selector is Operator); | |
1666 Operator op = node.selector; | |
1667 if (const SourceString("[]") == op.source) { | |
1668 HStatic target = new HStatic(interceptors.getIndexInterceptor()); | |
1669 add(target); | |
1670 visit(node.receiver); | |
1671 HInstruction receiver = pop(); | |
1672 visit(node.argumentsNode); | |
1673 HInstruction index = pop(); | |
1674 push(new HIndex(target, receiver, index)); | |
1675 } else if (const SourceString("&&") == op.source || | |
1676 const SourceString("||") == op.source) { | |
1677 visitLogicalAndOr(node, op); | |
1678 } else if (const SourceString("!") == op.source) { | |
1679 visitLogicalNot(node); | |
1680 } else if (node.argumentsNode is Prefix) { | |
1681 visitUnary(node, op); | |
1682 } else if (const SourceString("is") == op.source) { | |
1683 visit(node.receiver); | |
1684 HInstruction expression = pop(); | |
1685 Node argument = node.arguments.head; | |
1686 TypeAnnotation type = argument.asTypeAnnotation(); | |
1687 bool isNot = false; | |
1688 // TODO(ngeoffray): Duplicating pattern in resolver. We should | |
1689 // add a new kind of node. | |
1690 if (type == null) { | |
1691 type = argument.asSend().receiver; | |
1692 isNot = true; | |
1693 } | |
1694 HInstruction instruction = new HIs(elements[type], expression); | |
1695 if (isNot) { | |
1696 add(instruction); | |
1697 instruction = new HNot(instruction); | |
1698 } | |
1699 push(instruction); | |
1700 } else { | |
1701 visit(node.receiver); | |
1702 visit(node.argumentsNode); | |
1703 var right = pop(); | |
1704 var left = pop(); | |
1705 visitBinary(left, op, right); | |
1706 } | |
1707 } | |
1708 | |
1709 void addDynamicSendArgumentsToList(Send node, List<HInstruction> list) { | |
1710 Selector selector = elements.getSelector(node); | |
1711 if (selector.namedArgumentCount == 0) { | |
1712 addGenericSendArgumentsToList(node.arguments, list); | |
1713 } else { | |
1714 // Visit positional arguments and add them to the list. | |
1715 Link<Node> arguments = node.arguments; | |
1716 int positionalArgumentCount = selector.positionalArgumentCount; | |
1717 for (int i = 0; | |
1718 i < positionalArgumentCount; | |
1719 arguments = arguments.tail, i++) { | |
1720 visit(arguments.head); | |
1721 list.add(pop()); | |
1722 } | |
1723 | |
1724 // Visit named arguments and add them into a temporary map. | |
1725 Map<SourceString, HInstruction> instructions = | |
1726 new Map<SourceString, HInstruction>(); | |
1727 List<SourceString> namedArguments = selector.namedArguments; | |
1728 int nameIndex = 0; | |
1729 for (; !arguments.isEmpty(); arguments = arguments.tail) { | |
1730 visit(arguments.head); | |
1731 instructions[namedArguments[nameIndex++]] = pop(); | |
1732 } | |
1733 | |
1734 // Iterate through the named arguments to add them to the list | |
1735 // of instructions, in an order that can be shared with | |
1736 // selectors with the same named arguments. | |
1737 List<SourceString> orderedNames = selector.getOrderedNamedArguments(); | |
1738 for (SourceString name in orderedNames) { | |
1739 list.add(instructions[name]); | |
1740 } | |
1741 } | |
1742 } | |
1743 | |
1744 void addStaticSendArgumentsToList(Send node, | |
1745 FunctionElement element, | |
1746 List<HInstruction> list) { | |
1747 HInstruction compileArgument(Node argument) { | |
1748 visit(argument); | |
1749 return pop(); | |
1750 } | |
1751 | |
1752 HInstruction compileConstant(Element constantElement) { | |
1753 Constant constant = compiler.compileVariable(constantElement); | |
1754 return graph.addConstant(constant); | |
1755 } | |
1756 | |
1757 Selector selector = elements.getSelector(node); | |
1758 FunctionParameters parameters = element.computeParameters(compiler); | |
1759 bool succeeded = selector.addSendArgumentsToList(node, list, parameters, | |
1760 compileArgument, | |
1761 compileConstant); | |
1762 if (!succeeded) { | |
1763 // TODO(ngeoffray): Match the VM behavior and throw an | |
1764 // exception at runtime. | |
1765 compiler.cancel('Unimplemented non-matching static call', node: node); | |
1766 } | |
1767 } | |
1768 | |
1769 void addGenericSendArgumentsToList(Link<Node> link, List<HInstruction> list) { | |
1770 for (; !link.isEmpty(); link = link.tail) { | |
1771 visit(link.head); | |
1772 list.add(pop()); | |
1773 } | |
1774 } | |
1775 | |
1776 visitDynamicSend(Send node) { | |
1777 Selector selector = elements.getSelector(node); | |
1778 var inputs = <HInstruction>[]; | |
1779 | |
1780 SourceString dartMethodName; | |
1781 bool isNotEquals = false; | |
1782 if (node.isIndex && !node.arguments.tail.isEmpty()) { | |
1783 dartMethodName = Elements.constructOperatorName( | |
1784 const SourceString('operator'), | |
1785 const SourceString('[]=')); | |
1786 } else if (node.selector.asOperator() != null) { | |
1787 SourceString name = node.selector.asIdentifier().source; | |
1788 isNotEquals = name.stringValue === '!='; | |
1789 dartMethodName = Elements.constructOperatorName( | |
1790 const SourceString('operator'), | |
1791 name, | |
1792 node.argumentsNode is Prefix); | |
1793 } else { | |
1794 dartMethodName = node.selector.asIdentifier().source; | |
1795 } | |
1796 | |
1797 Element interceptor = null; | |
1798 if (methodInterceptionEnabled && node.receiver !== null) { | |
1799 interceptor = interceptors.getStaticInterceptor(dartMethodName, | |
1800 node.argumentCount()); | |
1801 } | |
1802 if (interceptor != null) { | |
1803 HStatic target = new HStatic(interceptor); | |
1804 add(target); | |
1805 inputs.add(target); | |
1806 visit(node.receiver); | |
1807 inputs.add(pop()); | |
1808 addGenericSendArgumentsToList(node.arguments, inputs); | |
1809 push(new HInvokeInterceptor(selector, dartMethodName, false, inputs)); | |
1810 return; | |
1811 } | |
1812 | |
1813 if (node.receiver === null) { | |
1814 inputs.add(localsHandler.readThis()); | |
1815 } else { | |
1816 visit(node.receiver); | |
1817 inputs.add(pop()); | |
1818 } | |
1819 | |
1820 addDynamicSendArgumentsToList(node, inputs); | |
1821 | |
1822 // The first entry in the inputs list is the receiver. | |
1823 push(new HInvokeDynamicMethod(selector, dartMethodName, inputs)); | |
1824 | |
1825 if (isNotEquals) { | |
1826 HNot not = new HNot(popBoolified()); | |
1827 push(not); | |
1828 } | |
1829 } | |
1830 | |
1831 visitClosureSend(Send node) { | |
1832 Selector selector = elements.getSelector(node); | |
1833 assert(node.receiver === null); | |
1834 Element element = elements[node]; | |
1835 HInstruction closureTarget; | |
1836 if (element === null) { | |
1837 visit(node.selector); | |
1838 closureTarget = pop(); | |
1839 } else { | |
1840 assert(Elements.isLocal(element)); | |
1841 closureTarget = localsHandler.readLocal(element); | |
1842 } | |
1843 var inputs = <HInstruction>[]; | |
1844 inputs.add(closureTarget); | |
1845 addDynamicSendArgumentsToList(node, inputs); | |
1846 push(new HInvokeClosure(selector, inputs)); | |
1847 } | |
1848 | |
1849 void handleForeignJs(Send node) { | |
1850 Link<Node> link = node.arguments; | |
1851 // If the invoke is on foreign code, don't visit the first | |
1852 // argument, which is the type, and the second argument, | |
1853 // which is the foreign code. | |
1854 if (link.isEmpty() || link.isEmpty()) { | |
1855 compiler.cancel('At least two arguments expected', node: node.arguments); | |
1856 } | |
1857 link = link.tail.tail; | |
1858 List<HInstruction> inputs = <HInstruction>[]; | |
1859 addGenericSendArgumentsToList(link, inputs); | |
1860 Node type = node.arguments.head; | |
1861 Node literal = node.arguments.tail.head; | |
1862 if (literal is !StringNode || literal.dynamic.isInterpolation) { | |
1863 compiler.cancel('JS code must be a string literal', node: literal); | |
1864 } | |
1865 if (type is !LiteralString) { | |
1866 compiler.cancel( | |
1867 'The type of a JS expression must be a string literal', node: type); | |
1868 } | |
1869 push(new HForeign( | |
1870 literal.dynamic.dartString, type.dynamic.dartString, inputs)); | |
1871 } | |
1872 | |
1873 void handleForeignUnintercepted(Send node) { | |
1874 Link<Node> link = node.arguments; | |
1875 if (!link.tail.isEmpty()) { | |
1876 compiler.cancel( | |
1877 'More than one expression in UNINTERCEPTED()', node: node); | |
1878 } | |
1879 Expression expression = link.head; | |
1880 disableMethodInterception(); | |
1881 visit(expression); | |
1882 enableMethodInterception(); | |
1883 } | |
1884 | |
1885 void handleForeignJsHasEquals(Send node) { | |
1886 List<HInstruction> inputs = <HInstruction>[]; | |
1887 if (!node.arguments.tail.isEmpty()) { | |
1888 compiler.cancel( | |
1889 'More than one expression in JS_HAS_EQUALS()', node: node); | |
1890 } | |
1891 addGenericSendArgumentsToList(node.arguments, inputs); | |
1892 String name = compiler.namer.instanceMethodName( | |
1893 currentLibrary, Namer.OPERATOR_EQUALS, 1); | |
1894 push(new HForeign(new DartString.literal('!!#.$name'), | |
1895 const LiteralDartString('bool'), | |
1896 inputs)); | |
1897 } | |
1898 | |
1899 void handleForeignJsCurrentIsolate(Send node) { | |
1900 if (!node.arguments.isEmpty()) { | |
1901 compiler.cancel( | |
1902 'Too many arguments to JS_CURRENT_ISOLATE', node: node); | |
1903 } | |
1904 | |
1905 if (!compiler.hasIsolateSupport()) { | |
1906 // If the isolate library is not used, we just generate code | |
1907 // to fetch the Leg's current isolate. | |
1908 String name = compiler.namer.CURRENT_ISOLATE; | |
1909 push(new HForeign(new DartString.literal(name), | |
1910 const LiteralDartString('var'), | |
1911 <HInstruction>[])); | |
1912 } else { | |
1913 // Call a helper method from the isolate library. The isolate | |
1914 // library uses its own isolate structure, that encapsulates | |
1915 // Leg's isolate. | |
1916 Element element = compiler.isolateLibrary.find( | |
1917 const SourceString('_currentIsolate')); | |
1918 if (element === null) { | |
1919 compiler.cancel( | |
1920 'Isolate library and compiler mismatch', node: node); | |
1921 } | |
1922 HStatic target = new HStatic(element); | |
1923 add(target); | |
1924 push(new HInvokeStatic(Selector.INVOCATION_0, | |
1925 <HInstruction>[target])); | |
1926 } | |
1927 } | |
1928 | |
1929 void handleForeignJsCallInIsolate(Send node) { | |
1930 Link<Node> link = node.arguments; | |
1931 if (!compiler.hasIsolateSupport()) { | |
1932 // If the isolate library is not used, we just invoke the | |
1933 // closure. | |
1934 visit(link.tail.head); | |
1935 push(new HInvokeClosure(Selector.INVOCATION_0, | |
1936 <HInstruction>[pop()])); | |
1937 } else { | |
1938 // Call a helper method from the isolate library. | |
1939 Element element = compiler.isolateLibrary.find( | |
1940 const SourceString('_callInIsolate')); | |
1941 if (element === null) { | |
1942 compiler.cancel( | |
1943 'Isolate library and compiler mismatch', node: node); | |
1944 } | |
1945 HStatic target = new HStatic(element); | |
1946 add(target); | |
1947 List<HInstruction> inputs = <HInstruction>[target]; | |
1948 addGenericSendArgumentsToList(link, inputs); | |
1949 push(new HInvokeStatic(Selector.INVOCATION_0, inputs)); | |
1950 } | |
1951 } | |
1952 | |
1953 void handleForeignDartClosureToJs(Send node) { | |
1954 if (node.arguments.isEmpty() || !node.arguments.tail.isEmpty()) { | |
1955 compiler.cancel('Exactly one argument required', node: node.arguments); | |
1956 } | |
1957 Node closure = node.arguments.head; | |
1958 Element element = elements[closure]; | |
1959 if (!Elements.isStaticOrTopLevelFunction(element)) { | |
1960 compiler.cancel( | |
1961 'JS_TO_CLOSURE requires a static or top-level method', | |
1962 node: closure); | |
1963 } | |
1964 FunctionElement function = element; | |
1965 FunctionParameters parameters = element.computeParameters(compiler); | |
1966 if (parameters.optionalParameterCount !== 0) { | |
1967 compiler.cancel( | |
1968 'JS_TO_CLOSURE does not handle closure with optional parameters', | |
1969 node: closure); | |
1970 } | |
1971 visit(closure); | |
1972 List<HInstruction> inputs = <HInstruction>[pop()]; | |
1973 String invocationName = compiler.namer.closureInvocationName( | |
1974 new Selector(SelectorKind.INVOCATION, | |
1975 parameters.requiredParameterCount)); | |
1976 push(new HForeign(new DartString.literal('#.$invocationName'), | |
1977 const LiteralDartString('var'), | |
1978 inputs)); | |
1979 } | |
1980 | |
1981 handleForeignSend(Send node) { | |
1982 Element element = elements[node]; | |
1983 if (element.name == const SourceString('JS')) { | |
1984 handleForeignJs(node); | |
1985 } else if (element.name == const SourceString('UNINTERCEPTED')) { | |
1986 handleForeignUnintercepted(node); | |
1987 } else if (element.name == const SourceString('JS_HAS_EQUALS')) { | |
1988 handleForeignJsHasEquals(node); | |
1989 } else if (element.name == const SourceString('JS_CURRENT_ISOLATE')) { | |
1990 handleForeignJsCurrentIsolate(node); | |
1991 } else if (element.name == const SourceString('JS_CALL_IN_ISOLATE')) { | |
1992 handleForeignJsCallInIsolate(node); | |
1993 } else if (element.name == const SourceString('DART_CLOSURE_TO_JS')) { | |
1994 handleForeignDartClosureToJs(node); | |
1995 } else if (element.name == const SourceString('native')) { | |
1996 native.handleSsaNative(this, node); | |
1997 } else { | |
1998 throw "Unknown foreign: ${node.selector}"; | |
1999 } | |
2000 } | |
2001 | |
2002 visitSuperSend(Send node) { | |
2003 Selector selector = elements.getSelector(node); | |
2004 Element element = elements[node]; | |
2005 if (element === null) { | |
2006 ClassElement cls = work.element.getEnclosingClass(); | |
2007 element = cls.lookupSuperMember(Compiler.NO_SUCH_METHOD); | |
2008 HStatic target = new HStatic(element); | |
2009 add(target); | |
2010 HInstruction self = localsHandler.readThis(); | |
2011 Identifier identifier = node.selector.asIdentifier(); | |
2012 String name = identifier.source.slowToString(); | |
2013 // TODO(ahe): Add the arguments to this list. | |
2014 push(new HLiteralList([], true)); | |
2015 var inputs = <HInstruction>[ | |
2016 target, | |
2017 self, | |
2018 graph.addConstantString(new DartString.literal(name)), | |
2019 pop()]; | |
2020 push(new HInvokeSuper(const Selector(SelectorKind.INVOCATION, 2), | |
2021 inputs)); | |
2022 return; | |
2023 } | |
2024 HInstruction target = new HStatic(element); | |
2025 HInstruction context = localsHandler.readThis(); | |
2026 add(target); | |
2027 var inputs = <HInstruction>[target, context]; | |
2028 if (element.kind == ElementKind.FUNCTION || | |
2029 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) { | |
2030 addStaticSendArgumentsToList(node, element, inputs); | |
2031 push(new HInvokeSuper(selector, inputs)); | |
2032 } else { | |
2033 target = new HInvokeSuper(Selector.GETTER, inputs); | |
2034 add(target); | |
2035 inputs = <HInstruction>[target]; | |
2036 addDynamicSendArgumentsToList(node, inputs); | |
2037 push(new HInvokeClosure(selector, inputs)); | |
2038 } | |
2039 } | |
2040 | |
2041 visitStaticSend(Send node) { | |
2042 Selector selector = elements.getSelector(node); | |
2043 Element element = elements[node]; | |
2044 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR) { | |
2045 compiler.resolver.resolveMethodElement(element); | |
2046 FunctionElement functionElement = element; | |
2047 element = functionElement.defaultImplementation; | |
2048 } | |
2049 HInstruction target = new HStatic(element); | |
2050 add(target); | |
2051 var inputs = <HInstruction>[]; | |
2052 inputs.add(target); | |
2053 if (element.kind == ElementKind.FUNCTION || | |
2054 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) { | |
2055 addStaticSendArgumentsToList(node, element, inputs); | |
2056 push(new HInvokeStatic(selector, inputs)); | |
2057 } else { | |
2058 if (element.kind == ElementKind.GETTER) { | |
2059 target = new HInvokeStatic(Selector.GETTER, inputs); | |
2060 add(target); | |
2061 inputs = <HInstruction>[target]; | |
2062 } | |
2063 addDynamicSendArgumentsToList(node, inputs); | |
2064 push(new HInvokeClosure(selector, inputs)); | |
2065 } | |
2066 } | |
2067 | |
2068 visitSend(Send node) { | |
2069 if (node.isSuperCall) { | |
2070 if (node.isPropertyAccess) { | |
2071 compiler.unimplemented('super property read', node: node); | |
2072 } | |
2073 visitSuperSend(node); | |
2074 } else if (node.selector is Operator && methodInterceptionEnabled) { | |
2075 visitOperatorSend(node); | |
2076 } else if (node.isPropertyAccess) { | |
2077 generateGetter(node, elements[node]); | |
2078 } else if (Elements.isClosureSend(node, elements)) { | |
2079 visitClosureSend(node); | |
2080 } else { | |
2081 Element element = elements[node]; | |
2082 if (element === null) { | |
2083 // Example: f() with 'f' unbound. | |
2084 // This can only happen inside an instance method. | |
2085 visitDynamicSend(node); | |
2086 } else if (element.kind == ElementKind.CLASS) { | |
2087 compiler.internalError("Cannot generate code for send", node: node); | |
2088 } else if (element.isInstanceMember()) { | |
2089 // Example: f() with 'f' bound to instance method. | |
2090 visitDynamicSend(node); | |
2091 } else if (element.kind === ElementKind.FOREIGN) { | |
2092 handleForeignSend(node); | |
2093 } else if (!element.isInstanceMember()) { | |
2094 // Example: A.f() or f() with 'f' bound to a static function. | |
2095 // Also includes new A() or new A.named() which is treated like a | |
2096 // static call to a factory. | |
2097 visitStaticSend(node); | |
2098 } else { | |
2099 compiler.internalError("Cannot generate code for send", node: node); | |
2100 } | |
2101 } | |
2102 } | |
2103 | |
2104 visitNewExpression(NewExpression node) { | |
2105 if (node.isConst()) { | |
2106 ConstantHandler handler = compiler.constantHandler; | |
2107 Constant constant = handler.compileNodeWithDefinitions(node, elements); | |
2108 stack.add(graph.addConstant(constant)); | |
2109 } else { | |
2110 visitSend(node.send); | |
2111 } | |
2112 } | |
2113 | |
2114 visitSendSet(SendSet node) { | |
2115 Operator op = node.assignmentOperator; | |
2116 if (node.isSuperCall) { | |
2117 compiler.unimplemented('super property store', node: node); | |
2118 } else if (node.isIndex) { | |
2119 if (!methodInterceptionEnabled) { | |
2120 assert(op.source.stringValue === '='); | |
2121 visitDynamicSend(node); | |
2122 } else { | |
2123 HStatic target = new HStatic( | |
2124 interceptors.getIndexAssignmentInterceptor()); | |
2125 add(target); | |
2126 visit(node.receiver); | |
2127 HInstruction receiver = pop(); | |
2128 visit(node.argumentsNode); | |
2129 if (const SourceString("=") == op.source) { | |
2130 HInstruction value = pop(); | |
2131 HInstruction index = pop(); | |
2132 add(new HIndexAssign(target, receiver, index, value)); | |
2133 stack.add(value); | |
2134 } else { | |
2135 HInstruction value; | |
2136 HInstruction index; | |
2137 bool isCompoundAssignment = op.source.stringValue.endsWith('='); | |
2138 // Compound assignments are considered as being prefix. | |
2139 bool isPrefix = !node.isPostfix; | |
2140 Element getter = elements[node.selector]; | |
2141 if (isCompoundAssignment) { | |
2142 value = pop(); | |
2143 index = pop(); | |
2144 } else { | |
2145 index = pop(); | |
2146 value = graph.addConstantInt(1); | |
2147 } | |
2148 HStatic indexMethod = new HStatic(interceptors.getIndexInterceptor()); | |
2149 add(indexMethod); | |
2150 HInstruction left = new HIndex(indexMethod, receiver, index); | |
2151 add(left); | |
2152 Element opElement = elements[op]; | |
2153 visitBinary(left, op, value); | |
2154 value = pop(); | |
2155 HInstruction assign = new HIndexAssign( | |
2156 target, receiver, index, value); | |
2157 add(assign); | |
2158 if (isPrefix) { | |
2159 stack.add(value); | |
2160 } else { | |
2161 stack.add(left); | |
2162 } | |
2163 } | |
2164 } | |
2165 } else if (const SourceString("=") == op.source) { | |
2166 Element element = elements[node]; | |
2167 Link<Node> link = node.arguments; | |
2168 assert(!link.isEmpty() && link.tail.isEmpty()); | |
2169 visit(link.head); | |
2170 HInstruction value = pop(); | |
2171 generateSetter(node, element, value); | |
2172 } else if (op.source.stringValue === "is") { | |
2173 compiler.internalError("is-operator as SendSet", node: op); | |
2174 } else { | |
2175 assert(const SourceString("++") == op.source || | |
2176 const SourceString("--") == op.source || | |
2177 node.assignmentOperator.source.stringValue.endsWith("=")); | |
2178 Element element = elements[node]; | |
2179 bool isCompoundAssignment = !node.arguments.isEmpty(); | |
2180 bool isPrefix = !node.isPostfix; // Compound assignments are prefix. | |
2181 generateGetter(node, elements[node.selector]); | |
2182 HInstruction left = pop(); | |
2183 HInstruction right; | |
2184 if (isCompoundAssignment) { | |
2185 visit(node.argumentsNode); | |
2186 right = pop(); | |
2187 } else { | |
2188 right = graph.addConstantInt(1); | |
2189 } | |
2190 visitBinary(left, op, right); | |
2191 HInstruction operation = pop(); | |
2192 assert(operation !== null); | |
2193 generateSetter(node, element, operation); | |
2194 if (!isPrefix) { | |
2195 pop(); | |
2196 stack.add(left); | |
2197 } | |
2198 } | |
2199 } | |
2200 | |
2201 void visitLiteralInt(LiteralInt node) { | |
2202 stack.add(graph.addConstantInt(node.value)); | |
2203 } | |
2204 | |
2205 void visitLiteralDouble(LiteralDouble node) { | |
2206 stack.add(graph.addConstantDouble(node.value)); | |
2207 } | |
2208 | |
2209 void visitLiteralBool(LiteralBool node) { | |
2210 stack.add(graph.addConstantBool(node.value)); | |
2211 } | |
2212 | |
2213 void visitLiteralString(LiteralString node) { | |
2214 stack.add(graph.addConstantString(node.dartString)); | |
2215 } | |
2216 | |
2217 void visitStringJuxtaposition(StringJuxtaposition node) { | |
2218 if (!node.isInterpolation) { | |
2219 // This is a simple string with no interpolations. | |
2220 stack.add(graph.addConstantString(node.dartString)); | |
2221 return; | |
2222 } | |
2223 int offset = node.getBeginToken().charOffset; | |
2224 StringBuilderVisitor stringBuilder = | |
2225 new StringBuilderVisitor(this, offset); | |
2226 stringBuilder.visit(node); | |
2227 stack.add(stringBuilder.result()); | |
2228 } | |
2229 | |
2230 void visitLiteralNull(LiteralNull node) { | |
2231 stack.add(graph.addConstantNull()); | |
2232 } | |
2233 | |
2234 visitNodeList(NodeList node) { | |
2235 for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) { | |
2236 if (isAborted()) { | |
2237 compiler.reportWarning(link.head, 'dead code'); | |
2238 } else { | |
2239 visit(link.head); | |
2240 } | |
2241 } | |
2242 } | |
2243 | |
2244 void visitParenthesizedExpression(ParenthesizedExpression node) { | |
2245 visit(node.expression); | |
2246 } | |
2247 | |
2248 visitOperator(Operator node) { | |
2249 // Operators are intercepted in their surrounding Send nodes. | |
2250 unreachable(); | |
2251 } | |
2252 | |
2253 visitReturn(Return node) { | |
2254 HInstruction value; | |
2255 if (node.expression === null) { | |
2256 value = graph.addConstantNull(); | |
2257 } else { | |
2258 visit(node.expression); | |
2259 value = pop(); | |
2260 } | |
2261 close(new HReturn(value)).addSuccessor(graph.exit); | |
2262 } | |
2263 | |
2264 visitThrow(Throw node) { | |
2265 if (node.expression === null) { | |
2266 HInstruction exception = rethrowableException; | |
2267 if (exception === null) { | |
2268 exception = graph.addConstantNull(); | |
2269 compiler.reportError(node, | |
2270 'throw without expression outside catch block'); | |
2271 } | |
2272 close(new HThrow(exception, isRethrow: true)); | |
2273 } else { | |
2274 visit(node.expression); | |
2275 close(new HThrow(pop())); | |
2276 } | |
2277 } | |
2278 | |
2279 visitTypeAnnotation(TypeAnnotation node) { | |
2280 compiler.internalError('visiting type annotation in SSA builder', | |
2281 node: node); | |
2282 } | |
2283 | |
2284 visitVariableDefinitions(VariableDefinitions node) { | |
2285 for (Link<Node> link = node.definitions.nodes; | |
2286 !link.isEmpty(); | |
2287 link = link.tail) { | |
2288 Node definition = link.head; | |
2289 if (definition is Identifier) { | |
2290 HInstruction initialValue = graph.addConstantNull(); | |
2291 localsHandler.updateLocal(elements[definition], initialValue); | |
2292 } else { | |
2293 assert(definition is SendSet); | |
2294 visitSendSet(definition); | |
2295 pop(); // Discard value. | |
2296 } | |
2297 } | |
2298 } | |
2299 | |
2300 visitLiteralList(LiteralList node) { | |
2301 List<HInstruction> inputs = <HInstruction>[]; | |
2302 for (Link<Node> link = node.elements.nodes; | |
2303 !link.isEmpty(); | |
2304 link = link.tail) { | |
2305 visit(link.head); | |
2306 inputs.add(pop()); | |
2307 } | |
2308 push(new HLiteralList(inputs, node.isConst())); | |
2309 } | |
2310 | |
2311 visitConditional(Conditional node) { | |
2312 visit(node.condition); | |
2313 HIf condition = new HIf(popBoolified(), true); | |
2314 HBasicBlock conditionBlock = close(condition); | |
2315 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
2316 | |
2317 HBasicBlock thenBlock = addNewBlock(); | |
2318 conditionBlock.addSuccessor(thenBlock); | |
2319 open(thenBlock); | |
2320 visit(node.thenExpression); | |
2321 HInstruction thenInstruction = pop(); | |
2322 SubGraph thenGraph = new SubGraph(thenBlock, current); | |
2323 thenBlock = close(new HGoto()); | |
2324 LocalsHandler thenLocals = localsHandler; | |
2325 localsHandler = savedLocals; | |
2326 | |
2327 HBasicBlock elseBlock = addNewBlock(); | |
2328 conditionBlock.addSuccessor(elseBlock); | |
2329 open(elseBlock); | |
2330 visit(node.elseExpression); | |
2331 HInstruction elseInstruction = pop(); | |
2332 SubGraph elseGraph = new SubGraph(elseBlock, current); | |
2333 elseBlock = close(new HGoto()); | |
2334 | |
2335 HBasicBlock joinBlock = addNewBlock(); | |
2336 thenBlock.addSuccessor(joinBlock); | |
2337 elseBlock.addSuccessor(joinBlock); | |
2338 condition.blockInformation = | |
2339 new HIfBlockInformation(condition, thenGraph, elseGraph, joinBlock); | |
2340 open(joinBlock); | |
2341 | |
2342 localsHandler.mergeWith(thenLocals, joinBlock); | |
2343 HPhi phi = new HPhi.manyInputs(null, | |
2344 <HInstruction>[thenInstruction, elseInstruction]); | |
2345 joinBlock.addPhi(phi); | |
2346 stack.add(phi); | |
2347 } | |
2348 | |
2349 visitStringInterpolation(StringInterpolation node) { | |
2350 int offset = node.getBeginToken().charOffset; | |
2351 StringBuilderVisitor stringBuilder = | |
2352 new StringBuilderVisitor(this, offset); | |
2353 stringBuilder.visit(node); | |
2354 stack.add(stringBuilder.result()); | |
2355 } | |
2356 | |
2357 visitStringInterpolationPart(StringInterpolationPart node) { | |
2358 // The parts are iterated in visitStringInterpolation. | |
2359 unreachable(); | |
2360 } | |
2361 | |
2362 visitEmptyStatement(EmptyStatement node) { | |
2363 // Do nothing, empty statement. | |
2364 } | |
2365 | |
2366 visitModifiers(Modifiers node) { | |
2367 compiler.unimplemented('SsaBuilder.visitModifiers', node: node); | |
2368 } | |
2369 | |
2370 visitBreakStatement(BreakStatement node) { | |
2371 work.allowSpeculativeOptimization = false; | |
2372 assert(!isAborted()); | |
2373 TargetElement target = elements[node]; | |
2374 assert(target !== null); | |
2375 JumpHandler handler = jumpTargets[target]; | |
2376 assert(handler !== null); | |
2377 if (node.target === null) { | |
2378 handler.generateBreak(); | |
2379 } else { | |
2380 LabelElement label = elements[node.target]; | |
2381 handler.generateBreak(label); | |
2382 } | |
2383 } | |
2384 | |
2385 visitContinueStatement(ContinueStatement node) { | |
2386 work.allowSpeculativeOptimization = false; | |
2387 TargetElement target = elements[node]; | |
2388 assert(target !== null); | |
2389 JumpHandler handler = jumpTargets[target]; | |
2390 assert(handler !== null); | |
2391 if (node.target === null) { | |
2392 handler.generateContinue(); | |
2393 } else { | |
2394 LabelElement label = elements[node.target]; | |
2395 handler.generateContinue(label); | |
2396 } | |
2397 } | |
2398 | |
2399 /** | |
2400 * Creates a [JumpHandler] for a statement. The node must be a jump | |
2401 * target. If there are no breaks or continues targeting the statement, | |
2402 * a special "null handler" is returned. | |
2403 */ | |
2404 JumpHandler createJumpHandler(Statement node) { | |
2405 TargetElement element = elements[node]; | |
2406 if (element === null || element.statement !== node) { | |
2407 // No breaks or continues to this node. | |
2408 return const NullJumpHandler(); | |
2409 } | |
2410 return new JumpHandler(this, element); | |
2411 } | |
2412 | |
2413 visitForInStatement(ForInStatement node) { | |
2414 // Generate a structure equivalent to: | |
2415 // Iterator<E> $iter = <iterable>.iterator() | |
2416 // while ($iter.hasNext()) { | |
2417 // E <declaredIdentifier> = $iter.next(); | |
2418 // <body> | |
2419 // } | |
2420 | |
2421 // All the generated calls are to zero-argument functions. | |
2422 Selector selector = Selector.INVOCATION_0; | |
2423 // The iterator is shared between initializer, condition and body. | |
2424 HInstruction iterator; | |
2425 void buildInitializer() { | |
2426 SourceString iteratorName = const SourceString("iterator"); | |
2427 Element interceptor = interceptors.getStaticInterceptor(iteratorName, 0); | |
2428 assert(interceptor != null); | |
2429 HStatic target = new HStatic(interceptor); | |
2430 add(target); | |
2431 visit(node.expression); | |
2432 List<HInstruction> inputs = <HInstruction>[target, pop()]; | |
2433 iterator = new HInvokeInterceptor(selector, iteratorName, false, inputs); | |
2434 add(iterator); | |
2435 } | |
2436 HInstruction buildCondition() { | |
2437 push(new HInvokeDynamicMethod( | |
2438 selector, const SourceString('hasNext'), <HInstruction>[iterator])); | |
2439 return popBoolified(); | |
2440 } | |
2441 void buildBody() { | |
2442 push(new HInvokeDynamicMethod( | |
2443 selector, const SourceString('next'), <HInstruction>[iterator])); | |
2444 | |
2445 Element variable; | |
2446 if (node.declaredIdentifier.asSend() !== null) { | |
2447 variable = elements[node.declaredIdentifier]; | |
2448 } else { | |
2449 assert(node.declaredIdentifier.asVariableDefinitions() !== null); | |
2450 VariableDefinitions variableDefinitions = node.declaredIdentifier; | |
2451 variable = elements[variableDefinitions.definitions.nodes.head]; | |
2452 } | |
2453 localsHandler.updateLocal(variable, pop()); | |
2454 | |
2455 visit(node.body); | |
2456 } | |
2457 handleLoop(node, buildInitializer, buildCondition, () {}, buildBody); | |
2458 } | |
2459 | |
2460 visitLabeledStatement(LabeledStatement node) { | |
2461 Statement body = node.getBody(); | |
2462 if (body is Loop || body is SwitchStatement) { | |
2463 // Loops and switches handle their own labels. | |
2464 visit(body); | |
2465 return; | |
2466 } | |
2467 // Non-loop statements can only be break targets, not continue targets. | |
2468 TargetElement targetElement = elements[body]; | |
2469 if (targetElement === null || targetElement.statement !== body) { | |
2470 // Labeled statements with no element on the body have no breaks. | |
2471 // A different target statement only happens if the body is itself | |
2472 // a break or continue for a different target. In that case, this | |
2473 // label is also always unused. | |
2474 visit(body); | |
2475 return; | |
2476 } | |
2477 LocalsHandler beforeLocals = new LocalsHandler.from(localsHandler); | |
2478 assert(targetElement.isBreakTarget); | |
2479 JumpHandler handler = new JumpHandler(this, targetElement); | |
2480 // Introduce a new basic block. | |
2481 HBasicBlock entryBlock = graph.addNewBlock(); | |
2482 goto(current, entryBlock); | |
2483 open(entryBlock); | |
2484 hackAroundPossiblyAbortingBody(node, () { visit(body); }); | |
2485 SubGraph bodyGraph = new SubGraph(entryBlock, lastOpenedBlock); | |
2486 | |
2487 HBasicBlock joinBlock = graph.addNewBlock(); | |
2488 List<LocalsHandler> breakLocals = <LocalsHandler>[]; | |
2489 handler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) { | |
2490 breakInstruction.block.addSuccessor(joinBlock); | |
2491 breakLocals.add(locals); | |
2492 }); | |
2493 bool hasBreak = breakLocals.length > 0; | |
2494 if (!isAborted()) { | |
2495 goto(current, joinBlock); | |
2496 breakLocals.add(localsHandler); | |
2497 } | |
2498 open(joinBlock); | |
2499 localsHandler = beforeLocals.mergeMultiple(breakLocals, joinBlock); | |
2500 | |
2501 if (hasBreak) { | |
2502 // There was at least one reachable break, so the label is needed. | |
2503 HLabeledBlockInformation blockInfo = | |
2504 new HLabeledBlockInformation(bodyGraph, joinBlock, handler.labels()); | |
2505 entryBlock.labeledBlockInformation = blockInfo; | |
2506 } | |
2507 handler.close(); | |
2508 } | |
2509 | |
2510 visitLiteralMap(LiteralMap node) { | |
2511 List<HInstruction> inputs = <HInstruction>[]; | |
2512 for (Link<Node> link = node.entries.nodes; | |
2513 !link.isEmpty(); | |
2514 link = link.tail) { | |
2515 visit(link.head); | |
2516 inputs.addLast(pop()); | |
2517 inputs.addLast(pop()); | |
2518 } | |
2519 HLiteralList keyValuePairs = new HLiteralList(inputs, node.isConst()); | |
2520 HStatic mapMaker = new HStatic(interceptors.getMapMaker()); | |
2521 add(keyValuePairs); | |
2522 add(mapMaker); | |
2523 inputs = <HInstruction>[mapMaker, keyValuePairs]; | |
2524 push(new HInvokeStatic(Selector.INVOCATION_1, inputs)); | |
2525 } | |
2526 | |
2527 visitLiteralMapEntry(LiteralMapEntry node) { | |
2528 visit(node.value); | |
2529 visit(node.key); | |
2530 } | |
2531 | |
2532 visitNamedArgument(NamedArgument node) { | |
2533 visit(node.expression); | |
2534 } | |
2535 | |
2536 visitSwitchStatement(SwitchStatement node) { | |
2537 work.allowSpeculativeOptimization = false; | |
2538 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
2539 HBasicBlock startBlock = graph.addNewBlock(); | |
2540 goto(current, startBlock); | |
2541 open(startBlock); | |
2542 visit(node.expression); | |
2543 HInstruction expression = pop(); | |
2544 if (node.cases.isEmpty()) { | |
2545 return; | |
2546 } | |
2547 Link<Node> cases = node.cases.nodes; | |
2548 | |
2549 JumpHandler jumpHandler = createJumpHandler(node); | |
2550 | |
2551 buildSwitchCases(cases, expression); | |
2552 | |
2553 HBasicBlock lastBlock = lastOpenedBlock; | |
2554 | |
2555 // Create merge block for break targets. | |
2556 HBasicBlock joinBlock = new HBasicBlock(); | |
2557 List<LocalsHandler> caseLocals = <LocalsHandler>[]; | |
2558 jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) { | |
2559 instruction.block.addSuccessor(joinBlock); | |
2560 caseLocals.add(locals); | |
2561 }); | |
2562 if (!isAborted()) { | |
2563 // The current flow is only aborted if the switch has a default that | |
2564 // aborts (all previous cases must abort, and if there is no default, | |
2565 // it's possible to miss all the cases). | |
2566 caseLocals.add(localsHandler); | |
2567 goto(current, joinBlock); | |
2568 } | |
2569 if (caseLocals.length != 0) { | |
2570 graph.addBlock(joinBlock); | |
2571 open(joinBlock); | |
2572 if (caseLocals.length == 1) { | |
2573 localsHandler = caseLocals[0]; | |
2574 } else { | |
2575 localsHandler = savedLocals.mergeMultiple(caseLocals, joinBlock); | |
2576 } | |
2577 } else { | |
2578 // The joinblock is not used. | |
2579 joinBlock = null; | |
2580 } | |
2581 startBlock.labeledBlockInformation = new HLabeledBlockInformation.implicit( | |
2582 new SubGraph(startBlock, lastBlock), | |
2583 joinBlock, | |
2584 elements[node]); | |
2585 jumpHandler.close(); | |
2586 } | |
2587 | |
2588 | |
2589 // Recursively build an if/else structure to match the cases. | |
2590 buildSwitchCases(Link<Node> cases, HInstruction expression) { | |
2591 SwitchCase node = cases.head; | |
2592 | |
2593 // Called for the statements on all but the last case block. | |
2594 // Ensures that a user expecting a fallthrough gets an error. | |
2595 void visitStatementsAndAbort() { | |
2596 visit(node.statements); | |
2597 if (!isAborted()) { | |
2598 compiler.reportWarning(node, 'Missing break at end of switch case'); | |
2599 Element element = | |
2600 compiler.findHelper(const SourceString("getFallThroughError")); | |
2601 push(new HStatic(element)); | |
2602 HInstruction error = new HInvokeStatic( | |
2603 Selector.INVOCATION_0, <HInstruction>[pop()]); | |
2604 add(error); | |
2605 close(new HThrow(error)); | |
2606 } | |
2607 } | |
2608 | |
2609 Link<Node> expressions = node.expressions.nodes; | |
2610 if (expressions.isEmpty()) { | |
2611 // Default case with no expressions. | |
2612 if (!node.isDefaultCase) { | |
2613 compiler.internalError("Case with no expression and not default", | |
2614 node: node); | |
2615 } | |
2616 visit(node.statements); | |
2617 // This must be the final case (otherwise "default" would be invalid), | |
2618 // so we don't need to check for fallthrough. | |
2619 return; | |
2620 } | |
2621 | |
2622 // Recursively build the test conditions. Leaves the result on the | |
2623 // expression stack. | |
2624 void buildTests(Link<Node> expressions) { | |
2625 // Build comparison for one case expression. | |
2626 void left() { | |
2627 Element equalsHelper = interceptors.getEqualsInterceptor(); | |
2628 HInstruction target = new HStatic(equalsHelper); | |
2629 add(target); | |
2630 visit(expressions.head); | |
2631 push(new HEquals(target, pop(), expression)); | |
2632 } | |
2633 | |
2634 // If this is the last expression, just return it. | |
2635 if (expressions.tail.isEmpty()) { | |
2636 left(); | |
2637 return; | |
2638 } | |
2639 | |
2640 void right() { | |
2641 buildTests(expressions.tail); | |
2642 } | |
2643 handleLogicalAndOr(left, right, isAnd: false); | |
2644 } | |
2645 | |
2646 buildTests(expressions); | |
2647 HInstruction result = popBoolified(); | |
2648 | |
2649 if (node.isDefaultCase) { | |
2650 // Don't actually use the condition result. | |
2651 // This must be final case, so don't check for abort. | |
2652 visit(node.statements); | |
2653 } else { | |
2654 stack.add(result); | |
2655 if (cases.tail.isEmpty()) { | |
2656 handleIf(() { visit(node.statements); }, null); | |
2657 } else { | |
2658 handleIf(() { visitStatementsAndAbort(); }, | |
2659 () { buildSwitchCases(cases.tail, expression); }); | |
2660 } | |
2661 } | |
2662 } | |
2663 | |
2664 visitSwitchCase(SwitchCase node) { | |
2665 unreachable(); | |
2666 } | |
2667 | |
2668 visitTryStatement(TryStatement node) { | |
2669 work.allowSpeculativeOptimization = false; | |
2670 HBasicBlock enterBlock = graph.addNewBlock(); | |
2671 close(new HGoto()).addSuccessor(enterBlock); | |
2672 open(enterBlock); | |
2673 HTry tryInstruction = new HTry(); | |
2674 List<HBasicBlock> blocks = <HBasicBlock>[]; | |
2675 blocks.add(close(tryInstruction)); | |
2676 | |
2677 HBasicBlock tryBody = graph.addNewBlock(); | |
2678 enterBlock.addSuccessor(tryBody); | |
2679 open(tryBody); | |
2680 visit(node.tryBlock); | |
2681 if (!isAborted()) blocks.add(close(new HGoto())); | |
2682 | |
2683 if (!node.catchBlocks.isEmpty()) { | |
2684 HBasicBlock block = graph.addNewBlock(); | |
2685 enterBlock.addSuccessor(block); | |
2686 open(block); | |
2687 // Note that the name of this element is irrelevant. | |
2688 Element element = new Element( | |
2689 const SourceString('exception'), ElementKind.PARAMETER, work.element); | |
2690 HParameterValue exception = new HParameterValue(element); | |
2691 add(exception); | |
2692 HInstruction oldRethrowableException = rethrowableException; | |
2693 rethrowableException = exception; | |
2694 push(new HStatic(interceptors.getExceptionUnwrapper())); | |
2695 List<HInstruction> inputs = <HInstruction>[pop(), exception]; | |
2696 HInvokeStatic unwrappedException = | |
2697 new HInvokeStatic(Selector.INVOCATION_1, inputs); | |
2698 add(unwrappedException); | |
2699 tryInstruction.exception = exception; | |
2700 | |
2701 Link<Node> link = node.catchBlocks.nodes; | |
2702 | |
2703 void pushCondition(CatchBlock catchBlock) { | |
2704 VariableDefinitions declaration = catchBlock.formals.nodes.head; | |
2705 HInstruction condition = null; | |
2706 if (declaration.type == null) { | |
2707 condition = graph.addConstantBool(true); | |
2708 stack.add(condition); | |
2709 } else { | |
2710 Element typeElement = elements[declaration.type]; | |
2711 if (typeElement == null) { | |
2712 compiler.cancel('Catch with unresolved type', node: catchBlock); | |
2713 } | |
2714 condition = new HIs(typeElement, unwrappedException, nullOk: true); | |
2715 push(condition); | |
2716 } | |
2717 } | |
2718 | |
2719 void visitThen() { | |
2720 CatchBlock catchBlock = link.head; | |
2721 link = link.tail; | |
2722 localsHandler.updateLocal(elements[catchBlock.exception], | |
2723 unwrappedException); | |
2724 Node trace = catchBlock.trace; | |
2725 if (trace != null) { | |
2726 push(new HStatic(interceptors.getTraceFromException())); | |
2727 HInstruction traceInstruction = new HInvokeStatic( | |
2728 Selector.INVOCATION_1, <HInstruction>[pop(), exception]); | |
2729 add(traceInstruction); | |
2730 localsHandler.updateLocal(elements[trace], traceInstruction); | |
2731 } | |
2732 visit(catchBlock); | |
2733 } | |
2734 | |
2735 void visitElse() { | |
2736 if (link.isEmpty()) { | |
2737 close(new HThrow(exception, isRethrow: true)); | |
2738 } else { | |
2739 CatchBlock newBlock = link.head; | |
2740 pushCondition(newBlock); | |
2741 handleIf(visitThen, visitElse); | |
2742 } | |
2743 } | |
2744 | |
2745 CatchBlock firstBlock = link.head; | |
2746 pushCondition(firstBlock); | |
2747 handleIf(visitThen, visitElse); | |
2748 if (!isAborted()) blocks.add(close(new HGoto())); | |
2749 rethrowableException = oldRethrowableException; | |
2750 } | |
2751 | |
2752 if (node.finallyBlock != null) { | |
2753 HBasicBlock finallyBlock = graph.addNewBlock(); | |
2754 enterBlock.addSuccessor(finallyBlock); | |
2755 open(finallyBlock); | |
2756 visit(node.finallyBlock); | |
2757 if (!isAborted()) blocks.add(close(new HGoto())); | |
2758 tryInstruction.finallyBlock = finallyBlock; | |
2759 } | |
2760 | |
2761 HBasicBlock exitBlock = graph.addNewBlock(); | |
2762 | |
2763 for (HBasicBlock block in blocks) { | |
2764 block.addSuccessor(exitBlock); | |
2765 } | |
2766 | |
2767 open(exitBlock); | |
2768 } | |
2769 | |
2770 visitScriptTag(ScriptTag node) { | |
2771 compiler.unimplemented('SsaBuilder.visitScriptTag', node: node); | |
2772 } | |
2773 | |
2774 visitCatchBlock(CatchBlock node) { | |
2775 visit(node.block); | |
2776 } | |
2777 | |
2778 visitTypedef(Typedef node) { | |
2779 compiler.unimplemented('SsaBuilder.visitTypedef', node: node); | |
2780 } | |
2781 | |
2782 visitTypeVariable(TypeVariable node) { | |
2783 compiler.internalError('SsaBuilder.visitTypeVariable'); | |
2784 } | |
2785 | |
2786 generateUnimplemented(String reason, [bool isExpression = false]) { | |
2787 DartString string = new DartString.literal(reason); | |
2788 HInstruction message = graph.addConstantString(string); | |
2789 | |
2790 // Normally, we would call [close] here. However, then we hit | |
2791 // another unimplemented feature: aborting loop body. Simply | |
2792 // calling [add] does not work as it asserts that the instruction | |
2793 // isn't a control flow instruction. So we inline parts of [add]. | |
2794 current.addAfter(current.last, new HThrow(message)); | |
2795 if (isExpression) { | |
2796 stack.add(graph.addConstantNull()); | |
2797 } | |
2798 } | |
2799 | |
2800 /** HACK HACK HACK */ | |
2801 void hackAroundPossiblyAbortingBody(Node statement, void body()) { | |
2802 stack.add(graph.addConstantBool(true)); | |
2803 buildBody() { | |
2804 // TODO(lrn): Make sure to take continue into account. | |
2805 body(); | |
2806 if (isAborted()) { | |
2807 compiler.reportWarning(statement, "aborting loop body"); | |
2808 } | |
2809 } | |
2810 handleIf(buildBody, null); | |
2811 } | |
2812 } | |
2813 | |
2814 /** | |
2815 * Visitor that handles generation of string literals (LiteralString, | |
2816 * StringInterpolation), and otherwise delegates to the given visitor for | |
2817 * non-literal subexpressions. | |
2818 * TODO(lrn): Consider whether to handle compile time constant int/boolean | |
2819 * expressions as well. | |
2820 */ | |
2821 class StringBuilderVisitor extends AbstractVisitor { | |
2822 final SsaBuilder builder; | |
2823 | |
2824 /** | |
2825 * Offset used for the synthetic operator token used by concat. | |
2826 * Can probably be removed when we stop using String.operator+. | |
2827 */ | |
2828 final int offset; | |
2829 | |
2830 /** | |
2831 * Used to collect concatenated string literals into a single literal | |
2832 * instead of introducing unnecessary concatenations. | |
2833 */ | |
2834 DartString literalAccumulator = const LiteralDartString(""); | |
2835 | |
2836 /** | |
2837 * The string value generated so far (not including that which is still | |
2838 * in [literalAccumulator]). | |
2839 */ | |
2840 HInstruction prefix = null; | |
2841 | |
2842 StringBuilderVisitor(this.builder, this.offset); | |
2843 | |
2844 void visit(Node node) { | |
2845 node.accept(this); | |
2846 } | |
2847 | |
2848 visitNode(Node node) { | |
2849 compiler.internalError('unexpected node', node: node); | |
2850 } | |
2851 | |
2852 void visitExpression(Node node) { | |
2853 flushLiterals(); | |
2854 node.accept(builder); | |
2855 HInstruction asString = buildToString(node, builder.pop()); | |
2856 prefix = concat(prefix, asString); | |
2857 } | |
2858 | |
2859 void visitLiteralNull(LiteralNull node) { | |
2860 addLiteral(const LiteralDartString("null")); | |
2861 } | |
2862 | |
2863 void visitLiteralInt(LiteralInt node) { | |
2864 addLiteral(new DartString.literal(node.value.toString())); | |
2865 } | |
2866 | |
2867 void visitLiteralDouble(LiteralDouble node) { | |
2868 addLiteral(new DartString.literal(node.value.toString())); | |
2869 } | |
2870 | |
2871 void visitLiteralBool(LiteralBool node) { | |
2872 addLiteral(node.value ? const LiteralDartString("true") | |
2873 : const LiteralDartString("false")); | |
2874 } | |
2875 | |
2876 void visitStringInterpolation(StringInterpolation node) { | |
2877 node.visitChildren(this); | |
2878 } | |
2879 | |
2880 void visitStringInterpolationPart(StringInterpolationPart node) { | |
2881 visit(node.expression); | |
2882 visit(node.string); | |
2883 } | |
2884 | |
2885 void visitLiteralString(LiteralString node) { | |
2886 addLiteral(node.dartString); | |
2887 } | |
2888 | |
2889 void visitStringJuxtaposition(StringJuxtaposition node) { | |
2890 node.visitChildren(this); | |
2891 } | |
2892 | |
2893 void visitNodeList(NodeList node) { | |
2894 node.visitChildren(this); | |
2895 } | |
2896 | |
2897 /** | |
2898 * Add another literal string to the literalAccumulator. | |
2899 */ | |
2900 void addLiteral(DartString dartString) { | |
2901 literalAccumulator = new DartString.concat(literalAccumulator, dartString); | |
2902 } | |
2903 | |
2904 /** | |
2905 * Combine the strings in [literalAccumulator] into the prefix instruction. | |
2906 * After this, the [literalAccumulator] is empty and [prefix] is non-null. | |
2907 */ | |
2908 void flushLiterals() { | |
2909 if (literalAccumulator.isEmpty()) { | |
2910 if (prefix === null) { | |
2911 prefix = builder.graph.addConstantString(literalAccumulator); | |
2912 } | |
2913 return; | |
2914 } | |
2915 HInstruction string = builder.graph.addConstantString(literalAccumulator); | |
2916 literalAccumulator = new DartString.empty(); | |
2917 if (prefix !== null) { | |
2918 prefix = concat(prefix, string); | |
2919 } else { | |
2920 prefix = string; | |
2921 } | |
2922 } | |
2923 | |
2924 HInstruction concat(HInstruction left, HInstruction right) { | |
2925 SourceString dartMethodName = const SourceString("concat"); | |
2926 if (!builder.methodInterceptionEnabled) { | |
2927 builder.compiler.internalError( | |
2928 "Using string interpolations in non-intercepted code.", | |
2929 instruction: right); | |
2930 } | |
2931 Element interceptor = | |
2932 builder.interceptors.getStaticInterceptor(dartMethodName, 1); | |
2933 if (interceptor === null) { | |
2934 builder.compiler.internalError( | |
2935 "concat not intercepted.", instruction: left); | |
2936 } | |
2937 HStatic target = new HStatic(interceptor); | |
2938 builder.add(target); | |
2939 builder.push(new HInvokeInterceptor(Selector.INVOCATION_1, | |
2940 dartMethodName, | |
2941 false, | |
2942 <HInstruction>[target, left, right])); | |
2943 return builder.pop(); | |
2944 } | |
2945 | |
2946 HInstruction buildToString(Node node, HInstruction input) { | |
2947 SourceString dartMethodName = const SourceString("toString"); | |
2948 if (!builder.methodInterceptionEnabled) { | |
2949 builder.compiler.internalError( | |
2950 "Using string interpolations in non-intercepted code.", node: node); | |
2951 } | |
2952 Element interceptor = | |
2953 builder.interceptors.getStaticInterceptor(dartMethodName, 0); | |
2954 if (interceptor === null) { | |
2955 builder.compiler.internalError( | |
2956 "toString not intercepted.", node: node); | |
2957 } | |
2958 HStatic target = new HStatic(interceptor); | |
2959 builder.add(target); | |
2960 builder.push(new HInvokeInterceptor(Selector.INVOCATION_0, | |
2961 dartMethodName, | |
2962 false, | |
2963 <HInstruction>[target, input])); | |
2964 return builder.pop(); | |
2965 } | |
2966 | |
2967 HInstruction result() { | |
2968 flushLiterals(); | |
2969 return prefix; | |
2970 } | |
2971 } | |
OLD | NEW |