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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/ssa/codegen.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 class SsaCodeGeneratorTask extends CompilerTask {
8
9 final JavaScriptBackend backend;
10
11 SsaCodeGeneratorTask(JavaScriptBackend backend)
12 : this.backend = backend,
13 super(backend.compiler);
14 String get name => 'SSA code generator';
15 NativeEmitter get nativeEmitter => backend.emitter.nativeEmitter;
16
17
18 js.Node attachPosition(js.Node node, AstElement element) {
19 // TODO(sra): Attaching positions might be cleaner if the source position
20 // was on a wrapping node.
21 SourceFile sourceFile = sourceFileOfElement(element);
22 String name = element.name;
23 AstElement implementation = element.implementation;
24 ast.Node expression = implementation.node;
25 Token beginToken;
26 Token endToken;
27 if (expression == null) {
28 // Synthesized node. Use the enclosing element for the location.
29 beginToken = endToken = element.position;
30 } else {
31 beginToken = expression.getBeginToken();
32 endToken = expression.getEndToken();
33 }
34 // TODO(podivilov): find the right sourceFile here and remove offset
35 // checks below.
36 var sourcePosition, endSourcePosition;
37 if (beginToken.charOffset < sourceFile.length) {
38 sourcePosition =
39 new TokenSourceFileLocation(sourceFile, beginToken, name);
40 }
41 if (endToken.charOffset < sourceFile.length) {
42 endSourcePosition =
43 new TokenSourceFileLocation(sourceFile, endToken, name);
44 }
45 return node.withPosition(sourcePosition, endSourcePosition);
46 }
47
48 SourceFile sourceFileOfElement(Element element) {
49 return element.implementation.compilationUnit.script.file;
50 }
51
52 js.Fun buildJavaScriptFunction(FunctionElement element,
53 List<js.Parameter> parameters,
54 js.Block body) {
55 return attachPosition(new js.Fun(parameters, body), element);
56 }
57
58 js.Expression generateCode(CodegenWorkItem work, HGraph graph) {
59 if (work.element.isField) {
60 return generateLazyInitializer(work, graph);
61 } else {
62 return generateMethod(work, graph);
63 }
64 }
65
66 js.Expression generateLazyInitializer(work, graph) {
67 return measure(() {
68 compiler.tracer.traceGraph("codegen", graph);
69 SsaCodeGenerator codegen = new SsaCodeGenerator(backend, work);
70 codegen.visitGraph(graph);
71 return new js.Fun(codegen.parameters,
72 attachPosition(codegen.body, work.element));
73 });
74 }
75
76 js.Expression generateMethod(CodegenWorkItem work, HGraph graph) {
77 return measure(() {
78 SsaCodeGenerator codegen = new SsaCodeGenerator(backend, work);
79 codegen.visitGraph(graph);
80 compiler.tracer.traceGraph("codegen", graph);
81 FunctionElement element = work.element;
82 return buildJavaScriptFunction(element, codegen.parameters, codegen.body);
83 });
84 }
85 }
86
87 typedef void EntityAction(Entity element);
88
89 class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
90 /**
91 * Returned by [expressionType] to tell how code can be generated for
92 * a subgraph.
93 * - [TYPE_STATEMENT] means that the graph must be generated as a statement,
94 * which is always possible.
95 * - [TYPE_EXPRESSION] means that the graph can be generated as an expression,
96 * or possibly several comma-separated expressions.
97 * - [TYPE_DECLARATION] means that the graph can be generated as an
98 * expression, and that it only generates expressions of the form
99 * variable = expression
100 * which are also valid as parts of a "var" declaration.
101 */
102 static const int TYPE_STATEMENT = 0;
103 static const int TYPE_EXPRESSION = 1;
104 static const int TYPE_DECLARATION = 2;
105
106 /**
107 * Whether we are currently generating expressions instead of statements.
108 * This includes declarations, which are generated as expressions.
109 */
110 bool isGeneratingExpression = false;
111
112 final JavaScriptBackend backend;
113 final CodegenWorkItem work;
114
115 final Set<HInstruction> generateAtUseSite;
116 final Set<HInstruction> controlFlowOperators;
117 final Map<Entity, EntityAction> breakAction;
118 final Map<Entity, EntityAction> continueAction;
119 final List<js.Parameter> parameters;
120
121 js.Block currentContainer;
122 js.Block get body => currentContainer;
123 List<js.Expression> expressionStack;
124 List<js.Block> oldContainerStack;
125
126 /**
127 * Contains the names of the instructions, as well as the parallel
128 * copies to perform on block transitioning.
129 */
130 VariableNames variableNames;
131 bool shouldGroupVarDeclarations = false;
132
133 /**
134 * While generating expressions, we can't insert variable declarations.
135 * Instead we declare them at the start of the function. When minifying
136 * we do this most of the time, because it reduces the size unless there
137 * is only one variable.
138 */
139 final Set<String> collectedVariableDeclarations;
140
141 /**
142 * Set of variables and parameters that have already been declared.
143 */
144 final Set<String> declaredLocals;
145
146 HGraph currentGraph;
147
148 // Records a block-information that is being handled specially.
149 // Used to break bad recursion.
150 HBlockInformation currentBlockInformation;
151 // The subgraph is used to delimit traversal for some constructions, e.g.,
152 // if branches.
153 SubGraph subGraph;
154
155 SsaCodeGenerator(this.backend, CodegenWorkItem work)
156 : this.work = work,
157 declaredLocals = new Set<String>(),
158 collectedVariableDeclarations = new Set<String>(),
159 currentContainer = new js.Block.empty(),
160 parameters = <js.Parameter>[],
161 expressionStack = <js.Expression>[],
162 oldContainerStack = <js.Block>[],
163 generateAtUseSite = new Set<HInstruction>(),
164 controlFlowOperators = new Set<HInstruction>(),
165 breakAction = new Map<Entity, EntityAction>(),
166 continueAction = new Map<Entity, EntityAction>();
167
168 Compiler get compiler => backend.compiler;
169 NativeEmitter get nativeEmitter => backend.emitter.nativeEmitter;
170 CodegenRegistry get registry => work.registry;
171
172 bool isGenerateAtUseSite(HInstruction instruction) {
173 return generateAtUseSite.contains(instruction);
174 }
175
176 bool hasNonBitOpUser(HInstruction instruction, Set<HPhi> phiSet) {
177 for (HInstruction user in instruction.usedBy) {
178 if (user is HPhi) {
179 if (!phiSet.contains(user)) {
180 phiSet.add(user);
181 if (hasNonBitOpUser(user, phiSet)) return true;
182 }
183 } else if (user is! HBitNot && user is! HBinaryBitOp) {
184 return true;
185 }
186 }
187 return false;
188 }
189
190 bool requiresUintConversion(instruction) {
191 if (instruction.isUInt31(compiler)) return false;
192 // If the result of a bit-operation is only used by other bit
193 // operations, we do not have to convert to an unsigned integer.
194 return hasNonBitOpUser(instruction, new Set<HPhi>());
195 }
196
197 /**
198 * If the [instruction] is not `null` it will be used to attach the position
199 * to the [statement].
200 */
201 void pushStatement(js.Statement statement, [HInstruction instruction]) {
202 assert(expressionStack.isEmpty);
203 if (instruction != null) {
204 statement = attachLocation(statement, instruction);
205 }
206 currentContainer.statements.add(statement);
207 }
208
209 void insertStatementAtStart(js.Statement statement) {
210 currentContainer.statements.insert(0, statement);
211 }
212
213 /**
214 * If the [instruction] is not `null` it will be used to attach the position
215 * to the [expression].
216 */
217 pushExpressionAsStatement(js.Expression expression,
218 [HInstruction instruction]) {
219 pushStatement(new js.ExpressionStatement(expression), instruction);
220 }
221
222 /**
223 * If the [instruction] is not `null` it will be used to attach the position
224 * to the [expression].
225 */
226 push(js.Expression expression, [HInstruction instruction]) {
227 if (instruction != null) {
228 expression = attachLocation(expression, instruction);
229 }
230 expressionStack.add(expression);
231 }
232
233 js.Expression pop() {
234 return expressionStack.removeLast();
235 }
236
237 attachLocationToLast(HInstruction instruction) {
238 int index = expressionStack.length - 1;
239 expressionStack[index] =
240 attachLocation(expressionStack[index], instruction);
241 }
242
243 js.Node attachLocation(js.Node jsNode, HInstruction instruction) {
244 return jsNode.withLocation(instruction.sourcePosition);
245 }
246
247 js.Node attachLocationRange(js.Node jsNode,
248 SourceFileLocation sourcePosition,
249 SourceFileLocation endSourcePosition) {
250 return jsNode.withPosition(sourcePosition, endSourcePosition);
251 }
252
253 void preGenerateMethod(HGraph graph) {
254 new SsaInstructionSelection(compiler).visitGraph(graph);
255 new SsaTypeKnownRemover().visitGraph(graph);
256 new SsaInstructionMerger(generateAtUseSite, compiler).visitGraph(graph);
257 new SsaConditionMerger(
258 generateAtUseSite, controlFlowOperators).visitGraph(graph);
259 SsaLiveIntervalBuilder intervalBuilder = new SsaLiveIntervalBuilder(
260 compiler, generateAtUseSite, controlFlowOperators);
261 intervalBuilder.visitGraph(graph);
262 SsaVariableAllocator allocator = new SsaVariableAllocator(
263 compiler,
264 intervalBuilder.liveInstructions,
265 intervalBuilder.liveIntervals,
266 generateAtUseSite);
267 allocator.visitGraph(graph);
268 variableNames = allocator.names;
269 shouldGroupVarDeclarations = allocator.names.numberOfVariables > 1;
270 }
271
272 void handleDelayedVariableDeclarations() {
273 // If we have only one variable declaration and the first statement is an
274 // assignment to that variable then we can merge the two. We count the
275 // number of variables in the variable allocator to try to avoid this issue,
276 // but it sometimes happens that the variable allocator introduces a
277 // temporary variable that it later eliminates.
278 if (!collectedVariableDeclarations.isEmpty) {
279 if (collectedVariableDeclarations.length == 1 &&
280 currentContainer.statements.length >= 1 &&
281 currentContainer.statements[0] is js.ExpressionStatement) {
282 String name = collectedVariableDeclarations.first;
283 js.ExpressionStatement statement = currentContainer.statements[0];
284 if (statement.expression is js.Assignment) {
285 js.Assignment assignment = statement.expression;
286 if (!assignment.isCompound &&
287 assignment.leftHandSide is js.VariableReference) {
288 js.VariableReference variableReference = assignment.leftHandSide;
289 if (variableReference.name == name) {
290 js.VariableDeclaration decl = new js.VariableDeclaration(name);
291 js.VariableInitialization initialization =
292 new js.VariableInitialization(decl, assignment.value);
293 currentContainer.statements[0] = new js.ExpressionStatement(
294 new js.VariableDeclarationList([initialization]));
295 return;
296 }
297 }
298 }
299 }
300 // If we can't merge the declaration with the first assignment then we
301 // just do it with a new var z,y,x; statement.
302 List<js.VariableInitialization> declarations =
303 <js.VariableInitialization>[];
304 collectedVariableDeclarations.forEach((String name) {
305 declarations.add(new js.VariableInitialization(
306 new js.VariableDeclaration(name), null));
307 });
308 var declarationList = new js.VariableDeclarationList(declarations);
309 insertStatementAtStart(new js.ExpressionStatement(declarationList));
310 }
311 }
312
313 visitGraph(HGraph graph) {
314 preGenerateMethod(graph);
315 currentGraph = graph;
316 subGraph = new SubGraph(graph.entry, graph.exit);
317 visitBasicBlock(graph.entry);
318 handleDelayedVariableDeclarations();
319 }
320
321 void visitSubGraph(SubGraph newSubGraph) {
322 SubGraph oldSubGraph = subGraph;
323 subGraph = newSubGraph;
324 visitBasicBlock(subGraph.start);
325 subGraph = oldSubGraph;
326 }
327
328 /**
329 * Check whether a sub-graph can be generated as an expression, or even
330 * as a declaration, or if it has to fall back to being generated as
331 * a statement.
332 * Expressions are anything that doesn't generate control flow constructs.
333 * Declarations must only generate assignments on the form "id = expression",
334 * and not, e.g., expressions where the value isn't assigned, or where it's
335 * assigned to something that's not a simple variable.
336 */
337 int expressionType(HExpressionInformation info) {
338 // The only HExpressionInformation used as part of a HBlockInformation is
339 // current HSubExpressionBlockInformation, so it's the only one reaching
340 // here. If we start using the other HExpressionInformation types too,
341 // this code should be generalized.
342 assert(info is HSubExpressionBlockInformation);
343 HSubExpressionBlockInformation expressionInfo = info;
344 SubGraph limits = expressionInfo.subExpression;
345
346 // Start assuming that we can generate declarations. If we find a
347 // counter-example, we degrade our assumption to either expression or
348 // statement, and in the latter case, we can return immediately since
349 // it can't get any worse. E.g., a function call where the return value
350 // isn't used can't be in a declaration.
351 int result = TYPE_DECLARATION;
352 HBasicBlock basicBlock = limits.start;
353 do {
354 HInstruction current = basicBlock.first;
355 while (current != basicBlock.last) {
356 // E.g, bounds check.
357 if (current.isControlFlow()) {
358 return TYPE_STATEMENT;
359 }
360 // HFieldSet generates code on the form x.y = ..., which isn't
361 // valid in a declaration, but it also always have no uses, so
362 // it's caught by that test too.
363 assert(current is! HFieldSet || current.usedBy.isEmpty);
364 if (current.usedBy.isEmpty) {
365 result = TYPE_EXPRESSION;
366 }
367 current = current.next;
368 }
369 if (current is HGoto) {
370 basicBlock = basicBlock.successors[0];
371 } else if (current is HConditionalBranch) {
372 if (generateAtUseSite.contains(current)) {
373 // Short-circuit control flow operator trickery.
374 // Check the second half, which will continue into the join.
375 // (The first half is [inputs[0]], the second half is [successors[0]],
376 // and [successors[1]] is the join-block).
377 basicBlock = basicBlock.successors[0];
378 } else {
379 // We allow an expression to end on an HIf (a condition expression).
380 return identical(basicBlock, limits.end) ? result : TYPE_STATEMENT;
381 }
382 } else {
383 // Expression-incompatible control flow.
384 return TYPE_STATEMENT;
385 }
386 } while (limits.contains(basicBlock));
387 return result;
388 }
389
390 bool isJSExpression(HExpressionInformation info) {
391 return !identical(expressionType(info), TYPE_STATEMENT);
392 }
393
394 bool isJSCondition(HExpressionInformation info) {
395 HSubExpressionBlockInformation graph = info;
396 SubExpression limits = graph.subExpression;
397 return !identical(expressionType(info), TYPE_STATEMENT) &&
398 (limits.end.last is HConditionalBranch);
399 }
400
401 /**
402 * Generate statements from block information.
403 * If the block information contains expressions, generate only
404 * assignments, and if it ends in a conditional branch, don't generate
405 * the condition.
406 */
407 void generateStatements(HBlockInformation block) {
408 if (block is HStatementInformation) {
409 block.accept(this);
410 } else {
411 HSubExpressionBlockInformation expression = block;
412 visitSubGraph(expression.subExpression);
413 }
414 }
415
416 js.Block generateStatementsInNewBlock(HBlockInformation block) {
417 js.Block result = new js.Block.empty();
418 js.Block oldContainer = currentContainer;
419 currentContainer = result;
420 generateStatements(block);
421 currentContainer = oldContainer;
422 return result;
423 }
424
425 /**
426 * If the [block] only contains one statement returns that statement. If the
427 * that statement itself is a block, recursively calls this method.
428 *
429 * If the block is empty, returns a new instance of [js.NOP].
430 */
431 js.Statement unwrapStatement(js.Block block) {
432 int len = block.statements.length;
433 if (len == 0) return new js.EmptyStatement();
434 if (len == 1) {
435 js.Statement result = block.statements[0];
436 if (result is ast.Block) return unwrapStatement(result);
437 return result;
438 }
439 return block;
440 }
441
442 /**
443 * Generate expressions from block information.
444 */
445 js.Expression generateExpression(HExpressionInformation expression) {
446 // Currently we only handle sub-expression graphs.
447 assert(expression is HSubExpressionBlockInformation);
448
449 bool oldIsGeneratingExpression = isGeneratingExpression;
450 isGeneratingExpression = true;
451 List<js.Expression> oldExpressionStack = expressionStack;
452 List<js.Expression> sequenceElements = <js.Expression>[];
453 expressionStack = sequenceElements;
454 HSubExpressionBlockInformation expressionSubGraph = expression;
455 visitSubGraph(expressionSubGraph.subExpression);
456 expressionStack = oldExpressionStack;
457 isGeneratingExpression = oldIsGeneratingExpression;
458 if (sequenceElements.isEmpty) {
459 // Happens when the initializer, condition or update of a loop is empty.
460 return null;
461 } else if (sequenceElements.length == 1) {
462 return sequenceElements[0];
463 } else {
464 js.Expression result = sequenceElements.removeLast();
465 while (sequenceElements.isNotEmpty) {
466 result = new js.Binary(',', sequenceElements.removeLast(), result);
467 }
468 return result;
469 }
470 }
471
472 /**
473 * Only visits the arguments starting at inputs[HInvoke.ARGUMENTS_OFFSET].
474 */
475 List<js.Expression> visitArguments(List<HInstruction> inputs,
476 {int start: HInvoke.ARGUMENTS_OFFSET}) {
477 assert(inputs.length >= start);
478 List<js.Expression> result = new List<js.Expression>(inputs.length - start);
479 for (int i = start; i < inputs.length; i++) {
480 use(inputs[i]);
481 result[i - start] = pop();
482 }
483 return result;
484 }
485
486 bool isVariableDeclared(String variableName) {
487 return declaredLocals.contains(variableName) ||
488 collectedVariableDeclarations.contains(variableName);
489 }
490
491 js.Expression generateExpressionAssignment(String variableName,
492 js.Expression value) {
493 if (value is js.Binary) {
494 js.Binary binary = value;
495 String op = binary.op;
496 if (op == '+' || op == '-' || op == '/' || op == '*' || op == '%' ||
497 op == '^' || op == '&' || op == '|') {
498 if (binary.left is js.VariableUse &&
499 (binary.left as js.VariableUse).name == variableName) {
500 // We know now, that we can shorten x = x + y into x += y.
501 // Also check for the shortcut where y equals 1: x++ and x--.
502 if ((op == '+' || op == '-') &&
503 binary.right is js.LiteralNumber &&
504 (binary.right as js.LiteralNumber).value == "1") {
505 return new js.Prefix(op == '+' ? '++' : '--', binary.left);
506 }
507 return new js.Assignment.compound(binary.left, op, binary.right);
508 }
509 }
510 }
511 return new js.Assignment(new js.VariableUse(variableName), value);
512 }
513
514 void assignVariable(String variableName, js.Expression value) {
515 if (isGeneratingExpression) {
516 // If we are in an expression then we can't declare the variable here.
517 // We have no choice, but to use it and then declare it separately.
518 if (!isVariableDeclared(variableName)) {
519 collectedVariableDeclarations.add(variableName);
520 }
521 push(generateExpressionAssignment(variableName, value));
522 // Otherwise if we are trying to declare inline and we are in a statement
523 // then we declare (unless it was already declared).
524 } else if (!shouldGroupVarDeclarations &&
525 !declaredLocals.contains(variableName)) {
526 // It may be necessary to remove it from the ones to be declared later.
527 collectedVariableDeclarations.remove(variableName);
528 declaredLocals.add(variableName);
529 js.VariableDeclaration decl = new js.VariableDeclaration(variableName);
530 js.VariableInitialization initialization =
531 new js.VariableInitialization(decl, value);
532
533 pushExpressionAsStatement(new js.VariableDeclarationList(
534 <js.VariableInitialization>[initialization]));
535 } else {
536 // Otherwise we are just going to use it. If we have not already declared
537 // it then we make sure we will declare it later.
538 if (!declaredLocals.contains(variableName)) {
539 collectedVariableDeclarations.add(variableName);
540 }
541 pushExpressionAsStatement(
542 generateExpressionAssignment(variableName, value));
543 }
544 }
545
546 void define(HInstruction instruction) {
547 // For simple type checks like i = intTypeCheck(i), we don't have to
548 // emit an assignment, because the intTypeCheck just returns its
549 // argument.
550 bool needsAssignment = true;
551 if (instruction is HTypeConversion) {
552 HTypeConversion typeConversion = instruction;
553 String inputName = variableNames.getName(typeConversion.checkedInput);
554 if (variableNames.getName(instruction) == inputName) {
555 needsAssignment = false;
556 }
557 }
558 if (instruction is HLocalValue) {
559 needsAssignment = false;
560 }
561
562 if (needsAssignment &&
563 !instruction.isControlFlow() && variableNames.hasName(instruction)) {
564 visitExpression(instruction);
565 assignVariable(variableNames.getName(instruction), pop());
566 return;
567 }
568
569 if (isGeneratingExpression) {
570 visitExpression(instruction);
571 } else {
572 visitStatement(instruction);
573 }
574 }
575
576 void use(HInstruction argument) {
577 if (isGenerateAtUseSite(argument)) {
578 visitExpression(argument);
579 } else if (argument is HCheck && !variableNames.hasName(argument)) {
580 HCheck check = argument;
581 // This can only happen if the checked node does not have a name.
582 assert(!variableNames.hasName(check.checkedInput));
583 use(check.checkedInput);
584 } else {
585 assert(variableNames.hasName(argument));
586 push(new js.VariableUse(variableNames.getName(argument)));
587 }
588 }
589
590 visit(HInstruction node) {
591 node.accept(this);
592 }
593
594 visitExpression(HInstruction node) {
595 bool oldIsGeneratingExpression = isGeneratingExpression;
596 isGeneratingExpression = true;
597 visit(node);
598 isGeneratingExpression = oldIsGeneratingExpression;
599 }
600
601 visitStatement(HInstruction node) {
602 assert(!isGeneratingExpression);
603 visit(node);
604 if (!expressionStack.isEmpty) {
605 assert(expressionStack.length == 1);
606 pushExpressionAsStatement(pop());
607 }
608 }
609
610 void continueAsBreak(LabelDefinition target) {
611 pushStatement(new js.Break(backend.namer.continueLabelName(target)));
612 }
613
614 void implicitContinueAsBreak(JumpTarget target) {
615 pushStatement(new js.Break(
616 backend.namer.implicitContinueLabelName(target)));
617 }
618
619 void implicitBreakWithLabel(JumpTarget target) {
620 pushStatement(new js.Break(backend.namer.implicitBreakLabelName(target)));
621 }
622
623 js.Statement wrapIntoLabels(js.Statement result, List<LabelDefinition> labels) {
624 for (LabelDefinition label in labels) {
625 if (label.isTarget) {
626 String breakLabelString = backend.namer.breakLabelName(label);
627 result = new js.LabeledStatement(breakLabelString, result);
628 }
629 }
630 return result;
631 }
632
633
634 // The regular [visitIf] method implements the needed logic.
635 bool visitIfInfo(HIfBlockInformation info) => false;
636
637 bool visitSwitchInfo(HSwitchBlockInformation info) {
638 bool isExpression = isJSExpression(info.expression);
639 if (!isExpression) {
640 generateStatements(info.expression);
641 }
642
643 if (isExpression) {
644 push(generateExpression(info.expression));
645 } else {
646 use(info.expression.conditionExpression);
647 }
648 js.Expression key = pop();
649 List<js.SwitchClause> cases = <js.SwitchClause>[];
650 HSwitch switchInstruction = info.expression.end.last;
651 List<HInstruction> inputs = switchInstruction.inputs;
652 List<HBasicBlock> successors = switchInstruction.block.successors;
653
654 js.Block oldContainer = currentContainer;
655 for (int inputIndex = 1, statementIndex = 0;
656 inputIndex < inputs.length;
657 statementIndex++) {
658 HBasicBlock successor = successors[inputIndex - 1];
659 // If liveness analysis has figured out that this case is dead,
660 // omit the code for it.
661 if (successor.isLive) {
662 do {
663 visit(inputs[inputIndex]);
664 currentContainer = new js.Block.empty();
665 cases.add(new js.Case(pop(), currentContainer));
666 inputIndex++;
667 } while ((successors[inputIndex - 1] == successor)
668 && (inputIndex < inputs.length));
669
670 generateStatements(info.statements[statementIndex]);
671 } else {
672 // Skip all the case statements that belong to this
673 // block.
674 while ((successors[inputIndex - 1] == successor)
675 && (inputIndex < inputs.length)) {
676 ++inputIndex;
677 }
678 }
679 }
680
681 // If the default case is dead, we omit it. Likewise, if it is an
682 // empty block, we omit it, too.
683 if (info.statements.last.start.isLive) {
684 currentContainer = new js.Block.empty();
685 generateStatements(info.statements.last);
686 if (currentContainer.statements.isNotEmpty) {
687 cases.add(new js.Default(currentContainer));
688 }
689 }
690
691 currentContainer = oldContainer;
692
693 js.Statement result = new js.Switch(key, cases);
694 pushStatement(wrapIntoLabels(result, info.labels));
695 return true;
696 }
697
698 bool visitSequenceInfo(HStatementSequenceInformation info) {
699 return false;
700 }
701
702 bool visitSubGraphInfo(HSubGraphBlockInformation info) {
703 visitSubGraph(info.subGraph);
704 return true;
705 }
706
707 bool visitSubExpressionInfo(HSubExpressionBlockInformation info) {
708 return false;
709 }
710
711 bool visitAndOrInfo(HAndOrBlockInformation info) {
712 return false;
713 }
714
715 bool visitTryInfo(HTryBlockInformation info) {
716 js.Block body = generateStatementsInNewBlock(info.body);
717 js.Catch catchPart = null;
718 js.Block finallyPart = null;
719 if (info.catchBlock != null) {
720 void register(ClassElement classElement) {
721 if (classElement != null) {
722 registry.registerInstantiatedClass(classElement);
723 }
724 }
725 register(backend.jsPlainJavaScriptObjectClass);
726 register(backend.jsUnknownJavaScriptObjectClass);
727
728 HLocalValue exception = info.catchVariable;
729 String name = variableNames.getName(exception);
730 js.VariableDeclaration decl = new js.VariableDeclaration(name);
731 js.Block catchBlock = generateStatementsInNewBlock(info.catchBlock);
732 catchPart = new js.Catch(decl, catchBlock);
733 }
734 if (info.finallyBlock != null) {
735 finallyPart = generateStatementsInNewBlock(info.finallyBlock);
736 }
737 pushStatement(new js.Try(body, catchPart, finallyPart));
738 return true;
739 }
740
741 void visitBodyIgnoreLabels(HLoopBlockInformation info) {
742 if (info.body.start.isLabeledBlock()) {
743 HBlockInformation oldInfo = currentBlockInformation;
744 currentBlockInformation = info.body.start.blockFlow.body;
745 generateStatements(info.body);
746 currentBlockInformation = oldInfo;
747 } else {
748 generateStatements(info.body);
749 }
750 }
751
752 bool visitLoopInfo(HLoopBlockInformation info) {
753 HExpressionInformation condition = info.condition;
754 bool isConditionExpression = isJSCondition(condition);
755
756 js.Loop loop;
757
758 switch (info.kind) {
759 // Treate all three "test-first" loops the same way.
760 case HLoopBlockInformation.FOR_LOOP:
761 case HLoopBlockInformation.WHILE_LOOP:
762 case HLoopBlockInformation.FOR_IN_LOOP:
763 case HLoopBlockInformation.SWITCH_CONTINUE_LOOP:
764 HBlockInformation initialization = info.initializer;
765 int initializationType = TYPE_STATEMENT;
766 if (initialization != null) {
767 initializationType = expressionType(initialization);
768 if (initializationType == TYPE_STATEMENT) {
769 generateStatements(initialization);
770 initialization = null;
771 }
772 }
773
774 // We inserted a basic block to avoid critical edges. This block is
775 // part of the LoopBlockInformation and must therefore be handled here.
776 js.Block oldContainer = currentContainer;
777 js.Block avoidContainer = new js.Block.empty();
778 currentContainer = avoidContainer;
779 assignPhisOfSuccessors(condition.end.successors.last);
780 bool hasPhiUpdates = !avoidContainer.statements.isEmpty;
781 currentContainer = oldContainer;
782
783 if (isConditionExpression &&
784 !hasPhiUpdates &&
785 info.updates != null && isJSExpression(info.updates)) {
786 // If we have an updates graph, and it's expressible as an
787 // expression, generate a for-loop.
788 js.Expression jsInitialization = null;
789 if (initialization != null) {
790 int delayedVariablesCount = collectedVariableDeclarations.length;
791 jsInitialization = generateExpression(initialization);
792 if (!shouldGroupVarDeclarations &&
793 delayedVariablesCount < collectedVariableDeclarations.length) {
794 // We just added a new delayed variable-declaration. See if we can
795 // put in a 'var' in front of the initialization to make it go
796 // away. We walk the 'tree' of comma-operators to find the
797 // expressions and see if they are all assignments that can be
798 // converted into declarations.
799
800 List<js.Assignment> assignments;
801
802 bool allSimpleAssignments(js.Expression expression) {
803 if (expression is js.Assignment) {
804 js.Assignment assignment = expression;
805 if (assignment.leftHandSide is js.VariableUse &&
806 !assignment.isCompound) {
807 if (assignments == null) assignments = <js.Assignment>[];
808 assignments.add(expression);
809 return true;
810 }
811 } else if (expression.isCommaOperator) {
812 js.Binary binary = expression;
813 return allSimpleAssignments(binary.left)
814 && allSimpleAssignments(binary.right);
815 }
816 return false;
817 }
818
819 if (allSimpleAssignments(jsInitialization)) {
820 List<js.VariableInitialization> inits =
821 <js.VariableInitialization>[];
822 for (js.Assignment assignment in assignments) {
823 String id = (assignment.leftHandSide as js.VariableUse).name;
824 js.Node declaration = new js.VariableDeclaration(id);
825 inits.add(new js.VariableInitialization(declaration,
826 assignment.value));
827 collectedVariableDeclarations.remove(id);
828 declaredLocals.add(id);
829 }
830 jsInitialization = new js.VariableDeclarationList(inits);
831 }
832 }
833 }
834 js.Expression jsCondition = generateExpression(condition);
835 js.Expression jsUpdates = generateExpression(info.updates);
836 // The body might be labeled. Ignore this when recursing on the
837 // subgraph.
838 // TODO(lrn): Remove this extra labeling when handling all loops
839 // using subgraphs.
840 oldContainer = currentContainer;
841 js.Statement body = new js.Block.empty();
842 currentContainer = body;
843 visitBodyIgnoreLabels(info);
844 currentContainer = oldContainer;
845 body = unwrapStatement(body);
846 loop = new js.For(jsInitialization, jsCondition, jsUpdates, body);
847 } else {
848 // We have either no update graph, or it's too complex to
849 // put in an expression.
850 if (initialization != null) {
851 generateStatements(initialization);
852 }
853 js.Expression jsCondition;
854 js.Block oldContainer = currentContainer;
855 js.Statement body = new js.Block.empty();
856 if (isConditionExpression && !hasPhiUpdates) {
857 jsCondition = generateExpression(condition);
858 currentContainer = body;
859 } else {
860 jsCondition = newLiteralBool(true);
861 currentContainer = body;
862 generateStatements(condition);
863 use(condition.conditionExpression);
864 js.Expression ifTest = new js.Prefix("!", pop());
865 js.Statement jsBreak = new js.Break(null);
866 js.Statement exitLoop;
867 if (avoidContainer.statements.isEmpty) {
868 exitLoop = jsBreak;
869 } else {
870 avoidContainer.statements.add(jsBreak);
871 exitLoop = avoidContainer;
872 }
873 pushStatement(new js.If.noElse(ifTest, exitLoop));
874 }
875 if (info.updates != null) {
876 wrapLoopBodyForContinue(info);
877 generateStatements(info.updates);
878 } else {
879 visitBodyIgnoreLabels(info);
880 }
881 currentContainer = oldContainer;
882 body = unwrapStatement(body);
883 loop = new js.While(jsCondition, body);
884 }
885 break;
886 case HLoopBlockInformation.DO_WHILE_LOOP:
887 if (info.initializer != null) {
888 generateStatements(info.initializer);
889 }
890 // We inserted a basic block to avoid critical edges. This block is
891 // part of the LoopBlockInformation and must therefore be handled here.
892 js.Block oldContainer = currentContainer;
893 js.Block exitAvoidContainer = new js.Block.empty();
894 currentContainer = exitAvoidContainer;
895 assignPhisOfSuccessors(condition.end.successors.last);
896 bool hasExitPhiUpdates = !exitAvoidContainer.statements.isEmpty;
897 currentContainer = oldContainer;
898
899
900 oldContainer = currentContainer;
901 js.Block body = new js.Block.empty();
902 // If there are phi copies in the block that jumps to the
903 // loop entry, we must emit the condition like this:
904 // do {
905 // body;
906 // if (condition) {
907 // phi updates;
908 // continue;
909 // } else {
910 // break;
911 // }
912 // } while (true);
913 HBasicBlock avoidEdge = info.end.successors[0];
914 js.Block updateBody = new js.Block.empty();
915 currentContainer = updateBody;
916 assignPhisOfSuccessors(avoidEdge);
917 bool hasPhiUpdates = !updateBody.statements.isEmpty;
918 currentContainer = body;
919 visitBodyIgnoreLabels(info);
920 if (info.updates != null) {
921 generateStatements(info.updates);
922 }
923 if (isConditionExpression) {
924 push(generateExpression(condition));
925 } else {
926 generateStatements(condition);
927 use(condition.conditionExpression);
928 }
929 js.Expression jsCondition = pop();
930 if (jsCondition == null) {
931 // If the condition is dead code, we turn the do-while into
932 // a simpler while because we will never reach the condition
933 // at the end of the loop anyway.
934 loop = new js.While(newLiteralBool(true), unwrapStatement(body));
935 } else {
936 if (hasPhiUpdates || hasExitPhiUpdates) {
937 updateBody.statements.add(new js.Continue(null));
938 js.Statement jsBreak = new js.Break(null);
939 js.Statement exitLoop;
940 if (exitAvoidContainer.statements.isEmpty) {
941 exitLoop = jsBreak;
942 } else {
943 exitAvoidContainer.statements.add(jsBreak);
944 exitLoop = exitAvoidContainer;
945 }
946 body.statements.add(
947 new js.If(jsCondition, updateBody, exitLoop));
948 jsCondition = newLiteralBool(true);
949 }
950 loop = new js.Do(unwrapStatement(body), jsCondition);
951 }
952 currentContainer = oldContainer;
953 break;
954 default:
955 compiler.internalError(condition.conditionExpression,
956 'Unexpected loop kind: ${info.kind}.');
957 }
958 js.Statement result =
959 attachLocationRange(loop, info.sourcePosition, info.endSourcePosition);
960 if (info.kind == HLoopBlockInformation.SWITCH_CONTINUE_LOOP) {
961 String continueLabelString =
962 backend.namer.implicitContinueLabelName(info.target);
963 result = new js.LabeledStatement(continueLabelString, result);
964 }
965 pushStatement(wrapIntoLabels(result, info.labels));
966 return true;
967 }
968
969 bool visitLabeledBlockInfo(HLabeledBlockInformation labeledBlockInfo) {
970 Link<Entity> continueOverrides = const Link<Entity>();
971
972 js.Block oldContainer = currentContainer;
973 js.Block body = new js.Block.empty();
974 js.Statement result = body;
975
976 currentContainer = body;
977
978 // If [labeledBlockInfo.isContinue], the block is an artificial
979 // block around the body of a loop with an update block, so that
980 // continues of the loop can be written as breaks of the body
981 // block.
982 if (labeledBlockInfo.isContinue) {
983 for (LabelDefinition label in labeledBlockInfo.labels) {
984 if (label.isContinueTarget) {
985 String labelName = backend.namer.continueLabelName(label);
986 result = new js.LabeledStatement(labelName, result);
987 continueAction[label] = continueAsBreak;
988 continueOverrides = continueOverrides.prepend(label);
989 }
990 }
991 // For handling unlabeled continues from the body of a loop.
992 // TODO(lrn): Consider recording whether the target is in fact
993 // a target of an unlabeled continue, and not generate this if it isn't.
994 JumpTarget target = labeledBlockInfo.target;
995 String labelName = backend.namer.implicitContinueLabelName(target);
996 result = new js.LabeledStatement(labelName, result);
997 continueAction[target] = implicitContinueAsBreak;
998 continueOverrides = continueOverrides.prepend(target);
999 } else {
1000 for (LabelDefinition label in labeledBlockInfo.labels) {
1001 if (label.isBreakTarget) {
1002 String labelName = backend.namer.breakLabelName(label);
1003 result = new js.LabeledStatement(labelName, result);
1004 }
1005 }
1006 }
1007 JumpTarget target = labeledBlockInfo.target;
1008 if (target.isSwitch) {
1009 // This is an extra block around a switch that is generated
1010 // as a nested if/else chain. We add an extra break target
1011 // so that case code can break.
1012 String labelName = backend.namer.implicitBreakLabelName(target);
1013 result = new js.LabeledStatement(labelName, result);
1014 breakAction[target] = implicitBreakWithLabel;
1015 }
1016
1017 currentContainer = body;
1018 generateStatements(labeledBlockInfo.body);
1019
1020 if (labeledBlockInfo.isContinue) {
1021 while (!continueOverrides.isEmpty) {
1022 continueAction.remove(continueOverrides.head);
1023 continueOverrides = continueOverrides.tail;
1024 }
1025 } else {
1026 breakAction.remove(labeledBlockInfo.target);
1027 }
1028
1029 currentContainer = oldContainer;
1030 pushStatement(result);
1031 return true;
1032 }
1033
1034 // Wraps a loop body in a block to make continues have a target to break
1035 // to (if necessary).
1036 void wrapLoopBodyForContinue(HLoopBlockInformation info) {
1037 JumpTarget target = info.target;
1038 if (target != null && target.isContinueTarget) {
1039 js.Block oldContainer = currentContainer;
1040 js.Block body = new js.Block.empty();
1041 currentContainer = body;
1042 js.Statement result = body;
1043 for (LabelDefinition label in info.labels) {
1044 if (label.isContinueTarget) {
1045 String labelName = backend.namer.continueLabelName(label);
1046 result = new js.LabeledStatement(labelName, result);
1047 continueAction[label] = continueAsBreak;
1048 }
1049 }
1050 String labelName = backend.namer.implicitContinueLabelName(target);
1051 result = new js.LabeledStatement(labelName, result);
1052 continueAction[info.target] = implicitContinueAsBreak;
1053 visitBodyIgnoreLabels(info);
1054 continueAction.remove(info.target);
1055 for (LabelDefinition label in info.labels) {
1056 if (label.isContinueTarget) {
1057 continueAction.remove(label);
1058 }
1059 }
1060 currentContainer = oldContainer;
1061 pushStatement(result);
1062 } else {
1063 // Loop body contains no continues, so we don't need a break target.
1064 generateStatements(info.body);
1065 }
1066 }
1067
1068 bool handleBlockFlow(HBlockFlow block) {
1069 HBlockInformation info = block.body;
1070 // If we reach here again while handling the attached information,
1071 // e.g., because we call visitSubGraph on a subgraph starting on
1072 // the same block, don't handle it again.
1073 // When the structure graph is complete, we will be able to have
1074 // different structures starting on the same basic block (e.g., an
1075 // "if" and its condition).
1076 if (identical(info, currentBlockInformation)) return false;
1077
1078 HBlockInformation oldBlockInformation = currentBlockInformation;
1079 currentBlockInformation = info;
1080 bool success = info.accept(this);
1081 currentBlockInformation = oldBlockInformation;
1082 if (success) {
1083 HBasicBlock continuation = block.continuation;
1084 if (continuation != null) {
1085 visitBasicBlock(continuation);
1086 }
1087 }
1088 return success;
1089 }
1090
1091 void visitBasicBlock(HBasicBlock node) {
1092 if (!node.isLive) return;
1093
1094 // Abort traversal if we are leaving the currently active sub-graph.
1095 if (!subGraph.contains(node)) return;
1096
1097 // If this node has block-structure based information attached,
1098 // try using that to traverse from here.
1099 if (node.blockFlow != null && handleBlockFlow(node.blockFlow)) {
1100 return;
1101 }
1102 iterateBasicBlock(node);
1103 }
1104
1105 void emitAssignment(String destination, String source) {
1106 assignVariable(destination, new js.VariableUse(source));
1107 }
1108
1109 /**
1110 * Sequentialize a list of conceptually parallel copies. Parallel
1111 * copies may contain cycles, that this method breaks.
1112 */
1113 void sequentializeCopies(Iterable<Copy> copies,
1114 String tempName,
1115 void doAssignment(String target, String source)) {
1116 // Map to keep track of the current location (ie the variable that
1117 // holds the initial value) of a variable.
1118 Map<String, String> currentLocation = new Map<String, String>();
1119
1120 // Map to keep track of the initial value of a variable.
1121 Map<String, String> initialValue = new Map<String, String>();
1122
1123 // List of variables to assign a value.
1124 List<String> worklist = <String>[];
1125
1126 // List of variables that we can assign a value to (ie are not
1127 // being used anymore).
1128 List<String> ready = <String>[];
1129
1130 // Prune [copies] by removing self-copies.
1131 List<Copy> prunedCopies = <Copy>[];
1132 for (Copy copy in copies) {
1133 if (copy.source != copy.destination) {
1134 prunedCopies.add(copy);
1135 }
1136 }
1137 copies = prunedCopies;
1138
1139
1140 // For each copy, set the current location of the source to
1141 // itself, and the initial value of the destination to the source.
1142 // Add the destination to the list of copies to make.
1143 for (Copy copy in copies) {
1144 currentLocation[copy.source] = copy.source;
1145 initialValue[copy.destination] = copy.source;
1146 worklist.add(copy.destination);
1147 }
1148
1149 // For each copy, if the destination does not have a current
1150 // location, then we can safely assign to it.
1151 for (Copy copy in copies) {
1152 if (currentLocation[copy.destination] == null) {
1153 ready.add(copy.destination);
1154 }
1155 }
1156
1157 while (!worklist.isEmpty) {
1158 while (!ready.isEmpty) {
1159 String destination = ready.removeLast();
1160 String source = initialValue[destination];
1161 // Since [source] might have been updated, use the current
1162 // location of [source]
1163 String copy = currentLocation[source];
1164 doAssignment(destination, copy);
1165 // Now [destination] is the current location of [source].
1166 currentLocation[source] = destination;
1167 // If [source] hasn't been updated and needs to have a value,
1168 // add it to the list of variables that can be updated. Copies
1169 // of [source] will now use [destination].
1170 if (source == copy && initialValue[source] != null) {
1171 ready.add(source);
1172 }
1173 }
1174
1175 // Check if we have a cycle.
1176 String current = worklist.removeLast();
1177 // If [current] is used as a source, and the assignment has been
1178 // done, we are done with this variable. Otherwise there is a
1179 // cycle that we break by using a temporary name.
1180 if (currentLocation[current] != null
1181 && current != currentLocation[initialValue[current]]) {
1182 doAssignment(tempName, current);
1183 currentLocation[current] = tempName;
1184 // [current] can now be safely updated. Copies of [current]
1185 // will now use [tempName].
1186 ready.add(current);
1187 }
1188 }
1189 }
1190
1191 void assignPhisOfSuccessors(HBasicBlock node) {
1192 CopyHandler handler = variableNames.getCopyHandler(node);
1193 if (handler == null) return;
1194
1195 // Map the instructions to strings.
1196 Iterable<Copy> copies = handler.copies.map((Copy copy) {
1197 return new Copy(variableNames.getName(copy.source),
1198 variableNames.getName(copy.destination));
1199 });
1200
1201 sequentializeCopies(copies, variableNames.getSwapTemp(), emitAssignment);
1202
1203 for (Copy copy in handler.assignments) {
1204 String name = variableNames.getName(copy.destination);
1205 use(copy.source);
1206 assignVariable(name, pop());
1207 }
1208 }
1209
1210 void iterateBasicBlock(HBasicBlock node) {
1211 HInstruction instruction = node.first;
1212 while (!identical(instruction, node.last)) {
1213 if (!isGenerateAtUseSite(instruction)) {
1214 define(instruction);
1215 }
1216 instruction = instruction.next;
1217 }
1218 assignPhisOfSuccessors(node);
1219 visit(instruction);
1220 }
1221
1222 visitInvokeBinary(HInvokeBinary node, String op) {
1223 use(node.left);
1224 js.Expression jsLeft = pop();
1225 use(node.right);
1226 push(new js.Binary(op, jsLeft, pop()), node);
1227 }
1228
1229 visitRelational(HRelational node, String op) => visitInvokeBinary(node, op);
1230
1231 // We want the outcome of bit-operations to be positive. We use the unsigned
1232 // shift operator to achieve this.
1233 visitBitInvokeBinary(HBinaryBitOp node, String op) {
1234 visitInvokeBinary(node, op);
1235 if (op != '>>>' && requiresUintConversion(node)) {
1236 push(new js.Binary(">>>", pop(), new js.LiteralNumber("0")), node);
1237 }
1238 }
1239
1240 visitInvokeUnary(HInvokeUnary node, String op) {
1241 use(node.operand);
1242 push(new js.Prefix(op, pop()), node);
1243 }
1244
1245 // We want the outcome of bit-operations to be positive. We use the unsigned
1246 // shift operator to achieve this.
1247 visitBitInvokeUnary(HInvokeUnary node, String op) {
1248 visitInvokeUnary(node, op);
1249 if (requiresUintConversion(node)) {
1250 push(new js.Binary(">>>", pop(), new js.LiteralNumber("0")), node);
1251 }
1252 }
1253
1254 void emitIdentityComparison(HIdentity instruction, bool inverse) {
1255 String op = instruction.singleComparisonOp;
1256 HInstruction left = instruction.left;
1257 HInstruction right = instruction.right;
1258 if (op != null) {
1259 use(left);
1260 js.Expression jsLeft = pop();
1261 use(right);
1262 push(new js.Binary(mapRelationalOperator(op, inverse), jsLeft, pop()));
1263 } else {
1264 assert(NullConstantValue.JsNull == 'null');
1265 use(left);
1266 js.Binary leftEqualsNull =
1267 new js.Binary("==", pop(), new js.LiteralNull());
1268 use(right);
1269 js.Binary rightEqualsNull =
1270 new js.Binary(mapRelationalOperator("==", inverse),
1271 pop(), new js.LiteralNull());
1272 use(right);
1273 use(left);
1274 js.Binary tripleEq = new js.Binary(mapRelationalOperator("===", inverse),
1275 pop(), pop());
1276
1277 push(new js.Conditional(leftEqualsNull, rightEqualsNull, tripleEq));
1278 }
1279 }
1280
1281 visitIdentity(HIdentity node) {
1282 emitIdentityComparison(node, false);
1283 }
1284
1285 visitAdd(HAdd node) => visitInvokeBinary(node, '+');
1286 visitDivide(HDivide node) => visitInvokeBinary(node, '/');
1287 visitMultiply(HMultiply node) => visitInvokeBinary(node, '*');
1288 visitSubtract(HSubtract node) => visitInvokeBinary(node, '-');
1289 visitBitAnd(HBitAnd node) => visitBitInvokeBinary(node, '&');
1290 visitBitNot(HBitNot node) => visitBitInvokeUnary(node, '~');
1291 visitBitOr(HBitOr node) => visitBitInvokeBinary(node, '|');
1292 visitBitXor(HBitXor node) => visitBitInvokeBinary(node, '^');
1293 visitShiftLeft(HShiftLeft node) => visitBitInvokeBinary(node, '<<');
1294 visitShiftRight(HShiftRight node) => visitBitInvokeBinary(node, '>>>');
1295
1296 visitTruncatingDivide(HTruncatingDivide node) {
1297 assert(node.left.isUInt31(compiler));
1298 assert(node.right.isPositiveInteger(compiler));
1299 use(node.left);
1300 js.Expression jsLeft = pop();
1301 use(node.right);
1302 push(new js.Binary('/', jsLeft, pop()), node);
1303 push(new js.Binary('|', pop(), new js.LiteralNumber("0")), node);
1304 }
1305
1306 visitNegate(HNegate node) => visitInvokeUnary(node, '-');
1307
1308 visitLess(HLess node) => visitRelational(node, '<');
1309 visitLessEqual(HLessEqual node) => visitRelational(node, '<=');
1310 visitGreater(HGreater node) => visitRelational(node, '>');
1311 visitGreaterEqual(HGreaterEqual node) => visitRelational(node, '>=');
1312
1313 visitBoolify(HBoolify node) {
1314 assert(node.inputs.length == 1);
1315 use(node.inputs[0]);
1316 push(new js.Binary('===', pop(), newLiteralBool(true)), node);
1317 }
1318
1319 visitExit(HExit node) {
1320 // Don't do anything.
1321 }
1322
1323 visitGoto(HGoto node) {
1324 HBasicBlock block = node.block;
1325 assert(block.successors.length == 1);
1326 List<HBasicBlock> dominated = block.dominatedBlocks;
1327 // With the exception of the entry-node which dominates its successor
1328 // and the exit node, no block finishing with a 'goto' can have more than
1329 // one dominated block (since it has only one successor).
1330 // If the successor is dominated by another block, then the other block
1331 // is responsible for visiting the successor.
1332 if (dominated.isEmpty) return;
1333 if (dominated.length > 2) {
1334 compiler.internalError(node, 'dominated.length = ${dominated.length}');
1335 }
1336 if (dominated.length == 2 && block != currentGraph.entry) {
1337 compiler.internalError(node, 'node.block != currentGraph.entry');
1338 }
1339 assert(dominated[0] == block.successors[0]);
1340 visitBasicBlock(dominated[0]);
1341 }
1342
1343 visitLoopBranch(HLoopBranch node) {
1344 assert(node.block == subGraph.end);
1345 // We are generating code for a loop condition.
1346 // If we are generating the subgraph as an expression, the
1347 // condition will be generated as the expression.
1348 // Otherwise, we don't generate the expression, and leave that
1349 // to the code that called [visitSubGraph].
1350 if (isGeneratingExpression) {
1351 use(node.inputs[0]);
1352 }
1353 }
1354
1355 /**
1356 * Checks if [map] contains an [EntityAction] for [entity], and
1357 * if so calls that action and returns true.
1358 * Otherwise returns false.
1359 */
1360 bool tryCallAction(Map<Entity, EntityAction> map, Entity entity) {
1361 EntityAction action = map[entity];
1362 if (action == null) return false;
1363 action(entity);
1364 return true;
1365 }
1366
1367 visitBreak(HBreak node) {
1368 assert(node.block.successors.length == 1);
1369 if (node.label != null) {
1370 LabelDefinition label = node.label;
1371 if (!tryCallAction(breakAction, label)) {
1372 pushStatement(new js.Break(backend.namer.breakLabelName(label)), node);
1373 }
1374 } else {
1375 JumpTarget target = node.target;
1376 if (!tryCallAction(breakAction, target)) {
1377 if (node.breakSwitchContinueLoop) {
1378 pushStatement(new js.Break(
1379 backend.namer.implicitContinueLabelName(target)), node);
1380 } else {
1381 pushStatement(new js.Break(null), node);
1382 }
1383 }
1384 }
1385 }
1386
1387 visitContinue(HContinue node) {
1388 assert(node.block.successors.length == 1);
1389 if (node.label != null) {
1390 LabelDefinition label = node.label;
1391 if (!tryCallAction(continueAction, label)) {
1392 // TODO(floitsch): should this really be the breakLabelName?
1393 pushStatement(new js.Continue(backend.namer.breakLabelName(label)),
1394 node);
1395 }
1396 } else {
1397 JumpTarget target = node.target;
1398 if (!tryCallAction(continueAction, target)) {
1399 if (target.statement is ast.SwitchStatement) {
1400 pushStatement(new js.Continue(
1401 backend.namer.implicitContinueLabelName(target)), node);
1402 } else {
1403 pushStatement(new js.Continue(null), node);
1404 }
1405 }
1406 }
1407 }
1408
1409 visitExitTry(HExitTry node) {
1410 // An [HExitTry] is used to represent the control flow graph of a
1411 // try/catch block, ie the try body is always a predecessor
1412 // of the catch and finally. Here, we continue visiting the try
1413 // body by visiting the block that contains the user-level control
1414 // flow instruction.
1415 visitBasicBlock(node.bodyTrySuccessor);
1416 }
1417
1418 visitTry(HTry node) {
1419 // We should never get here. Try/catch/finally is always handled using block
1420 // information in [visitTryInfo].
1421 compiler.internalError(node, 'visitTry should not be called.');
1422 }
1423
1424 bool tryControlFlowOperation(HIf node) {
1425 if (!controlFlowOperators.contains(node)) return false;
1426 HPhi phi = node.joinBlock.phis.first;
1427 bool atUseSite = isGenerateAtUseSite(phi);
1428 // Don't generate a conditional operator in this situation:
1429 // i = condition ? bar() : i;
1430 // But generate this instead:
1431 // if (condition) i = bar();
1432 // Usually, the variable name is longer than 'if' and it takes up
1433 // more space to duplicate the name.
1434 if (!atUseSite
1435 && variableNames.getName(phi) == variableNames.getName(phi.inputs[1])) {
1436 return false;
1437 }
1438 if (!atUseSite) define(phi);
1439 visitBasicBlock(node.joinBlock);
1440 return true;
1441 }
1442
1443 void generateIf(HIf node, HIfBlockInformation info) {
1444 use(node.inputs[0]);
1445 js.Expression test = pop();
1446
1447 HStatementInformation thenGraph = info.thenGraph;
1448 HStatementInformation elseGraph = info.elseGraph;
1449 js.Statement thenPart =
1450 unwrapStatement(generateStatementsInNewBlock(thenGraph));
1451 js.Statement elsePart =
1452 unwrapStatement(generateStatementsInNewBlock(elseGraph));
1453
1454 pushStatement(new js.If(test, thenPart, elsePart), node);
1455 }
1456
1457 visitIf(HIf node) {
1458 if (tryControlFlowOperation(node)) return;
1459
1460 HInstruction condition = node.inputs[0];
1461 HIfBlockInformation info = node.blockInformation.body;
1462
1463 if (condition.isConstant()) {
1464 HConstant constant = condition;
1465 if (constant.constant.isTrue) {
1466 generateStatements(info.thenGraph);
1467 } else {
1468 generateStatements(info.elseGraph);
1469 }
1470 } else {
1471 generateIf(node, info);
1472 }
1473
1474 HBasicBlock joinBlock = node.joinBlock;
1475 if (joinBlock != null && !identical(joinBlock.dominator, node.block)) {
1476 // The join block is dominated by a block in one of the branches.
1477 // The subgraph traversal never reached it, so we visit it here
1478 // instead.
1479 visitBasicBlock(joinBlock);
1480 }
1481
1482 // Visit all the dominated blocks that are not part of the then or else
1483 // branches, and is not the join block.
1484 // Depending on how the then/else branches terminate
1485 // (e.g., return/throw/break) there can be any number of these.
1486 List<HBasicBlock> dominated = node.block.dominatedBlocks;
1487 for (int i = 2; i < dominated.length; i++) {
1488 visitBasicBlock(dominated[i]);
1489 }
1490 }
1491
1492 js.Call jsPropertyCall(js.Expression receiver,
1493 String fieldName,
1494 List<js.Expression> arguments) {
1495 return new js.Call(new js.PropertyAccess.field(receiver, fieldName),
1496 arguments);
1497 }
1498
1499 void visitInterceptor(HInterceptor node) {
1500 registry.registerSpecializedGetInterceptor(node.interceptedClasses);
1501 String name = backend.namer.getInterceptorName(
1502 backend.getInterceptorMethod, node.interceptedClasses);
1503 var isolate = new js.VariableUse(
1504 backend.namer.globalObjectFor(backend.interceptorsLibrary));
1505 use(node.receiver);
1506 List<js.Expression> arguments = <js.Expression>[pop()];
1507 push(jsPropertyCall(isolate, name, arguments), node);
1508 registry.registerUseInterceptor();
1509 }
1510
1511 visitInvokeDynamicMethod(HInvokeDynamicMethod node) {
1512 use(node.receiver);
1513 js.Expression object = pop();
1514 String name = node.selector.name;
1515 String methodName;
1516 List<js.Expression> arguments = visitArguments(node.inputs);
1517 Element target = node.element;
1518
1519 if (target != null && !node.isInterceptedCall) {
1520 if (target == backend.jsArrayAdd) {
1521 methodName = 'push';
1522 } else if (target == backend.jsArrayRemoveLast) {
1523 methodName = 'pop';
1524 } else if (target == backend.jsStringSplit) {
1525 methodName = 'split';
1526 // Split returns a List, so we make sure the backend knows the
1527 // list class is instantiated.
1528 registry.registerInstantiatedClass(compiler.listClass);
1529 } else if (target.isNative && target.isFunction
1530 && !node.isInterceptedCall) {
1531 // A direct (i.e. non-interceptor) native call is the result of
1532 // optimization. The optimization ensures any type checks or
1533 // conversions have been satisified.
1534 methodName = target.fixedBackendName;
1535 }
1536 }
1537
1538 if (methodName == null) {
1539 methodName = backend.namer.invocationName(node.selector);
1540 registerMethodInvoke(node);
1541 }
1542 push(jsPropertyCall(object, methodName, arguments), node);
1543 }
1544
1545 void visitInvokeConstructorBody(HInvokeConstructorBody node) {
1546 use(node.inputs[0]);
1547 js.Expression object = pop();
1548 String methodName = backend.namer.getNameOfInstanceMember(node.element);
1549 List<js.Expression> arguments = visitArguments(node.inputs);
1550 push(jsPropertyCall(object, methodName, arguments), node);
1551 registry.registerStaticUse(node.element);
1552 }
1553
1554 void visitOneShotInterceptor(HOneShotInterceptor node) {
1555 List<js.Expression> arguments = visitArguments(node.inputs);
1556 var isolate = new js.VariableUse(
1557 backend.namer.globalObjectFor(backend.interceptorsLibrary));
1558 Selector selector = getOptimizedSelectorFor(node, node.selector);
1559 String methodName = backend.registerOneShotInterceptor(selector);
1560 push(jsPropertyCall(isolate, methodName, arguments), node);
1561 if (selector.isGetter) {
1562 registerGetter(node);
1563 } else if (selector.isSetter) {
1564 registerSetter(node);
1565 } else {
1566 registerMethodInvoke(node);
1567 }
1568 registry.registerUseInterceptor();
1569 }
1570
1571 Selector getOptimizedSelectorFor(HInvokeDynamic node, Selector selector) {
1572 if (node.element != null) {
1573 // Create an artificial type mask to make sure only
1574 // [node.element] will be enqueued. We're not using the receiver
1575 // type because our optimizations might end up in a state where the
1576 // invoke dynamic knows more than the receiver.
1577 ClassElement enclosing = node.element.enclosingClass;
1578 TypeMask receiverType =
1579 new TypeMask.nonNullExact(enclosing.declaration, compiler.world);
1580 return new TypedSelector(receiverType, selector, compiler.world);
1581 }
1582 // If [JSInvocationMirror._invokeOn] is enabled, and this call
1583 // might hit a `noSuchMethod`, we register an untyped selector.
1584 return selector.extendIfReachesAll(compiler);
1585 }
1586
1587 void registerMethodInvoke(HInvokeDynamic node) {
1588 Selector selector = getOptimizedSelectorFor(node, node.selector);
1589
1590 // If we don't know what we're calling or if we are calling a getter,
1591 // we need to register that fact that we may be calling a closure
1592 // with the same arguments.
1593 Element target = node.element;
1594 if (target == null || target.isGetter) {
1595 // TODO(kasperl): If we have a typed selector for the call, we
1596 // may know something about the types of closures that need
1597 // the specific closure call method.
1598 Selector call = new Selector.callClosureFrom(selector);
1599 registry.registerDynamicInvocation(call);
1600 }
1601 registry.registerDynamicInvocation(selector);
1602 }
1603
1604 void registerSetter(HInvokeDynamic node) {
1605 Selector selector = getOptimizedSelectorFor(node, node.selector);
1606 registry.registerDynamicSetter(selector);
1607 }
1608
1609 void registerGetter(HInvokeDynamic node) {
1610 Selector selector = getOptimizedSelectorFor(node, node.selector);
1611 registry.registerDynamicGetter(selector);
1612 }
1613
1614 visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
1615 use(node.receiver);
1616 String name = backend.namer.invocationName(node.selector);
1617 push(jsPropertyCall(pop(), name, visitArguments(node.inputs)), node);
1618 registerSetter(node);
1619 }
1620
1621 visitInvokeDynamicGetter(HInvokeDynamicGetter node) {
1622 use(node.receiver);
1623 String name = backend.namer.invocationName(node.selector);
1624 push(jsPropertyCall(pop(), name, visitArguments(node.inputs)), node);
1625 registerGetter(node);
1626 }
1627
1628 visitInvokeClosure(HInvokeClosure node) {
1629 Selector call = new Selector.callClosureFrom(node.selector);
1630 use(node.receiver);
1631 push(jsPropertyCall(pop(),
1632 backend.namer.invocationName(call),
1633 visitArguments(node.inputs)),
1634 node);
1635 registry.registerDynamicInvocation(call);
1636 }
1637
1638 visitInvokeStatic(HInvokeStatic node) {
1639 Element element = node.element;
1640 ClassElement cls = element.enclosingClass;
1641 List<DartType> instantiatedTypes = node.instantiatedTypes;
1642
1643 registry.registerStaticUse(element);
1644
1645 if (instantiatedTypes != null && !instantiatedTypes.isEmpty) {
1646 instantiatedTypes.forEach((type) {
1647 registry.registerInstantiatedType(type);
1648 });
1649 }
1650
1651 push(backend.namer.elementAccess(node.element));
1652 push(new js.Call(pop(), visitArguments(node.inputs, start: 0)), node);
1653 }
1654
1655 visitInvokeSuper(HInvokeSuper node) {
1656 Element superMethod = node.element;
1657 registry.registerStaticUse(superMethod);
1658 ClassElement superClass = superMethod.enclosingClass;
1659 if (superMethod.kind == ElementKind.FIELD) {
1660 String fieldName = backend.namer.instanceFieldPropertyName(superMethod);
1661 use(node.inputs[0]);
1662 js.PropertyAccess access =
1663 new js.PropertyAccess.field(pop(), fieldName);
1664 if (node.isSetter) {
1665 use(node.value);
1666 push(new js.Assignment(access, pop()), node);
1667 } else {
1668 push(access, node);
1669 }
1670 } else {
1671 Selector selector = node.selector;
1672 String methodName;
1673 if (selector.isGetter) {
1674 // If the selector we need to register a typed getter to the
1675 // [world]. The emitter needs to know if it needs to emit a
1676 // bound closure for a method.
1677 TypeMask receiverType =
1678 new TypeMask.nonNullExact(superClass, compiler.world);
1679 selector = new TypedSelector(receiverType, selector, compiler.world);
1680 // TODO(floitsch): we know the target. We shouldn't register a
1681 // dynamic getter.
1682 registry.registerDynamicGetter(selector);
1683 registry.registerGetterForSuperMethod(node.element);
1684 methodName = backend.namer.invocationName(selector);
1685 } else {
1686 methodName = backend.namer.getNameOfInstanceMember(superMethod);
1687 }
1688 push(
1689 js.js('#.prototype.#.call(#)', [
1690 backend.namer.elementAccess(superClass),
1691 methodName, visitArguments(node.inputs, start: 0)]),
1692 node);
1693 }
1694 }
1695
1696 visitFieldGet(HFieldGet node) {
1697 use(node.receiver);
1698 Element element = node.element;
1699 if (node.isNullCheck) {
1700 // We access a JavaScript member we know all objects besides
1701 // null and undefined have: V8 does not like accessing a member
1702 // that does not exist.
1703 push(new js.PropertyAccess.field(pop(), 'toString'), node);
1704 } else if (element == backend.jsIndexableLength) {
1705 // We're accessing a native JavaScript property called 'length'
1706 // on a JS String or a JS array. Therefore, the name of that
1707 // property should not be mangled.
1708 push(new js.PropertyAccess.field(pop(), 'length'), node);
1709 } else {
1710 String name = backend.namer.instanceFieldPropertyName(element);
1711 push(new js.PropertyAccess.field(pop(), name), node);
1712 registry.registerFieldGetter(element);
1713 }
1714 }
1715
1716 visitFieldSet(HFieldSet node) {
1717 Element element = node.element;
1718 registry.registerFieldSetter(element);
1719 String name = backend.namer.instanceFieldPropertyName(element);
1720 use(node.receiver);
1721 js.Expression receiver = pop();
1722 use(node.value);
1723 push(new js.Assignment(new js.PropertyAccess.field(receiver, name), pop()),
1724 node);
1725 }
1726
1727 visitReadModifyWrite(HReadModifyWrite node) {
1728 Element element = node.element;
1729 registry.registerFieldSetter(element);
1730 String name = backend.namer.instanceFieldPropertyName(element);
1731 use(node.receiver);
1732 js.Expression fieldReference = new js.PropertyAccess.field(pop(), name);
1733 if (node.isPreOp) {
1734 push(new js.Prefix(node.jsOp, fieldReference), node);
1735 } else if (node.isPostOp) {
1736 push(new js.Postfix(node.jsOp, fieldReference), node);
1737 } else {
1738 use(node.value);
1739 push(new js.Assignment.compound(fieldReference, node.jsOp, pop()), node);
1740 }
1741 }
1742
1743 visitLocalGet(HLocalGet node) {
1744 use(node.receiver);
1745 }
1746
1747 visitLocalSet(HLocalSet node) {
1748 use(node.value);
1749 assignVariable(variableNames.getName(node.receiver), pop());
1750 }
1751
1752 void registerForeignTypes(HForeign node) {
1753 native.NativeBehavior nativeBehavior = node.nativeBehavior;
1754 if (nativeBehavior == null) return;
1755 nativeBehavior.typesReturned.forEach((type) {
1756 if (type is InterfaceType) {
1757 registry.registerInstantiatedType(type);
1758 }
1759 });
1760 }
1761
1762 visitForeign(HForeign node) {
1763 List<HInstruction> inputs = node.inputs;
1764 if (node.isJsStatement()) {
1765 List<js.Expression> interpolatedExpressions = <js.Expression>[];
1766 for (int i = 0; i < inputs.length; i++) {
1767 use(inputs[i]);
1768 interpolatedExpressions.add(pop());
1769 }
1770 pushStatement(node.codeTemplate.instantiate(interpolatedExpressions));
1771 } else {
1772 List<js.Expression> interpolatedExpressions = <js.Expression>[];
1773 for (int i = 0; i < inputs.length; i++) {
1774 use(inputs[i]);
1775 interpolatedExpressions.add(pop());
1776 }
1777 push(node.codeTemplate.instantiate(interpolatedExpressions));
1778 }
1779
1780 // TODO(sra): Tell world.nativeEnqueuer about the types created here.
1781 registerForeignTypes(node);
1782 }
1783
1784 visitForeignNew(HForeignNew node) {
1785 js.Expression jsClassReference = backend.namer.elementAccess(node.element);
1786 List<js.Expression> arguments = visitArguments(node.inputs, start: 0);
1787 push(new js.New(jsClassReference, arguments), node);
1788 registerForeignTypes(node);
1789 if (node.instantiatedTypes == null) {
1790 return;
1791 }
1792 node.instantiatedTypes.forEach((type) {
1793 registry.registerInstantiatedType(type);
1794 });
1795 }
1796
1797 js.Expression newLiteralBool(bool value) {
1798 if (compiler.enableMinification) {
1799 // Use !0 for true, !1 for false.
1800 return new js.Prefix("!", new js.LiteralNumber(value ? "0" : "1"));
1801 } else {
1802 return new js.LiteralBool(value);
1803 }
1804 }
1805
1806 void generateConstant(ConstantValue constant) {
1807 if (constant.isFunction) {
1808 FunctionConstantValue function = constant;
1809 registry.registerStaticUse(function.element);
1810 }
1811 if (constant.isType) {
1812 // If the type is a web component, we need to ensure the constructors are
1813 // available to 'upgrade' the native object.
1814 TypeConstantValue type = constant;
1815 Element element = type.representedType.element;
1816 if (element != null && element.isClass) {
1817 registry.registerTypeConstant(element);
1818 }
1819 }
1820 push(backend.emitter.constantReference(constant));
1821 }
1822
1823 visitConstant(HConstant node) {
1824 assert(isGenerateAtUseSite(node));
1825 generateConstant(node.constant);
1826
1827 registry.registerCompileTimeConstant(node.constant);
1828 backend.constants.addCompileTimeConstantForEmission(node.constant);
1829 }
1830
1831 visitNot(HNot node) {
1832 assert(node.inputs.length == 1);
1833 generateNot(node.inputs[0]);
1834 attachLocationToLast(node);
1835 }
1836
1837 static String mapRelationalOperator(String op, bool inverse) {
1838 Map<String, String> inverseOperator = const <String, String>{
1839 "==" : "!=",
1840 "!=" : "==",
1841 "===": "!==",
1842 "!==": "===",
1843 "<" : ">=",
1844 "<=" : ">",
1845 ">" : "<=",
1846 ">=" : "<"
1847 };
1848 return inverse ? inverseOperator[op] : op;
1849 }
1850
1851 void generateNot(HInstruction input) {
1852 bool canGenerateOptimizedComparison(HInstruction instruction) {
1853 if (instruction is !HRelational) return false;
1854
1855 HRelational relational = instruction;
1856 BinaryOperation operation = relational.operation(backend.constantSystem);
1857
1858 HInstruction left = relational.left;
1859 HInstruction right = relational.right;
1860 if (left.isStringOrNull(compiler) && right.isStringOrNull(compiler)) {
1861 return true;
1862 }
1863
1864 // This optimization doesn't work for NaN, so we only do it if the
1865 // type is known to be an integer.
1866 return left.isInteger(compiler) && right.isInteger(compiler);
1867 }
1868
1869 bool handledBySpecialCase = false;
1870 if (isGenerateAtUseSite(input)) {
1871 handledBySpecialCase = true;
1872 if (input is HIs) {
1873 emitIs(input, '!==');
1874 } else if (input is HIsViaInterceptor) {
1875 emitIsViaInterceptor(input, true);
1876 } else if (input is HNot) {
1877 use(input.inputs[0]);
1878 } else if (input is HIdentity) {
1879 emitIdentityComparison(input, true);
1880 } else if (input is HBoolify) {
1881 use(input.inputs[0]);
1882 push(new js.Binary("!==", pop(), newLiteralBool(true)), input);
1883 } else if (canGenerateOptimizedComparison(input)) {
1884 HRelational relational = input;
1885 BinaryOperation operation =
1886 relational.operation(backend.constantSystem);
1887 String op = mapRelationalOperator(operation.name, true);
1888 visitRelational(input, op);
1889 } else {
1890 handledBySpecialCase = false;
1891 }
1892 }
1893 if (!handledBySpecialCase) {
1894 use(input);
1895 push(new js.Prefix("!", pop()));
1896 }
1897 }
1898
1899 visitParameterValue(HParameterValue node) {
1900 assert(!isGenerateAtUseSite(node));
1901 String name = variableNames.getName(node);
1902 parameters.add(new js.Parameter(name));
1903 declaredLocals.add(name);
1904 }
1905
1906 visitLocalValue(HLocalValue node) {
1907 assert(!isGenerateAtUseSite(node));
1908 String name = variableNames.getName(node);
1909 collectedVariableDeclarations.add(name);
1910 }
1911
1912 visitPhi(HPhi node) {
1913 // This method is only called for phis that are generated at use
1914 // site. A phi can be generated at use site only if it is the
1915 // result of a control flow operation.
1916 HBasicBlock ifBlock = node.block.dominator;
1917 assert(controlFlowOperators.contains(ifBlock.last));
1918 HInstruction input = ifBlock.last.inputs[0];
1919 if (input.isConstantFalse()) {
1920 use(node.inputs[1]);
1921 } else if (input.isConstantTrue()) {
1922 use(node.inputs[0]);
1923 } else if (node.inputs[1].isConstantBoolean()) {
1924 String operation = node.inputs[1].isConstantFalse() ? '&&' : '||';
1925 if (operation == '||') {
1926 generateNot(input);
1927 } else {
1928 use(input);
1929 }
1930 js.Expression left = pop();
1931 use(node.inputs[0]);
1932 push(new js.Binary(operation, left, pop()));
1933 } else {
1934 use(input);
1935 js.Expression test = pop();
1936 use(node.inputs[0]);
1937 js.Expression then = pop();
1938 use(node.inputs[1]);
1939 push(new js.Conditional(test, then, pop()));
1940 }
1941 }
1942
1943 visitReturn(HReturn node) {
1944 assert(node.inputs.length == 1);
1945 HInstruction input = node.inputs[0];
1946 if (input.isConstantNull()) {
1947 pushStatement(new js.Return(null), node);
1948 } else {
1949 use(node.inputs[0]);
1950 pushStatement(new js.Return(pop()), node);
1951 }
1952 }
1953
1954 visitThis(HThis node) {
1955 push(new js.This());
1956 }
1957
1958 visitThrow(HThrow node) {
1959 if (node.isRethrow) {
1960 use(node.inputs[0]);
1961 pushStatement(new js.Throw(pop()), node);
1962 } else {
1963 generateThrowWithHelper('wrapException', node.inputs[0]);
1964 }
1965 }
1966
1967 visitRangeConversion(HRangeConversion node) {
1968 // Range conversion instructions are removed by the value range
1969 // analyzer.
1970 assert(false);
1971 }
1972
1973 visitBoundsCheck(HBoundsCheck node) {
1974 // TODO(ngeoffray): Separate the two checks of the bounds check, so,
1975 // e.g., the zero checks can be shared if possible.
1976
1977 // If the checks always succeeds, we would have removed the bounds check
1978 // completely.
1979 assert(node.staticChecks != HBoundsCheck.ALWAYS_TRUE);
1980 if (node.staticChecks != HBoundsCheck.ALWAYS_FALSE) {
1981 js.Expression under;
1982 js.Expression over;
1983 if (node.staticChecks != HBoundsCheck.ALWAYS_ABOVE_ZERO) {
1984 use(node.index);
1985 if (node.index.isInteger(compiler)) {
1986 under = js.js("# < 0", pop());
1987 } else {
1988 js.Expression jsIndex = pop();
1989 under = js.js("# >>> 0 !== #", [jsIndex, jsIndex]);
1990 }
1991 } else if (!node.index.isInteger(compiler)) {
1992 checkInt(node.index, '!==');
1993 under = pop();
1994 }
1995 if (node.staticChecks != HBoundsCheck.ALWAYS_BELOW_LENGTH) {
1996 var index = node.index;
1997 use(index);
1998 js.Expression jsIndex = pop();
1999 use(node.length);
2000 over = new js.Binary(">=", jsIndex, pop());
2001 }
2002 assert(over != null || under != null);
2003 js.Expression underOver = under == null
2004 ? over
2005 : over == null
2006 ? under
2007 : new js.Binary("||", under, over);
2008 js.Statement thenBody = new js.Block.empty();
2009 js.Block oldContainer = currentContainer;
2010 currentContainer = thenBody;
2011 generateThrowWithHelper('ioore', [node.array, node.index]);
2012 currentContainer = oldContainer;
2013 thenBody = unwrapStatement(thenBody);
2014 pushStatement(new js.If.noElse(underOver, thenBody), node);
2015 } else {
2016 generateThrowWithHelper('ioore', [node.array, node.index]);
2017 }
2018 }
2019
2020 void generateThrowWithHelper(String helperName, argument) {
2021 Element helper = backend.findHelper(helperName);
2022 registry.registerStaticUse(helper);
2023 js.Expression jsHelper = backend.namer.elementAccess(helper);
2024 List arguments = [];
2025 var location;
2026 if (argument is List) {
2027 location = argument[0];
2028 argument.forEach((instruction) {
2029 use(instruction);
2030 arguments.add(pop());
2031 });
2032 } else {
2033 location = argument;
2034 use(argument);
2035 arguments.add(pop());
2036 }
2037 js.Call value = new js.Call(jsHelper, arguments.toList(growable: false));
2038 value = attachLocation(value, location);
2039 // BUG(4906): Using throw/return here adds to the size of the generated code
2040 // but it has the advantage of explicitly telling the JS engine that
2041 // this code path will terminate abruptly. Needs more work.
2042 if (helperName == 'wrapException') {
2043 pushStatement(new js.Throw(value));
2044 } else {
2045 pushStatement(new js.Return(value));
2046 }
2047 }
2048
2049 visitThrowExpression(HThrowExpression node) {
2050 HInstruction argument = node.inputs[0];
2051 use(argument);
2052
2053 Element helper = backend.findHelper("throwExpression");
2054 registry.registerStaticUse(helper);
2055
2056 js.Expression jsHelper = backend.namer.elementAccess(helper);
2057 js.Call value = new js.Call(jsHelper, [pop()]);
2058 value = attachLocation(value, argument);
2059 push(value, node);
2060 }
2061
2062 void visitSwitch(HSwitch node) {
2063 // Switches are handled using [visitSwitchInfo].
2064 }
2065
2066 void visitStatic(HStatic node) {
2067 Element element = node.element;
2068 if (element.isFunction) {
2069 push(backend.namer.isolateStaticClosureAccess(node.element));
2070 } else {
2071 push(backend.namer.elementAccess(node.element));
2072 }
2073 registry.registerStaticUse(element);
2074 }
2075
2076 void visitLazyStatic(HLazyStatic node) {
2077 Element element = node.element;
2078 registry.registerStaticUse(element);
2079 js.Expression lazyGetter =
2080 backend.namer.isolateLazyInitializerAccess(element);
2081 js.Call call = new js.Call(lazyGetter, <js.Expression>[]);
2082 push(call, node);
2083 }
2084
2085 void visitStaticStore(HStaticStore node) {
2086 registry.registerStaticUse(node.element);
2087 js.Node variable = backend.namer.elementAccess(node.element);
2088 use(node.inputs[0]);
2089 push(new js.Assignment(variable, pop()), node);
2090 }
2091
2092 void visitStringConcat(HStringConcat node) {
2093 use(node.left);
2094 js.Expression jsLeft = pop();
2095 use(node.right);
2096 push(new js.Binary('+', jsLeft, pop()), node);
2097 }
2098
2099 void visitStringify(HStringify node) {
2100 HInstruction input = node.inputs.first;
2101 if (input.isString(compiler)) {
2102 use(input);
2103 } else if (input.isInteger(compiler) || input.isBoolean(compiler)) {
2104 // JavaScript's + operator with a string for the left operand will convert
2105 // the right operand to a string, and the conversion result is correct.
2106 use(input);
2107 if (node.usedBy.length == 1
2108 && node.usedBy[0] is HStringConcat
2109 && node.usedBy[0].inputs[1] == node) {
2110 // The context is already <string> + value.
2111 } else {
2112 // Force an empty string for the first operand.
2113 push(new js.Binary('+', js.string(""), pop()), node);
2114 }
2115 } else {
2116 Element convertToString = backend.getStringInterpolationHelper();
2117 registry.registerStaticUse(convertToString);
2118 js.Expression jsHelper = backend.namer.elementAccess(convertToString);
2119 use(input);
2120 push(new js.Call(jsHelper, <js.Expression>[pop()]), node);
2121 }
2122 }
2123
2124 void visitLiteralList(HLiteralList node) {
2125 registry.registerInstantiatedClass(compiler.listClass);
2126 generateArrayLiteral(node);
2127 }
2128
2129 void generateArrayLiteral(HLiteralList node) {
2130 int len = node.inputs.length;
2131 List<js.ArrayElement> elements = <js.ArrayElement>[];
2132 for (int i = 0; i < len; i++) {
2133 use(node.inputs[i]);
2134 elements.add(new js.ArrayElement(i, pop()));
2135 }
2136 push(new js.ArrayInitializer(len, elements), node);
2137 }
2138
2139 void visitIndex(HIndex node) {
2140 use(node.receiver);
2141 js.Expression receiver = pop();
2142 use(node.index);
2143 push(new js.PropertyAccess(receiver, pop()), node);
2144 }
2145
2146 void visitIndexAssign(HIndexAssign node) {
2147 use(node.receiver);
2148 js.Expression receiver = pop();
2149 use(node.index);
2150 js.Expression index = pop();
2151 use(node.value);
2152 push(new js.Assignment(new js.PropertyAccess(receiver, index), pop()),
2153 node);
2154 }
2155
2156 void checkInt(HInstruction input, String cmp) {
2157 use(input);
2158 js.Expression left = pop();
2159 use(input);
2160 js.Expression or0 = new js.Binary("|", pop(), new js.LiteralNumber("0"));
2161 push(new js.Binary(cmp, left, or0));
2162 }
2163
2164 void checkBigInt(HInstruction input, String cmp) {
2165 use(input);
2166 js.Expression left = pop();
2167 use(input);
2168 js.Expression right = pop();
2169 // TODO(4984): Deal with infinity and -0.0.
2170 push(js.js('Math.floor(#) $cmp #', <js.Expression>[left, right]));
2171 }
2172
2173 void checkTypeOf(HInstruction input, String cmp, String typeName) {
2174 use(input);
2175 js.Expression typeOf = new js.Prefix("typeof", pop());
2176 push(new js.Binary(cmp, typeOf, js.string(typeName)));
2177 }
2178
2179 void checkNum(HInstruction input, String cmp)
2180 => checkTypeOf(input, cmp, 'number');
2181
2182 void checkDouble(HInstruction input, String cmp) => checkNum(input, cmp);
2183
2184 void checkString(HInstruction input, String cmp)
2185 => checkTypeOf(input, cmp, 'string');
2186
2187 void checkBool(HInstruction input, String cmp)
2188 => checkTypeOf(input, cmp, 'boolean');
2189
2190 void checkObject(HInstruction input, String cmp) {
2191 assert(NullConstantValue.JsNull == 'null');
2192 if (cmp == "===") {
2193 checkTypeOf(input, '===', 'object');
2194 js.Expression left = pop();
2195 use(input);
2196 js.Expression notNull = new js.Binary("!==", pop(), new js.LiteralNull());
2197 push(new js.Binary("&&", left, notNull));
2198 } else {
2199 assert(cmp == "!==");
2200 checkTypeOf(input, '!==', 'object');
2201 js.Expression left = pop();
2202 use(input);
2203 js.Expression eqNull = new js.Binary("===", pop(), new js.LiteralNull());
2204 push(new js.Binary("||", left, eqNull));
2205 }
2206 }
2207
2208 void checkArray(HInstruction input, String cmp) {
2209 use(input);
2210 js.PropertyAccess constructor =
2211 new js.PropertyAccess.field(pop(), 'constructor');
2212 push(new js.Binary(cmp, constructor, new js.VariableUse('Array')));
2213 }
2214
2215 void checkFieldExists(HInstruction input, String fieldName) {
2216 use(input);
2217 js.PropertyAccess field = new js.PropertyAccess.field(pop(), fieldName);
2218 // Double negate to boolify the result.
2219 push(new js.Prefix('!', new js.Prefix('!', field)));
2220 }
2221
2222 void checkFieldDoesNotExist(HInstruction input, String fieldName) {
2223 use(input);
2224 js.PropertyAccess field = new js.PropertyAccess.field(pop(), fieldName);
2225 push(new js.Prefix('!', field));
2226 }
2227
2228 void checkImmutableArray(HInstruction input) {
2229 checkFieldExists(input, 'immutable\$list');
2230 }
2231
2232 void checkMutableArray(HInstruction input) {
2233 checkFieldDoesNotExist(input, 'immutable\$list');
2234 }
2235
2236 void checkExtendableArray(HInstruction input) {
2237 checkFieldDoesNotExist(input, 'fixed\$length');
2238 }
2239
2240 void checkFixedArray(HInstruction input) {
2241 checkFieldExists(input, 'fixed\$length');
2242 }
2243
2244 void checkNull(HInstruction input) {
2245 use(input);
2246 push(new js.Binary('==', pop(), new js.LiteralNull()));
2247 }
2248
2249 void checkNonNull(HInstruction input) {
2250 use(input);
2251 push(new js.Binary('!=', pop(), new js.LiteralNull()));
2252 }
2253
2254 bool checkIndexingBehavior(HInstruction input, {bool negative: false}) {
2255 if (!compiler.resolverWorld.isInstantiated(
2256 backend.jsIndexingBehaviorInterface)) {
2257 return false;
2258 }
2259
2260 use(input);
2261 js.Expression object1 = pop();
2262 use(input);
2263 js.Expression object2 = pop();
2264 push(backend.generateIsJsIndexableCall(object1, object2));
2265 if (negative) push(new js.Prefix('!', pop()));
2266 return true;
2267 }
2268
2269 void checkType(HInstruction input, HInstruction interceptor,
2270 DartType type, {bool negative: false}) {
2271 Element element = type.element;
2272 if (element == backend.jsArrayClass) {
2273 checkArray(input, negative ? '!==': '===');
2274 return;
2275 } else if (element == backend.jsMutableArrayClass) {
2276 if (negative) {
2277 checkImmutableArray(input);
2278 } else {
2279 checkMutableArray(input);
2280 }
2281 return;
2282 } else if (element == backend.jsExtendableArrayClass) {
2283 if (negative) {
2284 checkFixedArray(input);
2285 } else {
2286 checkExtendableArray(input);
2287 }
2288 return;
2289 } else if (element == backend.jsFixedArrayClass) {
2290 if (negative) {
2291 checkExtendableArray(input);
2292 } else {
2293 checkFixedArray(input);
2294 }
2295 return;
2296 }
2297 if (interceptor != null) {
2298 checkTypeViaProperty(interceptor, type, negative);
2299 } else {
2300 checkTypeViaProperty(input, type, negative);
2301 }
2302 }
2303
2304 void checkTypeViaProperty(HInstruction input, DartType type, bool negative) {
2305 registry.registerIsCheck(type);
2306
2307 use(input);
2308
2309 js.PropertyAccess field =
2310 new js.PropertyAccess.field(pop(), backend.namer.operatorIsType(type));
2311 // We always negate at least once so that the result is boolified.
2312 push(new js.Prefix('!', field));
2313 // If the result is not negated, put another '!' in front.
2314 if (!negative) push(new js.Prefix('!', pop()));
2315 }
2316
2317 void handleNumberOrStringSupertypeCheck(HInstruction input,
2318 HInstruction interceptor,
2319 DartType type,
2320 { bool negative: false }) {
2321 assert(!identical(type.element, compiler.listClass)
2322 && !Elements.isListSupertype(type.element, compiler)
2323 && !Elements.isStringOnlySupertype(type.element, compiler));
2324 String relation = negative ? '!==' : '===';
2325 checkNum(input, relation);
2326 js.Expression numberTest = pop();
2327 checkString(input, relation);
2328 js.Expression stringTest = pop();
2329 checkObject(input, relation);
2330 js.Expression objectTest = pop();
2331 checkType(input, interceptor, type, negative: negative);
2332 String combiner = negative ? '&&' : '||';
2333 String combiner2 = negative ? '||' : '&&';
2334 push(new js.Binary(combiner,
2335 new js.Binary(combiner, numberTest, stringTest),
2336 new js.Binary(combiner2, objectTest, pop())));
2337 }
2338
2339 void handleStringSupertypeCheck(HInstruction input,
2340 HInstruction interceptor,
2341 DartType type,
2342 { bool negative: false }) {
2343 assert(!identical(type.element, compiler.listClass)
2344 && !Elements.isListSupertype(type.element, compiler)
2345 && !Elements.isNumberOrStringSupertype(type.element, compiler));
2346 String relation = negative ? '!==' : '===';
2347 checkString(input, relation);
2348 js.Expression stringTest = pop();
2349 checkObject(input, relation);
2350 js.Expression objectTest = pop();
2351 checkType(input, interceptor, type, negative: negative);
2352 String combiner = negative ? '||' : '&&';
2353 push(new js.Binary(negative ? '&&' : '||',
2354 stringTest,
2355 new js.Binary(combiner, objectTest, pop())));
2356 }
2357
2358 void handleListOrSupertypeCheck(HInstruction input,
2359 HInstruction interceptor,
2360 DartType type,
2361 { bool negative: false }) {
2362 assert(!identical(type.element, compiler.stringClass)
2363 && !Elements.isStringOnlySupertype(type.element, compiler)
2364 && !Elements.isNumberOrStringSupertype(type.element, compiler));
2365 String relation = negative ? '!==' : '===';
2366 checkObject(input, relation);
2367 js.Expression objectTest = pop();
2368 checkArray(input, relation);
2369 js.Expression arrayTest = pop();
2370 checkType(input, interceptor, type, negative: negative);
2371 String combiner = negative ? '&&' : '||';
2372 push(new js.Binary(negative ? '||' : '&&',
2373 objectTest,
2374 new js.Binary(combiner, arrayTest, pop())));
2375 }
2376
2377 void visitIs(HIs node) {
2378 emitIs(node, "===");
2379 }
2380
2381 void visitIsViaInterceptor(HIsViaInterceptor node) {
2382 emitIsViaInterceptor(node, false);
2383 }
2384
2385 void emitIs(HIs node, String relation) {
2386 DartType type = node.typeExpression;
2387 registry.registerIsCheck(type);
2388 HInstruction input = node.expression;
2389
2390 // If this is changed to single == there are several places below that must
2391 // be changed to match.
2392 assert(relation == '===' || relation == '!==');
2393 bool negative = relation == '!==';
2394
2395 if (node.isVariableCheck || node.isCompoundCheck) {
2396 use(node.checkCall);
2397 if (negative) push(new js.Prefix('!', pop()));
2398 } else {
2399 assert(node.isRawCheck);
2400 HInstruction interceptor = node.interceptor;
2401 LibraryElement coreLibrary = compiler.coreLibrary;
2402 ClassElement objectClass = compiler.objectClass;
2403 Element element = type.element;
2404 if (element == compiler.nullClass) {
2405 if (negative) {
2406 checkNonNull(input);
2407 } else {
2408 checkNull(input);
2409 }
2410 } else if (identical(element, objectClass) || type.treatAsDynamic) {
2411 // The constant folder also does this optimization, but we make
2412 // it safe by assuming it may have not run.
2413 push(newLiteralBool(!negative), node);
2414 } else if (element == compiler.stringClass) {
2415 checkString(input, relation);
2416 attachLocationToLast(node);
2417 } else if (element == compiler.doubleClass) {
2418 checkDouble(input, relation);
2419 attachLocationToLast(node);
2420 } else if (element == compiler.numClass) {
2421 checkNum(input, relation);
2422 attachLocationToLast(node);
2423 } else if (element == compiler.boolClass) {
2424 checkBool(input, relation);
2425 attachLocationToLast(node);
2426 } else if (element == compiler.intClass) {
2427 // The is check in the code tells us that it might not be an
2428 // int. So we do a typeof first to avoid possible
2429 // deoptimizations on the JS engine due to the Math.floor check.
2430 checkNum(input, relation);
2431 js.Expression numTest = pop();
2432 checkBigInt(input, relation);
2433 push(new js.Binary(negative ? '||' : '&&', numTest, pop()), node);
2434 } else if (Elements.isNumberOrStringSupertype(element, compiler)) {
2435 handleNumberOrStringSupertypeCheck(
2436 input, interceptor, type, negative: negative);
2437 attachLocationToLast(node);
2438 } else if (Elements.isStringOnlySupertype(element, compiler)) {
2439 handleStringSupertypeCheck(
2440 input, interceptor, type, negative: negative);
2441 attachLocationToLast(node);
2442 } else if (identical(element, compiler.listClass)
2443 || Elements.isListSupertype(element, compiler)) {
2444 handleListOrSupertypeCheck(
2445 input, interceptor, type, negative: negative);
2446 attachLocationToLast(node);
2447 } else if (type.isFunctionType) {
2448 checkType(input, interceptor, type, negative: negative);
2449 attachLocationToLast(node);
2450 } else if ((input.canBePrimitive(compiler)
2451 && !input.canBePrimitiveArray(compiler))
2452 || input.canBeNull()) {
2453 checkObject(input, relation);
2454 js.Expression objectTest = pop();
2455 checkType(input, interceptor, type, negative: negative);
2456 push(new js.Binary(negative ? '||' : '&&', objectTest, pop()), node);
2457 } else {
2458 checkType(input, interceptor, type, negative: negative);
2459 attachLocationToLast(node);
2460 }
2461 }
2462 }
2463
2464 void emitIsViaInterceptor(HIsViaInterceptor node, bool negative) {
2465 checkTypeViaProperty(node.interceptor, node.typeExpression, negative);
2466 attachLocationToLast(node);
2467 }
2468
2469 js.Expression generateTest(HInstruction input, TypeMask checkedType) {
2470 ClassWorld classWorld = compiler.world;
2471 TypeMask receiver = input.instructionType;
2472 // Figure out if it is beneficial to turn this into a null check.
2473 // V8 generally prefers 'typeof' checks, but for integers and
2474 // indexable primitives we cannot compile this test into a single
2475 // typeof check so the null check is cheaper.
2476 bool turnIntoNumCheck = input.isIntegerOrNull(compiler)
2477 && checkedType.containsOnlyInt(classWorld);
2478 bool turnIntoNullCheck = !turnIntoNumCheck
2479 && (checkedType.nullable() == receiver)
2480 && (checkedType.containsOnlyInt(classWorld)
2481 || checkedType.satisfies(backend.jsIndexableClass, classWorld));
2482 js.Expression test;
2483 if (turnIntoNullCheck) {
2484 use(input);
2485 test = new js.Binary("==", pop(), new js.LiteralNull());
2486 } else if (checkedType.containsOnlyInt(classWorld) && !turnIntoNumCheck) {
2487 // input is !int
2488 checkInt(input, '!==');
2489 test = pop();
2490 } else if (checkedType.containsOnlyNum(classWorld) || turnIntoNumCheck) {
2491 // input is !num
2492 checkNum(input, '!==');
2493 test = pop();
2494 } else if (checkedType.containsOnlyBool(classWorld)) {
2495 // input is !bool
2496 checkBool(input, '!==');
2497 test = pop();
2498 } else if (checkedType.containsOnlyString(classWorld)) {
2499 // input is !string
2500 checkString(input, '!==');
2501 test = pop();
2502 } else if (checkedType.satisfies(backend.jsExtendableArrayClass,
2503 classWorld)) {
2504 // input is !Object || input is !Array || input.isFixed
2505 checkObject(input, '!==');
2506 js.Expression objectTest = pop();
2507 checkArray(input, '!==');
2508 js.Expression arrayTest = pop();
2509 checkFixedArray(input);
2510 test = new js.Binary('||', objectTest, arrayTest);
2511 test = new js.Binary('||', test, pop());
2512 } else if (checkedType.satisfies(backend.jsMutableArrayClass, classWorld)) {
2513 // input is !Object
2514 // || ((input is !Array || input.isImmutable)
2515 // && input is !JsIndexingBehavior)
2516 checkObject(input, '!==');
2517 js.Expression objectTest = pop();
2518 checkArray(input, '!==');
2519 js.Expression arrayTest = pop();
2520 checkImmutableArray(input);
2521 js.Binary notArrayOrImmutable = new js.Binary('||', arrayTest, pop());
2522
2523 js.Binary notIndexing = checkIndexingBehavior(input, negative: true)
2524 ? new js.Binary('&&', notArrayOrImmutable, pop())
2525 : notArrayOrImmutable;
2526 test = new js.Binary('||', objectTest, notIndexing);
2527 } else if (checkedType.satisfies(backend.jsArrayClass, classWorld)) {
2528 // input is !Object
2529 // || (input is !Array && input is !JsIndexingBehavior)
2530 checkObject(input, '!==');
2531 js.Expression objectTest = pop();
2532 checkArray(input, '!==');
2533 js.Expression arrayTest = pop();
2534
2535 js.Expression notIndexing = checkIndexingBehavior(input, negative: true)
2536 ? new js.Binary('&&', arrayTest, pop())
2537 : arrayTest;
2538 test = new js.Binary('||', objectTest, notIndexing);
2539 } else if (checkedType.satisfies(backend.jsIndexableClass, classWorld)) {
2540 // input is !String
2541 // && (input is !Object
2542 // || (input is !Array && input is !JsIndexingBehavior))
2543 checkString(input, '!==');
2544 js.Expression stringTest = pop();
2545 checkObject(input, '!==');
2546 js.Expression objectTest = pop();
2547 checkArray(input, '!==');
2548 js.Expression arrayTest = pop();
2549
2550 js.Binary notIndexingTest = checkIndexingBehavior(input, negative: true)
2551 ? new js.Binary('&&', arrayTest, pop())
2552 : arrayTest;
2553 js.Binary notObjectOrIndexingTest =
2554 new js.Binary('||', objectTest, notIndexingTest);
2555 test = new js.Binary('&&', stringTest, notObjectOrIndexingTest);
2556 } else {
2557 compiler.internalError(input, 'Unexpected check.');
2558 }
2559 return test;
2560 }
2561
2562 void visitTypeConversion(HTypeConversion node) {
2563 if (node.isArgumentTypeCheck || node.isReceiverTypeCheck) {
2564 ClassWorld classWorld = compiler.world;
2565 // An int check if the input is not int or null, is not
2566 // sufficient for doing a argument or receiver check.
2567 assert(!node.checkedType.containsOnlyInt(classWorld) ||
2568 node.checkedInput.isIntegerOrNull(compiler));
2569 js.Expression test = generateTest(node.checkedInput, node.checkedType);
2570 js.Block oldContainer = currentContainer;
2571 js.Statement body = new js.Block.empty();
2572 currentContainer = body;
2573 if (node.isArgumentTypeCheck) {
2574 generateThrowWithHelper('iae', node.checkedInput);
2575 } else if (node.isReceiverTypeCheck) {
2576 use(node.checkedInput);
2577 String methodName =
2578 backend.namer.invocationName(node.receiverTypeCheckSelector);
2579 js.Expression call = jsPropertyCall(pop(), methodName, []);
2580 pushStatement(new js.Return(call));
2581 }
2582 currentContainer = oldContainer;
2583 body = unwrapStatement(body);
2584 pushStatement(new js.If.noElse(test, body), node);
2585 return;
2586 }
2587
2588 assert(node.isCheckedModeCheck || node.isCastTypeCheck);
2589 DartType type = node.typeExpression;
2590 assert(type.kind != TypeKind.TYPEDEF);
2591 if (type.isFunctionType) {
2592 // TODO(5022): We currently generate $isFunction checks for
2593 // function types.
2594 registry.registerIsCheck(compiler.functionClass.rawType);
2595 }
2596 registry.registerIsCheck(type);
2597
2598 CheckedModeHelper helper;
2599 if (node.isBooleanConversionCheck) {
2600 helper =
2601 const CheckedModeHelper('boolConversionCheck');
2602 } else {
2603 helper =
2604 backend.getCheckedModeHelper(type, typeCast: node.isCastTypeCheck);
2605 }
2606
2607 if (helper == null) {
2608 assert(type.isFunctionType);
2609 use(node.inputs[0]);
2610 } else {
2611 push(helper.generateCall(this, node));
2612 }
2613 }
2614
2615 void visitTypeKnown(HTypeKnown node) {
2616 // [HTypeKnown] instructions are removed before generating code.
2617 assert(false);
2618 }
2619
2620 void visitFunctionType(HFunctionType node) {
2621 FunctionType type = node.dartType;
2622 int inputCount = 0;
2623 use(node.inputs[inputCount++]);
2624 js.Expression returnType = pop();
2625
2626 List<js.Expression> parameterTypes = <js.Expression>[];
2627 for (var _ in type.parameterTypes) {
2628 use(node.inputs[inputCount++]);
2629 parameterTypes.add(pop());
2630 }
2631
2632 List<js.Expression> optionalParameterTypes = <js.Expression>[];
2633 for (var _ in type.optionalParameterTypes) {
2634 use(node.inputs[inputCount++]);
2635 optionalParameterTypes.add(pop());
2636 }
2637
2638 List<js.Property> namedParameters = <js.Property>[];
2639 for (var _ in type.namedParameters) {
2640 use(node.inputs[inputCount++]);
2641 js.Expression name = pop();
2642 use(node.inputs[inputCount++]);
2643 namedParameters.add(new js.Property(name, pop()));
2644 }
2645
2646 if (namedParameters.isEmpty) {
2647 var arguments = [returnType];
2648 if (!parameterTypes.isEmpty || !optionalParameterTypes.isEmpty) {
2649 arguments.add(new js.ArrayInitializer.from(parameterTypes));
2650 }
2651 if (!optionalParameterTypes.isEmpty) {
2652 arguments.add(new js.ArrayInitializer.from(optionalParameterTypes));
2653 }
2654 push(js.js('#(#)', [accessHelper('buildFunctionType'), arguments]));
2655 } else {
2656 var arguments = [
2657 returnType,
2658 new js.ArrayInitializer.from(parameterTypes),
2659 new js.ObjectInitializer(namedParameters)];
2660 push(js.js('#(#)', [accessHelper('buildNamedFunctionType'), arguments]));
2661 }
2662 }
2663
2664 void visitReadTypeVariable(HReadTypeVariable node) {
2665 TypeVariableElement element = node.dartType.element;
2666 Element helperElement = backend.findHelper('convertRtiToRuntimeType');
2667 registry.registerStaticUse(helperElement);
2668
2669 use(node.inputs[0]);
2670 if (node.hasReceiver) {
2671 if (backend.isInterceptorClass(element.enclosingClass)) {
2672 int index = RuntimeTypes.getTypeVariableIndex(element);
2673 js.Expression receiver = pop();
2674 js.Expression helper = backend.namer.elementAccess(helperElement);
2675 push(js.js(r'#(#.$builtinTypeInfo && #.$builtinTypeInfo[#])',
2676 [helper, receiver, receiver, js.js.number(index)]));
2677 } else {
2678 backend.emitter.registerReadTypeVariable(element);
2679 push(js.js('#.#()',
2680 [pop(), backend.namer.readTypeVariableName(element)]));
2681 }
2682 } else {
2683 push(js.js('#(#)', [
2684 backend.namer.elementAccess(
2685 backend.findHelper('convertRtiToRuntimeType')),
2686 pop()]));
2687 }
2688 }
2689
2690 void visitInterfaceType(HInterfaceType node) {
2691 List<js.Expression> typeArguments = <js.Expression>[];
2692 for (HInstruction type in node.inputs) {
2693 use(type);
2694 typeArguments.add(pop());
2695 }
2696
2697 ClassElement cls = node.dartType.element;
2698 var arguments = [backend.namer.elementAccess(cls)];
2699 if (!typeArguments.isEmpty) {
2700 arguments.add(new js.ArrayInitializer.from(typeArguments));
2701 }
2702 push(js.js('#(#)', [accessHelper('buildInterfaceType'), arguments]));
2703 }
2704
2705 void visitVoidType(HVoidType node) {
2706 push(js.js('#()', accessHelper('getVoidRuntimeType')));
2707 }
2708
2709 void visitDynamicType(HDynamicType node) {
2710 push(js.js('#()', accessHelper('getDynamicRuntimeType')));
2711 }
2712
2713 js.PropertyAccess accessHelper(String name) {
2714 Element helper = backend.findHelper(name);
2715 if (helper == null) {
2716 // For mocked-up tests.
2717 return js.js('(void 0).$name');
2718 }
2719 registry.registerStaticUse(helper);
2720 return backend.namer.elementAccess(helper);
2721 }
2722 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698