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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/ssa/builder.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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
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 part of ssa;
6
7 /// A synthetic local variable only used with the SSA graph.
8 ///
9 /// For instance used for holding return value of function or the exception of a
10 /// try-catch statement.
11 class SyntheticLocal extends Local {
12 final String name;
13 final ExecutableElement executableContext;
14
15 SyntheticLocal(this.name, this.executableContext);
16 }
17
18 class SsaBuilderTask extends CompilerTask {
19 final CodeEmitterTask emitter;
20 final JavaScriptBackend backend;
21
22 String get name => 'SSA builder';
23
24 SsaBuilderTask(JavaScriptBackend backend)
25 : emitter = backend.emitter,
26 backend = backend,
27 super(backend.compiler);
28
29 HGraph build(CodegenWorkItem work) {
30 return measure(() {
31 Element element = work.element.implementation;
32 return compiler.withCurrentElement(element, () {
33 HInstruction.idCounter = 0;
34 SsaBuilder builder =
35 new SsaBuilder(backend, work, emitter.nativeEmitter);
36 HGraph graph;
37 ElementKind kind = element.kind;
38 if (kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
39 graph = compileConstructor(builder, work);
40 } else if (kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY ||
41 kind == ElementKind.FUNCTION ||
42 kind == ElementKind.GETTER ||
43 kind == ElementKind.SETTER) {
44 graph = builder.buildMethod(element);
45 } else if (kind == ElementKind.FIELD) {
46 if (element.isInstanceMember) {
47 assert(compiler.enableTypeAssertions);
48 graph = builder.buildCheckedSetter(element);
49 } else {
50 graph = builder.buildLazyInitializer(element);
51 }
52 } else {
53 compiler.internalError(element, 'Unexpected element kind $kind.');
54 }
55 assert(graph.isValid());
56 if (!identical(kind, ElementKind.FIELD)) {
57 FunctionElement function = element;
58 FunctionSignature signature = function.functionSignature;
59 signature.forEachOptionalParameter((ParameterElement parameter) {
60 // This ensures the default value will be computed.
61 ConstantValue constant =
62 backend.constants.getConstantForVariable(parameter).value;
63 CodegenRegistry registry = work.registry;
64 registry.registerCompileTimeConstant(constant);
65 });
66 }
67 if (compiler.tracer.isEnabled) {
68 String name;
69 if (element.isClassMember) {
70 String className = element.enclosingClass.name;
71 String memberName = element.name;
72 name = "$className.$memberName";
73 if (element.isGenerativeConstructorBody) {
74 name = "$name (body)";
75 }
76 } else {
77 name = "${element.name}";
78 }
79 compiler.tracer.traceCompilation(
80 name, work.compilationContext);
81 compiler.tracer.traceGraph('builder', graph);
82 }
83 return graph;
84 });
85 });
86 }
87
88 HGraph compileConstructor(SsaBuilder builder, CodegenWorkItem work) {
89 return builder.buildFactory(work.element);
90 }
91 }
92
93 /**
94 * Keeps track of locals (including parameters and phis) when building. The
95 * 'this' reference is treated as parameter and hence handled by this class,
96 * too.
97 */
98 class LocalsHandler {
99 /**
100 * The values of locals that can be directly accessed (without redirections
101 * to boxes or closure-fields).
102 *
103 * [directLocals] is iterated, so it is "insertion ordered" to make the
104 * iteration order a function only of insertions and not a function of
105 * e.g. Element hash codes. I'd prefer to use a SortedMap but some elements
106 * don't have source locations for [Elements.compareByPosition].
107 */
108 Map<Local, HInstruction> directLocals =
109 new Map<Local, HInstruction>();
110 Map<Local, CapturedVariable> redirectionMapping =
111 new Map<Local, CapturedVariable>();
112 SsaBuilder builder;
113 ClosureClassMap closureData;
114 Map<TypeVariableType, TypeVariableLocal> typeVariableLocals =
115 new Map<TypeVariableType, TypeVariableLocal>();
116 final ExecutableElement executableContext;
117
118 /// The class that defines the current type environment or null if no type
119 /// variables are in scope.
120 ClassElement get contextClass => executableContext.contextClass;
121
122 LocalsHandler(this.builder, this.executableContext);
123
124 /// Substituted type variables occurring in [type] into the context of
125 /// [contextClass].
126 DartType substInContext(DartType type) {
127 if (contextClass != null) {
128 ClassElement typeContext = Types.getClassContext(type);
129 if (typeContext != null) {
130 type = type.substByContext(
131 contextClass.asInstanceOf(typeContext));
132 }
133 }
134 return type;
135 }
136
137 get typesTask => builder.compiler.typesTask;
138
139 /**
140 * Creates a new [LocalsHandler] based on [other]. We only need to
141 * copy the [directLocals], since the other fields can be shared
142 * throughout the AST visit.
143 */
144 LocalsHandler.from(LocalsHandler other)
145 : directLocals = new Map<Local, HInstruction>.from(other.directLocals),
146 redirectionMapping = other.redirectionMapping,
147 executableContext = other.executableContext,
148 builder = other.builder,
149 closureData = other.closureData;
150
151 /**
152 * Redirects accesses from element [from] to element [to]. The [to] element
153 * must be a boxed variable or a variable that is stored in a closure-field.
154 */
155 void redirectElement(Local from, CapturedVariable to) {
156 assert(redirectionMapping[from] == null);
157 redirectionMapping[from] = to;
158 assert(isStoredInClosureField(from) || isBoxed(from));
159 }
160
161 HInstruction createBox() {
162 // TODO(floitsch): Clean up this hack. Should we create a box-object by
163 // just creating an empty object literal?
164 JavaScriptBackend backend = builder.backend;
165 HInstruction box = new HForeign(js.js.parseForeignJS('{}'),
166 backend.nonNullType,
167 <HInstruction>[]);
168 builder.add(box);
169 return box;
170 }
171
172 /**
173 * If the scope (function or loop) [node] has captured variables then this
174 * method creates a box and sets up the redirections.
175 */
176 void enterScope(ast.Node node, Element element) {
177 // See if any variable in the top-scope of the function is captured. If yes
178 // we need to create a box-object.
179 ClosureScope scopeData = closureData.capturingScopes[node];
180 if (scopeData == null) return;
181 HInstruction box;
182 // The scope has captured variables.
183 if (element != null && element.isGenerativeConstructorBody) {
184 // The box is passed as a parameter to a generative
185 // constructor body.
186 JavaScriptBackend backend = builder.backend;
187 box = builder.addParameter(scopeData.boxElement, backend.nonNullType);
188 } else {
189 box = createBox();
190 }
191 // Add the box to the known locals.
192 directLocals[scopeData.boxElement] = box;
193 // Make sure that accesses to the boxed locals go into the box. We also
194 // need to make sure that parameters are copied into the box if necessary.
195 scopeData.forEachCapturedVariable(
196 (LocalVariableElement from, BoxFieldElement to) {
197 // The [from] can only be a parameter for function-scopes and not
198 // loop scopes.
199 if (from.isParameter && !element.isGenerativeConstructorBody) {
200 // Now that the redirection is set up, the update to the local will
201 // write the parameter value into the box.
202 // Store the captured parameter in the box. Get the current value
203 // before we put the redirection in place.
204 // We don't need to update the local for a generative
205 // constructor body, because it receives a box that already
206 // contains the updates as the last parameter.
207 HInstruction instruction = readLocal(from);
208 redirectElement(from, to);
209 updateLocal(from, instruction);
210 } else {
211 redirectElement(from, to);
212 }
213 });
214 }
215
216 /**
217 * Replaces the current box with a new box and copies over the given list
218 * of elements from the old box into the new box.
219 */
220 void updateCaptureBox(BoxLocal boxElement,
221 List<LocalVariableElement> toBeCopiedElements) {
222 // Create a new box and copy over the values from the old box into the
223 // new one.
224 HInstruction oldBox = readLocal(boxElement);
225 HInstruction newBox = createBox();
226 for (LocalVariableElement boxedVariable in toBeCopiedElements) {
227 // [readLocal] uses the [boxElement] to find its box. By replacing it
228 // behind its back we can still get to the old values.
229 updateLocal(boxElement, oldBox);
230 HInstruction oldValue = readLocal(boxedVariable);
231 updateLocal(boxElement, newBox);
232 updateLocal(boxedVariable, oldValue);
233 }
234 updateLocal(boxElement, newBox);
235 }
236
237 /**
238 * Documentation wanted -- johnniwinther
239 *
240 * Invariant: [function] must be an implementation element.
241 */
242 void startFunction(Element element, ast.Node node) {
243 assert(invariant(element, element.isImplementation));
244 Compiler compiler = builder.compiler;
245 closureData = compiler.closureToClassMapper.computeClosureToClassMapping(
246 element, node, builder.elements);
247
248 if (element is FunctionElement) {
249 FunctionElement functionElement = element;
250 FunctionSignature params = functionElement.functionSignature;
251 ClosureScope scopeData = closureData.capturingScopes[node];
252 params.orderedForEachParameter((ParameterElement parameterElement) {
253 if (element.isGenerativeConstructorBody) {
254 if (scopeData != null &&
255 scopeData.isCapturedVariable(parameterElement)) {
256 // The parameter will be a field in the box passed as the
257 // last parameter. So no need to have it.
258 return;
259 }
260 }
261 HInstruction parameter = builder.addParameter(
262 parameterElement,
263 TypeMaskFactory.inferredTypeForElement(parameterElement, compiler));
264 builder.parameters[parameterElement] = parameter;
265 directLocals[parameterElement] = parameter;
266 });
267 }
268
269 enterScope(node, element);
270
271 // If the freeVariableMapping is not empty, then this function was a
272 // nested closure that captures variables. Redirect the captured
273 // variables to fields in the closure.
274 closureData.forEachFreeVariable((Local from, CapturedVariable to) {
275 redirectElement(from, to);
276 });
277 JavaScriptBackend backend = compiler.backend;
278 if (closureData.isClosure) {
279 // Inside closure redirect references to itself to [:this:].
280 HThis thisInstruction = new HThis(closureData.thisLocal,
281 backend.nonNullType);
282 builder.graph.thisInstruction = thisInstruction;
283 builder.graph.entry.addAtEntry(thisInstruction);
284 updateLocal(closureData.closureElement, thisInstruction);
285 } else if (element.isInstanceMember) {
286 // Once closures have been mapped to classes their instance members might
287 // not have any thisElement if the closure was created inside a static
288 // context.
289 HThis thisInstruction = new HThis(
290 closureData.thisLocal, builder.getTypeOfThis());
291 builder.graph.thisInstruction = thisInstruction;
292 builder.graph.entry.addAtEntry(thisInstruction);
293 directLocals[closureData.thisLocal] = thisInstruction;
294 }
295
296 // If this method is an intercepted method, add the extra
297 // parameter to it, that is the actual receiver for intercepted
298 // classes, or the same as [:this:] for non-intercepted classes.
299 ClassElement cls = element.enclosingClass;
300
301 // When the class extends a native class, the instance is pre-constructed
302 // and passed to the generative constructor factory function as a parameter.
303 // Instead of allocating and initializing the object, the constructor
304 // 'upgrades' the native subclass object by initializing the Dart fields.
305 bool isNativeUpgradeFactory = element.isGenerativeConstructor
306 && Elements.isNativeOrExtendsNative(cls);
307 if (backend.isInterceptedMethod(element)) {
308 bool isInterceptorClass = backend.isInterceptorClass(cls.declaration);
309 String name = isInterceptorClass ? 'receiver' : '_';
310 SyntheticLocal parameter = new SyntheticLocal(name, executableContext);
311 HParameterValue value =
312 new HParameterValue(parameter, builder.getTypeOfThis());
313 builder.graph.explicitReceiverParameter = value;
314 builder.graph.entry.addAfter(
315 directLocals[closureData.thisLocal], value);
316 if (isInterceptorClass) {
317 // Only use the extra parameter in intercepted classes.
318 directLocals[closureData.thisLocal] = value;
319 }
320 } else if (isNativeUpgradeFactory) {
321 SyntheticLocal parameter =
322 new SyntheticLocal('receiver', executableContext);
323 // Unlike `this`, receiver is nullable since direct calls to generative
324 // constructor call the constructor with `null`.
325 ClassWorld classWorld = compiler.world;
326 HParameterValue value =
327 new HParameterValue(parameter, new TypeMask.exact(cls, classWorld));
328 builder.graph.explicitReceiverParameter = value;
329 builder.graph.entry.addAtEntry(value);
330 }
331 }
332
333 /**
334 * Returns true if the local can be accessed directly. Boxed variables or
335 * captured variables that are stored in the closure-field return [:false:].
336 */
337 bool isAccessedDirectly(Local local) {
338 assert(local != null);
339 return !redirectionMapping.containsKey(local)
340 && !closureData.usedVariablesInTry.contains(local);
341 }
342
343 bool isStoredInClosureField(Local local) {
344 assert(local != null);
345 if (isAccessedDirectly(local)) return false;
346 CapturedVariable redirectTarget = redirectionMapping[local];
347 if (redirectTarget == null) return false;
348 return redirectTarget is ClosureFieldElement;
349 }
350
351 bool isBoxed(Local local) {
352 if (isAccessedDirectly(local)) return false;
353 if (isStoredInClosureField(local)) return false;
354 return redirectionMapping.containsKey(local);
355 }
356
357 bool isUsedInTry(Local local) {
358 return closureData.usedVariablesInTry.contains(local);
359 }
360
361 /**
362 * Returns an [HInstruction] for the given element. If the element is
363 * boxed or stored in a closure then the method generates code to retrieve
364 * the value.
365 */
366 HInstruction readLocal(Local local) {
367 if (isAccessedDirectly(local)) {
368 if (directLocals[local] == null) {
369 if (local is TypeVariableElement) {
370 builder.compiler.internalError(builder.compiler.currentElement,
371 "Runtime type information not available for $local.");
372 } else {
373 builder.compiler.internalError(local,
374 "Cannot find value $local.");
375 }
376 }
377 return directLocals[local];
378 } else if (isStoredInClosureField(local)) {
379 ClosureFieldElement redirect = redirectionMapping[local];
380 HInstruction receiver = readLocal(closureData.closureElement);
381 TypeMask type = local is BoxLocal
382 ? builder.backend.nonNullType
383 : builder.getTypeOfCapturedVariable(redirect);
384 HInstruction fieldGet = new HFieldGet(redirect, receiver, type);
385 builder.add(fieldGet);
386 return fieldGet;
387 } else if (isBoxed(local)) {
388 BoxFieldElement redirect = redirectionMapping[local];
389 // In the function that declares the captured variable the box is
390 // accessed as direct local. Inside the nested closure the box is
391 // accessed through a closure-field.
392 // Calling [readLocal] makes sure we generate the correct code to get
393 // the box.
394 HInstruction box = readLocal(redirect.box);
395 HInstruction lookup = new HFieldGet(
396 redirect, box, builder.getTypeOfCapturedVariable(redirect));
397 builder.add(lookup);
398 return lookup;
399 } else {
400 assert(isUsedInTry(local));
401 HLocalValue localValue = getLocal(local);
402 HInstruction instruction = new HLocalGet(
403 local, localValue, builder.backend.dynamicType);
404 builder.add(instruction);
405 return instruction;
406 }
407 }
408
409 HInstruction readThis() {
410 HInstruction res = readLocal(closureData.thisLocal);
411 if (res.instructionType == null) {
412 res.instructionType = builder.getTypeOfThis();
413 }
414 return res;
415 }
416
417 HLocalValue getLocal(Local local) {
418 // If the element is a parameter, we already have a
419 // HParameterValue for it. We cannot create another one because
420 // it could then have another name than the real parameter. And
421 // the other one would not know it is just a copy of the real
422 // parameter.
423 if (local is ParameterElement) return builder.parameters[local];
424
425 return builder.activationVariables.putIfAbsent(local, () {
426 JavaScriptBackend backend = builder.backend;
427 HLocalValue localValue = new HLocalValue(local, backend.nonNullType);
428 builder.graph.entry.addAtExit(localValue);
429 return localValue;
430 });
431 }
432
433 Local getTypeVariableAsLocal(TypeVariableType type) {
434 return typeVariableLocals.putIfAbsent(type, () {
435 return new TypeVariableLocal(type, executableContext);
436 });
437 }
438
439 /**
440 * Sets the [element] to [value]. If the element is boxed or stored in a
441 * closure then the method generates code to set the value.
442 */
443 void updateLocal(Local local, HInstruction value) {
444 assert(!isStoredInClosureField(local));
445 if (isAccessedDirectly(local)) {
446 directLocals[local] = value;
447 } else if (isBoxed(local)) {
448 BoxFieldElement redirect = redirectionMapping[local];
449 // The box itself could be captured, or be local. A local variable that
450 // is captured will be boxed, but the box itself will be a local.
451 // Inside the closure the box is stored in a closure-field and cannot
452 // be accessed directly.
453 HInstruction box = readLocal(redirect.box);
454 builder.add(new HFieldSet(redirect, box, value));
455 } else {
456 assert(isUsedInTry(local));
457 HLocalValue localValue = getLocal(local);
458 builder.add(new HLocalSet(local, localValue, value));
459 }
460 }
461
462 /**
463 * This function, startLoop, must be called before visiting any children of
464 * the loop. In particular it needs to be called before executing the
465 * initializers.
466 *
467 * The [LocalsHandler] will make the boxes and updates at the right moment.
468 * The builder just needs to call [enterLoopBody] and [enterLoopUpdates]
469 * (for [ast.For] loops) at the correct places. For phi-handling
470 * [beginLoopHeader] and [endLoop] must also be called.
471 *
472 * The correct place for the box depends on the given loop. In most cases
473 * the box will be created when entering the loop-body: while, do-while, and
474 * for-in (assuming the call to [:next:] is inside the body) can always be
475 * constructed this way.
476 *
477 * Things are slightly more complicated for [ast.For] loops. If no declared
478 * loop variable is boxed then the loop-body approach works here too. If a
479 * loop-variable is boxed we need to introduce a new box for the
480 * loop-variable before we enter the initializer so that the initializer
481 * writes the values into the box. In any case we need to create the box
482 * before the condition since the condition could box the variable.
483 * Since the first box is created outside the actual loop we have a second
484 * location where a box is created: just before the updates. This is
485 * necessary since updates are considered to be part of the next iteration
486 * (and can again capture variables).
487 *
488 * For example the following Dart code prints 1 3 -- 3 4.
489 *
490 * var fs = [];
491 * for (var i = 0; i < 3; (f() { fs.add(f); print(i); i++; })()) {
492 * i++;
493 * }
494 * print("--");
495 * for (var i = 0; i < 2; i++) fs[i]();
496 *
497 * We solve this by emitting the following code (only for [ast.For] loops):
498 * <Create box> <== move the first box creation outside the loop.
499 * <initializer>;
500 * loop-entry:
501 * if (!<condition>) goto loop-exit;
502 * <body>
503 * <update box> // create a new box and copy the captured loop-variables.
504 * <updates>
505 * goto loop-entry;
506 * loop-exit:
507 */
508 void startLoop(ast.Node node) {
509 ClosureScope scopeData = closureData.capturingScopes[node];
510 if (scopeData == null) return;
511 if (scopeData.hasBoxedLoopVariables()) {
512 // If there are boxed loop variables then we set up the box and
513 // redirections already now. This way the initializer can write its
514 // values into the box.
515 // For other loops the box will be created when entering the body.
516 enterScope(node, null);
517 }
518 }
519
520 /**
521 * Create phis at the loop entry for local variables (ready for the values
522 * from the back edge). Populate the phis with the current values.
523 */
524 void beginLoopHeader(HBasicBlock loopEntry) {
525 // Create a copy because we modify the map while iterating over it.
526 Map<Local, HInstruction> savedDirectLocals =
527 new Map<Local, HInstruction>.from(directLocals);
528
529 JavaScriptBackend backend = builder.backend;
530 // Create phis for all elements in the definitions environment.
531 savedDirectLocals.forEach((Local local,
532 HInstruction instruction) {
533 if (isAccessedDirectly(local)) {
534 // We know 'this' cannot be modified.
535 if (local != closureData.thisLocal) {
536 HPhi phi = new HPhi.singleInput(
537 local, instruction, backend.dynamicType);
538 loopEntry.addPhi(phi);
539 directLocals[local] = phi;
540 } else {
541 directLocals[local] = instruction;
542 }
543 }
544 });
545 }
546
547 void enterLoopBody(ast.Node node) {
548 ClosureScope scopeData = closureData.capturingScopes[node];
549 if (scopeData == null) return;
550 // If there are no declared boxed loop variables then we did not create the
551 // box before the initializer and we have to create the box now.
552 if (!scopeData.hasBoxedLoopVariables()) {
553 enterScope(node, null);
554 }
555 }
556
557 void enterLoopUpdates(ast.Node node) {
558 // If there are declared boxed loop variables then the updates might have
559 // access to the box and we must switch to a new box before executing the
560 // updates.
561 // In all other cases a new box will be created when entering the body of
562 // the next iteration.
563 ClosureScope scopeData = closureData.capturingScopes[node];
564 if (scopeData == null) return;
565 if (scopeData.hasBoxedLoopVariables()) {
566 updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables);
567 }
568 }
569
570 /**
571 * Goes through the phis created in beginLoopHeader entry and adds the
572 * input from the back edge (from the current value of directLocals) to them.
573 */
574 void endLoop(HBasicBlock loopEntry) {
575 // If the loop has an aborting body, we don't update the loop
576 // phis.
577 if (loopEntry.predecessors.length == 1) return;
578 loopEntry.forEachPhi((HPhi phi) {
579 Local element = phi.sourceElement;
580 HInstruction postLoopDefinition = directLocals[element];
581 phi.addInput(postLoopDefinition);
582 });
583 }
584
585 /**
586 * Merge [otherLocals] into this locals handler, creating phi-nodes when
587 * there is a conflict.
588 * If a phi node is necessary, it will use this handler's instruction as the
589 * first input, and the otherLocals instruction as the second.
590 */
591 void mergeWith(LocalsHandler otherLocals, HBasicBlock joinBlock) {
592 // If an element is in one map but not the other we can safely
593 // ignore it. It means that a variable was declared in the
594 // block. Since variable declarations are scoped the declared
595 // variable cannot be alive outside the block. Note: this is only
596 // true for nodes where we do joins.
597 Map<Local, HInstruction> joinedLocals =
598 new Map<Local, HInstruction>();
599 JavaScriptBackend backend = builder.backend;
600 otherLocals.directLocals.forEach((Local local,
601 HInstruction instruction) {
602 // We know 'this' cannot be modified.
603 if (local == closureData.thisLocal) {
604 assert(directLocals[local] == instruction);
605 joinedLocals[local] = instruction;
606 } else {
607 HInstruction mine = directLocals[local];
608 if (mine == null) return;
609 if (identical(instruction, mine)) {
610 joinedLocals[local] = instruction;
611 } else {
612 HInstruction phi = new HPhi.manyInputs(
613 local, <HInstruction>[mine, instruction], backend.dynamicType);
614 joinBlock.addPhi(phi);
615 joinedLocals[local] = phi;
616 }
617 }
618 });
619 directLocals = joinedLocals;
620 }
621
622 /**
623 * When control flow merges, this method can be used to merge several
624 * localsHandlers into a new one using phis. The new localsHandler is
625 * returned. Unless it is also in the list, the current localsHandler is not
626 * used for its values, only for its declared variables. This is a way to
627 * exclude local values from the result when they are no longer in scope.
628 */
629 LocalsHandler mergeMultiple(List<LocalsHandler> localsHandlers,
630 HBasicBlock joinBlock) {
631 assert(localsHandlers.length > 0);
632 if (localsHandlers.length == 1) return localsHandlers[0];
633 Map<Local, HInstruction> joinedLocals =
634 new Map<Local, HInstruction>();
635 HInstruction thisValue = null;
636 JavaScriptBackend backend = builder.backend;
637 directLocals.forEach((Local local, HInstruction instruction) {
638 if (local != closureData.thisLocal) {
639 HPhi phi = new HPhi.noInputs(local, backend.dynamicType);
640 joinedLocals[local] = phi;
641 joinBlock.addPhi(phi);
642 } else {
643 // We know that "this" never changes, if it's there.
644 // Save it for later. While merging, there is no phi for "this",
645 // so we don't have to special case it in the merge loop.
646 thisValue = instruction;
647 }
648 });
649 for (LocalsHandler handler in localsHandlers) {
650 handler.directLocals.forEach((Local local,
651 HInstruction instruction) {
652 HPhi phi = joinedLocals[local];
653 if (phi != null) {
654 phi.addInput(instruction);
655 }
656 });
657 }
658 if (thisValue != null) {
659 // If there was a "this" for the scope, add it to the new locals.
660 joinedLocals[closureData.thisLocal] = thisValue;
661 }
662
663 // Remove locals that are not in all handlers.
664 directLocals = new Map<Local, HInstruction>();
665 joinedLocals.forEach((Local local,
666 HInstruction instruction) {
667 if (local != closureData.thisLocal
668 && instruction.inputs.length != localsHandlers.length) {
669 joinBlock.removePhi(instruction);
670 } else {
671 directLocals[local] = instruction;
672 }
673 });
674 return this;
675 }
676 }
677
678
679 // Represents a single break/continue instruction.
680 class JumpHandlerEntry {
681 final HJump jumpInstruction;
682 final LocalsHandler locals;
683 bool isBreak() => jumpInstruction is HBreak;
684 bool isContinue() => jumpInstruction is HContinue;
685 JumpHandlerEntry(this.jumpInstruction, this.locals);
686 }
687
688
689 abstract class JumpHandler {
690 factory JumpHandler(SsaBuilder builder, JumpTarget target) {
691 return new TargetJumpHandler(builder, target);
692 }
693 void generateBreak([LabelDefinition label]);
694 void generateContinue([LabelDefinition label]);
695 void forEachBreak(void action(HBreak instruction, LocalsHandler locals));
696 void forEachContinue(void action(HContinue instruction,
697 LocalsHandler locals));
698 bool hasAnyContinue();
699 bool hasAnyBreak();
700 void close();
701 final JumpTarget target;
702 List<LabelDefinition> labels();
703 }
704
705 // Insert break handler used to avoid null checks when a target isn't
706 // used as the target of a break, and therefore doesn't need a break
707 // handler associated with it.
708 class NullJumpHandler implements JumpHandler {
709 final Compiler compiler;
710
711 NullJumpHandler(this.compiler);
712
713 void generateBreak([LabelDefinition label]) {
714 compiler.internalError(CURRENT_ELEMENT_SPANNABLE,
715 'NullJumpHandler.generateBreak should not be called.');
716 }
717
718 void generateContinue([LabelDefinition label]) {
719 compiler.internalError(CURRENT_ELEMENT_SPANNABLE,
720 'NullJumpHandler.generateContinue should not be called.');
721 }
722
723 void forEachBreak(Function ignored) { }
724 void forEachContinue(Function ignored) { }
725 void close() { }
726 bool hasAnyContinue() => false;
727 bool hasAnyBreak() => false;
728
729 List<LabelDefinition> labels() => const <LabelDefinition>[];
730 JumpTarget get target => null;
731 }
732
733 // Records breaks until a target block is available.
734 // Breaks are always forward jumps.
735 // Continues in loops are implemented as breaks of the body.
736 // Continues in switches is currently not handled.
737 class TargetJumpHandler implements JumpHandler {
738 final SsaBuilder builder;
739 final JumpTarget target;
740 final List<JumpHandlerEntry> jumps;
741
742 TargetJumpHandler(SsaBuilder builder, this.target)
743 : this.builder = builder,
744 jumps = <JumpHandlerEntry>[] {
745 assert(builder.jumpTargets[target] == null);
746 builder.jumpTargets[target] = this;
747 }
748
749 void generateBreak([LabelDefinition label]) {
750 HInstruction breakInstruction;
751 if (label == null) {
752 breakInstruction = new HBreak(target);
753 } else {
754 breakInstruction = new HBreak.toLabel(label);
755 }
756 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
757 builder.close(breakInstruction);
758 jumps.add(new JumpHandlerEntry(breakInstruction, locals));
759 }
760
761 void generateContinue([LabelDefinition label]) {
762 HInstruction continueInstruction;
763 if (label == null) {
764 continueInstruction = new HContinue(target);
765 } else {
766 continueInstruction = new HContinue.toLabel(label);
767 // Switch case continue statements must be handled by the
768 // [SwitchCaseJumpHandler].
769 assert(label.target.statement is! ast.SwitchCase);
770 }
771 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
772 builder.close(continueInstruction);
773 jumps.add(new JumpHandlerEntry(continueInstruction, locals));
774 }
775
776 void forEachBreak(Function action) {
777 for (JumpHandlerEntry entry in jumps) {
778 if (entry.isBreak()) action(entry.jumpInstruction, entry.locals);
779 }
780 }
781
782 void forEachContinue(Function action) {
783 for (JumpHandlerEntry entry in jumps) {
784 if (entry.isContinue()) action(entry.jumpInstruction, entry.locals);
785 }
786 }
787
788 bool hasAnyContinue() {
789 for (JumpHandlerEntry entry in jumps) {
790 if (entry.isContinue()) return true;
791 }
792 return false;
793 }
794
795 bool hasAnyBreak() {
796 for (JumpHandlerEntry entry in jumps) {
797 if (entry.isBreak()) return true;
798 }
799 return false;
800 }
801
802 void close() {
803 // The mapping from TargetElement to JumpHandler is no longer needed.
804 builder.jumpTargets.remove(target);
805 }
806
807 List<LabelDefinition> labels() {
808 List<LabelDefinition> result = null;
809 for (LabelDefinition element in target.labels) {
810 if (result == null) result = <LabelDefinition>[];
811 result.add(element);
812 }
813 return (result == null) ? const <LabelDefinition>[] : result;
814 }
815 }
816
817 /// Special [JumpHandler] implementation used to handle continue statements
818 /// targeting switch cases.
819 class SwitchCaseJumpHandler extends TargetJumpHandler {
820 /// Map from switch case targets to indices used to encode the flow of the
821 /// switch case loop.
822 final Map<JumpTarget, int> targetIndexMap = new Map<JumpTarget, int>();
823
824 SwitchCaseJumpHandler(SsaBuilder builder,
825 JumpTarget target,
826 ast.SwitchStatement node)
827 : super(builder, target) {
828 // The switch case indices must match those computed in
829 // [SsaFromAstMixin.buildSwitchCaseConstants].
830 // Switch indices are 1-based so we can bypass the synthetic loop when no
831 // cases match simply by branching on the index (which defaults to null).
832 int switchIndex = 1;
833 for (ast.SwitchCase switchCase in node.cases) {
834 for (ast.Node labelOrCase in switchCase.labelsAndCases) {
835 ast.Node label = labelOrCase.asLabel();
836 if (label != null) {
837 LabelDefinition labelElement =
838 builder.elements.getLabelDefinition(label);
839 if (labelElement != null && labelElement.isContinueTarget) {
840 JumpTarget continueTarget = labelElement.target;
841 targetIndexMap[continueTarget] = switchIndex;
842 assert(builder.jumpTargets[continueTarget] == null);
843 builder.jumpTargets[continueTarget] = this;
844 }
845 }
846 }
847 switchIndex++;
848 }
849 }
850
851 void generateBreak([LabelDefinition label]) {
852 if (label == null) {
853 // Creates a special break instruction for the synthetic loop generated
854 // for a switch statement with continue statements. See
855 // [SsaFromAstMixin.buildComplexSwitchStatement] for detail.
856
857 HInstruction breakInstruction =
858 new HBreak(target, breakSwitchContinueLoop: true);
859 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
860 builder.close(breakInstruction);
861 jumps.add(new JumpHandlerEntry(breakInstruction, locals));
862 } else {
863 super.generateBreak(label);
864 }
865 }
866
867 bool isContinueToSwitchCase(LabelDefinition label) {
868 return label != null && targetIndexMap.containsKey(label.target);
869 }
870
871 void generateContinue([LabelDefinition label]) {
872 if (isContinueToSwitchCase(label)) {
873 // Creates the special instructions 'label = i; continue l;' used in
874 // switch statements with continue statements. See
875 // [SsaFromAstMixin.buildComplexSwitchStatement] for detail.
876
877 assert(label != null);
878 HInstruction value = builder.graph.addConstantInt(
879 targetIndexMap[label.target],
880 builder.compiler);
881 builder.localsHandler.updateLocal(target, value);
882
883 assert(label.target.labels.contains(label));
884 HInstruction continueInstruction = new HContinue(target);
885 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
886 builder.close(continueInstruction);
887 jumps.add(new JumpHandlerEntry(continueInstruction, locals));
888 } else {
889 super.generateContinue(label);
890 }
891 }
892
893 void close() {
894 // The mapping from TargetElement to JumpHandler is no longer needed.
895 for (JumpTarget target in targetIndexMap.keys) {
896 builder.jumpTargets.remove(target);
897 }
898 super.close();
899 }
900 }
901
902 /**
903 * This class builds SSA nodes for functions represented in AST.
904 */
905 class SsaBuilder extends ResolvedVisitor {
906 final Compiler compiler;
907 final JavaScriptBackend backend;
908 final ConstantSystem constantSystem;
909 final CodegenWorkItem work;
910 final RuntimeTypes rti;
911
912 /* This field is used by the native handler. */
913 final NativeEmitter nativeEmitter;
914
915 final HGraph graph = new HGraph();
916
917 /**
918 * The current block to add instructions to. Might be null, if we are
919 * visiting dead code, but see [isReachable].
920 */
921 HBasicBlock _current;
922
923 HBasicBlock get current => _current;
924
925 void set current(c) {
926 isReachable = c != null;
927 _current = c;
928 }
929
930 /**
931 * The most recently opened block. Has the same value as [current] while
932 * the block is open, but unlike [current], it isn't cleared when the
933 * current block is closed.
934 */
935 HBasicBlock lastOpenedBlock;
936
937 /**
938 * Indicates whether the current block is dead (because it has a throw or a
939 * return further up). If this is false, then [current] may be null. If the
940 * block is dead then it may also be aborted, but for simplicity we only
941 * abort on statement boundaries, not in the middle of expressions. See
942 * isAborted.
943 */
944 bool isReachable = true;
945
946 /**
947 * True if we are visiting the expression of a throw statement; we assume this
948 * is a slow path.
949 */
950 bool inExpressionOfThrow = false;
951
952 /**
953 * The loop nesting is consulted when inlining a function invocation in
954 * [tryInlineMethod]. The inlining heuristics take this information into
955 * account.
956 */
957 int loopNesting = 0;
958
959 /**
960 * This stack contains declaration elements of the functions being built
961 * or inlined by this builder.
962 */
963 final List<Element> sourceElementStack = <Element>[];
964
965 LocalsHandler localsHandler;
966
967 HInstruction rethrowableException;
968
969 HParameterValue lastAddedParameter;
970
971 Map<ParameterElement, HInstruction> parameters =
972 <ParameterElement, HInstruction>{};
973
974 Map<JumpTarget, JumpHandler> jumpTargets = <JumpTarget, JumpHandler>{};
975
976 /**
977 * Variables stored in the current activation. These variables are
978 * being updated in try/catch blocks, and should be
979 * accessed indirectly through [HLocalGet] and [HLocalSet].
980 */
981 Map<Local, HLocalValue> activationVariables =
982 <Local, HLocalValue>{};
983
984 // We build the Ssa graph by simulating a stack machine.
985 List<HInstruction> stack = <HInstruction>[];
986
987 SsaBuilder(JavaScriptBackend backend,
988 CodegenWorkItem work,
989 this.nativeEmitter)
990 : this.compiler = backend.compiler,
991 this.backend = backend,
992 this.constantSystem = backend.constantSystem,
993 this.work = work,
994 this.rti = backend.rti,
995 super(work.resolutionTree) {
996 localsHandler = new LocalsHandler(this, work.element);
997 sourceElementStack.add(work.element);
998 }
999
1000 CodegenRegistry get registry => work.registry;
1001
1002 /// Returns the current source element.
1003 ///
1004 /// The returned element is a declaration element.
1005 // TODO(johnniwinther): Check that all usages of sourceElement agree on
1006 // implementation/declaration distinction.
1007 Element get sourceElement => sourceElementStack.last;
1008
1009 HBasicBlock addNewBlock() {
1010 HBasicBlock block = graph.addNewBlock();
1011 // If adding a new block during building of an expression, it is due to
1012 // conditional expressions or short-circuit logical operators.
1013 return block;
1014 }
1015
1016 void open(HBasicBlock block) {
1017 block.open();
1018 current = block;
1019 lastOpenedBlock = block;
1020 }
1021
1022 HBasicBlock close(HControlFlow end) {
1023 HBasicBlock result = current;
1024 current.close(end);
1025 current = null;
1026 return result;
1027 }
1028
1029 HBasicBlock closeAndGotoExit(HControlFlow end) {
1030 HBasicBlock result = current;
1031 current.close(end);
1032 current = null;
1033 result.addSuccessor(graph.exit);
1034 return result;
1035 }
1036
1037 void goto(HBasicBlock from, HBasicBlock to) {
1038 from.close(new HGoto());
1039 from.addSuccessor(to);
1040 }
1041
1042 bool isAborted() {
1043 return current == null;
1044 }
1045
1046 /**
1047 * Creates a new block, transitions to it from any current block, and
1048 * opens the new block.
1049 */
1050 HBasicBlock openNewBlock() {
1051 HBasicBlock newBlock = addNewBlock();
1052 if (!isAborted()) goto(current, newBlock);
1053 open(newBlock);
1054 return newBlock;
1055 }
1056
1057 void add(HInstruction instruction) {
1058 current.add(instruction);
1059 }
1060
1061 void addWithPosition(HInstruction instruction, ast.Node node) {
1062 add(attachPosition(instruction, node));
1063 }
1064
1065 SourceFile currentSourceFile() {
1066 return sourceElement.implementation.compilationUnit.script.file;
1067 }
1068
1069 void checkValidSourceFileLocation(
1070 SourceFileLocation location, SourceFile sourceFile, int offset) {
1071 if (!location.isValid()) {
1072 throw MessageKind.INVALID_SOURCE_FILE_LOCATION.message(
1073 {'offset': offset,
1074 'fileName': sourceFile.filename,
1075 'length': sourceFile.length});
1076 }
1077 }
1078
1079 /**
1080 * Returns a complete argument list for a call of [function].
1081 */
1082 List<HInstruction> completeSendArgumentsList(
1083 FunctionElement function,
1084 Selector selector,
1085 List<HInstruction> providedArguments,
1086 ast.Node currentNode) {
1087 assert(invariant(function, function.isImplementation));
1088 assert(providedArguments != null);
1089
1090 bool isInstanceMember = function.isInstanceMember;
1091 // For static calls, [providedArguments] is complete, default arguments
1092 // have been included if necessary, see [addStaticSendArgumentsToList].
1093 if (!isInstanceMember
1094 || currentNode == null // In erroneous code, currentNode can be null.
1095 || providedArgumentsKnownToBeComplete(currentNode)
1096 || function.isGenerativeConstructorBody
1097 || selector.isGetter) {
1098 // For these cases, the provided argument list is known to be complete.
1099 return providedArguments;
1100 } else {
1101 return completeDynamicSendArgumentsList(
1102 selector, function, providedArguments);
1103 }
1104 }
1105
1106 /**
1107 * Returns a complete argument list for a dynamic call of [function]. The
1108 * initial argument list [providedArguments], created by
1109 * [addDynamicSendArgumentsToList], does not include values for default
1110 * arguments used in the call. The reason is that the target function (which
1111 * defines the defaults) is not known.
1112 *
1113 * However, inlining can only be performed when the target function can be
1114 * resolved statically. The defaults can therefore be included at this point.
1115 *
1116 * The [providedArguments] list contains first all positional arguments, then
1117 * the provided named arguments (the named arguments that are defined in the
1118 * [selector]) in a specific order (see [addDynamicSendArgumentsToList]).
1119 */
1120 List<HInstruction> completeDynamicSendArgumentsList(
1121 Selector selector,
1122 FunctionElement function,
1123 List<HInstruction> providedArguments) {
1124 assert(selector.applies(function, compiler.world));
1125 FunctionSignature signature = function.functionSignature;
1126 List<HInstruction> compiledArguments = new List<HInstruction>(
1127 signature.parameterCount + 1); // Plus one for receiver.
1128
1129 compiledArguments[0] = providedArguments[0]; // Receiver.
1130 int index = 1;
1131 for (; index <= signature.requiredParameterCount; index++) {
1132 compiledArguments[index] = providedArguments[index];
1133 }
1134 if (!signature.optionalParametersAreNamed) {
1135 signature.forEachOptionalParameter((element) {
1136 if (index < providedArguments.length) {
1137 compiledArguments[index] = providedArguments[index];
1138 } else {
1139 compiledArguments[index] =
1140 handleConstantForOptionalParameter(element);
1141 }
1142 index++;
1143 });
1144 } else {
1145 /* Example:
1146 * void foo(a, {b, d, c})
1147 * foo(0, d = 1, b = 2)
1148 *
1149 * providedArguments = [0, 2, 1]
1150 * selectorArgumentNames = [b, d]
1151 * signature.orderedOptionalParameters = [b, c, d]
1152 *
1153 * For each parameter name in the signature, if the argument name matches
1154 * we use the next provided argument, otherwise we get the default.
1155 */
1156 List<String> selectorArgumentNames = selector.getOrderedNamedArguments();
1157 int namedArgumentIndex = 0;
1158 int firstProvidedNamedArgument = index;
1159 signature.orderedOptionalParameters.forEach((element) {
1160 if (namedArgumentIndex < selectorArgumentNames.length &&
1161 element.name == selectorArgumentNames[namedArgumentIndex]) {
1162 // The named argument was provided in the function invocation.
1163 compiledArguments[index] = providedArguments[
1164 firstProvidedNamedArgument + namedArgumentIndex++];
1165 } else {
1166 compiledArguments[index] =
1167 handleConstantForOptionalParameter(element);
1168 }
1169 index++;
1170 });
1171 }
1172 return compiledArguments;
1173 }
1174
1175 /**
1176 * Try to inline [element] within the currect context of the builder. The
1177 * insertion point is the state of the builder.
1178 */
1179 bool tryInlineMethod(Element element,
1180 Selector selector,
1181 List<HInstruction> providedArguments,
1182 ast.Node currentNode) {
1183 // TODO(johnniwinther): Register this on the [registry]. Currently the
1184 // [CodegenRegistry] calls the enqueuer, but [element] should _not_ be
1185 // enqueued.
1186 backend.registerStaticUse(element, compiler.enqueuer.codegen);
1187
1188 // Ensure that [element] is an implementation element.
1189 element = element.implementation;
1190 FunctionElement function = element;
1191 bool insideLoop = loopNesting > 0 || graph.calledInLoop;
1192
1193 // Bail out early if the inlining decision is in the cache and we can't
1194 // inline (no need to check the hard constraints).
1195 bool cachedCanBeInlined =
1196 backend.inlineCache.canInline(function, insideLoop: insideLoop);
1197 if (cachedCanBeInlined == false) return false;
1198
1199 bool meetsHardConstraints() {
1200 // Don't inline from one output unit to another. If something is deferred
1201 // it is to save space in the loading code.
1202 if (!compiler.deferredLoadTask
1203 .inSameOutputUnit(element,compiler.currentElement)) {
1204 return false;
1205 }
1206 if (compiler.disableInlining) return false;
1207
1208 assert(selector != null
1209 || Elements.isStaticOrTopLevel(element)
1210 || element.isGenerativeConstructorBody);
1211 if (selector != null && !selector.applies(function, compiler.world)) {
1212 return false;
1213 }
1214
1215 // Don't inline operator== methods if the parameter can be null.
1216 if (element.name == '==') {
1217 if (element.enclosingClass != compiler.objectClass
1218 && providedArguments[1].canBeNull()) {
1219 return false;
1220 }
1221 }
1222
1223 // Generative constructors of native classes should not be called directly
1224 // and have an extra argument that causes problems with inlining.
1225 if (element.isGenerativeConstructor
1226 && Elements.isNativeOrExtendsNative(element.enclosingClass)) {
1227 return false;
1228 }
1229
1230 // A generative constructor body is not seen by global analysis,
1231 // so we should not query for its type.
1232 if (!element.isGenerativeConstructorBody) {
1233 // Don't inline if the return type was inferred to be non-null empty.
1234 // This means that the function always throws an exception.
1235 TypeMask returnType =
1236 compiler.typesTask.getGuaranteedReturnTypeOfElement(element);
1237 if (returnType != null
1238 && returnType.isEmpty
1239 && !returnType.isNullable) {
1240 isReachable = false;
1241 return false;
1242 }
1243 }
1244
1245 return true;
1246 }
1247
1248 bool heuristicSayGoodToGo() {
1249 // Don't inline recursivly
1250 if (inliningStack.any((entry) => entry.function == function)) {
1251 return false;
1252 }
1253
1254 if (inExpressionOfThrow) return false;
1255
1256 if (element.isSynthesized) return true;
1257
1258 if (cachedCanBeInlined == true) return cachedCanBeInlined;
1259
1260 if (backend.functionsToAlwaysInline.contains(function)) {
1261 // Inline this function regardless of it's size.
1262 assert(InlineWeeder.canBeInlined(function.node, -1, false,
1263 allowLoops: true));
1264 return true;
1265 }
1266
1267 int numParameters = function.functionSignature.parameterCount;
1268 int maxInliningNodes;
1269 bool useMaxInliningNodes = true;
1270 if (insideLoop) {
1271 maxInliningNodes = InlineWeeder.INLINING_NODES_INSIDE_LOOP +
1272 InlineWeeder.INLINING_NODES_INSIDE_LOOP_ARG_FACTOR * numParameters;
1273 } else {
1274 maxInliningNodes = InlineWeeder.INLINING_NODES_OUTSIDE_LOOP +
1275 InlineWeeder.INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR * numParameters;
1276 }
1277
1278 // If a method is called only once, and all the methods in the
1279 // inlining stack are called only once as well, we know we will
1280 // save on output size by inlining this method.
1281 TypesInferrer inferrer = compiler.typesTask.typesInferrer;
1282 if (inferrer.isCalledOnce(element) && allInlinedFunctionsCalledOnce) {
1283 useMaxInliningNodes = false;
1284 }
1285 bool canInline;
1286 ast.FunctionExpression functionNode = function.node;
1287 canInline = InlineWeeder.canBeInlined(
1288 functionNode, maxInliningNodes, useMaxInliningNodes);
1289 if (canInline) {
1290 backend.inlineCache.markAsInlinable(element, insideLoop: insideLoop);
1291 } else {
1292 backend.inlineCache.markAsNonInlinable(element, insideLoop: insideLoop);
1293 }
1294 return canInline;
1295 }
1296
1297 void doInlining() {
1298 // Add an explicit null check on the receiver before doing the
1299 // inlining. We use [element] to get the same name in the
1300 // NoSuchMethodError message as if we had called it.
1301 if (element.isInstanceMember
1302 && !element.isGenerativeConstructorBody
1303 && (selector.mask == null || selector.mask.isNullable)) {
1304 addWithPosition(
1305 new HFieldGet(null, providedArguments[0], backend.dynamicType,
1306 isAssignable: false),
1307 currentNode);
1308 }
1309 List<HInstruction> compiledArguments = completeSendArgumentsList(
1310 function, selector, providedArguments, currentNode);
1311 enterInlinedMethod(function, currentNode, compiledArguments);
1312 inlinedFrom(function, () {
1313 if (!isReachable) {
1314 emitReturn(graph.addConstantNull(compiler), null);
1315 } else {
1316 doInline(function);
1317 }
1318 });
1319 leaveInlinedMethod();
1320 }
1321
1322 if (meetsHardConstraints() && heuristicSayGoodToGo()) {
1323 doInlining();
1324 registry.registerInlining(
1325 element,
1326 compiler.currentElement);
1327 return true;
1328 }
1329
1330 return false;
1331 }
1332
1333 bool get allInlinedFunctionsCalledOnce {
1334 return inliningStack.isEmpty || inliningStack.last.allFunctionsCalledOnce;
1335 }
1336
1337 inlinedFrom(Element element, f()) {
1338 assert(element is FunctionElement || element is VariableElement);
1339 return compiler.withCurrentElement(element, () {
1340 // The [sourceElementStack] contains declaration elements.
1341 sourceElementStack.add(element.declaration);
1342 var result = f();
1343 sourceElementStack.removeLast();
1344 return result;
1345 });
1346 }
1347
1348 HInstruction handleConstantForOptionalParameter(Element parameter) {
1349 ConstantExpression constant =
1350 backend.constants.getConstantForVariable(parameter);
1351 assert(invariant(parameter, constant != null,
1352 message: 'No constant computed for $parameter'));
1353 return graph.addConstant(constant.value, compiler);
1354 }
1355
1356 Element get currentNonClosureClass {
1357 ClassElement cls = sourceElement.enclosingClass;
1358 if (cls != null && cls.isClosure) {
1359 var closureClass = cls;
1360 return closureClass.methodElement.enclosingClass;
1361 } else {
1362 return cls;
1363 }
1364 }
1365
1366 /**
1367 * Returns whether this builder is building code for [element].
1368 */
1369 bool isBuildingFor(Element element) {
1370 return work.element == element;
1371 }
1372
1373 /// A stack of [DartType]s the have been seen during inlining of factory
1374 /// constructors. These types are preserved in [HInvokeStatic]s and
1375 /// [HForeignNews] inside the inline code and registered during code
1376 /// generation for these nodes.
1377 // TODO(karlklose): consider removing this and keeping the (substituted)
1378 // types of the type variables in an environment (like the [LocalsHandler]).
1379 final List<DartType> currentInlinedInstantiations = <DartType>[];
1380
1381 final List<AstInliningState> inliningStack = <AstInliningState>[];
1382
1383 Local returnLocal;
1384 DartType returnType;
1385
1386 bool inTryStatement = false;
1387
1388 ConstantValue getConstantForNode(ast.Node node) {
1389 ConstantExpression constant =
1390 backend.constants.getConstantForNode(node, elements);
1391 assert(invariant(node, constant != null,
1392 message: 'No constant computed for $node'));
1393 return constant.value;
1394 }
1395
1396 HInstruction addConstant(ast.Node node) {
1397 return graph.addConstant(getConstantForNode(node), compiler);
1398 }
1399
1400 bool isLazilyInitialized(VariableElement element) {
1401 ConstantExpression initialValue =
1402 backend.constants.getConstantForVariable(element);
1403 return initialValue == null;
1404 }
1405
1406 TypeMask cachedTypeOfThis;
1407
1408 TypeMask getTypeOfThis() {
1409 TypeMask result = cachedTypeOfThis;
1410 if (result == null) {
1411 ThisLocal local = localsHandler.closureData.thisLocal;
1412 ClassElement cls = local.enclosingClass;
1413 ClassWorld classWorld = compiler.world;
1414 if (classWorld.isUsedAsMixin(cls)) {
1415 // If the enclosing class is used as a mixin, [:this:] can be
1416 // of the class that mixins the enclosing class. These two
1417 // classes do not have a subclass relationship, so, for
1418 // simplicity, we mark the type as an interface type.
1419 result = new TypeMask.nonNullSubtype(cls.declaration, compiler.world);
1420 } else {
1421 result = new TypeMask.nonNullSubclass(cls.declaration, compiler.world);
1422 }
1423 cachedTypeOfThis = result;
1424 }
1425 return result;
1426 }
1427
1428 Map<Element, TypeMask> cachedTypesOfCapturedVariables =
1429 new Map<Element, TypeMask>();
1430
1431 TypeMask getTypeOfCapturedVariable(Element element) {
1432 assert(element.isField);
1433 return cachedTypesOfCapturedVariables.putIfAbsent(element, () {
1434 return TypeMaskFactory.inferredTypeForElement(element, compiler);
1435 });
1436 }
1437
1438 /**
1439 * Documentation wanted -- johnniwinther
1440 *
1441 * Invariant: [functionElement] must be an implementation element.
1442 */
1443 HGraph buildMethod(FunctionElement functionElement) {
1444 assert(invariant(functionElement, functionElement.isImplementation));
1445 graph.calledInLoop = compiler.world.isCalledInLoop(functionElement);
1446 ast.FunctionExpression function = functionElement.node;
1447 assert(function != null);
1448 assert(!function.modifiers.isExternal);
1449 assert(elements.getFunctionDefinition(function) != null);
1450 openFunction(functionElement, function);
1451 String name = functionElement.name;
1452 // If [functionElement] is `operator==` we explicitely add a null check at
1453 // the beginning of the method. This is to avoid having call sites do the
1454 // null check.
1455 if (name == '==') {
1456 if (!backend.operatorEqHandlesNullArgument(functionElement)) {
1457 handleIf(
1458 function,
1459 () {
1460 HParameterValue parameter = parameters.values.first;
1461 push(new HIdentity(
1462 parameter, graph.addConstantNull(compiler), null,
1463 backend.boolType));
1464 },
1465 () {
1466 closeAndGotoExit(new HReturn(
1467 graph.addConstantBool(false, compiler)));
1468 },
1469 null);
1470 }
1471 }
1472 function.body.accept(this);
1473 return closeFunction();
1474 }
1475
1476 HGraph buildCheckedSetter(VariableElement field) {
1477 openFunction(field, field.node);
1478 HInstruction thisInstruction = localsHandler.readThis();
1479 // Use dynamic type because the type computed by the inferrer is
1480 // narrowed to the type annotation.
1481 HInstruction parameter = new HParameterValue(field, backend.dynamicType);
1482 // Add the parameter as the last instruction of the entry block.
1483 // If the method is intercepted, we want the actual receiver
1484 // to be the first parameter.
1485 graph.entry.addBefore(graph.entry.last, parameter);
1486 HInstruction value =
1487 potentiallyCheckType(parameter, field.type);
1488 add(new HFieldSet(field, thisInstruction, value));
1489 return closeFunction();
1490 }
1491
1492 HGraph buildLazyInitializer(VariableElement variable) {
1493 ast.Node node = variable.node;
1494 openFunction(variable, node);
1495 assert(variable.initializer != null);
1496 visit(variable.initializer);
1497 HInstruction value = pop();
1498 value = potentiallyCheckType(value, variable.type);
1499 closeAndGotoExit(new HReturn(value));
1500 return closeFunction();
1501 }
1502
1503 /**
1504 * Returns the constructor body associated with the given constructor or
1505 * creates a new constructor body, if none can be found.
1506 *
1507 * Returns [:null:] if the constructor does not have a body.
1508 */
1509 ConstructorBodyElement getConstructorBody(FunctionElement constructor) {
1510 assert(constructor.isGenerativeConstructor);
1511 assert(invariant(constructor, constructor.isImplementation));
1512 if (constructor.isSynthesized) return null;
1513 ast.FunctionExpression node = constructor.node;
1514 // If we know the body doesn't have any code, we don't generate it.
1515 if (!node.hasBody()) return null;
1516 if (node.hasEmptyBody()) return null;
1517 ClassElement classElement = constructor.enclosingClass;
1518 ConstructorBodyElement bodyElement;
1519 classElement.forEachBackendMember((Element backendMember) {
1520 if (backendMember.isGenerativeConstructorBody) {
1521 ConstructorBodyElement body = backendMember;
1522 if (body.constructor == constructor) {
1523 // TODO(kasperl): Find a way of stopping the iteration
1524 // through the backend members.
1525 bodyElement = backendMember;
1526 }
1527 }
1528 });
1529 if (bodyElement == null) {
1530 bodyElement = new ConstructorBodyElementX(constructor);
1531 classElement.addBackendMember(bodyElement);
1532
1533 if (constructor.isPatch) {
1534 // Create origin body element for patched constructors.
1535 ConstructorBodyElementX patch = bodyElement;
1536 ConstructorBodyElementX origin =
1537 new ConstructorBodyElementX(constructor.origin);
1538 origin.applyPatch(patch);
1539 classElement.origin.addBackendMember(bodyElement.origin);
1540 }
1541 }
1542 assert(bodyElement.isGenerativeConstructorBody);
1543 return bodyElement;
1544 }
1545
1546 HParameterValue addParameter(Entity parameter, TypeMask type) {
1547 assert(inliningStack.isEmpty);
1548 HParameterValue result = new HParameterValue(parameter, type);
1549 if (lastAddedParameter == null) {
1550 graph.entry.addBefore(graph.entry.first, result);
1551 } else {
1552 graph.entry.addAfter(lastAddedParameter, result);
1553 }
1554 lastAddedParameter = result;
1555 return result;
1556 }
1557
1558 /**
1559 * This method sets up the local state of the builder for inlining [function].
1560 * The arguments of the function are inserted into the [localsHandler].
1561 *
1562 * When inlining a function, [:return:] statements are not emitted as
1563 * [HReturn] instructions. Instead, the value of a synthetic element is
1564 * updated in the [localsHandler]. This function creates such an element and
1565 * stores it in the [returnLocal] field.
1566 */
1567 void setupStateForInlining(FunctionElement function,
1568 List<HInstruction> compiledArguments) {
1569 localsHandler = new LocalsHandler(this, function);
1570 localsHandler.closureData =
1571 compiler.closureToClassMapper.computeClosureToClassMapping(
1572 function, function.node, elements);
1573 returnLocal = new SyntheticLocal("result", function);
1574 localsHandler.updateLocal(returnLocal,
1575 graph.addConstantNull(compiler));
1576
1577 inTryStatement = false; // TODO(lry): why? Document.
1578
1579 int argumentIndex = 0;
1580 if (function.isInstanceMember) {
1581 localsHandler.updateLocal(localsHandler.closureData.thisLocal,
1582 compiledArguments[argumentIndex++]);
1583 }
1584
1585 FunctionSignature signature = function.functionSignature;
1586 signature.orderedForEachParameter((ParameterElement parameter) {
1587 HInstruction argument = compiledArguments[argumentIndex++];
1588 localsHandler.updateLocal(parameter, argument);
1589 });
1590
1591 ClassElement enclosing = function.enclosingClass;
1592 if ((function.isConstructor || function.isGenerativeConstructorBody)
1593 && backend.classNeedsRti(enclosing)) {
1594 enclosing.typeVariables.forEach((TypeVariableType typeVariable) {
1595 HInstruction argument = compiledArguments[argumentIndex++];
1596 localsHandler.updateLocal(
1597 localsHandler.getTypeVariableAsLocal(typeVariable), argument);
1598 });
1599 }
1600 assert(argumentIndex == compiledArguments.length);
1601
1602 elements = function.resolvedAst.elements;
1603 assert(elements != null);
1604 returnType = signature.type.returnType;
1605 stack = <HInstruction>[];
1606
1607 insertTraceCall(function);
1608 }
1609
1610 void restoreState(AstInliningState state) {
1611 localsHandler = state.oldLocalsHandler;
1612 returnLocal = state.oldReturnLocal;
1613 inTryStatement = state.inTryStatement;
1614 elements = state.oldElements;
1615 returnType = state.oldReturnType;
1616 assert(stack.isEmpty);
1617 stack = state.oldStack;
1618 }
1619
1620 /**
1621 * Run this builder on the body of the [function] to be inlined.
1622 */
1623 void visitInlinedFunction(FunctionElement function) {
1624 potentiallyCheckInlinedParameterTypes(function);
1625 if (function.isGenerativeConstructor) {
1626 buildFactory(function);
1627 } else {
1628 ast.FunctionExpression functionNode = function.node;
1629 functionNode.body.accept(this);
1630 }
1631 }
1632
1633
1634 addInlinedInstantiation(DartType type) {
1635 if (type != null) {
1636 currentInlinedInstantiations.add(type);
1637 }
1638 }
1639
1640 removeInlinedInstantiation(DartType type) {
1641 if (type != null) {
1642 currentInlinedInstantiations.removeLast();
1643 }
1644 }
1645
1646 bool providedArgumentsKnownToBeComplete(ast.Node currentNode) {
1647 /* When inlining the iterator methods generated for a [:for-in:] loop, the
1648 * [currentNode] is the [ForIn] tree. The compiler-generated iterator
1649 * invocations are known to have fully specified argument lists, no default
1650 * arguments are used. See invocations of [pushInvokeDynamic] in
1651 * [visitForIn].
1652 */
1653 return currentNode.asForIn() != null;
1654 }
1655
1656 /**
1657 * In checked mode, generate type tests for the parameters of the inlined
1658 * function.
1659 */
1660 void potentiallyCheckInlinedParameterTypes(FunctionElement function) {
1661 if (!compiler.enableTypeAssertions) return;
1662
1663 FunctionSignature signature = function.functionSignature;
1664 signature.orderedForEachParameter((ParameterElement parameter) {
1665 HInstruction argument = localsHandler.readLocal(parameter);
1666 potentiallyCheckType(argument, parameter.type);
1667 });
1668 }
1669
1670 /**
1671 * Documentation wanted -- johnniwinther
1672 *
1673 * Invariant: [constructors] must contain only implementation elements.
1674 */
1675 void inlineSuperOrRedirect(FunctionElement callee,
1676 List<HInstruction> compiledArguments,
1677 List<FunctionElement> constructors,
1678 Map<Element, HInstruction> fieldValues,
1679 FunctionElement caller) {
1680 callee = callee.implementation;
1681 compiler.withCurrentElement(callee, () {
1682 constructors.add(callee);
1683 ClassElement enclosingClass = callee.enclosingClass;
1684 if (backend.classNeedsRti(enclosingClass)) {
1685 // If [enclosingClass] needs RTI, we have to give a value to its
1686 // type parameters.
1687 ClassElement currentClass = caller.enclosingClass;
1688 // For a super constructor call, the type is the supertype of
1689 // [currentClass]. For a redirecting constructor, the type is
1690 // the current type. [InterfaceType.asInstanceOf] takes care
1691 // of both.
1692 InterfaceType type = currentClass.thisType.asInstanceOf(enclosingClass);
1693 type = localsHandler.substInContext(type);
1694 List<DartType> arguments = type.typeArguments;
1695 List<DartType> typeVariables = enclosingClass.typeVariables;
1696 if (!type.isRaw) {
1697 assert(arguments.length == typeVariables.length);
1698 Iterator<DartType> variables = typeVariables.iterator;
1699 type.typeArguments.forEach((DartType argument) {
1700 variables.moveNext();
1701 TypeVariableType typeVariable = variables.current;
1702 localsHandler.updateLocal(
1703 localsHandler.getTypeVariableAsLocal(typeVariable),
1704 analyzeTypeArgument(argument));
1705 });
1706 } else {
1707 // If the supertype is a raw type, we need to set to null the
1708 // type variables.
1709 for (TypeVariableType variable in typeVariables) {
1710 localsHandler.updateLocal(
1711 localsHandler.getTypeVariableAsLocal(variable),
1712 graph.addConstantNull(compiler));
1713 }
1714 }
1715 }
1716
1717 // For redirecting constructors, the fields have already been
1718 // initialized by the caller.
1719 if (callee.enclosingClass != caller.enclosingClass) {
1720 inlinedFrom(callee, () {
1721 buildFieldInitializers(callee.enclosingElement.implementation,
1722 fieldValues);
1723 });
1724 }
1725
1726 int index = 0;
1727 FunctionSignature params = callee.functionSignature;
1728 params.orderedForEachParameter((ParameterElement parameter) {
1729 HInstruction argument = compiledArguments[index++];
1730 // Because we are inlining the initializer, we must update
1731 // what was given as parameter. This will be used in case
1732 // there is a parameter check expression in the initializer.
1733 parameters[parameter] = argument;
1734 localsHandler.updateLocal(parameter, argument);
1735 // Don't forget to update the field, if the parameter is of the
1736 // form [:this.x:].
1737 if (parameter.isInitializingFormal) {
1738 InitializingFormalElement fieldParameterElement = parameter;
1739 fieldValues[fieldParameterElement.fieldElement] = argument;
1740 }
1741 });
1742
1743 // Build the initializers in the context of the new constructor.
1744 TreeElements oldElements = elements;
1745 ResolvedAst resolvedAst = callee.resolvedAst;
1746 elements = resolvedAst.elements;
1747 ClosureClassMap oldClosureData = localsHandler.closureData;
1748 ast.Node node = resolvedAst.node;
1749 ClosureClassMap newClosureData =
1750 compiler.closureToClassMapper.computeClosureToClassMapping(
1751 callee, node, elements);
1752 localsHandler.closureData = newClosureData;
1753 localsHandler.enterScope(node, callee);
1754 buildInitializers(callee, constructors, fieldValues);
1755 localsHandler.closureData = oldClosureData;
1756 elements = oldElements;
1757 });
1758 }
1759
1760 /**
1761 * Run through the initializers and inline all field initializers. Recursively
1762 * inlines super initializers.
1763 *
1764 * The constructors of the inlined initializers is added to [constructors]
1765 * with sub constructors having a lower index than super constructors.
1766 *
1767 * Invariant: The [constructor] and elements in [constructors] must all be
1768 * implementation elements.
1769 */
1770 void buildInitializers(ConstructorElement constructor,
1771 List<FunctionElement> constructors,
1772 Map<Element, HInstruction> fieldValues) {
1773 assert(invariant(constructor, constructor.isImplementation));
1774 if (constructor.isSynthesized) {
1775 List<HInstruction> arguments = <HInstruction>[];
1776 HInstruction compileArgument(ParameterElement parameter) {
1777 return localsHandler.readLocal(parameter);
1778 }
1779
1780 Element target = constructor.definingConstructor.implementation;
1781 bool match = Selector.addForwardingElementArgumentsToList(
1782 constructor,
1783 arguments,
1784 target,
1785 compileArgument,
1786 handleConstantForOptionalParameter,
1787 compiler.world);
1788 if (!match) {
1789 // If this fails, the selector we constructed for the call to a
1790 // forwarding constructor in a mixin application did not match the
1791 // constructor (which, for example, may happen when the libraries are
1792 // not compatible for private names, see issue 20394).
1793 compiler.internalError(constructor,
1794 'forwarding constructor call does not match');
1795 }
1796 inlineSuperOrRedirect(
1797 target,
1798 arguments,
1799 constructors,
1800 fieldValues,
1801 constructor);
1802 return;
1803 }
1804 ast.FunctionExpression functionNode = constructor.node;
1805
1806 bool foundSuperOrRedirect = false;
1807 if (functionNode.initializers != null) {
1808 Link<ast.Node> initializers = functionNode.initializers.nodes;
1809 for (Link<ast.Node> link = initializers; !link.isEmpty; link = link.tail) {
1810 assert(link.head is ast.Send);
1811 if (link.head is !ast.SendSet) {
1812 // A super initializer or constructor redirection.
1813 foundSuperOrRedirect = true;
1814 ast.Send call = link.head;
1815 assert(ast.Initializers.isSuperConstructorCall(call) ||
1816 ast.Initializers.isConstructorRedirect(call));
1817 FunctionElement target = elements[call].implementation;
1818 Selector selector = elements.getSelector(call);
1819 Link<ast.Node> arguments = call.arguments;
1820 List<HInstruction> compiledArguments = new List<HInstruction>();
1821 inlinedFrom(constructor, () {
1822 addStaticSendArgumentsToList(selector,
1823 arguments,
1824 target,
1825 compiledArguments);
1826 });
1827 inlineSuperOrRedirect(target,
1828 compiledArguments,
1829 constructors,
1830 fieldValues,
1831 constructor);
1832 } else {
1833 // A field initializer.
1834 ast.SendSet init = link.head;
1835 Link<ast.Node> arguments = init.arguments;
1836 assert(!arguments.isEmpty && arguments.tail.isEmpty);
1837 inlinedFrom(constructor, () {
1838 visit(arguments.head);
1839 });
1840 fieldValues[elements[init]] = pop();
1841 }
1842 }
1843 }
1844
1845 if (!foundSuperOrRedirect) {
1846 // No super initializer found. Try to find the default constructor if
1847 // the class is not Object.
1848 ClassElement enclosingClass = constructor.enclosingClass;
1849 ClassElement superClass = enclosingClass.superclass;
1850 if (!enclosingClass.isObject) {
1851 assert(superClass != null);
1852 assert(superClass.resolutionState == STATE_DONE);
1853 Selector selector =
1854 new Selector.callDefaultConstructor(enclosingClass.library);
1855 // TODO(johnniwinther): Should we find injected constructors as well?
1856 FunctionElement target = superClass.lookupConstructor(selector);
1857 if (target == null) {
1858 compiler.internalError(superClass,
1859 "No default constructor available.");
1860 }
1861 List<HInstruction> arguments = <HInstruction>[];
1862 selector.addArgumentsToList(const Link<ast.Node>(),
1863 arguments,
1864 target.implementation,
1865 null,
1866 handleConstantForOptionalParameter,
1867 compiler.world);
1868 inlineSuperOrRedirect(target,
1869 arguments,
1870 constructors,
1871 fieldValues,
1872 constructor);
1873 }
1874 }
1875 }
1876
1877 /**
1878 * Run through the fields of [cls] and add their potential
1879 * initializers.
1880 *
1881 * Invariant: [classElement] must be an implementation element.
1882 */
1883 void buildFieldInitializers(ClassElement classElement,
1884 Map<Element, HInstruction> fieldValues) {
1885 assert(invariant(classElement, classElement.isImplementation));
1886 classElement.forEachInstanceField(
1887 (ClassElement enclosingClass, VariableElement member) {
1888 compiler.withCurrentElement(member, () {
1889 TreeElements definitions = member.treeElements;
1890 ast.Node node = member.node;
1891 ast.Expression initializer = member.initializer;
1892 if (initializer == null) {
1893 // Unassigned fields of native classes are not initialized to
1894 // prevent overwriting pre-initialized native properties.
1895 if (!Elements.isNativeOrExtendsNative(classElement)) {
1896 fieldValues[member] = graph.addConstantNull(compiler);
1897 }
1898 } else {
1899 ast.Node right = initializer;
1900 TreeElements savedElements = elements;
1901 elements = definitions;
1902 // In case the field initializer uses closures, run the
1903 // closure to class mapper.
1904 compiler.closureToClassMapper.computeClosureToClassMapping(
1905 member, node, elements);
1906 inlinedFrom(member, () => right.accept(this));
1907 elements = savedElements;
1908 fieldValues[member] = pop();
1909 }
1910 });
1911 });
1912 }
1913
1914 /**
1915 * Build the factory function corresponding to the constructor
1916 * [functionElement]:
1917 * - Initialize fields with the values of the field initializers of the
1918 * current constructor and super constructors or constructors redirected
1919 * to, starting from the current constructor.
1920 * - Call the constructor bodies, starting from the constructor(s) in the
1921 * super class(es).
1922 */
1923 HGraph buildFactory(FunctionElement functionElement) {
1924 functionElement = functionElement.implementation;
1925 ClassElement classElement =
1926 functionElement.enclosingClass.implementation;
1927 bool isNativeUpgradeFactory =
1928 Elements.isNativeOrExtendsNative(classElement);
1929 ast.FunctionExpression function = functionElement.node;
1930 // Note that constructors (like any other static function) do not need
1931 // to deal with optional arguments. It is the callers job to provide all
1932 // arguments as if they were positional.
1933
1934 if (inliningStack.isEmpty) {
1935 // The initializer list could contain closures.
1936 openFunction(functionElement, function);
1937 }
1938
1939 Map<Element, HInstruction> fieldValues = new Map<Element, HInstruction>();
1940
1941 // Compile the possible initialization code for local fields and
1942 // super fields.
1943 buildFieldInitializers(classElement, fieldValues);
1944
1945 // Compile field-parameters such as [:this.x:].
1946 FunctionSignature params = functionElement.functionSignature;
1947 params.orderedForEachParameter((ParameterElement parameter) {
1948 if (parameter.isInitializingFormal) {
1949 // If the [element] is a field-parameter then
1950 // initialize the field element with its value.
1951 InitializingFormalElement fieldParameter = parameter;
1952 HInstruction parameterValue =
1953 localsHandler.readLocal(fieldParameter);
1954 fieldValues[fieldParameter.fieldElement] = parameterValue;
1955 }
1956 });
1957
1958 // Analyze the constructor and all referenced constructors and collect
1959 // initializers and constructor bodies.
1960 List<FunctionElement> constructors = <FunctionElement>[functionElement];
1961 buildInitializers(functionElement, constructors, fieldValues);
1962
1963 // Call the JavaScript constructor with the fields as argument.
1964 List<HInstruction> constructorArguments = <HInstruction>[];
1965 List<Element> fields = <Element>[];
1966
1967 classElement.forEachInstanceField(
1968 (ClassElement enclosingClass, VariableElement member) {
1969 HInstruction value = fieldValues[member];
1970 if (value == null) {
1971 // Uninitialized native fields are pre-initialized by the native
1972 // implementation.
1973 assert(isNativeUpgradeFactory);
1974 } else {
1975 fields.add(member);
1976 DartType type = localsHandler.substInContext(member.type);
1977 constructorArguments.add(potentiallyCheckType(value, type));
1978 }
1979 },
1980 includeSuperAndInjectedMembers: true);
1981
1982 InterfaceType type = classElement.thisType;
1983 TypeMask ssaType =
1984 new TypeMask.nonNullExact(classElement.declaration, compiler.world);
1985 List<DartType> instantiatedTypes;
1986 addInlinedInstantiation(type);
1987 if (!currentInlinedInstantiations.isEmpty) {
1988 instantiatedTypes = new List<DartType>.from(currentInlinedInstantiations);
1989 }
1990
1991 HInstruction newObject;
1992 if (!isNativeUpgradeFactory) {
1993 newObject = new HForeignNew(classElement,
1994 ssaType,
1995 constructorArguments,
1996 instantiatedTypes);
1997 add(newObject);
1998 } else {
1999 // Bulk assign to the initialized fields.
2000 newObject = graph.explicitReceiverParameter;
2001 // Null guard ensures an error if we are being called from an explicit
2002 // 'new' of the constructor instead of via an upgrade. It is optimized out
2003 // if there are field initializers.
2004 add(new HFieldGet(
2005 null, newObject, backend.dynamicType, isAssignable: false));
2006 for (int i = 0; i < fields.length; i++) {
2007 add(new HFieldSet(fields[i], newObject, constructorArguments[i]));
2008 }
2009 }
2010 removeInlinedInstantiation(type);
2011 // Create the runtime type information, if needed.
2012 if (backend.classNeedsRti(classElement)) {
2013 // Read the values of the type arguments and create a list to set on the
2014 // newly create object. We can identify the case where the new list
2015 // would be of the form:
2016 // [getTypeArgumentByIndex(this, 0), .., getTypeArgumentByIndex(this, k)]
2017 // and k is the number of type arguments of this. If this is the case,
2018 // we can simply copy the list from this.
2019
2020 // These locals are modified by [isIndexedTypeArgumentGet].
2021 HThis source; // The source of the type arguments.
2022 bool allIndexed = true;
2023 int expectedIndex = 0;
2024 ClassElement contextClass; // The class of `this`.
2025 int remainingTypeVariables; // The number of 'remaining type variables'
2026 // of `this`.
2027
2028 /// Helper to identify instructions that read a type variable without
2029 /// substitution (that is, directly use the index). These instructions
2030 /// are of the form:
2031 /// HInvokeStatic(getTypeArgumentByIndex, this, index)
2032 ///
2033 /// Return `true` if [instruction] is of that form and the index is the
2034 /// next index in the sequence (held in [expectedIndex]).
2035 bool isIndexedTypeArgumentGet(HInstruction instruction) {
2036 if (instruction is! HInvokeStatic) return false;
2037 HInvokeStatic invoke = instruction;
2038 if (invoke.element != backend.getGetTypeArgumentByIndex()) {
2039 return false;
2040 }
2041 HConstant index = invoke.inputs[1];
2042 HInstruction newSource = invoke.inputs[0];
2043 if (newSource is! HThis) {
2044 return false;
2045 }
2046 if (source == null) {
2047 // This is the first match. Extract the context class for the type
2048 // variables and get the list of type variables to keep track of how
2049 // many arguments we need to process.
2050 source = newSource;
2051 contextClass = source.sourceElement.enclosingClass;
2052 remainingTypeVariables = contextClass.typeVariables.length;
2053 } else {
2054 assert(source == newSource);
2055 }
2056 // If there are no more type variables, then there are more type
2057 // arguments for the new object than the source has, and it can't be
2058 // a copy. Otherwise remove one argument.
2059 if (remainingTypeVariables == 0) return false;
2060 remainingTypeVariables--;
2061 // Check that the index is the one we expect.
2062 IntConstantValue constant = index.constant;
2063 return constant.primitiveValue == expectedIndex++;
2064 }
2065
2066 List<HInstruction> typeArguments = <HInstruction>[];
2067 classElement.typeVariables.forEach((TypeVariableType typeVariable) {
2068 HInstruction argument = localsHandler.readLocal(
2069 localsHandler.getTypeVariableAsLocal(typeVariable));
2070 if (allIndexed && !isIndexedTypeArgumentGet(argument)) {
2071 allIndexed = false;
2072 }
2073 typeArguments.add(argument);
2074 });
2075
2076 if (source != null && allIndexed && remainingTypeVariables == 0) {
2077 copyRuntimeTypeInfo(source, newObject);
2078 } else {
2079 newObject =
2080 callSetRuntimeTypeInfo(classElement, typeArguments, newObject);
2081 }
2082 }
2083
2084 // Generate calls to the constructor bodies.
2085 HInstruction interceptor = null;
2086 for (int index = constructors.length - 1; index >= 0; index--) {
2087 FunctionElement constructor = constructors[index];
2088 assert(invariant(functionElement, constructor.isImplementation));
2089 ConstructorBodyElement body = getConstructorBody(constructor);
2090 if (body == null) continue;
2091
2092 List bodyCallInputs = <HInstruction>[];
2093 if (isNativeUpgradeFactory) {
2094 if (interceptor == null) {
2095 ConstantValue constant =
2096 new InterceptorConstantValue(classElement.thisType);
2097 interceptor = graph.addConstant(constant, compiler);
2098 }
2099 bodyCallInputs.add(interceptor);
2100 }
2101 bodyCallInputs.add(newObject);
2102 ResolvedAst resolvedAst = constructor.resolvedAst;
2103 TreeElements elements = resolvedAst.elements;
2104 ast.Node node = resolvedAst.node;
2105 ClosureClassMap parameterClosureData =
2106 compiler.closureToClassMapper.getMappingForNestedFunction(node);
2107
2108 FunctionSignature functionSignature = body.functionSignature;
2109 // Provide the parameters to the generative constructor body.
2110 functionSignature.orderedForEachParameter((ParameterElement parameter) {
2111 // If [parameter] is boxed, it will be a field in the box passed as the
2112 // last parameter. So no need to directly pass it.
2113 if (!localsHandler.isBoxed(parameter)) {
2114 bodyCallInputs.add(localsHandler.readLocal(parameter));
2115 }
2116 });
2117
2118 ClassElement currentClass = constructor.enclosingClass;
2119 if (backend.classNeedsRti(currentClass)) {
2120 // If [currentClass] needs RTI, we add the type variables as
2121 // parameters of the generative constructor body.
2122 currentClass.typeVariables.forEach((TypeVariableType argument) {
2123 // TODO(johnniwinther): Substitute [argument] with
2124 // `localsHandler.substInContext(argument)`.
2125 bodyCallInputs.add(localsHandler.readLocal(
2126 localsHandler.getTypeVariableAsLocal(argument)));
2127 });
2128 }
2129
2130 // If there are locals that escape (ie mutated in closures), we
2131 // pass the box to the constructor.
2132 ClosureScope scopeData = parameterClosureData.capturingScopes[node];
2133 if (scopeData != null) {
2134 bodyCallInputs.add(localsHandler.readLocal(scopeData.boxElement));
2135 }
2136
2137 if (!isNativeUpgradeFactory && // TODO(13836): Fix inlining.
2138 tryInlineMethod(body, null, bodyCallInputs, function)) {
2139 pop();
2140 } else {
2141 HInvokeConstructorBody invoke = new HInvokeConstructorBody(
2142 body.declaration, bodyCallInputs, backend.nonNullType);
2143 invoke.sideEffects =
2144 compiler.world.getSideEffectsOfElement(constructor);
2145 add(invoke);
2146 }
2147 }
2148 if (inliningStack.isEmpty) {
2149 closeAndGotoExit(new HReturn(newObject));
2150 return closeFunction();
2151 } else {
2152 localsHandler.updateLocal(returnLocal, newObject);
2153 return null;
2154 }
2155 }
2156
2157 /**
2158 * Documentation wanted -- johnniwinther
2159 *
2160 * Invariant: [functionElement] must be the implementation element.
2161 */
2162 void openFunction(Element element, ast.Node node) {
2163 assert(invariant(element, element.isImplementation));
2164 HBasicBlock block = graph.addNewBlock();
2165 open(graph.entry);
2166
2167 localsHandler.startFunction(element, node);
2168 close(new HGoto()).addSuccessor(block);
2169
2170 open(block);
2171
2172 // Add the type parameters of the class as parameters of this method. This
2173 // must be done before adding the normal parameters, because their types
2174 // may contain references to type variables.
2175 var enclosing = element.enclosingElement;
2176 if ((element.isConstructor || element.isGenerativeConstructorBody)
2177 && backend.classNeedsRti(enclosing)) {
2178 enclosing.typeVariables.forEach((TypeVariableType typeVariable) {
2179 HParameterValue param = addParameter(
2180 typeVariable.element, backend.nonNullType);
2181 localsHandler.directLocals[
2182 localsHandler.getTypeVariableAsLocal(typeVariable)] = param;
2183 });
2184 }
2185
2186 if (element is FunctionElement) {
2187 FunctionElement functionElement = element;
2188 FunctionSignature signature = functionElement.functionSignature;
2189
2190 // Put the type checks in the first successor of the entry,
2191 // because that is where the type guards will also be inserted.
2192 // This way we ensure that a type guard will dominate the type
2193 // check.
2194 ClosureScope scopeData =
2195 localsHandler.closureData.capturingScopes[node];
2196 signature.orderedForEachParameter((ParameterElement parameterElement) {
2197 if (element.isGenerativeConstructorBody) {
2198 if (scopeData != null &&
2199 scopeData.isCapturedVariable(parameterElement)) {
2200 // The parameter will be a field in the box passed as the
2201 // last parameter. So no need to have it.
2202 return;
2203 }
2204 }
2205 HInstruction newParameter =
2206 localsHandler.directLocals[parameterElement];
2207 if (!element.isConstructor ||
2208 !(element as ConstructorElement).isRedirectingFactory) {
2209 // Redirection factories must not check their argument types.
2210 // Example:
2211 //
2212 // class A {
2213 // A(String foo) = A.b;
2214 // A(int foo) { print(foo); }
2215 // }
2216 // main() {
2217 // new A(499); // valid even in checked mode.
2218 // new A("foo"); // invalid in checked mode.
2219 //
2220 // Only the final target is allowed to check for the argument types.
2221 newParameter =
2222 potentiallyCheckType(newParameter, parameterElement.type);
2223 }
2224 localsHandler.directLocals[parameterElement] = newParameter;
2225 });
2226
2227 returnType = signature.type.returnType;
2228 } else {
2229 // Otherwise it is a lazy initializer which does not have parameters.
2230 assert(element is VariableElement);
2231 }
2232
2233 insertTraceCall(element);
2234 }
2235
2236 insertTraceCall(Element element) {
2237 if (JavaScriptBackend.TRACE_CALLS) {
2238 if (element == backend.traceHelper) return;
2239 n(e) => e == null ? '' : e.name;
2240 String name = "${n(element.library)}:${n(element.enclosingClass)}."
2241 "${n(element)}";
2242 HConstant nameConstant = addConstantString(name);
2243 add(new HInvokeStatic(backend.traceHelper,
2244 <HInstruction>[nameConstant],
2245 backend.dynamicType));
2246 }
2247 }
2248
2249 /// Check that [type] is valid in the context of `localsHandler.contextClass`.
2250 /// This should only be called in assertions.
2251 bool assertTypeInContext(DartType type, [Spannable spannable]) {
2252 return invariant(spannable == null ? CURRENT_ELEMENT_SPANNABLE : spannable,
2253 () {
2254 ClassElement contextClass = Types.getClassContext(type);
2255 return contextClass == null ||
2256 contextClass == localsHandler.contextClass;
2257 },
2258 message: "Type '$type' is not valid context of "
2259 "${localsHandler.contextClass}.");
2260 }
2261
2262 /// Build a [HTypeConversion] for convertion [original] to type [type].
2263 ///
2264 /// Invariant: [type] must be valid in the context.
2265 /// See [LocalsHandler.substInContext].
2266 HInstruction buildTypeConversion(HInstruction original,
2267 DartType type,
2268 int kind) {
2269 if (type == null) return original;
2270 type = type.unalias(compiler);
2271 assert(assertTypeInContext(type, original));
2272 if (type.isInterfaceType && !type.treatAsRaw) {
2273 TypeMask subtype = new TypeMask.subtype(type.element, compiler.world);
2274 HInstruction representations = buildTypeArgumentRepresentations(type);
2275 add(representations);
2276 return new HTypeConversion.withTypeRepresentation(type, kind, subtype,
2277 original, representations);
2278 } else if (type.isTypeVariable) {
2279 TypeMask subtype = original.instructionType;
2280 HInstruction typeVariable = addTypeVariableReference(type);
2281 return new HTypeConversion.withTypeRepresentation(type, kind, subtype,
2282 original, typeVariable);
2283 } else if (type.isFunctionType) {
2284 String name = kind == HTypeConversion.CAST_TYPE_CHECK
2285 ? '_asCheck' : '_assertCheck';
2286
2287 List arguments = [buildFunctionType(type), original];
2288 pushInvokeDynamic(
2289 null,
2290 new Selector.call(name, backend.jsHelperLibrary, 1),
2291 arguments);
2292
2293 return new HTypeConversion(type, kind, original.instructionType, pop());
2294 } else {
2295 return original.convertType(compiler, type, kind);
2296 }
2297 }
2298
2299 HInstruction potentiallyBuildTypeHint(HInstruction original, DartType type) {
2300 if (!compiler.trustTypeAnnotations || type == null) return original;
2301 type = localsHandler.substInContext(type);
2302 if (!type.isInterfaceType) return original;
2303 TypeMask mask = new TypeMask.subtype(type.element, compiler.world);
2304 var result = new HTypeKnown.pinned(mask, original);
2305 return result;
2306 }
2307
2308 HInstruction potentiallyCheckType(HInstruction original, DartType type,
2309 { int kind: HTypeConversion.CHECKED_MODE_CHECK }) {
2310 if (!compiler.enableTypeAssertions) return original;
2311 type = localsHandler.substInContext(type);
2312 HInstruction other = buildTypeConversion(original, type, kind);
2313 if (other != original) add(other);
2314 registry.registerIsCheck(type);
2315 return other;
2316 }
2317
2318 void assertIsSubtype(ast.Node node, DartType subtype, DartType supertype,
2319 String message) {
2320 HInstruction subtypeInstruction =
2321 analyzeTypeArgument(localsHandler.substInContext(subtype));
2322 HInstruction supertypeInstruction =
2323 analyzeTypeArgument(localsHandler.substInContext(supertype));
2324 HInstruction messageInstruction =
2325 graph.addConstantString(new ast.DartString.literal(message), compiler);
2326 Element element = backend.getAssertIsSubtype();
2327 var inputs = <HInstruction>[subtypeInstruction, supertypeInstruction,
2328 messageInstruction];
2329 HInstruction assertIsSubtype = new HInvokeStatic(
2330 element, inputs, subtypeInstruction.instructionType);
2331 registry.registerTypeVariableBoundsSubtypeCheck(subtype, supertype);
2332 add(assertIsSubtype);
2333 }
2334
2335 HGraph closeFunction() {
2336 // TODO(kasperl): Make this goto an implicit return.
2337 if (!isAborted()) closeAndGotoExit(new HGoto());
2338 graph.finalize();
2339 return graph;
2340 }
2341
2342 void push(HInstruction instruction) {
2343 add(instruction);
2344 stack.add(instruction);
2345 }
2346
2347 void pushWithPosition(HInstruction instruction, ast.Node node) {
2348 push(attachPosition(instruction, node));
2349 }
2350
2351 HInstruction pop() {
2352 return stack.removeLast();
2353 }
2354
2355 void dup() {
2356 stack.add(stack.last);
2357 }
2358
2359 HInstruction popBoolified() {
2360 HInstruction value = pop();
2361 if (compiler.enableTypeAssertions) {
2362 return potentiallyCheckType(
2363 value,
2364 compiler.boolClass.rawType,
2365 kind: HTypeConversion.BOOLEAN_CONVERSION_CHECK);
2366 }
2367 HInstruction result = new HBoolify(value, backend.boolType);
2368 add(result);
2369 return result;
2370 }
2371
2372 HInstruction attachPosition(HInstruction target, ast.Node node) {
2373 if (node != null) {
2374 target.sourcePosition = sourceFileLocationForBeginToken(node);
2375 }
2376 return target;
2377 }
2378
2379 SourceFileLocation sourceFileLocationForBeginToken(ast.Node node) =>
2380 sourceFileLocationForToken(node, node.getBeginToken());
2381
2382 SourceFileLocation sourceFileLocationForEndToken(ast.Node node) =>
2383 sourceFileLocationForToken(node, node.getEndToken());
2384
2385 SourceFileLocation sourceFileLocationForToken(ast.Node node, Token token) {
2386 SourceFile sourceFile = currentSourceFile();
2387 SourceFileLocation location =
2388 new TokenSourceFileLocation(sourceFile, token, sourceElement.name);
2389 checkValidSourceFileLocation(location, sourceFile, token.charOffset);
2390 return location;
2391 }
2392
2393 void visit(ast.Node node) {
2394 if (node != null) node.accept(this);
2395 }
2396
2397 visitBlock(ast.Block node) {
2398 assert(!isAborted());
2399 if (!isReachable) return; // This can only happen when inlining.
2400 for (Link<ast.Node> link = node.statements.nodes;
2401 !link.isEmpty;
2402 link = link.tail) {
2403 visit(link.head);
2404 if (!isReachable) {
2405 // The block has been aborted by a return or a throw.
2406 if (!stack.isEmpty) {
2407 compiler.internalError(node, 'Non-empty instruction stack.');
2408 }
2409 return;
2410 }
2411 }
2412 assert(!current.isClosed());
2413 if (!stack.isEmpty) {
2414 compiler.internalError(node, 'Non-empty instruction stack.');
2415 }
2416 }
2417
2418 visitClassNode(ast.ClassNode node) {
2419 compiler.internalError(node,
2420 'SsaBuilder.visitClassNode should not be called.');
2421 }
2422
2423 visitThrowExpression(ast.Expression expression) {
2424 bool old = inExpressionOfThrow;
2425 try {
2426 inExpressionOfThrow = true;
2427 visit(expression);
2428 } finally {
2429 inExpressionOfThrow = old;
2430 }
2431 }
2432
2433 visitExpressionStatement(ast.ExpressionStatement node) {
2434 if (!isReachable) return;
2435 ast.Throw throwExpression = node.expression.asThrow();
2436 if (throwExpression != null && inliningStack.isEmpty) {
2437 visitThrowExpression(throwExpression.expression);
2438 handleInTryStatement();
2439 closeAndGotoExit(new HThrow(pop()));
2440 } else {
2441 visit(node.expression);
2442 pop();
2443 }
2444 }
2445
2446 /**
2447 * Creates a new loop-header block. The previous [current] block
2448 * is closed with an [HGoto] and replaced by the newly created block.
2449 * Also notifies the locals handler that we're entering a loop.
2450 */
2451 JumpHandler beginLoopHeader(ast.Node node) {
2452 assert(!isAborted());
2453 HBasicBlock previousBlock = close(new HGoto());
2454
2455 JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: true);
2456 HBasicBlock loopEntry = graph.addNewLoopHeaderBlock(
2457 jumpHandler.target,
2458 jumpHandler.labels());
2459 previousBlock.addSuccessor(loopEntry);
2460 open(loopEntry);
2461
2462 localsHandler.beginLoopHeader(loopEntry);
2463 return jumpHandler;
2464 }
2465
2466 /**
2467 * Ends the loop:
2468 * - creates a new block and adds it as successor to the [branchExitBlock] and
2469 * any blocks that end in break.
2470 * - opens the new block (setting as [current]).
2471 * - notifies the locals handler that we're exiting a loop.
2472 * [savedLocals] are the locals from the end of the loop condition.
2473 * [branchExitBlock] is the exit (branching) block of the condition. Generally
2474 * this is not the top of the loop, since this would lead to critical edges.
2475 * It is null for degenerate do-while loops that have
2476 * no back edge because they abort (throw/return/break in the body and have
2477 * no continues).
2478 */
2479 void endLoop(HBasicBlock loopEntry,
2480 HBasicBlock branchExitBlock,
2481 JumpHandler jumpHandler,
2482 LocalsHandler savedLocals) {
2483 HBasicBlock loopExitBlock = addNewBlock();
2484
2485 List<LocalsHandler> breakHandlers = <LocalsHandler>[];
2486 // Collect data for the successors and the phis at each break.
2487 jumpHandler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) {
2488 breakInstruction.block.addSuccessor(loopExitBlock);
2489 breakHandlers.add(locals);
2490 });
2491
2492 // The exit block is a successor of the loop condition if it is reached.
2493 // We don't add the successor in the case of a while/for loop that aborts
2494 // because the caller of endLoop will be wiring up a special empty else
2495 // block instead.
2496 if (branchExitBlock != null) {
2497 branchExitBlock.addSuccessor(loopExitBlock);
2498 }
2499 // Update the phis at the loop entry with the current values of locals.
2500 localsHandler.endLoop(loopEntry);
2501
2502 // Start generating code for the exit block.
2503 open(loopExitBlock);
2504
2505 // Create a new localsHandler for the loopExitBlock with the correct phis.
2506 if (!breakHandlers.isEmpty) {
2507 if (branchExitBlock != null) {
2508 // Add the values of the locals at the end of the condition block to
2509 // the phis. These are the values that flow to the exit if the
2510 // condition fails.
2511 breakHandlers.add(savedLocals);
2512 }
2513 localsHandler = savedLocals.mergeMultiple(breakHandlers, loopExitBlock);
2514 } else {
2515 localsHandler = savedLocals;
2516 }
2517 }
2518
2519 HSubGraphBlockInformation wrapStatementGraph(SubGraph statements) {
2520 if (statements == null) return null;
2521 return new HSubGraphBlockInformation(statements);
2522 }
2523
2524 HSubExpressionBlockInformation wrapExpressionGraph(SubExpression expression) {
2525 if (expression == null) return null;
2526 return new HSubExpressionBlockInformation(expression);
2527 }
2528
2529 // For while loops, initializer and update are null.
2530 // The condition function must return a boolean result.
2531 // None of the functions must leave anything on the stack.
2532 void handleLoop(ast.Node loop,
2533 void initialize(),
2534 HInstruction condition(),
2535 void update(),
2536 void body()) {
2537 // Generate:
2538 // <initializer>
2539 // loop-entry:
2540 // if (!<condition>) goto loop-exit;
2541 // <body>
2542 // <updates>
2543 // goto loop-entry;
2544 // loop-exit:
2545
2546 localsHandler.startLoop(loop);
2547
2548 // The initializer.
2549 SubExpression initializerGraph = null;
2550 HBasicBlock startBlock;
2551 if (initialize != null) {
2552 HBasicBlock initializerBlock = openNewBlock();
2553 startBlock = initializerBlock;
2554 initialize();
2555 assert(!isAborted());
2556 initializerGraph =
2557 new SubExpression(initializerBlock, current);
2558 }
2559
2560 loopNesting++;
2561 JumpHandler jumpHandler = beginLoopHeader(loop);
2562 HLoopInformation loopInfo = current.loopInformation;
2563 HBasicBlock conditionBlock = current;
2564 if (startBlock == null) startBlock = conditionBlock;
2565
2566 HInstruction conditionInstruction = condition();
2567 HBasicBlock conditionEndBlock =
2568 close(new HLoopBranch(conditionInstruction));
2569 SubExpression conditionExpression =
2570 new SubExpression(conditionBlock, conditionEndBlock);
2571
2572 // Save the values of the local variables at the end of the condition
2573 // block. These are the values that will flow to the loop exit if the
2574 // condition fails.
2575 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
2576
2577 // The body.
2578 HBasicBlock beginBodyBlock = addNewBlock();
2579 conditionEndBlock.addSuccessor(beginBodyBlock);
2580 open(beginBodyBlock);
2581
2582 localsHandler.enterLoopBody(loop);
2583 body();
2584
2585 SubGraph bodyGraph = new SubGraph(beginBodyBlock, lastOpenedBlock);
2586 HBasicBlock bodyBlock = current;
2587 if (current != null) close(new HGoto());
2588
2589 SubExpression updateGraph;
2590
2591 bool loopIsDegenerate = !jumpHandler.hasAnyContinue() && bodyBlock == null;
2592 if (!loopIsDegenerate) {
2593 // Update.
2594 // We create an update block, even when we are in a while loop. There the
2595 // update block is the jump-target for continue statements. We could avoid
2596 // the creation if there is no continue, but for now we always create it.
2597 HBasicBlock updateBlock = addNewBlock();
2598
2599 List<LocalsHandler> continueHandlers = <LocalsHandler>[];
2600 jumpHandler.forEachContinue((HContinue instruction,
2601 LocalsHandler locals) {
2602 instruction.block.addSuccessor(updateBlock);
2603 continueHandlers.add(locals);
2604 });
2605
2606
2607 if (bodyBlock != null) {
2608 continueHandlers.add(localsHandler);
2609 bodyBlock.addSuccessor(updateBlock);
2610 }
2611
2612 open(updateBlock);
2613 localsHandler =
2614 continueHandlers[0].mergeMultiple(continueHandlers, updateBlock);
2615
2616 HLabeledBlockInformation labelInfo;
2617 List<LabelDefinition> labels = jumpHandler.labels();
2618 JumpTarget target = elements.getTargetDefinition(loop);
2619 if (!labels.isEmpty) {
2620 beginBodyBlock.setBlockFlow(
2621 new HLabeledBlockInformation(
2622 new HSubGraphBlockInformation(bodyGraph),
2623 jumpHandler.labels(),
2624 isContinue: true),
2625 updateBlock);
2626 } else if (target != null && target.isContinueTarget) {
2627 beginBodyBlock.setBlockFlow(
2628 new HLabeledBlockInformation.implicit(
2629 new HSubGraphBlockInformation(bodyGraph),
2630 target,
2631 isContinue: true),
2632 updateBlock);
2633 }
2634
2635 localsHandler.enterLoopUpdates(loop);
2636
2637 update();
2638
2639 HBasicBlock updateEndBlock = close(new HGoto());
2640 // The back-edge completing the cycle.
2641 updateEndBlock.addSuccessor(conditionBlock);
2642 updateGraph = new SubExpression(updateBlock, updateEndBlock);
2643
2644 // Avoid a critical edge from the condition to the loop-exit body.
2645 HBasicBlock conditionExitBlock = addNewBlock();
2646 open(conditionExitBlock);
2647 close(new HGoto());
2648 conditionEndBlock.addSuccessor(conditionExitBlock);
2649
2650 endLoop(conditionBlock, conditionExitBlock, jumpHandler, savedLocals);
2651
2652 conditionBlock.postProcessLoopHeader();
2653 HLoopBlockInformation info =
2654 new HLoopBlockInformation(
2655 HLoopBlockInformation.loopType(loop),
2656 wrapExpressionGraph(initializerGraph),
2657 wrapExpressionGraph(conditionExpression),
2658 wrapStatementGraph(bodyGraph),
2659 wrapExpressionGraph(updateGraph),
2660 conditionBlock.loopInformation.target,
2661 conditionBlock.loopInformation.labels,
2662 sourceFileLocationForBeginToken(loop),
2663 sourceFileLocationForEndToken(loop));
2664
2665 startBlock.setBlockFlow(info, current);
2666 loopInfo.loopBlockInformation = info;
2667 } else {
2668 // The body of the for/while loop always aborts, so there is no back edge.
2669 // We turn the code into:
2670 // if (condition) {
2671 // body;
2672 // } else {
2673 // // We always create an empty else block to avoid critical edges.
2674 // }
2675 //
2676 // If there is any break in the body, we attach a synthetic
2677 // label to the if.
2678 HBasicBlock elseBlock = addNewBlock();
2679 open(elseBlock);
2680 close(new HGoto());
2681 // Pass the elseBlock as the branchBlock, because that's the block we go
2682 // to just before leaving the 'loop'.
2683 endLoop(conditionBlock, elseBlock, jumpHandler, savedLocals);
2684
2685 SubGraph elseGraph = new SubGraph(elseBlock, elseBlock);
2686 // Remove the loop information attached to the header.
2687 conditionBlock.loopInformation = null;
2688
2689 // Remove the [HLoopBranch] instruction and replace it with
2690 // [HIf].
2691 HInstruction condition = conditionEndBlock.last.inputs[0];
2692 conditionEndBlock.addAtExit(new HIf(condition));
2693 conditionEndBlock.addSuccessor(elseBlock);
2694 conditionEndBlock.remove(conditionEndBlock.last);
2695 HIfBlockInformation info =
2696 new HIfBlockInformation(
2697 wrapExpressionGraph(conditionExpression),
2698 wrapStatementGraph(bodyGraph),
2699 wrapStatementGraph(elseGraph));
2700
2701 conditionEndBlock.setBlockFlow(info, current);
2702 HIf ifBlock = conditionEndBlock.last;
2703 ifBlock.blockInformation = conditionEndBlock.blockFlow;
2704
2705 // If the body has any break, attach a synthesized label to the
2706 // if block.
2707 if (jumpHandler.hasAnyBreak()) {
2708 JumpTarget target = elements.getTargetDefinition(loop);
2709 LabelDefinition label = target.addLabel(null, 'loop');
2710 label.setBreakTarget();
2711 SubGraph labelGraph = new SubGraph(conditionBlock, current);
2712 HLabeledBlockInformation labelInfo = new HLabeledBlockInformation(
2713 new HSubGraphBlockInformation(labelGraph),
2714 <LabelDefinition>[label]);
2715
2716 conditionBlock.setBlockFlow(labelInfo, current);
2717
2718 jumpHandler.forEachBreak((HBreak breakInstruction, _) {
2719 HBasicBlock block = breakInstruction.block;
2720 block.addAtExit(new HBreak.toLabel(label));
2721 block.remove(breakInstruction);
2722 });
2723 }
2724 }
2725 jumpHandler.close();
2726 loopNesting--;
2727 }
2728
2729 visitFor(ast.For node) {
2730 assert(isReachable);
2731 assert(node.body != null);
2732 void buildInitializer() {
2733 ast.Node initializer = node.initializer;
2734 if (initializer == null) return;
2735 visit(initializer);
2736 if (initializer.asExpression() != null) {
2737 pop();
2738 }
2739 }
2740 HInstruction buildCondition() {
2741 if (node.condition == null) {
2742 return graph.addConstantBool(true, compiler);
2743 }
2744 visit(node.condition);
2745 return popBoolified();
2746 }
2747 void buildUpdate() {
2748 for (ast.Expression expression in node.update) {
2749 visit(expression);
2750 assert(!isAborted());
2751 // The result of the update instruction isn't used, and can just
2752 // be dropped.
2753 HInstruction updateInstruction = pop();
2754 }
2755 }
2756 void buildBody() {
2757 visit(node.body);
2758 }
2759 handleLoop(node, buildInitializer, buildCondition, buildUpdate, buildBody);
2760 }
2761
2762 visitWhile(ast.While node) {
2763 assert(isReachable);
2764 HInstruction buildCondition() {
2765 visit(node.condition);
2766 return popBoolified();
2767 }
2768 handleLoop(node,
2769 () {},
2770 buildCondition,
2771 () {},
2772 () { visit(node.body); });
2773 }
2774
2775 visitDoWhile(ast.DoWhile node) {
2776 assert(isReachable);
2777 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
2778 localsHandler.startLoop(node);
2779 loopNesting++;
2780 JumpHandler jumpHandler = beginLoopHeader(node);
2781 HLoopInformation loopInfo = current.loopInformation;
2782 HBasicBlock loopEntryBlock = current;
2783 HBasicBlock bodyEntryBlock = current;
2784 JumpTarget target = elements.getTargetDefinition(node);
2785 bool hasContinues = target != null && target.isContinueTarget;
2786 if (hasContinues) {
2787 // Add extra block to hang labels on.
2788 // It doesn't currently work if they are on the same block as the
2789 // HLoopInfo. The handling of HLabeledBlockInformation will visit a
2790 // SubGraph that starts at the same block again, so the HLoopInfo is
2791 // either handled twice, or it's handled after the labeled block info,
2792 // both of which generate the wrong code.
2793 // Using a separate block is just a simple workaround.
2794 bodyEntryBlock = openNewBlock();
2795 }
2796 localsHandler.enterLoopBody(node);
2797 visit(node.body);
2798
2799 // If there are no continues we could avoid the creation of the condition
2800 // block. This could also lead to a block having multiple entries and exits.
2801 HBasicBlock bodyExitBlock;
2802 bool isAbortingBody = false;
2803 if (current != null) {
2804 bodyExitBlock = close(new HGoto());
2805 } else {
2806 isAbortingBody = true;
2807 bodyExitBlock = lastOpenedBlock;
2808 }
2809
2810 SubExpression conditionExpression;
2811 bool loopIsDegenerate = isAbortingBody && !hasContinues;
2812 if (!loopIsDegenerate) {
2813 HBasicBlock conditionBlock = addNewBlock();
2814
2815 List<LocalsHandler> continueHandlers = <LocalsHandler>[];
2816 jumpHandler.forEachContinue((HContinue instruction,
2817 LocalsHandler locals) {
2818 instruction.block.addSuccessor(conditionBlock);
2819 continueHandlers.add(locals);
2820 });
2821
2822 if (!isAbortingBody) {
2823 bodyExitBlock.addSuccessor(conditionBlock);
2824 }
2825
2826 if (!continueHandlers.isEmpty) {
2827 if (!isAbortingBody) continueHandlers.add(localsHandler);
2828 localsHandler =
2829 savedLocals.mergeMultiple(continueHandlers, conditionBlock);
2830 SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock);
2831 List<LabelDefinition> labels = jumpHandler.labels();
2832 HSubGraphBlockInformation bodyInfo =
2833 new HSubGraphBlockInformation(bodyGraph);
2834 HLabeledBlockInformation info;
2835 if (!labels.isEmpty) {
2836 info = new HLabeledBlockInformation(bodyInfo, labels,
2837 isContinue: true);
2838 } else {
2839 info = new HLabeledBlockInformation.implicit(bodyInfo, target,
2840 isContinue: true);
2841 }
2842 bodyEntryBlock.setBlockFlow(info, conditionBlock);
2843 }
2844 open(conditionBlock);
2845
2846 visit(node.condition);
2847 assert(!isAborted());
2848 HInstruction conditionInstruction = popBoolified();
2849 HBasicBlock conditionEndBlock = close(
2850 new HLoopBranch(conditionInstruction, HLoopBranch.DO_WHILE_LOOP));
2851
2852 HBasicBlock avoidCriticalEdge = addNewBlock();
2853 conditionEndBlock.addSuccessor(avoidCriticalEdge);
2854 open(avoidCriticalEdge);
2855 close(new HGoto());
2856 avoidCriticalEdge.addSuccessor(loopEntryBlock); // The back-edge.
2857
2858 conditionExpression =
2859 new SubExpression(conditionBlock, conditionEndBlock);
2860
2861 // Avoid a critical edge from the condition to the loop-exit body.
2862 HBasicBlock conditionExitBlock = addNewBlock();
2863 open(conditionExitBlock);
2864 close(new HGoto());
2865 conditionEndBlock.addSuccessor(conditionExitBlock);
2866
2867 endLoop(loopEntryBlock, conditionExitBlock, jumpHandler, localsHandler);
2868
2869 loopEntryBlock.postProcessLoopHeader();
2870 SubGraph bodyGraph = new SubGraph(loopEntryBlock, bodyExitBlock);
2871 HLoopBlockInformation loopBlockInfo =
2872 new HLoopBlockInformation(
2873 HLoopBlockInformation.DO_WHILE_LOOP,
2874 null,
2875 wrapExpressionGraph(conditionExpression),
2876 wrapStatementGraph(bodyGraph),
2877 null,
2878 loopEntryBlock.loopInformation.target,
2879 loopEntryBlock.loopInformation.labels,
2880 sourceFileLocationForBeginToken(node),
2881 sourceFileLocationForEndToken(node));
2882 loopEntryBlock.setBlockFlow(loopBlockInfo, current);
2883 loopInfo.loopBlockInformation = loopBlockInfo;
2884 } else {
2885 // Since the loop has no back edge, we remove the loop information on the
2886 // header.
2887 loopEntryBlock.loopInformation = null;
2888
2889 if (jumpHandler.hasAnyBreak()) {
2890 // Null branchBlock because the body of the do-while loop always aborts,
2891 // so we never get to the condition.
2892 endLoop(loopEntryBlock, null, jumpHandler, localsHandler);
2893
2894 // Since the body of the loop has a break, we attach a synthesized label
2895 // to the body.
2896 SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock);
2897 JumpTarget target = elements.getTargetDefinition(node);
2898 LabelDefinition label = target.addLabel(null, 'loop');
2899 label.setBreakTarget();
2900 HLabeledBlockInformation info = new HLabeledBlockInformation(
2901 new HSubGraphBlockInformation(bodyGraph), <LabelDefinition>[label]);
2902 loopEntryBlock.setBlockFlow(info, current);
2903 jumpHandler.forEachBreak((HBreak breakInstruction, _) {
2904 HBasicBlock block = breakInstruction.block;
2905 block.addAtExit(new HBreak.toLabel(label));
2906 block.remove(breakInstruction);
2907 });
2908 }
2909 }
2910 jumpHandler.close();
2911 loopNesting--;
2912 }
2913
2914 visitFunctionExpression(ast.FunctionExpression node) {
2915 ClosureClassMap nestedClosureData =
2916 compiler.closureToClassMapper.getMappingForNestedFunction(node);
2917 assert(nestedClosureData != null);
2918 assert(nestedClosureData.closureClassElement != null);
2919 ClosureClassElement closureClassElement =
2920 nestedClosureData.closureClassElement;
2921 FunctionElement callElement = nestedClosureData.callElement;
2922 // TODO(ahe): This should be registered in codegen, not here.
2923 // TODO(johnniwinther): Is [registerStaticUse] equivalent to
2924 // [addToWorkList]?
2925 registry.registerStaticUse(callElement);
2926 // TODO(ahe): This should be registered in codegen, not here.
2927 registry.registerInstantiatedClass(closureClassElement);
2928
2929 List<HInstruction> capturedVariables = <HInstruction>[];
2930 closureClassElement.closureFields.forEach((ClosureFieldElement field) {
2931 Local capturedLocal =
2932 nestedClosureData.getLocalVariableForClosureField(field);
2933 assert(capturedLocal != null);
2934 capturedVariables.add(localsHandler.readLocal(capturedLocal));
2935 });
2936
2937 TypeMask type =
2938 new TypeMask.nonNullExact(compiler.functionClass, compiler.world);
2939 push(new HForeignNew(closureClassElement, type, capturedVariables));
2940
2941 Element methodElement = nestedClosureData.closureElement;
2942 if (compiler.backend.methodNeedsRti(methodElement)) {
2943 registry.registerClosureWithFreeTypeVariables(methodElement);
2944 }
2945 }
2946
2947 visitFunctionDeclaration(ast.FunctionDeclaration node) {
2948 assert(isReachable);
2949 visit(node.function);
2950 LocalFunctionElement localFunction =
2951 elements.getFunctionDefinition(node.function);
2952 localsHandler.updateLocal(localFunction, pop());
2953 }
2954
2955 visitIdentifier(ast.Identifier node) {
2956 if (node.isThis()) {
2957 stack.add(localsHandler.readThis());
2958 } else {
2959 compiler.internalError(node,
2960 "SsaFromAstMixin.visitIdentifier on non-this.");
2961 }
2962 }
2963
2964 visitIf(ast.If node) {
2965 assert(isReachable);
2966 handleIf(node,
2967 () => visit(node.condition),
2968 () => visit(node.thenPart),
2969 node.elsePart != null ? () => visit(node.elsePart) : null);
2970 }
2971
2972 void handleIf(ast.Node diagnosticNode,
2973 void visitCondition(), void visitThen(), void visitElse()) {
2974 SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, diagnosticNode);
2975 branchBuilder.handleIf(visitCondition, visitThen, visitElse);
2976 }
2977
2978 void visitLogicalAndOr(ast.Send node, ast.Operator op) {
2979 SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, node);
2980 branchBuilder.handleLogicalAndOrWithLeftNode(
2981 node.receiver,
2982 () { visit(node.argumentsNode); },
2983 isAnd: ("&&" == op.source));
2984 }
2985
2986 void visitLogicalNot(ast.Send node) {
2987 assert(node.argumentsNode is ast.Prefix);
2988 visit(node.receiver);
2989 HNot not = new HNot(popBoolified(), backend.boolType);
2990 pushWithPosition(not, node);
2991 }
2992
2993 void visitUnary(ast.Send node, ast.Operator op) {
2994 assert(node.argumentsNode is ast.Prefix);
2995 visit(node.receiver);
2996 assert(!identical(op.token.kind, PLUS_TOKEN));
2997 HInstruction operand = pop();
2998
2999 // See if we can constant-fold right away. This avoids rewrites later on.
3000 if (operand is HConstant) {
3001 UnaryOperation operation = constantSystem.lookupUnary(op.source);
3002 HConstant constant = operand;
3003 ConstantValue folded = operation.fold(constant.constant);
3004 if (folded != null) {
3005 stack.add(graph.addConstant(folded, compiler));
3006 return;
3007 }
3008 }
3009
3010 pushInvokeDynamic(node, elements.getSelector(node), [operand]);
3011 }
3012
3013 void visitBinary(HInstruction left,
3014 ast.Operator op,
3015 HInstruction right,
3016 Selector selector,
3017 ast.Send send) {
3018 switch (op.source) {
3019 case "===":
3020 pushWithPosition(
3021 new HIdentity(left, right, null, backend.boolType), op);
3022 return;
3023 case "!==":
3024 HIdentity eq = new HIdentity(left, right, null, backend.boolType);
3025 add(eq);
3026 pushWithPosition(new HNot(eq, backend.boolType), op);
3027 return;
3028 }
3029
3030 pushInvokeDynamic(send, selector, [left, right], location: op);
3031 if (op.source == '!=') {
3032 pushWithPosition(new HNot(popBoolified(), backend.boolType), op);
3033 }
3034 }
3035
3036 HInstruction generateInstanceSendReceiver(ast.Send send) {
3037 assert(Elements.isInstanceSend(send, elements));
3038 if (send.receiver == null) {
3039 return localsHandler.readThis();
3040 }
3041 visit(send.receiver);
3042 return pop();
3043 }
3044
3045 String noSuchMethodTargetSymbolString(ErroneousElement error,
3046 [String prefix]) {
3047 String result = error.name;
3048 if (prefix == "set") return "$result=";
3049 return result;
3050 }
3051
3052 /**
3053 * Returns a set of interceptor classes that contain the given
3054 * [selector].
3055 */
3056 void generateInstanceGetterWithCompiledReceiver(ast.Send send,
3057 Selector selector,
3058 HInstruction receiver) {
3059 assert(Elements.isInstanceSend(send, elements));
3060 assert(selector.isGetter);
3061 pushInvokeDynamic(send, selector, [receiver]);
3062 }
3063
3064 /// Inserts a call to checkDeferredIsLoaded if the send has a prefix that
3065 /// resolves to a deferred library.
3066 void generateIsDeferredLoadedCheckIfNeeded(ast.Send node) {
3067 DeferredLoadTask deferredTask = compiler.deferredLoadTask;
3068 PrefixElement prefixElement =
3069 deferredTask.deferredPrefixElement(node, elements);
3070 if (prefixElement != null) {
3071 String loadId =
3072 deferredTask.importDeferName[prefixElement.deferredImport];
3073 HInstruction loadIdConstant = addConstantString(loadId);
3074 String uri = prefixElement.deferredImport.uri.dartString.slowToString();
3075 HInstruction uriConstant = addConstantString(uri);
3076 Element helper = backend.getCheckDeferredIsLoaded();
3077 pushInvokeStatic(node, helper, [loadIdConstant, uriConstant]);
3078 pop();
3079 }
3080 }
3081
3082 void generateGetter(ast.Send send, Element element) {
3083 if (element != null && element.isForeign(backend)) {
3084 visitForeignGetter(send);
3085 } else if (Elements.isStaticOrTopLevelField(element)) {
3086 ConstantExpression constant;
3087 if (element.isField && !element.isAssignable) {
3088 // A static final or const. Get its constant value and inline it if
3089 // the value can be compiled eagerly.
3090 constant = backend.constants.getConstantForVariable(element);
3091 }
3092 if (constant != null) {
3093 ConstantValue value = constant.value;
3094 HConstant instruction;
3095 // Constants that are referred via a deferred prefix should be referred
3096 // by reference.
3097 PrefixElement prefix = compiler.deferredLoadTask
3098 .deferredPrefixElement(send, elements);
3099 if (prefix != null) {
3100 instruction = graph.addDeferredConstant(value, prefix, compiler);
3101 } else {
3102 instruction = graph.addConstant(value, compiler);
3103 }
3104 stack.add(instruction);
3105 // The inferrer may have found a better type than the constant
3106 // handler in the case of lists, because the constant handler
3107 // does not look at elements in the list.
3108 TypeMask type =
3109 TypeMaskFactory.inferredTypeForElement(element, compiler);
3110 if (!type.containsAll(compiler.world) &&
3111 !instruction.isConstantNull()) {
3112 // TODO(13429): The inferrer should know that an element
3113 // cannot be null.
3114 instruction.instructionType = type.nonNullable();
3115 }
3116 } else if (element.isField && isLazilyInitialized(element)) {
3117 HInstruction instruction = new HLazyStatic(
3118 element,
3119 TypeMaskFactory.inferredTypeForElement(element, compiler));
3120 push(instruction);
3121 } else {
3122 if (element.isGetter) {
3123 pushInvokeStatic(send, element, <HInstruction>[]);
3124 } else {
3125 // TODO(5346): Try to avoid the need for calling [declaration] before
3126 // creating an [HStatic].
3127 HInstruction instruction = new HStatic(
3128 element.declaration,
3129 TypeMaskFactory.inferredTypeForElement(element, compiler));
3130 push(instruction);
3131 }
3132 }
3133 } else if (Elements.isInstanceSend(send, elements)) {
3134 HInstruction receiver = generateInstanceSendReceiver(send);
3135 generateInstanceGetterWithCompiledReceiver(
3136 send, elements.getSelector(send), receiver);
3137 } else if (Elements.isStaticOrTopLevelFunction(element)) {
3138 // TODO(5346): Try to avoid the need for calling [declaration] before
3139 // creating an [HStatic].
3140 push(new HStatic(element.declaration, backend.nonNullType));
3141 // TODO(ahe): This should be registered in codegen.
3142 registry.registerGetOfStaticFunction(element.declaration);
3143 } else if (Elements.isErroneousElement(element)) {
3144 // An erroneous element indicates an unresolved static getter.
3145 generateThrowNoSuchMethod(send,
3146 noSuchMethodTargetSymbolString(element, 'get'),
3147 argumentNodes: const Link<ast.Node>());
3148 } else {
3149 LocalElement local = element;
3150 stack.add(localsHandler.readLocal(local));
3151 }
3152 }
3153
3154 void generateInstanceSetterWithCompiledReceiver(ast.Send send,
3155 HInstruction receiver,
3156 HInstruction value,
3157 {Selector selector,
3158 ast.Node location}) {
3159 assert(send == null || Elements.isInstanceSend(send, elements));
3160 if (selector == null) {
3161 assert(send != null);
3162 selector = elements.getSelector(send);
3163 }
3164 if (location == null) {
3165 assert(send != null);
3166 location = send;
3167 }
3168 assert(selector.isSetter);
3169 pushInvokeDynamic(location, selector, [receiver, value]);
3170 pop();
3171 stack.add(value);
3172 }
3173
3174 void generateNonInstanceSetter(ast.SendSet send,
3175 Element element,
3176 HInstruction value,
3177 {ast.Node location}) {
3178 assert(send == null || !Elements.isInstanceSend(send, elements));
3179 if (location == null) {
3180 assert(send != null);
3181 location = send;
3182 }
3183 if (Elements.isStaticOrTopLevelField(element)) {
3184 if (element.isSetter) {
3185 pushInvokeStatic(location, element, <HInstruction>[value]);
3186 pop();
3187 } else {
3188 VariableElement field = element;
3189 value =
3190 potentiallyCheckType(value, field.type);
3191 addWithPosition(new HStaticStore(element, value), location);
3192 }
3193 stack.add(value);
3194 } else if (Elements.isErroneousElement(element)) {
3195 List<HInstruction> arguments =
3196 send == null ? const <HInstruction>[] : <HInstruction>[value];
3197 // An erroneous element indicates an unresolved static setter.
3198 generateThrowNoSuchMethod(location,
3199 noSuchMethodTargetSymbolString(element, 'set'),
3200 argumentValues: arguments);
3201 } else {
3202 stack.add(value);
3203 LocalElement local = element;
3204 // If the value does not already have a name, give it here.
3205 if (value.sourceElement == null) {
3206 value.sourceElement = local;
3207 }
3208 HInstruction checked =
3209 potentiallyCheckType(value, local.type);
3210 if (!identical(checked, value)) {
3211 pop();
3212 stack.add(checked);
3213 }
3214 HInstruction trusted =
3215 potentiallyBuildTypeHint(checked, local.type);
3216 if (!identical(trusted, checked)) {
3217 pop();
3218 push(trusted);
3219 }
3220
3221 localsHandler.updateLocal(local, trusted);
3222 }
3223 }
3224
3225 HInstruction invokeInterceptor(HInstruction receiver) {
3226 HInterceptor interceptor = new HInterceptor(receiver, backend.nonNullType);
3227 add(interceptor);
3228 return interceptor;
3229 }
3230
3231 HForeign createForeign(js.Template code,
3232 TypeMask type,
3233 List<HInstruction> inputs) {
3234 return new HForeign(code, type, inputs);
3235 }
3236
3237 HLiteralList buildLiteralList(List<HInstruction> inputs) {
3238 return new HLiteralList(inputs, backend.extendableArrayType);
3239 }
3240
3241 // TODO(karlklose): change construction of the representations to be GVN'able
3242 // (dartbug.com/7182).
3243 HInstruction buildTypeArgumentRepresentations(DartType type) {
3244 // Compute the representation of the type arguments, including access
3245 // to the runtime type information for type variables as instructions.
3246 if (type.isTypeVariable) {
3247 return buildLiteralList(<HInstruction>[addTypeVariableReference(type)]);
3248 } else {
3249 assert(type.element.isClass);
3250 InterfaceType interface = type;
3251 List<HInstruction> inputs = <HInstruction>[];
3252 bool first = true;
3253 List<String> templates = <String>[];
3254 for (DartType argument in interface.typeArguments) {
3255 templates.add(rti.getTypeRepresentationWithHashes(argument, (variable) {
3256 HInstruction runtimeType = addTypeVariableReference(variable);
3257 inputs.add(runtimeType);
3258 }));
3259 }
3260 String template = '[${templates.join(', ')}]';
3261 // TODO(sra): This is a fresh template each time. We can't let the
3262 // template manager build them.
3263 js.Template code = js.js.uncachedExpressionTemplate(template);
3264 HInstruction representation =
3265 createForeign(code, backend.readableArrayType, inputs);
3266 return representation;
3267 }
3268 }
3269
3270 visitOperatorSend(ast.Send node) {
3271 ast.Operator op = node.selector;
3272 if ("[]" == op.source) {
3273 visitDynamicSend(node);
3274 } else if ("&&" == op.source ||
3275 "||" == op.source) {
3276 visitLogicalAndOr(node, op);
3277 } else if ("!" == op.source) {
3278 visitLogicalNot(node);
3279 } else if (node.argumentsNode is ast.Prefix) {
3280 visitUnary(node, op);
3281 } else if ("is" == op.source) {
3282 visitIsSend(node);
3283 } else if ("as" == op.source) {
3284 visit(node.receiver);
3285 HInstruction expression = pop();
3286 DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast);
3287 if (type.isMalformed) {
3288 ErroneousElement element = type.element;
3289 generateTypeError(node, element.message);
3290 } else {
3291 HInstruction converted = buildTypeConversion(
3292 expression,
3293 localsHandler.substInContext(type),
3294 HTypeConversion.CAST_TYPE_CHECK);
3295 if (converted != expression) add(converted);
3296 stack.add(converted);
3297 }
3298 } else {
3299 visit(node.receiver);
3300 visit(node.argumentsNode);
3301 var right = pop();
3302 var left = pop();
3303 visitBinary(left, op, right, elements.getSelector(node), node);
3304 }
3305 }
3306
3307 void visitIsSend(ast.Send node) {
3308 visit(node.receiver);
3309 HInstruction expression = pop();
3310 bool isNot = node.isIsNotCheck;
3311 DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast);
3312 HInstruction instruction = buildIsNode(node, type, expression);
3313 if (isNot) {
3314 add(instruction);
3315 instruction = new HNot(instruction, backend.boolType);
3316 }
3317 push(instruction);
3318 }
3319
3320 HInstruction buildIsNode(ast.Node node,
3321 DartType type,
3322 HInstruction expression) {
3323 type = localsHandler.substInContext(type).unalias(compiler);
3324 if (type.isFunctionType) {
3325 List arguments = [buildFunctionType(type), expression];
3326 pushInvokeDynamic(
3327 node, new Selector.call('_isTest', backend.jsHelperLibrary, 1),
3328 arguments);
3329 return new HIs.compound(type, expression, pop(), backend.boolType);
3330 } else if (type.isTypeVariable) {
3331 HInstruction runtimeType = addTypeVariableReference(type);
3332 Element helper = backend.getCheckSubtypeOfRuntimeType();
3333 List<HInstruction> inputs = <HInstruction>[expression, runtimeType];
3334 pushInvokeStatic(null, helper, inputs, backend.boolType);
3335 HInstruction call = pop();
3336 return new HIs.variable(type, expression, call, backend.boolType);
3337 } else if (RuntimeTypes.hasTypeArguments(type)) {
3338 ClassElement element = type.element;
3339 Element helper = backend.getCheckSubtype();
3340 HInstruction representations =
3341 buildTypeArgumentRepresentations(type);
3342 add(representations);
3343 String operator = backend.namer.operatorIs(element);
3344 HInstruction isFieldName = addConstantString(operator);
3345 HInstruction asFieldName = compiler.world.hasAnyStrictSubtype(element)
3346 ? addConstantString(backend.namer.substitutionName(element))
3347 : graph.addConstantNull(compiler);
3348 List<HInstruction> inputs = <HInstruction>[expression,
3349 isFieldName,
3350 representations,
3351 asFieldName];
3352 pushInvokeStatic(node, helper, inputs, backend.boolType);
3353 HInstruction call = pop();
3354 return new HIs.compound(type, expression, call, backend.boolType);
3355 } else if (type.isMalformed) {
3356 ErroneousElement element = type.element;
3357 generateTypeError(node, element.message);
3358 HInstruction call = pop();
3359 return new HIs.compound(type, expression, call, backend.boolType);
3360 } else {
3361 if (backend.hasDirectCheckFor(type)) {
3362 return new HIs.direct(type, expression, backend.boolType);
3363 }
3364 // TODO(johnniwinther): Avoid interceptor if unneeded.
3365 return new HIs.raw(
3366 type, expression, invokeInterceptor(expression), backend.boolType);
3367 }
3368 }
3369
3370 HInstruction buildFunctionType(FunctionType type) {
3371 type.accept(new TypeBuilder(compiler.world), this);
3372 return pop();
3373 }
3374
3375 void addDynamicSendArgumentsToList(ast.Send node, List<HInstruction> list) {
3376 Selector selector = elements.getSelector(node);
3377 if (selector.namedArgumentCount == 0) {
3378 addGenericSendArgumentsToList(node.arguments, list);
3379 } else {
3380 // Visit positional arguments and add them to the list.
3381 Link<ast.Node> arguments = node.arguments;
3382 int positionalArgumentCount = selector.positionalArgumentCount;
3383 for (int i = 0;
3384 i < positionalArgumentCount;
3385 arguments = arguments.tail, i++) {
3386 visit(arguments.head);
3387 list.add(pop());
3388 }
3389
3390 // Visit named arguments and add them into a temporary map.
3391 Map<String, HInstruction> instructions =
3392 new Map<String, HInstruction>();
3393 List<String> namedArguments = selector.namedArguments;
3394 int nameIndex = 0;
3395 for (; !arguments.isEmpty; arguments = arguments.tail) {
3396 visit(arguments.head);
3397 instructions[namedArguments[nameIndex++]] = pop();
3398 }
3399
3400 // Iterate through the named arguments to add them to the list
3401 // of instructions, in an order that can be shared with
3402 // selectors with the same named arguments.
3403 List<String> orderedNames = selector.getOrderedNamedArguments();
3404 for (String name in orderedNames) {
3405 list.add(instructions[name]);
3406 }
3407 }
3408 }
3409
3410 /**
3411 * Returns true if the arguments were compatible with the function signature.
3412 *
3413 * Invariant: [element] must be an implementation element.
3414 */
3415 bool addStaticSendArgumentsToList(Selector selector,
3416 Link<ast.Node> arguments,
3417 FunctionElement element,
3418 List<HInstruction> list) {
3419 assert(invariant(element, element.isImplementation));
3420
3421 HInstruction compileArgument(ast.Node argument) {
3422 visit(argument);
3423 return pop();
3424 }
3425
3426 return selector.addArgumentsToList(arguments,
3427 list,
3428 element,
3429 compileArgument,
3430 handleConstantForOptionalParameter,
3431 compiler.world);
3432 }
3433
3434 void addGenericSendArgumentsToList(Link<ast.Node> link, List<HInstruction> lis t) {
3435 for (; !link.isEmpty; link = link.tail) {
3436 visit(link.head);
3437 list.add(pop());
3438 }
3439 }
3440
3441 visitDynamicSend(ast.Send node) {
3442 Selector selector = elements.getSelector(node);
3443
3444 List<HInstruction> inputs = <HInstruction>[];
3445 HInstruction receiver = generateInstanceSendReceiver(node);
3446 inputs.add(receiver);
3447 addDynamicSendArgumentsToList(node, inputs);
3448
3449 pushInvokeDynamic(node, selector, inputs);
3450 if (selector.isSetter || selector.isIndexSet) {
3451 pop();
3452 stack.add(inputs.last);
3453 }
3454 }
3455
3456 visitClosureSend(ast.Send node) {
3457 Selector selector = elements.getSelector(node);
3458 assert(node.receiver == null);
3459 Element element = elements[node];
3460 HInstruction closureTarget;
3461 if (element == null) {
3462 visit(node.selector);
3463 closureTarget = pop();
3464 } else {
3465 LocalElement local = element;
3466 closureTarget = localsHandler.readLocal(local);
3467 }
3468 var inputs = <HInstruction>[];
3469 inputs.add(closureTarget);
3470 addDynamicSendArgumentsToList(node, inputs);
3471 Selector closureSelector = new Selector.callClosureFrom(selector);
3472 pushWithPosition(
3473 new HInvokeClosure(closureSelector, inputs, backend.dynamicType),
3474 node);
3475 }
3476
3477 void handleForeignJs(ast.Send node) {
3478 Link<ast.Node> link = node.arguments;
3479 // If the invoke is on foreign code, don't visit the first
3480 // argument, which is the type, and the second argument,
3481 // which is the foreign code.
3482 if (link.isEmpty || link.tail.isEmpty) {
3483 compiler.internalError(node.argumentsNode,
3484 'At least two arguments expected.');
3485 }
3486 native.NativeBehavior nativeBehavior =
3487 compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node);
3488
3489 List<HInstruction> inputs = <HInstruction>[];
3490 addGenericSendArgumentsToList(link.tail.tail, inputs);
3491
3492 TypeMask ssaType =
3493 TypeMaskFactory.fromNativeBehavior(nativeBehavior, compiler);
3494
3495 if (nativeBehavior.codeTemplate.isExpression) {
3496 push(new HForeign(nativeBehavior.codeTemplate, ssaType, inputs,
3497 effects: nativeBehavior.sideEffects,
3498 nativeBehavior: nativeBehavior));
3499 } else {
3500 push(new HForeign(nativeBehavior.codeTemplate, ssaType, inputs,
3501 isStatement: true,
3502 effects: nativeBehavior.sideEffects,
3503 nativeBehavior: nativeBehavior,
3504 canThrow: true));
3505 }
3506 }
3507
3508 void handleJsStringConcat(ast.Send node) {
3509 List<HInstruction> inputs = <HInstruction>[];
3510 addGenericSendArgumentsToList(node.arguments, inputs);
3511 if (inputs.length != 2) {
3512 compiler.internalError(node.argumentsNode, 'Two arguments expected.');
3513 }
3514 push(new HStringConcat(inputs[0], inputs[1], node, backend.stringType));
3515 }
3516
3517 void handleForeignJsCurrentIsolateContext(ast.Send node) {
3518 if (!node.arguments.isEmpty) {
3519 compiler.internalError(node,
3520 'Too many arguments to JS_CURRENT_ISOLATE_CONTEXT.');
3521 }
3522
3523 if (!compiler.hasIsolateSupport) {
3524 // If the isolate library is not used, we just generate code
3525 // to fetch the current isolate.
3526 String name = backend.namer.currentIsolate;
3527 push(new HForeign(js.js.parseForeignJS(name),
3528 backend.dynamicType,
3529 <HInstruction>[]));
3530 } else {
3531 // Call a helper method from the isolate library. The isolate
3532 // library uses its own isolate structure, that encapsulates
3533 // Leg's isolate.
3534 Element element = backend.isolateHelperLibrary.find('_currentIsolate');
3535 if (element == null) {
3536 compiler.internalError(node,
3537 'Isolate library and compiler mismatch.');
3538 }
3539 pushInvokeStatic(null, element, [], backend.dynamicType);
3540 }
3541 }
3542
3543 void handleForeingJsGetFlag(ast.Send node) {
3544 List<ast.Node> arguments = node.arguments.toList();
3545 ast.Node argument;
3546 switch (arguments.length) {
3547 case 0:
3548 compiler.reportError(
3549 node, MessageKind.GENERIC,
3550 {'text': 'Error: Expected one argument to JS_GET_FLAG.'});
3551 return;
3552 case 1:
3553 argument = arguments[0];
3554 break;
3555 default:
3556 for (int i = 1; i < arguments.length; i++) {
3557 compiler.reportError(
3558 arguments[i], MessageKind.GENERIC,
3559 {'text': 'Error: Extra argument to JS_GET_FLAG.'});
3560 }
3561 return;
3562 }
3563 ast.LiteralString string = argument.asLiteralString();
3564 if (string == null) {
3565 compiler.reportError(
3566 argument, MessageKind.GENERIC,
3567 {'text': 'Error: Expected a literal string.'});
3568 }
3569 String name = string.dartString.slowToString();
3570 bool value = false;
3571 if (name == 'MUST_RETAIN_METADATA') {
3572 value = backend.mustRetainMetadata;
3573 } else {
3574 compiler.reportError(
3575 node, MessageKind.GENERIC,
3576 {'text': 'Error: Unknown internal flag "$name".'});
3577 }
3578 stack.add(graph.addConstantBool(value, compiler));
3579 }
3580
3581 void handleForeignJsGetName(ast.Send node) {
3582 List<ast.Node> arguments = node.arguments.toList();
3583 ast.Node argument;
3584 switch (arguments.length) {
3585 case 0:
3586 compiler.reportError(
3587 node, MessageKind.GENERIC,
3588 {'text': 'Error: Expected one argument to JS_GET_NAME.'});
3589 return;
3590 case 1:
3591 argument = arguments[0];
3592 break;
3593 default:
3594 for (int i = 1; i < arguments.length; i++) {
3595 compiler.reportError(
3596 arguments[i], MessageKind.GENERIC,
3597 {'text': 'Error: Extra argument to JS_GET_NAME.'});
3598 }
3599 return;
3600 }
3601 ast.LiteralString string = argument.asLiteralString();
3602 if (string == null) {
3603 compiler.reportError(
3604 argument, MessageKind.GENERIC,
3605 {'text': 'Error: Expected a literal string.'});
3606 }
3607 stack.add(
3608 addConstantString(
3609 backend.namer.getNameForJsGetName(
3610 argument, string.dartString.slowToString())));
3611 }
3612
3613 void handleForeignJsEmbeddedGlobal(ast.Send node) {
3614 List<ast.Node> arguments = node.arguments.toList();
3615 ast.Node globalNameNode;
3616 switch (arguments.length) {
3617 case 0:
3618 case 1:
3619 compiler.reportError(
3620 node, MessageKind.GENERIC,
3621 {'text': 'Error: Expected two arguments to JS_EMBEDDED_GLOBAL.'});
3622 return;
3623 case 2:
3624 // The type has been extracted earlier. We are only interested in the
3625 // name in this function.
3626 globalNameNode = arguments[1];
3627 break;
3628 default:
3629 for (int i = 2; i < arguments.length; i++) {
3630 compiler.reportError(
3631 arguments[i], MessageKind.GENERIC,
3632 {'text': 'Error: Extra argument to JS_EMBEDDED_GLOBAL.'});
3633 }
3634 return;
3635 }
3636 visit(arguments[1]);
3637 HInstruction globalNameHNode = pop();
3638 if (!globalNameHNode.isConstantString()) {
3639 compiler.reportError(
3640 arguments[1], MessageKind.GENERIC,
3641 {'text': 'Error: Expected String as second argument '
3642 'to JS_EMBEDDED_GLOBAL.'});
3643 return;
3644 }
3645 HConstant hConstant = globalNameHNode;
3646 StringConstantValue constant = hConstant.constant;
3647 String globalName = constant.primitiveValue.slowToString();
3648 js.Template expr = js.js.expressionTemplateYielding(
3649 backend.emitter.generateEmbeddedGlobalAccess(globalName));
3650 native.NativeBehavior nativeBehavior =
3651 compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node);
3652 TypeMask ssaType =
3653 TypeMaskFactory.fromNativeBehavior(nativeBehavior, compiler);
3654 push(new HForeign(expr, ssaType, const []));
3655 }
3656
3657 void handleJsInterceptorConstant(ast.Send node) {
3658 // Single argument must be a TypeConstant which is converted into a
3659 // InterceptorConstant.
3660 if (!node.arguments.isEmpty && node.arguments.tail.isEmpty) {
3661 ast.Node argument = node.arguments.head;
3662 visit(argument);
3663 HInstruction argumentInstruction = pop();
3664 if (argumentInstruction is HConstant) {
3665 ConstantValue argumentConstant = argumentInstruction.constant;
3666 if (argumentConstant is TypeConstantValue) {
3667 ConstantValue constant =
3668 new InterceptorConstantValue(argumentConstant.representedType);
3669 HInstruction instruction = graph.addConstant(constant, compiler);
3670 stack.add(instruction);
3671 return;
3672 }
3673 }
3674 }
3675 compiler.reportError(node,
3676 MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT);
3677 stack.add(graph.addConstantNull(compiler));
3678 }
3679
3680 void handleForeignJsCallInIsolate(ast.Send node) {
3681 Link<ast.Node> link = node.arguments;
3682 if (!compiler.hasIsolateSupport) {
3683 // If the isolate library is not used, we just invoke the
3684 // closure.
3685 visit(link.tail.head);
3686 Selector selector = new Selector.callClosure(0);
3687 push(new HInvokeClosure(selector,
3688 <HInstruction>[pop()],
3689 backend.dynamicType));
3690 } else {
3691 // Call a helper method from the isolate library.
3692 Element element = backend.isolateHelperLibrary.find('_callInIsolate');
3693 if (element == null) {
3694 compiler.internalError(node,
3695 'Isolate library and compiler mismatch.');
3696 }
3697 List<HInstruction> inputs = <HInstruction>[];
3698 addGenericSendArgumentsToList(link, inputs);
3699 pushInvokeStatic(node, element, inputs, backend.dynamicType);
3700 }
3701 }
3702
3703 FunctionSignature handleForeignRawFunctionRef(ast.Send node, String name) {
3704 if (node.arguments.isEmpty || !node.arguments.tail.isEmpty) {
3705 compiler.internalError(node.argumentsNode,
3706 '"$name" requires exactly one argument.');
3707 }
3708 ast.Node closure = node.arguments.head;
3709 Element element = elements[closure];
3710 if (!Elements.isStaticOrTopLevelFunction(element)) {
3711 compiler.internalError(closure,
3712 '"$name" requires a static or top-level method.');
3713 }
3714 FunctionElement function = element;
3715 // TODO(johnniwinther): Try to eliminate the need to distinguish declaration
3716 // and implementation signatures. Currently it is need because the
3717 // signatures have different elements for parameters.
3718 FunctionElement implementation = function.implementation;
3719 FunctionSignature params = implementation.functionSignature;
3720 if (params.optionalParameterCount != 0) {
3721 compiler.internalError(closure,
3722 '"$name" does not handle closure with optional parameters.');
3723 }
3724
3725 registry.registerStaticUse(element);
3726 push(new HForeign(js.js.expressionTemplateYielding(
3727 backend.namer.elementAccess(element)),
3728 backend.dynamicType,
3729 <HInstruction>[]));
3730 return params;
3731 }
3732
3733 void handleForeignDartClosureToJs(ast.Send node, String name) {
3734 // TODO(ahe): This implements DART_CLOSURE_TO_JS and should probably take
3735 // care to wrap the closure in another closure that saves the current
3736 // isolate.
3737 handleForeignRawFunctionRef(node, name);
3738 }
3739
3740 void handleForeignSetCurrentIsolate(ast.Send node) {
3741 if (node.arguments.isEmpty || !node.arguments.tail.isEmpty) {
3742 compiler.internalError(node.argumentsNode,
3743 'Exactly one argument required.');
3744 }
3745 visit(node.arguments.head);
3746 String isolateName = backend.namer.currentIsolate;
3747 SideEffects sideEffects = new SideEffects.empty();
3748 sideEffects.setAllSideEffects();
3749 push(new HForeign(js.js.parseForeignJS("$isolateName = #"),
3750 backend.dynamicType,
3751 <HInstruction>[pop()],
3752 effects: sideEffects));
3753 }
3754
3755 void handleForeignCreateIsolate(ast.Send node) {
3756 if (!node.arguments.isEmpty) {
3757 compiler.internalError(node.argumentsNode, 'Too many arguments.');
3758 }
3759 String constructorName = backend.namer.isolateName;
3760 push(new HForeign(js.js.parseForeignJS("new $constructorName()"),
3761 backend.dynamicType,
3762 <HInstruction>[]));
3763 }
3764
3765 void handleForeignDartObjectJsConstructorFunction(ast.Send node) {
3766 if (!node.arguments.isEmpty) {
3767 compiler.internalError(node.argumentsNode, 'Too many arguments.');
3768 }
3769 push(new HForeign(js.js.expressionTemplateYielding(
3770 backend.namer.elementAccess(compiler.objectClass)),
3771 backend.dynamicType,
3772 <HInstruction>[]));
3773 }
3774
3775 void handleForeignJsCurrentIsolate(ast.Send node) {
3776 if (!node.arguments.isEmpty) {
3777 compiler.internalError(node.argumentsNode, 'Too many arguments.');
3778 }
3779 push(new HForeign(js.js.parseForeignJS(backend.namer.currentIsolate),
3780 backend.dynamicType,
3781 <HInstruction>[]));
3782 }
3783
3784 visitForeignSend(ast.Send node) {
3785 Selector selector = elements.getSelector(node);
3786 String name = selector.name;
3787 if (name == 'JS') {
3788 handleForeignJs(node);
3789 } else if (name == 'JS_CURRENT_ISOLATE_CONTEXT') {
3790 handleForeignJsCurrentIsolateContext(node);
3791 } else if (name == 'JS_CALL_IN_ISOLATE') {
3792 handleForeignJsCallInIsolate(node);
3793 } else if (name == 'DART_CLOSURE_TO_JS') {
3794 handleForeignDartClosureToJs(node, 'DART_CLOSURE_TO_JS');
3795 } else if (name == 'RAW_DART_FUNCTION_REF') {
3796 handleForeignRawFunctionRef(node, 'RAW_DART_FUNCTION_REF');
3797 } else if (name == 'JS_SET_CURRENT_ISOLATE') {
3798 handleForeignSetCurrentIsolate(node);
3799 } else if (name == 'JS_CREATE_ISOLATE') {
3800 handleForeignCreateIsolate(node);
3801 } else if (name == 'JS_OPERATOR_IS_PREFIX') {
3802 stack.add(addConstantString(backend.namer.operatorIsPrefix()));
3803 } else if (name == 'JS_OBJECT_CLASS_NAME') {
3804 String name = backend.namer.getRuntimeTypeName(compiler.objectClass);
3805 stack.add(addConstantString(name));
3806 } else if (name == 'JS_NULL_CLASS_NAME') {
3807 String name = backend.namer.getRuntimeTypeName(compiler.nullClass);
3808 stack.add(addConstantString(name));
3809 } else if (name == 'JS_FUNCTION_CLASS_NAME') {
3810 String name = backend.namer.getRuntimeTypeName(compiler.functionClass);
3811 stack.add(addConstantString(name));
3812 } else if (name == 'JS_OPERATOR_AS_PREFIX') {
3813 stack.add(addConstantString(backend.namer.operatorAsPrefix()));
3814 } else if (name == 'JS_SIGNATURE_NAME') {
3815 stack.add(addConstantString(backend.namer.operatorSignature()));
3816 } else if (name == 'JS_FUNCTION_TYPE_TAG') {
3817 stack.add(addConstantString(backend.namer.functionTypeTag()));
3818 } else if (name == 'JS_FUNCTION_TYPE_VOID_RETURN_TAG') {
3819 stack.add(addConstantString(backend.namer.functionTypeVoidReturnTag()));
3820 } else if (name == 'JS_FUNCTION_TYPE_RETURN_TYPE_TAG') {
3821 stack.add(addConstantString(backend.namer.functionTypeReturnTypeTag()));
3822 } else if (name ==
3823 'JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG') {
3824 stack.add(addConstantString(
3825 backend.namer.functionTypeRequiredParametersTag()));
3826 } else if (name ==
3827 'JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG') {
3828 stack.add(addConstantString(
3829 backend.namer.functionTypeOptionalParametersTag()));
3830 } else if (name ==
3831 'JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG') {
3832 stack.add(addConstantString(
3833 backend.namer.functionTypeNamedParametersTag()));
3834 } else if (name == 'JS_DART_OBJECT_CONSTRUCTOR') {
3835 handleForeignDartObjectJsConstructorFunction(node);
3836 } else if (name == 'JS_IS_INDEXABLE_FIELD_NAME') {
3837 Element element = backend.findHelper('JavaScriptIndexingBehavior');
3838 stack.add(addConstantString(backend.namer.operatorIs(element)));
3839 } else if (name == 'JS_CURRENT_ISOLATE') {
3840 handleForeignJsCurrentIsolate(node);
3841 } else if (name == 'JS_GET_NAME') {
3842 handleForeignJsGetName(node);
3843 } else if (name == 'JS_EMBEDDED_GLOBAL') {
3844 handleForeignJsEmbeddedGlobal(node);
3845 } else if (name == 'JS_GET_FLAG') {
3846 handleForeingJsGetFlag(node);
3847 } else if (name == 'JS_EFFECT') {
3848 stack.add(graph.addConstantNull(compiler));
3849 } else if (name == 'JS_INTERCEPTOR_CONSTANT') {
3850 handleJsInterceptorConstant(node);
3851 } else if (name == 'JS_STRING_CONCAT') {
3852 handleJsStringConcat(node);
3853 } else {
3854 throw "Unknown foreign: ${selector}";
3855 }
3856 }
3857
3858 visitForeignGetter(ast.Send node) {
3859 Element element = elements[node];
3860 // Until now we only handle these as getters.
3861 invariant(node, element.isDeferredLoaderGetter);
3862 FunctionElement deferredLoader = element;
3863 Element loadFunction = compiler.loadLibraryFunction;
3864 PrefixElement prefixElement = deferredLoader.enclosingElement;
3865 String loadId = compiler.deferredLoadTask
3866 .importDeferName[prefixElement.deferredImport];
3867 var inputs = [graph.addConstantString(
3868 new ast.DartString.literal(loadId), compiler)];
3869 push(new HInvokeStatic(loadFunction, inputs, backend.nonNullType,
3870 targetCanThrow: false));
3871 }
3872
3873 generateSuperNoSuchMethodSend(ast.Send node,
3874 Selector selector,
3875 List<HInstruction> arguments) {
3876 String name = selector.name;
3877
3878 ClassElement cls = currentNonClosureClass;
3879 Element element = cls.lookupSuperMember(Compiler.NO_SUCH_METHOD);
3880 if (compiler.enabledInvokeOn
3881 && element.enclosingElement.declaration != compiler.objectClass) {
3882 // Register the call as dynamic if [noSuchMethod] on the super
3883 // class is _not_ the default implementation from [Object], in
3884 // case the [noSuchMethod] implementation calls
3885 // [JSInvocationMirror._invokeOn].
3886 registry.registerSelectorUse(selector.asUntyped);
3887 }
3888 String publicName = name;
3889 if (selector.isSetter) publicName += '=';
3890
3891 ConstantValue nameConstant = constantSystem.createString(
3892 new ast.DartString.literal(publicName));
3893
3894 String internalName = backend.namer.invocationName(selector);
3895 ConstantValue internalNameConstant =
3896 constantSystem.createString(new ast.DartString.literal(internalName));
3897
3898 Element createInvocationMirror = backend.getCreateInvocationMirror();
3899 var argumentsInstruction = buildLiteralList(arguments);
3900 add(argumentsInstruction);
3901
3902 var argumentNames = new List<HInstruction>();
3903 for (String argumentName in selector.namedArguments) {
3904 ConstantValue argumentNameConstant =
3905 constantSystem.createString(new ast.DartString.literal(argumentName));
3906 argumentNames.add(graph.addConstant(argumentNameConstant, compiler));
3907 }
3908 var argumentNamesInstruction = buildLiteralList(argumentNames);
3909 add(argumentNamesInstruction);
3910
3911 ConstantValue kindConstant =
3912 constantSystem.createInt(selector.invocationMirrorKind);
3913
3914 pushInvokeStatic(null,
3915 createInvocationMirror,
3916 [graph.addConstant(nameConstant, compiler),
3917 graph.addConstant(internalNameConstant, compiler),
3918 graph.addConstant(kindConstant, compiler),
3919 argumentsInstruction,
3920 argumentNamesInstruction],
3921 backend.dynamicType);
3922
3923 var inputs = <HInstruction>[pop()];
3924 push(buildInvokeSuper(compiler.noSuchMethodSelector, element, inputs));
3925 }
3926
3927 visitSuperSend(ast.Send node) {
3928 Selector selector = elements.getSelector(node);
3929 Element element = elements[node];
3930 if (Elements.isUnresolved(element)) {
3931 List<HInstruction> arguments = <HInstruction>[];
3932 if (!node.isPropertyAccess) {
3933 addGenericSendArgumentsToList(node.arguments, arguments);
3934 }
3935 return generateSuperNoSuchMethodSend(node, selector, arguments);
3936 }
3937 List<HInstruction> inputs = <HInstruction>[];
3938 if (node.isPropertyAccess) {
3939 push(buildInvokeSuper(selector, element, inputs));
3940 } else if (element.isFunction || element.isGenerativeConstructor) {
3941 if (selector.applies(element, compiler.world)) {
3942 // TODO(5347): Try to avoid the need for calling [implementation] before
3943 // calling [addStaticSendArgumentsToList].
3944 FunctionElement function = element.implementation;
3945 bool succeeded = addStaticSendArgumentsToList(selector, node.arguments,
3946 function, inputs);
3947 assert(succeeded);
3948 push(buildInvokeSuper(selector, element, inputs));
3949 } else if (element.isGenerativeConstructor) {
3950 generateWrongArgumentCountError(node, element, node.arguments);
3951 } else {
3952 addGenericSendArgumentsToList(node.arguments, inputs);
3953 generateSuperNoSuchMethodSend(node, selector, inputs);
3954 }
3955 } else {
3956 HInstruction target = buildInvokeSuper(selector, element, inputs);
3957 add(target);
3958 inputs = <HInstruction>[target];
3959 addDynamicSendArgumentsToList(node, inputs);
3960 Selector closureSelector = new Selector.callClosureFrom(selector);
3961 push(new HInvokeClosure(closureSelector, inputs, backend.dynamicType));
3962 }
3963 }
3964
3965 bool needsSubstitutionForTypeVariableAccess(ClassElement cls) {
3966 ClassWorld classWorld = compiler.world;
3967 if (classWorld.isUsedAsMixin(cls)) return true;
3968
3969 Iterable<ClassElement> subclasses = compiler.world.strictSubclassesOf(cls);
3970 return subclasses.any((ClassElement subclass) {
3971 return !rti.isTrivialSubstitution(subclass, cls);
3972 });
3973 }
3974
3975 /**
3976 * Generate code to extract the type arguments from the object, substitute
3977 * them as an instance of the type we are testing against (if necessary), and
3978 * extract the type argument by the index of the variable in the list of type
3979 * variables for that class.
3980 */
3981 HInstruction readTypeVariable(ClassElement cls,
3982 TypeVariableElement variable) {
3983 assert(sourceElement.isInstanceMember);
3984
3985 HInstruction target = localsHandler.readThis();
3986 HConstant index = graph.addConstantInt(
3987 RuntimeTypes.getTypeVariableIndex(variable),
3988 compiler);
3989
3990 if (needsSubstitutionForTypeVariableAccess(cls)) {
3991 // TODO(ahe): Creating a string here is unfortunate. It is slow (due to
3992 // string concatenation in the implementation), and may prevent
3993 // segmentation of '$'.
3994 String substitutionNameString = backend.namer.getNameForRti(cls);
3995 HInstruction substitutionName = graph.addConstantString(
3996 new ast.LiteralDartString(substitutionNameString), compiler);
3997 pushInvokeStatic(null,
3998 backend.getGetRuntimeTypeArgument(),
3999 [target, substitutionName, index],
4000 backend.dynamicType);
4001 } else {
4002 pushInvokeStatic(null, backend.getGetTypeArgumentByIndex(),
4003 [target, index],
4004 backend.dynamicType);
4005 }
4006 return pop();
4007 }
4008
4009 // TODO(karlklose): this is needed to avoid a bug where the resolved type is
4010 // not stored on a type annotation in the closure translator. Remove when
4011 // fixed.
4012 bool hasDirectLocal(Local local) {
4013 return !localsHandler.isAccessedDirectly(local) ||
4014 localsHandler.directLocals[local] != null;
4015 }
4016
4017 /**
4018 * Helper to create an instruction that gets the value of a type variable.
4019 */
4020 HInstruction addTypeVariableReference(TypeVariableType type) {
4021 assert(assertTypeInContext(type));
4022 Element member = sourceElement;
4023 bool isClosure = member.enclosingElement.isClosure;
4024 if (isClosure) {
4025 ClosureClassElement closureClass = member.enclosingElement;
4026 member = closureClass.methodElement;
4027 member = member.outermostEnclosingMemberOrTopLevel;
4028 }
4029 bool isInConstructorContext = member.isConstructor ||
4030 member.isGenerativeConstructorBody;
4031 Local typeVariableLocal = localsHandler.getTypeVariableAsLocal(type);
4032 if (isClosure) {
4033 if (member.isFactoryConstructor ||
4034 (isInConstructorContext && hasDirectLocal(typeVariableLocal))) {
4035 // The type variable is used from a closure in a factory constructor.
4036 // The value of the type argument is stored as a local on the closure
4037 // itself.
4038 return localsHandler.readLocal(typeVariableLocal);
4039 } else if (member.isFunction ||
4040 member.isGetter ||
4041 member.isSetter ||
4042 isInConstructorContext) {
4043 // The type variable is stored on the "enclosing object" and needs to be
4044 // accessed using the this-reference in the closure.
4045 return readTypeVariable(member.enclosingClass, type.element);
4046 } else {
4047 assert(member.isField);
4048 // The type variable is stored in a parameter of the method.
4049 return localsHandler.readLocal(typeVariableLocal);
4050 }
4051 } else if (isInConstructorContext ||
4052 // When [member] is a field, we can be either
4053 // generating a checked setter or inlining its
4054 // initializer in a constructor. An initializer is
4055 // never built standalone, so [isBuildingFor] will
4056 // always return true when seeing one.
4057 (member.isField && !isBuildingFor(member))) {
4058 // The type variable is stored in a parameter of the method.
4059 return localsHandler.readLocal(typeVariableLocal);
4060 } else if (member.isInstanceMember) {
4061 // The type variable is stored on the object.
4062 return readTypeVariable(member.enclosingClass,
4063 type.element);
4064 } else {
4065 // TODO(ngeoffray): Match the VM behavior and throw an
4066 // exception at runtime.
4067 compiler.internalError(type.element,
4068 'Unimplemented unresolved type variable.');
4069 return null;
4070 }
4071 }
4072
4073 HInstruction analyzeTypeArgument(DartType argument) {
4074 assert(assertTypeInContext(argument));
4075 if (argument.treatAsDynamic) {
4076 // Represent [dynamic] as [null].
4077 return graph.addConstantNull(compiler);
4078 }
4079
4080 if (argument.isTypeVariable) {
4081 return addTypeVariableReference(argument);
4082 }
4083
4084 List<HInstruction> inputs = <HInstruction>[];
4085
4086 String template = rti.getTypeRepresentationWithHashes(argument, (variable) {
4087 inputs.add(addTypeVariableReference(variable));
4088 });
4089
4090 js.Template code = js.js.uncachedExpressionTemplate(template);
4091 HInstruction result = createForeign(code, backend.stringType, inputs);
4092 add(result);
4093 return result;
4094 }
4095
4096 HInstruction handleListConstructor(InterfaceType type,
4097 ast.Node currentNode,
4098 HInstruction newObject) {
4099 if (!backend.classNeedsRti(type.element) || type.treatAsRaw) {
4100 return newObject;
4101 }
4102 List<HInstruction> inputs = <HInstruction>[];
4103 type = localsHandler.substInContext(type);
4104 type.typeArguments.forEach((DartType argument) {
4105 inputs.add(analyzeTypeArgument(argument));
4106 });
4107 // TODO(15489): Register at codegen.
4108 registry.registerInstantiatedType(type);
4109 return callSetRuntimeTypeInfo(type.element, inputs, newObject);
4110 }
4111
4112 void copyRuntimeTypeInfo(HInstruction source, HInstruction target) {
4113 Element copyHelper = backend.getCopyTypeArguments();
4114 pushInvokeStatic(null, copyHelper, [source, target]);
4115 pop();
4116 }
4117
4118 HInstruction callSetRuntimeTypeInfo(ClassElement element,
4119 List<HInstruction> rtiInputs,
4120 HInstruction newObject) {
4121 if (!backend.classNeedsRti(element) || element.typeVariables.isEmpty) {
4122 return newObject;
4123 }
4124
4125 HInstruction typeInfo = buildLiteralList(rtiInputs);
4126 add(typeInfo);
4127
4128 // Set the runtime type information on the object.
4129 Element typeInfoSetterElement = backend.getSetRuntimeTypeInfo();
4130 pushInvokeStatic(
4131 null,
4132 typeInfoSetterElement,
4133 <HInstruction>[newObject, typeInfo],
4134 backend.dynamicType);
4135
4136 // The new object will now be referenced through the
4137 // `setRuntimeTypeInfo` call. We therefore set the type of that
4138 // instruction to be of the object's type.
4139 assert(stack.last is HInvokeStatic || stack.last == newObject);
4140 stack.last.instructionType = newObject.instructionType;
4141 return pop();
4142 }
4143
4144 handleNewSend(ast.NewExpression node) {
4145 ast.Send send = node.send;
4146 generateIsDeferredLoadedCheckIfNeeded(send);
4147
4148 bool isFixedList = false;
4149 bool isFixedListConstructorCall =
4150 Elements.isFixedListConstructorCall(elements[send], send, compiler);
4151 bool isGrowableListConstructorCall =
4152 Elements.isGrowableListConstructorCall(elements[send], send, compiler);
4153
4154 TypeMask computeType(element) {
4155 Element originalElement = elements[send];
4156 if (isFixedListConstructorCall
4157 || Elements.isFilledListConstructorCall(
4158 originalElement, send, compiler)) {
4159 isFixedList = true;
4160 TypeMask inferred =
4161 TypeMaskFactory.inferredForNode(sourceElement, send, compiler);
4162 return inferred.containsAll(compiler.world)
4163 ? backend.fixedArrayType
4164 : inferred;
4165 } else if (isGrowableListConstructorCall) {
4166 TypeMask inferred =
4167 TypeMaskFactory.inferredForNode(sourceElement, send, compiler);
4168 return inferred.containsAll(compiler.world)
4169 ? backend.extendableArrayType
4170 : inferred;
4171 } else if (Elements.isConstructorOfTypedArraySubclass(
4172 originalElement, compiler)) {
4173 isFixedList = true;
4174 TypeMask inferred =
4175 TypeMaskFactory.inferredForNode(sourceElement, send, compiler);
4176 ClassElement cls = element.enclosingClass;
4177 assert(cls.thisType.element.isNative);
4178 return inferred.containsAll(compiler.world)
4179 ? new TypeMask.nonNullExact(cls.thisType.element, compiler.world)
4180 : inferred;
4181 } else if (element.isGenerativeConstructor) {
4182 ClassElement cls = element.enclosingClass;
4183 return new TypeMask.nonNullExact(cls.thisType.element, compiler.world);
4184 } else {
4185 return TypeMaskFactory.inferredReturnTypeForElement(
4186 originalElement, compiler);
4187 }
4188 }
4189
4190 Element constructor = elements[send];
4191 Selector selector = elements.getSelector(send);
4192 ConstructorElement constructorDeclaration = constructor;
4193 ConstructorElement constructorImplementation = constructor.implementation;
4194 constructor = constructorImplementation.effectiveTarget;
4195
4196 final bool isSymbolConstructor =
4197 constructorDeclaration == compiler.symbolConstructor;
4198 final bool isJSArrayTypedConstructor =
4199 constructorDeclaration == backend.jsArrayTypedConstructor;
4200
4201 if (isSymbolConstructor) {
4202 constructor = compiler.symbolValidatedConstructor;
4203 assert(invariant(send, constructor != null,
4204 message: 'Constructor Symbol.validated is missing'));
4205 selector = compiler.symbolValidatedConstructorSelector;
4206 assert(invariant(send, selector != null,
4207 message: 'Constructor Symbol.validated is missing'));
4208 }
4209
4210 bool isRedirected = constructorDeclaration.isRedirectingFactory;
4211 InterfaceType type = elements.getType(node);
4212 InterfaceType expectedType =
4213 constructorDeclaration.computeEffectiveTargetType(type);
4214 expectedType = localsHandler.substInContext(expectedType);
4215
4216 if (checkTypeVariableBounds(node, type)) return;
4217
4218 var inputs = <HInstruction>[];
4219 if (constructor.isGenerativeConstructor &&
4220 Elements.isNativeOrExtendsNative(constructor.enclosingClass)) {
4221 // Native class generative constructors take a pre-constructed object.
4222 inputs.add(graph.addConstantNull(compiler));
4223 }
4224 // TODO(5347): Try to avoid the need for calling [implementation] before
4225 // calling [addStaticSendArgumentsToList].
4226 bool succeeded = addStaticSendArgumentsToList(selector, send.arguments,
4227 constructor.implementation,
4228 inputs);
4229 if (!succeeded) {
4230 generateWrongArgumentCountError(send, constructor, send.arguments);
4231 return;
4232 }
4233
4234 if (constructor.isFactoryConstructor &&
4235 !expectedType.typeArguments.isEmpty) {
4236 registry.registerFactoryWithTypeArguments();
4237 }
4238
4239 TypeMask elementType = computeType(constructor);
4240 if (isFixedListConstructorCall) {
4241 if (!inputs[0].isNumber(compiler)) {
4242 HTypeConversion conversion = new HTypeConversion(
4243 null, HTypeConversion.ARGUMENT_TYPE_CHECK, backend.numType,
4244 inputs[0], null);
4245 add(conversion);
4246 inputs[0] = conversion;
4247 }
4248 js.Template code = js.js.parseForeignJS('Array(#)');
4249 var behavior = new native.NativeBehavior();
4250 behavior.typesReturned.add(expectedType);
4251 // The allocation can throw only if the given length is a double
4252 // or negative.
4253 bool canThrow = true;
4254 if (inputs[0].isInteger(compiler) && inputs[0] is HConstant) {
4255 var constant = inputs[0];
4256 if (constant.constant.primitiveValue >= 0) canThrow = false;
4257 }
4258 HForeign foreign = new HForeign(
4259 code, elementType, inputs, nativeBehavior: behavior,
4260 canThrow: canThrow);
4261 push(foreign);
4262 TypesInferrer inferrer = compiler.typesTask.typesInferrer;
4263 if (inferrer.isFixedArrayCheckedForGrowable(send)) {
4264 js.Template code = js.js.parseForeignJS(r'#.fixed$length = init');
4265 // We set the instruction as [canThrow] to avoid it being dead code.
4266 // We need a finer grained side effect.
4267 add(new HForeign(
4268 code, backend.nullType, [stack.last], canThrow: true));
4269 }
4270 } else if (isGrowableListConstructorCall) {
4271 push(buildLiteralList(<HInstruction>[]));
4272 stack.last.instructionType = elementType;
4273 } else {
4274 ClassElement cls = constructor.enclosingClass;
4275 if (cls.isAbstract && constructor.isGenerativeConstructor) {
4276 generateAbstractClassInstantiationError(send, cls.name);
4277 return;
4278 }
4279 potentiallyAddTypeArguments(inputs, cls, expectedType);
4280
4281 addInlinedInstantiation(expectedType);
4282 pushInvokeStatic(node, constructor, inputs, elementType);
4283 removeInlinedInstantiation(expectedType);
4284 }
4285 HInstruction newInstance = stack.last;
4286 if (isFixedList) {
4287 // Overwrite the element type, in case the allocation site has
4288 // been inlined.
4289 newInstance.instructionType = elementType;
4290 JavaScriptItemCompilationContext context = work.compilationContext;
4291 context.allocatedFixedLists.add(newInstance);
4292 }
4293
4294 // The List constructor forwards to a Dart static method that does
4295 // not know about the type argument. Therefore we special case
4296 // this constructor to have the setRuntimeTypeInfo called where
4297 // the 'new' is done.
4298 if (backend.classNeedsRti(compiler.listClass) &&
4299 (isFixedListConstructorCall || isGrowableListConstructorCall ||
4300 isJSArrayTypedConstructor)) {
4301 newInstance = handleListConstructor(type, send, pop());
4302 stack.add(newInstance);
4303 }
4304
4305 // Finally, if we called a redirecting factory constructor, check the type.
4306 if (isRedirected) {
4307 HInstruction checked = potentiallyCheckType(newInstance, type);
4308 if (checked != newInstance) {
4309 pop();
4310 stack.add(checked);
4311 }
4312 }
4313 }
4314
4315 void potentiallyAddTypeArguments(List<HInstruction> inputs, ClassElement cls,
4316 InterfaceType expectedType) {
4317 if (!backend.classNeedsRti(cls)) return;
4318 assert(expectedType.typeArguments.isEmpty ||
4319 cls.typeVariables.length == expectedType.typeArguments.length);
4320 expectedType.typeArguments.forEach((DartType argument) {
4321 inputs.add(analyzeTypeArgument(argument));
4322 });
4323 }
4324
4325 /// In checked mode checks the [type] of [node] to be well-bounded. The method
4326 /// returns [:true:] if an error can be statically determined.
4327 bool checkTypeVariableBounds(ast.NewExpression node, InterfaceType type) {
4328 if (!compiler.enableTypeAssertions) return false;
4329
4330 Map<DartType, Set<DartType>> seenChecksMap =
4331 new Map<DartType, Set<DartType>>();
4332 bool definitelyFails = false;
4333
4334 addTypeVariableBoundCheck(GenericType instance,
4335 DartType typeArgument,
4336 TypeVariableType typeVariable,
4337 DartType bound) {
4338 if (definitelyFails) return;
4339
4340 int subtypeRelation = compiler.types.computeSubtypeRelation(typeArgument, bound);
4341 if (subtypeRelation == Types.IS_SUBTYPE) return;
4342
4343 String message =
4344 "Can't create an instance of malbounded type '$type': "
4345 "'${typeArgument}' is not a subtype of bound '${bound}' for "
4346 "type variable '${typeVariable}' of type "
4347 "${type == instance
4348 ? "'${type.element.thisType}'"
4349 : "'${instance.element.thisType}' on the supertype "
4350 "'${instance}' of '${type}'"
4351 }.";
4352 if (subtypeRelation == Types.NOT_SUBTYPE) {
4353 generateTypeError(node, message);
4354 definitelyFails = true;
4355 return;
4356 } else if (subtypeRelation == Types.MAYBE_SUBTYPE) {
4357 Set<DartType> seenChecks =
4358 seenChecksMap.putIfAbsent(typeArgument, () => new Set<DartType>());
4359 if (!seenChecks.contains(bound)) {
4360 seenChecks.add(bound);
4361 assertIsSubtype(node, typeArgument, bound, message);
4362 }
4363 }
4364 }
4365
4366 compiler.types.checkTypeVariableBounds(type, addTypeVariableBoundCheck);
4367 if (definitelyFails) {
4368 return true;
4369 }
4370 for (InterfaceType supertype in type.element.allSupertypes) {
4371 DartType instance = type.asInstanceOf(supertype.element);
4372 compiler.types.checkTypeVariableBounds(instance,
4373 addTypeVariableBoundCheck);
4374 if (definitelyFails) {
4375 return true;
4376 }
4377 }
4378 return false;
4379 }
4380
4381 visitAssert(node) {
4382 if (!compiler.enableUserAssertions) {
4383 stack.add(graph.addConstantNull(compiler));
4384 return;
4385 }
4386 // TODO(johnniwinther): Don't handle assert like a regular static call.
4387 // It breaks the selector name check since the assert helper method cannot
4388 // be called `assert` and therefore does not match the selector like a
4389 // regular method.
4390 visitStaticSend(node);
4391 }
4392
4393 visitStaticSend(ast.Send node) {
4394 Selector selector = elements.getSelector(node);
4395 Element element = elements[node];
4396 if (elements.isAssert(node)) {
4397 element = backend.assertMethod;
4398 }
4399 if (element.isForeign(backend) && element.isFunction) {
4400 visitForeignSend(node);
4401 return;
4402 }
4403 if (element.isErroneous) {
4404 // An erroneous element indicates that the funciton could not be resolved
4405 // (a warning has been issued).
4406 generateThrowNoSuchMethod(node,
4407 noSuchMethodTargetSymbolString(element),
4408 argumentNodes: node.arguments);
4409 return;
4410 }
4411 invariant(element, !element.isGenerativeConstructor);
4412 generateIsDeferredLoadedCheckIfNeeded(node);
4413 if (element.isFunction) {
4414 var inputs = <HInstruction>[];
4415 // TODO(5347): Try to avoid the need for calling [implementation] before
4416 // calling [addStaticSendArgumentsToList].
4417 bool succeeded = addStaticSendArgumentsToList(selector, node.arguments,
4418 element.implementation,
4419 inputs);
4420 if (!succeeded) {
4421 generateWrongArgumentCountError(node, element, node.arguments);
4422 return;
4423 }
4424
4425 if (element == compiler.identicalFunction) {
4426 pushWithPosition(
4427 new HIdentity(inputs[0], inputs[1], null, backend.boolType), node);
4428 return;
4429 }
4430
4431 pushInvokeStatic(node, element, inputs);
4432 } else {
4433 generateGetter(node, element);
4434 List<HInstruction> inputs = <HInstruction>[pop()];
4435 addDynamicSendArgumentsToList(node, inputs);
4436 Selector closureSelector = new Selector.callClosureFrom(selector);
4437 pushWithPosition(
4438 new HInvokeClosure(closureSelector, inputs, backend.dynamicType),
4439 node);
4440 }
4441 }
4442
4443 HConstant addConstantString(String string) {
4444 ast.DartString dartString = new ast.DartString.literal(string);
4445 ConstantValue constant = constantSystem.createString(dartString);
4446 return graph.addConstant(constant, compiler);
4447 }
4448
4449 visitTypePrefixSend(ast.Send node) {
4450 compiler.internalError(node, "visitTypePrefixSend should not be called.");
4451 }
4452
4453 visitTypeLiteralSend(ast.Send node) {
4454 DartType type = elements.getTypeLiteralType(node);
4455 if (type.isInterfaceType || type.isTypedef || type.isDynamic) {
4456 // TODO(karlklose): add type representation
4457 if (node.isCall) {
4458 // The node itself is not a constant but we register the selector (the
4459 // identifier that refers to the class/typedef) as a constant.
4460 stack.add(addConstant(node.selector));
4461 } else {
4462 stack.add(addConstant(node));
4463 }
4464 } else if (type.isTypeVariable) {
4465 type = localsHandler.substInContext(type);
4466 HInstruction value = analyzeTypeArgument(type);
4467 pushInvokeStatic(node,
4468 backend.getRuntimeTypeToString(),
4469 [value],
4470 backend.stringType);
4471 pushInvokeStatic(node,
4472 backend.getCreateRuntimeType(),
4473 [pop()]);
4474 } else {
4475 internalError('unexpected type kind ${type.kind}', node: node);
4476 }
4477 if (node.isCall) {
4478 // This send is of the form 'e(...)', where e is resolved to a type
4479 // reference. We create a regular closure call on the result of the type
4480 // reference instead of creating a NoSuchMethodError to avoid pulling it
4481 // in if it is not used (e.g., in a try/catch).
4482 HInstruction target = pop();
4483 Selector selector = elements.getSelector(node);
4484 List<HInstruction> inputs = <HInstruction>[target];
4485 addDynamicSendArgumentsToList(node, inputs);
4486 Selector closureSelector = new Selector.callClosureFrom(selector);
4487 push(new HInvokeClosure(closureSelector, inputs, backend.dynamicType));
4488 }
4489 }
4490
4491 visitGetterSend(ast.Send node) {
4492 generateIsDeferredLoadedCheckIfNeeded(node);
4493 generateGetter(node, elements[node]);
4494 }
4495
4496 // TODO(antonm): migrate rest of SsaFromAstMixin to internalError.
4497 internalError(String reason, {ast.Node node}) {
4498 compiler.internalError(node, reason);
4499 }
4500
4501 void generateError(ast.Node node, String message, Element helper) {
4502 HInstruction errorMessage = addConstantString(message);
4503 pushInvokeStatic(node, helper, [errorMessage]);
4504 }
4505
4506 void generateRuntimeError(ast.Node node, String message) {
4507 generateError(node, message, backend.getThrowRuntimeError());
4508 }
4509
4510 void generateTypeError(ast.Node node, String message) {
4511 generateError(node, message, backend.getThrowTypeError());
4512 }
4513
4514 void generateAbstractClassInstantiationError(ast.Node node, String message) {
4515 generateError(node,
4516 message,
4517 backend.getThrowAbstractClassInstantiationError());
4518 }
4519
4520 void generateThrowNoSuchMethod(ast.Node diagnosticNode,
4521 String methodName,
4522 {Link<ast.Node> argumentNodes,
4523 List<HInstruction> argumentValues,
4524 List<String> existingArguments}) {
4525 Element helper = backend.getThrowNoSuchMethod();
4526 ConstantValue receiverConstant =
4527 constantSystem.createString(new ast.DartString.empty());
4528 HInstruction receiver = graph.addConstant(receiverConstant, compiler);
4529 ast.DartString dartString = new ast.DartString.literal(methodName);
4530 ConstantValue nameConstant = constantSystem.createString(dartString);
4531 HInstruction name = graph.addConstant(nameConstant, compiler);
4532 if (argumentValues == null) {
4533 argumentValues = <HInstruction>[];
4534 argumentNodes.forEach((argumentNode) {
4535 visit(argumentNode);
4536 HInstruction value = pop();
4537 argumentValues.add(value);
4538 });
4539 }
4540 HInstruction arguments = buildLiteralList(argumentValues);
4541 add(arguments);
4542 HInstruction existingNamesList;
4543 if (existingArguments != null) {
4544 List<HInstruction> existingNames = <HInstruction>[];
4545 for (String name in existingArguments) {
4546 HInstruction nameConstant =
4547 graph.addConstantString(new ast.DartString.literal(name), compiler);
4548 existingNames.add(nameConstant);
4549 }
4550 existingNamesList = buildLiteralList(existingNames);
4551 add(existingNamesList);
4552 } else {
4553 existingNamesList = graph.addConstantNull(compiler);
4554 }
4555 pushInvokeStatic(diagnosticNode,
4556 helper,
4557 [receiver, name, arguments, existingNamesList]);
4558 }
4559
4560 /**
4561 * Generate code to throw a [NoSuchMethodError] exception for calling a
4562 * method with a wrong number of arguments or mismatching named optional
4563 * arguments.
4564 */
4565 void generateWrongArgumentCountError(ast.Node diagnosticNode,
4566 FunctionElement function,
4567 Link<ast.Node> argumentNodes) {
4568 List<String> existingArguments = <String>[];
4569 FunctionSignature signature = function.functionSignature;
4570 signature.forEachParameter((Element parameter) {
4571 existingArguments.add(parameter.name);
4572 });
4573 generateThrowNoSuchMethod(diagnosticNode,
4574 function.name,
4575 argumentNodes: argumentNodes,
4576 existingArguments: existingArguments);
4577 }
4578
4579 visitNewExpression(ast.NewExpression node) {
4580 Element element = elements[node.send];
4581 final bool isSymbolConstructor = element == compiler.symbolConstructor;
4582 if (!Elements.isErroneousElement(element)) {
4583 ConstructorElement function = element;
4584 element = function.effectiveTarget;
4585 }
4586 if (Elements.isErroneousElement(element)) {
4587 ErroneousElement error = element;
4588 if (error.messageKind == MessageKind.CANNOT_FIND_CONSTRUCTOR) {
4589 generateThrowNoSuchMethod(
4590 node.send,
4591 noSuchMethodTargetSymbolString(error, 'constructor'),
4592 argumentNodes: node.send.arguments);
4593 } else {
4594 Message message = error.messageKind.message(error.messageArguments);
4595 generateRuntimeError(node.send, message.toString());
4596 }
4597 } else if (node.isConst) {
4598 stack.add(addConstant(node));
4599 if (isSymbolConstructor) {
4600 ConstructedConstantValue symbol = getConstantForNode(node);
4601 StringConstantValue stringConstant = symbol.fields.single;
4602 String nameString = stringConstant.toDartString().slowToString();
4603 registry.registerConstSymbol(nameString);
4604 }
4605 } else {
4606 handleNewSend(node);
4607 }
4608 }
4609
4610 void pushInvokeDynamic(ast.Node node,
4611 Selector selector,
4612 List<HInstruction> arguments,
4613 {ast.Node location}) {
4614 if (location == null) location = node;
4615
4616 // We prefer to not inline certain operations on indexables,
4617 // because the constant folder will handle them better and turn
4618 // them into simpler instructions that allow further
4619 // optimizations.
4620 bool isOptimizableOperationOnIndexable(Selector selector, Element element) {
4621 bool isLength = selector.isGetter
4622 && selector.name == "length";
4623 if (isLength || selector.isIndex) {
4624 TypeMask type = new TypeMask.nonNullExact(
4625 element.enclosingClass.declaration, compiler.world);
4626 return type.satisfies(backend.jsIndexableClass, compiler.world);
4627 } else if (selector.isIndexSet) {
4628 TypeMask type = new TypeMask.nonNullExact(
4629 element.enclosingClass.declaration, compiler.world);
4630 return type.satisfies(backend.jsMutableIndexableClass, compiler.world);
4631 } else {
4632 return false;
4633 }
4634 }
4635
4636 bool isOptimizableOperation(Selector selector, Element element) {
4637 ClassElement cls = element.enclosingClass;
4638 if (isOptimizableOperationOnIndexable(selector, element)) return true;
4639 if (!backend.interceptedClasses.contains(cls)) return false;
4640 if (selector.isOperator) return true;
4641 if (selector.isSetter) return true;
4642 if (selector.isIndex) return true;
4643 if (selector.isIndexSet) return true;
4644 if (element == backend.jsArrayAdd
4645 || element == backend.jsArrayRemoveLast
4646 || element == backend.jsStringSplit) {
4647 return true;
4648 }
4649 return false;
4650 }
4651
4652 Element element = compiler.world.locateSingleElement(selector);
4653 if (element != null
4654 && !element.isField
4655 && !(element.isGetter && selector.isCall)
4656 && !(element.isFunction && selector.isGetter)
4657 && !isOptimizableOperation(selector, element)) {
4658 if (tryInlineMethod(element, selector, arguments, node)) {
4659 return;
4660 }
4661 }
4662
4663 HInstruction receiver = arguments[0];
4664 List<HInstruction> inputs = <HInstruction>[];
4665 bool isIntercepted = backend.isInterceptedSelector(selector);
4666 if (isIntercepted) {
4667 inputs.add(invokeInterceptor(receiver));
4668 }
4669 inputs.addAll(arguments);
4670 TypeMask type = TypeMaskFactory.inferredTypeForSelector(selector, compiler);
4671 if (selector.isGetter) {
4672 pushWithPosition(
4673 new HInvokeDynamicGetter(selector, null, inputs, type),
4674 location);
4675 } else if (selector.isSetter) {
4676 pushWithPosition(
4677 new HInvokeDynamicSetter(selector, null, inputs, type),
4678 location);
4679 } else {
4680 pushWithPosition(
4681 new HInvokeDynamicMethod(selector, inputs, type, isIntercepted),
4682 location);
4683 }
4684 }
4685
4686 void pushInvokeStatic(ast.Node location,
4687 Element element,
4688 List<HInstruction> arguments,
4689 [TypeMask type]) {
4690 if (tryInlineMethod(element, null, arguments, location)) {
4691 return;
4692 }
4693
4694 if (type == null) {
4695 type = TypeMaskFactory.inferredReturnTypeForElement(element, compiler);
4696 }
4697 bool targetCanThrow = !compiler.world.getCannotThrow(element);
4698 // TODO(5346): Try to avoid the need for calling [declaration] before
4699 // creating an [HInvokeStatic].
4700 HInvokeStatic instruction = new HInvokeStatic(
4701 element.declaration, arguments, type, targetCanThrow: targetCanThrow);
4702 if (!currentInlinedInstantiations.isEmpty) {
4703 instruction.instantiatedTypes = new List<DartType>.from(
4704 currentInlinedInstantiations);
4705 }
4706 instruction.sideEffects = compiler.world.getSideEffectsOfElement(element);
4707 if (location == null) {
4708 push(instruction);
4709 } else {
4710 pushWithPosition(instruction, location);
4711 }
4712 }
4713
4714 HInstruction buildInvokeSuper(Selector selector,
4715 Element element,
4716 List<HInstruction> arguments) {
4717 HInstruction receiver = localsHandler.readThis();
4718 // TODO(5346): Try to avoid the need for calling [declaration] before
4719 // creating an [HStatic].
4720 List<HInstruction> inputs = <HInstruction>[];
4721 if (backend.isInterceptedSelector(selector) &&
4722 // Fields don't need an interceptor; consider generating HFieldGet/Set
4723 // instead.
4724 element.kind != ElementKind.FIELD) {
4725 inputs.add(invokeInterceptor(receiver));
4726 }
4727 inputs.add(receiver);
4728 inputs.addAll(arguments);
4729 TypeMask type;
4730 if (!element.isGetter && selector.isGetter) {
4731 type = TypeMaskFactory.inferredTypeForElement(element, compiler);
4732 } else {
4733 type = TypeMaskFactory.inferredReturnTypeForElement(element, compiler);
4734 }
4735 HInstruction instruction = new HInvokeSuper(
4736 element,
4737 currentNonClosureClass,
4738 selector,
4739 inputs,
4740 type,
4741 isSetter: selector.isSetter || selector.isIndexSet);
4742 instruction.sideEffects = compiler.world.getSideEffectsOfSelector(selector);
4743 return instruction;
4744 }
4745
4746 void handleComplexOperatorSend(ast.SendSet node,
4747 HInstruction receiver,
4748 Link<ast.Node> arguments) {
4749 HInstruction rhs;
4750 if (node.isPrefix || node.isPostfix) {
4751 rhs = graph.addConstantInt(1, compiler);
4752 } else {
4753 visit(arguments.head);
4754 assert(arguments.tail.isEmpty);
4755 rhs = pop();
4756 }
4757 visitBinary(receiver, node.assignmentOperator, rhs,
4758 elements.getOperatorSelectorInComplexSendSet(node), node);
4759 }
4760
4761 visitSendSet(ast.SendSet node) {
4762 generateIsDeferredLoadedCheckIfNeeded(node);
4763 Element element = elements[node];
4764 if (!Elements.isUnresolved(element) && element.impliesType) {
4765 ast.Identifier selector = node.selector;
4766 generateThrowNoSuchMethod(node, selector.source,
4767 argumentNodes: node.arguments);
4768 return;
4769 }
4770 ast.Operator op = node.assignmentOperator;
4771 if (node.isSuperCall) {
4772 HInstruction result;
4773 List<HInstruction> setterInputs = <HInstruction>[];
4774 if (identical(node.assignmentOperator.source, '=')) {
4775 addDynamicSendArgumentsToList(node, setterInputs);
4776 result = setterInputs.last;
4777 } else {
4778 Element getter = elements[node.selector];
4779 List<HInstruction> getterInputs = <HInstruction>[];
4780 Link<ast.Node> arguments = node.arguments;
4781 if (node.isIndex) {
4782 // If node is of the from [:super.foo[0] += 2:], the send has
4783 // two arguments: the index and the left hand side. We get
4784 // the index and add it as input of the getter and the
4785 // setter.
4786 visit(arguments.head);
4787 arguments = arguments.tail;
4788 HInstruction index = pop();
4789 getterInputs.add(index);
4790 setterInputs.add(index);
4791 }
4792 HInstruction getterInstruction;
4793 Selector getterSelector =
4794 elements.getGetterSelectorInComplexSendSet(node);
4795 if (Elements.isUnresolved(getter)) {
4796 generateSuperNoSuchMethodSend(
4797 node,
4798 getterSelector,
4799 getterInputs);
4800 getterInstruction = pop();
4801 } else {
4802 getterInstruction = buildInvokeSuper(
4803 getterSelector, getter, getterInputs);
4804 add(getterInstruction);
4805 }
4806 handleComplexOperatorSend(node, getterInstruction, arguments);
4807 setterInputs.add(pop());
4808
4809 if (node.isPostfix) {
4810 result = getterInstruction;
4811 } else {
4812 result = setterInputs.last;
4813 }
4814 }
4815 Selector setterSelector = elements.getSelector(node);
4816 if (Elements.isUnresolved(element)
4817 || !setterSelector.applies(element, compiler.world)) {
4818 generateSuperNoSuchMethodSend(
4819 node, setterSelector, setterInputs);
4820 pop();
4821 } else {
4822 add(buildInvokeSuper(setterSelector, element, setterInputs));
4823 }
4824 stack.add(result);
4825 } else if (node.isIndex) {
4826 if ("=" == op.source) {
4827 visitDynamicSend(node);
4828 } else {
4829 visit(node.receiver);
4830 HInstruction receiver = pop();
4831 Link<ast.Node> arguments = node.arguments;
4832 HInstruction index;
4833 if (node.isIndex) {
4834 visit(arguments.head);
4835 arguments = arguments.tail;
4836 index = pop();
4837 }
4838
4839 pushInvokeDynamic(
4840 node,
4841 elements.getGetterSelectorInComplexSendSet(node),
4842 [receiver, index]);
4843 HInstruction getterInstruction = pop();
4844
4845 handleComplexOperatorSend(node, getterInstruction, arguments);
4846 HInstruction value = pop();
4847
4848 pushInvokeDynamic(
4849 node, elements.getSelector(node), [receiver, index, value]);
4850 pop();
4851
4852 if (node.isPostfix) {
4853 stack.add(getterInstruction);
4854 } else {
4855 stack.add(value);
4856 }
4857 }
4858 } else if ("=" == op.source) {
4859 Link<ast.Node> link = node.arguments;
4860 assert(!link.isEmpty && link.tail.isEmpty);
4861 if (Elements.isInstanceSend(node, elements)) {
4862 HInstruction receiver = generateInstanceSendReceiver(node);
4863 visit(link.head);
4864 generateInstanceSetterWithCompiledReceiver(node, receiver, pop());
4865 } else {
4866 visit(link.head);
4867 generateNonInstanceSetter(node, element, pop());
4868 }
4869 } else if (identical(op.source, "is")) {
4870 compiler.internalError(op, "is-operator as SendSet.");
4871 } else {
4872 assert("++" == op.source || "--" == op.source ||
4873 node.assignmentOperator.source.endsWith("="));
4874
4875 // [receiver] is only used if the node is an instance send.
4876 HInstruction receiver = null;
4877 Element getter = elements[node.selector];
4878
4879 if (!Elements.isUnresolved(getter) && getter.impliesType) {
4880 ast.Identifier selector = node.selector;
4881 generateThrowNoSuchMethod(node, selector.source,
4882 argumentNodes: node.arguments);
4883 return;
4884 } else if (Elements.isInstanceSend(node, elements)) {
4885 receiver = generateInstanceSendReceiver(node);
4886 generateInstanceGetterWithCompiledReceiver(
4887 node, elements.getGetterSelectorInComplexSendSet(node), receiver);
4888 } else {
4889 generateGetter(node, getter);
4890 }
4891 HInstruction getterInstruction = pop();
4892 handleComplexOperatorSend(node, getterInstruction, node.arguments);
4893 HInstruction value = pop();
4894 assert(value != null);
4895 if (Elements.isInstanceSend(node, elements)) {
4896 assert(receiver != null);
4897 generateInstanceSetterWithCompiledReceiver(node, receiver, value);
4898 } else {
4899 assert(receiver == null);
4900 generateNonInstanceSetter(node, element, value);
4901 }
4902 if (node.isPostfix) {
4903 pop();
4904 stack.add(getterInstruction);
4905 }
4906 }
4907 }
4908
4909 void visitLiteralInt(ast.LiteralInt node) {
4910 stack.add(graph.addConstantInt(node.value, compiler));
4911 }
4912
4913 void visitLiteralDouble(ast.LiteralDouble node) {
4914 stack.add(graph.addConstantDouble(node.value, compiler));
4915 }
4916
4917 void visitLiteralBool(ast.LiteralBool node) {
4918 stack.add(graph.addConstantBool(node.value, compiler));
4919 }
4920
4921 void visitLiteralString(ast.LiteralString node) {
4922 stack.add(graph.addConstantString(node.dartString, compiler));
4923 }
4924
4925 void visitLiteralSymbol(ast.LiteralSymbol node) {
4926 stack.add(addConstant(node));
4927 registry.registerConstSymbol(node.slowNameString);
4928 }
4929
4930 void visitStringJuxtaposition(ast.StringJuxtaposition node) {
4931 if (!node.isInterpolation) {
4932 // This is a simple string with no interpolations.
4933 stack.add(graph.addConstantString(node.dartString, compiler));
4934 return;
4935 }
4936 StringBuilderVisitor stringBuilder = new StringBuilderVisitor(this, node);
4937 stringBuilder.visit(node);
4938 stack.add(stringBuilder.result);
4939 }
4940
4941 void visitLiteralNull(ast.LiteralNull node) {
4942 stack.add(graph.addConstantNull(compiler));
4943 }
4944
4945 visitNodeList(ast.NodeList node) {
4946 for (Link<ast.Node> link = node.nodes; !link.isEmpty; link = link.tail) {
4947 if (isAborted()) {
4948 compiler.reportWarning(link.head,
4949 MessageKind.GENERIC, {'text': 'dead code'});
4950 } else {
4951 visit(link.head);
4952 }
4953 }
4954 }
4955
4956 void visitParenthesizedExpression(ast.ParenthesizedExpression node) {
4957 visit(node.expression);
4958 }
4959
4960 visitOperator(ast.Operator node) {
4961 // Operators are intercepted in their surrounding Send nodes.
4962 compiler.internalError(node,
4963 'SsaBuilder.visitOperator should not be called.');
4964 }
4965
4966 visitCascade(ast.Cascade node) {
4967 visit(node.expression);
4968 // Remove the result and reveal the duplicated receiver on the stack.
4969 pop();
4970 }
4971
4972 visitCascadeReceiver(ast.CascadeReceiver node) {
4973 visit(node.expression);
4974 dup();
4975 }
4976
4977 void handleInTryStatement() {
4978 if (!inTryStatement) return;
4979 HBasicBlock block = close(new HExitTry());
4980 HBasicBlock newBlock = graph.addNewBlock();
4981 block.addSuccessor(newBlock);
4982 open(newBlock);
4983 }
4984
4985 visitRethrow(ast.Rethrow node) {
4986 HInstruction exception = rethrowableException;
4987 if (exception == null) {
4988 exception = graph.addConstantNull(compiler);
4989 compiler.internalError(node,
4990 'rethrowableException should not be null.');
4991 }
4992 handleInTryStatement();
4993 closeAndGotoExit(new HThrow(exception, isRethrow: true));
4994 }
4995
4996 visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) {
4997 ConstructorElement targetConstructor =
4998 elements.getRedirectingTargetConstructor(node).implementation;
4999 ConstructorElement redirectingConstructor = sourceElement.implementation;
5000 List<HInstruction> inputs = <HInstruction>[];
5001 FunctionSignature targetSignature = targetConstructor.functionSignature;
5002 FunctionSignature redirectingSignature =
5003 redirectingConstructor.functionSignature;
5004 redirectingSignature.forEachRequiredParameter((ParameterElement element) {
5005 inputs.add(localsHandler.readLocal(element));
5006 });
5007 List<Element> targetOptionals =
5008 targetSignature.orderedOptionalParameters;
5009 List<Element> redirectingOptionals =
5010 redirectingSignature.orderedOptionalParameters;
5011 int i = 0;
5012 for (; i < redirectingOptionals.length; i++) {
5013 ParameterElement parameter = redirectingOptionals[i];
5014 inputs.add(localsHandler.readLocal(parameter));
5015 }
5016 for (; i < targetOptionals.length; i++) {
5017 inputs.add(handleConstantForOptionalParameter(targetOptionals[i]));
5018 }
5019 ClassElement targetClass = targetConstructor.enclosingClass;
5020 if (backend.classNeedsRti(targetClass)) {
5021 ClassElement cls = redirectingConstructor.enclosingClass;
5022 InterfaceType targetType =
5023 redirectingConstructor.computeEffectiveTargetType(cls.thisType);
5024 targetType = localsHandler.substInContext(targetType);
5025 targetType.typeArguments.forEach((DartType argument) {
5026 inputs.add(analyzeTypeArgument(argument));
5027 });
5028 }
5029 pushInvokeStatic(node, targetConstructor, inputs);
5030 HInstruction value = pop();
5031 emitReturn(value, node);
5032 }
5033
5034 visitReturn(ast.Return node) {
5035 if (identical(node.beginToken.stringValue, 'native')) {
5036 native.handleSsaNative(this, node.expression);
5037 return;
5038 }
5039 HInstruction value;
5040 if (node.expression == null) {
5041 value = graph.addConstantNull(compiler);
5042 } else {
5043 visit(node.expression);
5044 value = pop();
5045 value = potentiallyCheckType(value, returnType);
5046 }
5047
5048 handleInTryStatement();
5049 emitReturn(value, node);
5050 }
5051
5052 visitThrow(ast.Throw node) {
5053 visitThrowExpression(node.expression);
5054 if (isReachable) {
5055 handleInTryStatement();
5056 push(new HThrowExpression(pop()));
5057 isReachable = false;
5058 }
5059 }
5060
5061 visitYield(ast.Yield node) {
5062 // Dummy implementation.
5063 visit(node.expression);
5064 pop();
5065 }
5066
5067 visitAwait(ast.Await node) {
5068 // Dummy implementation.
5069 visit(node.expression);
5070 }
5071
5072 visitTypeAnnotation(ast.TypeAnnotation node) {
5073 compiler.internalError(node,
5074 'Visiting type annotation in SSA builder.');
5075 }
5076
5077 visitVariableDefinitions(ast.VariableDefinitions node) {
5078 assert(isReachable);
5079 for (Link<ast.Node> link = node.definitions.nodes;
5080 !link.isEmpty;
5081 link = link.tail) {
5082 ast.Node definition = link.head;
5083 if (definition is ast.Identifier) {
5084 HInstruction initialValue = graph.addConstantNull(compiler);
5085 LocalElement local = elements[definition];
5086 localsHandler.updateLocal(local, initialValue);
5087 } else {
5088 assert(definition is ast.SendSet);
5089 visitSendSet(definition);
5090 pop(); // Discard value.
5091 }
5092 }
5093 }
5094
5095 HInstruction setRtiIfNeeded(HInstruction object, ast.Node node) {
5096 InterfaceType type = localsHandler.substInContext(elements.getType(node));
5097 if (!backend.classNeedsRti(type.element) || type.treatAsRaw) {
5098 return object;
5099 }
5100 List<HInstruction> arguments = <HInstruction>[];
5101 for (DartType argument in type.typeArguments) {
5102 arguments.add(analyzeTypeArgument(argument));
5103 }
5104 // TODO(15489): Register at codegen.
5105 registry.registerInstantiatedType(type);
5106 return callSetRuntimeTypeInfo(type.element, arguments, object);
5107 }
5108
5109 visitLiteralList(ast.LiteralList node) {
5110 HInstruction instruction;
5111
5112 if (node.isConst) {
5113 instruction = addConstant(node);
5114 } else {
5115 List<HInstruction> inputs = <HInstruction>[];
5116 for (Link<ast.Node> link = node.elements.nodes;
5117 !link.isEmpty;
5118 link = link.tail) {
5119 visit(link.head);
5120 inputs.add(pop());
5121 }
5122 instruction = buildLiteralList(inputs);
5123 add(instruction);
5124 instruction = setRtiIfNeeded(instruction, node);
5125 }
5126
5127 TypeMask type =
5128 TypeMaskFactory.inferredForNode(sourceElement, node, compiler);
5129 if (!type.containsAll(compiler.world)) instruction.instructionType = type;
5130 stack.add(instruction);
5131 }
5132
5133 visitConditional(ast.Conditional node) {
5134 SsaBranchBuilder brancher = new SsaBranchBuilder(this, node);
5135 brancher.handleConditional(() => visit(node.condition),
5136 () => visit(node.thenExpression),
5137 () => visit(node.elseExpression));
5138 }
5139
5140 visitStringInterpolation(ast.StringInterpolation node) {
5141 StringBuilderVisitor stringBuilder = new StringBuilderVisitor(this, node);
5142 stringBuilder.visit(node);
5143 stack.add(stringBuilder.result);
5144 }
5145
5146 visitStringInterpolationPart(ast.StringInterpolationPart node) {
5147 // The parts are iterated in visitStringInterpolation.
5148 compiler.internalError(node,
5149 'SsaBuilder.visitStringInterpolation should not be called.');
5150 }
5151
5152 visitEmptyStatement(ast.EmptyStatement node) {
5153 // Do nothing, empty statement.
5154 }
5155
5156 visitModifiers(ast.Modifiers node) {
5157 compiler.unimplemented(node, 'SsaFromAstMixin.visitModifiers.');
5158 }
5159
5160 visitBreakStatement(ast.BreakStatement node) {
5161 assert(!isAborted());
5162 handleInTryStatement();
5163 JumpTarget target = elements.getTargetOf(node);
5164 assert(target != null);
5165 JumpHandler handler = jumpTargets[target];
5166 assert(handler != null);
5167 if (node.target == null) {
5168 handler.generateBreak();
5169 } else {
5170 LabelDefinition label = elements.getTargetLabel(node);
5171 handler.generateBreak(label);
5172 }
5173 }
5174
5175 visitContinueStatement(ast.ContinueStatement node) {
5176 handleInTryStatement();
5177 JumpTarget target = elements.getTargetOf(node);
5178 assert(target != null);
5179 JumpHandler handler = jumpTargets[target];
5180 assert(handler != null);
5181 if (node.target == null) {
5182 handler.generateContinue();
5183 } else {
5184 LabelDefinition label = elements.getTargetLabel(node);
5185 assert(label != null);
5186 handler.generateContinue(label);
5187 }
5188 }
5189
5190 /**
5191 * Creates a [JumpHandler] for a statement. The node must be a jump
5192 * target. If there are no breaks or continues targeting the statement,
5193 * a special "null handler" is returned.
5194 *
5195 * [isLoopJump] is [:true:] when the jump handler is for a loop. This is used
5196 * to distinguish the synthetized loop created for a switch statement with
5197 * continue statements from simple switch statements.
5198 */
5199 JumpHandler createJumpHandler(ast.Statement node, {bool isLoopJump}) {
5200 JumpTarget element = elements.getTargetDefinition(node);
5201 if (element == null || !identical(element.statement, node)) {
5202 // No breaks or continues to this node.
5203 return new NullJumpHandler(compiler);
5204 }
5205 if (isLoopJump && node is ast.SwitchStatement) {
5206 // Create a special jump handler for loops created for switch statements
5207 // with continue statements.
5208 return new SwitchCaseJumpHandler(this, element, node);
5209 }
5210 return new JumpHandler(this, element);
5211 }
5212
5213 visitForIn(ast.ForIn node) {
5214 // Generate a structure equivalent to:
5215 // Iterator<E> $iter = <iterable>.iterator;
5216 // while ($iter.moveNext()) {
5217 // E <declaredIdentifier> = $iter.current;
5218 // <body>
5219 // }
5220
5221 // The iterator is shared between initializer, condition and body.
5222 HInstruction iterator;
5223 void buildInitializer() {
5224 Selector selector = elements.getIteratorSelector(node);
5225 visit(node.expression);
5226 HInstruction receiver = pop();
5227 pushInvokeDynamic(node, selector, [receiver]);
5228 iterator = pop();
5229 }
5230 HInstruction buildCondition() {
5231 Selector selector = elements.getMoveNextSelector(node);
5232 pushInvokeDynamic(node, selector, [iterator]);
5233 return popBoolified();
5234 }
5235 void buildBody() {
5236 Selector call = elements.getCurrentSelector(node);
5237 pushInvokeDynamic(node, call, [iterator]);
5238
5239 ast.Node identifier = node.declaredIdentifier;
5240 Element variable = elements.getForInVariable(node);
5241 Selector selector = elements.getSelector(identifier);
5242
5243 HInstruction value = pop();
5244 if (identifier.asSend() != null
5245 && Elements.isInstanceSend(identifier, elements)) {
5246 HInstruction receiver = generateInstanceSendReceiver(identifier);
5247 assert(receiver != null);
5248 generateInstanceSetterWithCompiledReceiver(
5249 null,
5250 receiver,
5251 value,
5252 selector: selector,
5253 location: identifier);
5254 } else {
5255 generateNonInstanceSetter(null, variable, value, location: identifier);
5256 }
5257 pop(); // Pop the value pushed by the setter call.
5258
5259 visit(node.body);
5260 }
5261 handleLoop(node, buildInitializer, buildCondition, () {}, buildBody);
5262 }
5263
5264 visitLabel(ast.Label node) {
5265 compiler.internalError(node, 'SsaFromAstMixin.visitLabel.');
5266 }
5267
5268 visitLabeledStatement(ast.LabeledStatement node) {
5269 ast.Statement body = node.statement;
5270 if (body is ast.Loop
5271 || body is ast.SwitchStatement
5272 || Elements.isUnusedLabel(node, elements)) {
5273 // Loops and switches handle their own labels.
5274 visit(body);
5275 return;
5276 }
5277 JumpTarget targetElement = elements.getTargetDefinition(body);
5278 LocalsHandler beforeLocals = new LocalsHandler.from(localsHandler);
5279 assert(targetElement.isBreakTarget);
5280 JumpHandler handler = new JumpHandler(this, targetElement);
5281 // Introduce a new basic block.
5282 HBasicBlock entryBlock = openNewBlock();
5283 visit(body);
5284 SubGraph bodyGraph = new SubGraph(entryBlock, lastOpenedBlock);
5285
5286 HBasicBlock joinBlock = graph.addNewBlock();
5287 List<LocalsHandler> breakHandlers = <LocalsHandler>[];
5288 handler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) {
5289 breakInstruction.block.addSuccessor(joinBlock);
5290 breakHandlers.add(locals);
5291 });
5292 bool hasBreak = breakHandlers.length > 0;
5293 if (!isAborted()) {
5294 goto(current, joinBlock);
5295 breakHandlers.add(localsHandler);
5296 }
5297 open(joinBlock);
5298 localsHandler = beforeLocals.mergeMultiple(breakHandlers, joinBlock);
5299
5300 if (hasBreak) {
5301 // There was at least one reachable break, so the label is needed.
5302 entryBlock.setBlockFlow(
5303 new HLabeledBlockInformation(new HSubGraphBlockInformation(bodyGraph),
5304 handler.labels()),
5305 joinBlock);
5306 }
5307 handler.close();
5308 }
5309
5310 visitLiteralMap(ast.LiteralMap node) {
5311 if (node.isConst) {
5312 stack.add(addConstant(node));
5313 return;
5314 }
5315 List<HInstruction> listInputs = <HInstruction>[];
5316 for (Link<ast.Node> link = node.entries.nodes;
5317 !link.isEmpty;
5318 link = link.tail) {
5319 visit(link.head);
5320 listInputs.add(pop());
5321 listInputs.add(pop());
5322 }
5323
5324 ConstructorElement constructor;
5325 List<HInstruction> inputs = <HInstruction>[];
5326
5327 if (listInputs.isEmpty) {
5328 constructor = backend.mapLiteralConstructorEmpty;
5329 } else {
5330 constructor = backend.mapLiteralConstructor;
5331 HLiteralList keyValuePairs = buildLiteralList(listInputs);
5332 add(keyValuePairs);
5333 inputs.add(keyValuePairs);
5334 }
5335
5336 assert(constructor.isFactoryConstructor);
5337
5338 ConstructorElement functionElement = constructor;
5339 constructor = functionElement.effectiveTarget;
5340
5341 InterfaceType type = elements.getType(node);
5342 InterfaceType expectedType =
5343 functionElement.computeEffectiveTargetType(type);
5344 expectedType = localsHandler.substInContext(expectedType);
5345
5346 if (constructor.isFactoryConstructor) {
5347 registry.registerFactoryWithTypeArguments();
5348 }
5349
5350 ClassElement cls = constructor.enclosingClass;
5351
5352 if (backend.classNeedsRti(cls)) {
5353 List<DartType> typeVariable = cls.typeVariables;
5354 expectedType.typeArguments.forEach((DartType argument) {
5355 inputs.add(analyzeTypeArgument(argument));
5356 });
5357 }
5358
5359 // The instruction type will always be a subtype of the mapLiteralClass, but
5360 // type inference might discover a more specific type, or find nothing (in
5361 // dart2js unit tests).
5362 TypeMask mapType =
5363 new TypeMask.nonNullSubtype(backend.mapLiteralClass, compiler.world);
5364 TypeMask returnTypeMask = TypeMaskFactory.inferredReturnTypeForElement(
5365 constructor, compiler);
5366 TypeMask instructionType =
5367 mapType.intersection(returnTypeMask, compiler.world);
5368
5369 addInlinedInstantiation(expectedType);
5370 pushInvokeStatic(node, constructor, inputs, instructionType);
5371 removeInlinedInstantiation(expectedType);
5372 }
5373
5374 visitLiteralMapEntry(ast.LiteralMapEntry node) {
5375 visit(node.value);
5376 visit(node.key);
5377 }
5378
5379 visitNamedArgument(ast.NamedArgument node) {
5380 visit(node.expression);
5381 }
5382
5383 Map<ast.CaseMatch, ConstantValue> buildSwitchCaseConstants(
5384 ast.SwitchStatement node) {
5385
5386 Map<ast.CaseMatch, ConstantValue> constants =
5387 new Map<ast.CaseMatch, ConstantValue>();
5388 for (ast.SwitchCase switchCase in node.cases) {
5389 for (ast.Node labelOrCase in switchCase.labelsAndCases) {
5390 if (labelOrCase is ast.CaseMatch) {
5391 ast.CaseMatch match = labelOrCase;
5392 ConstantValue constant = getConstantForNode(match.expression);
5393 constants[labelOrCase] = constant;
5394 }
5395 }
5396 }
5397 return constants;
5398 }
5399
5400 visitSwitchStatement(ast.SwitchStatement node) {
5401 Map<ast.CaseMatch, ConstantValue> constants =
5402 buildSwitchCaseConstants(node);
5403
5404 // The switch case indices must match those computed in
5405 // [SwitchCaseJumpHandler].
5406 bool hasContinue = false;
5407 Map<ast.SwitchCase, int> caseIndex = new Map<ast.SwitchCase, int>();
5408 int switchIndex = 1;
5409 bool hasDefault = false;
5410 for (ast.SwitchCase switchCase in node.cases) {
5411 for (ast.Node labelOrCase in switchCase.labelsAndCases) {
5412 ast.Node label = labelOrCase.asLabel();
5413 if (label != null) {
5414 LabelDefinition labelElement = elements.getLabelDefinition(label);
5415 if (labelElement != null && labelElement.isContinueTarget) {
5416 hasContinue = true;
5417 }
5418 }
5419 }
5420 if (switchCase.isDefaultCase) {
5421 hasDefault = true;
5422 }
5423 caseIndex[switchCase] = switchIndex;
5424 switchIndex++;
5425 }
5426 if (!hasContinue) {
5427 // If the switch statement has no switch cases targeted by continue
5428 // statements we encode the switch statement directly.
5429 buildSimpleSwitchStatement(node, constants);
5430 } else {
5431 buildComplexSwitchStatement(node, constants, caseIndex, hasDefault);
5432 }
5433 }
5434
5435 /**
5436 * Builds a simple switch statement which does not handle uses of continue
5437 * statements to labeled switch cases.
5438 */
5439 void buildSimpleSwitchStatement(ast.SwitchStatement node,
5440 Map<ast.CaseMatch, ConstantValue> constants) {
5441 JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: false);
5442 HInstruction buildExpression() {
5443 visit(node.expression);
5444 return pop();
5445 }
5446 Iterable<ConstantValue> getConstants(ast.SwitchCase switchCase) {
5447 List<ConstantValue> constantList = <ConstantValue>[];
5448 for (ast.Node labelOrCase in switchCase.labelsAndCases) {
5449 if (labelOrCase is ast.CaseMatch) {
5450 constantList.add(constants[labelOrCase]);
5451 }
5452 }
5453 return constantList;
5454 }
5455 bool isDefaultCase(ast.SwitchCase switchCase) {
5456 return switchCase.isDefaultCase;
5457 }
5458 void buildSwitchCase(ast.SwitchCase node) {
5459 visit(node.statements);
5460 }
5461 handleSwitch(node,
5462 jumpHandler,
5463 buildExpression,
5464 node.cases,
5465 getConstants,
5466 isDefaultCase,
5467 buildSwitchCase);
5468 jumpHandler.close();
5469 }
5470
5471 /**
5472 * Builds a switch statement that can handle arbitrary uses of continue
5473 * statements to labeled switch cases.
5474 */
5475 void buildComplexSwitchStatement(ast.SwitchStatement node,
5476 Map<ast.CaseMatch, ConstantValue> constants,
5477 Map<ast.SwitchCase, int> caseIndex,
5478 bool hasDefault) {
5479 // If the switch statement has switch cases targeted by continue
5480 // statements we create the following encoding:
5481 //
5482 // switch (e) {
5483 // l_1: case e0: s_1; break;
5484 // l_2: case e1: s_2; continue l_i;
5485 // ...
5486 // l_n: default: s_n; continue l_j;
5487 // }
5488 //
5489 // is encoded as
5490 //
5491 // var target;
5492 // switch (e) {
5493 // case e1: target = 1; break;
5494 // case e2: target = 2; break;
5495 // ...
5496 // default: target = n; break;
5497 // }
5498 // l: while (true) {
5499 // switch (target) {
5500 // case 1: s_1; break l;
5501 // case 2: s_2; target = i; continue l;
5502 // ...
5503 // case n: s_n; target = j; continue l;
5504 // }
5505 // }
5506
5507 JumpTarget switchTarget = elements.getTargetDefinition(node);
5508 HInstruction initialValue = graph.addConstantNull(compiler);
5509 localsHandler.updateLocal(switchTarget, initialValue);
5510
5511 JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: false);
5512 var switchCases = node.cases;
5513 if (!hasDefault) {
5514 // Use [:null:] as the marker for a synthetic default clause.
5515 // The synthetic default is added because otherwise, there would be no
5516 // good place to give a default value to the local.
5517 switchCases = node.cases.nodes.toList()..add(null);
5518 }
5519 HInstruction buildExpression() {
5520 visit(node.expression);
5521 return pop();
5522 }
5523 Iterable<ConstantValue> getConstants(ast.SwitchCase switchCase) {
5524 List<ConstantValue> constantList = <ConstantValue>[];
5525 if (switchCase != null) {
5526 for (ast.Node labelOrCase in switchCase.labelsAndCases) {
5527 if (labelOrCase is ast.CaseMatch) {
5528 constantList.add(constants[labelOrCase]);
5529 }
5530 }
5531 }
5532 return constantList;
5533 }
5534 bool isDefaultCase(ast.SwitchCase switchCase) {
5535 return switchCase == null || switchCase.isDefaultCase;
5536 }
5537 void buildSwitchCase(ast.SwitchCase switchCase) {
5538 if (switchCase != null) {
5539 // Generate 'target = i; break;' for switch case i.
5540 int index = caseIndex[switchCase];
5541 HInstruction value = graph.addConstantInt(index, compiler);
5542 localsHandler.updateLocal(switchTarget, value);
5543 } else {
5544 // Generate synthetic default case 'target = null; break;'.
5545 HInstruction value = graph.addConstantNull(compiler);
5546 localsHandler.updateLocal(switchTarget, value);
5547 }
5548 jumpTargets[switchTarget].generateBreak();
5549 }
5550 handleSwitch(node,
5551 jumpHandler,
5552 buildExpression,
5553 switchCases,
5554 getConstants,
5555 isDefaultCase,
5556 buildSwitchCase);
5557 jumpHandler.close();
5558
5559 HInstruction buildCondition() =>
5560 graph.addConstantBool(true, compiler);
5561
5562 void buildSwitch() {
5563 HInstruction buildExpression() {
5564 return localsHandler.readLocal(switchTarget);
5565 }
5566 Iterable<ConstantValue> getConstants(ast.SwitchCase switchCase) {
5567 return <ConstantValue>[constantSystem.createInt(caseIndex[switchCase])];
5568 }
5569 void buildSwitchCase(ast.SwitchCase switchCase) {
5570 visit(switchCase.statements);
5571 if (!isAborted()) {
5572 // Ensure that we break the loop if the case falls through. (This
5573 // is only possible for the last case.)
5574 jumpTargets[switchTarget].generateBreak();
5575 }
5576 }
5577 // Pass a [NullJumpHandler] because the target for the contained break
5578 // is not the generated switch statement but instead the loop generated
5579 // in the call to [handleLoop] below.
5580 handleSwitch(node,
5581 new NullJumpHandler(compiler),
5582 buildExpression, node.cases, getConstants,
5583 (_) => false, // No case is default.
5584 buildSwitchCase);
5585 }
5586
5587 void buildLoop() {
5588 handleLoop(node,
5589 () {},
5590 buildCondition,
5591 () {},
5592 buildSwitch);
5593 }
5594
5595 if (hasDefault) {
5596 buildLoop();
5597 } else {
5598 // If the switch statement has no default case, surround the loop with
5599 // a test of the target.
5600 void buildCondition() {
5601 js.Template code = js.js.parseForeignJS('#');
5602 push(createForeign(code,
5603 backend.boolType,
5604 [localsHandler.readLocal(switchTarget)]));
5605 }
5606 handleIf(node, buildCondition, buildLoop, () => {});
5607 }
5608 }
5609
5610 /**
5611 * Creates a switch statement.
5612 *
5613 * [jumpHandler] is the [JumpHandler] for the created switch statement.
5614 * [buildExpression] creates the switch expression.
5615 * [switchCases] must be either an [Iterable] of [ast.SwitchCase] nodes or
5616 * a [Link] or a [ast.NodeList] of [ast.SwitchCase] nodes.
5617 * [getConstants] returns the set of constants for a switch case.
5618 * [isDefaultCase] returns [:true:] if the provided switch case should be
5619 * considered default for the created switch statement.
5620 * [buildSwitchCase] creates the statements for the switch case.
5621 */
5622 void handleSwitch(
5623 ast.Node errorNode,
5624 JumpHandler jumpHandler,
5625 HInstruction buildExpression(),
5626 var switchCases,
5627 Iterable<ConstantValue> getConstants(ast.SwitchCase switchCase),
5628 bool isDefaultCase(ast.SwitchCase switchCase),
5629 void buildSwitchCase(ast.SwitchCase switchCase)) {
5630
5631 Map<ast.CaseMatch, ConstantValue> constants =
5632 new Map<ast.CaseMatch, ConstantValue>();
5633
5634 HBasicBlock expressionStart = openNewBlock();
5635 HInstruction expression = buildExpression();
5636 if (switchCases.isEmpty) {
5637 return;
5638 }
5639
5640 HSwitch switchInstruction = new HSwitch(<HInstruction>[expression]);
5641 HBasicBlock expressionEnd = close(switchInstruction);
5642 LocalsHandler savedLocals = localsHandler;
5643
5644 List<HStatementInformation> statements = <HStatementInformation>[];
5645 bool hasDefault = false;
5646 Element getFallThroughErrorElement = backend.getFallThroughError();
5647 HasNextIterator<ast.Node> caseIterator =
5648 new HasNextIterator<ast.Node>(switchCases.iterator);
5649 while (caseIterator.hasNext) {
5650 ast.SwitchCase switchCase = caseIterator.next();
5651 HBasicBlock block = graph.addNewBlock();
5652 for (ConstantValue constant in getConstants(switchCase)) {
5653 HConstant hConstant = graph.addConstant(constant, compiler);
5654 switchInstruction.inputs.add(hConstant);
5655 hConstant.usedBy.add(switchInstruction);
5656 expressionEnd.addSuccessor(block);
5657 }
5658
5659 if (isDefaultCase(switchCase)) {
5660 // An HSwitch has n inputs and n+1 successors, the last being the
5661 // default case.
5662 expressionEnd.addSuccessor(block);
5663 hasDefault = true;
5664 }
5665 open(block);
5666 localsHandler = new LocalsHandler.from(savedLocals);
5667 buildSwitchCase(switchCase);
5668 if (!isAborted()) {
5669 if (caseIterator.hasNext) {
5670 pushInvokeStatic(switchCase, getFallThroughErrorElement, []);
5671 HInstruction error = pop();
5672 closeAndGotoExit(new HThrow(error));
5673 } else if (!isDefaultCase(switchCase)) {
5674 // If there is no default, we will add one later to avoid
5675 // the critical edge. So we generate a break statement to make
5676 // sure the last case does not fall through to the default case.
5677 jumpHandler.generateBreak();
5678 }
5679 }
5680 statements.add(
5681 new HSubGraphBlockInformation(new SubGraph(block, lastOpenedBlock)));
5682 }
5683
5684 // Add a join-block if necessary.
5685 // We create [joinBlock] early, and then go through the cases that might
5686 // want to jump to it. In each case, if we add [joinBlock] as a successor
5687 // of another block, we also add an element to [caseHandlers] that is used
5688 // to create the phis in [joinBlock].
5689 // If we never jump to the join block, [caseHandlers] will stay empty, and
5690 // the join block is never added to the graph.
5691 HBasicBlock joinBlock = new HBasicBlock();
5692 List<LocalsHandler> caseHandlers = <LocalsHandler>[];
5693 jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) {
5694 instruction.block.addSuccessor(joinBlock);
5695 caseHandlers.add(locals);
5696 });
5697 jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) {
5698 assert(invariant(errorNode, false,
5699 message: 'Continue cannot target a switch.'));
5700 });
5701 if (!isAborted()) {
5702 current.close(new HGoto());
5703 lastOpenedBlock.addSuccessor(joinBlock);
5704 caseHandlers.add(localsHandler);
5705 }
5706 if (!hasDefault) {
5707 // Always create a default case, to avoid a critical edge in the
5708 // graph.
5709 HBasicBlock defaultCase = addNewBlock();
5710 expressionEnd.addSuccessor(defaultCase);
5711 open(defaultCase);
5712 close(new HGoto());
5713 defaultCase.addSuccessor(joinBlock);
5714 caseHandlers.add(savedLocals);
5715 statements.add(new HSubGraphBlockInformation(new SubGraph(
5716 defaultCase, defaultCase)));
5717 }
5718 assert(caseHandlers.length == joinBlock.predecessors.length);
5719 if (caseHandlers.length != 0) {
5720 graph.addBlock(joinBlock);
5721 open(joinBlock);
5722 if (caseHandlers.length == 1) {
5723 localsHandler = caseHandlers[0];
5724 } else {
5725 localsHandler = savedLocals.mergeMultiple(caseHandlers, joinBlock);
5726 }
5727 } else {
5728 // The joinblock is not used.
5729 joinBlock = null;
5730 }
5731
5732 HSubExpressionBlockInformation expressionInfo =
5733 new HSubExpressionBlockInformation(new SubExpression(expressionStart,
5734 expressionEnd));
5735 expressionStart.setBlockFlow(
5736 new HSwitchBlockInformation(expressionInfo,
5737 statements,
5738 jumpHandler.target,
5739 jumpHandler.labels()),
5740 joinBlock);
5741
5742 jumpHandler.close();
5743 }
5744
5745 visitSwitchCase(ast.SwitchCase node) {
5746 compiler.internalError(node, 'SsaFromAstMixin.visitSwitchCase.');
5747 }
5748
5749 visitCaseMatch(ast.CaseMatch node) {
5750 compiler.internalError(node, 'SsaFromAstMixin.visitCaseMatch.');
5751 }
5752
5753 visitTryStatement(ast.TryStatement node) {
5754 // Save the current locals. The catch block and the finally block
5755 // must not reuse the existing locals handler. None of the variables
5756 // that have been defined in the body-block will be used, but for
5757 // loops we will add (unnecessary) phis that will reference the body
5758 // variables. This makes it look as if the variables were used
5759 // in a non-dominated block.
5760 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
5761 HBasicBlock enterBlock = openNewBlock();
5762 HTry tryInstruction = new HTry();
5763 close(tryInstruction);
5764 bool oldInTryStatement = inTryStatement;
5765 inTryStatement = true;
5766
5767 HBasicBlock startTryBlock;
5768 HBasicBlock endTryBlock;
5769 HBasicBlock startCatchBlock;
5770 HBasicBlock endCatchBlock;
5771 HBasicBlock startFinallyBlock;
5772 HBasicBlock endFinallyBlock;
5773
5774 startTryBlock = graph.addNewBlock();
5775 open(startTryBlock);
5776 visit(node.tryBlock);
5777 // We use a [HExitTry] instead of a [HGoto] for the try block
5778 // because it will have multiple successors: the join block, and
5779 // the catch or finally block.
5780 if (!isAborted()) endTryBlock = close(new HExitTry());
5781 SubGraph bodyGraph = new SubGraph(startTryBlock, lastOpenedBlock);
5782 SubGraph catchGraph = null;
5783 HLocalValue exception = null;
5784
5785 if (!node.catchBlocks.isEmpty) {
5786 localsHandler = new LocalsHandler.from(savedLocals);
5787 startCatchBlock = graph.addNewBlock();
5788 open(startCatchBlock);
5789 // Note that the name of this local is irrelevant.
5790 SyntheticLocal local =
5791 new SyntheticLocal('exception', localsHandler.executableContext);
5792 exception = new HLocalValue(local, backend.nonNullType);
5793 add(exception);
5794 HInstruction oldRethrowableException = rethrowableException;
5795 rethrowableException = exception;
5796
5797 pushInvokeStatic(node, backend.getExceptionUnwrapper(), [exception]);
5798 HInvokeStatic unwrappedException = pop();
5799 tryInstruction.exception = exception;
5800 Link<ast.Node> link = node.catchBlocks.nodes;
5801
5802 void pushCondition(ast.CatchBlock catchBlock) {
5803 if (catchBlock.onKeyword != null) {
5804 DartType type = elements.getType(catchBlock.type);
5805 if (type == null) {
5806 compiler.internalError(catchBlock.type, 'On with no type.');
5807 }
5808 HInstruction condition =
5809 buildIsNode(catchBlock.type, type, unwrappedException);
5810 push(condition);
5811 } else {
5812 ast.VariableDefinitions declaration = catchBlock.formals.nodes.head;
5813 HInstruction condition = null;
5814 if (declaration.type == null) {
5815 condition = graph.addConstantBool(true, compiler);
5816 stack.add(condition);
5817 } else {
5818 // TODO(aprelev@gmail.com): Once old catch syntax is removed
5819 // "if" condition above and this "else" branch should be deleted as
5820 // type of declared variable won't matter for the catch
5821 // condition.
5822 DartType type = elements.getType(declaration.type);
5823 if (type == null) {
5824 compiler.internalError(catchBlock, 'Catch with unresolved type.');
5825 }
5826 condition = buildIsNode(declaration.type, type, unwrappedException);
5827 push(condition);
5828 }
5829 }
5830 }
5831
5832 void visitThen() {
5833 ast.CatchBlock catchBlock = link.head;
5834 link = link.tail;
5835 if (catchBlock.exception != null) {
5836 LocalVariableElement exceptionVariable =
5837 elements[catchBlock.exception];
5838 localsHandler.updateLocal(exceptionVariable,
5839 unwrappedException);
5840 }
5841 ast.Node trace = catchBlock.trace;
5842 if (trace != null) {
5843 pushInvokeStatic(trace, backend.getTraceFromException(), [exception]);
5844 HInstruction traceInstruction = pop();
5845 LocalVariableElement traceVariable = elements[trace];
5846 localsHandler.updateLocal(traceVariable, traceInstruction);
5847 }
5848 visit(catchBlock);
5849 }
5850
5851 void visitElse() {
5852 if (link.isEmpty) {
5853 closeAndGotoExit(new HThrow(exception, isRethrow: true));
5854 } else {
5855 ast.CatchBlock newBlock = link.head;
5856 handleIf(node,
5857 () { pushCondition(newBlock); },
5858 visitThen, visitElse);
5859 }
5860 }
5861
5862 ast.CatchBlock firstBlock = link.head;
5863 handleIf(node, () { pushCondition(firstBlock); }, visitThen, visitElse);
5864 if (!isAborted()) endCatchBlock = close(new HGoto());
5865
5866 rethrowableException = oldRethrowableException;
5867 tryInstruction.catchBlock = startCatchBlock;
5868 catchGraph = new SubGraph(startCatchBlock, lastOpenedBlock);
5869 }
5870
5871 SubGraph finallyGraph = null;
5872 if (node.finallyBlock != null) {
5873 localsHandler = new LocalsHandler.from(savedLocals);
5874 startFinallyBlock = graph.addNewBlock();
5875 open(startFinallyBlock);
5876 visit(node.finallyBlock);
5877 if (!isAborted()) endFinallyBlock = close(new HGoto());
5878 tryInstruction.finallyBlock = startFinallyBlock;
5879 finallyGraph = new SubGraph(startFinallyBlock, lastOpenedBlock);
5880 }
5881
5882 HBasicBlock exitBlock = graph.addNewBlock();
5883
5884 addOptionalSuccessor(b1, b2) { if (b2 != null) b1.addSuccessor(b2); }
5885 addExitTrySuccessor(successor) {
5886 if (successor == null) return;
5887 // Iterate over all blocks created inside this try/catch, and
5888 // attach successor information to blocks that end with
5889 // [HExitTry].
5890 for (int i = startTryBlock.id; i < successor.id; i++) {
5891 HBasicBlock block = graph.blocks[i];
5892 var last = block.last;
5893 if (last is HExitTry) {
5894 block.addSuccessor(successor);
5895 }
5896 }
5897 }
5898
5899 // Setup all successors. The entry block that contains the [HTry]
5900 // has 1) the body, 2) the catch, 3) the finally, and 4) the exit
5901 // blocks as successors.
5902 enterBlock.addSuccessor(startTryBlock);
5903 addOptionalSuccessor(enterBlock, startCatchBlock);
5904 addOptionalSuccessor(enterBlock, startFinallyBlock);
5905 enterBlock.addSuccessor(exitBlock);
5906
5907 // The body has either the catch or the finally block as successor.
5908 if (endTryBlock != null) {
5909 assert(startCatchBlock != null || startFinallyBlock != null);
5910 endTryBlock.addSuccessor(
5911 startCatchBlock != null ? startCatchBlock : startFinallyBlock);
5912 endTryBlock.addSuccessor(exitBlock);
5913 }
5914
5915 // The catch block has either the finally or the exit block as
5916 // successor.
5917 if (endCatchBlock != null) {
5918 endCatchBlock.addSuccessor(
5919 startFinallyBlock != null ? startFinallyBlock : exitBlock);
5920 }
5921
5922 // The finally block has the exit block as successor.
5923 if (endFinallyBlock != null) {
5924 endFinallyBlock.addSuccessor(exitBlock);
5925 }
5926
5927 // If a block inside try/catch aborts (eg with a return statement),
5928 // we explicitely mark this block a predecessor of the catch
5929 // block and the finally block.
5930 addExitTrySuccessor(startCatchBlock);
5931 addExitTrySuccessor(startFinallyBlock);
5932
5933 // Use the locals handler not altered by the catch and finally
5934 // blocks.
5935 localsHandler = savedLocals;
5936 open(exitBlock);
5937 enterBlock.setBlockFlow(
5938 new HTryBlockInformation(
5939 wrapStatementGraph(bodyGraph),
5940 exception,
5941 wrapStatementGraph(catchGraph),
5942 wrapStatementGraph(finallyGraph)),
5943 exitBlock);
5944 inTryStatement = oldInTryStatement;
5945 }
5946
5947 visitCatchBlock(ast.CatchBlock node) {
5948 visit(node.block);
5949 }
5950
5951 visitTypedef(ast.Typedef node) {
5952 compiler.unimplemented(node, 'SsaFromAstMixin.visitTypedef.');
5953 }
5954
5955 visitTypeVariable(ast.TypeVariable node) {
5956 compiler.internalError(node, 'SsaFromAstMixin.visitTypeVariable.');
5957 }
5958
5959 /**
5960 * This method is invoked before inlining the body of [function] into this
5961 * [SsaBuilder].
5962 */
5963 void enterInlinedMethod(FunctionElement function,
5964 ast.Node _,
5965 List<HInstruction> compiledArguments) {
5966 TypesInferrer inferrer = compiler.typesTask.typesInferrer;
5967 AstInliningState state = new AstInliningState(
5968 function, returnLocal, returnType, elements, stack, localsHandler,
5969 inTryStatement,
5970 allInlinedFunctionsCalledOnce && inferrer.isCalledOnce(function));
5971 inliningStack.add(state);
5972
5973 // Setting up the state of the (AST) builder is performed even when the
5974 // inlined function is in IR, because the irInliner uses the [returnElement]
5975 // of the AST builder.
5976 setupStateForInlining(function, compiledArguments);
5977 }
5978
5979 void leaveInlinedMethod() {
5980 HInstruction result = localsHandler.readLocal(returnLocal);
5981 AstInliningState state = inliningStack.removeLast();
5982 restoreState(state);
5983 stack.add(result);
5984 }
5985
5986 void doInline(FunctionElement function) {
5987 visitInlinedFunction(function);
5988 }
5989
5990 void emitReturn(HInstruction value, ast.Node node) {
5991 if (inliningStack.isEmpty) {
5992 closeAndGotoExit(attachPosition(new HReturn(value), node));
5993 } else {
5994 localsHandler.updateLocal(returnLocal, value);
5995 }
5996 }
5997 }
5998
5999 /**
6000 * Visitor that handles generation of string literals (LiteralString,
6001 * StringInterpolation), and otherwise delegates to the given visitor for
6002 * non-literal subexpressions.
6003 */
6004 class StringBuilderVisitor extends ast.Visitor {
6005 final SsaBuilder builder;
6006 final ast.Node diagnosticNode;
6007
6008 /**
6009 * The string value generated so far.
6010 */
6011 HInstruction result = null;
6012
6013 StringBuilderVisitor(this.builder, this.diagnosticNode);
6014
6015 Compiler get compiler => builder.compiler;
6016
6017 void visit(ast.Node node) {
6018 node.accept(this);
6019 }
6020
6021 visitNode(ast.Node node) {
6022 builder.compiler.internalError(node, 'Unexpected node.');
6023 }
6024
6025 void visitExpression(ast.Node node) {
6026 node.accept(builder);
6027 HInstruction expression = builder.pop();
6028
6029 // We want to use HStringify when:
6030 // 1. The value is known to be a primitive type, because it might get
6031 // constant-folded and codegen has some tricks with JavaScript
6032 // conversions.
6033 // 2. The value can be primitive, because the library stringifier has
6034 // fast-path code for most primitives.
6035 if (expression.canBePrimitive(compiler)) {
6036 append(stringify(node, expression));
6037 return;
6038 }
6039
6040 // If the `toString` method is guaranteed to return a string we can call it
6041 // directly.
6042 Selector selector =
6043 new TypedSelector(expression.instructionType,
6044 new Selector.call('toString', null, 0), compiler.world);
6045 TypeMask type = TypeMaskFactory.inferredTypeForSelector(selector, compiler);
6046 if (type.containsOnlyString(compiler.world)) {
6047 builder.pushInvokeDynamic(node, selector, <HInstruction>[expression]);
6048 append(builder.pop());
6049 return;
6050 }
6051
6052 append(stringify(node, expression));
6053 }
6054
6055 void visitStringInterpolation(ast.StringInterpolation node) {
6056 node.visitChildren(this);
6057 }
6058
6059 void visitStringInterpolationPart(ast.StringInterpolationPart node) {
6060 visit(node.expression);
6061 visit(node.string);
6062 }
6063
6064 void visitStringJuxtaposition(ast.StringJuxtaposition node) {
6065 node.visitChildren(this);
6066 }
6067
6068 void visitNodeList(ast.NodeList node) {
6069 node.visitChildren(this);
6070 }
6071
6072 void append(HInstruction expression) {
6073 result = (result == null) ? expression : concat(result, expression);
6074 }
6075
6076 HInstruction concat(HInstruction left, HInstruction right) {
6077 HInstruction instruction = new HStringConcat(
6078 left, right, diagnosticNode, builder.backend.stringType);
6079 builder.add(instruction);
6080 return instruction;
6081 }
6082
6083 HInstruction stringify(ast.Node node, HInstruction expression) {
6084 HInstruction instruction =
6085 new HStringify(expression, node, builder.backend.stringType);
6086 builder.add(instruction);
6087 return instruction;
6088 }
6089 }
6090
6091 /**
6092 * This class visits the method that is a candidate for inlining and
6093 * finds whether it is too difficult to inline.
6094 */
6095 // TODO(karlklose): refactor to make it possible to distinguish between
6096 // implementation restrictions (for example, we *can't* inline multiple returns)
6097 // and heuristics (we *shouldn't* inline large functions).
6098 class InlineWeeder extends ast.Visitor {
6099 // Invariant: *INSIDE_LOOP* > *OUTSIDE_LOOP*
6100 static const INLINING_NODES_OUTSIDE_LOOP = 18;
6101 static const INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR = 3;
6102 static const INLINING_NODES_INSIDE_LOOP = 42;
6103 static const INLINING_NODES_INSIDE_LOOP_ARG_FACTOR = 4;
6104
6105 bool seenReturn = false;
6106 bool tooDifficult = false;
6107 int nodeCount = 0;
6108 final int maxInliningNodes;
6109 final bool useMaxInliningNodes;
6110 final bool allowLoops;
6111
6112 InlineWeeder(this.maxInliningNodes,
6113 this.useMaxInliningNodes,
6114 this.allowLoops);
6115
6116 static bool canBeInlined(ast.FunctionExpression functionExpression,
6117 int maxInliningNodes,
6118 bool useMaxInliningNodes,
6119 {bool allowLoops: false}) {
6120 InlineWeeder weeder =
6121 new InlineWeeder(maxInliningNodes, useMaxInliningNodes, allowLoops);
6122 weeder.visit(functionExpression.initializers);
6123 weeder.visit(functionExpression.body);
6124 return !weeder.tooDifficult;
6125 }
6126
6127 bool registerNode() {
6128 if (!useMaxInliningNodes) return true;
6129 if (nodeCount++ > maxInliningNodes) {
6130 tooDifficult = true;
6131 return false;
6132 } else {
6133 return true;
6134 }
6135 }
6136
6137 void visit(ast.Node node) {
6138 if (node != null) node.accept(this);
6139 }
6140
6141 void visitNode(ast.Node node) {
6142 if (!registerNode()) return;
6143 if (seenReturn) {
6144 tooDifficult = true;
6145 } else {
6146 node.visitChildren(this);
6147 }
6148 }
6149
6150 void visitFunctionExpression(ast.Node node) {
6151 if (!registerNode()) return;
6152 tooDifficult = true;
6153 }
6154
6155 void visitFunctionDeclaration(ast.Node node) {
6156 if (!registerNode()) return;
6157 tooDifficult = true;
6158 }
6159
6160 void visitSend(ast.Send node) {
6161 if (!registerNode()) return;
6162 node.visitChildren(this);
6163 }
6164
6165 visitLoop(ast.Node node) {
6166 // It's actually not difficult to inline a method with a loop, but
6167 // our measurements show that it's currently better to not inline a
6168 // method that contains a loop.
6169 if (!allowLoops) tooDifficult = true;
6170 }
6171
6172 void visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) {
6173 if (!registerNode()) return;
6174 tooDifficult = true;
6175 }
6176
6177 void visitRethrow(ast.Rethrow node) {
6178 if (!registerNode()) return;
6179 tooDifficult = true;
6180 }
6181
6182 void visitReturn(ast.Return node) {
6183 if (!registerNode()) return;
6184 if (seenReturn
6185 || identical(node.beginToken.stringValue, 'native')) {
6186 tooDifficult = true;
6187 return;
6188 }
6189 node.visitChildren(this);
6190 seenReturn = true;
6191 }
6192
6193 void visitTryStatement(ast.Node node) {
6194 if (!registerNode()) return;
6195 tooDifficult = true;
6196 }
6197
6198 void visitThrow(ast.Throw node) {
6199 if (!registerNode()) return;
6200 // For now, we don't want to handle throw after a return even if
6201 // it is in an "if".
6202 if (seenReturn) {
6203 tooDifficult = true;
6204 } else {
6205 node.visitChildren(this);
6206 }
6207 }
6208 }
6209
6210 abstract class InliningState {
6211 /**
6212 * Invariant: [function] must be an implementation element.
6213 */
6214 final FunctionElement function;
6215
6216 InliningState(this.function) {
6217 assert(function.isImplementation);
6218 }
6219 }
6220
6221 class AstInliningState extends InliningState {
6222 final Local oldReturnLocal;
6223 final DartType oldReturnType;
6224 final TreeElements oldElements;
6225 final List<HInstruction> oldStack;
6226 final LocalsHandler oldLocalsHandler;
6227 final bool inTryStatement;
6228 final bool allFunctionsCalledOnce;
6229
6230 AstInliningState(FunctionElement function,
6231 this.oldReturnLocal,
6232 this.oldReturnType,
6233 this.oldElements,
6234 this.oldStack,
6235 this.oldLocalsHandler,
6236 this.inTryStatement,
6237 this.allFunctionsCalledOnce)
6238 : super(function);
6239 }
6240
6241 class SsaBranch {
6242 final SsaBranchBuilder branchBuilder;
6243 final HBasicBlock block;
6244 LocalsHandler startLocals;
6245 LocalsHandler exitLocals;
6246 SubGraph graph;
6247
6248 SsaBranch(this.branchBuilder) : block = new HBasicBlock();
6249 }
6250
6251 class SsaBranchBuilder {
6252 final SsaBuilder builder;
6253 final ast.Node diagnosticNode;
6254
6255 SsaBranchBuilder(this.builder, [this.diagnosticNode]);
6256
6257 Compiler get compiler => builder.compiler;
6258
6259 void checkNotAborted() {
6260 if (builder.isAborted()) {
6261 compiler.unimplemented(diagnosticNode, "aborted control flow");
6262 }
6263 }
6264
6265 void buildCondition(void visitCondition(),
6266 SsaBranch conditionBranch,
6267 SsaBranch thenBranch,
6268 SsaBranch elseBranch) {
6269 startBranch(conditionBranch);
6270 visitCondition();
6271 checkNotAborted();
6272 assert(identical(builder.current, builder.lastOpenedBlock));
6273 HInstruction conditionValue = builder.popBoolified();
6274 HIf branch = new HIf(conditionValue);
6275 HBasicBlock conditionExitBlock = builder.current;
6276 builder.close(branch);
6277 conditionBranch.exitLocals = builder.localsHandler;
6278 conditionExitBlock.addSuccessor(thenBranch.block);
6279 conditionExitBlock.addSuccessor(elseBranch.block);
6280 bool conditionBranchLocalsCanBeReused =
6281 mergeLocals(conditionBranch, thenBranch, mayReuseFromLocals: true);
6282 mergeLocals(conditionBranch, elseBranch,
6283 mayReuseFromLocals: conditionBranchLocalsCanBeReused);
6284
6285 conditionBranch.graph =
6286 new SubExpression(conditionBranch.block, conditionExitBlock);
6287 }
6288
6289 /**
6290 * Returns true if the locals of the [fromBranch] may be reused. A [:true:]
6291 * return value implies that [mayReuseFromLocals] was set to [:true:].
6292 */
6293 bool mergeLocals(SsaBranch fromBranch, SsaBranch toBranch,
6294 {bool mayReuseFromLocals}) {
6295 LocalsHandler fromLocals = fromBranch.exitLocals;
6296 if (toBranch.startLocals == null) {
6297 if (mayReuseFromLocals) {
6298 toBranch.startLocals = fromLocals;
6299 return false;
6300 } else {
6301 toBranch.startLocals = new LocalsHandler.from(fromLocals);
6302 return true;
6303 }
6304 } else {
6305 toBranch.startLocals.mergeWith(fromLocals, toBranch.block);
6306 return true;
6307 }
6308 }
6309
6310 void startBranch(SsaBranch branch) {
6311 builder.graph.addBlock(branch.block);
6312 builder.localsHandler = branch.startLocals;
6313 builder.open(branch.block);
6314 }
6315
6316 HInstruction buildBranch(SsaBranch branch,
6317 void visitBranch(),
6318 SsaBranch joinBranch,
6319 bool isExpression) {
6320 startBranch(branch);
6321 visitBranch();
6322 branch.graph = new SubGraph(branch.block, builder.lastOpenedBlock);
6323 branch.exitLocals = builder.localsHandler;
6324 if (!builder.isAborted()) {
6325 builder.goto(builder.current, joinBranch.block);
6326 mergeLocals(branch, joinBranch, mayReuseFromLocals: true);
6327 }
6328 if (isExpression) {
6329 checkNotAborted();
6330 return builder.pop();
6331 }
6332 return null;
6333 }
6334
6335 handleIf(void visitCondition(), void visitThen(), void visitElse()) {
6336 if (visitElse == null) {
6337 // Make sure to have an else part to avoid a critical edge. A
6338 // critical edge is an edge that connects a block with multiple
6339 // successors to a block with multiple predecessors. We avoid
6340 // such edges because they prevent inserting copies during code
6341 // generation of phi instructions.
6342 visitElse = () {};
6343 }
6344
6345 _handleDiamondBranch(visitCondition, visitThen, visitElse, false);
6346 }
6347
6348 handleConditional(void visitCondition(), void visitThen(), void visitElse()) {
6349 assert(visitElse != null);
6350 _handleDiamondBranch(visitCondition, visitThen, visitElse, true);
6351 }
6352
6353 void handleLogicalAndOr(void left(), void right(), {bool isAnd}) {
6354 // x && y is transformed into:
6355 // t0 = boolify(x);
6356 // if (t0) {
6357 // t1 = boolify(y);
6358 // }
6359 // result = phi(t1, false);
6360 //
6361 // x || y is transformed into:
6362 // t0 = boolify(x);
6363 // if (not(t0)) {
6364 // t1 = boolify(y);
6365 // }
6366 // result = phi(t1, true);
6367 HInstruction boolifiedLeft;
6368 HInstruction boolifiedRight;
6369
6370 void visitCondition() {
6371 left();
6372 boolifiedLeft = builder.popBoolified();
6373 builder.stack.add(boolifiedLeft);
6374 if (!isAnd) {
6375 builder.push(new HNot(builder.pop(), builder.backend.boolType));
6376 }
6377 }
6378
6379 void visitThen() {
6380 right();
6381 boolifiedRight = builder.popBoolified();
6382 }
6383
6384 handleIf(visitCondition, visitThen, null);
6385 HConstant notIsAnd =
6386 builder.graph.addConstantBool(!isAnd, builder.compiler);
6387 JavaScriptBackend backend = builder.backend;
6388 HPhi result = new HPhi.manyInputs(null,
6389 <HInstruction>[boolifiedRight, notIsAnd],
6390 backend.dynamicType);
6391 builder.current.addPhi(result);
6392 builder.stack.add(result);
6393 }
6394
6395 void handleLogicalAndOrWithLeftNode(ast.Node left,
6396 void visitRight(),
6397 {bool isAnd}) {
6398 // This method is similar to [handleLogicalAndOr] but optimizes the case
6399 // where left is a logical "and" or logical "or".
6400 //
6401 // For example (x && y) && z is transformed into x && (y && z):
6402 // t0 = boolify(x);
6403 // if (t0) {
6404 // t1 = boolify(y);
6405 // if (t1) {
6406 // t2 = boolify(z);
6407 // }
6408 // t3 = phi(t2, false);
6409 // }
6410 // result = phi(t3, false);
6411
6412 ast.Send send = left.asSend();
6413 if (send != null &&
6414 (isAnd ? send.isLogicalAnd : send.isLogicalOr)) {
6415 ast.Node newLeft = send.receiver;
6416 Link<ast.Node> link = send.argumentsNode.nodes;
6417 assert(link.tail.isEmpty);
6418 ast.Node middle = link.head;
6419 handleLogicalAndOrWithLeftNode(
6420 newLeft,
6421 () => handleLogicalAndOrWithLeftNode(middle, visitRight,
6422 isAnd: isAnd),
6423 isAnd: isAnd);
6424 } else {
6425 handleLogicalAndOr(() => builder.visit(left), visitRight, isAnd: isAnd);
6426 }
6427 }
6428
6429 void _handleDiamondBranch(void visitCondition(),
6430 void visitThen(),
6431 void visitElse(),
6432 bool isExpression) {
6433 SsaBranch conditionBranch = new SsaBranch(this);
6434 SsaBranch thenBranch = new SsaBranch(this);
6435 SsaBranch elseBranch = new SsaBranch(this);
6436 SsaBranch joinBranch = new SsaBranch(this);
6437
6438 conditionBranch.startLocals = builder.localsHandler;
6439 builder.goto(builder.current, conditionBranch.block);
6440
6441 buildCondition(visitCondition, conditionBranch, thenBranch, elseBranch);
6442 HInstruction thenValue =
6443 buildBranch(thenBranch, visitThen, joinBranch, isExpression);
6444 HInstruction elseValue =
6445 buildBranch(elseBranch, visitElse, joinBranch, isExpression);
6446
6447 if (isExpression) {
6448 assert(thenValue != null && elseValue != null);
6449 JavaScriptBackend backend = builder.backend;
6450 HPhi phi = new HPhi.manyInputs(
6451 null, <HInstruction>[thenValue, elseValue], backend.dynamicType);
6452 joinBranch.block.addPhi(phi);
6453 builder.stack.add(phi);
6454 }
6455
6456 HBasicBlock thenBlock = thenBranch.block;
6457 HBasicBlock elseBlock = elseBranch.block;
6458 HBasicBlock joinBlock;
6459 // If at least one branch did not abort, open the joinBranch.
6460 if (!joinBranch.block.predecessors.isEmpty) {
6461 startBranch(joinBranch);
6462 joinBlock = joinBranch.block;
6463 }
6464
6465 HIfBlockInformation info =
6466 new HIfBlockInformation(
6467 new HSubExpressionBlockInformation(conditionBranch.graph),
6468 new HSubGraphBlockInformation(thenBranch.graph),
6469 new HSubGraphBlockInformation(elseBranch.graph));
6470
6471 HBasicBlock conditionStartBlock = conditionBranch.block;
6472 conditionStartBlock.setBlockFlow(info, joinBlock);
6473 SubGraph conditionGraph = conditionBranch.graph;
6474 HIf branch = conditionGraph.end.last;
6475 assert(branch is HIf);
6476 branch.blockInformation = conditionStartBlock.blockFlow;
6477 }
6478 }
6479
6480 class TypeBuilder implements DartTypeVisitor<dynamic, SsaBuilder> {
6481 final ClassWorld classWorld;
6482
6483 TypeBuilder(this.classWorld);
6484
6485 void visitType(DartType type, _) {
6486 throw 'Internal error $type';
6487 }
6488
6489 void visitVoidType(VoidType type, SsaBuilder builder) {
6490 ClassElement cls = builder.backend.findHelper('VoidRuntimeType');
6491 builder.push(new HVoidType(type, new TypeMask.exact(cls, classWorld)));
6492 }
6493
6494 void visitTypeVariableType(TypeVariableType type,
6495 SsaBuilder builder) {
6496 ClassElement cls = builder.backend.findHelper('RuntimeType');
6497 TypeMask instructionType = new TypeMask.subclass(cls, classWorld);
6498 if (!builder.sourceElement.enclosingElement.isClosure &&
6499 builder.sourceElement.isInstanceMember) {
6500 HInstruction receiver = builder.localsHandler.readThis();
6501 builder.push(new HReadTypeVariable(type, receiver, instructionType));
6502 } else {
6503 builder.push(
6504 new HReadTypeVariable.noReceiver(
6505 type, builder.addTypeVariableReference(type), instructionType));
6506 }
6507 }
6508
6509 void visitFunctionType(FunctionType type, SsaBuilder builder) {
6510 type.returnType.accept(this, builder);
6511 HInstruction returnType = builder.pop();
6512 List<HInstruction> inputs = <HInstruction>[returnType];
6513
6514 for (DartType parameter in type.parameterTypes) {
6515 parameter.accept(this, builder);
6516 inputs.add(builder.pop());
6517 }
6518
6519 for (DartType parameter in type.optionalParameterTypes) {
6520 parameter.accept(this, builder);
6521 inputs.add(builder.pop());
6522 }
6523
6524 List<DartType> namedParameterTypes = type.namedParameterTypes;
6525 List<String> names = type.namedParameters;
6526 for (int index = 0; index < names.length; index++) {
6527 ast.DartString dartString = new ast.DartString.literal(names[index]);
6528 inputs.add(
6529 builder.graph.addConstantString(dartString, builder.compiler));
6530 namedParameterTypes[index].accept(this, builder);
6531 inputs.add(builder.pop());
6532 }
6533
6534 ClassElement cls = builder.backend.findHelper('RuntimeFunctionType');
6535 builder.push(new HFunctionType(inputs, type,
6536 new TypeMask.exact(cls, classWorld)));
6537 }
6538
6539 void visitMalformedType(MalformedType type, SsaBuilder builder) {
6540 visitDynamicType(const DynamicType(), builder);
6541 }
6542
6543 void visitStatementType(StatementType type, SsaBuilder builder) {
6544 throw 'not implemented visitStatementType($type)';
6545 }
6546
6547 void visitGenericType(GenericType type, SsaBuilder builder) {
6548 throw 'not implemented visitGenericType($type)';
6549 }
6550
6551 void visitInterfaceType(InterfaceType type, SsaBuilder builder) {
6552 List<HInstruction> inputs = <HInstruction>[];
6553 for (DartType typeArgument in type.typeArguments) {
6554 typeArgument.accept(this, builder);
6555 inputs.add(builder.pop());
6556 }
6557 ClassElement cls;
6558 if (type.typeArguments.isEmpty) {
6559 cls = builder.backend.findHelper('RuntimeTypePlain');
6560 } else {
6561 cls = builder.backend.findHelper('RuntimeTypeGeneric');
6562 }
6563 builder.push(new HInterfaceType(inputs, type,
6564 new TypeMask.exact(cls, classWorld)));
6565 }
6566
6567 void visitTypedefType(TypedefType type, SsaBuilder builder) {
6568 DartType unaliased = type.unalias(builder.compiler);
6569 if (unaliased is TypedefType) throw 'unable to unalias $type';
6570 unaliased.accept(this, builder);
6571 }
6572
6573 void visitDynamicType(DynamicType type, SsaBuilder builder) {
6574 JavaScriptBackend backend = builder.compiler.backend;
6575 ClassElement cls = backend.findHelper('DynamicRuntimeType');
6576 builder.push(new HDynamicType(type, new TypeMask.exact(cls, classWorld)));
6577 }
6578 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698