Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(542)

Side by Side Diff: frog/leg/ssa/builder.dart

Issue 9873021: Move frog/leg to lib/compiler/implementation. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « frog/leg/ssa/bailout.dart ('k') | frog/leg/ssa/closure.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « frog/leg/ssa/bailout.dart ('k') | frog/leg/ssa/closure.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698