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

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: . 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 ClassElement enclosingClass = element.getEnclosingClass(); 46 ClassElement enclosingClass = element.getEnclosingClass();
62 if (element.isInstanceMember() 47 if (element.isInstanceMember()
63 && enclosingClass.isNative() 48 && enclosingClass.isNative()
64 && native.isOverriddenMethod( 49 && native.isOverriddenMethod(
65 element, enclosingClass, nativeEmitter)) { 50 element, enclosingClass, 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 nativeCode = new js.LiteralStatement(buffer.toString());
74 code.add(buffer); 60 body = new js.Block(<js.Statement>[nativeCode]);
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 1405
1735 // Register this invocation to collect the types used at all call sites. 1406 // Register this invocation to collect the types used at all call sites.
1736 Selector selector = getOptimizedSelectorFor(node, node.selector); 1407 Selector selector = getOptimizedSelectorFor(node, node.selector);
1737 backend.registerDynamicInvocation(node, selector); 1408 backend.registerDynamicInvocation(node, selector);
1738 1409
1739 if (node.element !== null) { 1410 if (node.element !== null) {
1740 // If we know we're calling a specific method, register that 1411 // If we know we're calling a specific method, register that
1741 // method only. 1412 // method only.
1742 if (inLoop) backend.builder.functionsCalledInLoop.add(node.element); 1413 if (inLoop) backend.builder.functionsCalledInLoop.add(node.element);
1743 world.registerDynamicInvocationOf(node.element); 1414 world.registerDynamicInvocationOf(node.element);
1744 } else { 1415 } else {
1745 if (inLoop) backend.builder.selectorsCalledInLoop[node.name] = selector; 1416 if (inLoop) backend.builder.selectorsCalledInLoop[node.name] = selector;
1746 world.registerDynamicInvocation(node.name, selector); 1417 world.registerDynamicInvocation(node.name, selector);
1747 } 1418 }
1748 } 1419 }
1749 endExpression(JSPrecedence.CALL_PRECEDENCE); 1420 push(jsPropertyCall(object, methodName, arguments), node);
1750 } 1421 }
1751 1422
1752 Selector getOptimizedSelectorFor(HInvoke node, Selector defaultSelector) { 1423 Selector getOptimizedSelectorFor(HInvoke node, Selector defaultSelector) {
1753 // TODO(4434): For private members we need to use the untyped selector. 1424 // TODO(4434): For private members we need to use the untyped selector.
1754 if (node.name.isPrivate()) return defaultSelector; 1425 if (node.name.isPrivate()) return defaultSelector;
1755 Type receiverType = node.inputs[0].propagatedType.computeType(compiler); 1426 Type receiverType = node.inputs[0].propagatedType.computeType(compiler);
1756 if (receiverType !== null) { 1427 if (receiverType !== null) {
1757 return new TypedSelector(receiverType, defaultSelector); 1428 return new TypedSelector(receiverType, defaultSelector);
1758 } else { 1429 } else {
1759 return defaultSelector; 1430 return defaultSelector;
1760 } 1431 }
1761 } 1432 }
1762 1433
1763 visitInvokeDynamicSetter(HInvokeDynamicSetter node) { 1434 visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
1764 beginExpression(JSPrecedence.CALL_PRECEDENCE); 1435 use(node.receiver);
1765 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); 1436 push(jsPropertyCall(pop(),
1766 buffer.add('.'); 1437 compiler.namer.setterName(currentLibrary, node.name),
1767 buffer.add(compiler.namer.setterName(currentLibrary, node.name)); 1438 visitArguments(node.inputs)),
1768 visitArguments(node.inputs); 1439 node);
1769 world.registerDynamicSetter( 1440 world.registerDynamicSetter(
1770 node.name, getOptimizedSelectorFor(node, Selector.SETTER)); 1441 node.name, getOptimizedSelectorFor(node, Selector.SETTER));
1771 endExpression(JSPrecedence.CALL_PRECEDENCE);
1772 } 1442 }
1773 1443
1774 visitInvokeDynamicGetter(HInvokeDynamicGetter node) { 1444 visitInvokeDynamicGetter(HInvokeDynamicGetter node) {
1775 beginExpression(JSPrecedence.CALL_PRECEDENCE); 1445 use(node.receiver);
1776 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); 1446 push(jsPropertyCall(pop(),
1777 buffer.add('.'); 1447 compiler.namer.getterName(currentLibrary, node.name),
1778 buffer.add(compiler.namer.getterName(currentLibrary, node.name)); 1448 visitArguments(node.inputs)),
1779 visitArguments(node.inputs); 1449 node);
1780 world.registerDynamicGetter( 1450 world.registerDynamicGetter(
1781 node.name, getOptimizedSelectorFor(node, Selector.GETTER)); 1451 node.name, getOptimizedSelectorFor(node, Selector.GETTER));
1782 endExpression(JSPrecedence.CALL_PRECEDENCE);
1783 } 1452 }
1784 1453
1785 visitInvokeClosure(HInvokeClosure node) { 1454 visitInvokeClosure(HInvokeClosure node) {
1786 beginExpression(JSPrecedence.CALL_PRECEDENCE); 1455 use(node.receiver);
1787 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); 1456 push(jsPropertyCall(pop(),
1788 buffer.add('.'); 1457 compiler.namer.closureInvocationName(node.selector),
1789 buffer.add(compiler.namer.closureInvocationName(node.selector)); 1458 visitArguments(node.inputs)),
1790 visitArguments(node.inputs); 1459 node);
1791 // TODO(floitsch): we should have a separate list for closure invocations. 1460 // TODO(floitsch): we should have a separate list for closure invocations.
1792 world.registerDynamicInvocation(compiler.namer.CLOSURE_INVOCATION_NAME, 1461 world.registerDynamicInvocation(compiler.namer.CLOSURE_INVOCATION_NAME,
1793 node.selector); 1462 node.selector);
1794 endExpression(JSPrecedence.CALL_PRECEDENCE);
1795 } 1463 }
1796 1464
1797 visitInvokeStatic(HInvokeStatic node) { 1465 visitInvokeStatic(HInvokeStatic node) {
1798 beginExpression(JSPrecedence.CALL_PRECEDENCE); 1466 use(node.target);
1799 use(node.target, JSPrecedence.CALL_PRECEDENCE); 1467 push(new js.Call(pop(), visitArguments(node.inputs)), node);
1800 visitArguments(node.inputs);
1801 endExpression(JSPrecedence.CALL_PRECEDENCE);
1802 } 1468 }
1803 1469
1804 visitInvokeSuper(HInvokeSuper node) { 1470 visitInvokeSuper(HInvokeSuper node) {
1805 beginExpression(JSPrecedence.CALL_PRECEDENCE);
1806 Element superMethod = node.element; 1471 Element superMethod = node.element;
1807 Element superClass = superMethod.getEnclosingClass(); 1472 Element superClass = superMethod.getEnclosingClass();
1808 // Remove the element and 'this'. 1473 // Remove the element and 'this'.
1809 int argumentCount = node.inputs.length - 2; 1474 int argumentCount = node.inputs.length - 2;
1810 String className = compiler.namer.isolateAccess(superClass); 1475 String className = compiler.namer.isolateAccess(superClass);
1811 if (superMethod.kind == ElementKind.FUNCTION || 1476 if (superMethod.kind == ElementKind.FIELD) {
1812 superMethod.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
1813 String methodName = compiler.namer.instanceMethodName(
1814 currentLibrary, superMethod.name, argumentCount);
1815 buffer.add('$className.prototype.$methodName.call');
1816 visitArguments(node.inputs);
1817 } else if (superMethod.kind == ElementKind.FIELD) {
1818 ClassElement currentClass = work.element.getEnclosingClass(); 1477 ClassElement currentClass = work.element.getEnclosingClass();
1478 String fieldName;
1819 if (currentClass.isShadowedByField(superMethod)) { 1479 if (currentClass.isShadowedByField(superMethod)) {
1820 buffer.add('this.${compiler.namer.shadowedFieldName(superMethod)}'); 1480 fieldName = compiler.namer.shadowedFieldName(superMethod);
1821 } else { 1481 } else {
1822 LibraryElement library = superMethod.getLibrary(); 1482 LibraryElement library = superMethod.getLibrary();
1823 SourceString name = superMethod.name; 1483 SourceString name = superMethod.name;
1824 buffer.add('this.${compiler.namer.instanceFieldName(library, name)}'); 1484 fieldName = compiler.namer.instanceFieldName(library, name);
1825 } 1485 }
1486 push(new js.PropertyAccess.field(new js.This(), fieldName), node);
1826 } else { 1487 } else {
1827 assert(superMethod.kind == ElementKind.GETTER ||
1828 superMethod.kind == ElementKind.SETTER);
1829 String methodName; 1488 String methodName;
1830 if (superMethod.kind == ElementKind.GETTER) { 1489 if (superMethod.kind == ElementKind.FUNCTION ||
1490 superMethod.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
1491 methodName = compiler.namer.instanceMethodName(
1492 currentLibrary, superMethod.name, argumentCount);
1493 } else if (superMethod.kind == ElementKind.GETTER) {
1831 methodName = 1494 methodName =
1832 compiler.namer.getterName(currentLibrary, superMethod.name); 1495 compiler.namer.getterName(currentLibrary, superMethod.name);
1833 } else { 1496 } else {
1497 assert(superMethod.kind == ElementKind.SETTER);
1834 methodName = 1498 methodName =
1835 compiler.namer.setterName(currentLibrary, superMethod.name); 1499 compiler.namer.setterName(currentLibrary, superMethod.name);
1836 } 1500 }
1837 buffer.add('$className.prototype.$methodName.call'); 1501 js.VariableUse classReference = new js.VariableUse(className);
1838 visitArguments(node.inputs); 1502 js.PropertyAccess prototype =
1503 new js.PropertyAccess.field(classReference, "prototype");
1504 js.PropertyAccess method =
1505 new js.PropertyAccess.field(prototype, methodName);
1506 push(jsPropertyCall(method, "call", visitArguments(node.inputs)), node);
1839 } 1507 }
1840 endExpression(JSPrecedence.CALL_PRECEDENCE);
1841 world.registerStaticUse(superMethod); 1508 world.registerStaticUse(superMethod);
1842 } 1509 }
1843 1510
1844 visitFieldGet(HFieldGet node) { 1511 visitFieldGet(HFieldGet node) {
1845 String name = 1512 String name =
1846 compiler.namer.instanceFieldName(node.library, node.fieldName); 1513 compiler.namer.instanceFieldName(node.library, node.fieldName);
1847 beginExpression(JSPrecedence.MEMBER_PRECEDENCE); 1514 use(node.receiver);
1848 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); 1515 push(new js.PropertyAccess.field(pop(), name), node);
1849 buffer.add('.');
1850 buffer.add(name);
1851 beginExpression(JSPrecedence.MEMBER_PRECEDENCE);
1852 if (node.element == null) { 1516 if (node.element == null) {
1853 // If we don't have an element we register a dynamic field getter. 1517 // If we don't have an element we register a dynamic field getter.
1854 // This might lead to unnecessary getters, but these cases should be 1518 // This might lead to unnecessary getters, but these cases should be
1855 // rare. 1519 // rare.
1856 world.registerDynamicGetter(node.fieldName, Selector.GETTER); 1520 world.registerDynamicGetter(node.fieldName, Selector.GETTER);
1857 } else { 1521 } else {
1858 Type type = node.receiver.propagatedType.computeType(compiler); 1522 Type type = node.receiver.propagatedType.computeType(compiler);
1859 if (type != null) { 1523 if (type != null) {
1860 world.registerFieldGetter(node.element.name, type); 1524 world.registerFieldGetter(node.element.name, type);
1861 } 1525 }
(...skipping 17 matching lines...) Expand all
1879 if (node.element != null && 1543 if (node.element != null &&
1880 work.element.isGenerativeConstructorBody() && 1544 work.element.isGenerativeConstructorBody() &&
1881 node.element.isMember() && 1545 node.element.isMember() &&
1882 node.value.hasGuaranteedType() && 1546 node.value.hasGuaranteedType() &&
1883 node.block.dominates(currentGraph.exit)) { 1547 node.block.dominates(currentGraph.exit)) {
1884 backend.updateFieldConstructorSetters(node.element, 1548 backend.updateFieldConstructorSetters(node.element,
1885 node.value.guaranteedType); 1549 node.value.guaranteedType);
1886 } 1550 }
1887 String name = 1551 String name =
1888 compiler.namer.instanceFieldName(node.library, node.fieldName); 1552 compiler.namer.instanceFieldName(node.library, node.fieldName);
1889 beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
1890 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
1891 buffer.add('.');
1892 buffer.add(name);
1893 if (node.element == null) { 1553 if (node.element == null) {
1894 // If we don't have an element we register a dynamic field setter. 1554 // If we don't have an element we register a dynamic field setter.
1895 // This might lead to unnecessary setters, but these cases should be 1555 // This might lead to unnecessary setters, but these cases should be
1896 // rare. 1556 // rare.
1897 world.registerDynamicSetter(node.fieldName, Selector.SETTER); 1557 world.registerDynamicSetter(node.fieldName, Selector.SETTER);
1898 } else { 1558 } else {
1899 Type type = node.receiver.propagatedType.computeType(compiler); 1559 Type type = node.receiver.propagatedType.computeType(compiler);
1900 if (type != null) { 1560 if (type != null) {
1901 if (!work.element.isGenerativeConstructorBody()) { 1561 if (!work.element.isGenerativeConstructorBody()) {
1902 world.registerFieldSetter(node.element.name, type); 1562 world.registerFieldSetter(node.element.name, type);
1903 } 1563 }
1904 // Determine the types seen so far for the field. If only number 1564 // Determine the types seen so far for the field. If only number
1905 // types have been seen and the value of the field set is a 1565 // types have been seen and the value of the field set is a
1906 // simple number computation only depending on that field, we 1566 // simple number computation only depending on that field, we
1907 // can safely keep the number type for the field. 1567 // can safely keep the number type for the field.
1908 HType fieldSettersType = backend.fieldSettersTypeSoFar(node.element); 1568 HType fieldSettersType = backend.fieldSettersTypeSoFar(node.element);
1909 HType initializersType = 1569 HType initializersType =
1910 backend.typeFromInitializersSoFar(node.element); 1570 backend.typeFromInitializersSoFar(node.element);
1911 HType fieldType = fieldSettersType.union(initializersType); 1571 HType fieldType = fieldSettersType.union(initializersType);
1912 if (HType.NUMBER.union(fieldType) == HType.NUMBER && 1572 if (HType.NUMBER.union(fieldType) == HType.NUMBER &&
1913 isSimpleFieldNumberComputation(node.value, node)) { 1573 isSimpleFieldNumberComputation(node.value, node)) {
1914 backend.updateFieldSetters(node.element, HType.NUMBER); 1574 backend.updateFieldSetters(node.element, HType.NUMBER);
1915 } else { 1575 } else {
1916 backend.updateFieldSetters(node.element, 1576 backend.updateFieldSetters(node.element,
1917 node.value.propagatedType); 1577 node.value.propagatedType);
1918 } 1578 }
1919 } 1579 }
1920 } 1580 }
1921 buffer.add(' = '); 1581 use(node.receiver);
1922 use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE); 1582 js.Expression receiver = pop();
1923 endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); 1583 use(node.value);
1584 push(new js.Assignment(new js.PropertyAccess.field(receiver, name), pop()),
1585 node);
1924 } 1586 }
1925 1587
1926 visitLocalGet(HLocalGet node) { 1588 visitLocalGet(HLocalGet node) {
1927 use(node.receiver, JSPrecedence.EXPRESSION_PRECEDENCE); 1589 use(node.receiver);
1928 } 1590 }
1929 1591
1930 visitLocalSet(HLocalSet node) { 1592 visitLocalSet(HLocalSet node) {
1931 declareInstruction(node.receiver); 1593 use(node.value);
1932 buffer.add(' = '); 1594 assignVariable(variableNames.getName(node.receiver), pop());
1933 use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE);
1934 } 1595 }
1935 1596
1936 visitForeign(HForeign node) { 1597 visitForeign(HForeign node) {
1937 String code = node.code.slowToString(); 1598 String code = node.code.slowToString();
1938 List<HInstruction> inputs = node.inputs; 1599 List<HInstruction> inputs = node.inputs;
1939 List<String> parts = code.split('#'); 1600 if (node.isStatement) {
1940 if (parts.length != inputs.length + 1) { 1601 if (!inputs.isEmpty()) {
1941 compiler.internalError( 1602 compiler.internalError("foreign statement with inputs: $code",
1942 'Wrong number of arguments for JS', instruction: node); 1603 instruction: node);
1604 }
1605 pushStatement(new js.LiteralStatement(code), node);
1606 } else {
1607 List<js.Expression> data = <js.Expression>[];
1608 for (int i = 0; i < inputs.length; i++) {
1609 use(inputs[i]);
1610 data.add(pop());
1611 }
1612 push(new js.LiteralExpression.withData(code, data), node);
1943 } 1613 }
1944 beginExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
1945 buffer.add(parts[0]);
1946 for (int i = 0; i < inputs.length; i++) {
1947 use(inputs[i], JSPrecedence.EXPRESSION_PRECEDENCE);
1948 buffer.add(parts[i + 1]);
1949 }
1950 endExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
1951 } 1614 }
1952 1615
1953 visitForeignNew(HForeignNew node) { 1616 visitForeignNew(HForeignNew node) {
1954 int j = 0; 1617 int j = 0;
1955 node.element.forEachInstanceField( 1618 node.element.forEachInstanceField(
1956 includeBackendMembers: true, 1619 includeBackendMembers: true,
1957 includeSuperMembers: true, 1620 includeSuperMembers: true,
1958 f: (ClassElement enclosingClass, Element member) { 1621 f: (ClassElement enclosingClass, Element member) {
1959 backend.updateFieldInitializers(member, 1622 backend.updateFieldInitializers(member,
1960 node.inputs[j].propagatedType); 1623 node.inputs[j].propagatedType);
1961 j++; 1624 j++;
1962 }); 1625 });
1963 String jsClassReference = compiler.namer.isolateAccess(node.element); 1626 String jsClassReference = compiler.namer.isolateAccess(node.element);
1964 beginExpression(JSPrecedence.MEMBER_PRECEDENCE); 1627 List<HInstruction> inputs = node.inputs;
1965 buffer.add('new $jsClassReference(');
1966 // We can't use 'visitArguments', since our arguments start at input[0]. 1628 // We can't use 'visitArguments', since our arguments start at input[0].
1967 List<HInstruction> inputs = node.inputs; 1629 List<js.Expression> arguments = <js.Expression>[];
1968 for (int i = 0; i < inputs.length; i++) { 1630 for (int i = 0; i < inputs.length; i++) {
1969 if (i != 0) buffer.add(', '); 1631 use(inputs[i]);
1970 use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); 1632 arguments.add(pop());
1971 } 1633 }
1972 buffer.add(')'); 1634 // TODO(floitsch): jsClassReference is an Access. We shouldn't treat it
1973 endExpression(JSPrecedence.MEMBER_PRECEDENCE); 1635 // as if it was a string.
1636 push(new js.New(new js.VariableUse(jsClassReference), arguments), node);
1974 } 1637 }
1975 1638
1976 void generateConstant(Constant constant) { 1639 void generateConstant(Constant constant) {
1977 // TODO(floitsch): the compile-time constant handler and the codegen
1978 // need to work together to avoid the parenthesis. See r4928 for an
1979 // implementation that still dealt with precedence.
1980 ConstantHandler handler = compiler.constantHandler; 1640 ConstantHandler handler = compiler.constantHandler;
1981 String name = handler.getNameForConstant(constant); 1641 String name = handler.getNameForConstant(constant);
1982 if (name === null) { 1642 if (name === null) {
1983 assert(!constant.isObject()); 1643 assert(!constant.isObject());
1984 if (constant.isNum() 1644 if (constant.isBool()) {
1985 && expectedPrecedence == JSPrecedence.MEMBER_PRECEDENCE) { 1645 push(new js.LiteralBool((constant as BoolConstant).value));
1986 buffer.add('('); 1646 } else if (constant.isNum()) {
1647 // TODO(floitsch): get rid of the code buffer.
1648 CodeBuffer buffer = new CodeBuffer();
1987 handler.writeConstant(buffer, constant); 1649 handler.writeConstant(buffer, constant);
1988 buffer.add(')'); 1650 push(new js.LiteralNumber(buffer.toString()));
1651 } else if (constant.isNull()) {
1652 push(new js.LiteralNull());
1653 } else if (constant.isString()) {
1654 // TODO(floitsch): get rid of the code buffer.
1655 CodeBuffer buffer = new CodeBuffer();
1656 handler.writeConstant(buffer, constant);
1657 push(new js.LiteralString(buffer.toString()));
1989 } else { 1658 } else {
1990 handler.writeConstant(buffer, constant); 1659 compiler.internalError("Forgot constant $constant");
1991 } 1660 }
1992 } else { 1661 } else {
1993 buffer.add(compiler.namer.CURRENT_ISOLATE); 1662 js.VariableUse currentIsolateUse =
1994 buffer.add("."); 1663 new js.VariableUse(compiler.namer.CURRENT_ISOLATE);
1995 buffer.add(name); 1664 push(new js.PropertyAccess.field(currentIsolateUse, name));
1996 } 1665 }
1997
1998 } 1666 }
1999 1667
2000 visitConstant(HConstant node) { 1668 visitConstant(HConstant node) {
2001 assert(isGenerateAtUseSite(node)); 1669 assert(isGenerateAtUseSite(node));
2002 generateConstant(node.constant); 1670 generateConstant(node.constant);
2003 } 1671 }
2004 1672
2005 visitLoopBranch(HLoopBranch node) { 1673 visitLoopBranch(HLoopBranch node) {
2006 if (subGraph !== null && node.block === subGraph.end) { 1674 if (subGraph !== null && node.block === subGraph.end) {
2007 // We are generating code for a loop condition. 1675 // We are generating code for a loop condition.
2008 // If doing this as part of a SubGraph traversal, the 1676 // If doing this as part of a SubGraph traversal, the
2009 // calling code will handle the control flow logic. 1677 // calling code will handle the control flow logic.
2010 1678
2011 // If we are generating the subgraph as an expression, the 1679 // If we are generating the subgraph as an expression, the
2012 // condition will be generated as the expression. 1680 // condition will be generated as the expression.
2013 // Otherwise, we don't generate the expression, and leave that 1681 // Otherwise, we don't generate the expression, and leave that
2014 // to the code that called [visitSubGraph]. 1682 // to the code that called [visitSubGraph].
2015 if (isGeneratingExpression()) { 1683 if (isGeneratingExpression) {
2016 use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE); 1684 use(node.inputs[0]);
2017 } 1685 }
2018 return; 1686 return;
2019 } 1687 }
2020 HBasicBlock branchBlock = currentBlock; 1688 HBasicBlock branchBlock = currentBlock;
2021 addIndentation();
2022 handleLoopCondition(node); 1689 handleLoopCondition(node);
2023 List<HBasicBlock> dominated = currentBlock.dominatedBlocks; 1690 List<HBasicBlock> dominated = currentBlock.dominatedBlocks;
2024 // For a do while loop, the body has already been visited. 1691 // For a do while loop, the body has already been visited.
2025 if (!node.isDoWhile()) { 1692 if (!node.isDoWhile()) {
2026 visitBasicBlock(dominated[0]); 1693 visitBasicBlock(dominated[0]);
2027 } 1694 }
2028 endLoop(node.block); 1695 endLoop(node.block);
2029 1696
2030 // If the branch does not dominate the code after the loop, the 1697 // If the branch does not dominate the code after the loop, the
2031 // dominator will visit it. 1698 // dominator will visit it.
2032 if (branchBlock.successors[1].dominator !== branchBlock) return; 1699 if (branchBlock.successors[1].dominator !== branchBlock) return;
2033 1700
2034 visitBasicBlock(branchBlock.successors[1]); 1701 visitBasicBlock(branchBlock.successors[1]);
2035 // With labeled breaks we can have more dominated blocks. 1702 // With labeled breaks we can have more dominated blocks.
2036 if (dominated.length >= 3) { 1703 if (dominated.length >= 3) {
2037 for (int i = 2; i < dominated.length; i++) { 1704 for (int i = 2; i < dominated.length; i++) {
2038 visitBasicBlock(dominated[i]); 1705 visitBasicBlock(dominated[i]);
2039 } 1706 }
2040 } 1707 }
2041 } 1708 }
2042 1709
2043 visitNot(HNot node) { 1710 visitNot(HNot node) {
2044 assert(node.inputs.length == 1); 1711 assert(node.inputs.length == 1);
2045 generateNot(node.inputs[0]); 1712 generateNot(node.inputs[0]);
1713 attachLocationToLast(node);
2046 } 1714 }
2047 1715
2048 1716
2049 void generateNot(HInstruction input) { 1717 void generateNot(HInstruction input) {
2050 bool isBuiltinRelational(HInstruction instruction) { 1718 bool isBuiltinRelational(HInstruction instruction) {
2051 if (instruction is !HRelational) return false; 1719 if (instruction is !HRelational) return false;
2052 HRelational relational = instruction; 1720 HRelational relational = instruction;
2053 return relational.builtin; 1721 return relational.builtin;
2054 } 1722 }
2055 1723
2056 if (input is HBoolify && isGenerateAtUseSite(input)) { 1724 if (input is HBoolify && isGenerateAtUseSite(input)) {
2057 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 1725 use(input.inputs[0]);
2058 use(input.inputs[0], JSPrecedence.EQUALITY_PRECEDENCE); 1726 push(new js.Binary("!==", pop(), new js.LiteralBool(true)), input);
2059 buffer.add(' !== true');
2060 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2061 } else if (isBuiltinRelational(input) && 1727 } else if (isBuiltinRelational(input) &&
2062 isGenerateAtUseSite(input) && 1728 isGenerateAtUseSite(input) &&
2063 input.inputs[0].propagatedType.isUseful() && 1729 input.inputs[0].propagatedType.isUseful() &&
2064 !input.inputs[0].isDouble() && 1730 !input.inputs[0].isDouble() &&
2065 input.inputs[1].propagatedType.isUseful() && 1731 input.inputs[1].propagatedType.isUseful() &&
2066 !input.inputs[1].isDouble()) { 1732 !input.inputs[1].isDouble()) {
2067 // This optimization doesn't work for NaN, so we only do it if the 1733 // This optimization doesn't work for NaN, so we only do it if the
2068 // type is known to be non-Double. 1734 // type is known to be non-Double.
2069 Map<String, String> inverseOperator = const <String>{ 1735 Map<String, String> inverseOperator = const <String>{
2070 "==" : "!=", 1736 "==" : "!=",
2071 "!=" : "==", 1737 "!=" : "==",
2072 "===": "!==", 1738 "===": "!==",
2073 "!==": "===", 1739 "!==": "===",
2074 "<" : ">=", 1740 "<" : ">=",
2075 "<=" : ">", 1741 "<=" : ">",
2076 ">" : "<=", 1742 ">" : "<=",
2077 ">=" : "<" 1743 ">=" : "<"
2078 }; 1744 };
2079 HRelational relational = input; 1745 HRelational relational = input;
2080 visitInvokeBinary(input, 1746 visitInvokeBinary(input,
2081 inverseOperator[relational.operation.name.stringValue]); 1747 inverseOperator[relational.operation.name.stringValue]);
2082 } else { 1748 } else {
2083 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); 1749 use(input);
2084 buffer.add('!'); 1750 push(new js.Prefix("!", pop()));
2085 use(input, JSPrecedence.PREFIX_PRECEDENCE);
2086 endExpression(JSPrecedence.PREFIX_PRECEDENCE);
2087 } 1751 }
2088 } 1752 }
2089 1753
2090 visitParameterValue(HParameterValue node) => visitLocalValue(node); 1754 visitParameterValue(HParameterValue node) => visitLocalValue(node);
2091 1755
2092 visitLocalValue(HLocalValue node) { 1756 visitLocalValue(HLocalValue node) {
2093 assert(isGenerateAtUseSite(node)); 1757 assert(isGenerateAtUseSite(node));
2094 buffer.add(variableNames.getName(node)); 1758 push(new js.VariableUse(variableNames.getName(node)), node);
2095 } 1759 }
2096 1760
2097 visitPhi(HPhi node) { 1761 visitPhi(HPhi node) {
2098 // This method is only called for phis that are generated at use 1762 // This method is only called for phis that are generated at use
2099 // site. A phi can be generated at use site only if it is the 1763 // site. A phi can be generated at use site only if it is the
2100 // result of a control flow operation. 1764 // result of a control flow operation.
2101 HBasicBlock ifBlock = node.block.dominator; 1765 HBasicBlock ifBlock = node.block.dominator;
2102 assert(controlFlowOperators.contains(ifBlock.last)); 1766 assert(controlFlowOperators.contains(ifBlock.last));
2103 HInstruction input = ifBlock.last.inputs[0]; 1767 HInstruction input = ifBlock.last.inputs[0];
2104 if (input.isConstantFalse()) { 1768 if (input.isConstantFalse()) {
2105 use(node.inputs[1], expectedPrecedence); 1769 use(node.inputs[1]);
2106 } else if (input.isConstantTrue()) { 1770 } else if (input.isConstantTrue()) {
2107 use(node.inputs[0], expectedPrecedence); 1771 use(node.inputs[0]);
2108 } else if (node.inputs[1].isConstantBoolean()) { 1772 } else if (node.inputs[1].isConstantBoolean()) {
2109 String operation = node.inputs[1].isConstantFalse() ? '&&' : '||'; 1773 String operation = node.inputs[1].isConstantFalse() ? '&&' : '||';
2110 JSBinaryOperatorPrecedence operatorPrecedence =
2111 JSPrecedence.binary[operation];
2112 beginExpression(operatorPrecedence.precedence);
2113 if (operation == '||') { 1774 if (operation == '||') {
2114 if (input is HNot) { 1775 if (input is HNot) {
2115 use(input.inputs[0], operatorPrecedence.left); 1776 use(input.inputs[0]);
2116 } else { 1777 } else {
2117 generateNot(input); 1778 generateNot(input);
2118 } 1779 }
2119 } else { 1780 } else {
2120 use(input, operatorPrecedence.left); 1781 use(input);
2121 } 1782 }
2122 buffer.add(" $operation "); 1783 js.Expression left = pop();
2123 use(node.inputs[0], operatorPrecedence.right); 1784 use(node.inputs[0]);
2124 endExpression(operatorPrecedence.precedence); 1785 push(new js.Binary(operation, left, pop()));
2125 } else { 1786 } else {
2126 beginExpression(JSPrecedence.CONDITIONAL_PRECEDENCE); 1787 use(input);
2127 use(input, JSPrecedence.LOGICAL_OR_PRECEDENCE); 1788 js.Expression test = pop();
2128 buffer.add(' ? '); 1789 use(node.inputs[0]);
2129 use(node.inputs[0], JSPrecedence.ASSIGNMENT_PRECEDENCE); 1790 js.Expression then = pop();
2130 buffer.add(' : '); 1791 use(node.inputs[1]);
2131 use(node.inputs[1], JSPrecedence.ASSIGNMENT_PRECEDENCE); 1792 push(new js.Conditional(test, then, pop()));
2132 endExpression(JSPrecedence.CONDITIONAL_PRECEDENCE);
2133 } 1793 }
2134 } 1794 }
2135 1795
2136 visitReturn(HReturn node) { 1796 visitReturn(HReturn node) {
2137 addIndentation();
2138 assert(node.inputs.length == 1); 1797 assert(node.inputs.length == 1);
2139 HInstruction input = node.inputs[0]; 1798 HInstruction input = node.inputs[0];
2140 if (input.isConstantNull()) { 1799 if (input.isConstantNull()) {
2141 buffer.add('return;\n'); 1800 pushStatement(new js.Return(null), node);
2142 } else { 1801 } else {
2143 buffer.add('return '); 1802 use(node.inputs[0]);
2144 use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE); 1803 pushStatement(new js.Return(pop()), node);
2145 buffer.add(';\n');
2146 } 1804 }
2147 } 1805 }
2148 1806
2149 visitThis(HThis node) { 1807 visitThis(HThis node) {
2150 buffer.add('this'); 1808 push(new js.This());
2151 } 1809 }
2152 1810
2153 visitThrow(HThrow node) { 1811 visitThrow(HThrow node) {
2154 addIndentation();
2155 if (node.isRethrow) { 1812 if (node.isRethrow) {
2156 buffer.add('throw '); 1813 use(node.inputs[0]);
2157 use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE); 1814 pushStatement(new js.Throw(pop()), node);
2158 } else { 1815 } else {
2159 generateThrowWithHelper('captureStackTrace', node.inputs[0]); 1816 generateThrowWithHelper('captureStackTrace', node.inputs[0]);
2160 } 1817 }
2161 buffer.add(';\n');
2162 } 1818 }
2163 1819
2164 visitBoundsCheck(HBoundsCheck node) { 1820 visitBoundsCheck(HBoundsCheck node) {
2165 // TODO(ngeoffray): Separate the two checks of the bounds check, so, 1821 // TODO(ngeoffray): Separate the two checks of the bounds check, so,
2166 // e.g., the zero checks can be shared if possible. 1822 // e.g., the zero checks can be shared if possible.
2167 1823
2168 // If the checks always succeede, we would have removed the bounds check 1824 // If the checks always succeeds, we would have removed the bounds check
2169 // completely. 1825 // completely.
2170 assert(node.staticChecks != HBoundsCheck.ALWAYS_TRUE); 1826 assert(node.staticChecks != HBoundsCheck.ALWAYS_TRUE);
2171 if (node.staticChecks != HBoundsCheck.ALWAYS_FALSE) { 1827 if (node.staticChecks != HBoundsCheck.ALWAYS_FALSE) {
2172 buffer.add('if ('); 1828 js.Binary under;
2173 if (node.staticChecks != HBoundsCheck.ALWAYS_ABOVE_ZERO) { 1829 if (node.staticChecks != HBoundsCheck.ALWAYS_ABOVE_ZERO) {
2174 assert(node.staticChecks == HBoundsCheck.FULL_CHECK); 1830 assert(node.staticChecks == HBoundsCheck.FULL_CHECK);
2175 use(node.index, JSPrecedence.RELATIONAL_PRECEDENCE); 1831 use(node.index);
2176 buffer.add(' < 0 || '); 1832 under = new js.Binary("<", pop(), new js.LiteralNumber("0"));
2177 } 1833 }
2178 use(node.index, JSPrecedence.RELATIONAL_PRECEDENCE); 1834 use(node.index);
2179 buffer.add(' >= '); 1835 js.Expression index = pop();
2180 use(node.length, JSPrecedence.SHIFT_PRECEDENCE); 1836 use(node.length);
2181 buffer.add(") "); 1837 js.Binary over = new js.Binary(">=", index, pop());
1838 js.Binary underOver =
1839 under == null ? over : new js.Binary("||", under, over);
1840 js.Statement thenBody = new js.Block.empty();
1841 js.Block oldContainer = currentContainer;
1842 currentContainer = thenBody;
1843 generateThrowWithHelper('ioore', node.index);
1844 currentContainer = oldContainer;
1845 thenBody = unwrapStatement(thenBody);
1846 pushStatement(new js.If.then(underOver, thenBody), node);
1847 } else {
1848 generateThrowWithHelper('ioore', node.index);
2182 } 1849 }
2183 generateThrowWithHelper('ioore', node.index);
2184 } 1850 }
2185 1851
2186 visitIntegerCheck(HIntegerCheck node) { 1852 visitIntegerCheck(HIntegerCheck node) {
2187 if (!node.alwaysFalse) { 1853 if (!node.alwaysFalse) {
2188 buffer.add('if (');
2189 checkInt(node.value, '!=='); 1854 checkInt(node.value, '!==');
2190 buffer.add(') '); 1855 js.Expression test = pop();
1856 js.Statement thenBody = new js.Block.empty();
1857 js.Block oldContainer = currentContainer;
1858 currentContainer = thenBody;
1859 generateThrowWithHelper('iae', node.value);
1860 currentContainer = oldContainer;
1861 thenBody = unwrapStatement(thenBody);
1862 pushStatement(new js.If.then(test, thenBody), node);
1863 } else {
1864 generateThrowWithHelper('iae', node.value);
2191 } 1865 }
2192 generateThrowWithHelper('iae', node.value);
2193 } 1866 }
2194 1867
2195 void generateThrowWithHelper(String helperName, HInstruction argument) { 1868 void generateThrowWithHelper(String helperName, HInstruction argument) {
2196 Element helper = compiler.findHelper(new SourceString(helperName)); 1869 Element helper = compiler.findHelper(new SourceString(helperName));
2197 world.registerStaticUse(helper); 1870 world.registerStaticUse(helper);
2198 buffer.add('throw '); 1871 js.VariableUse jsHelper =
2199 beginExpression(JSPrecedence.EXPRESSION_PRECEDENCE); 1872 new js.VariableUse(compiler.namer.isolateAccess(helper));
2200 beginExpression(JSPrecedence.CALL_PRECEDENCE); 1873 js.Call value = new js.Call(jsHelper, visitArguments([null, argument]));
2201 buffer.add(compiler.namer.isolateAccess(helper)); 1874 attachLocation(value, argument);
2202 visitArguments([null, argument]); 1875 pushStatement(new js.Throw(value));
2203 endExpression(JSPrecedence.CALL_PRECEDENCE);
2204 endExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
2205 }
2206
2207 void addIndentation() {
2208 for (int i = 0; i < indent; i++) {
2209 buffer.add(' ');
2210 }
2211 }
2212
2213 void addIndented(String text) {
2214 addIndentation();
2215 buffer.add(text);
2216 } 1876 }
2217 1877
2218 void visitSwitch(HSwitch node) { 1878 void visitSwitch(HSwitch node) {
2219 // Switches are handled using [visitSwitchInfo]. 1879 // Switches are handled using [visitSwitchInfo].
2220 } 1880 }
2221 1881
2222 void visitStatic(HStatic node) { 1882 void visitStatic(HStatic node) {
2223 world.registerStaticUse(node.element); 1883 world.registerStaticUse(node.element);
2224 buffer.add(compiler.namer.isolateAccess(node.element)); 1884 push(new js.VariableUse(compiler.namer.isolateAccess(node.element)));
2225 } 1885 }
2226 1886
2227 void visitStaticStore(HStaticStore node) { 1887 void visitStaticStore(HStaticStore node) {
2228 world.registerStaticUse(node.element); 1888 world.registerStaticUse(node.element);
2229 beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); 1889 js.VariableUse variableUse =
2230 buffer.add(compiler.namer.isolateAccess(node.element)); 1890 new js.VariableUse(compiler.namer.isolateAccess(node.element));
2231 buffer.add(' = '); 1891 use(node.inputs[0]);
2232 use(node.inputs[0], JSPrecedence.ASSIGNMENT_PRECEDENCE); 1892 push(new js.Assignment(variableUse, pop()), node);
2233 endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
2234 } 1893 }
2235 1894
2236 void visitStringConcat(HStringConcat node) { 1895 void visitStringConcat(HStringConcat node) {
2237 if (isEmptyString(node.left)) { 1896 if (isEmptyString(node.left)) {
2238 useStringified(node.right, expectedPrecedence); 1897 useStringified(node.right);
2239 } else if (isEmptyString(node.right)) { 1898 } else if (isEmptyString(node.right)) {
2240 useStringified(node.left, expectedPrecedence); 1899 useStringified(node.left);
2241 } else { 1900 } else {
2242 JSBinaryOperatorPrecedence operatorPrecedences = JSPrecedence.binary['+']; 1901 useStringified(node.left);
2243 beginExpression(operatorPrecedences.precedence); 1902 js.Expression left = pop();
2244 useStringified(node.left, operatorPrecedences.left); 1903 useStringified(node.right);
2245 buffer.add(' + '); 1904 push(new js.Binary("+", left, pop()), node);
2246 // If the right hand side is a string concatenation itself it is
2247 // safe to make it left associative.
2248 int rightPrecedence = (node.right is HStringConcat)
2249 ? JSPrecedence.ADDITIVE_PRECEDENCE
2250 : operatorPrecedences.right;
2251 useStringified(node.right, rightPrecedence);
2252 endExpression(operatorPrecedences.precedence);
2253 } 1905 }
2254 } 1906 }
2255 1907
2256 bool isEmptyString(HInstruction node) { 1908 bool isEmptyString(HInstruction node) {
2257 if (!node.isConstantString()) return false; 1909 if (!node.isConstantString()) return false;
2258 HConstant constant = node; 1910 HConstant constant = node;
2259 StringConstant string = constant.constant; 1911 StringConstant string = constant.constant;
2260 return string.value.length == 0; 1912 return string.value.length == 0;
2261 } 1913 }
2262 1914
2263 void useStringified(HInstruction node, int precedence) { 1915 void useStringified(HInstruction node) {
2264 if (node.isString()) { 1916 if (node.isString()) {
2265 use(node, precedence); 1917 use(node);
2266 } else { 1918 } else {
2267 Element convertToString = compiler.findHelper(const SourceString("S")); 1919 Element convertToString = compiler.findHelper(const SourceString("S"));
2268 world.registerStaticUse(convertToString); 1920 world.registerStaticUse(convertToString);
2269 buffer.add(compiler.namer.isolateAccess(convertToString)); 1921 js.VariableUse variableUse =
2270 buffer.add('('); 1922 new js.VariableUse(compiler.namer.isolateAccess(convertToString));
2271 use(node, JSPrecedence.EXPRESSION_PRECEDENCE); 1923 use(node);
2272 buffer.add(')'); 1924 push(new js.Call(variableUse, <js.Expression>[pop()]), node);
2273 } 1925 }
2274 } 1926 }
2275 1927
2276 void visitLiteralList(HLiteralList node) { 1928 void visitLiteralList(HLiteralList node) {
2277 generateArrayLiteral(node); 1929 generateArrayLiteral(node);
2278 } 1930 }
2279 1931
2280 void generateArrayLiteral(HLiteralList node) { 1932 void generateArrayLiteral(HLiteralList node) {
2281 buffer.add('[');
2282 int len = node.inputs.length; 1933 int len = node.inputs.length;
1934 List<js.ArrayElement> elements = <js.ArrayElement>[];
2283 for (int i = 0; i < len; i++) { 1935 for (int i = 0; i < len; i++) {
2284 if (i != 0) buffer.add(', '); 1936 use(node.inputs[i]);
2285 use(node.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); 1937 elements.add(new js.ArrayElement(i, pop()));
2286 } 1938 }
2287 buffer.add(']'); 1939 push(new js.ArrayInitializer(len, elements), node);
2288 } 1940 }
2289 1941
2290 void visitIndex(HIndex node) { 1942 void visitIndex(HIndex node) {
2291 if (node.builtin) { 1943 if (node.builtin) {
2292 beginExpression(JSPrecedence.MEMBER_PRECEDENCE); 1944 use(node.inputs[1]);
2293 use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE); 1945 js.Expression receiver = pop();
2294 buffer.add('['); 1946 use(node.inputs[2]);
2295 use(node.inputs[2], JSPrecedence.EXPRESSION_PRECEDENCE); 1947 push(new js.PropertyAccess(receiver, pop()), node);
2296 buffer.add(']');
2297 endExpression(JSPrecedence.MEMBER_PRECEDENCE);
2298 } else { 1948 } else {
2299 visitInvokeStatic(node); 1949 visitInvokeStatic(node);
2300 } 1950 }
2301 } 1951 }
2302 1952
2303 void visitIndexAssign(HIndexAssign node) { 1953 void visitIndexAssign(HIndexAssign node) {
2304 if (node.builtin) { 1954 if (node.builtin) {
2305 beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); 1955 use(node.inputs[1]);
2306 use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE); 1956 js.Expression receiver = pop();
2307 buffer.add('['); 1957 use(node.inputs[2]);
2308 use(node.inputs[2], JSPrecedence.EXPRESSION_PRECEDENCE); 1958 js.Expression index = pop();
2309 buffer.add('] = '); 1959 use(node.inputs[3]);
2310 use(node.inputs[3], JSPrecedence.ASSIGNMENT_PRECEDENCE); 1960 push(new js.Assignment(new js.PropertyAccess(receiver, index), pop()),
2311 endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); 1961 node);
2312 } else { 1962 } else {
2313 visitInvokeStatic(node); 1963 visitInvokeStatic(node);
2314 } 1964 }
2315 } 1965 }
2316 1966
2317 String builtinJsName(HInvokeInterceptor interceptor) { 1967 String builtinJsName(HInvokeInterceptor interceptor) {
2318 // Don't count the target method or the receiver in the arity. 1968 // Don't count the target method or the receiver in the arity.
2319 int arity = interceptor.inputs.length - 2; 1969 int arity = interceptor.inputs.length - 2;
2320 HInstruction receiver = interceptor.inputs[1]; 1970 HInstruction receiver = interceptor.inputs[1];
2321 bool getter = interceptor.getter; 1971 bool getter = interceptor.getter;
(...skipping 16 matching lines...) Expand all
2338 } 1988 }
2339 } 1989 }
2340 1990
2341 return null; 1991 return null;
2342 } 1992 }
2343 1993
2344 void visitInvokeInterceptor(HInvokeInterceptor node) { 1994 void visitInvokeInterceptor(HInvokeInterceptor node) {
2345 String builtin = builtinJsName(node); 1995 String builtin = builtinJsName(node);
2346 if (builtin !== null) { 1996 if (builtin !== null) {
2347 if (builtin == '+') { 1997 if (builtin == '+') {
2348 beginExpression(JSPrecedence.ADDITIVE_PRECEDENCE); 1998 use(node.inputs[1]);
2349 use(node.inputs[1], JSPrecedence.ADDITIVE_PRECEDENCE); 1999 js.Expression left = pop();
2350 buffer.add(' + '); 2000 use(node.inputs[2]);
2351 use(node.inputs[2], JSPrecedence.MULTIPLICATIVE_PRECEDENCE); 2001 push(new js.Binary("+", left, pop()), node);
2352 endExpression(JSPrecedence.ADDITIVE_PRECEDENCE);
2353 } else { 2002 } else {
2354 beginExpression(JSPrecedence.CALL_PRECEDENCE); 2003 use(node.inputs[1]);
2355 use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE); 2004 js.PropertyAccess access = new js.PropertyAccess.field(pop(), builtin);
2356 buffer.add('.'); 2005 if (node.getter) {
2357 buffer.add(builtin); 2006 push(access, node);
2358 if (node.getter) return; 2007 return;
2359 buffer.add('('); 2008 }
2009 List<js.Expression> arguments = <js.Expression>[];
2360 for (int i = 2; i < node.inputs.length; i++) { 2010 for (int i = 2; i < node.inputs.length; i++) {
2361 if (i != 2) buffer.add(', '); 2011 use(node.inputs[i]);
2362 use(node.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); 2012 arguments.add(pop());
2363 } 2013 }
2364 buffer.add(")"); 2014 push(new js.Call(access, arguments), node);
2365 endExpression(JSPrecedence.CALL_PRECEDENCE);
2366 } 2015 }
2367 } else { 2016 } else {
2368 return visitInvokeStatic(node); 2017 return visitInvokeStatic(node);
2369 } 2018 }
2370 } 2019 }
2371 2020
2372 void checkInt(HInstruction input, String cmp) { 2021 void checkInt(HInstruction input, String cmp) {
2373 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2022 use(input);
2374 use(input, JSPrecedence.EQUALITY_PRECEDENCE); 2023 js.Expression left = pop();
2375 buffer.add(' $cmp ('); 2024 use(input);
2376 use(input, JSPrecedence.BITWISE_OR_PRECEDENCE); 2025 js.Expression or0 = new js.Binary("|", pop(), new js.LiteralNumber("0"));
2377 buffer.add(' | 0)'); 2026 push(new js.Binary(cmp, left, or0));
2378 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2379 } 2027 }
2380 2028
2381 void checkNum(HInstruction input, String cmp) { 2029 void checkTypeOf(HInstruction input, String cmp, String typeName) {
2382 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2030 use(input);
2383 buffer.add('typeof '); 2031 js.Expression typeOf = new js.Prefix("typeof", pop());
2384 use(input, JSPrecedence.PREFIX_PRECEDENCE); 2032 push(new js.Binary(cmp, typeOf, new js.LiteralString("'$typeName'")));
2385 buffer.add(" $cmp 'number'");
2386 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2387 } 2033 }
2388 2034
2389 void checkDouble(HInstruction input, String cmp) { 2035 void checkNum(HInstruction input, String cmp)
2390 checkNum(input, cmp); 2036 => checkTypeOf(input, cmp, 'number');
2391 }
2392 2037
2393 void checkString(HInstruction input, String cmp) { 2038 void checkDouble(HInstruction input, String cmp) => checkNum(input, cmp);
2394 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2395 buffer.add('typeof ');
2396 use(input, JSPrecedence.PREFIX_PRECEDENCE);
2397 buffer.add(" $cmp 'string'");
2398 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2399 }
2400 2039
2401 void checkBool(HInstruction input, String cmp) { 2040 void checkString(HInstruction input, String cmp)
2402 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2041 => checkTypeOf(input, cmp, 'string');
2403 buffer.add('typeof '); 2042
2404 use(input, JSPrecedence.PREFIX_PRECEDENCE); 2043 void checkBool(HInstruction input, String cmp)
2405 buffer.add(" $cmp 'boolean'"); 2044 => checkTypeOf(input, cmp, 'boolean');
2406 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2407 }
2408 2045
2409 void checkObject(HInstruction input, String cmp) { 2046 void checkObject(HInstruction input, String cmp) {
2410 assert(NullConstant.JsNull == 'null'); 2047 assert(NullConstant.JsNull == 'null');
2411 if (cmp == "===") { 2048 if (cmp == "===") {
2412 withPrecedence(JSPrecedence.LOGICAL_AND_PRECEDENCE, () { 2049 checkTypeOf(input, '===', 'object');
2413 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2050 js.Expression left = pop();
2414 buffer.add('typeof '); 2051 use(input);
2415 use(input, JSPrecedence.PREFIX_PRECEDENCE); 2052 js.Expression notNull = new js.Binary("!==", pop(), new js.LiteralNull());
2416 buffer.add(" === 'object'"); 2053 push(new js.Binary("&&", left, notNull));
2417 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2418 buffer.add(" && ");
2419 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2420 use(input, JSPrecedence.PREFIX_PRECEDENCE);
2421 buffer.add(" !== null");
2422 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2423 });
2424 } else { 2054 } else {
2425 assert(cmp == "!=="); 2055 assert(cmp == "!==");
2426 withPrecedence(JSPrecedence.LOGICAL_OR_PRECEDENCE, () { 2056 checkTypeOf(input, '!==', 'object');
2427 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2057 js.Expression left = pop();
2428 buffer.add('typeof '); 2058 use(input);
2429 use(input, JSPrecedence.PREFIX_PRECEDENCE); 2059 js.Expression eqNull = new js.Binary("===", pop(), new js.LiteralNull());
2430 buffer.add(" !== 'object'"); 2060 push(new js.Binary("||", left, eqNull));
2431 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2432 buffer.add(" || ");
2433 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2434 use(input, JSPrecedence.PREFIX_PRECEDENCE);
2435 buffer.add(" === null");
2436 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2437 });
2438 } 2061 }
2439 } 2062 }
2440 2063
2441 void checkArray(HInstruction input, String cmp) { 2064 void checkArray(HInstruction input, String cmp) {
2442 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2065 use(input);
2443 use(input, JSPrecedence.MEMBER_PRECEDENCE); 2066 js.PropertyAccess constructor =
2444 buffer.add('.constructor $cmp Array'); 2067 new js.PropertyAccess.field(pop(), 'constructor');
2445 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2068 push(new js.Binary(cmp, constructor, new js.VariableUse('Array')));
2069 }
2070
2071 void checkFieldExists(HInstruction input, String fieldName) {
2072 use(input);
2073 js.PropertyAccess field = new js.PropertyAccess.field(pop(), fieldName);
2074 // Double negate to boolify the result.
2075 push(new js.Prefix('!', new js.Prefix('!', field)));
2446 } 2076 }
2447 2077
2448 void checkImmutableArray(HInstruction input) { 2078 void checkImmutableArray(HInstruction input) {
2449 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); 2079 checkFieldExists(input, 'immutable\$list');
2450 buffer.add('!!');
2451 use(input, JSPrecedence.MEMBER_PRECEDENCE);
2452 buffer.add('.immutable\$list');
2453 endExpression(JSPrecedence.PREFIX_PRECEDENCE);
2454 } 2080 }
2455 2081
2456 void checkExtendableArray(HInstruction input) { 2082 void checkExtendableArray(HInstruction input) {
2457 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); 2083 checkFieldExists(input, 'fixed\$length');
2458 buffer.add('!!');
2459 use(input, JSPrecedence.MEMBER_PRECEDENCE);
2460 buffer.add('.fixed\$length');
2461 endExpression(JSPrecedence.PREFIX_PRECEDENCE);
2462 } 2084 }
2463 2085
2464 void checkFixedArray(HInstruction input) { 2086 void checkFixedArray(HInstruction input) {
2465 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); 2087 checkFieldExists(input, 'fixed\$length');
2466 use(input, JSPrecedence.MEMBER_PRECEDENCE);
2467 buffer.add('.fixed\$length');
2468 endExpression(JSPrecedence.PREFIX_PRECEDENCE);
2469 } 2088 }
2470 2089
2471 void checkNull(HInstruction input) { 2090 void checkNull(HInstruction input) {
2472 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2091 use(input);
2473 use(input, JSPrecedence.EQUALITY_PRECEDENCE); 2092 push(new js.Binary('==', pop(), new js.LiteralNull()));
2474 buffer.add(" == null");
2475 endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
2476 } 2093 }
2477 2094
2478 void checkFunction(HInstruction input, Element element) { 2095 void checkFunction(HInstruction input, Element element) {
2479 withPrecedence(JSPrecedence.LOGICAL_OR_PRECEDENCE, () { 2096 checkTypeOf(input, '===', 'function');
2480 beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2097 js.Expression functionTest = pop();
2481 buffer.add('typeof '); 2098 checkObject(input, '===');
2482 use(input, JSPrecedence.PREFIX_PRECEDENCE); 2099 js.Expression objectTest = pop();
2483 buffer.add(" === 'function'"); 2100 checkType(input, element);
2484 endExpression(JSPrecedence.EQUALITY_PRECEDENCE); 2101 push(new js.Binary('||',
2485 buffer.add(" || "); 2102 functionTest,
2486 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); 2103 new js.Binary('&&', objectTest, pop())));
2487 checkObject(input, '===');
2488 buffer.add(" && ");
2489 checkType(input, element);
2490 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
2491 });
2492 } 2104 }
2493 2105
2494 void checkType(HInstruction input, Element element, [bool negative = false]) { 2106 void checkType(HInstruction input, Element element, [bool negative = false]) {
2495 world.registerIsCheck(element); 2107 world.registerIsCheck(element);
2496 bool requiresNativeIsCheck = 2108 use(input);
2497 backend.emitter.nativeEmitter.requiresNativeIsCheck(element); 2109 js.PropertyAccess field =
2498 if (!requiresNativeIsCheck) { 2110 new js.PropertyAccess.field(pop(), compiler.namer.operatorIs(element));
2499 if (negative) { 2111 if (backend.emitter.nativeEmitter.requiresNativeIsCheck(element)) {
2500 buffer.add('!'); 2112 push(new js.Call(field, <js.Expression>[]));
2501 } else { 2113 if (negative) push(new js.Prefix('!', pop()));
2502 buffer.add('!!'); 2114 } else {
2503 } 2115 // We always negate at least once so that the result is boolified.
2504 } else if (negative) { 2116 push(new js.Prefix('!', field));
2505 buffer.add('!'); 2117 // If the result is not negated, put another '!' in front.
2118 if (!negative) push(new js.Prefix('!', pop()));
2506 } 2119 }
2507 use(input, JSPrecedence.MEMBER_PRECEDENCE);
2508 buffer.add('.');
2509 buffer.add(compiler.namer.operatorIs(element));
2510 if (requiresNativeIsCheck) buffer.add('()');
2511 } 2120 }
2512 2121
2513 void handleStringSupertypeCheck(HInstruction input, Element element) { 2122 void handleStringSupertypeCheck(HInstruction input, Element element) {
2514 // Make sure List and String don't share supertypes, otherwise we 2123 // Make sure List and String don't share supertypes, otherwise we
2515 // would need to check for List too. 2124 // would need to check for List too.
2516 assert(element !== compiler.listClass 2125 assert(element !== compiler.listClass
2517 && !Elements.isListSupertype(element, compiler)); 2126 && !Elements.isListSupertype(element, compiler));
2518 withPrecedence(JSPrecedence.LOGICAL_OR_PRECEDENCE, () { 2127 checkString(input, '===');
2519 checkString(input, '==='); 2128 js.Expression stringTest = pop();
2520 buffer.add(' || '); 2129 checkObject(input, '===');
2521 withPrecedence(JSPrecedence.LOGICAL_AND_PRECEDENCE, () { 2130 js.Expression objectTest = pop();
2522 checkObject(input, '==='); 2131 checkType(input, element);
2523 buffer.add(' && '); 2132 push(new js.Binary('||',
2524 checkType(input, element); 2133 stringTest,
2525 }); 2134 new js.Binary('&&', objectTest, pop())));
2526 });
2527 } 2135 }
2528 2136
2529 void handleListOrSupertypeCheck(HInstruction input, Element element) { 2137 void handleListOrSupertypeCheck(HInstruction input, Element element) {
2530 // Make sure List and String don't share supertypes, otherwise we 2138 // Make sure List and String don't share supertypes, otherwise we
2531 // would need to check for String too. 2139 // would need to check for String too.
2532 assert(element !== compiler.stringClass 2140 assert(element !== compiler.stringClass
2533 && !Elements.isStringSupertype(element, compiler)); 2141 && !Elements.isStringSupertype(element, compiler));
2534 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
2535 checkObject(input, '==='); 2142 checkObject(input, '===');
2536 buffer.add(' && ('); 2143 js.Expression objectTest = pop();
2537 beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
2538 checkArray(input, '==='); 2144 checkArray(input, '===');
2539 buffer.add(' || '); 2145 js.Expression arrayTest = pop();
2540 checkType(input, element); 2146 checkType(input, element);
2541 buffer.add(')'); 2147 push(new js.Binary('&&',
2542 endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE); 2148 objectTest,
2543 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); 2149 new js.Binary('||', arrayTest, pop())));
2544 } 2150 }
2545 2151
2546 void visitIs(HIs node) { 2152 void visitIs(HIs node) {
2547 Type type = node.typeExpression; 2153 Type type = node.typeExpression;
2548 Element element = type.element; 2154 Element element = type.element;
2549 if (element.kind === ElementKind.TYPE_VARIABLE) { 2155 if (element.kind === ElementKind.TYPE_VARIABLE) {
2550 compiler.unimplemented("visitIs for type variables", instruction: node); 2156 compiler.unimplemented("visitIs for type variables", instruction: node);
2551 } else if (element.kind === ElementKind.TYPEDEF) { 2157 } else if (element.kind === ElementKind.TYPEDEF) {
2552 compiler.unimplemented("visitIs for typedefs", instruction: node); 2158 compiler.unimplemented("visitIs for typedefs", instruction: node);
2553 } 2159 }
2554 LibraryElement coreLibrary = compiler.coreLibrary; 2160 LibraryElement coreLibrary = compiler.coreLibrary;
2555 ClassElement objectClass = compiler.objectClass; 2161 ClassElement objectClass = compiler.objectClass;
2556 HInstruction input = node.expression; 2162 HInstruction input = node.expression;
2557 2163
2558 int oldPrecedence;
2559 if (node.nullOk) {
2560 oldPrecedence = expectedPrecedence;
2561 beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
2562 expectedPrecedence = JSPrecedence.LOGICAL_OR_PRECEDENCE;
2563 checkNull(input);
2564 buffer.add(' || ');
2565 }
2566 if (element === objectClass || element === compiler.dynamicClass) { 2164 if (element === objectClass || element === compiler.dynamicClass) {
2567 // The constant folder also does this optimization, but we make 2165 // The constant folder also does this optimization, but we make
2568 // it safe by assuming it may have not run. 2166 // it safe by assuming it may have not run.
2569 buffer.add('true'); 2167 push(new js.LiteralBool(true), node);
2570 } else if (element == compiler.stringClass) { 2168 } else if (element == compiler.stringClass) {
2571 checkString(input, '==='); 2169 checkString(input, '===');
2170 attachLocationToLast(node);
2572 } else if (element == compiler.doubleClass) { 2171 } else if (element == compiler.doubleClass) {
2573 checkDouble(input, '==='); 2172 checkDouble(input, '===');
2173 attachLocationToLast(node);
2574 } else if (element == compiler.numClass) { 2174 } else if (element == compiler.numClass) {
2575 checkNum(input, '==='); 2175 checkNum(input, '===');
2176 attachLocationToLast(node);
2576 } else if (element == compiler.boolClass) { 2177 } else if (element == compiler.boolClass) {
2577 checkBool(input, '==='); 2178 checkBool(input, '===');
2179 attachLocationToLast(node);
2578 } else if (element == compiler.functionClass) { 2180 } else if (element == compiler.functionClass) {
2579 checkFunction(input, element); 2181 checkFunction(input, element);
2182 attachLocationToLast(node);
2580 } else if (element == compiler.intClass) { 2183 } else if (element == compiler.intClass) {
2581 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
2582 checkNum(input, '==='); 2184 checkNum(input, '===');
2583 buffer.add(' && '); 2185 js.Expression numTest = pop();
2584 checkInt(input, '==='); 2186 checkInt(input, '===');
2585 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); 2187 push(new js.Binary('&&', numTest, pop()), node);
2586 } else if (Elements.isStringSupertype(element, compiler)) { 2188 } else if (Elements.isStringSupertype(element, compiler)) {
2587 handleStringSupertypeCheck(input, element); 2189 handleStringSupertypeCheck(input, element);
2190 attachLocationToLast(node);
2588 } else if (element === compiler.listClass 2191 } else if (element === compiler.listClass
2589 || Elements.isListSupertype(element, compiler)) { 2192 || Elements.isListSupertype(element, compiler)) {
2590 handleListOrSupertypeCheck(input, element); 2193 handleListOrSupertypeCheck(input, element);
2194 attachLocationToLast(node);
2591 } else if (input.propagatedType.canBePrimitive() 2195 } else if (input.propagatedType.canBePrimitive()
2592 || input.propagatedType.canBeNull()) { 2196 || input.propagatedType.canBeNull()) {
2593 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
2594 checkObject(input, '==='); 2197 checkObject(input, '===');
2595 buffer.add(' && '); 2198 js.Expression objectTest = pop();
2596 checkType(input, element); 2199 checkType(input, element);
2597 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); 2200 push(new js.Binary('&&', objectTest, pop()), node);
2598 } else { 2201 } else {
2599 checkType(input, element); 2202 checkType(input, element);
2203 attachLocationToLast(node);
2600 } 2204 }
2601 if (compiler.codegenWorld.rti.hasTypeArguments(type)) { 2205 if (compiler.codegenWorld.rti.hasTypeArguments(type)) {
2602 InterfaceType interfaceType = type; 2206 InterfaceType interfaceType = type;
2603 ClassElement cls = type.element; 2207 ClassElement cls = type.element;
2604 Link<Type> arguments = interfaceType.arguments; 2208 Link<Type> arguments = interfaceType.arguments;
2605 buffer.add(' && '); 2209 js.Expression result = pop();
2606 checkObject(node.typeInfoCall, '==='); 2210 checkObject(node.typeInfoCall, '===');
2211 result = new js.Binary('&&', result, pop());
2607 for (TypeVariableType typeVariable in cls.typeVariables) { 2212 for (TypeVariableType typeVariable in cls.typeVariables) {
2608 buffer.add(' && '); 2213 use(node.typeInfoCall);
2609 beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
2610 use(node.typeInfoCall, JSPrecedence.EQUALITY_PRECEDENCE);
2611 // TODO(johnniwinther): Retrieve the type name properly and not through 2214 // TODO(johnniwinther): Retrieve the type name properly and not through
2612 // [toString]. Note: Two cases below [typeVariable] and 2215 // [toString]. Note: Two cases below [typeVariable] and
2613 // [arguments.head]. 2216 // [arguments.head].
2614 buffer.add( 2217 js.PropertyAccess field =
2615 ".${typeVariable} === '${arguments.head}'"); 2218 new js.PropertyAccess.field(pop(), typeVariable.toString());
2616 endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); 2219 js.Expression genericName = new js.LiteralString("'${arguments.head}'");
2220 js.Binary eqTest = new js.Binary('===', field, genericName);
2221 result = new js.Binary('&&', result, eqTest);
2617 } 2222 }
2223 push(result, node);
2618 } 2224 }
2619 if (node.nullOk) { 2225 if (node.nullOk) {
2620 expectedPrecedence = oldPrecedence; 2226 checkNull(input);
2621 endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE); 2227 push(new js.Binary('||', pop(), pop()), node);
2622 } 2228 }
2623 } 2229 }
2624 2230
2625 void visitTypeConversion(HTypeConversion node) { 2231 void visitTypeConversion(HTypeConversion node) {
2626 Map<String, SourceString> castNames = const <SourceString> { 2232 Map<String, SourceString> castNames = const <SourceString> {
2627 "stringTypeCheck": 2233 "stringTypeCheck":
2628 const SourceString("stringTypeCast"), 2234 const SourceString("stringTypeCast"),
2629 "doubleTypeCheck": 2235 "doubleTypeCheck":
2630 const SourceString("doubleTypeCast"), 2236 const SourceString("doubleTypeCast"),
2631 "numTypeCheck": 2237 "numTypeCheck":
(...skipping 20 matching lines...) Expand all
2652 const SourceString("propertyTypeCast") 2258 const SourceString("propertyTypeCast")
2653 }; 2259 };
2654 2260
2655 if (node.isChecked) { 2261 if (node.isChecked) {
2656 Element element = node.type.computeType(compiler).element; 2262 Element element = node.type.computeType(compiler).element;
2657 world.registerIsCheck(element); 2263 world.registerIsCheck(element);
2658 SourceString helper; 2264 SourceString helper;
2659 String additionalArgument; 2265 String additionalArgument;
2660 bool nativeCheck = 2266 bool nativeCheck =
2661 backend.emitter.nativeEmitter.requiresNativeIsCheck(element); 2267 backend.emitter.nativeEmitter.requiresNativeIsCheck(element);
2662 beginExpression(JSPrecedence.CALL_PRECEDENCE);
2663 2268
2664 if (node.isArgumentTypeCheck) { 2269 if (node.isArgumentTypeCheck) {
2665 buffer.add('if (');
2666 if (element == compiler.intClass) { 2270 if (element == compiler.intClass) {
2667 checkInt(node.checkedInput, '!=='); 2271 checkInt(node.checkedInput, '!==');
2668 } else { 2272 } else {
2669 assert(element == compiler.numClass); 2273 assert(element == compiler.numClass);
2670 checkNum(node.checkedInput, '!=='); 2274 checkNum(node.checkedInput, '!==');
2671 } 2275 }
2672 buffer.add(') '); 2276 js.Expression test = pop();
2277 js.Block oldContainer = currentContainer;
2278 js.Statement body = new js.Block.empty();
2279 currentContainer = body;
2673 generateThrowWithHelper('iae', node.checkedInput); 2280 generateThrowWithHelper('iae', node.checkedInput);
2281 currentContainer = oldContainer;
2282 body = unwrapStatement(body);
2283 pushStatement(new js.If.then(test, body), node);
2674 return; 2284 return;
2675 } 2285 }
2676 2286
2677 assert(node.isCheckedModeCheck || node.isCastTypeCheck); 2287 assert(node.isCheckedModeCheck || node.isCastTypeCheck);
2678 if (element == compiler.stringClass) { 2288 if (element == compiler.stringClass) {
2679 helper = const SourceString('stringTypeCheck'); 2289 helper = const SourceString('stringTypeCheck');
2680 } else if (element == compiler.doubleClass) { 2290 } else if (element == compiler.doubleClass) {
2681 helper = const SourceString('doubleTypeCheck'); 2291 helper = const SourceString('doubleTypeCheck');
2682 } else if (element == compiler.numClass) { 2292 } else if (element == compiler.numClass) {
2683 helper = const SourceString('numTypeCheck'); 2293 helper = const SourceString('numTypeCheck');
(...skipping 24 matching lines...) Expand all
2708 helper = const SourceString('callTypeCheck'); 2318 helper = const SourceString('callTypeCheck');
2709 } else { 2319 } else {
2710 helper = const SourceString('propertyTypeCheck'); 2320 helper = const SourceString('propertyTypeCheck');
2711 } 2321 }
2712 } 2322 }
2713 if (node.isCastTypeCheck) { 2323 if (node.isCastTypeCheck) {
2714 helper = castNames[helper.stringValue]; 2324 helper = castNames[helper.stringValue];
2715 } 2325 }
2716 Element helperElement = compiler.findHelper(helper); 2326 Element helperElement = compiler.findHelper(helper);
2717 world.registerStaticUse(helperElement); 2327 world.registerStaticUse(helperElement);
2718 buffer.add(compiler.namer.isolateAccess(helperElement)); 2328 List<js.Expression> arguments = <js.Expression>[];
2719 buffer.add('('); 2329 use(node.checkedInput);
2720 use(node.checkedInput, JSPrecedence.EXPRESSION_PRECEDENCE); 2330 arguments.add(pop());
2721 if (additionalArgument !== null) buffer.add(", '$additionalArgument'"); 2331 if (additionalArgument !== null) {
2722 buffer.add(')'); 2332 arguments.add(new js.LiteralString("'$additionalArgument'"));
2723 endExpression(JSPrecedence.CALL_PRECEDENCE); 2333 }
2334 String helperName = compiler.namer.isolateAccess(helperElement);
2335 push(new js.Call(new js.VariableUse(helperName), arguments));
2724 } else { 2336 } else {
2725 use(node.checkedInput, expectedPrecedence); 2337 use(node.checkedInput);
2726 } 2338 }
2727 } 2339 }
2728 } 2340 }
2729 2341
2730 class SsaOptimizedCodeGenerator extends SsaCodeGenerator { 2342 class SsaOptimizedCodeGenerator extends SsaCodeGenerator {
2731 SsaOptimizedCodeGenerator(backend, work, parameters, parameterNames) 2343 SsaOptimizedCodeGenerator(backend, work, parameters, parameterNames)
2732 : super(backend, work, parameters, parameterNames) { 2344 : super(backend, work, parameterNames) {
2733 // Declare the parameter names only for the optimized version. The 2345 // Declare the parameter names only for the optimized version. The
2734 // unoptimized version has different parameters. 2346 // unoptimized version has different parameters.
2735 parameterNames.forEach((Element element, String name) { 2347 parameterNames.forEach((Element element, String name) {
2736 declaredVariables.add(name); 2348 declaredVariables.add(name);
2737 }); 2349 });
2738 } 2350 }
2739 2351
2740 int maxBailoutParameters; 2352 int maxBailoutParameters;
2741 2353
2742 HBasicBlock beginGraph(HGraph graph) => graph.entry; 2354 HBasicBlock beginGraph(HGraph graph) => graph.entry;
2743 void endGraph(HGraph graph) {} 2355 void endGraph(HGraph graph) {}
2744 2356
2745 void bailout(HTypeGuard guard, String reason) { 2357 js.Statement bailout(HTypeGuard guard, String reason) {
2746 if (maxBailoutParameters === null) { 2358 if (maxBailoutParameters === null) {
2747 maxBailoutParameters = 0; 2359 maxBailoutParameters = 0;
2748 work.guards.forEach((HTypeGuard workGuard) { 2360 work.guards.forEach((HTypeGuard workGuard) {
2749 HBailoutTarget target = workGuard.bailoutTarget; 2361 HBailoutTarget target = workGuard.bailoutTarget;
2750 int inputLength = target.inputs.length; 2362 int inputLength = target.inputs.length;
2751 if (inputLength > maxBailoutParameters) { 2363 if (inputLength > maxBailoutParameters) {
2752 maxBailoutParameters = inputLength; 2364 maxBailoutParameters = inputLength;
2753 } 2365 }
2754 }); 2366 });
2755 } 2367 }
2756 HInstruction input = guard.guarded; 2368 HInstruction input = guard.guarded;
2757 HBailoutTarget target = guard.bailoutTarget; 2369 HBailoutTarget target = guard.bailoutTarget;
2758 Namer namer = compiler.namer; 2370 Namer namer = compiler.namer;
2759 Element element = work.element; 2371 Element element = work.element;
2760 buffer.add('return '); 2372 List<js.Expression> arguments = <js.Expression>[];
2761 if (element.isInstanceMember()) { 2373 arguments.add(new js.LiteralNumber("${guard.state}"));
2762 // TODO(ngeoffray): This does not work in case we come from a
2763 // super call. We must make bailout names unique.
2764 buffer.add('this.${namer.getBailoutName(element)}');
2765 } else {
2766 buffer.add(namer.isolateBailoutAccess(element));
2767 }
2768 buffer.add('(${guard.state}');
2769 // TODO(ngeoffray): try to put a variable at a deterministic 2374 // TODO(ngeoffray): try to put a variable at a deterministic
2770 // location, so that multiple bailout calls put the variable at 2375 // location, so that multiple bailout calls put the variable at
2771 // the same parameter index. 2376 // the same parameter index.
2772 int i = 0; 2377 int i = 0;
2773 for (; i < target.inputs.length; i++) { 2378 for (; i < target.inputs.length; i++) {
2774 assert(guard.inputs.indexOf(target.inputs[i]) >= 0); 2379 assert(guard.inputs.indexOf(target.inputs[i]) >= 0);
2775 buffer.add(', '); 2380 use(target.inputs[i]);
2776 use(target.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); 2381 arguments.add(pop());
2777 } 2382 }
2778 // Make sure we call the bailout method with the number of 2383 // Make sure we call the bailout method with the number of
2779 // arguments it expects. This avoids having the underlying 2384 // arguments it expects. This avoids having the underlying
2780 // JS engine fill them in for us. 2385 // JS engine fill them in for us.
2781 for (; i < maxBailoutParameters; i++) { 2386 for (; i < maxBailoutParameters; i++) {
2782 buffer.add(', 0'); 2387 arguments.add(new js.LiteralNumber('0'));
2783 } 2388 }
2784 buffer.add(')'); 2389
2390 js.Expression bailoutTarget;
2391 if (element.isInstanceMember()) {
2392 // TODO(ngeoffray): This does not work in case we come from a
2393 // super call. We must make bailout names unique.
2394 String bailoutName = namer.getBailoutName(element);
2395 bailoutTarget = new js.PropertyAccess.field(new js.This(), bailoutName);
2396 } else {
2397 bailoutTarget = new js.VariableUse(namer.isolateBailoutAccess(element));
2398 }
2399 js.Call call = new js.Call(bailoutTarget, arguments);
2400 attachLocation(call, guard);
2401 return new js.Return(call);
2785 } 2402 }
2786 2403
2787 void visitTypeGuard(HTypeGuard node) { 2404 void visitTypeGuard(HTypeGuard node) {
2788 addIndentation();
2789 HInstruction input = node.guarded; 2405 HInstruction input = node.guarded;
2790 Element indexingBehavior = compiler.jsIndexingBehaviorInterface; 2406 Element indexingBehavior = compiler.jsIndexingBehaviorInterface;
2791 if (node.isInteger()) { 2407 if (node.isInteger()) {
2792 // if (input is !int) bailout 2408 // if (input is !int) bailout
2793 buffer.add('if (');
2794 checkInt(input, '!=='); 2409 checkInt(input, '!==');
2795 buffer.add(') '); 2410 pushStatement(new js.If.then(pop(), bailout(node, 'Not an integer')),
2796 bailout(node, 'Not an integer'); 2411 node);
2797 } else if (node.isNumber()) { 2412 } else if (node.isNumber()) {
2798 // if (input is !num) bailout 2413 // if (input is !num) bailout
2799 buffer.add('if (');
2800 checkNum(input, '!=='); 2414 checkNum(input, '!==');
2801 buffer.add(') '); 2415 pushStatement(new js.If.then(pop(), bailout(node, 'Not a number')), node);
2802 bailout(node, 'Not a number');
2803 } else if (node.isBoolean()) { 2416 } else if (node.isBoolean()) {
2804 // if (input is !bool) bailout 2417 // if (input is !bool) bailout
2805 buffer.add('if (');
2806 checkBool(input, '!=='); 2418 checkBool(input, '!==');
2807 buffer.add(') '); 2419 pushStatement(new js.If.then(pop(), bailout(node, 'Not a boolean')),
2808 bailout(node, 'Not a boolean'); 2420 node);
2809 } else if (node.isString()) { 2421 } else if (node.isString()) {
2810 // if (input is !string) bailout 2422 // if (input is !string) bailout
2811 buffer.add('if (');
2812 checkString(input, '!=='); 2423 checkString(input, '!==');
2813 buffer.add(') '); 2424 pushStatement(new js.If.then(pop(), bailout(node, 'Not a string')), node);
2814 bailout(node, 'Not a string');
2815 } else if (node.isExtendableArray()) { 2425 } else if (node.isExtendableArray()) {
2816 // if (input is !Object || input is !Array || input.isFixed) bailout 2426 // if (input is !Object || input is !Array || input.isFixed) bailout
2817 buffer.add('if (');
2818 checkObject(input, '!=='); 2427 checkObject(input, '!==');
2819 buffer.add('||'); 2428 js.Expression objectTest = pop();
2820 checkArray(input, '!=='); 2429 checkArray(input, '!==');
2821 buffer.add('||'); 2430 js.Expression arrayTest = pop();
2822 checkFixedArray(input); 2431 checkFixedArray(input);
2823 buffer.add(') '); 2432 js.Expression test = new js.Binary('||', objectTest, arrayTest);
2824 bailout(node, 'Not an extendable array'); 2433 test = new js.Binary('||', test, pop());
2434 pushStatement(new js.If.then(test,
2435 bailout(node, 'Not an extendable array')),
2436 node);
2825 } else if (node.isMutableArray()) { 2437 } else if (node.isMutableArray()) {
2826 // if (input is !Object 2438 // if (input is !Object
2827 // || ((input is !Array || input.isImmutable) 2439 // || ((input is !Array || input.isImmutable)
2828 // && input is !JsIndexingBehavior)) bailout 2440 // && input is !JsIndexingBehavior)) bailout
2829 buffer.add('if (');
2830 checkObject(input, '!=='); 2441 checkObject(input, '!==');
2831 buffer.add(' || (('); 2442 js.Expression objectTest = pop();
2832 checkArray(input, '!=='); 2443 checkArray(input, '!==');
2833 buffer.add(' || '); 2444 js.Expression arrayTest = pop();
2834 checkImmutableArray(input); 2445 checkImmutableArray(input);
2835 buffer.add(') && '); 2446 js.Binary notArrayOrImmutable = new js.Binary('||', arrayTest, pop());
2836 checkType(input, indexingBehavior, negative: true); 2447 checkType(input, indexingBehavior, negative: true);
2837 buffer.add(')) '); 2448 js.Binary notIndexing = new js.Binary('&&', notArrayOrImmutable, pop());
2838 bailout(node, 'Not a mutable array'); 2449 pushStatement(new js.If.then(new js.Binary('||', objectTest, notIndexing),
2450 bailout(node, 'Not a mutable array')),
2451 node);
2839 } else if (node.isReadableArray()) { 2452 } else if (node.isReadableArray()) {
2840 // if (input is !Object 2453 // if (input is !Object
2841 // || (input is !Array && input is !JsIndexingBehavior)) bailout 2454 // || (input is !Array && input is !JsIndexingBehavior)) bailout
2842 buffer.add('if (');
2843 checkObject(input, '!=='); 2455 checkObject(input, '!==');
2844 buffer.add(' || ('); 2456 js.Expression objectTest = pop();
2845 checkArray(input, '!=='); 2457 checkArray(input, '!==');
2846 buffer.add(' && '); 2458 js.Expression arrayTest = pop();
2847 checkType(input, indexingBehavior, negative: true); 2459 checkType(input, indexingBehavior, negative: true);
2848 buffer.add(')) '); 2460 js.Expression notIndexing = new js.Binary('&&', arrayTest, pop());
2849 bailout(node, 'Not an array'); 2461 pushStatement(new js.If.then(new js.Binary('||', objectTest, notIndexing),
2462 bailout(node, 'Not an array')),
2463 node);
2850 } else if (node.isIndexablePrimitive()) { 2464 } else if (node.isIndexablePrimitive()) {
2851 // if (input is !String 2465 // if (input is !String
2852 // && (input is !Object 2466 // && (input is !Object
2853 // || (input is !Array && input is !JsIndexingBehavior))) bailout 2467 // || (input is !Array && input is !JsIndexingBehavior))) bailout
2854 buffer.add('if (');
2855 checkString(input, '!=='); 2468 checkString(input, '!==');
2856 buffer.add(' && ('); 2469 js.Expression stringTest = pop();
2857 checkObject(input, '!=='); 2470 checkObject(input, '!==');
2858 buffer.add(' || ('); 2471 js.Expression objectTest = pop();
2859 checkArray(input, '!=='); 2472 checkArray(input, '!==');
2860 buffer.add(' && '); 2473 js.Expression arrayTest = pop();
2861 checkType(input, indexingBehavior, negative: true); 2474 checkType(input, indexingBehavior, negative: true);
2862 buffer.add('))) '); 2475 js.Binary notIndexingTest = new js.Binary('&&', arrayTest, pop());
2863 bailout(node, 'Not a string or array'); 2476 js.Binary notObjectOrIndexingTest =
2477 new js.Binary('||', objectTest, notIndexingTest);
2478 js.Binary condition =
2479 new js.Binary('&&', stringTest, notObjectOrIndexingTest);
2480 pushStatement(new js.If.then(condition,
2481 bailout(node, 'Not a string or array')),
2482 node);
2864 } else { 2483 } else {
2865 compiler.internalError('Unexpected type guard', instruction: input); 2484 compiler.internalError('Unexpected type guard', instruction: input);
2866 } 2485 }
2867 buffer.add(';\n');
2868 } 2486 }
2869 2487
2870 void visitBailoutTarget(HBailoutTarget target) { 2488 void visitBailoutTarget(HBailoutTarget target) {
2871 // Do nothing. Bailout targets are only used in the non-optimized version. 2489 // Do nothing. Bailout targets are only used in the non-optimized version.
2872 } 2490 }
2873 2491
2874 void beginLoop(HBasicBlock block) { 2492 void beginLoop(HBasicBlock block) {
2875 addIndentation(); 2493 oldContainerStack.add(currentContainer);
2876 HLoopInformation info = block.loopInformation; 2494 currentContainer = new js.Block.empty();
2877 for (LabelElement label in info.labels) {
2878 writeLabel(label);
2879 buffer.add(":");
2880 }
2881 buffer.add('while (true) {\n');
2882 indent++;
2883 } 2495 }
2884 2496
2885 void endLoop(HBasicBlock block) { 2497 void endLoop(HBasicBlock block) {
2886 indent--; 2498 js.Statement body = currentContainer;
2887 addIndented('}\n'); // Close 'while' loop. 2499 currentContainer = oldContainerStack.removeLast();
2500 body = unwrapStatement(body);
2501 js.While loop = new js.While(new js.LiteralBool(true), body);
2502
2503 HLoopInformation info = block.loopInformation;
2504 attachLocationRange(loop, info.loopBlockInformation.sourcePosition);
2505 pushStatement(wrapIntoLabels(loop, info.labels));
2888 } 2506 }
2889 2507
2890 void handleLoopCondition(HLoopBranch node) { 2508 void handleLoopCondition(HLoopBranch node) {
2891 buffer.add('if (!'); 2509 use(node.inputs[0]);
2892 use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE); 2510 pushStatement(new js.If.then(pop(), new js.Break(null)), node);
2893 buffer.add(') break;\n');
2894 } 2511 }
2895 2512
2896 2513
2897 void preLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { 2514 void preLabeledBlock(HLabeledBlockInformation labeledBlockInfo) {
2898 } 2515 }
2899 2516
2900 void startLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { 2517 void startLabeledBlock(HLabeledBlockInformation labeledBlockInfo) {
2901 } 2518 }
2902 2519
2903 void endLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { 2520 void endLabeledBlock(HLabeledBlockInformation labeledBlockInfo) {
2904 } 2521 }
2905 } 2522 }
2906 2523
2907 class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator { 2524 class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator {
2908 2525
2909 final CodeBuffer setup; 2526 js.Statement setup;
2910 final CodeBuffer newParameters; 2527 js.Switch currentBailoutSwitch;
2528 final List<js.Switch> oldBailoutSwitches;
2529 final List<js.Parameter> newParameters;
2911 final List<String> labels; 2530 final List<String> labels;
2912 int labelId = 0; 2531 int labelId = 0;
2913 /** 2532 /**
2914 * Keeps track if a bailout switch already used its [:default::] clause. New 2533 * Keeps track if a bailout switch already used its [:default::] clause. New
2915 * bailout-switches just push [:false:] on the stack and replace it when 2534 * bailout-switches just push [:false:] on the stack and replace it when
2916 * they used the [:default::] clause. 2535 * they used the [:default::] clause.
2917 */ 2536 */
2918 final List<bool> defaultClauseUsedInBailoutStack; 2537 final List<bool> defaultClauseUsedInBailoutStack;
2919 2538
2920 SsaBailoutPropagator propagator; 2539 SsaBailoutPropagator propagator;
2921 HInstruction savedFirstInstruction; 2540 HInstruction savedFirstInstruction;
2922 2541
2923 SsaUnoptimizedCodeGenerator(backend, work, parameters, parameterNames) 2542 SsaUnoptimizedCodeGenerator(backend, work, parameters, parameterNames)
2924 : super(backend, work, parameters, parameterNames), 2543 : super(backend, work, parameterNames),
2925 setup = new CodeBuffer(), 2544 setup = new js.EmptyStatement(),
2926 newParameters = new CodeBuffer(), 2545 oldBailoutSwitches = <js.Switch>[],
2546 newParameters = <js.Parameter>[],
2927 labels = <String>[], 2547 labels = <String>[],
2928 defaultClauseUsedInBailoutStack = <bool>[]; 2548 defaultClauseUsedInBailoutStack = <bool>[];
2929 2549
2930 String pushLabel() { 2550 String pushLabel() {
2931 String label = 'L${labelId++}'; 2551 String label = 'L${labelId++}';
2932 labels.addLast(label); 2552 labels.addLast(label);
2933 return label; 2553 return label;
2934 } 2554 }
2935 2555
2936 String popLabel() { 2556 String popLabel() {
2937 return labels.removeLast(); 2557 return labels.removeLast();
2938 } 2558 }
2939 2559
2940 String currentLabel() { 2560 String currentLabel() {
2941 return labels.last(); 2561 return labels.last();
2942 } 2562 }
2943 2563
2944 HBasicBlock beginGraph(HGraph graph) { 2564 HBasicBlock beginGraph(HGraph graph) {
2945 propagator = new SsaBailoutPropagator(compiler, generateAtUseSite); 2565 propagator = new SsaBailoutPropagator(compiler, generateAtUseSite);
2946 propagator.visitGraph(graph); 2566 propagator.visitGraph(graph);
2947 // TODO(ngeoffray): We could avoid generating the state at the 2567 // TODO(ngeoffray): We could avoid generating the state at the
2948 // call site for non-complex bailout methods. 2568 // call site for non-complex bailout methods.
2949 newParameters.add('state'); 2569 newParameters.add(new js.Parameter('state'));
2950 2570
2951 if (propagator.hasComplexBailoutTargets) { 2571 if (propagator.hasComplexBailoutTargets) {
2952 // Use generic parameters that will be assigned to 2572 // Use generic parameters that will be assigned to
2953 // the right variables in the setup phase. 2573 // the right variables in the setup phase.
2954 for (int i = 0; i < propagator.maxBailoutParameters; i++) { 2574 for (int i = 0; i < propagator.maxBailoutParameters; i++) {
2955 String name = 'env$i'; 2575 String name = 'env$i';
2956 declaredVariables.add(name); 2576 declaredVariables.add(name);
2957 newParameters.add(', $name'); 2577 newParameters.add(new js.Parameter(name));
2958 } 2578 }
2959 2579
2960 startBailoutSwitch(); 2580 startBailoutSwitch();
2961 2581
2962 // The setup phase of a bailout function sets up the environment for 2582 // The setup phase of a bailout function sets up the environment for
2963 // each bailout target. Each bailout target will populate this 2583 // each bailout target. Each bailout target will populate this
2964 // setup phase. It is put at the beginning of the function. 2584 // setup phase. It is put at the beginning of the function.
2965 setup.add(' switch (state) {\n'); 2585 setup = new js.Switch(new js.VariableUse('state'), <js.SwitchClause>[]);
2966 return graph.entry; 2586 return graph.entry;
2967 } else { 2587 } else {
2968 // We have a simple bailout target, so we can reuse the names that 2588 // We have a simple bailout target, so we can reuse the names that
2969 // the bailout target expects. 2589 // the bailout target expects.
2970 for (HInstruction input in propagator.firstBailoutTarget.inputs) { 2590 for (HInstruction input in propagator.firstBailoutTarget.inputs) {
2971 input = unwrap(input); 2591 input = unwrap(input);
2972 String name = variableNames.getName(input); 2592 String name = variableNames.getName(input);
2973 declaredVariables.add(name); 2593 declaredVariables.add(name);
2974 newParameters.add(', $name'); 2594 newParameters.add(new js.Parameter(name));
2975 } 2595 }
2976 2596
2977 // We change the first instruction of the first guard to be the 2597 // We change the first instruction of the first guard to be the
2978 // bailout target. We will change it back in the call to [endGraph]. 2598 // bailout target. We will change it back in the call to [endGraph].
2979 HBasicBlock block = propagator.firstBailoutTarget.block; 2599 HBasicBlock block = propagator.firstBailoutTarget.block;
2980 savedFirstInstruction = block.first; 2600 savedFirstInstruction = block.first;
2981 block.first = propagator.firstBailoutTarget; 2601 block.first = propagator.firstBailoutTarget;
2982 return block; 2602 return block;
2983 } 2603 }
2984 } 2604 }
2985 2605
2986 // If argument is a [HCheck] and it does not have a name, we try to 2606 // If argument is a [HCheck] and it does not have a name, we try to
2987 // find the name of its checked input. Note that there must be a 2607 // find the name of its checked input. Note that there must be a
2988 // name, otherwise the instruction would not be in the live 2608 // name, otherwise the instruction would not be in the live
2989 // environment. 2609 // environment.
2990 HInstruction unwrap(HInstruction argument) { 2610 HInstruction unwrap(HInstruction argument) {
2991 while (argument is HCheck && !variableNames.hasName(argument)) { 2611 while (argument is HCheck && !variableNames.hasName(argument)) {
2992 argument = argument.checkedInput; 2612 argument = argument.checkedInput;
2993 } 2613 }
2994 assert(variableNames.hasName(argument)); 2614 assert(variableNames.hasName(argument));
2995 return argument; 2615 return argument;
2996 } 2616 }
2997 2617
2998 void endGraph(HGraph graph) { 2618 void endGraph(HGraph graph) {
2999 if (propagator.hasComplexBailoutTargets) { 2619 if (propagator.hasComplexBailoutTargets) {
3000 indent--; // Close original case. 2620 endBailoutSwitch();
3001 indent--;
3002 addIndented('}\n'); // Close 'switch'.
3003 setup.add(' }\n');
3004 } else { 2621 } else {
3005 // Put back the original first instruction of the block. 2622 // Put back the original first instruction of the block.
3006 propagator.firstBailoutTarget.block.first = savedFirstInstruction; 2623 propagator.firstBailoutTarget.block.first = savedFirstInstruction;
3007 } 2624 }
3008 } 2625 }
3009 2626
3010 bool visitAndOrInfo(HAndOrBlockInformation info) => false; 2627 bool visitAndOrInfo(HAndOrBlockInformation info) => false;
3011 2628
3012 bool visitIfInfo(HIfBlockInformation info) { 2629 bool visitIfInfo(HIfBlockInformation info) {
3013 if (info.thenGraph.start.hasBailoutTargets()) return false; 2630 if (info.thenGraph.start.hasBailoutTargets()) return false;
(...skipping 10 matching lines...) Expand all
3024 bool visitTryInfo(HTryBlockInformation info) => false; 2641 bool visitTryInfo(HTryBlockInformation info) => false;
3025 bool visitSequenceInfo(HStatementSequenceInformation info) => false; 2642 bool visitSequenceInfo(HStatementSequenceInformation info) => false;
3026 2643
3027 void visitTypeGuard(HTypeGuard node) { 2644 void visitTypeGuard(HTypeGuard node) {
3028 // Do nothing. Type guards are only used in the optimized version. 2645 // Do nothing. Type guards are only used in the optimized version.
3029 } 2646 }
3030 2647
3031 void visitBailoutTarget(HBailoutTarget node) { 2648 void visitBailoutTarget(HBailoutTarget node) {
3032 if (!propagator.hasComplexBailoutTargets) return; 2649 if (!propagator.hasComplexBailoutTargets) return;
3033 2650
3034 indent--; 2651 js.Block nextBlock = new js.Block.empty();
3035 addIndented('case ${node.state}:\n'); 2652 js.Case clause = new js.Case(new js.LiteralNumber('${node.state}'),
3036 indent++; 2653 nextBlock);
3037 addIndented('state = 0;\n'); 2654 currentBailoutSwitch.cases.add(clause);
3038 2655 currentContainer = nextBlock;
3039 setup.add(' case ${node.state}:\n'); 2656 pushExpressionAsStatement(new js.Assignment(new js.VariableUse('state'),
2657 new js.LiteralNumber('0')));
2658 js.Block setupBlock = new js.Block.empty();
3040 int i = 0; 2659 int i = 0;
3041 for (HInstruction input in node.inputs) { 2660 for (HInstruction input in node.inputs) {
3042 input = unwrap(input); 2661 input = unwrap(input);
3043 String name = variableNames.getName(input); 2662 String name = variableNames.getName(input);
3044 setup.add(' ');
3045 if (!isVariableDeclared(name)) { 2663 if (!isVariableDeclared(name)) {
3046 declaredVariables.add(name); 2664 declaredVariables.add(name);
3047 setup.add('var '); 2665 js.VariableInitialization init =
2666 new js.VariableInitialization(new js.VariableDeclaration(name),
2667 new js.VariableUse('env$i'));
2668 js.Expression varList =
2669 new js.VariableDeclarationList(<js.VariableInitialization>[init]);
2670 setupBlock.statements.add(new js.ExpressionStatement(varList));
2671 } else {
2672 js.Expression target = new js.VariableUse(name);
2673 js.Expression source = new js.VariableUse('env$i');
2674 js.Expression assignment = new js.Assignment(target, source);
2675 setupBlock.statements.add(new js.ExpressionStatement(assignment));
3048 } 2676 }
3049 setup.add('$name = env$i;\n');
3050 i++; 2677 i++;
3051 } 2678 }
3052 setup.add(' break;\n'); 2679 setupBlock.statements.add(new js.Break(null));
2680 js.Case setupClause =
2681 new js.Case(new js.LiteralNumber('${node.state}'), setupBlock);
2682 (setup as js.Switch).cases.add(setupClause);
3053 } 2683 }
3054 2684
3055 void startBailoutCase(List<HBailoutTarget> bailouts1, 2685 void startBailoutCase(List<HBailoutTarget> bailouts1,
3056 List<HBailoutTarget> bailouts2) { 2686 [List<HBailoutTarget> bailouts2 = const []]) {
3057 indent--;
3058 if (!defaultClauseUsedInBailoutStack.last() && 2687 if (!defaultClauseUsedInBailoutStack.last() &&
3059 bailouts1.length + bailouts2.length >= 2) { 2688 bailouts1.length + bailouts2.length >= 2) {
3060 addIndented('default:\n'); 2689 currentContainer = new js.Block.empty();
2690 currentBailoutSwitch.cases.add(new js.Default(currentContainer));
3061 int len = defaultClauseUsedInBailoutStack.length; 2691 int len = defaultClauseUsedInBailoutStack.length;
3062 defaultClauseUsedInBailoutStack[len - 1] = true; 2692 defaultClauseUsedInBailoutStack[len - 1] = true;
3063 } else { 2693 } else {
3064 handleBailoutCase(bailouts1); 2694 _handleBailoutCase(bailouts1);
3065 handleBailoutCase(bailouts2); 2695 _handleBailoutCase(bailouts2);
2696 currentContainer = currentBailoutSwitch.cases.last().body;
3066 } 2697 }
3067 indent++;
3068 } 2698 }
3069 2699
3070 void handleBailoutCase(List<HBailoutTarget> targets) { 2700 void _handleBailoutCase(List<HBailoutTarget> targets) {
3071 if (!defaultClauseUsedInBailoutStack.last() && targets.length >= 2) { 2701 for (int i = 0, len = targets.length; i < len; i++) {
3072 addIndented('default:\n'); 2702 js.LiteralNumber expr = new js.LiteralNumber('${targets[i].state}');
3073 int len = defaultClauseUsedInBailoutStack.length; 2703 currentBailoutSwitch.cases.add(new js.Case(expr, new js.Block.empty()));
3074 defaultClauseUsedInBailoutStack[len - 1] = true;
3075 } else {
3076 for (int i = 0, len = targets.length; i < len; i++) {
3077 addIndented('case ${targets[i].state}:\n');
3078 }
3079 } 2704 }
3080 } 2705 }
3081 2706
3082 void startBailoutSwitch() { 2707 void startBailoutSwitch() {
3083 defaultClauseUsedInBailoutStack.add(false); 2708 defaultClauseUsedInBailoutStack.add(false);
3084 addIndented('switch (state) {\n'); 2709 oldBailoutSwitches.add(currentBailoutSwitch);
3085 indent++; 2710 List<js.SwitchClause> cases = <js.SwitchClause>[];
3086 addIndented('case 0:\n'); 2711 js.Block firstBlock = new js.Block.empty();
3087 indent++; 2712 cases.add(new js.Case(new js.LiteralNumber("0"), firstBlock));
2713 currentBailoutSwitch = new js.Switch(new js.VariableUse('state'), cases);
2714 pushStatement(currentBailoutSwitch);
2715 oldContainerStack.add(currentContainer);
2716 currentContainer = firstBlock;
3088 } 2717 }
3089 2718
3090 void endBailoutSwitch() { 2719 js.Switch endBailoutSwitch() {
3091 indent--; // Close 'case'. 2720 js.Switch result = currentBailoutSwitch;
3092 indent--; 2721 currentBailoutSwitch = oldBailoutSwitches.removeLast();
3093 addIndented('}\n'); // Close 'switch'.
3094 defaultClauseUsedInBailoutStack.removeLast(); 2722 defaultClauseUsedInBailoutStack.removeLast();
2723 currentContainer = oldContainerStack.removeLast();
2724 return result;
3095 } 2725 }
3096 2726
3097 void beginLoop(HBasicBlock block) { 2727 void beginLoop(HBasicBlock block) {
3098 String newLabel = pushLabel(); 2728 String loopLabel = pushLabel();
3099 if (block.hasBailoutTargets()) { 2729 if (block.hasBailoutTargets()) {
3100 startBailoutCase(block.bailoutTargets, const <HBailoutTarget>[]); 2730 startBailoutCase(block.bailoutTargets);
3101 } 2731 }
3102 2732 oldContainerStack.add(currentContainer);
3103 addIndentation(); 2733 currentContainer = new js.Block.empty();
3104 HLoopInformation loopInformation = block.loopInformation;
3105 for (LabelElement label in loopInformation.labels) {
3106 writeLabel(label);
3107 buffer.add(":");
3108 }
3109 buffer.add('$newLabel: while (true) {\n');
3110 indent++;
3111
3112 if (block.hasBailoutTargets()) { 2734 if (block.hasBailoutTargets()) {
3113 startBailoutSwitch(); 2735 startBailoutSwitch();
2736 HLoopInformation loopInformation = block.loopInformation;
3114 if (loopInformation.target !== null) { 2737 if (loopInformation.target !== null) {
3115 breakAction[loopInformation.target] = (TargetElement target) { 2738 breakAction[loopInformation.target] = (TargetElement target) {
3116 addIndented("break $newLabel;\n"); 2739 pushStatement(new js.Break(loopLabel));
3117 }; 2740 };
3118 } 2741 }
3119 } 2742 }
3120 } 2743 }
3121 2744
3122 void endLoop(HBasicBlock block) { 2745 void endLoop(HBasicBlock block) {
3123 popLabel(); 2746 String loopLabel = popLabel();
2747
3124 HBasicBlock header = block.isLoopHeader() ? block : block.parentLoopHeader; 2748 HBasicBlock header = block.isLoopHeader() ? block : block.parentLoopHeader;
2749 HLoopInformation info = header.loopInformation;
3125 if (header.hasBailoutTargets()) { 2750 if (header.hasBailoutTargets()) {
3126 endBailoutSwitch(); 2751 endBailoutSwitch();
3127 HLoopInformation info = header.loopInformation;
3128 if (info.target != null) breakAction.remove(info.target); 2752 if (info.target != null) breakAction.remove(info.target);
3129 } 2753 }
3130 indent--; 2754
3131 addIndented('}\n'); // Close 'while'. 2755 js.Statement body = unwrapStatement(currentContainer);
2756 currentContainer = oldContainerStack.removeLast();
2757
2758 js.Statement result = new js.While(new js.LiteralBool(true), body);
2759 attachLocationRange(result, info.loopBlockInformation.sourcePosition);
2760 result = new js.LabeledStatement(loopLabel, result);
2761 result = wrapIntoLabels(result, info.labels);
2762 pushStatement(result);
3132 } 2763 }
3133 2764
3134 void handleLoopCondition(HLoopBranch node) { 2765 void handleLoopCondition(HLoopBranch node) {
3135 buffer.add('if (!'); 2766 use(node.inputs[0]);
3136 use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE); 2767 pushStatement(new js.If.then(new js.Prefix('!', pop()),
3137 buffer.add(') break ${currentLabel()};\n'); 2768 new js.Break(currentLabel())),
2769 node);
3138 } 2770 }
3139 2771
3140 void generateIf(HIf node, HIfBlockInformation info) { 2772 void generateIf(HIf node, HIfBlockInformation info) {
3141 HStatementInformation thenGraph = info.thenGraph; 2773 HStatementInformation thenGraph = info.thenGraph;
3142 HStatementInformation elseGraph = info.elseGraph; 2774 HStatementInformation elseGraph = info.elseGraph;
3143 bool thenHasGuards = thenGraph.start.hasBailoutTargets(); 2775 bool thenHasGuards = thenGraph.start.hasBailoutTargets();
3144 bool elseHasGuards = elseGraph.start.hasBailoutTargets(); 2776 bool elseHasGuards = elseGraph.start.hasBailoutTargets();
3145 bool hasGuards = thenHasGuards || elseHasGuards; 2777 bool hasGuards = thenHasGuards || elseHasGuards;
3146 if (!hasGuards) return super.generateIf(node, info); 2778 if (!hasGuards) {
3147 2779 super.generateIf(node, info);
3148 int elseKind = analyzeGraphForCodegen(elseGraph); 2780 return;
3149 bool emptyElse = elseKind == SsaCodeGenerator.EMPTY; 2781 }
3150 2782
3151 startBailoutCase(thenGraph.start.bailoutTargets, 2783 startBailoutCase(thenGraph.start.bailoutTargets,
3152 emptyElse ? const <HBailoutTarget>[] : elseGraph.start.bailoutTargets); 2784 elseGraph.start.bailoutTargets);
3153 2785
3154 addIndented('if ('); 2786 use(node.inputs[0]);
3155 int precedence = JSPrecedence.EXPRESSION_PRECEDENCE; 2787 js.Binary stateEquals0 =
2788 new js.Binary('===',
2789 new js.VariableUse('state'), new js.LiteralNumber('0'));
2790 js.Expression condition = new js.Binary('&&', stateEquals0, pop());
3156 // TODO(ngeoffray): Put the condition initialization in the 2791 // TODO(ngeoffray): Put the condition initialization in the
3157 // [setup] buffer. 2792 // [setup] buffer.
3158 List<HBailoutTarget> targets = node.thenBlock.bailoutTargets; 2793 List<HBailoutTarget> targets = node.thenBlock.bailoutTargets;
3159 for (int i = 0, len = targets.length; i < len; i++) { 2794 for (int i = 0, len = targets.length; i < len; i++) {
3160 buffer.add('state == ${targets[i].state} || '); 2795 js.VariableUse stateRef = new js.VariableUse('state');
2796 js.Expression targetState = new js.LiteralNumber('${targets[i].state}');
2797 js.Binary stateTest = new js.Binary('===', stateRef, targetState);
2798 condition = new js.Binary('||', stateTest, condition);
3161 } 2799 }
3162 buffer.add('(state == 0 && ');
3163 precedence = JSPrecedence.BITWISE_OR_PRECEDENCE;
3164 use(node.inputs[0], precedence);
3165 2800
3166 buffer.add(')) {\n'); 2801 js.Statement thenBody = new js.Block.empty();
3167 2802 js.Block oldContainer = currentContainer;
3168 indent++; 2803 currentContainer = thenBody;
3169 if (thenHasGuards) startBailoutSwitch(); 2804 if (thenHasGuards) startBailoutSwitch();
3170 generateStatements(thenGraph); 2805 generateStatements(thenGraph);
3171 if (thenHasGuards) endBailoutSwitch(); 2806 if (thenHasGuards) endBailoutSwitch();
3172 indent--; 2807 thenBody = unwrapStatement(thenBody);
3173 2808
3174 if (!emptyElse) { 2809 js.Statement elseBody = null;
3175 addIndented('} else {\n'); 2810 elseBody = new js.Block.empty();
3176 indent++; 2811 currentContainer = elseBody;
3177 if (elseHasGuards) startBailoutSwitch(); 2812 if (elseHasGuards) startBailoutSwitch();
3178 generateStatements(elseGraph); 2813 generateStatements(elseGraph);
3179 if (elseHasGuards) endBailoutSwitch(); 2814 if (elseHasGuards) endBailoutSwitch();
3180 indent--; 2815 elseBody = unwrapStatement(elseBody);
3181 }
3182 2816
3183 addIndented('}\n'); 2817 currentContainer = oldContainer;
2818 pushStatement(new js.If(condition, thenBody, elseBody), node);
3184 } 2819 }
3185 2820
3186 void preLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { 2821 void preLabeledBlock(HLabeledBlockInformation labeledBlockInfo) {
3187 if (labeledBlockInfo.body.start.hasBailoutTargets()) { 2822 if (labeledBlockInfo.body.start.hasBailoutTargets()) {
3188 indent--; 2823 indent--;
3189 handleBailoutCase(labeledBlockInfo.body.start.bailoutTargets); 2824 startBailoutCase(labeledBlockInfo.body.start.bailoutTargets);
3190 indent++; 2825 indent++;
3191 } 2826 }
3192 } 2827 }
3193 2828
3194 void startLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { 2829 void startLabeledBlock(HLabeledBlockInformation labeledBlockInfo) {
3195 if (labeledBlockInfo.body.start.hasBailoutTargets()) { 2830 if (labeledBlockInfo.body.start.hasBailoutTargets()) {
3196 startBailoutSwitch(); 2831 startBailoutSwitch();
3197 } 2832 }
3198 } 2833 }
3199 2834
(...skipping 12 matching lines...) Expand all
3212 if (leftType.canBeNull() && rightType.canBeNull()) { 2847 if (leftType.canBeNull() && rightType.canBeNull()) {
3213 if (left.isConstantNull() || right.isConstantNull() || 2848 if (left.isConstantNull() || right.isConstantNull() ||
3214 (leftType.isPrimitive() && leftType == rightType)) { 2849 (leftType.isPrimitive() && leftType == rightType)) {
3215 return '=='; 2850 return '==';
3216 } 2851 }
3217 return null; 2852 return null;
3218 } else { 2853 } else {
3219 return '==='; 2854 return '===';
3220 } 2855 }
3221 } 2856 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698