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

Side by Side Diff: lib/compiler/implementation/ssa/codegen.dart

Issue 10825180: Add JavaScript AST. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Cosmetic changes (comments). Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 class SsaCodeGeneratorTask extends CompilerTask { 5 class SsaCodeGeneratorTask extends CompilerTask {
6 final JavaScriptBackend backend; 6 final JavaScriptBackend backend;
7 SsaCodeGeneratorTask(JavaScriptBackend backend) 7 SsaCodeGeneratorTask(JavaScriptBackend backend)
8 : this.backend = backend, 8 : this.backend = backend,
9 super(backend.compiler); 9 super(backend.compiler);
10 String get name() => 'SSA code generator'; 10 String get name() => 'SSA code generator';
11 NativeEmitter get nativeEmitter() => backend.emitter.nativeEmitter; 11 NativeEmitter get nativeEmitter() => backend.emitter.nativeEmitter;
12 12
13 13
14 CodeBuffer buildJavaScriptFunction(FunctionElement element, 14 js.Fun buildJavaScriptFunction(FunctionElement element,
15 String parameters, 15 List<js.Parameter> parameters,
16 CodeBuffer body) { 16 js.Block body) {
17 String extraSpace = ""; 17 FunctionExpression expression = element.cachedNode;
18 // Members are emitted inside a JavaScript object literal. To line up the 18 js.Fun result = new js.Fun(parameters, body);
19 // indentation we want the closing curly brace to be indented by one space. 19 result.sourcePosition = expression.getBeginToken();
20 // Example: 20 result.endSourcePosition = expression.getEndToken();
21 // defineClass("A", "B", ... , { 21 return result;
22 // foo$1: function(..) { 22 }
23 // }, /* <========== indent by 1. */
24 // bar$2: function(..) {
25 // }, /* <========== indent by 1. */
26 //
27 // For static functions this is not necessary:
28 // $.staticFun = function() {
29 // ...
30 // };
31 if (element.isInstanceMember() ||
32 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
33 extraSpace = " ";
34 }
35 23
36 FunctionExpression expression = element.cachedNode; 24 CodeBuffer prettyPrint(js.Node node, Element positionElement) {
37 CodeBuffer buffer = new CodeBuffer(); 25 return js.prettyPrint(node, compiler, positionElement);
38 buffer.setSourceLocation(element, expression.getBeginToken());
39 buffer.add('function($parameters) {\n');
40 buffer.add(body);
41 buffer.add(extraSpace);
42 buffer.setSourceLocation(element, expression.getEndToken());
43 buffer.add('}');
44 return buffer;
45 } 26 }
46 27
47 CodeBuffer generateMethod(WorkItem work, HGraph graph) { 28 CodeBuffer generateMethod(WorkItem work, HGraph graph) {
48 return measure(() { 29 return measure(() {
49 compiler.tracer.traceGraph("codegen", graph); 30 compiler.tracer.traceGraph("codegen", graph);
50 Map<Element, String> parameterNames = getParameterNames(work); 31 Map<Element, String> parameterNames = getParameterNames(work);
51 parameterNames.forEach((element, name) { 32 parameterNames.forEach((element, name) {
52 compiler.enqueuer.codegen.addToWorkList(element); 33 compiler.enqueuer.codegen.addToWorkList(element);
53 }); 34 });
54 String parameters = Strings.join(parameterNames.getValues(), ', '); 35 List<js.Parameter> parameters = <js.Parameter>[];
36 parameterNames.forEach((element, name) {
37 parameters.add(new js.Parameter(name));
38 });
39 String parametersString = Strings.join(parameterNames.getValues(), ", ");
55 SsaOptimizedCodeGenerator codegen = new SsaOptimizedCodeGenerator( 40 SsaOptimizedCodeGenerator codegen = new SsaOptimizedCodeGenerator(
56 backend, work, parameters, parameterNames); 41 backend, work, parameters, parameterNames);
57 codegen.visitGraph(graph); 42 codegen.visitGraph(graph);
58 43
59 FunctionElement element = work.element; 44 FunctionElement element = work.element;
60 CodeBuffer code; 45 js.Block body;
61 if (element.isInstanceMember() 46 if (element.isInstanceMember()
62 && element.enclosingElement.isClass() 47 && element.enclosingElement.isClass()
63 && element.enclosingElement.isNative() 48 && element.enclosingElement.isNative()
64 && native.isOverriddenMethod( 49 && native.isOverriddenMethod(
65 element, element.enclosingElement, nativeEmitter)) { 50 element, element.enclosingElement, nativeEmitter)) {
66 // Record that this method is overridden. In case of optional 51 // Record that this method is overridden. In case of optional
67 // arguments, the emitter will generate stubs to handle them, 52 // arguments, the emitter will generate stubs to handle them,
68 // and needs to know if the method is overridden. 53 // and needs to know if the method is overridden.
69 nativeEmitter.overriddenMethods.add(element); 54 nativeEmitter.overriddenMethods.add(element);
70 StringBuffer buffer = new StringBuffer(); 55 StringBuffer buffer = new StringBuffer();
56 String codeString = prettyPrint(codegen.body, work.element).toString();
71 native.generateMethodWithPrototypeCheckForElement( 57 native.generateMethodWithPrototypeCheckForElement(
72 compiler, buffer, element, '${codegen.buffer}', parameters); 58 compiler, buffer, element, codeString, parametersString);
73 code = new CodeBuffer(); 59 js.Node blob = new js.LiteralStatement(buffer.toString());
74 code.add(buffer); 60 body = new js.Block(<js.Statement>[new js.ExpressionStatement(blob)]);
75 } else { 61 } else {
76 code = codegen.buffer; 62 body = codegen.body;
77 } 63 }
78 return buildJavaScriptFunction(element, parameters, code); 64 js.Fun fun = buildJavaScriptFunction(element, parameters, body);
65 return prettyPrint(fun, work.element);
79 }); 66 });
80 } 67 }
81 68
82 CodeBuffer generateBailoutMethod(WorkItem work, HGraph graph) { 69 CodeBuffer generateBailoutMethod(WorkItem work, HGraph graph) {
83 return measure(() { 70 return measure(() {
84 compiler.tracer.traceGraph("codegen-bailout", graph); 71 compiler.tracer.traceGraph("codegen-bailout", graph);
85 72
86 Map<Element, String> parameterNames = getParameterNames(work); 73 Map<Element, String> parameterNames = getParameterNames(work);
87 String parameters = Strings.join(parameterNames.getValues(), ', '); 74 List<js.Parameter> parameters = <js.Parameter>[];
75 parameterNames.forEach((element, name) {
76 parameters.add(new js.Parameter(name));
77 });
88 SsaUnoptimizedCodeGenerator codegen = new SsaUnoptimizedCodeGenerator( 78 SsaUnoptimizedCodeGenerator codegen = new SsaUnoptimizedCodeGenerator(
89 backend, work, parameters, parameterNames); 79 backend, work, parameters, parameterNames);
90 codegen.visitGraph(graph); 80 codegen.visitGraph(graph);
91 81
92 CodeBuffer code = new CodeBuffer(); 82 js.Block body = new js.Block(<js.Statement>[]);
93 code.add(codegen.setup); 83 body.statements.add(codegen.setup);
94 code.add(codegen.buffer); 84 body.statements.add(codegen.body);
95 return buildJavaScriptFunction( 85 js.Fun fun =
96 work.element, codegen.newParameters.toString(), code); 86 buildJavaScriptFunction(work.element, codegen.newParameters, body);
87 return prettyPrint(fun, work.element);
97 }); 88 });
98 } 89 }
99 90
100 Map<Element, String> getParameterNames(WorkItem work) { 91 Map<Element, String> getParameterNames(WorkItem work) {
101 Map<Element, String> parameterNames = new LinkedHashMap<Element, String>(); 92 Map<Element, String> parameterNames = new LinkedHashMap<Element, String>();
102 FunctionElement function = work.element; 93 FunctionElement function = work.element;
103 94
104 // The dom/html libraries have inline JS code that reference 95 // The dom/html libraries have inline JS code that reference
105 // parameter names directly. Long-term such code will be rejected. 96 // parameter names directly. Long-term such code will be rejected.
106 // Now, just don't mangle the parameter name. 97 // Now, just don't mangle the parameter name.
107 function.computeSignature(compiler).forEachParameter((Element element) { 98 function.computeSignature(compiler).forEachParameter((Element element) {
108 parameterNames[element] = function.isNative() 99 parameterNames[element] = function.isNative()
109 ? element.name.slowToString() 100 ? element.name.slowToString()
110 : JsNames.getValid('${element.name.slowToString()}'); 101 : JsNames.getValid('${element.name.slowToString()}');
111 }); 102 });
112 return parameterNames; 103 return parameterNames;
113 } 104 }
114 } 105 }
115 106
116 typedef void ElementAction(Element element); 107 typedef void ElementAction(Element element);
117 108
118 class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { 109 class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
119 /** 110 /**
120 * Current state for generating simple (non-local-control) code.
121 * It is generated as either statements (indented and ';'-terminated),
122 * expressions (comma separated) or declarations (also comma separated,
123 * but expected to be preceeded by a 'var' so it declares its variables);
124 */
125 static final int STATE_STATEMENT = 0;
126 static final int STATE_FIRST_EXPRESSION = 1;
127 static final int STATE_FIRST_DECLARATION = 2;
128 static final int STATE_EXPRESSION = 3;
129 static final int STATE_DECLARATION = 4;
130
131 /**
132 * When analyzing a [HStatementGraph] we try to recognize if it has
133 * the following properties.
134 */
135 static final int ONE_STATEMENT = 0;
136 static final int ONE_EXPRESSION = 1;
137 static final int EMPTY = 2;
138 static final int MULTIPLE_STATEMENTS = 3;
139
140 /**
141 * Returned by [expressionType] to tell how code can be generated for 111 * Returned by [expressionType] to tell how code can be generated for
142 * a subgraph. 112 * a subgraph.
143 * - [TYPE_STATEMENT] means that the graph must be generated as a statement, 113 * - [TYPE_STATEMENT] means that the graph must be generated as a statement,
144 * which is always possible. 114 * which is always possible.
145 * - [TYPE_EXPRESSION] means that the graph can be generated as an expression, 115 * - [TYPE_EXPRESSION] means that the graph can be generated as an expression,
146 * or possibly several comma-separated expressions. 116 * or possibly several comma-separated expressions.
147 * - [TYPE_DECLARATION] means that the graph can be generated as an 117 * - [TYPE_DECLARATION] means that the graph can be generated as an
148 * expression, and that it only generates expressions of the form 118 * expression, and that it only generates expressions of the form
149 * variable = expression 119 * variable = expression
150 * which are also valid as parts of a "var" declaration. 120 * which are also valid as parts of a "var" declaration.
151 */ 121 */
152 static final int TYPE_STATEMENT = 0; 122 static final int TYPE_STATEMENT = 0;
153 static final int TYPE_EXPRESSION = 1; 123 static final int TYPE_EXPRESSION = 1;
154 static final int TYPE_DECLARATION = 2; 124 static final int TYPE_DECLARATION = 2;
155 125
126 /**
127 * Whether we are currently generating expressions instead of statements.
128 * This includes declarations, which are generated as expressions.
129 */
130 bool isGeneratingExpression = false;
131
156 final JavaScriptBackend backend; 132 final JavaScriptBackend backend;
157 final WorkItem work; 133 final WorkItem work;
158 final CodeBuffer buffer;
159 final String parameters;
160 134
161 final Set<HInstruction> generateAtUseSite; 135 final Set<HInstruction> generateAtUseSite;
162 final Set<HInstruction> controlFlowOperators; 136 final Set<HInstruction> controlFlowOperators;
163 final Map<Element, ElementAction> breakAction; 137 final Map<Element, ElementAction> breakAction;
164 final Map<Element, ElementAction> continueAction; 138 final Map<Element, ElementAction> continueAction;
165 final Map<Element, String> parameterNames; 139 final Map<Element, String> parameterNames;
166 140
141 js.Block currentContainer;
142 js.Block get body() => currentContainer;
143 List<js.Expression> expressionStack;
144 List<js.Block> oldContainerStack;
145
167 /** 146 /**
168 * Contains the names of the instructions, as well as the parallel 147 * Contains the names of the instructions, as well as the parallel
169 * copies to perform on block transitioning. 148 * copies to perform on block transitioning.
170 */ 149 */
171 VariableNames variableNames; 150 VariableNames variableNames;
172 151
173 /** 152 /**
174 * While generating expressions, we can't insert variable declarations. 153 * While generating expressions, we can't insert variable declarations.
175 * Instead we declare them at the end of the function 154 * Instead we declare them at the end of the function
176 */ 155 */
177 final Set<String> delayedVariableDeclarations; 156 final Set<String> delayedVariableDeclarations;
178 157
179 /** 158 /**
180 * Set of variables that have already been declared. 159 * Set of variables that have already been declared.
181 */ 160 */
182 final Set<String> declaredVariables; 161 final Set<String> declaredVariables;
183 162
184 Element equalsNullElement; 163 Element equalsNullElement;
185 Element boolifiedEqualsNullElement; 164 Element boolifiedEqualsNullElement;
186 int indent = 0; 165 int indent = 0;
187 int expectedPrecedence = JSPrecedence.STATEMENT_PRECEDENCE;
188 JSBinaryOperatorPrecedence unsignedShiftPrecedences;
189 HGraph currentGraph; 166 HGraph currentGraph;
190 /**
191 * Whether the code-generation should try to generate an expression
192 * instead of a sequence of statements.
193 */
194 int generationState = STATE_STATEMENT;
195 HBasicBlock currentBlock; 167 HBasicBlock currentBlock;
196 168
197 // Records a block-information that is being handled specially. 169 // Records a block-information that is being handled specially.
198 // Used to break bad recursion. 170 // Used to break bad recursion.
199 HBlockInformation currentBlockInformation; 171 HBlockInformation currentBlockInformation;
200 // The subgraph is used to delimit traversal for some constructions, e.g., 172 // The subgraph is used to delimit traversal for some constructions, e.g.,
201 // if branches. 173 // if branches.
202 SubGraph subGraph; 174 SubGraph subGraph;
203 175
204 LibraryElement get currentLibrary() => work.element.getLibrary(); 176 LibraryElement get currentLibrary() => work.element.getLibrary();
(...skipping 10 matching lines...) Expand all
215 int value = 187 int value =
216 ((instruction as HConstant).constant as PrimitiveConstant).value; 188 ((instruction as HConstant).constant as PrimitiveConstant).value;
217 if (value >= 0 && value < (1 << 31)) { 189 if (value >= 0 && value < (1 << 31)) {
218 return true; 190 return true;
219 } 191 }
220 } 192 }
221 return false; 193 return false;
222 } 194 }
223 195
224 bool hasNonBitOpUser(HInstruction instruction, Set<HPhi> phiSet) { 196 bool hasNonBitOpUser(HInstruction instruction, Set<HPhi> phiSet) {
225 for (HInstruction use in instruction.usedBy) { 197 for (HInstruction user in instruction.usedBy) {
226 if (use is HPhi) { 198 if (user is HPhi) {
227 if (!phiSet.contains(use)) { 199 if (!phiSet.contains(user)) {
228 phiSet.add(use); 200 phiSet.add(user);
229 if (hasNonBitOpUser(use, phiSet)) return true; 201 if (hasNonBitOpUser(user, phiSet)) return true;
230 } 202 }
231 } else if (use is! HBitNot && use is! HBinaryBitOp) { 203 } else if (user is! HBitNot && user is! HBinaryBitOp) {
232 return true; 204 return true;
233 } 205 }
234 } 206 }
235 return false; 207 return false;
236 } 208 }
237 209
238 // We want the outcome of bit-operations to be positive. However, if 210 // We want the outcome of bit-operations to be positive. However, if
239 // the result of a bit-operation is only used by other bit 211 // the result of a bit-operation is only used by other bit
240 // operations we do not have to convert to an unsigned 212 // operations we do not have to convert to an unsigned
241 // integer. Also, if we are using & with a positive constant we know 213 // integer. Also, if we are using & with a positive constant we know
242 // that the result is positive already and need no conversion. 214 // that the result is positive already and need no conversion.
243 bool requiresUintConversion(HInstruction instruction) { 215 bool requiresUintConversion(HInstruction instruction) {
244 if (instruction is HBitAnd && 216 if (instruction is HBitAnd &&
245 (isNonNegativeInt32Constant((instruction as HBitAnd).left) || 217 (isNonNegativeInt32Constant((instruction as HBitAnd).left) ||
246 isNonNegativeInt32Constant((instruction as HBitAnd).right))) { 218 isNonNegativeInt32Constant((instruction as HBitAnd).right))) {
247 return false; 219 return false;
248 } 220 }
249 return hasNonBitOpUser(instruction, new Set<HPhi>()); 221 return hasNonBitOpUser(instruction, new Set<HPhi>());
250 } 222 }
251 223
224 /**
225 * If the [instruction] is not `null` it will be used to attach the position
226 * to the [statement].
227 */
228 void pushStatement(js.Statement statement, [HInstruction instruction]) {
229 assert(expressionStack.isEmpty());
230 if (instruction != null) {
231 attachLocation(statement, instruction);
232 }
233 currentContainer.statements.add(statement);
234 }
235
236 /**
237 * If the [instruction] is not `null` it will be used to attach the position
238 * to the [expression].
239 */
240 pushExpressionAsStatement(js.Expression expression,
241 [HInstruction instruction]) {
242 pushStatement(new js.ExpressionStatement(expression), instruction);
243 }
244
245 /**
246 * If the [instruction] is not `null` it will be used to attach the position
247 * to the [expression].
248 */
249 push(js.Expression expression, [HInstruction instruction]) {
250 if (instruction != null) {
251 attachLocation(expression, instruction);
252 }
253 expressionStack.add(expression);
254 }
255
256 js.Expression pop() {
257 return expressionStack.removeLast();
258 }
259
260 attachLocationToLast(HInstruction instruction) {
261 attachLocation(expressionStack.last(), instruction);
262 }
263
264 js.Node attachLocation(js.Node jsNode, HInstruction instruction) {
265 if (instruction.sourcePosition !== null) {
266 jsNode.sourcePosition = instruction.sourcePosition;
267 }
268 return jsNode;
269 }
270
271 js.Node attachLocationRange(js.Node jsNode, Node node) {
272 jsNode.sourcePosition = node.getBeginToken();
273 jsNode.endSourcePosition = node.getEndToken();
274 return jsNode;
275 }
276
252 SsaCodeGenerator(this.backend, 277 SsaCodeGenerator(this.backend,
253 this.work, 278 this.work,
254 this.parameters,
255 this.parameterNames) 279 this.parameterNames)
256 : declaredVariables = new Set<String>(), 280 : declaredVariables = new Set<String>(),
257 delayedVariableDeclarations = new Set<String>(), 281 delayedVariableDeclarations = new Set<String>(),
258 buffer = new CodeBuffer(), 282 currentContainer = new js.Block.empty(),
283 expressionStack = <js.Expression>[],
284 oldContainerStack = <js.Block>[],
259 generateAtUseSite = new Set<HInstruction>(), 285 generateAtUseSite = new Set<HInstruction>(),
260 controlFlowOperators = new Set<HInstruction>(), 286 controlFlowOperators = new Set<HInstruction>(),
261 breakAction = new Map<Element, ElementAction>(), 287 breakAction = new Map<Element, ElementAction>(),
262 continueAction = new Map<Element, ElementAction>(), 288 continueAction = new Map<Element, ElementAction>();
263 unsignedShiftPrecedences = JSPrecedence.binary['>>>'] {
264 }
265 289
266 abstract visitTypeGuard(HTypeGuard node); 290 abstract visitTypeGuard(HTypeGuard node);
267 abstract visitBailoutTarget(HBailoutTarget node); 291 abstract visitBailoutTarget(HBailoutTarget node);
268 292
269 abstract beginGraph(HGraph graph); 293 abstract beginGraph(HGraph graph);
270 abstract endGraph(HGraph graph); 294 abstract endGraph(HGraph graph);
271 295
272 abstract beginLoop(HBasicBlock block); 296 abstract beginLoop(HBasicBlock block);
273 abstract endLoop(HBasicBlock block); 297 abstract endLoop(HBasicBlock block);
274 abstract handleLoopCondition(HLoopBranch node); 298 abstract handleLoopCondition(HLoopBranch node);
275 299
276 abstract preLabeledBlock(HLabeledBlockInformation labeledBlockInfo); 300 abstract preLabeledBlock(HLabeledBlockInformation labeledBlockInfo);
277 abstract startLabeledBlock(HLabeledBlockInformation labeledBlockInfo); 301 abstract startLabeledBlock(HLabeledBlockInformation labeledBlockInfo);
278 abstract endLabeledBlock(HLabeledBlockInformation labeledBlockInfo); 302 abstract endLabeledBlock(HLabeledBlockInformation labeledBlockInfo);
279 303
280 void beginExpression(int precedence) {
281 if (precedence < expectedPrecedence) {
282 buffer.add('(');
283 }
284 }
285
286 void endExpression(int precedence) {
287 if (precedence < expectedPrecedence) {
288 buffer.add(')');
289 }
290 }
291
292 void withPrecedence(int precedence, void action()) {
293 int oldPrecedence = expectedPrecedence;
294 beginExpression(precedence);
295 expectedPrecedence = precedence;
296 action();
297 expectedPrecedence = oldPrecedence;
298 endExpression(precedence);
299 }
300
301 void preGenerateMethod(HGraph graph) { 304 void preGenerateMethod(HGraph graph) {
302 new SsaInstructionMerger(generateAtUseSite).visitGraph(graph); 305 new SsaInstructionMerger(generateAtUseSite).visitGraph(graph);
303 new SsaConditionMerger(generateAtUseSite, 306 new SsaConditionMerger(generateAtUseSite,
304 controlFlowOperators).visitGraph(graph); 307 controlFlowOperators).visitGraph(graph);
305 SsaLiveIntervalBuilder intervalBuilder = 308 SsaLiveIntervalBuilder intervalBuilder =
306 new SsaLiveIntervalBuilder(compiler, generateAtUseSite); 309 new SsaLiveIntervalBuilder(compiler, generateAtUseSite);
307 intervalBuilder.visitGraph(graph); 310 intervalBuilder.visitGraph(graph);
308 SsaVariableAllocator allocator = new SsaVariableAllocator( 311 SsaVariableAllocator allocator = new SsaVariableAllocator(
309 compiler, 312 compiler,
310 intervalBuilder.liveInstructions, 313 intervalBuilder.liveInstructions,
311 intervalBuilder.liveIntervals, 314 intervalBuilder.liveIntervals,
312 generateAtUseSite, 315 generateAtUseSite,
313 parameterNames); 316 parameterNames);
314 allocator.visitGraph(graph); 317 allocator.visitGraph(graph);
315 variableNames = allocator.names; 318 variableNames = allocator.names;
316 } 319 }
317 320
318 visitGraph(HGraph graph) { 321 visitGraph(HGraph graph) {
319 preGenerateMethod(graph); 322 preGenerateMethod(graph);
320 currentGraph = graph; 323 currentGraph = graph;
321 indent++; // We are already inside a function. 324 indent++; // We are already inside a function.
322 subGraph = new SubGraph(graph.entry, graph.exit); 325 subGraph = new SubGraph(graph.entry, graph.exit);
323 HBasicBlock start = beginGraph(graph); 326 HBasicBlock start = beginGraph(graph);
324 visitBasicBlock(start); 327 visitBasicBlock(start);
325 if (!delayedVariableDeclarations.isEmpty()) { 328 if (!delayedVariableDeclarations.isEmpty()) {
326 addIndented("var "); 329 List<js.VariableInitialization> declarations =
327 buffer.add(Strings.join( 330 <js.VariableInitialization>[];
328 new List<String>.from(delayedVariableDeclarations), ', ')); 331 delayedVariableDeclarations.forEach((String name) {
329 buffer.add(";\n"); 332 declarations.add(new js.VariableInitialization(
333 new js.VariableDeclaration(name), null));
334 });
335 pushExpressionAsStatement(new js.VariableDeclarationList(declarations));
330 } 336 }
331 endGraph(graph); 337 endGraph(graph);
332 } 338 }
333 339
334 void visitSubGraph(SubGraph newSubGraph) { 340 void visitSubGraph(SubGraph newSubGraph) {
335 SubGraph oldSubGraph = subGraph; 341 SubGraph oldSubGraph = subGraph;
336 subGraph = newSubGraph; 342 subGraph = newSubGraph;
337 visitBasicBlock(subGraph.start); 343 visitBasicBlock(subGraph.start);
338 subGraph = oldSubGraph; 344 subGraph = oldSubGraph;
339 } 345 }
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
416 (limits.end.last is HConditionalBranch); 422 (limits.end.last is HConditionalBranch);
417 } 423 }
418 424
419 /** 425 /**
420 * Generate statements from block information. 426 * Generate statements from block information.
421 * If the block information contains expressions, generate only 427 * If the block information contains expressions, generate only
422 * assignments, and if it ends in a conditional branch, don't generate 428 * assignments, and if it ends in a conditional branch, don't generate
423 * the condition. 429 * the condition.
424 */ 430 */
425 void generateStatements(HBlockInformation block) { 431 void generateStatements(HBlockInformation block) {
426 int oldState = generationState;
427 generationState = STATE_STATEMENT;
428 if (block is HStatementInformation) { 432 if (block is HStatementInformation) {
429 block.accept(this); 433 block.accept(this);
430 } else { 434 } else {
431 HSubExpressionBlockInformation expression = block; 435 HSubExpressionBlockInformation expression = block;
432 visitSubGraph(expression.subExpression); 436 visitSubGraph(expression.subExpression);
433 } 437 }
434 generationState = oldState; 438 }
439
440 js.Block generateStatementsInNewBlock(HBlockInformation block) {
441 js.Block result = new js.Block.empty();
442 js.Block oldContainer = currentContainer;
443 currentContainer = result;
444 generateStatements(block);
445 currentContainer = oldContainer;
446 return result;
447 }
448
449 /**
450 * If the [block] only contains one statement returns that statement. If the
451 * that statement itself is a block, recursively calls this method.
452 *
453 * If the block is empty, returns a new instance of [js.NOP].
454 */
455 js.Statement unwrapStatement(js.Block block) {
456 int len = block.statements.length;
457 if (len == 0) return new js.EmptyStatement();
458 if (len == 1) {
459 js.Statement result = block.statements[0];
460 if (result is Block) return unwrapStatement(result);
461 return result;
462 }
463 return block;
435 } 464 }
436 465
437 /** 466 /**
438 * Generate expressions from block information. 467 * Generate expressions from block information.
439 */ 468 */
440 void generateExpression(HExpressionInformation expression) { 469 js.Expression generateExpression(HExpressionInformation expression) {
441 // Currently we only handle sub-expression graphs. 470 // Currently we only handle sub-expression graphs.
442 assert(expression is HSubExpressionBlockInformation); 471 assert(expression is HSubExpressionBlockInformation);
443 // [visitSubGraph] will reset the [expectedPrecedence]. Make sure we don't 472
444 // need parenthesis. I.e., this only expects to be called for top-level 473 bool oldIsGeneratingExpression = isGeneratingExpression;
445 // expressions, not sub-expressions. 474 isGeneratingExpression = true;
446 assert(expectedPrecedence == JSPrecedence.STATEMENT_PRECEDENCE 475 List<js.Expression> oldExpressionStack = expressionStack;
447 || expectedPrecedence == JSPrecedence.EXPRESSION_PRECEDENCE); 476 List<js.Expression> sequenceElements = <js.Expression>[];
448 477 expressionStack = sequenceElements;
449 HSubExpressionBlockInformation expressionSubGraph = expression; 478 HSubExpressionBlockInformation expressionSubGraph = expression;
450
451 int oldState = generationState;
452 generationState = STATE_FIRST_EXPRESSION;
453 visitSubGraph(expressionSubGraph.subExpression); 479 visitSubGraph(expressionSubGraph.subExpression);
454 generationState = oldState; 480 expressionStack = oldExpressionStack;
455 } 481 isGeneratingExpression = oldIsGeneratingExpression;
456 482 if (sequenceElements.isEmpty()) {
457 void generateDeclaration(HExpressionInformation expression) { 483 // Happens when the initializer, condition or update of a loop is empty.
458 // Currently we only handle sub-expression graphs. 484 return null;
459 assert(expression is HSubExpressionBlockInformation); 485 } else if (sequenceElements.length == 1) {
460 HSubExpressionBlockInformation expressionSubGraph = expression; 486 return sequenceElements[0];
461 487 } else {
462 int oldState = generationState; 488 return new js.Sequence(sequenceElements);
463 generationState = STATE_FIRST_DECLARATION; 489 }
464 visitSubGraph(expressionSubGraph.subExpression);
465 generationState = oldState;
466 }
467
468 void generateCondition(HBlockInformation condition) {
469 generateExpression(condition);
470 } 490 }
471 491
472 /** 492 /**
473 * Only visits the arguments starting at inputs[HInvoke.ARGUMENTS_OFFSET]. 493 * Only visits the arguments starting at inputs[HInvoke.ARGUMENTS_OFFSET].
474 */ 494 */
475 void visitArguments(List<HInstruction> inputs) { 495 List<js.Expression> visitArguments(List<HInstruction> inputs) {
476 assert(inputs.length >= HInvoke.ARGUMENTS_OFFSET); 496 assert(inputs.length >= HInvoke.ARGUMENTS_OFFSET);
477 buffer.add('('); 497 List<js.Expression> result = <js.Expression>[];
478 for (int i = HInvoke.ARGUMENTS_OFFSET; i < inputs.length; i++) { 498 for (int i = HInvoke.ARGUMENTS_OFFSET; i < inputs.length; i++) {
479 if (i != HInvoke.ARGUMENTS_OFFSET) buffer.add(', '); 499 use(inputs[i]);
480 use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); 500 result.add(pop());
481 } 501 }
482 buffer.add(')'); 502 return result;
483 }
484
485 /**
486 * Whether we are currently generating expressions instead of statements.
487 * This includes declarations, which are generated as expressions.
488 */
489 bool isGeneratingExpression() {
490 return generationState != STATE_STATEMENT;
491 }
492
493 /**
494 * Whether we are generating a declaration.
495 */
496 bool isGeneratingDeclaration() {
497 return (generationState == STATE_DECLARATION ||
498 generationState == STATE_FIRST_DECLARATION);
499 }
500
501 /**
502 * Called before writing an expression.
503 * Ensures that expressions are comma spearated.
504 */
505 void addExpressionSeparator() {
506 if (generationState == STATE_FIRST_EXPRESSION) {
507 generationState = STATE_EXPRESSION;
508 } else if (generationState != STATE_FIRST_DECLARATION) {
509 buffer.add(", ");
510 }
511 // If the state is [STATE_FIRST_DECLARATION] the potential
512 // declaration of the variable will be done by the instruction.
513 } 503 }
514 504
515 bool isVariableDeclared(String variableName) { 505 bool isVariableDeclared(String variableName) {
516 return declaredVariables.contains(variableName); 506 return declaredVariables.contains(variableName);
517 } 507 }
518 508
519 void declareVariable(String variableName) { 509 js.Expression generateExpressionAssignment(String variableName,
520 if (isGeneratingExpression()) { 510 js.Expression value) {
521 if (generationState == STATE_FIRST_DECLARATION) { 511 if (value is js.Binary) {
522 if (!isVariableDeclared(variableName)) { 512 js.Binary binary = value;
523 declaredVariables.add(variableName); 513 String op = binary.op;
524 buffer.add("var "); 514 if (op == '+' || op == '-' || op == '/' || op == '*' || op == '%' ||
525 generationState = STATE_DECLARATION; 515 op == '^' || op == '&' || op == '|') {
526 } else { 516 if (binary.left is js.VariableUse &&
527 generationState = STATE_EXPRESSION; 517 (binary.left as js.VariableUse).name == variableName) {
518 // We know now, that we can shorten x = x + y into x += y.
519 // Also check for the shortcut where y equals 1: x++ and x--.
520 if ((op == '+' || op == '-') &&
521 binary.right is js.LiteralNumber &&
522 (binary.right as js.LiteralNumber).value == "1") {
523 return new js.Prefix(op == '+' ? '++' : '--', binary.left);
524 }
525 return new js.Assignment.compound(binary.left, op, binary.right);
528 } 526 }
529 527 }
530 } else if (!isVariableDeclared(variableName)) { 528 }
531 if (!isGeneratingDeclaration()) { 529 return new js.Assignment(new js.VariableUse(variableName), value);
532 delayedVariableDeclarations.add(variableName); 530 }
533 } 531
534 // No matter if we are declaring the variable now or if we are 532 void assignVariable(String variableName, js.Expression value) {
535 // delaying the declaration we can treat the variable as 533 if (isGeneratingExpression) {
536 // being declared from this point on. 534 if (!isVariableDeclared(variableName)) {
535 delayedVariableDeclarations.add(variableName);
536 // We can treat the variable as being declared from this point on.
537 declaredVariables.add(variableName); 537 declaredVariables.add(variableName);
538 } 538 }
539 } else if (!isVariableDeclared(variableName)) { 539 push(generateExpressionAssignment(variableName, value));
540 } else if (!isVariableDeclared(variableName) ||
541 delayedVariableDeclarations.contains(variableName)) {
540 declaredVariables.add(variableName); 542 declaredVariables.add(variableName);
541 buffer.add("var "); 543 delayedVariableDeclarations.remove(variableName);
542 } 544 js.VariableDeclaration decl = new js.VariableDeclaration(variableName);
543 buffer.add(variableName); 545 js.VariableInitialization initialization =
544 } 546 new js.VariableInitialization(decl, value);
545 547
546 void declareInstruction(HInstruction instruction) { 548 pushExpressionAsStatement(new js.VariableDeclarationList(
547 declareVariable(variableNames.getName(instruction)); 549 <js.VariableInitialization>[initialization]));
548 } 550 } else {
549 551 pushExpressionAsStatement(
550 // For simple updates of the form 'i = i op constant' generate 552 generateExpressionAssignment(variableName, value));
551 // 'i op= constant' instead. 553 }
552 bool handleSimpleUpdateDefinition(HInstruction instruction, String name) {
553 // If the variable is not declared the short update syntax cannot
554 // be used since it is a declaration and not an update.
555 if (!isVariableDeclared(name)) return false;
556
557 // Check that the operation is one of +, *, - or /. Record whether
558 // or not the operation is commutative.
559 var isCommutative = false;
560 if (instruction is HAdd || instruction is HMultiply) {
561 isCommutative = true;
562 } else if (instruction is !HSubtract && instruction is !HDivide) {
563 return false;
564 }
565
566 // Is it a builtin operation involving +, -, /, or *?
567 HBinaryArithmetic binaryInstruction = instruction;
568 assert(binaryInstruction.inputs.length == 3);
569 if (binaryInstruction.builtin) {
570 var left = binaryInstruction.left;
571 var right = binaryInstruction.right;
572 if (isCommutative && variableNames.getName(right) == name) {
573 var tmp = right;
574 right = left;
575 left = tmp;
576 }
577
578 // Check that left has the same name as the definition and emit
579 // the short update definition if it is.
580 if (variableNames.getName(left) == name) {
581 // Check if the right operand is constant one.
582 bool rightIsOne = false;
583 if (right.isConstantNumber()) {
584 HConstant rightConstant = right;
585 NumConstant numConstant = rightConstant.constant;
586 rightIsOne = (numConstant.value == 1);
587 }
588 if (binaryInstruction is HAdd && rightIsOne) {
589 beginExpression(JSPrecedence.PREFIX_PRECEDENCE);
590 buffer.add('++');
591 declareVariable(name);
592 endExpression(JSPrecedence.PREFIX_PRECEDENCE);
593 } else if (binaryInstruction is HSubtract && rightIsOne) {
594 beginExpression(JSPrecedence.PREFIX_PRECEDENCE);
595 buffer.add('--');
596 declareVariable(name);
597 endExpression(JSPrecedence.PREFIX_PRECEDENCE);
598 } else {
599 var operation = binaryInstruction.operation.name;
600 beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
601 declareVariable(name);
602 buffer.add(' ${operation}= ');
603 use(right, JSPrecedence.ASSIGNMENT_PRECEDENCE);
604 endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
605 }
606 return true;
607 }
608 }
609 return false;
610 }
611
612 // For simple type checks like i = intTypeCheck(i), we don't have to
613 // emit an assignment, because the intTypeCheck just returns its
614 // argument.
615 bool handleTypeConversion(instruction, name) {
616 if (instruction is !HTypeConversion) return false;
617 String inputName = variableNames.getName(instruction.checkedInput);
618 if (name != inputName) return false;
619 visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE);
620 return true;
621 } 554 }
622 555
623 void define(HInstruction instruction) { 556 void define(HInstruction instruction) {
624 if (isGeneratingExpression()) { 557 // For simple type checks like i = intTypeCheck(i), we don't have to
625 addExpressionSeparator(); 558 // emit an assignment, because the intTypeCheck just returns its
626 } else { 559 // argument.
627 assert(expectedPrecedence == JSPrecedence.STATEMENT_PRECEDENCE); 560 bool needsAssignment = true;
628 addIndentation(); 561 if (instruction is HTypeConversion) {
629 } 562 String inputName = variableNames.getName(instruction.checkedInput);
630 if (!instruction.isControlFlow() && variableNames.hasName(instruction)) { 563 if (variableNames.getName(instruction) == inputName) {
631 var name = variableNames.getName(instruction); 564 needsAssignment = false;
632 if (!handleSimpleUpdateDefinition(instruction, name) 565 }
633 && !handleTypeConversion(instruction, name)) { 566 }
634 withPrecedence(JSPrecedence.ASSIGNMENT_PRECEDENCE, () { 567
635 declareInstruction(instruction); 568 if (needsAssignment &&
636 buffer.add(" = "); 569 !instruction.isControlFlow() && variableNames.hasName(instruction)) {
637 visit(instruction, JSPrecedence.ASSIGNMENT_PRECEDENCE); 570 visitExpression(instruction);
638 }); 571 assignVariable(variableNames.getName(instruction), pop());
639 } 572 return;
640 } else { 573 }
641 visit(instruction, expectedPrecedence); 574
642 } 575 if (isGeneratingExpression) {
643 if (!isGeneratingExpression()) buffer.add(';\n'); 576 visitExpression(instruction);
644 } 577 } else {
645 578 visitStatement(instruction);
646 void use(HInstruction argument, int expectedPrecedenceForArgument) { 579 }
580 }
581
582 void use(HInstruction argument) {
647 if (isGenerateAtUseSite(argument)) { 583 if (isGenerateAtUseSite(argument)) {
648 visit(argument, expectedPrecedenceForArgument); 584 visitExpression(argument);
649 } else if (argument is HCheck && argument.isControlFlow()) { 585 } else if (argument is HCheck && argument.isControlFlow()) {
650 // A [HCheck] that has control flow can never be used as an 586 // A [HCheck] that has control flow can never be used as an
651 // expression and may not have a name. Therefore we just use the 587 // expression and may not have a name. Therefore we just use the
652 // checked instruction. 588 // checked instruction.
653 HCheck check = argument; 589 HCheck check = argument;
654 use(check.checkedInput, expectedPrecedenceForArgument); 590 use(check.checkedInput);
655 } else { 591 } else {
656 buffer.add(variableNames.getName(argument)); 592 push(new js.VariableUse(variableNames.getName(argument)), argument);
657 } 593 }
658 } 594 }
659 595
660 visit(HInstruction node, int expectedPrecedenceForNode) { 596 visit(HInstruction node) {
661 int oldPrecedence = this.expectedPrecedence;
662 this.expectedPrecedence = expectedPrecedenceForNode;
663 if (node.sourcePosition !== null) {
664 buffer.setSourceLocation(work.element, node.sourcePosition);
665 }
666 node.accept(this); 597 node.accept(this);
667 this.expectedPrecedence = oldPrecedence; 598 }
599
600 visitExpression(HInstruction node) {
601 bool oldIsGeneratingExpression = isGeneratingExpression;
602 isGeneratingExpression = true;
603 visit(node);
604 isGeneratingExpression = oldIsGeneratingExpression;
605 }
606
607 visitStatement(HInstruction node) {
608 assert(!isGeneratingExpression);
609 visit(node);
610 if (!expressionStack.isEmpty()) {
611 assert(expressionStack.length == 1);
612 pushExpressionAsStatement(pop());
613 }
668 } 614 }
669 615
670 void continueAsBreak(LabelElement target) { 616 void continueAsBreak(LabelElement target) {
671 addIndented("break "); 617 pushStatement(new js.Break(compiler.namer.continueLabelName(target)));
672 writeContinueLabel(target);
673 buffer.add(";\n");
674 } 618 }
675 619
676 void implicitContinueAsBreak(TargetElement target) { 620 void implicitContinueAsBreak(TargetElement target) {
677 addIndented("break "); 621 pushStatement(new js.Break(
678 writeImplicitContinueLabel(target); 622 compiler.namer.implicitContinueLabelName(target)));
679 buffer.add(";\n");
680 } 623 }
681 624
682 void implicitBreakWithLabel(TargetElement target) { 625 void implicitBreakWithLabel(TargetElement target) {
683 addIndented("break "); 626 pushStatement(new js.Break(compiler.namer.implicitBreakLabelName(target)));
684 writeImplicitLabel(target); 627 }
685 buffer.add(";\n"); 628
686 } 629 js.Statement wrapIntoLabels(js.Statement result, List<LabelElement> labels) {
630 for (LabelElement label in labels) {
631 if (label.isTarget) {
632 String breakLabelString = compiler.namer.breakLabelName(label);
633 result = new js.LabeledStatement(breakLabelString, result);
634 }
635 }
636 return result;
637 }
638
687 639
688 // The regular [visitIf] method implements the needed logic. 640 // The regular [visitIf] method implements the needed logic.
689 bool visitIfInfo(HIfBlockInformation info) => false; 641 bool visitIfInfo(HIfBlockInformation info) => false;
690 642
691 bool visitSwitchInfo(HSwitchBlockInformation info) { 643 bool visitSwitchInfo(HSwitchBlockInformation info) {
692 bool isExpression = isJSExpression(info.expression); 644 bool isExpression = isJSExpression(info.expression);
693 if (!isExpression) { 645 if (!isExpression) {
694 generateStatements(info.expression); 646 generateStatements(info.expression);
695 } 647 }
696 addIndentation(); 648
697 for (LabelElement label in info.labels) { 649 if (isExpression) {
698 if (label.isTarget) { 650 push(generateExpression(info.expression));
699 writeLabel(label); 651 } else {
700 buffer.add(":"); 652 use(info.expression.conditionExpression);
701 }
702 } 653 }
703 buffer.add("switch ("); 654 js.Expression key = pop();
704 if (isExpression) { 655 List<js.SwitchClause> cases = <js.SwitchClause>[];
705 generateExpression(info.expression); 656
706 } else { 657 js.Block oldContainer = currentContainer;
707 use(info.expression.conditionExpression,
708 JSPrecedence.EXPRESSION_PRECEDENCE);
709 }
710 buffer.add(") {\n");
711 indent++;
712 for (int i = 0; i < info.matchExpressions.length; i++) { 658 for (int i = 0; i < info.matchExpressions.length; i++) {
713 for (Constant constant in info.matchExpressions[i]) { 659 for (Constant constant in info.matchExpressions[i]) {
714 addIndented("case ");
715 generateConstant(constant); 660 generateConstant(constant);
716 buffer.add(":\n"); 661 currentContainer = new js.Block.empty();
662 cases.add(new js.Case(pop(), currentContainer));
717 } 663 }
718 if (i == info.matchExpressions.length - 1 && info.hasDefault) { 664 if (i == info.matchExpressions.length - 1 && info.hasDefault) {
719 addIndented("default:\n"); 665 currentContainer = new js.Block.empty();
666 cases.add(new js.Default(currentContainer));
720 } 667 }
721 indent++;
722 generateStatements(info.statements[i]); 668 generateStatements(info.statements[i]);
723 indent--;
724 } 669 }
725 indent--; 670 currentContainer = oldContainer;
726 addIndented("}\n"); 671
672 js.Statement result = new js.Switch(key, cases);
673 pushStatement(wrapIntoLabels(result, info.labels));
727 return true; 674 return true;
728 } 675 }
729 676
730 bool visitSequenceInfo(HStatementSequenceInformation info) { 677 bool visitSequenceInfo(HStatementSequenceInformation info) {
731 return false; 678 return false;
732 } 679 }
733 680
734 bool visitSubGraphInfo(HSubGraphBlockInformation info) { 681 bool visitSubGraphInfo(HSubGraphBlockInformation info) {
735 visitSubGraph(info.subGraph); 682 visitSubGraph(info.subGraph);
736 return true; 683 return true;
737 } 684 }
738 685
739 bool visitSubExpressionInfo(HSubExpressionBlockInformation info) { 686 bool visitSubExpressionInfo(HSubExpressionBlockInformation info) {
740 return false; 687 return false;
741 } 688 }
742 689
743 bool visitAndOrInfo(HAndOrBlockInformation info) { 690 bool visitAndOrInfo(HAndOrBlockInformation info) {
744 return false; 691 return false;
745 } 692 }
746 693
747 bool visitTryInfo(HTryBlockInformation info) { 694 bool visitTryInfo(HTryBlockInformation info) {
748 addIndented("try {\n"); 695 js.Block body = generateStatementsInNewBlock(info.body);
749 indent++; 696 js.Catch catchPart = null;
750 generateStatements(info.body); 697 js.Block finallyPart = null;
751 indent--;
752 addIndented("}");
753 if (info.catchBlock !== null) { 698 if (info.catchBlock !== null) {
754 // Printing the catch part.
755 HParameterValue exception = info.catchVariable; 699 HParameterValue exception = info.catchVariable;
756 String name = variableNames.getName(exception); 700 String name = variableNames.getName(exception);
757 parameterNames[exception.sourceElement] = name; 701 parameterNames[exception.sourceElement] = name;
758 buffer.add(' catch ($name) {\n'); 702 js.VariableDeclaration decl = new js.VariableDeclaration(name);
759 indent++; 703 js.Block catchBlock = generateStatementsInNewBlock(info.catchBlock);
760 generateStatements(info.catchBlock); 704 catchPart = new js.Catch(decl, catchBlock);
761 parameterNames.remove(exception.sourceElement);
762 indent--;
763 addIndented('}');
764 } 705 }
765 if (info.finallyBlock != null) { 706 if (info.finallyBlock != null) {
766 buffer.add(" finally {\n"); 707 finallyPart = generateStatementsInNewBlock(info.finallyBlock);
767 indent++;
768 generateStatements(info.finallyBlock);
769 indent--;
770 addIndented("}");
771 } 708 }
772 buffer.add("\n"); 709 pushStatement(new js.Try(body, catchPart, finallyPart));
773 return true; 710 return true;
774 } 711 }
775 712
776 void visitBodyIgnoreLabels(HLoopBlockInformation info) { 713 void visitBodyIgnoreLabels(HLoopBlockInformation info) {
777 if (info.body.start.isLabeledBlock()) { 714 if (info.body.start.isLabeledBlock()) {
778 HBlockInformation oldInfo = currentBlockInformation; 715 HBlockInformation oldInfo = currentBlockInformation;
779 currentBlockInformation = info.body.start.blockFlow.body; 716 currentBlockInformation = info.body.start.blockFlow.body;
780 generateStatements(info.body); 717 generateStatements(info.body);
781 currentBlockInformation = oldInfo; 718 currentBlockInformation = oldInfo;
782 } else { 719 } else {
783 generateStatements(info.body); 720 generateStatements(info.body);
784 } 721 }
785 } 722 }
786 723
787 bool visitLoopInfo(HLoopBlockInformation info) { 724 bool visitLoopInfo(HLoopBlockInformation info) {
788 HExpressionInformation condition = info.condition; 725 HExpressionInformation condition = info.condition;
789 bool isConditionExpression = isJSCondition(condition); 726 bool isConditionExpression = isJSCondition(condition);
790 buffer.setSourceLocation(work.element, info.sourcePosition.getBeginToken()); 727
728 js.Loop loop;
791 729
792 switch (info.kind) { 730 switch (info.kind) {
793 // Treate all three "test-first" loops the same way. 731 // Treate all three "test-first" loops the same way.
794 case HLoopBlockInformation.FOR_LOOP: 732 case HLoopBlockInformation.FOR_LOOP:
795 case HLoopBlockInformation.WHILE_LOOP: 733 case HLoopBlockInformation.WHILE_LOOP:
796 case HLoopBlockInformation.FOR_IN_LOOP: { 734 case HLoopBlockInformation.FOR_IN_LOOP: {
797 HBlockInformation initialization = info.initializer; 735 HBlockInformation initialization = info.initializer;
798 int initializationType = TYPE_STATEMENT; 736 int initializationType = TYPE_STATEMENT;
799 if (initialization !== null) { 737 if (initialization !== null) {
800 initializationType = expressionType(initialization); 738 initializationType = expressionType(initialization);
801 if (initializationType == TYPE_STATEMENT) { 739 if (initializationType == TYPE_STATEMENT) {
802 generateStatements(initialization); 740 generateStatements(initialization);
803 initialization = null; 741 initialization = null;
804 } 742 }
805 } 743 }
806 for (LabelElement label in info.labels) {
807 if (label.isTarget) {
808 writeLabel(label);
809 buffer.add(":");
810 }
811 }
812 if (isConditionExpression && 744 if (isConditionExpression &&
813 info.updates !== null && isJSExpression(info.updates)) { 745 info.updates !== null && isJSExpression(info.updates)) {
814 // If we have an updates graph, and it's expressible as an 746 // If we have an updates graph, and it's expressible as an
815 // expression, generate a for-loop. 747 // expression, generate a for-loop.
816 addIndented("for ("); 748 js.Expression jsInitialization = null;
817 if (initialization !== null) { 749 if (initialization !== null) {
818 if (initializationType != TYPE_DECLARATION) { 750 int delayedVariablesCount = delayedVariableDeclarations.length;
819 generateExpression(initialization); 751 jsInitialization = generateExpression(initialization);
820 } else { 752 if (delayedVariablesCount < delayedVariableDeclarations.length) {
821 generateDeclaration(initialization); 753 // We just added a new delayed variable-declaration. See if we
754 // can put in a 'var' in front of the initialization to make it
755 // go away.
756 List<js.Expression> expressions;
757 if (jsInitialization is js.Sequence) {
758 expressions = jsInitialization.expressions;
759 } else {
760 expressions = <js.Expression>[jsInitialization];
761 }
762 bool canTransformToVariableDeclaration = true;
763 for (js.Expression expression in expressions) {
764 bool expressionIsVariableAssignment = false;
765 if (expression is js.Assignment) {
766 js.Assignment assignment = expression;
767 if (assignment.leftHandSide is js.VariableUse &&
768 assignment.compoundTarget == null) {
769 expressionIsVariableAssignment = true;
770 }
771 }
772 if (!expressionIsVariableAssignment) {
773 canTransformToVariableDeclaration = false;
774 break;
775 }
776 }
777 if (canTransformToVariableDeclaration) {
778 List<js.VariableInitialization> inits =
779 <js.VariableInitialization>[];
780 for (js.Assignment assignment in expressions) {
781 String id = (assignment.leftHandSide as js.VariableUse).name;
782 js.Node declaration = new js.VariableDeclaration(id);
783 inits.add(new js.VariableInitialization(declaration,
784 assignment.value));
785 delayedVariableDeclarations.remove(id);
786 }
787 jsInitialization = new js.VariableDeclarationList(inits);
788 }
822 } 789 }
823 } 790 }
824 buffer.add("; "); 791 js.Expression jsCondition = generateExpression(condition);
825 generateCondition(condition); 792 js.Expression jsUpdates = generateExpression(info.updates);
826 buffer.add("; ");
827 generateExpression(info.updates);
828 buffer.add(") {\n");
829 indent++;
830 // The body might be labeled. Ignore this when recursing on the 793 // The body might be labeled. Ignore this when recursing on the
831 // subgraph. 794 // subgraph.
832 // TODO(lrn): Remove this extra labeling when handling all loops 795 // TODO(lrn): Remove this extra labeling when handling all loops
833 // using subgraphs. 796 // using subgraphs.
797 js.Block oldContainer = currentContainer;
798 js.Statement body = new js.Block.empty();
799 currentContainer = body;
834 visitBodyIgnoreLabels(info); 800 visitBodyIgnoreLabels(info);
835 801 currentContainer = oldContainer;
836 indent--; 802 body = unwrapStatement(body);
803 loop = new js.For(jsInitialization, jsCondition, jsUpdates, body);
837 } else { 804 } else {
838 // We have either no update graph, or it's too complex to 805 // We have either no update graph, or it's too complex to
839 // put in an expression. 806 // put in an expression.
840 if (initialization !== null) { 807 if (initialization !== null) {
841 generateStatements(initialization); 808 generateStatements(initialization);
842 } 809 }
843 addIndented("while ("); 810 js.Expression jsCondition;
811 js.Block oldContainer = currentContainer;
812 js.Statement body = new js.Block.empty();
844 if (isConditionExpression) { 813 if (isConditionExpression) {
845 generateCondition(condition); 814 jsCondition = generateExpression(condition);
846 buffer.add(") {\n"); 815 currentContainer = body;
847 indent++;
848 } else { 816 } else {
849 buffer.add("true) {\n"); 817 jsCondition = new js.LiteralBool(true);
850 indent++; 818 currentContainer = body;
851 generateStatements(condition); 819 generateStatements(condition);
852 addIndented("if (!"); 820 use(condition.conditionExpression);
853 use(condition.conditionExpression, JSPrecedence.PREFIX_PRECEDENCE); 821 js.Expression ifTest = new js.Prefix("!", pop());
854 buffer.add(") break;\n"); 822 js.Break jsBreak = new js.Break(null);
823 pushStatement(new js.If.then(ifTest, jsBreak));
855 } 824 }
856 if (info.updates !== null) { 825 if (info.updates !== null) {
857 wrapLoopBodyForContinue(info); 826 wrapLoopBodyForContinue(info);
858 generateStatements(info.updates); 827 generateStatements(info.updates);
859 } else { 828 } else {
860 visitBodyIgnoreLabels(info); 829 visitBodyIgnoreLabels(info);
861 } 830 }
862 indent--; 831 currentContainer = oldContainer;
832 body = unwrapStatement(body);
833 loop = new js.While(jsCondition, body);
863 } 834 }
864 addIndented("}\n");
865 break; 835 break;
866 } 836 }
867 case HLoopBlockInformation.DO_WHILE_LOOP: { 837 case HLoopBlockInformation.DO_WHILE_LOOP: {
868 // Generate do-while loop in all cases. 838 // Generate do-while loop in all cases.
869 if (info.initializer !== null) { 839 if (info.initializer !== null) {
870 generateStatements(info.initializer); 840 generateStatements(info.initializer);
871 } 841 }
872 addIndentation(); 842 js.Block oldContainer = currentContainer;
873 for (LabelElement label in info.labels) { 843 js.Statement body = new js.Block.empty();
874 if (label.isTarget) { 844 currentContainer = body;
875 writeLabel(label);
876 buffer.add(":");
877 }
878 }
879 buffer.add("do {\n");
880 indent++;
881 if (!isConditionExpression || info.updates !== null) { 845 if (!isConditionExpression || info.updates !== null) {
882 wrapLoopBodyForContinue(info); 846 wrapLoopBodyForContinue(info);
883 } else { 847 } else {
884 visitBodyIgnoreLabels(info); 848 visitBodyIgnoreLabels(info);
885 } 849 }
886 if (info.updates !== null) { 850 if (info.updates !== null) {
887 generateStatements(info.updates); 851 generateStatements(info.updates);
888 } 852 }
889 if (isConditionExpression) { 853 if (isConditionExpression) {
890 indent--; 854 push(generateExpression(condition));
891 addIndented("} while (");
892 generateExpression(condition);
893 buffer.add(");\n");
894 } else { 855 } else {
895 generateStatements(condition); 856 generateStatements(condition);
896 indent--; 857 use(condition.conditionExpression);
897 addIndented("} while (");
898 use(condition.conditionExpression, JSPrecedence.PREFIX_PRECEDENCE);
899 buffer.add(");\n");
900 } 858 }
859 js.Expression jsCondition = pop();
860 currentContainer = oldContainer;
861 body = unwrapStatement(body);
862 loop = new js.Do(body, jsCondition);
901 break; 863 break;
902 } 864 }
903 default: 865 default:
904 compiler.internalError( 866 compiler.internalError(
905 'Unexpected loop kind: ${info.kind}', 867 'Unexpected loop kind: ${info.kind}',
906 instruction: condition.conditionExpression); 868 instruction: condition.conditionExpression);
907 } 869 }
908 buffer.setSourceLocation(work.element, info.sourcePosition.getEndToken()); 870 attachLocationRange(loop, info.sourcePosition);
871 pushStatement(wrapIntoLabels(loop, info.labels));
909 return true; 872 return true;
910 } 873 }
911 874
912 bool visitLabeledBlockInfo(HLabeledBlockInformation labeledBlockInfo) { 875 bool visitLabeledBlockInfo(HLabeledBlockInformation labeledBlockInfo) {
913 preLabeledBlock(labeledBlockInfo); 876 preLabeledBlock(labeledBlockInfo);
914 addIndentation();
915 Link<Element> continueOverrides = const EmptyLink<Element>(); 877 Link<Element> continueOverrides = const EmptyLink<Element>();
878
879 js.Block oldContainer = currentContainer;
880 js.Block body = new js.Block.empty();
881 js.Statement result = body;
882
883 currentContainer = body;
884
916 // If [labeledBlockInfo.isContinue], the block is an artificial 885 // If [labeledBlockInfo.isContinue], the block is an artificial
917 // block around the body of a loop with an update block, so that 886 // block around the body of a loop with an update block, so that
918 // continues of the loop can be written as breaks of the body 887 // continues of the loop can be written as breaks of the body
919 // block. 888 // block.
920 if (labeledBlockInfo.isContinue) { 889 if (labeledBlockInfo.isContinue) {
921 for (LabelElement label in labeledBlockInfo.labels) { 890 for (LabelElement label in labeledBlockInfo.labels) {
922 if (label.isContinueTarget) { 891 if (label.isContinueTarget) {
923 writeContinueLabel(label); 892 String labelName = compiler.namer.continueLabelName(label);
924 buffer.add(':'); 893 result = new js.LabeledStatement(labelName, result);
925 continueAction[label] = continueAsBreak; 894 continueAction[label] = continueAsBreak;
926 continueOverrides = continueOverrides.prepend(label); 895 continueOverrides = continueOverrides.prepend(label);
927 } 896 }
928 } 897 }
929 // For handling unlabeled continues from the body of a loop. 898 // For handling unlabeled continues from the body of a loop.
930 // TODO(lrn): Consider recording whether the target is in fact 899 // TODO(lrn): Consider recording whether the target is in fact
931 // a target of an unlabeled continue, and not generate this if it isn't. 900 // a target of an unlabeled continue, and not generate this if it isn't.
932 TargetElement target = labeledBlockInfo.target; 901 TargetElement target = labeledBlockInfo.target;
933 writeImplicitContinueLabel(target); 902 String labelName = compiler.namer.implicitContinueLabelName(target);
934 buffer.add(':'); 903 result = new js.LabeledStatement(labelName, result);
935 continueAction[target] = implicitContinueAsBreak; 904 continueAction[target] = implicitContinueAsBreak;
936 continueOverrides = continueOverrides.prepend(target); 905 continueOverrides = continueOverrides.prepend(target);
937 } else { 906 } else {
938 for (LabelElement label in labeledBlockInfo.labels) { 907 for (LabelElement label in labeledBlockInfo.labels) {
939 if (label.isBreakTarget) { 908 if (label.isBreakTarget) {
940 writeLabel(label); 909 String labelName = compiler.namer.breakLabelName(label);
941 buffer.add(':'); 910 result = new js.LabeledStatement(labelName, result);
942 } 911 }
943 } 912 }
944 TargetElement target = labeledBlockInfo.target; 913 TargetElement target = labeledBlockInfo.target;
945 if (target.isSwitch) { 914 if (target.isSwitch) {
946 // This is an extra block around a switch that is generated 915 // This is an extra block around a switch that is generated
947 // as a nested if/else chain. We add an extra break target 916 // as a nested if/else chain. We add an extra break target
948 // so that case code can break. 917 // so that case code can break.
949 writeImplicitLabel(target); 918 String labelName = compiler.namer.implicitBreakLabelName(target);
950 buffer.add(':'); 919 result = new js.LabeledStatement(labelName, result);
951 breakAction[target] = implicitBreakWithLabel; 920 breakAction[target] = implicitBreakWithLabel;
952 } 921 }
953 } 922 }
954 buffer.add('{\n');
955 indent++;
956 923
924 currentContainer = body;
957 startLabeledBlock(labeledBlockInfo); 925 startLabeledBlock(labeledBlockInfo);
958 generateStatements(labeledBlockInfo.body); 926 generateStatements(labeledBlockInfo.body);
959 endLabeledBlock(labeledBlockInfo); 927 endLabeledBlock(labeledBlockInfo);
960 928
961 indent--;
962 addIndented('}\n');
963
964 if (labeledBlockInfo.isContinue) { 929 if (labeledBlockInfo.isContinue) {
965 while (!continueOverrides.isEmpty()) { 930 while (!continueOverrides.isEmpty()) {
966 continueAction.remove(continueOverrides.head); 931 continueAction.remove(continueOverrides.head);
967 continueOverrides = continueOverrides.tail; 932 continueOverrides = continueOverrides.tail;
968 } 933 }
969 } else { 934 } else {
970 breakAction.remove(labeledBlockInfo.target); 935 breakAction.remove(labeledBlockInfo.target);
971 } 936 }
937
938 currentContainer = oldContainer;
939 pushStatement(result);
972 return true; 940 return true;
973 } 941 }
974 942
975 // Wraps a loop body in a block to make continues have a target to break 943 // Wraps a loop body in a block to make continues have a target to break
976 // to (if necessary). 944 // to (if necessary).
977 void wrapLoopBodyForContinue(HLoopBlockInformation info) { 945 void wrapLoopBodyForContinue(HLoopBlockInformation info) {
978 TargetElement target = info.target; 946 TargetElement target = info.target;
979 if (target !== null && target.isContinueTarget) { 947 if (target !== null && target.isContinueTarget) {
980 addIndentation(); 948 js.Block oldContainer = currentContainer;
949 js.Block body = new js.Block.empty();
950 currentContainer = body;
951 js.Statement result = body;
981 for (LabelElement label in info.labels) { 952 for (LabelElement label in info.labels) {
982 if (label.isContinueTarget) { 953 if (label.isContinueTarget) {
983 writeContinueLabel(label); 954 String labelName = compiler.namer.continueLabelName(label);
984 buffer.add(":"); 955 result = new js.LabeledStatement(labelName, result);
985 continueAction[label] = continueAsBreak; 956 continueAction[label] = continueAsBreak;
986 } 957 }
987 } 958 }
988 writeImplicitContinueLabel(target); 959 String labelName = compiler.namer.implicitContinueLabelName(target);
989 buffer.add(":{\n"); 960 result = new js.LabeledStatement(labelName, result);
990 continueAction[info.target] = implicitContinueAsBreak; 961 continueAction[info.target] = implicitContinueAsBreak;
991 indent++;
992 visitBodyIgnoreLabels(info); 962 visitBodyIgnoreLabels(info);
993 indent--;
994 addIndented("}\n");
995 continueAction.remove(info.target); 963 continueAction.remove(info.target);
996 for (LabelElement label in info.labels) { 964 for (LabelElement label in info.labels) {
997 if (label.isContinueTarget) { 965 if (label.isContinueTarget) {
998 continueAction.remove(label); 966 continueAction.remove(label);
999 } 967 }
1000 } 968 }
969 currentContainer = oldContainer;
970 pushStatement(result);
1001 } else { 971 } else {
1002 // Loop body contains no continues, so we don't need a break target. 972 // Loop body contains no continues, so we don't need a break target.
1003 generateStatements(info.body); 973 generateStatements(info.body);
1004 } 974 }
1005 } 975 }
1006 976
1007 bool handleBlockFlow(HBlockFlow block) { 977 bool handleBlockFlow(HBlockFlow block) {
1008 HBlockInformation info = block.body; 978 HBlockInformation info = block.body;
1009 // If we reach here again while handling the attached information, 979 // If we reach here again while handling the attached information,
1010 // e.g., because we call visitSubGraph on a subgraph starting on 980 // e.g., because we call visitSubGraph on a subgraph starting on
(...skipping 29 matching lines...) Expand all
1040 } 1010 }
1041 // Flow based traversal. 1011 // Flow based traversal.
1042 if (node.isLoopHeader() && 1012 if (node.isLoopHeader() &&
1043 node.loopInformation.loopBlockInformation !== currentBlockInformation) { 1013 node.loopInformation.loopBlockInformation !== currentBlockInformation) {
1044 beginLoop(node); 1014 beginLoop(node);
1045 } 1015 }
1046 iterateBasicBlock(node); 1016 iterateBasicBlock(node);
1047 } 1017 }
1048 1018
1049 void emitAssignment(String destination, String source) { 1019 void emitAssignment(String destination, String source) {
1050 if (isGeneratingExpression()) { 1020 assignVariable(destination, new js.VariableUse(source));
1051 addExpressionSeparator();
1052 } else {
1053 addIndentation();
1054 }
1055 declareVariable(destination);
1056 buffer.add(' = $source');
1057 if (!isGeneratingExpression()) {
1058 buffer.add(';\n');
1059 }
1060 } 1021 }
1061 1022
1062 /** 1023 /**
1063 * Sequentialize a list of conceptually parallel copies. Parallel 1024 * Sequentialize a list of conceptually parallel copies. Parallel
1064 * copies may contain cycles, that this method breaks. 1025 * copies may contain cycles, that this method breaks.
1065 */ 1026 */
1066 void sequentializeCopies(List<Copy> copies) { 1027 void sequentializeCopies(List<Copy> copies) {
1067 // Map to keep track of the current location (ie the variable that 1028 // Map to keep track of the current location (ie the variable that
1068 // holds the initial value) of a variable. 1029 // holds the initial value) of a variable.
1069 Map<String, String> currentLocation = new Map<String, String>(); 1030 Map<String, String> currentLocation = new Map<String, String>();
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
1142 } 1103 }
1143 } 1104 }
1144 1105
1145 void assignPhisOfSuccessors(HBasicBlock node) { 1106 void assignPhisOfSuccessors(HBasicBlock node) {
1146 CopyHandler handler = variableNames.getCopyHandler(node); 1107 CopyHandler handler = variableNames.getCopyHandler(node);
1147 if (handler == null) return; 1108 if (handler == null) return;
1148 1109
1149 sequentializeCopies(handler.copies); 1110 sequentializeCopies(handler.copies);
1150 1111
1151 for (Copy copy in handler.assignments) { 1112 for (Copy copy in handler.assignments) {
1152 if (isGeneratingExpression()) {
1153 addExpressionSeparator();
1154 } else {
1155 addIndentation();
1156 }
1157 String name = variableNames.getName(copy.destination); 1113 String name = variableNames.getName(copy.destination);
1158 if (!handleSimpleUpdateDefinition(copy.source, name)) { 1114 use(copy.source);
1159 declareVariable(name); 1115 assignVariable(name, pop());
1160 buffer.add(' = ');
1161 use(copy.source, JSPrecedence.ASSIGNMENT_PRECEDENCE);
1162 }
1163 if (!isGeneratingExpression()) {
1164 buffer.add(';\n');
1165 }
1166 } 1116 }
1167 } 1117 }
1168 1118
1169 void iterateBasicBlock(HBasicBlock node) { 1119 void iterateBasicBlock(HBasicBlock node) {
1170 HInstruction instruction = node.first; 1120 HInstruction instruction = node.first;
1171 while (instruction !== node.last) { 1121 while (instruction !== node.last) {
1172 if (instruction is HTypeGuard || instruction is HBailoutTarget) { 1122 if (instruction is HTypeGuard || instruction is HBailoutTarget) {
1173 visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE); 1123 visit(instruction);
1174 } else if (!isGenerateAtUseSite(instruction)) { 1124 } else if (!isGenerateAtUseSite(instruction)) {
1175 expectedPrecedence = JSPrecedence.STATEMENT_PRECEDENCE;
1176 define(instruction); 1125 define(instruction);
1177 } 1126 }
1178 instruction = instruction.next; 1127 instruction = instruction.next;
1179 } 1128 }
1180 assignPhisOfSuccessors(node); 1129 assignPhisOfSuccessors(node);
1181 if (instruction is HLoopBranch && isGeneratingExpression()) { 1130 visit(instruction);
1182 addExpressionSeparator();
1183 }
1184 visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE);
1185 } 1131 }
1186 1132
1187 visitInvokeBinary(HInvokeBinary node, String op) { 1133 visitInvokeBinary(HInvokeBinary node, String op) {
1188 if (node.builtin) { 1134 if (node.builtin) {
1189 JSBinaryOperatorPrecedence operatorPrecedences = JSPrecedence.binary[op]; 1135 use(node.left);
1190 beginExpression(operatorPrecedences.precedence); 1136 js.Expression jsLeft = pop();
1191 use(node.left, operatorPrecedences.left); 1137 use(node.right);
1192 buffer.add(' $op '); 1138 push(new js.Binary(op, jsLeft, pop()), node);
1193 use(node.right, operatorPrecedences.right);
1194 endExpression(operatorPrecedences.precedence);
1195 } else { 1139 } else {
1196 visitInvokeStatic(node); 1140 visitInvokeStatic(node);
1197 } 1141 }
1198 } 1142 }
1199 1143
1200 // We want the outcome of bit-operations to be positive. We use the unsigned 1144 // We want the outcome of bit-operations to be positive. We use the unsigned
1201 // shift operator to achieve this. 1145 // shift operator to achieve this.
1202 visitBitInvokeBinary(HBinaryBitOp node, String op) { 1146 visitBitInvokeBinary(HBinaryBitOp node, String op) {
1147 visitInvokeBinary(node, op);
1203 if (node.builtin && requiresUintConversion(node)) { 1148 if (node.builtin && requiresUintConversion(node)) {
1204 beginExpression(unsignedShiftPrecedences.precedence); 1149 push(new js.Binary(">>>", pop(), new js.LiteralNumber("0")), node);
1205 int oldPrecedence = this.expectedPrecedence;
1206 this.expectedPrecedence = JSPrecedence.SHIFT_PRECEDENCE;
1207 visitInvokeBinary(node, op);
1208 buffer.add(' >>> 0');
1209 this.expectedPrecedence = oldPrecedence;
1210 endExpression(unsignedShiftPrecedences.precedence);
1211 } else {
1212 visitInvokeBinary(node, op);
1213 } 1150 }
1214 } 1151 }
1215 1152
1216 visitInvokeUnary(HInvokeUnary node, String op) { 1153 visitInvokeUnary(HInvokeUnary node, String op) {
1217 if (node.builtin) { 1154 if (node.builtin) {
1218 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); 1155 use(node.operand);
1219 buffer.add('$op'); 1156 push(new js.Prefix(op, pop()), node);
1220 use(node.operand, JSPrecedence.PREFIX_PRECEDENCE);
1221 endExpression(JSPrecedence.PREFIX_PRECEDENCE);
1222 } else { 1157 } else {
1223 visitInvokeStatic(node); 1158 visitInvokeStatic(node);
1224 } 1159 }
1225 } 1160 }
1226 1161
1227 // We want the outcome of bit-operations to be positive. We use the unsigned 1162 // We want the outcome of bit-operations to be positive. We use the unsigned
1228 // shift operator to achieve this. 1163 // shift operator to achieve this.
1229 visitBitInvokeUnary(HInvokeUnary node, String op) { 1164 visitBitInvokeUnary(HInvokeUnary node, String op) {
1165 visitInvokeUnary(node, op);
1230 if (node.builtin && requiresUintConversion(node)) { 1166 if (node.builtin && requiresUintConversion(node)) {
1231 beginExpression(unsignedShiftPrecedences.precedence); 1167 push(new js.Binary(">>>", pop(), new js.LiteralNumber("0")), node);
1232 int oldPrecedence = this.expectedPrecedence;
1233 this.expectedPrecedence = JSPrecedence.SHIFT_PRECEDENCE;
1234 visitInvokeUnary(node, op);
1235 buffer.add(' >>> 0');
1236 this.expectedPrecedence = oldPrecedence;
1237 endExpression(unsignedShiftPrecedences.precedence);
1238 } else {
1239 visitInvokeUnary(node, op);
1240 } 1168 }
1241 } 1169 }
1242 1170
1243 void emitIdentityComparison(HInstruction left, HInstruction right) { 1171 void emitIdentityComparison(HInstruction left, HInstruction right) {
1244 String op = singleIdentityComparison(left, right); 1172 String op = singleIdentityComparison(left, right);
1245 if (op != null) { 1173 if (op != null) {
1246 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 1174 use(left);
1247 use(left, JSPrecedence.EQUALITY_PRECEDENCE); 1175 js.Expression jsLeft = pop();
1248 buffer.add(' $op '); 1176 use(right);
1249 use(right, JSPrecedence.RELATIONAL_PRECEDENCE); 1177 push(new js.Binary(op, jsLeft, pop()));
1250 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
1251 } else { 1178 } else {
1252 assert(NullConstant.JsNull == 'null'); 1179 assert(NullConstant.JsNull == 'null');
1253 withPrecedence(JSPrecedence.CONDITIONAL_PRECEDENCE, () { 1180 use(left);
1254 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 1181 js.Binary leftEqualsNull =
1255 use(left, JSPrecedence.EQUALITY_PRECEDENCE); 1182 new js.Binary("==", pop(), new js.LiteralNull());
1256 buffer.add(' == null'); 1183 use(right);
1257 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); 1184 js.Binary rightEqualsNull =
1258 buffer.add(' ? '); 1185 new js.Binary("==", pop(), new js.LiteralNull());
1259 this.expectedPrecedence = JSPrecedence.ASSIGNMENT_PRECEDENCE; 1186 use(right);
1260 withPrecedence(JSPrecedence.LOGICAL_AND_PRECEDENCE, () { 1187 use(left);
1261 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 1188 js.Binary tripleEq = new js.Binary("===", pop(), pop());
1262 use(right, JSPrecedence.EQUALITY_PRECEDENCE); 1189
1263 buffer.add(' == null'); 1190 push(new js.Conditional(leftEqualsNull, rightEqualsNull, tripleEq));
1264 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
1265 buffer.add(" : ");
1266 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
1267 use(left, JSPrecedence.EQUALITY_PRECEDENCE);
1268 buffer.add(' === ');
1269 use(right, JSPrecedence.EQUALITY_PRECEDENCE);
1270 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
1271 });
1272 });
1273 } 1191 }
1274 } 1192 }
1275 1193
1276 visitEquals(HEquals node) { 1194 visitEquals(HEquals node) {
1277 if (node.builtin) { 1195 if (node.builtin) {
1278 emitIdentityComparison(node.left, node.right); 1196 emitIdentityComparison(node.left, node.right);
1279 } else { 1197 } else {
1280 visitInvokeStatic(node); 1198 visitInvokeStatic(node);
1281 } 1199 }
1282 } 1200 }
(...skipping 20 matching lines...) Expand all
1303 visitShiftLeft(HShiftLeft node) => visitBitInvokeBinary(node, '<<'); 1221 visitShiftLeft(HShiftLeft node) => visitBitInvokeBinary(node, '<<');
1304 1222
1305 visitNegate(HNegate node) => visitInvokeUnary(node, '-'); 1223 visitNegate(HNegate node) => visitInvokeUnary(node, '-');
1306 1224
1307 visitLess(HLess node) => visitInvokeBinary(node, '<'); 1225 visitLess(HLess node) => visitInvokeBinary(node, '<');
1308 visitLessEqual(HLessEqual node) => visitInvokeBinary(node, '<='); 1226 visitLessEqual(HLessEqual node) => visitInvokeBinary(node, '<=');
1309 visitGreater(HGreater node) => visitInvokeBinary(node, '>'); 1227 visitGreater(HGreater node) => visitInvokeBinary(node, '>');
1310 visitGreaterEqual(HGreaterEqual node) => visitInvokeBinary(node, '>='); 1228 visitGreaterEqual(HGreaterEqual node) => visitInvokeBinary(node, '>=');
1311 1229
1312 visitBoolify(HBoolify node) { 1230 visitBoolify(HBoolify node) {
1313 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
1314 assert(node.inputs.length == 1); 1231 assert(node.inputs.length == 1);
1315 use(node.inputs[0], JSPrecedence.EQUALITY_PRECEDENCE); 1232 use(node.inputs[0]);
1316 buffer.add(' === true'); 1233 push(new js.Binary('===', pop(), new js.LiteralBool(true)), node);
1317 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
1318 } 1234 }
1319 1235
1320 visitExit(HExit node) { 1236 visitExit(HExit node) {
1321 // Don't do anything. 1237 // Don't do anything.
1322 } 1238 }
1323 1239
1324 visitGoto(HGoto node) { 1240 visitGoto(HGoto node) {
1325 assert(currentBlock.successors.length == 1); 1241 assert(currentBlock.successors.length == 1);
1326 List<HBasicBlock> dominated = currentBlock.dominatedBlocks; 1242 List<HBasicBlock> dominated = currentBlock.dominatedBlocks;
1327 // With the exception of the entry-node which dominates its successor 1243 // 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 1244 // and the exit node, no block finishing with a 'goto' can have more than
1329 // one dominated block (since it has only one successor). 1245 // one dominated block (since it has only one successor).
1330 // If the successor is dominated by another block, then the other block 1246 // If the successor is dominated by another block, then the other block
1331 // is responsible for visiting the successor. 1247 // is responsible for visiting the successor.
1332 if (dominated.isEmpty()) return; 1248 if (dominated.isEmpty()) return;
1333 if (dominated.length > 2) { 1249 if (dominated.length > 2) {
1334 compiler.internalError('dominated.length = ${dominated.length}', 1250 compiler.internalError('dominated.length = ${dominated.length}',
1335 instruction: node); 1251 instruction: node);
1336 } 1252 }
1337 if (dominated.length == 2 && currentBlock !== currentGraph.entry) { 1253 if (dominated.length == 2 && currentBlock !== currentGraph.entry) {
1338 compiler.internalError('currentBlock !== currentGraph.entry', 1254 compiler.internalError('currentBlock !== currentGraph.entry',
1339 instruction: node); 1255 instruction: node);
1340 } 1256 }
1341 assert(dominated[0] == currentBlock.successors[0]); 1257 assert(dominated[0] == currentBlock.successors[0]);
1342 visitBasicBlock(dominated[0]); 1258 visitBasicBlock(dominated[0]);
1343 } 1259 }
1344 1260
1345 // Used to write the name of labels.
1346 void writeLabel(LabelElement label) {
1347 buffer.add('\$${label.labelName}\$${label.target.nestingLevel}');
1348 }
1349
1350 void writeImplicitLabel(TargetElement target) {
1351 buffer.add('\$${target.nestingLevel}');
1352 }
1353
1354 // We sometimes handle continue targets differently from break targets,
1355 // so we have special continue-only labels.
1356 void writeContinueLabel(LabelElement label) {
1357 buffer.add('c\$${label.labelName}\$${label.target.nestingLevel}');
1358 }
1359
1360 void writeImplicitContinueLabel(TargetElement target) {
1361 buffer.add('c\$${target.nestingLevel}');
1362 }
1363
1364 /** 1261 /**
1365 * Checks if [map] contains an [ElementAction] for [element], and 1262 * Checks if [map] contains an [ElementAction] for [element], and
1366 * if so calls that action and returns true. 1263 * if so calls that action and returns true.
1367 * Otherwise returns false. 1264 * Otherwise returns false.
1368 */ 1265 */
1369 bool tryCallAction(Map<Element, ElementAction> map, Element element) { 1266 bool tryCallAction(Map<Element, ElementAction> map, Element element) {
1370 ElementAction action = map[element]; 1267 ElementAction action = map[element];
1371 if (action === null) return false; 1268 if (action === null) return false;
1372 action(element); 1269 action(element);
1373 return true; 1270 return true;
1374 } 1271 }
1375 1272
1376 visitBreak(HBreak node) { 1273 visitBreak(HBreak node) {
1377 assert(currentBlock.successors.length == 1); 1274 assert(currentBlock.successors.length == 1);
1378 if (node.label !== null) { 1275 if (node.label !== null) {
1379 LabelElement label = node.label; 1276 LabelElement label = node.label;
1380 if (!tryCallAction(breakAction, label)) { 1277 if (!tryCallAction(breakAction, label)) {
1381 addIndented("break "); 1278 pushStatement(new js.Break(compiler.namer.breakLabelName(label)), node);
1382 writeLabel(label);
1383 buffer.add(";\n");
1384 } 1279 }
1385 } else { 1280 } else {
1386 TargetElement target = node.target; 1281 TargetElement target = node.target;
1387 if (!tryCallAction(breakAction, target)) { 1282 if (!tryCallAction(breakAction, target)) {
1388 addIndented("break;\n"); 1283 pushStatement(new js.Break(null), node);
1389 } 1284 }
1390 } 1285 }
1391 } 1286 }
1392 1287
1393 visitContinue(HContinue node) { 1288 visitContinue(HContinue node) {
1394 assert(currentBlock.successors.length == 1); 1289 assert(currentBlock.successors.length == 1);
1395 if (node.label !== null) { 1290 if (node.label !== null) {
1396 LabelElement label = node.label; 1291 LabelElement label = node.label;
1397 if (!tryCallAction(continueAction, label)) { 1292 if (!tryCallAction(continueAction, label)) {
1398 addIndented("continue "); 1293 // TODO(floitsch): should this really be the breakLabelName?
1399 writeLabel(label); 1294 pushStatement(new js.Continue(compiler.namer.breakLabelName(label)),
1400 buffer.add(";\n"); 1295 node);
1401 } 1296 }
1402 } else { 1297 } else {
1403 TargetElement target = node.target; 1298 TargetElement target = node.target;
1404 if (!tryCallAction(continueAction, target)) { 1299 if (!tryCallAction(continueAction, target)) {
1405 addIndented("continue;\n"); 1300 pushStatement(new js.Continue(null), node);
1406 } 1301 }
1407 } 1302 }
1408 } 1303 }
1409 1304
1410 visitTry(HTry node) { 1305 visitTry(HTry node) {
1411 // We should never get here. Try/catch/finally is always handled using block 1306 // We should never get here. Try/catch/finally is always handled using block
1412 // information in [visitTryInfo], or not at all, in the case of the bailout 1307 // information in [visitTryInfo], or not at all, in the case of the bailout
1413 // generator. 1308 // generator.
1414 compiler.internalError('visitTry should not be called', instruction: node); 1309 compiler.internalError('visitTry should not be called', instruction: node);
1415 } 1310 }
1416 1311
1417 /**
1418 * Analyzes the given [graph] to know whether it is empty, or
1419 * contains one statement, one expression, or multiple statements.
1420 */
1421 int analyzeGraphForCodegen(HStatementInformation graph) {
1422 HBasicBlock start = graph.start;
1423 HBasicBlock end = graph.end;
1424 // Only deal with single blocks for now. TODO(ngeoffray): analyze
1425 // all blocks.
1426 if (start !== end) return MULTIPLE_STATEMENTS;
1427
1428 int kind = EMPTY;
1429 bool updateKind(int newKind) {
1430 if (kind != EMPTY) return false;
1431 kind = newKind;
1432 return true;
1433 }
1434
1435 for (HInstruction instruction = start.first;
1436 instruction != start.last;
1437 instruction = instruction.next) {
1438 if (instruction.isStatement) {
1439 if (!updateKind(ONE_STATEMENT)) return MULTIPLE_STATEMENTS;
1440 } else if (!isGenerateAtUseSite(instruction)) {
1441 if (!updateKind(ONE_EXPRESSION)) return MULTIPLE_STATEMENTS;
1442 }
1443 }
1444
1445 HInstruction last = start.last;
1446 if (last is !HGoto) {
1447 if (!updateKind(last.isStatement ? ONE_STATEMENT : ONE_EXPRESSION)) {
1448 return MULTIPLE_STATEMENTS;
1449 }
1450 }
1451
1452 CopyHandler handler = variableNames.getCopyHandler(start);
1453 if (handler !== null && !handler.isEmpty()) {
1454 if (handler.assignments.length > 1) return MULTIPLE_STATEMENTS;
1455 if (handler.assignments.length == 1) {
1456 if (!updateKind(ONE_STATEMENT)) return MULTIPLE_STATEMENTS;
1457 }
1458 // If the block has a copy where the destination and source are
1459 // different, we will emit that copy, and therefore the block is
1460 // not empty.
1461 for (Copy copy in handler.copies) {
1462 String sourceName = variableNames.getName(copy.source);
1463 String destinationName = variableNames.getName(copy.destination);
1464 if (sourceName != destinationName) {
1465 if (!updateKind(ONE_STATEMENT)) return MULTIPLE_STATEMENTS;
1466 }
1467 }
1468 }
1469 return kind;
1470 }
1471
1472 bool tryControlFlowOperation(HIf node) { 1312 bool tryControlFlowOperation(HIf node) {
1473 if (!controlFlowOperators.contains(node)) return false; 1313 if (!controlFlowOperators.contains(node)) return false;
1474 HPhi phi = node.joinBlock.phis.first; 1314 HPhi phi = node.joinBlock.phis.first;
1475 bool atUseSite = isGenerateAtUseSite(phi); 1315 bool atUseSite = isGenerateAtUseSite(phi);
1476 // Don't generate a conditional operator in this situation: 1316 // Don't generate a conditional operator in this situation:
1477 // i = condition ? bar() : i; 1317 // i = condition ? bar() : i;
1478 // But generate this instead: 1318 // But generate this instead:
1479 // if (condition) i = bar(); 1319 // if (condition) i = bar();
1480 // Usually, the variable name is longer than 'if' and it takes up 1320 // Usually, the variable name is longer than 'if' and it takes up
1481 // more space to duplicate the name. 1321 // more space to duplicate the name.
1482 if (!atUseSite 1322 if (!atUseSite
1483 && variableNames.getName(phi) == variableNames.getName(phi.inputs[1])) { 1323 && variableNames.getName(phi) == variableNames.getName(phi.inputs[1])) {
1484 return false; 1324 return false;
1485 } 1325 }
1486 if (!atUseSite) define(phi); 1326 if (!atUseSite) define(phi);
1487 visitBasicBlock(node.joinBlock); 1327 visitBasicBlock(node.joinBlock);
1488 return true; 1328 return true;
1489 } 1329 }
1490 1330
1491 void generateIf(HIf node, HIfBlockInformation info) { 1331 void generateIf(HIf node, HIfBlockInformation info) {
1332 use(node.inputs[0]);
1333 js.Expression test = pop();
1334
1492 HStatementInformation thenGraph = info.thenGraph; 1335 HStatementInformation thenGraph = info.thenGraph;
1493 HStatementInformation elseGraph = info.elseGraph; 1336 HStatementInformation elseGraph = info.elseGraph;
1494 int thenKind = analyzeGraphForCodegen(thenGraph); 1337 js.Statement thenPart =
1495 int elseKind = analyzeGraphForCodegen(elseGraph); 1338 unwrapStatement(generateStatementsInNewBlock(thenGraph));
1339 js.Statement elsePart =
1340 unwrapStatement(generateStatementsInNewBlock(elseGraph));
1496 1341
1497 void visitWithoutIndent(HStatementInformation toVisit) { 1342 pushStatement(new js.If(test, thenPart, elsePart), node);
1498 int oldIndent = indent;
1499 indent = 0;
1500 generateStatements(toVisit);
1501 indent = oldIndent;
1502 }
1503
1504 void visitExpression(HStatementInformation toVisit) {
1505 // [generateExpression] only works if the [expectedPrecedence] is a
1506 // statement or an expression. We therefore have to duplicate some
1507 // work here.
1508 assert(toVisit.start == toVisit.end);
1509 assert(toVisit.start.last is HGoto);
1510 // Find the expression (there must only be one).
1511 HInstruction expression = toVisit.start.first;
1512 while (generateAtUseSite.contains(expression)) {
1513 expression = expression.next;
1514 }
1515 assert(() {
1516 HInstruction remaining = expression.next;
1517 while (remaining is !HGoto) {
1518 if (!generateAtUseSite.contains(remaining)) return false;
1519 remaining = remaining.next;
1520 }
1521 return true;
1522 });
1523
1524 int oldState = generationState;
1525 generationState = STATE_FIRST_EXPRESSION;
1526 define(expression);
1527 generationState = oldState;
1528 }
1529
1530 void visitWithIndent(HStatementInformation toVisit) {
1531 buffer.add('{\n');
1532 indent++;
1533 generateStatements(toVisit);
1534 indent--;
1535 addIndented('}');
1536 }
1537
1538 void emitIf() {
1539 addIndented('if (');
1540 use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE);
1541 buffer.add(') ');
1542 }
1543
1544 JSBinaryOperatorPrecedence operatorPrecedence = JSPrecedence.binary['&&'];
1545 void generateAnd(HStatementInformation toVisit, Function condition) {
1546 addIndentation();
1547 beginExpression(operatorPrecedence.precedence);
1548 var oldPrecedence = expectedPrecedence;
1549 expectedPrecedence = operatorPrecedence.left;
1550 condition();
1551 buffer.add(" && ");
1552 expectedPrecedence = operatorPrecedence.right;
1553 visitExpression(toVisit);
1554 expectedPrecedence = oldPrecedence;
1555 endExpression(operatorPrecedence.precedence);
1556 buffer.add(";\n");
1557 }
1558
1559 List<HBasicBlock> thenSuccessors = thenGraph.end.successors;
1560 bool thenGraphHasSuccessor = thenSuccessors.length != 0
1561 && thenSuccessors[0] !== currentGraph.exit;
1562
1563 switch (thenKind) {
1564 case EMPTY:
1565 switch (elseKind) {
1566 case EMPTY:
1567 if (isGenerateAtUseSite(node.inputs[0])) {
1568 addIndentation();
1569 use(node.inputs[0], JSPrecedence.STATEMENT_PRECEDENCE);
1570 buffer.add(';\n');
1571 }
1572 break;
1573
1574 case ONE_EXPRESSION:
1575 generateAnd(elseGraph, () { generateNot(node.inputs[0]); });
1576 break;
1577
1578 case ONE_STATEMENT:
1579 addIndented('if (');
1580 generateNot(node.inputs[0]);
1581 buffer.add(') ');
1582 visitWithoutIndent(elseGraph);
1583 break;
1584
1585 case MULTIPLE_STATEMENTS:
1586 addIndented('if (');
1587 generateNot(node.inputs[0]);
1588 buffer.add(') ');
1589 visitWithIndent(elseGraph);
1590 buffer.add('\n');
1591 break;
1592 }
1593
1594 break;
1595
1596 case ONE_EXPRESSION:
1597 case ONE_STATEMENT:
1598 switch (elseKind) {
1599 case EMPTY:
1600 if (thenKind == ONE_EXPRESSION) {
1601 int precedence = operatorPrecedence.left;
1602 generateAnd(thenGraph, () { use(node.inputs[0], precedence); });
1603 } else {
1604 emitIf();
1605 visitWithoutIndent(thenGraph);
1606 }
1607 break;
1608
1609 case ONE_EXPRESSION:
1610 case ONE_STATEMENT:
1611 // TODO(ngeoffray): Generate a conditional.
1612 emitIf();
1613 visitWithoutIndent(thenGraph);
1614 if (thenGraphHasSuccessor) {
1615 addIndented('else ');
1616 visitWithoutIndent(elseGraph);
1617 } else {
1618 generateStatements(elseGraph);
1619 }
1620 break;
1621
1622 case MULTIPLE_STATEMENTS:
1623 emitIf();
1624 visitWithoutIndent(thenGraph);
1625 if (thenGraphHasSuccessor) {
1626 addIndented('else ');
1627 visitWithIndent(elseGraph);
1628 buffer.add('\n');
1629 } else {
1630 generateStatements(elseGraph);
1631 }
1632 break;
1633 }
1634 break;
1635
1636 case MULTIPLE_STATEMENTS:
1637 emitIf();
1638 visitWithIndent(thenGraph);
1639
1640 switch (elseKind) {
1641 case EMPTY:
1642 buffer.add('\n');
1643 break;
1644
1645 case ONE_EXPRESSION:
1646 case ONE_STATEMENT:
1647 if (thenGraphHasSuccessor) {
1648 buffer.add(' else ');
1649 visitWithoutIndent(elseGraph);
1650 } else {
1651 buffer.add('\n');
1652 generateStatements(elseGraph);
1653 }
1654 break;
1655
1656 case MULTIPLE_STATEMENTS:
1657 if (thenGraphHasSuccessor) {
1658 buffer.add(' else ');
1659 visitWithIndent(elseGraph);
1660 buffer.add('\n');
1661 } else {
1662 buffer.add('\n');
1663 generateStatements(elseGraph);
1664 }
1665 break;
1666 }
1667 break;
1668 }
1669 } 1343 }
1670 1344
1671
1672 visitIf(HIf node) { 1345 visitIf(HIf node) {
1673 if (tryControlFlowOperation(node)) return; 1346 if (tryControlFlowOperation(node)) return;
1674 1347
1675 if (subGraph !== null && node.block === subGraph.end) {
1676 if (isGeneratingExpression()) {
1677 use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE);
1678 }
1679 return;
1680 }
1681
1682 HInstruction condition = node.inputs[0]; 1348 HInstruction condition = node.inputs[0];
1683 HIfBlockInformation info = node.blockInformation.body; 1349 HIfBlockInformation info = node.blockInformation.body;
1684 1350
1685 if (condition.isConstant()) { 1351 if (condition.isConstant()) {
1686 HConstant constant = condition; 1352 HConstant constant = condition;
1687 if (constant.constant.isTrue()) { 1353 if (constant.constant.isTrue()) {
1688 generateStatements(info.thenGraph); 1354 generateStatements(info.thenGraph);
1689 } else { 1355 } else {
1690 generateStatements(info.elseGraph); 1356 generateStatements(info.elseGraph);
1691 } 1357 }
(...skipping 12 matching lines...) Expand all
1704 // Visit all the dominated blocks that are not part of the then or else 1370 // Visit all the dominated blocks that are not part of the then or else
1705 // branches, and is not the join block. 1371 // branches, and is not the join block.
1706 // Depending on how the then/else branches terminate 1372 // Depending on how the then/else branches terminate
1707 // (e.g., return/throw/break) there can be any number of these. 1373 // (e.g., return/throw/break) there can be any number of these.
1708 List<HBasicBlock> dominated = node.block.dominatedBlocks; 1374 List<HBasicBlock> dominated = node.block.dominatedBlocks;
1709 for (int i = 2; i < dominated.length; i++) { 1375 for (int i = 2; i < dominated.length; i++) {
1710 visitBasicBlock(dominated[i]); 1376 visitBasicBlock(dominated[i]);
1711 } 1377 }
1712 } 1378 }
1713 1379
1380 js.Call jsPropertyCall(js.Expression receiver,
1381 String fieldName,
1382 List<js.Expression> arguments) {
1383 return new js.Call(new js.PropertyAccess.field(receiver, fieldName),
1384 arguments);
1385 }
1386
1714 visitInvokeDynamicMethod(HInvokeDynamicMethod node) { 1387 visitInvokeDynamicMethod(HInvokeDynamicMethod node) {
1715 beginExpression(JSPrecedence.CALL_PRECEDENCE); 1388 use(node.receiver);
1716 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); 1389 js.Expression object = pop();
1717 buffer.add('.'); 1390 String methodName;
1391 List<js.Expression> arguments;
1392
1718 // Avoid adding the generative constructor name to the list of 1393 // Avoid adding the generative constructor name to the list of
1719 // seen selectors. 1394 // seen selectors.
1720 if (node.inputs[0] is HForeignNew) { 1395 if (node.inputs[0] is HForeignNew) {
1721 HForeignNew foreignNew = node.inputs[0];
1722 // Remove 'this' from the number of arguments.
1723 int argumentCount = node.inputs.length - 1;
1724
1725 // TODO(ahe): The constructor name was statically resolved in 1396 // TODO(ahe): The constructor name was statically resolved in
1726 // SsaBuilder.buildFactory. Is there a cleaner way to do this? 1397 // SsaBuilder.buildFactory. Is there a cleaner way to do this?
1727 node.name.printOn(buffer); 1398 methodName = node.name.slowToString();
1728 visitArguments(node.inputs); 1399 arguments = visitArguments(node.inputs);
1729 } else { 1400 } else {
1730 buffer.add(compiler.namer.instanceMethodInvocationName( 1401 methodName = compiler.namer.instanceMethodInvocationName(
1731 currentLibrary, node.name, node.selector)); 1402 currentLibrary, node.name, node.selector);
1732 visitArguments(node.inputs); 1403 arguments = visitArguments(node.inputs);
1733 bool inLoop = node.block.enclosingLoopHeader !== null; 1404 bool inLoop = node.block.enclosingLoopHeader !== null;
1734 if (node.element !== null) { 1405 if (node.element !== null) {
1735 // If we know we're calling a specific method, register that 1406 // If we know we're calling a specific method, register that
1736 // method only. 1407 // method only.
1737 if (inLoop) { 1408 if (inLoop) {
1738 backend.builder.functionsCalledInLoop.add(node.element); 1409 backend.builder.functionsCalledInLoop.add(node.element);
1739 } 1410 }
1740 world.registerDynamicInvocationOf(node.element); 1411 world.registerDynamicInvocationOf(node.element);
1741 } else { 1412 } else {
1742 Selector selector = getOptimizedSelectorFor(node, node.selector); 1413 Selector selector = getOptimizedSelectorFor(node, node.selector);
1743 world.registerDynamicInvocation(node.name, selector); 1414 world.registerDynamicInvocation(node.name, selector);
1744 if (inLoop) backend.builder.selectorsCalledInLoop[node.name] = selector; 1415 if (inLoop) backend.builder.selectorsCalledInLoop[node.name] = selector;
1745 } 1416 }
1746 } 1417 }
1747 endExpression(JSPrecedence.CALL_PRECEDENCE); 1418 push(jsPropertyCall(object, methodName, arguments), node);
1748 } 1419 }
1749 1420
1750 Selector getOptimizedSelectorFor(HInvoke node, Selector defaultSelector) { 1421 Selector getOptimizedSelectorFor(HInvoke node, Selector defaultSelector) {
1751 Type receiverType = node.inputs[0].propagatedType.computeType(compiler); 1422 Type receiverType = node.inputs[0].propagatedType.computeType(compiler);
1752 if (receiverType !== null) { 1423 if (receiverType !== null) {
1753 return new TypedSelector(receiverType, defaultSelector); 1424 return new TypedSelector(receiverType, defaultSelector);
1754 } else { 1425 } else {
1755 return defaultSelector; 1426 return defaultSelector;
1756 } 1427 }
1757 } 1428 }
1758 1429
1759 visitInvokeDynamicSetter(HInvokeDynamicSetter node) { 1430 visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
1760 beginExpression(JSPrecedence.CALL_PRECEDENCE); 1431 use(node.receiver);
1761 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); 1432 push(jsPropertyCall(pop(),
1762 buffer.add('.'); 1433 compiler.namer.setterName(currentLibrary, node.name),
1763 buffer.add(compiler.namer.setterName(currentLibrary, node.name)); 1434 visitArguments(node.inputs)),
1764 visitArguments(node.inputs); 1435 node);
1765 world.registerDynamicSetter( 1436 world.registerDynamicSetter(
1766 node.name, getOptimizedSelectorFor(node, Selector.SETTER)); 1437 node.name, getOptimizedSelectorFor(node, Selector.SETTER));
1767 endExpression(JSPrecedence.CALL_PRECEDENCE);
1768 } 1438 }
1769 1439
1770 visitInvokeDynamicGetter(HInvokeDynamicGetter node) { 1440 visitInvokeDynamicGetter(HInvokeDynamicGetter node) {
1771 beginExpression(JSPrecedence.CALL_PRECEDENCE); 1441 use(node.receiver);
1772 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); 1442 push(jsPropertyCall(pop(),
1773 buffer.add('.'); 1443 compiler.namer.getterName(currentLibrary, node.name),
1774 buffer.add(compiler.namer.getterName(currentLibrary, node.name)); 1444 visitArguments(node.inputs)),
1775 visitArguments(node.inputs); 1445 node);
1776 world.registerDynamicGetter( 1446 world.registerDynamicGetter(
1777 node.name, getOptimizedSelectorFor(node, Selector.GETTER)); 1447 node.name, getOptimizedSelectorFor(node, Selector.GETTER));
1778 endExpression(JSPrecedence.CALL_PRECEDENCE);
1779 } 1448 }
1780 1449
1781 visitInvokeClosure(HInvokeClosure node) { 1450 visitInvokeClosure(HInvokeClosure node) {
1782 beginExpression(JSPrecedence.CALL_PRECEDENCE); 1451 use(node.receiver);
1783 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); 1452 push(jsPropertyCall(pop(),
1784 buffer.add('.'); 1453 compiler.namer.closureInvocationName(node.selector),
1785 buffer.add(compiler.namer.closureInvocationName(node.selector)); 1454 visitArguments(node.inputs)),
1786 visitArguments(node.inputs); 1455 node);
1787 // TODO(floitsch): we should have a separate list for closure invocations. 1456 // TODO(floitsch): we should have a separate list for closure invocations.
1788 world.registerDynamicInvocation(compiler.namer.CLOSURE_INVOCATION_NAME, 1457 world.registerDynamicInvocation(compiler.namer.CLOSURE_INVOCATION_NAME,
1789 node.selector); 1458 node.selector);
1790 endExpression(JSPrecedence.CALL_PRECEDENCE);
1791 } 1459 }
1792 1460
1793 visitInvokeStatic(HInvokeStatic node) { 1461 visitInvokeStatic(HInvokeStatic node) {
1794 beginExpression(JSPrecedence.CALL_PRECEDENCE); 1462 use(node.target);
1795 use(node.target, JSPrecedence.CALL_PRECEDENCE); 1463 push(new js.Call(pop(), visitArguments(node.inputs)), node);
1796 visitArguments(node.inputs);
1797 endExpression(JSPrecedence.CALL_PRECEDENCE);
1798 } 1464 }
1799 1465
1800 visitInvokeSuper(HInvokeSuper node) { 1466 visitInvokeSuper(HInvokeSuper node) {
1801 beginExpression(JSPrecedence.CALL_PRECEDENCE);
1802 Element superMethod = node.element; 1467 Element superMethod = node.element;
1803 Element superClass = superMethod.enclosingElement; 1468 Element superClass = superMethod.enclosingElement;
1804 // Remove the element and 'this'. 1469 // Remove the element and 'this'.
1805 int argumentCount = node.inputs.length - 2; 1470 int argumentCount = node.inputs.length - 2;
1806 String className = compiler.namer.isolateAccess(superClass); 1471 String className = compiler.namer.isolateAccess(superClass);
1807 if (superMethod.kind == ElementKind.FUNCTION || 1472 if (superMethod.kind == ElementKind.FIELD) {
1808 superMethod.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
1809 String methodName = compiler.namer.instanceMethodName(
1810 currentLibrary, superMethod.name, argumentCount);
1811 buffer.add('$className.prototype.$methodName.call');
1812 visitArguments(node.inputs);
1813 } else if (superMethod.kind == ElementKind.FIELD) {
1814 ClassElement currentClass = work.element.enclosingElement; 1473 ClassElement currentClass = work.element.enclosingElement;
1474 String fieldName;
1815 if (currentClass.isShadowedByField(superMethod)) { 1475 if (currentClass.isShadowedByField(superMethod)) {
1816 buffer.add('this.${compiler.namer.shadowedFieldName(superMethod)}'); 1476 fieldName = compiler.namer.shadowedFieldName(superMethod);
1817 } else { 1477 } else {
1818 LibraryElement library = superMethod.getLibrary(); 1478 LibraryElement library = superMethod.getLibrary();
1819 SourceString name = superMethod.name; 1479 SourceString name = superMethod.name;
1820 buffer.add('this.${compiler.namer.instanceFieldName(library, name)}'); 1480 fieldName = compiler.namer.instanceFieldName(library, name);
1821 } 1481 }
1482 push(new js.PropertyAccess.field(new js.This(), fieldName), node);
1822 } else { 1483 } else {
1823 assert(superMethod.kind == ElementKind.GETTER ||
1824 superMethod.kind == ElementKind.SETTER);
1825 String methodName; 1484 String methodName;
1826 if (superMethod.kind == ElementKind.GETTER) { 1485 if (superMethod.kind == ElementKind.FUNCTION ||
1486 superMethod.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
1487 methodName = compiler.namer.instanceMethodName(
1488 currentLibrary, superMethod.name, argumentCount);
1489 } else if (superMethod.kind == ElementKind.GETTER) {
1827 methodName = 1490 methodName =
1828 compiler.namer.getterName(currentLibrary, superMethod.name); 1491 compiler.namer.getterName(currentLibrary, superMethod.name);
1829 } else { 1492 } else {
1493 assert(superMethod.kind == ElementKind.SETTER);
1830 methodName = 1494 methodName =
1831 compiler.namer.setterName(currentLibrary, superMethod.name); 1495 compiler.namer.setterName(currentLibrary, superMethod.name);
1832 } 1496 }
1833 buffer.add('$className.prototype.$methodName.call'); 1497 js.VariableUse classReference = new js.VariableUse(className);
1834 visitArguments(node.inputs); 1498 js.PropertyAccess prototype =
1499 new js.PropertyAccess.field(classReference, "prototype");
1500 js.PropertyAccess method =
1501 new js.PropertyAccess.field(prototype, methodName);
1502 push(jsPropertyCall(method, "call", visitArguments(node.inputs)), node);
1835 } 1503 }
1836 endExpression(JSPrecedence.CALL_PRECEDENCE);
1837 world.registerStaticUse(superMethod); 1504 world.registerStaticUse(superMethod);
1838 } 1505 }
1839 1506
1840 visitFieldGet(HFieldGet node) { 1507 visitFieldGet(HFieldGet node) {
1841 String name = 1508 String name =
1842 compiler.namer.instanceFieldName(node.library, node.fieldName); 1509 compiler.namer.instanceFieldName(node.library, node.fieldName);
1843 beginExpression(JSPrecedence.MEMBER_PRECEDENCE); 1510 use(node.receiver);
1844 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); 1511 push(new js.PropertyAccess.field(pop(), name), node);
1845 buffer.add('.');
1846 buffer.add(name);
1847 beginExpression(JSPrecedence.MEMBER_PRECEDENCE);
1848 if (node.element == null) { 1512 if (node.element == null) {
1849 // If we don't have an element we register a dynamic field getter. 1513 // If we don't have an element we register a dynamic field getter.
1850 // This might lead to unnecessary getters, but these cases should be 1514 // This might lead to unnecessary getters, but these cases should be
1851 // rare. 1515 // rare.
1852 world.registerDynamicGetter(node.fieldName, Selector.GETTER); 1516 world.registerDynamicGetter(node.fieldName, Selector.GETTER);
1853 } else { 1517 } else {
1854 Type type = node.receiver.propagatedType.computeType(compiler); 1518 Type type = node.receiver.propagatedType.computeType(compiler);
1855 if (type != null) { 1519 if (type != null) {
1856 world.registerFieldGetter(node.element.name, type); 1520 world.registerFieldGetter(node.element.name, type);
1857 } 1521 }
(...skipping 17 matching lines...) Expand all
1875 if (node.element != null && 1539 if (node.element != null &&
1876 work.element.isGenerativeConstructorBody() && 1540 work.element.isGenerativeConstructorBody() &&
1877 node.element.enclosingElement.isClass() && 1541 node.element.enclosingElement.isClass() &&
1878 node.value.hasGuaranteedType() && 1542 node.value.hasGuaranteedType() &&
1879 node.block.dominates(currentGraph.exit)) { 1543 node.block.dominates(currentGraph.exit)) {
1880 backend.updateFieldConstructorSetters(node.element, 1544 backend.updateFieldConstructorSetters(node.element,
1881 node.value.guaranteedType); 1545 node.value.guaranteedType);
1882 } 1546 }
1883 String name = 1547 String name =
1884 compiler.namer.instanceFieldName(node.library, node.fieldName); 1548 compiler.namer.instanceFieldName(node.library, node.fieldName);
1885 beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
1886 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
1887 buffer.add('.');
1888 buffer.add(name);
1889 if (node.element == null) { 1549 if (node.element == null) {
1890 // If we don't have an element we register a dynamic field setter. 1550 // If we don't have an element we register a dynamic field setter.
1891 // This might lead to unnecessary setters, but these cases should be 1551 // This might lead to unnecessary setters, but these cases should be
1892 // rare. 1552 // rare.
1893 world.registerDynamicSetter(node.fieldName, Selector.SETTER); 1553 world.registerDynamicSetter(node.fieldName, Selector.SETTER);
1894 } else { 1554 } else {
1895 Type type = node.receiver.propagatedType.computeType(compiler); 1555 Type type = node.receiver.propagatedType.computeType(compiler);
1896 if (type != null) { 1556 if (type != null) {
1897 if (!work.element.isGenerativeConstructorBody()) { 1557 if (!work.element.isGenerativeConstructorBody()) {
1898 world.registerFieldSetter(node.element.name, type); 1558 world.registerFieldSetter(node.element.name, type);
1899 } 1559 }
1900 // Determine the types seen so far for the field. If only number 1560 // Determine the types seen so far for the field. If only number
1901 // types have been seen and the value of the field set is a 1561 // types have been seen and the value of the field set is a
1902 // simple number computation only depending on that field, we 1562 // simple number computation only depending on that field, we
1903 // can safely keep the number type for the field. 1563 // can safely keep the number type for the field.
1904 HType fieldSettersType = backend.fieldSettersTypeSoFar(node.element); 1564 HType fieldSettersType = backend.fieldSettersTypeSoFar(node.element);
1905 HType initializersType = 1565 HType initializersType =
1906 backend.typeFromInitializersSoFar(node.element); 1566 backend.typeFromInitializersSoFar(node.element);
1907 HType fieldType = fieldSettersType.union(initializersType); 1567 HType fieldType = fieldSettersType.union(initializersType);
1908 if (HType.NUMBER.union(fieldType) == HType.NUMBER && 1568 if (HType.NUMBER.union(fieldType) == HType.NUMBER &&
1909 isSimpleFieldNumberComputation(node.value, node)) { 1569 isSimpleFieldNumberComputation(node.value, node)) {
1910 backend.updateFieldSetters(node.element, HType.NUMBER); 1570 backend.updateFieldSetters(node.element, HType.NUMBER);
1911 } else { 1571 } else {
1912 backend.updateFieldSetters(node.element, 1572 backend.updateFieldSetters(node.element,
1913 node.value.propagatedType); 1573 node.value.propagatedType);
1914 } 1574 }
1915 } 1575 }
1916 } 1576 }
1917 buffer.add(' = '); 1577 use(node.receiver);
1918 use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE); 1578 js.Expression receiver = pop();
1919 endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); 1579 use(node.value);
1580 push(new js.Assignment(new js.PropertyAccess.field(receiver, name), pop()),
1581 node);
1920 } 1582 }
1921 1583
1922 visitLocalGet(HLocalGet node) { 1584 visitLocalGet(HLocalGet node) {
1923 use(node.receiver, JSPrecedence.EXPRESSION_PRECEDENCE); 1585 use(node.receiver);
1924 } 1586 }
1925 1587
1926 visitLocalSet(HLocalSet node) { 1588 visitLocalSet(HLocalSet node) {
1927 declareInstruction(node.receiver); 1589 use(node.value);
1928 buffer.add(' = '); 1590 assignVariable(variableNames.getName(node.receiver), pop());
1929 use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE);
1930 } 1591 }
1931 1592
1932 visitForeign(HForeign node) { 1593 visitForeign(HForeign node) {
1933 String code = node.code.slowToString(); 1594 String code = node.code.slowToString();
1934 List<HInstruction> inputs = node.inputs; 1595 List<HInstruction> inputs = node.inputs;
1935 List<String> parts = code.split('#'); 1596 if (node.isStatement) {
1936 if (parts.length != inputs.length + 1) { 1597 if (!inputs.isEmpty()) {
1937 compiler.internalError( 1598 compiler.internalError("foreign statement with inputs: $code",
1938 'Wrong number of arguments for JS', instruction: node); 1599 instruction: node);
1600 }
1601 pushStatement(new js.LiteralStatement(code), node);
1602 } else {
1603 List<js.Expression> data = <js.Expression>[];
1604 for (int i = 0; i < inputs.length; i++) {
1605 use(inputs[i]);
1606 data.add(pop());
1607 }
1608 push(new js.LiteralExpression.withData(code, data), node);
1939 } 1609 }
1940 beginExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
1941 buffer.add(parts[0]);
1942 for (int i = 0; i < inputs.length; i++) {
1943 use(inputs[i], JSPrecedence.EXPRESSION_PRECEDENCE);
1944 buffer.add(parts[i + 1]);
1945 }
1946 endExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
1947 } 1610 }
1948 1611
1949 visitForeignNew(HForeignNew node) { 1612 visitForeignNew(HForeignNew node) {
1950 int j = 0; 1613 int j = 0;
1951 node.element.forEachInstanceField( 1614 node.element.forEachInstanceField(
1952 includeBackendMembers: true, 1615 includeBackendMembers: true,
1953 includeSuperMembers: true, 1616 includeSuperMembers: true,
1954 f: (ClassElement enclosingClass, Element member) { 1617 f: (ClassElement enclosingClass, Element member) {
1955 backend.updateFieldInitializers(member, 1618 backend.updateFieldInitializers(member,
1956 node.inputs[j].propagatedType); 1619 node.inputs[j].propagatedType);
1957 j++; 1620 j++;
1958 }); 1621 });
1959 String jsClassReference = compiler.namer.isolateAccess(node.element); 1622 String jsClassReference = compiler.namer.isolateAccess(node.element);
1960 beginExpression(JSPrecedence.MEMBER_PRECEDENCE); 1623 List<HInstruction> inputs = node.inputs;
1961 buffer.add('new $jsClassReference(');
1962 // We can't use 'visitArguments', since our arguments start at input[0]. 1624 // We can't use 'visitArguments', since our arguments start at input[0].
1963 List<HInstruction> inputs = node.inputs; 1625 List<js.Expression> arguments = <js.Expression>[];
1964 for (int i = 0; i < inputs.length; i++) { 1626 for (int i = 0; i < inputs.length; i++) {
1965 if (i != 0) buffer.add(', '); 1627 use(inputs[i]);
1966 use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); 1628 arguments.add(pop());
1967 } 1629 }
1968 buffer.add(')'); 1630 // TODO(floitsch): jsClassReference is an Access. We shouldn't treat it
1969 endExpression(JSPrecedence.MEMBER_PRECEDENCE); 1631 // as if it was a string.
1632 push(new js.New(new js.VariableUse(jsClassReference), arguments), node);
1970 } 1633 }
1971 1634
1972 void generateConstant(Constant constant) { 1635 void generateConstant(Constant constant) {
1973 // TODO(floitsch): the compile-time constant handler and the codegen
1974 // need to work together to avoid the parenthesis. See r4928 for an
1975 // implementation that still dealt with precedence.
1976 ConstantHandler handler = compiler.constantHandler; 1636 ConstantHandler handler = compiler.constantHandler;
1977 String name = handler.getNameForConstant(constant); 1637 String name = handler.getNameForConstant(constant);
1978 if (name === null) { 1638 if (name === null) {
1979 assert(!constant.isObject()); 1639 assert(!constant.isObject());
1980 if (constant.isNum() 1640 if (constant.isBool()) {
1981 && expectedPrecedence == JSPrecedence.MEMBER_PRECEDENCE) { 1641 push(new js.LiteralBool((constant as BoolConstant).value));
1982 buffer.add('('); 1642 } else if (constant.isNum()) {
1643 // TODO(floitsch): get rid of the code buffer.
1644 CodeBuffer buffer = new CodeBuffer();
1983 handler.writeConstant(buffer, constant); 1645 handler.writeConstant(buffer, constant);
1984 buffer.add(')'); 1646 push(new js.LiteralNumber(buffer.toString()));
1647 } else if (constant.isNull()) {
1648 push(new js.LiteralNull());
1649 } else if (constant.isString()) {
1650 // TODO(floitsch): get rid of the code buffer.
1651 CodeBuffer buffer = new CodeBuffer();
1652 handler.writeConstant(buffer, constant);
1653 push(new js.LiteralString(buffer.toString()));
1985 } else { 1654 } else {
1986 handler.writeConstant(buffer, constant); 1655 compiler.internalError("Forgot constant $constant");
1987 } 1656 }
1988 } else { 1657 } else {
1989 buffer.add(compiler.namer.CURRENT_ISOLATE); 1658 js.VariableUse currentIsolateUse =
1990 buffer.add("."); 1659 new js.VariableUse(compiler.namer.CURRENT_ISOLATE);
1991 buffer.add(name); 1660 push(new js.PropertyAccess.field(currentIsolateUse, name));
1992 } 1661 }
1993
1994 } 1662 }
1995 1663
1996 visitConstant(HConstant node) { 1664 visitConstant(HConstant node) {
1997 assert(isGenerateAtUseSite(node)); 1665 assert(isGenerateAtUseSite(node));
1998 generateConstant(node.constant); 1666 generateConstant(node.constant);
1999 } 1667 }
2000 1668
2001 visitLoopBranch(HLoopBranch node) { 1669 visitLoopBranch(HLoopBranch node) {
2002 if (subGraph !== null && node.block === subGraph.end) { 1670 if (subGraph !== null && node.block === subGraph.end) {
2003 // We are generating code for a loop condition. 1671 // We are generating code for a loop condition.
2004 // If doing this as part of a SubGraph traversal, the 1672 // If doing this as part of a SubGraph traversal, the
2005 // calling code will handle the control flow logic. 1673 // calling code will handle the control flow logic.
2006 1674
2007 // If we are generating the subgraph as an expression, the 1675 // If we are generating the subgraph as an expression, the
2008 // condition will be generated as the expression. 1676 // condition will be generated as the expression.
2009 // Otherwise, we don't generate the expression, and leave that 1677 // Otherwise, we don't generate the expression, and leave that
2010 // to the code that called [visitSubGraph]. 1678 // to the code that called [visitSubGraph].
2011 if (isGeneratingExpression()) { 1679 if (isGeneratingExpression) {
2012 use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE); 1680 use(node.inputs[0]);
2013 } 1681 }
2014 return; 1682 return;
2015 } 1683 }
2016 HBasicBlock branchBlock = currentBlock; 1684 HBasicBlock branchBlock = currentBlock;
2017 addIndentation();
2018 handleLoopCondition(node); 1685 handleLoopCondition(node);
2019 List<HBasicBlock> dominated = currentBlock.dominatedBlocks; 1686 List<HBasicBlock> dominated = currentBlock.dominatedBlocks;
2020 // For a do while loop, the body has already been visited. 1687 // For a do while loop, the body has already been visited.
2021 if (!node.isDoWhile()) { 1688 if (!node.isDoWhile()) {
2022 visitBasicBlock(dominated[0]); 1689 visitBasicBlock(dominated[0]);
2023 } 1690 }
2024 endLoop(node.block); 1691 endLoop(node.block);
2025 1692
2026 // If the branch does not dominate the code after the loop, the 1693 // If the branch does not dominate the code after the loop, the
2027 // dominator will visit it. 1694 // dominator will visit it.
2028 if (branchBlock.successors[1].dominator !== branchBlock) return; 1695 if (branchBlock.successors[1].dominator !== branchBlock) return;
2029 1696
2030 visitBasicBlock(branchBlock.successors[1]); 1697 visitBasicBlock(branchBlock.successors[1]);
2031 // With labeled breaks we can have more dominated blocks. 1698 // With labeled breaks we can have more dominated blocks.
2032 if (dominated.length >= 3) { 1699 if (dominated.length >= 3) {
2033 for (int i = 2; i < dominated.length; i++) { 1700 for (int i = 2; i < dominated.length; i++) {
2034 visitBasicBlock(dominated[i]); 1701 visitBasicBlock(dominated[i]);
2035 } 1702 }
2036 } 1703 }
2037 } 1704 }
2038 1705
2039 visitNot(HNot node) { 1706 visitNot(HNot node) {
2040 assert(node.inputs.length == 1); 1707 assert(node.inputs.length == 1);
2041 generateNot(node.inputs[0]); 1708 generateNot(node.inputs[0]);
1709 attachLocationToLast(node);
2042 } 1710 }
2043 1711
2044 1712
2045 void generateNot(HInstruction input) { 1713 void generateNot(HInstruction input) {
2046 bool isBuiltinRelational(HInstruction instruction) { 1714 bool isBuiltinRelational(HInstruction instruction) {
2047 if (instruction is !HRelational) return false; 1715 if (instruction is !HRelational) return false;
2048 HRelational relational = instruction; 1716 HRelational relational = instruction;
2049 return relational.builtin; 1717 return relational.builtin;
2050 } 1718 }
2051 1719
2052 if (input is HBoolify && isGenerateAtUseSite(input)) { 1720 if (input is HBoolify && isGenerateAtUseSite(input)) {
2053 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 1721 use(input.inputs[0]);
2054 use(input.inputs[0], JSPrecedence.EQUALITY_PRECEDENCE); 1722 push(new js.Binary("!==", pop(), new js.LiteralBool(true)), input);
2055 buffer.add(' !== true');
2056 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2057 } else if (isBuiltinRelational(input) && 1723 } else if (isBuiltinRelational(input) &&
2058 isGenerateAtUseSite(input) && 1724 isGenerateAtUseSite(input) &&
2059 input.inputs[0].propagatedType.isUseful() && 1725 input.inputs[0].propagatedType.isUseful() &&
2060 !input.inputs[0].isDouble() && 1726 !input.inputs[0].isDouble() &&
2061 input.inputs[1].propagatedType.isUseful() && 1727 input.inputs[1].propagatedType.isUseful() &&
2062 !input.inputs[1].isDouble()) { 1728 !input.inputs[1].isDouble()) {
2063 // This optimization doesn't work for NaN, so we only do it if the 1729 // This optimization doesn't work for NaN, so we only do it if the
2064 // type is known to be non-Double. 1730 // type is known to be non-Double.
2065 Map<String, String> inverseOperator = const <String>{ 1731 Map<String, String> inverseOperator = const <String>{
2066 "==" : "!=", 1732 "==" : "!=",
2067 "!=" : "==", 1733 "!=" : "==",
2068 "===": "!==", 1734 "===": "!==",
2069 "!==": "===", 1735 "!==": "===",
2070 "<" : ">=", 1736 "<" : ">=",
2071 "<=" : ">", 1737 "<=" : ">",
2072 ">" : "<=", 1738 ">" : "<=",
2073 ">=" : "<" 1739 ">=" : "<"
2074 }; 1740 };
2075 HRelational relational = input; 1741 HRelational relational = input;
2076 visitInvokeBinary(input, 1742 visitInvokeBinary(input,
2077 inverseOperator[relational.operation.name.stringValue]); 1743 inverseOperator[relational.operation.name.stringValue]);
2078 } else { 1744 } else {
2079 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); 1745 use(input);
2080 buffer.add('!'); 1746 push(new js.Prefix("!", pop()));
2081 use(input, JSPrecedence.PREFIX_PRECEDENCE);
2082 endExpression(JSPrecedence.PREFIX_PRECEDENCE);
2083 } 1747 }
2084 } 1748 }
2085 1749
2086 visitParameterValue(HParameterValue node) => visitLocalValue(node); 1750 visitParameterValue(HParameterValue node) => visitLocalValue(node);
2087 1751
2088 visitLocalValue(HLocalValue node) { 1752 visitLocalValue(HLocalValue node) {
2089 assert(isGenerateAtUseSite(node)); 1753 assert(isGenerateAtUseSite(node));
2090 buffer.add(variableNames.getName(node)); 1754 push(new js.VariableUse(variableNames.getName(node)), node);
2091 } 1755 }
2092 1756
2093 visitPhi(HPhi node) { 1757 visitPhi(HPhi node) {
2094 // This method is only called for phis that are generated at use 1758 // This method is only called for phis that are generated at use
2095 // site. A phi can be generated at use site only if it is the 1759 // site. A phi can be generated at use site only if it is the
2096 // result of a control flow operation. 1760 // result of a control flow operation.
2097 HBasicBlock ifBlock = node.block.dominator; 1761 HBasicBlock ifBlock = node.block.dominator;
2098 assert(controlFlowOperators.contains(ifBlock.last)); 1762 assert(controlFlowOperators.contains(ifBlock.last));
2099 HInstruction input = ifBlock.last.inputs[0]; 1763 HInstruction input = ifBlock.last.inputs[0];
2100 if (input.isConstantFalse()) { 1764 if (input.isConstantFalse()) {
2101 use(node.inputs[1], expectedPrecedence); 1765 use(node.inputs[1]);
2102 } else if (input.isConstantTrue()) { 1766 } else if (input.isConstantTrue()) {
2103 use(node.inputs[0], expectedPrecedence); 1767 use(node.inputs[0]);
2104 } else if (node.inputs[1].isConstantBoolean()) { 1768 } else if (node.inputs[1].isConstantBoolean()) {
2105 String operation = node.inputs[1].isConstantFalse() ? '&&' : '||'; 1769 String operation = node.inputs[1].isConstantFalse() ? '&&' : '||';
2106 JSBinaryOperatorPrecedence operatorPrecedence =
2107 JSPrecedence.binary[operation];
2108 beginExpression(operatorPrecedence.precedence);
2109 if (operation == '||') { 1770 if (operation == '||') {
2110 if (input is HNot) { 1771 if (input is HNot) {
2111 use(input.inputs[0], operatorPrecedence.left); 1772 use(input.inputs[0]);
2112 } else { 1773 } else {
2113 generateNot(input); 1774 generateNot(input);
2114 } 1775 }
2115 } else { 1776 } else {
2116 use(input, operatorPrecedence.left); 1777 use(input);
2117 } 1778 }
2118 buffer.add(" $operation "); 1779 js.Expression left = pop();
2119 use(node.inputs[0], operatorPrecedence.right); 1780 use(node.inputs[0]);
2120 endExpression(operatorPrecedence.precedence); 1781 push(new js.Binary(operation, left, pop()));
2121 } else { 1782 } else {
2122 beginExpression(JSPrecedence.CONDITIONAL_PRECEDENCE); 1783 use(input);
2123 use(input, JSPrecedence.LOGICAL_OR_PRECEDENCE); 1784 js.Expression test = pop();
2124 buffer.add(' ? '); 1785 use(node.inputs[0]);
2125 use(node.inputs[0], JSPrecedence.ASSIGNMENT_PRECEDENCE); 1786 js.Expression then = pop();
2126 buffer.add(' : '); 1787 use(node.inputs[1]);
2127 use(node.inputs[1], JSPrecedence.ASSIGNMENT_PRECEDENCE); 1788 push(new js.Conditional(test, then, pop()));
2128 endExpression(JSPrecedence.CONDITIONAL_PRECEDENCE);
2129 } 1789 }
2130 } 1790 }
2131 1791
2132 visitReturn(HReturn node) { 1792 visitReturn(HReturn node) {
2133 addIndentation();
2134 assert(node.inputs.length == 1); 1793 assert(node.inputs.length == 1);
2135 HInstruction input = node.inputs[0]; 1794 HInstruction input = node.inputs[0];
2136 if (input.isConstantNull()) { 1795 if (input.isConstantNull()) {
2137 buffer.add('return;\n'); 1796 pushStatement(new js.Return(null), node);
2138 } else { 1797 } else {
2139 buffer.add('return '); 1798 use(node.inputs[0]);
2140 use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE); 1799 pushStatement(new js.Return(pop()), node);
2141 buffer.add(';\n');
2142 } 1800 }
2143 } 1801 }
2144 1802
2145 visitThis(HThis node) { 1803 visitThis(HThis node) {
2146 buffer.add('this'); 1804 push(new js.This());
2147 } 1805 }
2148 1806
2149 visitThrow(HThrow node) { 1807 visitThrow(HThrow node) {
2150 addIndentation();
2151 if (node.isRethrow) { 1808 if (node.isRethrow) {
2152 buffer.add('throw '); 1809 use(node.inputs[0]);
2153 use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE); 1810 pushStatement(new js.Throw(pop()), node);
2154 } else { 1811 } else {
2155 generateThrowWithHelper('captureStackTrace', node.inputs[0]); 1812 generateThrowWithHelper('captureStackTrace', node.inputs[0]);
2156 } 1813 }
2157 buffer.add(';\n');
2158 } 1814 }
2159 1815
2160 visitBoundsCheck(HBoundsCheck node) { 1816 visitBoundsCheck(HBoundsCheck node) {
2161 // TODO(ngeoffray): Separate the two checks of the bounds check, so, 1817 // TODO(ngeoffray): Separate the two checks of the bounds check, so,
2162 // e.g., the zero checks can be shared if possible. 1818 // e.g., the zero checks can be shared if possible.
2163 1819
2164 // If the checks always succeede, we would have removed the bounds check 1820 // If the checks always succeeds, we would have removed the bounds check
2165 // completely. 1821 // completely.
2166 assert(node.staticChecks != HBoundsCheck.ALWAYS_TRUE); 1822 assert(node.staticChecks != HBoundsCheck.ALWAYS_TRUE);
2167 if (node.staticChecks != HBoundsCheck.ALWAYS_FALSE) { 1823 if (node.staticChecks != HBoundsCheck.ALWAYS_FALSE) {
2168 buffer.add('if ('); 1824 js.Binary under;
2169 if (node.staticChecks != HBoundsCheck.ALWAYS_ABOVE_ZERO) { 1825 if (node.staticChecks != HBoundsCheck.ALWAYS_ABOVE_ZERO) {
2170 assert(node.staticChecks == HBoundsCheck.FULL_CHECK); 1826 assert(node.staticChecks == HBoundsCheck.FULL_CHECK);
2171 use(node.index, JSPrecedence.RELATIONAL_PRECEDENCE); 1827 use(node.index);
2172 buffer.add(' < 0 || '); 1828 under = new js.Binary("<", pop(), new js.LiteralNumber("0"));
2173 } 1829 }
2174 use(node.index, JSPrecedence.RELATIONAL_PRECEDENCE); 1830 use(node.index);
2175 buffer.add(' >= '); 1831 js.Expression index = pop();
2176 use(node.length, JSPrecedence.SHIFT_PRECEDENCE); 1832 use(node.length);
2177 buffer.add(") "); 1833 js.Binary over = new js.Binary(">=", index, pop());
1834 js.Binary underOver =
1835 under == null ? over : new js.Binary("||", under, over);
1836 js.Statement thenBody = new js.Block.empty();
1837 js.Block oldContainer = currentContainer;
1838 currentContainer = thenBody;
1839 generateThrowWithHelper('ioore', node.index);
1840 currentContainer = oldContainer;
1841 thenBody = unwrapStatement(thenBody);
1842 pushStatement(new js.If.then(underOver, thenBody), node);
1843 } else {
1844 generateThrowWithHelper('ioore', node.index);
2178 } 1845 }
2179 generateThrowWithHelper('ioore', node.index);
2180 } 1846 }
2181 1847
2182 visitIntegerCheck(HIntegerCheck node) { 1848 visitIntegerCheck(HIntegerCheck node) {
2183 if (!node.alwaysFalse) { 1849 if (!node.alwaysFalse) {
2184 buffer.add('if (');
2185 checkInt(node.value, '!=='); 1850 checkInt(node.value, '!==');
2186 buffer.add(') '); 1851 js.Expression test = pop();
1852 js.Statement thenBody = new js.Block.empty();
1853 js.Block oldContainer = currentContainer;
1854 currentContainer = thenBody;
1855 generateThrowWithHelper('iae', node.value);
1856 currentContainer = oldContainer;
1857 thenBody = unwrapStatement(thenBody);
1858 pushStatement(new js.If.then(test, thenBody), node);
1859 } else {
1860 generateThrowWithHelper('iae', node.value);
2187 } 1861 }
2188 generateThrowWithHelper('iae', node.value);
2189 } 1862 }
2190 1863
2191 void generateThrowWithHelper(String helperName, HInstruction argument) { 1864 void generateThrowWithHelper(String helperName, HInstruction argument) {
2192 Element helper = compiler.findHelper(new SourceString(helperName)); 1865 Element helper = compiler.findHelper(new SourceString(helperName));
2193 world.registerStaticUse(helper); 1866 world.registerStaticUse(helper);
2194 buffer.add('throw '); 1867 js.VariableUse jsHelper =
2195 beginExpression(JSPrecedence.EXPRESSION_PRECEDENCE); 1868 new js.VariableUse(compiler.namer.isolateAccess(helper));
2196 beginExpression(JSPrecedence.CALL_PRECEDENCE); 1869 js.Call value = new js.Call(jsHelper, visitArguments([null, argument]));
2197 buffer.add(compiler.namer.isolateAccess(helper)); 1870 attachLocation(value, argument);
2198 visitArguments([null, argument]); 1871 pushStatement(new js.Throw(value));
2199 endExpression(JSPrecedence.CALL_PRECEDENCE);
2200 endExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
2201 }
2202
2203 void addIndentation() {
2204 for (int i = 0; i < indent; i++) {
2205 buffer.add(' ');
2206 }
2207 }
2208
2209 void addIndented(String text) {
2210 addIndentation();
2211 buffer.add(text);
2212 } 1872 }
2213 1873
2214 void visitSwitch(HSwitch node) { 1874 void visitSwitch(HSwitch node) {
2215 // Switches are handled using [visitSwitchInfo]. 1875 // Switches are handled using [visitSwitchInfo].
2216 } 1876 }
2217 1877
2218 void visitStatic(HStatic node) { 1878 void visitStatic(HStatic node) {
2219 world.registerStaticUse(node.element); 1879 world.registerStaticUse(node.element);
2220 buffer.add(compiler.namer.isolateAccess(node.element)); 1880 push(new js.VariableUse(compiler.namer.isolateAccess(node.element)));
2221 } 1881 }
2222 1882
2223 void visitStaticStore(HStaticStore node) { 1883 void visitStaticStore(HStaticStore node) {
2224 world.registerStaticUse(node.element); 1884 world.registerStaticUse(node.element);
2225 beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); 1885 js.VariableUse variableUse =
2226 buffer.add(compiler.namer.isolateAccess(node.element)); 1886 new js.VariableUse(compiler.namer.isolateAccess(node.element));
2227 buffer.add(' = '); 1887 use(node.inputs[0]);
2228 use(node.inputs[0], JSPrecedence.ASSIGNMENT_PRECEDENCE); 1888 push(new js.Assignment(variableUse, pop()), node);
2229 endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
2230 } 1889 }
2231 1890
2232 void visitStringConcat(HStringConcat node) { 1891 void visitStringConcat(HStringConcat node) {
2233 if (isEmptyString(node.left)) { 1892 if (isEmptyString(node.left)) {
2234 useStringified(node.right, expectedPrecedence); 1893 useStringified(node.right);
2235 } else if (isEmptyString(node.right)) { 1894 } else if (isEmptyString(node.right)) {
2236 useStringified(node.left, expectedPrecedence); 1895 useStringified(node.left);
2237 } else { 1896 } else {
2238 JSBinaryOperatorPrecedence operatorPrecedences = JSPrecedence.binary['+']; 1897 useStringified(node.left);
2239 beginExpression(operatorPrecedences.precedence); 1898 js.Expression left = pop();
2240 useStringified(node.left, operatorPrecedences.left); 1899 useStringified(node.right);
2241 buffer.add(' + '); 1900 push(new js.Binary("+", left, pop()), node);
2242 // If the right hand side is a string concatenation itself it is
2243 // safe to make it left associative.
2244 int rightPrecedence = (node.right is HStringConcat)
2245 ? JSPrecedence.ADDITIVE_PRECEDENCE
2246 : operatorPrecedences.right;
2247 useStringified(node.right, rightPrecedence);
2248 endExpression(operatorPrecedences.precedence);
2249 } 1901 }
2250 } 1902 }
2251 1903
2252 bool isEmptyString(HInstruction node) { 1904 bool isEmptyString(HInstruction node) {
2253 if (!node.isConstantString()) return false; 1905 if (!node.isConstantString()) return false;
2254 HConstant constant = node; 1906 HConstant constant = node;
2255 StringConstant string = constant.constant; 1907 StringConstant string = constant.constant;
2256 return string.value.length == 0; 1908 return string.value.length == 0;
2257 } 1909 }
2258 1910
2259 void useStringified(HInstruction node, int precedence) { 1911 void useStringified(HInstruction node) {
2260 if (node.isString()) { 1912 if (node.isString()) {
2261 use(node, precedence); 1913 use(node);
2262 } else { 1914 } else {
2263 Element convertToString = compiler.findHelper(const SourceString("S")); 1915 Element convertToString = compiler.findHelper(const SourceString("S"));
2264 world.registerStaticUse(convertToString); 1916 world.registerStaticUse(convertToString);
2265 buffer.add(compiler.namer.isolateAccess(convertToString)); 1917 js.VariableUse variableUse =
2266 buffer.add('('); 1918 new js.VariableUse(compiler.namer.isolateAccess(convertToString));
2267 use(node, JSPrecedence.EXPRESSION_PRECEDENCE); 1919 use(node);
2268 buffer.add(')'); 1920 push(new js.Call(variableUse, <js.Expression>[pop()]), node);
2269 } 1921 }
2270 } 1922 }
2271 1923
2272 void visitLiteralList(HLiteralList node) { 1924 void visitLiteralList(HLiteralList node) {
2273 generateArrayLiteral(node); 1925 generateArrayLiteral(node);
2274 } 1926 }
2275 1927
2276 void generateArrayLiteral(HLiteralList node) { 1928 void generateArrayLiteral(HLiteralList node) {
2277 buffer.add('[');
2278 int len = node.inputs.length; 1929 int len = node.inputs.length;
1930 List<js.ArrayElement> elements = <js.ArrayElement>[];
2279 for (int i = 0; i < len; i++) { 1931 for (int i = 0; i < len; i++) {
2280 if (i != 0) buffer.add(', '); 1932 use(node.inputs[i]);
2281 use(node.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); 1933 elements.add(new js.ArrayElement(i, pop()));
2282 } 1934 }
2283 buffer.add(']'); 1935 push(new js.ArrayInitialization(len, elements), node);
2284 } 1936 }
2285 1937
2286 void visitIndex(HIndex node) { 1938 void visitIndex(HIndex node) {
2287 if (node.builtin) { 1939 if (node.builtin) {
2288 beginExpression(JSPrecedence.MEMBER_PRECEDENCE); 1940 use(node.inputs[1]);
2289 use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE); 1941 js.Expression receiver = pop();
2290 buffer.add('['); 1942 use(node.inputs[2]);
2291 use(node.inputs[2], JSPrecedence.EXPRESSION_PRECEDENCE); 1943 push(new js.PropertyAccess(receiver, pop()), node);
2292 buffer.add(']');
2293 endExpression(JSPrecedence.MEMBER_PRECEDENCE);
2294 } else { 1944 } else {
2295 visitInvokeStatic(node); 1945 visitInvokeStatic(node);
2296 } 1946 }
2297 } 1947 }
2298 1948
2299 void visitIndexAssign(HIndexAssign node) { 1949 void visitIndexAssign(HIndexAssign node) {
2300 if (node.builtin) { 1950 if (node.builtin) {
2301 beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); 1951 use(node.inputs[1]);
2302 use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE); 1952 js.Expression receiver = pop();
2303 buffer.add('['); 1953 use(node.inputs[2]);
2304 use(node.inputs[2], JSPrecedence.EXPRESSION_PRECEDENCE); 1954 js.Expression index = pop();
2305 buffer.add('] = '); 1955 use(node.inputs[3]);
2306 use(node.inputs[3], JSPrecedence.ASSIGNMENT_PRECEDENCE); 1956 push(new js.Assignment(new js.PropertyAccess(receiver, index), pop()),
2307 endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); 1957 node);
2308 } else { 1958 } else {
2309 visitInvokeStatic(node); 1959 visitInvokeStatic(node);
2310 } 1960 }
2311 } 1961 }
2312 1962
2313 String builtinJsName(HInvokeInterceptor interceptor) { 1963 String builtinJsName(HInvokeInterceptor interceptor) {
2314 // Don't count the target method or the receiver in the arity. 1964 // Don't count the target method or the receiver in the arity.
2315 int arity = interceptor.inputs.length - 2; 1965 int arity = interceptor.inputs.length - 2;
2316 HInstruction receiver = interceptor.inputs[1]; 1966 HInstruction receiver = interceptor.inputs[1];
2317 bool getter = interceptor.getter; 1967 bool getter = interceptor.getter;
(...skipping 16 matching lines...) Expand all
2334 } 1984 }
2335 } 1985 }
2336 1986
2337 return null; 1987 return null;
2338 } 1988 }
2339 1989
2340 void visitInvokeInterceptor(HInvokeInterceptor node) { 1990 void visitInvokeInterceptor(HInvokeInterceptor node) {
2341 String builtin = builtinJsName(node); 1991 String builtin = builtinJsName(node);
2342 if (builtin !== null) { 1992 if (builtin !== null) {
2343 if (builtin == '+') { 1993 if (builtin == '+') {
2344 beginExpression(JSPrecedence.ADDITIVE_PRECEDENCE); 1994 use(node.inputs[1]);
2345 use(node.inputs[1], JSPrecedence.ADDITIVE_PRECEDENCE); 1995 js.Expression left = pop();
2346 buffer.add(' + '); 1996 use(node.inputs[2]);
2347 use(node.inputs[2], JSPrecedence.MULTIPLICATIVE_PRECEDENCE); 1997 push(new js.Binary("+", left, pop()), node);
2348 endExpression(JSPrecedence.ADDITIVE_PRECEDENCE);
2349 } else { 1998 } else {
2350 beginExpression(JSPrecedence.CALL_PRECEDENCE); 1999 use(node.inputs[1]);
2351 use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE); 2000 js.PropertyAccess access = new js.PropertyAccess.field(pop(), builtin);
2352 buffer.add('.'); 2001 if (node.getter) {
2353 buffer.add(builtin); 2002 push(access, node);
2354 if (node.getter) return; 2003 return;
2355 buffer.add('('); 2004 }
2005 List<js.Expression> arguments = <js.Expression>[];
2356 for (int i = 2; i < node.inputs.length; i++) { 2006 for (int i = 2; i < node.inputs.length; i++) {
2357 if (i != 2) buffer.add(', '); 2007 use(node.inputs[i]);
2358 use(node.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); 2008 arguments.add(pop());
2359 } 2009 }
2360 buffer.add(")"); 2010 push(new js.Call(access, arguments), node);
2361 endExpression(JSPrecedence.CALL_PRECEDENCE);
2362 } 2011 }
2363 } else { 2012 } else {
2364 return visitInvokeStatic(node); 2013 return visitInvokeStatic(node);
2365 } 2014 }
2366 } 2015 }
2367 2016
2368 void checkInt(HInstruction input, String cmp) { 2017 void checkInt(HInstruction input, String cmp) {
2369 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2018 use(input);
2370 use(input, JSPrecedence.EQUALITY_PRECEDENCE); 2019 js.Expression left = pop();
2371 buffer.add(' $cmp ('); 2020 use(input);
2372 use(input, JSPrecedence.BITWISE_OR_PRECEDENCE); 2021 js.Expression or0 = new js.Binary("|", pop(), new js.LiteralNumber("0"));
2373 buffer.add(' | 0)'); 2022 push(new js.Binary(cmp, left, or0));
2374 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2375 } 2023 }
2376 2024
2377 void checkNum(HInstruction input, String cmp) { 2025 void checkTypeOf(HInstruction input, String cmp, String typeName) {
2378 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2026 use(input);
2379 buffer.add('typeof '); 2027 js.Expression typeOf = new js.Prefix("typeof", pop());
2380 use(input, JSPrecedence.PREFIX_PRECEDENCE); 2028 push(new js.Binary(cmp, typeOf, new js.LiteralString("'$typeName'")));
2381 buffer.add(" $cmp 'number'");
2382 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2383 } 2029 }
2384 2030
2385 void checkDouble(HInstruction input, String cmp) { 2031 void checkNum(HInstruction input, String cmp)
2386 checkNum(input, cmp); 2032 => checkTypeOf(input, cmp, 'number');
2387 }
2388 2033
2389 void checkString(HInstruction input, String cmp) { 2034 void checkDouble(HInstruction input, String cmp) => checkNum(input, cmp);
2390 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2391 buffer.add('typeof ');
2392 use(input, JSPrecedence.PREFIX_PRECEDENCE);
2393 buffer.add(" $cmp 'string'");
2394 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2395 }
2396 2035
2397 void checkBool(HInstruction input, String cmp) { 2036 void checkString(HInstruction input, String cmp)
2398 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2037 => checkTypeOf(input, cmp, 'string');
2399 buffer.add('typeof '); 2038
2400 use(input, JSPrecedence.PREFIX_PRECEDENCE); 2039 void checkBool(HInstruction input, String cmp)
2401 buffer.add(" $cmp 'boolean'"); 2040 => checkTypeOf(input, cmp, 'boolean');
2402 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2403 }
2404 2041
2405 void checkObject(HInstruction input, String cmp) { 2042 void checkObject(HInstruction input, String cmp) {
2406 assert(NullConstant.JsNull == 'null'); 2043 assert(NullConstant.JsNull == 'null');
2407 if (cmp == "===") { 2044 if (cmp == "===") {
2408 withPrecedence(JSPrecedence.LOGICAL_AND_PRECEDENCE, () { 2045 checkTypeOf(input, '===', 'object');
2409 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2046 js.Expression left = pop();
2410 buffer.add('typeof '); 2047 use(input);
2411 use(input, JSPrecedence.PREFIX_PRECEDENCE); 2048 js.Expression notNull = new js.Binary("!==", pop(), new js.LiteralNull());
2412 buffer.add(" === 'object'"); 2049 push(new js.Binary("&&", left, notNull));
2413 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2414 buffer.add(" && ");
2415 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2416 use(input, JSPrecedence.PREFIX_PRECEDENCE);
2417 buffer.add(" !== null");
2418 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2419 });
2420 } else { 2050 } else {
2421 assert(cmp == "!=="); 2051 assert(cmp == "!==");
2422 withPrecedence(JSPrecedence.LOGICAL_OR_PRECEDENCE, () { 2052 checkTypeOf(input, '!==', 'object');
2423 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2053 js.Expression left = pop();
2424 buffer.add('typeof '); 2054 use(input);
2425 use(input, JSPrecedence.PREFIX_PRECEDENCE); 2055 js.Expression eqNull = new js.Binary("===", pop(), new js.LiteralNull());
2426 buffer.add(" !== 'object'"); 2056 push(new js.Binary("||", left, eqNull));
2427 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2428 buffer.add(" || ");
2429 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2430 use(input, JSPrecedence.PREFIX_PRECEDENCE);
2431 buffer.add(" === null");
2432 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2433 });
2434 } 2057 }
2435 } 2058 }
2436 2059
2437 void checkArray(HInstruction input, String cmp) { 2060 void checkArray(HInstruction input, String cmp) {
2438 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2061 use(input);
2439 use(input, JSPrecedence.MEMBER_PRECEDENCE); 2062 js.PropertyAccess constructor =
2440 buffer.add('.constructor $cmp Array'); 2063 new js.PropertyAccess.field(pop(), 'constructor');
2441 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2064 push(new js.Binary(cmp, constructor, new js.VariableUse('Array')));
2065 }
2066
2067 void checkFieldExists(HInstruction input, String fieldName) {
2068 use(input);
2069 js.PropertyAccess field = new js.PropertyAccess.field(pop(), fieldName);
2070 // Double negate to boolify the result.
2071 push(new js.Prefix('!', new js.Prefix('!', field)));
2442 } 2072 }
2443 2073
2444 void checkImmutableArray(HInstruction input) { 2074 void checkImmutableArray(HInstruction input) {
2445 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); 2075 checkFieldExists(input, 'immutable\$list');
2446 buffer.add('!!');
2447 use(input, JSPrecedence.MEMBER_PRECEDENCE);
2448 buffer.add('.immutable\$list');
2449 endExpression(JSPrecedence.PREFIX_PRECEDENCE);
2450 } 2076 }
2451 2077
2452 void checkExtendableArray(HInstruction input) { 2078 void checkExtendableArray(HInstruction input) {
2453 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); 2079 checkFieldExists(input, 'fixed\$length');
2454 buffer.add('!!');
2455 use(input, JSPrecedence.MEMBER_PRECEDENCE);
2456 buffer.add('.fixed\$length');
2457 endExpression(JSPrecedence.PREFIX_PRECEDENCE);
2458 } 2080 }
2459 2081
2460 void checkFixedArray(HInstruction input) { 2082 void checkFixedArray(HInstruction input) {
2461 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); 2083 checkFieldExists(input, 'fixed\$length');
2462 use(input, JSPrecedence.MEMBER_PRECEDENCE);
2463 buffer.add('.fixed\$length');
2464 endExpression(JSPrecedence.PREFIX_PRECEDENCE);
2465 } 2084 }
2466 2085
2467 void checkNull(HInstruction input) { 2086 void checkNull(HInstruction input) {
2468 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2087 use(input);
2469 use(input, JSPrecedence.EQUALITY_PRECEDENCE); 2088 push(new js.Binary('==', pop(), new js.LiteralNull()));
2470 buffer.add(" == null");
2471 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2472 } 2089 }
2473 2090
2474 void checkFunction(HInstruction input, Element element) { 2091 void checkFunction(HInstruction input, Element element) {
2475 withPrecedence(JSPrecedence.LOGICAL_OR_PRECEDENCE, () { 2092 checkTypeOf(input, '===', 'function');
2476 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2093 js.Expression functionTest = pop();
2477 buffer.add('typeof '); 2094 checkObject(input, '===');
2478 use(input, JSPrecedence.PREFIX_PRECEDENCE); 2095 js.Expression objectTest = pop();
2479 buffer.add(" === 'function'"); 2096 checkType(input, element);
2480 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2097 push(new js.Binary('||',
2481 buffer.add(" || "); 2098 functionTest,
2482 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); 2099 new js.Binary('&&', objectTest, pop())));
2483 checkObject(input, '===');
2484 buffer.add(" && ");
2485 checkType(input, element);
2486 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
2487 });
2488 } 2100 }
2489 2101
2490 void checkType(HInstruction input, Element element, [bool negative = false]) { 2102 void checkType(HInstruction input, Element element, [bool negative = false]) {
2491 world.registerIsCheck(element); 2103 world.registerIsCheck(element);
2492 bool requiresNativeIsCheck = 2104 use(input);
2493 backend.emitter.nativeEmitter.requiresNativeIsCheck(element); 2105 js.PropertyAccess field =
2494 if (!requiresNativeIsCheck) { 2106 new js.PropertyAccess.field(pop(), compiler.namer.operatorIs(element));
2495 if (negative) { 2107 if (backend.emitter.nativeEmitter.requiresNativeIsCheck(element)) {
2496 buffer.add('!'); 2108 push(new js.Call(field, <js.Expression>[]));
2497 } else { 2109 if (negative) push(new js.Prefix('!', pop()));
2498 buffer.add('!!'); 2110 } else {
2499 } 2111 // We always negate at least once so that the result is boolified.
2500 } else if (negative) { 2112 push(new js.Prefix('!', field));
2501 buffer.add('!'); 2113 // If the result is not negated, put another '!' in front.
2114 if (!negative) push(new js.Prefix('!', pop()));
2502 } 2115 }
2503 use(input, JSPrecedence.MEMBER_PRECEDENCE);
2504 buffer.add('.');
2505 buffer.add(compiler.namer.operatorIs(element));
2506 if (requiresNativeIsCheck) buffer.add('()');
2507 } 2116 }
2508 2117
2509 void handleStringSupertypeCheck(HInstruction input, Element element) { 2118 void handleStringSupertypeCheck(HInstruction input, Element element) {
2510 // Make sure List and String don't share supertypes, otherwise we 2119 // Make sure List and String don't share supertypes, otherwise we
2511 // would need to check for List too. 2120 // would need to check for List too.
2512 assert(element !== compiler.listClass 2121 assert(element !== compiler.listClass
2513 && !Elements.isListSupertype(element, compiler)); 2122 && !Elements.isListSupertype(element, compiler));
2514 withPrecedence(JSPrecedence.LOGICAL_OR_PRECEDENCE, () { 2123 checkString(input, '===');
2515 checkString(input, '==='); 2124 js.Expression stringTest = pop();
2516 buffer.add(' || '); 2125 checkObject(input, '===');
2517 withPrecedence(JSPrecedence.LOGICAL_AND_PRECEDENCE, () { 2126 js.Expression objectTest = pop();
2518 checkObject(input, '==='); 2127 checkType(input, element);
2519 buffer.add(' && '); 2128 push(new js.Binary('||',
2520 checkType(input, element); 2129 stringTest,
2521 }); 2130 new js.Binary('&&', objectTest, pop())));
2522 });
2523 } 2131 }
2524 2132
2525 void handleListOrSupertypeCheck(HInstruction input, Element element) { 2133 void handleListOrSupertypeCheck(HInstruction input, Element element) {
2526 // Make sure List and String don't share supertypes, otherwise we 2134 // Make sure List and String don't share supertypes, otherwise we
2527 // would need to check for String too. 2135 // would need to check for String too.
2528 assert(element !== compiler.stringClass 2136 assert(element !== compiler.stringClass
2529 && !Elements.isStringSupertype(element, compiler)); 2137 && !Elements.isStringSupertype(element, compiler));
2530 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
2531 checkObject(input, '==='); 2138 checkObject(input, '===');
2532 buffer.add(' && ('); 2139 js.Expression objectTest = pop();
2533 beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
2534 checkArray(input, '==='); 2140 checkArray(input, '===');
2535 buffer.add(' || '); 2141 js.Expression arrayTest = pop();
2536 checkType(input, element); 2142 checkType(input, element);
2537 buffer.add(')'); 2143 push(new js.Binary('&&',
2538 endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE); 2144 objectTest,
2539 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); 2145 new js.Binary('||', arrayTest, pop())));
2540 } 2146 }
2541 2147
2542 void visitIs(HIs node) { 2148 void visitIs(HIs node) {
2543 Type type = node.typeExpression; 2149 Type type = node.typeExpression;
2544 Element element = type.element; 2150 Element element = type.element;
2545 if (element.kind === ElementKind.TYPE_VARIABLE) { 2151 if (element.kind === ElementKind.TYPE_VARIABLE) {
2546 compiler.unimplemented("visitIs for type variables", instruction: node); 2152 compiler.unimplemented("visitIs for type variables", instruction: node);
2547 } else if (element.kind === ElementKind.TYPEDEF) { 2153 } else if (element.kind === ElementKind.TYPEDEF) {
2548 compiler.unimplemented("visitIs for typedefs", instruction: node); 2154 compiler.unimplemented("visitIs for typedefs", instruction: node);
2549 } 2155 }
2550 LibraryElement coreLibrary = compiler.coreLibrary; 2156 LibraryElement coreLibrary = compiler.coreLibrary;
2551 ClassElement objectClass = compiler.objectClass; 2157 ClassElement objectClass = compiler.objectClass;
2552 HInstruction input = node.expression; 2158 HInstruction input = node.expression;
2553 2159
2554 int oldPrecedence;
2555 if (node.nullOk) {
2556 oldPrecedence = expectedPrecedence;
2557 beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
2558 expectedPrecedence = JSPrecedence.LOGICAL_OR_PRECEDENCE;
2559 checkNull(input);
2560 buffer.add(' || ');
2561 }
2562 if (element === objectClass || element === compiler.dynamicClass) { 2160 if (element === objectClass || element === compiler.dynamicClass) {
2563 // The constant folder also does this optimization, but we make 2161 // The constant folder also does this optimization, but we make
2564 // it safe by assuming it may have not run. 2162 // it safe by assuming it may have not run.
2565 buffer.add('true'); 2163 push(new js.LiteralBool(true), node);
2566 } else if (element == compiler.stringClass) { 2164 } else if (element == compiler.stringClass) {
2567 checkString(input, '==='); 2165 checkString(input, '===');
2166 attachLocationToLast(node);
2568 } else if (element == compiler.doubleClass) { 2167 } else if (element == compiler.doubleClass) {
2569 checkDouble(input, '==='); 2168 checkDouble(input, '===');
2169 attachLocationToLast(node);
2570 } else if (element == compiler.numClass) { 2170 } else if (element == compiler.numClass) {
2571 checkNum(input, '==='); 2171 checkNum(input, '===');
2172 attachLocationToLast(node);
2572 } else if (element == compiler.boolClass) { 2173 } else if (element == compiler.boolClass) {
2573 checkBool(input, '==='); 2174 checkBool(input, '===');
2175 attachLocationToLast(node);
2574 } else if (element == compiler.functionClass) { 2176 } else if (element == compiler.functionClass) {
2575 checkFunction(input, element); 2177 checkFunction(input, element);
2178 attachLocationToLast(node);
2576 } else if (element == compiler.intClass) { 2179 } else if (element == compiler.intClass) {
2577 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
2578 checkNum(input, '==='); 2180 checkNum(input, '===');
2579 buffer.add(' && '); 2181 js.Expression numTest = pop();
2580 checkInt(input, '==='); 2182 checkInt(input, '===');
2581 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); 2183 push(new js.Binary('&&', numTest, pop()), node);
2582 } else if (Elements.isStringSupertype(element, compiler)) { 2184 } else if (Elements.isStringSupertype(element, compiler)) {
2583 handleStringSupertypeCheck(input, element); 2185 handleStringSupertypeCheck(input, element);
2186 attachLocationToLast(node);
2584 } else if (element === compiler.listClass 2187 } else if (element === compiler.listClass
2585 || Elements.isListSupertype(element, compiler)) { 2188 || Elements.isListSupertype(element, compiler)) {
2586 handleListOrSupertypeCheck(input, element); 2189 handleListOrSupertypeCheck(input, element);
2190 attachLocationToLast(node);
2587 } else if (input.propagatedType.canBePrimitive() 2191 } else if (input.propagatedType.canBePrimitive()
2588 || input.propagatedType.canBeNull()) { 2192 || input.propagatedType.canBeNull()) {
2589 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
2590 checkObject(input, '==='); 2193 checkObject(input, '===');
2591 buffer.add(' && '); 2194 js.Expression objectTest = pop();
2592 checkType(input, element); 2195 checkType(input, element);
2593 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); 2196 push(new js.Binary('&&', objectTest, pop()), node);
2594 } else { 2197 } else {
2595 checkType(input, element); 2198 checkType(input, element);
2199 attachLocationToLast(node);
2596 } 2200 }
2597 if (compiler.codegenWorld.rti.hasTypeArguments(type)) { 2201 if (compiler.codegenWorld.rti.hasTypeArguments(type)) {
2598 InterfaceType interfaceType = type; 2202 InterfaceType interfaceType = type;
2599 ClassElement cls = type.element; 2203 ClassElement cls = type.element;
2600 Link<Type> arguments = interfaceType.arguments; 2204 Link<Type> arguments = interfaceType.arguments;
2601 buffer.add(' && '); 2205 js.Expression result = pop();
2602 checkObject(node.typeInfoCall, '==='); 2206 checkObject(node.typeInfoCall, '===');
2207 result = new js.Binary('&&', result, pop());
2603 for (TypeVariableType typeVariable in cls.typeVariables) { 2208 for (TypeVariableType typeVariable in cls.typeVariables) {
2604 buffer.add(' && '); 2209 use(node.typeInfoCall);
2605 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
2606 use(node.typeInfoCall, JSPrecedence.EQUALITY_PRECEDENCE);
2607 // TODO(johnniwinther): Retrieve the type name properly and not through 2210 // TODO(johnniwinther): Retrieve the type name properly and not through
2608 // [toString]. Note: Two cases below [typeVariable] and 2211 // [toString]. Note: Two cases below [typeVariable] and
2609 // [arguments.head]. 2212 // [arguments.head].
2610 buffer.add( 2213 js.PropertyAccess field =
2611 ".${typeVariable} === '${arguments.head}'"); 2214 new js.PropertyAccess.field(pop(), typeVariable.toString());
2612 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); 2215 js.Expression genericName = new js.LiteralString("'${arguments.head}'");
2216 js.Binary eqTest = new js.Binary('===', field, genericName);
2217 result = new js.Binary('&&', result, eqTest);
2613 } 2218 }
2219 push(result, node);
2614 } 2220 }
2615 if (node.nullOk) { 2221 if (node.nullOk) {
2616 expectedPrecedence = oldPrecedence; 2222 checkNull(input);
2617 endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE); 2223 push(new js.Binary('||', pop(), pop()), node);
2618 } 2224 }
2619 } 2225 }
2620 2226
2621 void visitTypeConversion(HTypeConversion node) { 2227 void visitTypeConversion(HTypeConversion node) {
2622 Map<String, SourceString> castNames = const <SourceString> { 2228 Map<String, SourceString> castNames = const <SourceString> {
2623 "stringTypeCheck": 2229 "stringTypeCheck":
2624 const SourceString("stringTypeCast"), 2230 const SourceString("stringTypeCast"),
2625 "doubleTypeCheck": 2231 "doubleTypeCheck":
2626 const SourceString("doubleTypeCast"), 2232 const SourceString("doubleTypeCast"),
2627 "numTypeCheck": 2233 "numTypeCheck":
(...skipping 20 matching lines...) Expand all
2648 const SourceString("propertyTypeCast") 2254 const SourceString("propertyTypeCast")
2649 }; 2255 };
2650 2256
2651 if (node.isChecked) { 2257 if (node.isChecked) {
2652 Element element = node.type.computeType(compiler).element; 2258 Element element = node.type.computeType(compiler).element;
2653 world.registerIsCheck(element); 2259 world.registerIsCheck(element);
2654 SourceString helper; 2260 SourceString helper;
2655 String additionalArgument; 2261 String additionalArgument;
2656 bool nativeCheck = 2262 bool nativeCheck =
2657 backend.emitter.nativeEmitter.requiresNativeIsCheck(element); 2263 backend.emitter.nativeEmitter.requiresNativeIsCheck(element);
2658 beginExpression(JSPrecedence.CALL_PRECEDENCE);
2659 2264
2660 if (node.isArgumentTypeCheck) { 2265 if (node.isArgumentTypeCheck) {
2661 buffer.add('if (');
2662 if (element == compiler.intClass) { 2266 if (element == compiler.intClass) {
2663 checkInt(node.checkedInput, '!=='); 2267 checkInt(node.checkedInput, '!==');
2664 } else { 2268 } else {
2665 assert(element == compiler.numClass); 2269 assert(element == compiler.numClass);
2666 checkNum(node.checkedInput, '!=='); 2270 checkNum(node.checkedInput, '!==');
2667 } 2271 }
2668 buffer.add(') '); 2272 js.Expression test = pop();
2273 js.Block oldContainer = currentContainer;
2274 js.Statement body = new js.Block.empty();
2275 currentContainer = body;
2669 generateThrowWithHelper('iae', node.checkedInput); 2276 generateThrowWithHelper('iae', node.checkedInput);
2277 currentContainer = oldContainer;
2278 body = unwrapStatement(body);
2279 pushStatement(new js.If.then(test, body), node);
2670 return; 2280 return;
2671 } 2281 }
2672 2282
2673 assert(node.isCheckedModeCheck || node.isCastTypeCheck); 2283 assert(node.isCheckedModeCheck || node.isCastTypeCheck);
2674 if (element == compiler.stringClass) { 2284 if (element == compiler.stringClass) {
2675 helper = const SourceString('stringTypeCheck'); 2285 helper = const SourceString('stringTypeCheck');
2676 } else if (element == compiler.doubleClass) { 2286 } else if (element == compiler.doubleClass) {
2677 helper = const SourceString('doubleTypeCheck'); 2287 helper = const SourceString('doubleTypeCheck');
2678 } else if (element == compiler.numClass) { 2288 } else if (element == compiler.numClass) {
2679 helper = const SourceString('numTypeCheck'); 2289 helper = const SourceString('numTypeCheck');
(...skipping 24 matching lines...) Expand all
2704 helper = const SourceString('callTypeCheck'); 2314 helper = const SourceString('callTypeCheck');
2705 } else { 2315 } else {
2706 helper = const SourceString('propertyTypeCheck'); 2316 helper = const SourceString('propertyTypeCheck');
2707 } 2317 }
2708 } 2318 }
2709 if (node.isCastTypeCheck) { 2319 if (node.isCastTypeCheck) {
2710 helper = castNames[helper.stringValue]; 2320 helper = castNames[helper.stringValue];
2711 } 2321 }
2712 Element helperElement = compiler.findHelper(helper); 2322 Element helperElement = compiler.findHelper(helper);
2713 world.registerStaticUse(helperElement); 2323 world.registerStaticUse(helperElement);
2714 buffer.add(compiler.namer.isolateAccess(helperElement)); 2324 List<js.Expression> arguments = <js.Expression>[];
2715 buffer.add('('); 2325 use(node.checkedInput);
2716 use(node.checkedInput, JSPrecedence.EXPRESSION_PRECEDENCE); 2326 arguments.add(pop());
2717 if (additionalArgument !== null) buffer.add(", '$additionalArgument'"); 2327 if (additionalArgument !== null) {
2718 buffer.add(')'); 2328 arguments.add(new js.LiteralString("'$additionalArgument'"));
2719 endExpression(JSPrecedence.CALL_PRECEDENCE); 2329 }
2330 String helperName = compiler.namer.isolateAccess(helperElement);
2331 push(new js.Call(new js.VariableUse(helperName), arguments));
2720 } else { 2332 } else {
2721 use(node.checkedInput, expectedPrecedence); 2333 use(node.checkedInput);
2722 } 2334 }
2723 } 2335 }
2724 } 2336 }
2725 2337
2726 class SsaOptimizedCodeGenerator extends SsaCodeGenerator { 2338 class SsaOptimizedCodeGenerator extends SsaCodeGenerator {
2727 SsaOptimizedCodeGenerator(backend, work, parameters, parameterNames) 2339 SsaOptimizedCodeGenerator(backend, work, parameters, parameterNames)
2728 : super(backend, work, parameters, parameterNames) { 2340 : super(backend, work, parameterNames) {
2729 // Declare the parameter names only for the optimized version. The 2341 // Declare the parameter names only for the optimized version. The
2730 // unoptimized version has different parameters. 2342 // unoptimized version has different parameters.
2731 parameterNames.forEach((Element element, String name) { 2343 parameterNames.forEach((Element element, String name) {
2732 declaredVariables.add(name); 2344 declaredVariables.add(name);
2733 }); 2345 });
2734 } 2346 }
2735 2347
2736 int maxBailoutParameters; 2348 int maxBailoutParameters;
2737 2349
2738 HBasicBlock beginGraph(HGraph graph) => graph.entry; 2350 HBasicBlock beginGraph(HGraph graph) => graph.entry;
2739 void endGraph(HGraph graph) {} 2351 void endGraph(HGraph graph) {}
2740 2352
2741 void bailout(HTypeGuard guard, String reason) { 2353 js.Statement bailout(HTypeGuard guard, String reason) {
2742 if (maxBailoutParameters === null) { 2354 if (maxBailoutParameters === null) {
2743 maxBailoutParameters = 0; 2355 maxBailoutParameters = 0;
2744 work.guards.forEach((HTypeGuard workGuard) { 2356 work.guards.forEach((HTypeGuard workGuard) {
2745 HBailoutTarget target = workGuard.bailoutTarget; 2357 HBailoutTarget target = workGuard.bailoutTarget;
2746 int inputLength = target.inputs.length; 2358 int inputLength = target.inputs.length;
2747 if (inputLength > maxBailoutParameters) { 2359 if (inputLength > maxBailoutParameters) {
2748 maxBailoutParameters = inputLength; 2360 maxBailoutParameters = inputLength;
2749 } 2361 }
2750 }); 2362 });
2751 } 2363 }
2752 HInstruction input = guard.guarded; 2364 HInstruction input = guard.guarded;
2753 HBailoutTarget target = guard.bailoutTarget; 2365 HBailoutTarget target = guard.bailoutTarget;
2754 Namer namer = compiler.namer; 2366 Namer namer = compiler.namer;
2755 Element element = work.element; 2367 Element element = work.element;
2756 buffer.add('return '); 2368 List<js.Expression> arguments = <js.Expression>[];
2757 if (element.isInstanceMember()) { 2369 arguments.add(new js.LiteralNumber("${guard.state}"));
2758 // TODO(ngeoffray): This does not work in case we come from a
2759 // super call. We must make bailout names unique.
2760 buffer.add('this.${namer.getBailoutName(element)}');
2761 } else {
2762 buffer.add(namer.isolateBailoutAccess(element));
2763 }
2764 buffer.add('(${guard.state}');
2765 // TODO(ngeoffray): try to put a variable at a deterministic 2370 // TODO(ngeoffray): try to put a variable at a deterministic
2766 // location, so that multiple bailout calls put the variable at 2371 // location, so that multiple bailout calls put the variable at
2767 // the same parameter index. 2372 // the same parameter index.
2768 int i = 0; 2373 int i = 0;
2769 for (; i < target.inputs.length; i++) { 2374 for (; i < target.inputs.length; i++) {
2770 assert(guard.inputs.indexOf(target.inputs[i]) >= 0); 2375 assert(guard.inputs.indexOf(target.inputs[i]) >= 0);
2771 buffer.add(', '); 2376 use(target.inputs[i]);
2772 use(target.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); 2377 arguments.add(pop());
2773 } 2378 }
2774 // Make sure we call the bailout method with the number of 2379 // Make sure we call the bailout method with the number of
2775 // arguments it expects. This avoids having the underlying 2380 // arguments it expects. This avoids having the underlying
2776 // JS engine fill them in for us. 2381 // JS engine fill them in for us.
2777 for (; i < maxBailoutParameters; i++) { 2382 for (; i < maxBailoutParameters; i++) {
2778 buffer.add(', 0'); 2383 arguments.add(new js.LiteralNumber('0'));
2779 } 2384 }
2780 buffer.add(')'); 2385
2386 js.Expression bailoutTarget;
2387 if (element.isInstanceMember()) {
2388 // TODO(ngeoffray): This does not work in case we come from a
2389 // super call. We must make bailout names unique.
2390 String bailoutName = namer.getBailoutName(element);
2391 bailoutTarget = new js.PropertyAccess.field(new js.This(), bailoutName);
2392 } else {
2393 bailoutTarget = new js.VariableUse(namer.isolateBailoutAccess(element));
2394 }
2395 js.Call call = new js.Call(bailoutTarget, arguments);
2396 attachLocation(call, guard);
2397 return new js.Return(call);
2781 } 2398 }
2782 2399
2783 void visitTypeGuard(HTypeGuard node) { 2400 void visitTypeGuard(HTypeGuard node) {
2784 addIndentation();
2785 HInstruction input = node.guarded; 2401 HInstruction input = node.guarded;
2786 Element indexingBehavior = compiler.jsIndexingBehaviorInterface; 2402 Element indexingBehavior = compiler.jsIndexingBehaviorInterface;
2787 if (node.isInteger()) { 2403 if (node.isInteger()) {
2788 // if (input is !int) bailout 2404 // if (input is !int) bailout
2789 buffer.add('if (');
2790 checkInt(input, '!=='); 2405 checkInt(input, '!==');
2791 buffer.add(') '); 2406 pushStatement(new js.If.then(pop(), bailout(node, 'Not an integer')),
2792 bailout(node, 'Not an integer'); 2407 node);
2793 } else if (node.isNumber()) { 2408 } else if (node.isNumber()) {
2794 // if (input is !num) bailout 2409 // if (input is !num) bailout
2795 buffer.add('if (');
2796 checkNum(input, '!=='); 2410 checkNum(input, '!==');
2797 buffer.add(') '); 2411 pushStatement(new js.If.then(pop(), bailout(node, 'Not a number')), node);
2798 bailout(node, 'Not a number');
2799 } else if (node.isBoolean()) { 2412 } else if (node.isBoolean()) {
2800 // if (input is !bool) bailout 2413 // if (input is !bool) bailout
2801 buffer.add('if (');
2802 checkBool(input, '!=='); 2414 checkBool(input, '!==');
2803 buffer.add(') '); 2415 pushStatement(new js.If.then(pop(), bailout(node, 'Not a boolean')),
2804 bailout(node, 'Not a boolean'); 2416 node);
2805 } else if (node.isString()) { 2417 } else if (node.isString()) {
2806 // if (input is !string) bailout 2418 // if (input is !string) bailout
2807 buffer.add('if (');
2808 checkString(input, '!=='); 2419 checkString(input, '!==');
2809 buffer.add(') '); 2420 pushStatement(new js.If.then(pop(), bailout(node, 'Not a string')), node);
2810 bailout(node, 'Not a string');
2811 } else if (node.isExtendableArray()) { 2421 } else if (node.isExtendableArray()) {
2812 // if (input is !Object || input is !Array || input.isFixed) bailout 2422 // if (input is !Object || input is !Array || input.isFixed) bailout
2813 buffer.add('if (');
2814 checkObject(input, '!=='); 2423 checkObject(input, '!==');
2815 buffer.add('||'); 2424 js.Expression objectTest = pop();
2816 checkArray(input, '!=='); 2425 checkArray(input, '!==');
2817 buffer.add('||'); 2426 js.Expression arrayTest = pop();
2818 checkFixedArray(input); 2427 checkFixedArray(input);
2819 buffer.add(') '); 2428 js.Expression test = new js.Binary('||', objectTest, arrayTest);
2820 bailout(node, 'Not an extendable array'); 2429 test = new js.Binary('||', test, pop());
2430 pushStatement(new js.If.then(test,
2431 bailout(node, 'Not an extendable array')),
2432 node);
2821 } else if (node.isMutableArray()) { 2433 } else if (node.isMutableArray()) {
2822 // if (input is !Object 2434 // if (input is !Object
2823 // || ((input is !Array || input.isImmutable) 2435 // || ((input is !Array || input.isImmutable)
2824 // && input is !JsIndexingBehavior)) bailout 2436 // && input is !JsIndexingBehavior)) bailout
2825 buffer.add('if (');
2826 checkObject(input, '!=='); 2437 checkObject(input, '!==');
2827 buffer.add(' || (('); 2438 js.Expression objectTest = pop();
2828 checkArray(input, '!=='); 2439 checkArray(input, '!==');
2829 buffer.add(' || '); 2440 js.Expression arrayTest = pop();
2830 checkImmutableArray(input); 2441 checkImmutableArray(input);
2831 buffer.add(') && '); 2442 js.Binary notArrayOrImmutable = new js.Binary('||', arrayTest, pop());
2832 checkType(input, indexingBehavior, negative: true); 2443 checkType(input, indexingBehavior, negative: true);
2833 buffer.add(')) '); 2444 js.Binary notIndexing = new js.Binary('&&', notArrayOrImmutable, pop());
2834 bailout(node, 'Not a mutable array'); 2445 pushStatement(new js.If.then(new js.Binary('||', objectTest, notIndexing),
2446 bailout(node, 'Not a mutable array')),
2447 node);
2835 } else if (node.isReadableArray()) { 2448 } else if (node.isReadableArray()) {
2836 // if (input is !Object 2449 // if (input is !Object
2837 // || (input is !Array && input is !JsIndexingBehavior)) bailout 2450 // || (input is !Array && input is !JsIndexingBehavior)) bailout
2838 buffer.add('if (');
2839 checkObject(input, '!=='); 2451 checkObject(input, '!==');
2840 buffer.add(' || ('); 2452 js.Expression objectTest = pop();
2841 checkArray(input, '!=='); 2453 checkArray(input, '!==');
2842 buffer.add(' && '); 2454 js.Expression arrayTest = pop();
2843 checkType(input, indexingBehavior, negative: true); 2455 checkType(input, indexingBehavior, negative: true);
2844 buffer.add(')) '); 2456 js.Expression notIndexing = new js.Binary('&&', arrayTest, pop());
2845 bailout(node, 'Not an array'); 2457 pushStatement(new js.If.then(new js.Binary('||', objectTest, notIndexing),
2458 bailout(node, 'Not an array')),
2459 node);
2846 } else if (node.isIndexablePrimitive()) { 2460 } else if (node.isIndexablePrimitive()) {
2847 // if (input is !String 2461 // if (input is !String
2848 // && (input is !Object 2462 // && (input is !Object
2849 // || (input is !Array && input is !JsIndexingBehavior))) bailout 2463 // || (input is !Array && input is !JsIndexingBehavior))) bailout
2850 buffer.add('if (');
2851 checkString(input, '!=='); 2464 checkString(input, '!==');
2852 buffer.add(' && ('); 2465 js.Expression stringTest = pop();
2853 checkObject(input, '!=='); 2466 checkObject(input, '!==');
2854 buffer.add(' || ('); 2467 js.Expression objectTest = pop();
2855 checkArray(input, '!=='); 2468 checkArray(input, '!==');
2856 buffer.add(' && '); 2469 js.Expression arrayTest = pop();
2857 checkType(input, indexingBehavior, negative: true); 2470 checkType(input, indexingBehavior, negative: true);
2858 buffer.add('))) '); 2471 js.Binary notIndexingTest = new js.Binary('&&', arrayTest, pop());
2859 bailout(node, 'Not a string or array'); 2472 js.Binary notObjectOrIndexingTest =
2473 new js.Binary('||', objectTest, notIndexingTest);
2474 js.Binary condition =
2475 new js.Binary('&&', stringTest, notObjectOrIndexingTest);
2476 pushStatement(new js.If.then(condition,
2477 bailout(node, 'Not a string or array')),
2478 node);
2860 } else { 2479 } else {
2861 compiler.internalError('Unexpected type guard', instruction: input); 2480 compiler.internalError('Unexpected type guard', instruction: input);
2862 } 2481 }
2863 buffer.add(';\n');
2864 } 2482 }
2865 2483
2866 void visitBailoutTarget(HBailoutTarget target) { 2484 void visitBailoutTarget(HBailoutTarget target) {
2867 // Do nothing. Bailout targets are only used in the non-optimized version. 2485 // Do nothing. Bailout targets are only used in the non-optimized version.
2868 } 2486 }
2869 2487
2870 void beginLoop(HBasicBlock block) { 2488 void beginLoop(HBasicBlock block) {
2871 addIndentation(); 2489 oldContainerStack.add(currentContainer);
2872 HLoopInformation info = block.loopInformation; 2490 currentContainer = new js.Block.empty();
2873 for (LabelElement label in info.labels) {
2874 writeLabel(label);
2875 buffer.add(":");
2876 }
2877 buffer.add('while (true) {\n');
2878 indent++;
2879 } 2491 }
2880 2492
2881 void endLoop(HBasicBlock block) { 2493 void endLoop(HBasicBlock block) {
2882 indent--; 2494 js.Statement body = currentContainer;
2883 addIndented('}\n'); // Close 'while' loop. 2495 currentContainer = oldContainerStack.removeLast();
2496 body = unwrapStatement(body);
2497 js.While loop = new js.While(new js.LiteralBool(true), body);
2498
2499 HLoopInformation info = block.loopInformation;
2500 attachLocationRange(loop, info.loopBlockInformation.sourcePosition);
2501 pushStatement(wrapIntoLabels(loop, info.labels));
2884 } 2502 }
2885 2503
2886 void handleLoopCondition(HLoopBranch node) { 2504 void handleLoopCondition(HLoopBranch node) {
2887 buffer.add('if (!'); 2505 use(node.inputs[0]);
2888 use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE); 2506 pushStatement(new js.If.then(pop(), new js.Break(null)), node);
2889 buffer.add(') break;\n');
2890 } 2507 }
2891 2508
2892 2509
2893 void preLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { 2510 void preLabeledBlock(HLabeledBlockInformation labeledBlockInfo) {
2894 } 2511 }
2895 2512
2896 void startLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { 2513 void startLabeledBlock(HLabeledBlockInformation labeledBlockInfo) {
2897 } 2514 }
2898 2515
2899 void endLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { 2516 void endLabeledBlock(HLabeledBlockInformation labeledBlockInfo) {
2900 } 2517 }
2901 } 2518 }
2902 2519
2903 class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator { 2520 class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator {
2904 2521
2905 final CodeBuffer setup; 2522 js.Statement setup;
2906 final CodeBuffer newParameters; 2523 js.Switch currentBailoutSwitch;
2524 final List<js.Switch> oldBailoutSwitches;
2525 final List<js.Parameter> newParameters;
2907 final List<String> labels; 2526 final List<String> labels;
2908 int labelId = 0; 2527 int labelId = 0;
2909 /** 2528 /**
2910 * Keeps track if a bailout switch already used its [:default::] clause. New 2529 * Keeps track if a bailout switch already used its [:default::] clause. New
2911 * bailout-switches just push [:false:] on the stack and replace it when 2530 * bailout-switches just push [:false:] on the stack and replace it when
2912 * they used the [:default::] clause. 2531 * they used the [:default::] clause.
2913 */ 2532 */
2914 final List<bool> defaultClauseUsedInBailoutStack; 2533 final List<bool> defaultClauseUsedInBailoutStack;
2915 2534
2916 SsaBailoutPropagator propagator; 2535 SsaBailoutPropagator propagator;
2917 HInstruction savedFirstInstruction; 2536 HInstruction savedFirstInstruction;
2918 2537
2919 SsaUnoptimizedCodeGenerator(backend, work, parameters, parameterNames) 2538 SsaUnoptimizedCodeGenerator(backend, work, parameters, parameterNames)
2920 : super(backend, work, parameters, parameterNames), 2539 : super(backend, work, parameterNames),
2921 setup = new CodeBuffer(), 2540 setup = new js.EmptyStatement(),
2922 newParameters = new CodeBuffer(), 2541 oldBailoutSwitches = <js.Switch>[],
2542 newParameters = <js.Parameter>[],
2923 labels = <String>[], 2543 labels = <String>[],
2924 defaultClauseUsedInBailoutStack = <bool>[]; 2544 defaultClauseUsedInBailoutStack = <bool>[];
2925 2545
2926 String pushLabel() { 2546 String pushLabel() {
2927 String label = 'L${labelId++}'; 2547 String label = 'L${labelId++}';
2928 labels.addLast(label); 2548 labels.addLast(label);
2929 return label; 2549 return label;
2930 } 2550 }
2931 2551
2932 String popLabel() { 2552 String popLabel() {
2933 return labels.removeLast(); 2553 return labels.removeLast();
2934 } 2554 }
2935 2555
2936 String currentLabel() { 2556 String currentLabel() {
2937 return labels.last(); 2557 return labels.last();
2938 } 2558 }
2939 2559
2940 HBasicBlock beginGraph(HGraph graph) { 2560 HBasicBlock beginGraph(HGraph graph) {
2941 propagator = new SsaBailoutPropagator(compiler, generateAtUseSite); 2561 propagator = new SsaBailoutPropagator(compiler, generateAtUseSite);
2942 propagator.visitGraph(graph); 2562 propagator.visitGraph(graph);
2943 // TODO(ngeoffray): We could avoid generating the state at the 2563 // TODO(ngeoffray): We could avoid generating the state at the
2944 // call site for non-complex bailout methods. 2564 // call site for non-complex bailout methods.
2945 newParameters.add('state'); 2565 newParameters.add(new js.Parameter('state'));
2946 2566
2947 if (propagator.hasComplexBailoutTargets) { 2567 if (propagator.hasComplexBailoutTargets) {
2948 // Use generic parameters that will be assigned to 2568 // Use generic parameters that will be assigned to
2949 // the right variables in the setup phase. 2569 // the right variables in the setup phase.
2950 for (int i = 0; i < propagator.maxBailoutParameters; i++) { 2570 for (int i = 0; i < propagator.maxBailoutParameters; i++) {
2951 String name = 'env$i'; 2571 String name = 'env$i';
2952 declaredVariables.add(name); 2572 declaredVariables.add(name);
2953 newParameters.add(', $name'); 2573 newParameters.add(new js.Parameter(name));
2954 } 2574 }
2955 2575
2956 startBailoutSwitch(); 2576 startBailoutSwitch();
2957 2577
2958 // The setup phase of a bailout function sets up the environment for 2578 // The setup phase of a bailout function sets up the environment for
2959 // each bailout target. Each bailout target will populate this 2579 // each bailout target. Each bailout target will populate this
2960 // setup phase. It is put at the beginning of the function. 2580 // setup phase. It is put at the beginning of the function.
2961 setup.add(' switch (state) {\n'); 2581 setup = new js.Switch(new js.VariableUse('state'), <js.SwitchClause>[]);
2962 return graph.entry; 2582 return graph.entry;
2963 } else { 2583 } else {
2964 // We have a simple bailout target, so we can reuse the names that 2584 // We have a simple bailout target, so we can reuse the names that
2965 // the bailout target expects. 2585 // the bailout target expects.
2966 for (HInstruction input in propagator.firstBailoutTarget.inputs) { 2586 for (HInstruction input in propagator.firstBailoutTarget.inputs) {
2967 input = unwrap(input); 2587 input = unwrap(input);
2968 String name = variableNames.getName(input); 2588 String name = variableNames.getName(input);
2969 declaredVariables.add(name); 2589 declaredVariables.add(name);
2970 newParameters.add(', $name'); 2590 newParameters.add(new js.Parameter(name));
2971 } 2591 }
2972 2592
2973 // We change the first instruction of the first guard to be the 2593 // We change the first instruction of the first guard to be the
2974 // bailout target. We will change it back in the call to [endGraph]. 2594 // bailout target. We will change it back in the call to [endGraph].
2975 HBasicBlock block = propagator.firstBailoutTarget.block; 2595 HBasicBlock block = propagator.firstBailoutTarget.block;
2976 savedFirstInstruction = block.first; 2596 savedFirstInstruction = block.first;
2977 block.first = propagator.firstBailoutTarget; 2597 block.first = propagator.firstBailoutTarget;
2978 return block; 2598 return block;
2979 } 2599 }
2980 } 2600 }
2981 2601
2982 // If argument is a [HCheck] and it does not have a name, we try to 2602 // If argument is a [HCheck] and it does not have a name, we try to
2983 // find the name of its checked input. Note that there must be a 2603 // find the name of its checked input. Note that there must be a
2984 // name, otherwise the instruction would not be in the live 2604 // name, otherwise the instruction would not be in the live
2985 // environment. 2605 // environment.
2986 HInstruction unwrap(HInstruction argument) { 2606 HInstruction unwrap(HInstruction argument) {
2987 while (argument is HCheck && !variableNames.hasName(argument)) { 2607 while (argument is HCheck && !variableNames.hasName(argument)) {
2988 argument = argument.checkedInput; 2608 argument = argument.checkedInput;
2989 } 2609 }
2990 assert(variableNames.hasName(argument)); 2610 assert(variableNames.hasName(argument));
2991 return argument; 2611 return argument;
2992 } 2612 }
2993 2613
2994 void endGraph(HGraph graph) { 2614 void endGraph(HGraph graph) {
2995 if (propagator.hasComplexBailoutTargets) { 2615 if (propagator.hasComplexBailoutTargets) {
2996 indent--; // Close original case. 2616 endBailoutSwitch();
2997 indent--;
2998 addIndented('}\n'); // Close 'switch'.
2999 setup.add(' }\n');
3000 } else { 2617 } else {
3001 // Put back the original first instruction of the block. 2618 // Put back the original first instruction of the block.
3002 propagator.firstBailoutTarget.block.first = savedFirstInstruction; 2619 propagator.firstBailoutTarget.block.first = savedFirstInstruction;
3003 } 2620 }
3004 } 2621 }
3005 2622
3006 bool visitAndOrInfo(HAndOrBlockInformation info) => false; 2623 bool visitAndOrInfo(HAndOrBlockInformation info) => false;
3007 2624
3008 bool visitIfInfo(HIfBlockInformation info) { 2625 bool visitIfInfo(HIfBlockInformation info) {
3009 if (info.thenGraph.start.hasBailoutTargets()) return false; 2626 if (info.thenGraph.start.hasBailoutTargets()) return false;
(...skipping 10 matching lines...) Expand all
3020 bool visitTryInfo(HTryBlockInformation info) => false; 2637 bool visitTryInfo(HTryBlockInformation info) => false;
3021 bool visitSequenceInfo(HStatementSequenceInformation info) => false; 2638 bool visitSequenceInfo(HStatementSequenceInformation info) => false;
3022 2639
3023 void visitTypeGuard(HTypeGuard node) { 2640 void visitTypeGuard(HTypeGuard node) {
3024 // Do nothing. Type guards are only used in the optimized version. 2641 // Do nothing. Type guards are only used in the optimized version.
3025 } 2642 }
3026 2643
3027 void visitBailoutTarget(HBailoutTarget node) { 2644 void visitBailoutTarget(HBailoutTarget node) {
3028 if (!propagator.hasComplexBailoutTargets) return; 2645 if (!propagator.hasComplexBailoutTargets) return;
3029 2646
3030 indent--; 2647 js.Block nextBlock = new js.Block.empty();
3031 addIndented('case ${node.state}:\n'); 2648 js.Case clause = new js.Case(new js.LiteralNumber('${node.state}'),
3032 indent++; 2649 nextBlock);
3033 addIndented('state = 0;\n'); 2650 currentBailoutSwitch.cases.add(clause);
3034 2651 currentContainer = nextBlock;
3035 setup.add(' case ${node.state}:\n'); 2652 pushExpressionAsStatement(new js.Assignment(new js.VariableUse('state'),
2653 new js.LiteralNumber('0')));
2654 js.Block setupBlock = new js.Block.empty();
3036 int i = 0; 2655 int i = 0;
3037 for (HInstruction input in node.inputs) { 2656 for (HInstruction input in node.inputs) {
3038 input = unwrap(input); 2657 input = unwrap(input);
3039 String name = variableNames.getName(input); 2658 String name = variableNames.getName(input);
3040 setup.add(' ');
3041 if (!isVariableDeclared(name)) { 2659 if (!isVariableDeclared(name)) {
3042 declaredVariables.add(name); 2660 declaredVariables.add(name);
3043 setup.add('var '); 2661 js.VariableInitialization init =
2662 new js.VariableInitialization(new js.VariableDeclaration(name),
2663 new js.VariableUse('env$i'));
2664 js.Expression varList =
2665 new js.VariableDeclarationList(<js.VariableInitialization>[init]);
2666 setupBlock.statements.add(new js.ExpressionStatement(varList));
2667 } else {
2668 js.Expression target = new js.VariableUse(name);
2669 js.Expression source = new js.VariableUse('env$i');
2670 js.Expression assignment = new js.Assignment(target, source);
2671 setupBlock.statements.add(new js.ExpressionStatement(assignment));
3044 } 2672 }
3045 setup.add('$name = env$i;\n');
3046 i++; 2673 i++;
3047 } 2674 }
3048 setup.add(' break;\n'); 2675 setupBlock.statements.add(new js.Break(null));
2676 js.Case setupClause =
2677 new js.Case(new js.LiteralNumber('${node.state}'), setupBlock);
2678 (setup as js.Switch).cases.add(setupClause);
3049 } 2679 }
3050 2680
3051 void startBailoutCase(List<HBailoutTarget> bailouts1, 2681 void startBailoutCase(List<HBailoutTarget> bailouts1,
3052 List<HBailoutTarget> bailouts2) { 2682 [List<HBailoutTarget> bailouts2 = const []]) {
3053 indent--;
3054 if (!defaultClauseUsedInBailoutStack.last() && 2683 if (!defaultClauseUsedInBailoutStack.last() &&
3055 bailouts1.length + bailouts2.length >= 2) { 2684 bailouts1.length + bailouts2.length >= 2) {
3056 addIndented('default:\n'); 2685 currentContainer = new js.Block.empty();
2686 currentBailoutSwitch.cases.add(new js.Default(currentContainer));
3057 int len = defaultClauseUsedInBailoutStack.length; 2687 int len = defaultClauseUsedInBailoutStack.length;
3058 defaultClauseUsedInBailoutStack[len - 1] = true; 2688 defaultClauseUsedInBailoutStack[len - 1] = true;
3059 } else { 2689 } else {
3060 handleBailoutCase(bailouts1); 2690 _handleBailoutCase(bailouts1);
3061 handleBailoutCase(bailouts2); 2691 _handleBailoutCase(bailouts2);
2692 currentContainer = currentBailoutSwitch.cases.last().body;
3062 } 2693 }
3063 indent++;
3064 } 2694 }
3065 2695
3066 void handleBailoutCase(List<HBailoutTarget> targets) { 2696 void _handleBailoutCase(List<HBailoutTarget> targets) {
3067 if (!defaultClauseUsedInBailoutStack.last() && targets.length >= 2) { 2697 for (int i = 0, len = targets.length; i < len; i++) {
3068 addIndented('default:\n'); 2698 js.LiteralNumber expr = new js.LiteralNumber('${targets[i].state}');
3069 int len = defaultClauseUsedInBailoutStack.length; 2699 currentBailoutSwitch.cases.add(new js.Case(expr, new js.Block.empty()));
3070 defaultClauseUsedInBailoutStack[len - 1] = true;
3071 } else {
3072 for (int i = 0, len = targets.length; i < len; i++) {
3073 addIndented('case ${targets[i].state}:\n');
3074 }
3075 } 2700 }
3076 } 2701 }
3077 2702
3078 void startBailoutSwitch() { 2703 void startBailoutSwitch() {
3079 defaultClauseUsedInBailoutStack.add(false); 2704 defaultClauseUsedInBailoutStack.add(false);
3080 addIndented('switch (state) {\n'); 2705 oldBailoutSwitches.add(currentBailoutSwitch);
3081 indent++; 2706 List<js.SwitchClause> cases = <js.SwitchClause>[];
3082 addIndented('case 0:\n'); 2707 js.Block firstBlock = new js.Block.empty();
3083 indent++; 2708 cases.add(new js.Case(new js.LiteralNumber("0"), firstBlock));
2709 currentBailoutSwitch = new js.Switch(new js.VariableUse('state'), cases);
2710 pushStatement(currentBailoutSwitch);
2711 oldContainerStack.add(currentContainer);
2712 currentContainer = firstBlock;
3084 } 2713 }
3085 2714
3086 void endBailoutSwitch() { 2715 js.Switch endBailoutSwitch() {
3087 indent--; // Close 'case'. 2716 js.Switch result = currentBailoutSwitch;
3088 indent--; 2717 currentBailoutSwitch = oldBailoutSwitches.removeLast();
3089 addIndented('}\n'); // Close 'switch'.
3090 defaultClauseUsedInBailoutStack.removeLast(); 2718 defaultClauseUsedInBailoutStack.removeLast();
2719 currentContainer = oldContainerStack.removeLast();
2720 return result;
3091 } 2721 }
3092 2722
3093 void beginLoop(HBasicBlock block) { 2723 void beginLoop(HBasicBlock block) {
3094 String newLabel = pushLabel(); 2724 String loopLabel = pushLabel();
3095 if (block.hasBailoutTargets()) { 2725 if (block.hasBailoutTargets()) {
3096 startBailoutCase(block.bailoutTargets, const <HBailoutTarget>[]); 2726 startBailoutCase(block.bailoutTargets);
3097 } 2727 }
3098 2728 oldContainerStack.add(currentContainer);
3099 addIndentation(); 2729 currentContainer = new js.Block.empty();
3100 HLoopInformation loopInformation = block.loopInformation;
3101 for (LabelElement label in loopInformation.labels) {
3102 writeLabel(label);
3103 buffer.add(":");
3104 }
3105 buffer.add('$newLabel: while (true) {\n');
3106 indent++;
3107
3108 if (block.hasBailoutTargets()) { 2730 if (block.hasBailoutTargets()) {
3109 startBailoutSwitch(); 2731 startBailoutSwitch();
2732 HLoopInformation loopInformation = block.loopInformation;
3110 if (loopInformation.target !== null) { 2733 if (loopInformation.target !== null) {
3111 breakAction[loopInformation.target] = (TargetElement target) { 2734 breakAction[loopInformation.target] = (TargetElement target) {
3112 addIndented("break $newLabel;\n"); 2735 pushStatement(new js.Break(loopLabel));
3113 }; 2736 };
3114 } 2737 }
3115 } 2738 }
3116 } 2739 }
3117 2740
3118 void endLoop(HBasicBlock block) { 2741 void endLoop(HBasicBlock block) {
3119 popLabel(); 2742 String loopLabel = popLabel();
2743
3120 HBasicBlock header = block.isLoopHeader() ? block : block.parentLoopHeader; 2744 HBasicBlock header = block.isLoopHeader() ? block : block.parentLoopHeader;
2745 HLoopInformation info = header.loopInformation;
3121 if (header.hasBailoutTargets()) { 2746 if (header.hasBailoutTargets()) {
3122 endBailoutSwitch(); 2747 endBailoutSwitch();
3123 HLoopInformation info = header.loopInformation;
3124 if (info.target != null) breakAction.remove(info.target); 2748 if (info.target != null) breakAction.remove(info.target);
3125 } 2749 }
3126 indent--; 2750
3127 addIndented('}\n'); // Close 'while'. 2751 js.Statement body = unwrapStatement(currentContainer);
2752 currentContainer = oldContainerStack.removeLast();
2753
2754 js.Statement result = new js.While(new js.LiteralBool(true), body);
2755 attachLocationRange(result, info.loopBlockInformation.sourcePosition);
2756 result = new js.LabeledStatement(loopLabel, result);
2757 result = wrapIntoLabels(result, info.labels);
2758 pushStatement(result);
3128 } 2759 }
3129 2760
3130 void handleLoopCondition(HLoopBranch node) { 2761 void handleLoopCondition(HLoopBranch node) {
3131 buffer.add('if (!'); 2762 use(node.inputs[0]);
3132 use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE); 2763 pushStatement(new js.If.then(new js.Prefix('!', pop()),
3133 buffer.add(') break ${currentLabel()};\n'); 2764 new js.Break(currentLabel())),
2765 node);
3134 } 2766 }
3135 2767
3136 void generateIf(HIf node, HIfBlockInformation info) { 2768 void generateIf(HIf node, HIfBlockInformation info) {
3137 HStatementInformation thenGraph = info.thenGraph; 2769 HStatementInformation thenGraph = info.thenGraph;
3138 HStatementInformation elseGraph = info.elseGraph; 2770 HStatementInformation elseGraph = info.elseGraph;
3139 bool thenHasGuards = thenGraph.start.hasBailoutTargets(); 2771 bool thenHasGuards = thenGraph.start.hasBailoutTargets();
3140 bool elseHasGuards = elseGraph.start.hasBailoutTargets(); 2772 bool elseHasGuards = elseGraph.start.hasBailoutTargets();
3141 bool hasGuards = thenHasGuards || elseHasGuards; 2773 bool hasGuards = thenHasGuards || elseHasGuards;
3142 if (!hasGuards) return super.generateIf(node, info); 2774 if (!hasGuards) {
3143 2775 super.generateIf(node, info);
3144 int elseKind = analyzeGraphForCodegen(elseGraph); 2776 return;
3145 bool emptyElse = elseKind == SsaCodeGenerator.EMPTY; 2777 }
3146 2778
3147 startBailoutCase(thenGraph.start.bailoutTargets, 2779 startBailoutCase(thenGraph.start.bailoutTargets,
3148 emptyElse ? const <HBailoutTarget>[] : elseGraph.start.bailoutTargets); 2780 elseGraph.start.bailoutTargets);
3149 2781
3150 addIndented('if ('); 2782 use(node.inputs[0]);
3151 int precedence = JSPrecedence.EXPRESSION_PRECEDENCE; 2783 js.Binary stateEquals0 =
2784 new js.Binary('===',
2785 new js.VariableUse('state'), new js.LiteralNumber('0'));
2786 js.Expression condition = new js.Binary('&&', stateEquals0, pop());
3152 // TODO(ngeoffray): Put the condition initialization in the 2787 // TODO(ngeoffray): Put the condition initialization in the
3153 // [setup] buffer. 2788 // [setup] buffer.
3154 List<HBailoutTarget> targets = node.thenBlock.bailoutTargets; 2789 List<HBailoutTarget> targets = node.thenBlock.bailoutTargets;
3155 for (int i = 0, len = targets.length; i < len; i++) { 2790 for (int i = 0, len = targets.length; i < len; i++) {
3156 buffer.add('state == ${targets[i].state} || '); 2791 js.VariableUse stateRef = new js.VariableUse('state');
2792 js.Expression targetState = new js.LiteralNumber('${targets[i].state}');
2793 js.Binary stateTest = new js.Binary('===', stateRef, targetState);
2794 condition = new js.Binary('||', stateTest, condition);
3157 } 2795 }
3158 buffer.add('(state == 0 && ');
3159 precedence = JSPrecedence.BITWISE_OR_PRECEDENCE;
3160 use(node.inputs[0], precedence);
3161 2796
3162 buffer.add(')) {\n'); 2797 js.Statement thenBody = new js.Block.empty();
3163 2798 js.Block oldContainer = currentContainer;
3164 indent++; 2799 currentContainer = thenBody;
3165 if (thenHasGuards) startBailoutSwitch(); 2800 if (thenHasGuards) startBailoutSwitch();
3166 generateStatements(thenGraph); 2801 generateStatements(thenGraph);
3167 if (thenHasGuards) endBailoutSwitch(); 2802 if (thenHasGuards) endBailoutSwitch();
3168 indent--; 2803 thenBody = unwrapStatement(thenBody);
3169 2804
3170 if (!emptyElse) { 2805 js.Statement elseBody = null;
3171 addIndented('} else {\n'); 2806 elseBody = new js.Block.empty();
3172 indent++; 2807 currentContainer = elseBody;
3173 if (elseHasGuards) startBailoutSwitch(); 2808 if (elseHasGuards) startBailoutSwitch();
3174 generateStatements(elseGraph); 2809 generateStatements(elseGraph);
3175 if (elseHasGuards) endBailoutSwitch(); 2810 if (elseHasGuards) endBailoutSwitch();
3176 indent--; 2811 elseBody = unwrapStatement(elseBody);
3177 }
3178 2812
3179 addIndented('}\n'); 2813 currentContainer = oldContainer;
2814 pushStatement(new js.If(condition, thenBody, elseBody), node);
3180 } 2815 }
3181 2816
3182 void preLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { 2817 void preLabeledBlock(HLabeledBlockInformation labeledBlockInfo) {
3183 if (labeledBlockInfo.body.start.hasBailoutTargets()) { 2818 if (labeledBlockInfo.body.start.hasBailoutTargets()) {
3184 indent--; 2819 indent--;
3185 handleBailoutCase(labeledBlockInfo.body.start.bailoutTargets); 2820 startBailoutCase(labeledBlockInfo.body.start.bailoutTargets);
3186 indent++; 2821 indent++;
3187 } 2822 }
3188 } 2823 }
3189 2824
3190 void startLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { 2825 void startLabeledBlock(HLabeledBlockInformation labeledBlockInfo) {
3191 if (labeledBlockInfo.body.start.hasBailoutTargets()) { 2826 if (labeledBlockInfo.body.start.hasBailoutTargets()) {
3192 startBailoutSwitch(); 2827 startBailoutSwitch();
3193 } 2828 }
3194 } 2829 }
3195 2830
(...skipping 12 matching lines...) Expand all
3208 if (leftType.canBeNull() && rightType.canBeNull()) { 2843 if (leftType.canBeNull() && rightType.canBeNull()) {
3209 if (left.isConstantNull() || right.isConstantNull() || 2844 if (left.isConstantNull() || right.isConstantNull() ||
3210 (leftType.isPrimitive() && leftType == rightType)) { 2845 (leftType.isPrimitive() && leftType == rightType)) {
3211 return '=='; 2846 return '==';
3212 } 2847 }
3213 return null; 2848 return null;
3214 } else { 2849 } else {
3215 return '==='; 2850 return '===';
3216 } 2851 }
3217 } 2852 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698