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