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

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

Powered by Google App Engine
This is Rietveld 408576698