Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |