| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 class Interceptors { | |
| 6 Compiler compiler; | |
| 7 Interceptors(Compiler this.compiler); | |
| 8 | |
| 9 SourceString mapOperatorToMethodName(Operator op) { | |
| 10 String name = op.source.stringValue; | |
| 11 if (name === '+') return const SourceString('add'); | |
| 12 if (name === '-') return const SourceString('sub'); | |
| 13 if (name === '*') return const SourceString('mul'); | |
| 14 if (name === '/') return const SourceString('div'); | |
| 15 if (name === '~/') return const SourceString('tdiv'); | |
| 16 if (name === '%') return const SourceString('mod'); | |
| 17 if (name === '<<') return const SourceString('shl'); | |
| 18 if (name === '>>') return const SourceString('shr'); | |
| 19 if (name === '|') return const SourceString('or'); | |
| 20 if (name === '&') return const SourceString('and'); | |
| 21 if (name === '^') return const SourceString('xor'); | |
| 22 if (name === '<') return const SourceString('lt'); | |
| 23 if (name === '<=') return const SourceString('le'); | |
| 24 if (name === '>') return const SourceString('gt'); | |
| 25 if (name === '>=') return const SourceString('ge'); | |
| 26 if (name === '==') return const SourceString('eq'); | |
| 27 if (name === '!=') return const SourceString('eq'); | |
| 28 if (name === '===') return const SourceString('eqq'); | |
| 29 if (name === '!==') return const SourceString('eqq'); | |
| 30 if (name === '+=') return const SourceString('add'); | |
| 31 if (name === '-=') return const SourceString('sub'); | |
| 32 if (name === '*=') return const SourceString('mul'); | |
| 33 if (name === '/=') return const SourceString('div'); | |
| 34 if (name === '~/=') return const SourceString('tdiv'); | |
| 35 if (name === '%=') return const SourceString('mod'); | |
| 36 if (name === '<<=') return const SourceString('shl'); | |
| 37 if (name === '>>=') return const SourceString('shr'); | |
| 38 if (name === '|=') return const SourceString('or'); | |
| 39 if (name === '&=') return const SourceString('and'); | |
| 40 if (name === '^=') return const SourceString('xor'); | |
| 41 if (name === '++') return const SourceString('add'); | |
| 42 if (name === '--') return const SourceString('sub'); | |
| 43 compiler.unimplemented('Unknown operator', node: op); | |
| 44 } | |
| 45 | |
| 46 Element getStaticInterceptor(SourceString name, int parameters) { | |
| 47 String mangledName = "builtin\$${name.slowToString()}\$${parameters}"; | |
| 48 Element result = compiler.findHelper(new SourceString(mangledName)); | |
| 49 return result; | |
| 50 } | |
| 51 | |
| 52 Element getStaticGetInterceptor(SourceString name) { | |
| 53 String mangledName = "builtin\$get\$${name.slowToString()}"; | |
| 54 Element result = compiler.findHelper(new SourceString(mangledName)); | |
| 55 return result; | |
| 56 } | |
| 57 | |
| 58 Element getStaticSetInterceptor(SourceString name) { | |
| 59 String mangledName = "builtin\$set\$${name.slowToString()}"; | |
| 60 Element result = compiler.findHelper(new SourceString(mangledName)); | |
| 61 return result; | |
| 62 } | |
| 63 | |
| 64 Element getOperatorInterceptor(Operator op) { | |
| 65 SourceString name = mapOperatorToMethodName(op); | |
| 66 Element result = compiler.findHelper(name); | |
| 67 return result; | |
| 68 } | |
| 69 | |
| 70 Element getPrefixOperatorInterceptor(Operator op) { | |
| 71 String name = op.source.stringValue; | |
| 72 if (name === '~') { | |
| 73 return compiler.findHelper(const SourceString('not')); | |
| 74 } | |
| 75 if (name === '-') { | |
| 76 return compiler.findHelper(const SourceString('neg')); | |
| 77 } | |
| 78 compiler.unimplemented('Unknown operator', node: op); | |
| 79 } | |
| 80 | |
| 81 Element getIndexInterceptor() { | |
| 82 return compiler.findHelper(const SourceString('index')); | |
| 83 } | |
| 84 | |
| 85 Element getIndexAssignmentInterceptor() { | |
| 86 return compiler.findHelper(const SourceString('indexSet')); | |
| 87 } | |
| 88 | |
| 89 Element getEqualsNullInterceptor() { | |
| 90 return compiler.findHelper(const SourceString('eqNull')); | |
| 91 } | |
| 92 | |
| 93 Element getExceptionUnwrapper() { | |
| 94 return compiler.findHelper(const SourceString('unwrapException')); | |
| 95 } | |
| 96 | |
| 97 Element getClosureConverter() { | |
| 98 return compiler.findHelper(const SourceString('convertDartClosureToJS')); | |
| 99 } | |
| 100 | |
| 101 Element getTraceFromException() { | |
| 102 return compiler.findHelper(const SourceString('getTraceFromException')); | |
| 103 } | |
| 104 | |
| 105 Element getEqualsInterceptor() { | |
| 106 return compiler.findHelper(const SourceString('eq')); | |
| 107 } | |
| 108 | |
| 109 Element getMapMaker() { | |
| 110 return compiler.findHelper(const SourceString('makeLiteralMap')); | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 class SsaBuilderTask extends CompilerTask { | |
| 115 final Interceptors interceptors; | |
| 116 final Map<Node, ClosureData> closureDataCache; | |
| 117 | |
| 118 String get name() => 'SSA builder'; | |
| 119 | |
| 120 SsaBuilderTask(Compiler compiler) | |
| 121 : interceptors = new Interceptors(compiler), | |
| 122 closureDataCache = new HashMap<Node, ClosureData>(), | |
| 123 super(compiler); | |
| 124 | |
| 125 HGraph build(WorkItem work) { | |
| 126 return measure(() { | |
| 127 FunctionElement element = work.element; | |
| 128 HInstruction.idCounter = 0; | |
| 129 SsaBuilder builder = new SsaBuilder(compiler, work); | |
| 130 HGraph graph; | |
| 131 switch (element.kind) { | |
| 132 case ElementKind.GENERATIVE_CONSTRUCTOR: | |
| 133 graph = compileConstructor(builder, work); | |
| 134 break; | |
| 135 case ElementKind.GENERATIVE_CONSTRUCTOR_BODY: | |
| 136 case ElementKind.FUNCTION: | |
| 137 case ElementKind.GETTER: | |
| 138 case ElementKind.SETTER: | |
| 139 graph = builder.buildMethod(work.element); | |
| 140 break; | |
| 141 } | |
| 142 assert(graph.isValid()); | |
| 143 if (compiler.tracer.enabled) { | |
| 144 String name; | |
| 145 if (element.enclosingElement !== null && | |
| 146 element.enclosingElement.kind == ElementKind.CLASS) { | |
| 147 String className = element.enclosingElement.name.slowToString(); | |
| 148 String memberName = element.name.slowToString(); | |
| 149 name = "$className.$memberName"; | |
| 150 if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { | |
| 151 name = "$name (body)"; | |
| 152 } | |
| 153 } else { | |
| 154 name = "${element.name.slowToString()}"; | |
| 155 } | |
| 156 compiler.tracer.traceCompilation(name); | |
| 157 compiler.tracer.traceGraph('builder', graph); | |
| 158 } | |
| 159 return graph; | |
| 160 }); | |
| 161 } | |
| 162 | |
| 163 HGraph compileConstructor(SsaBuilder builder, WorkItem work) { | |
| 164 // The body of the constructor will be generated in a separate function. | |
| 165 final ClassElement classElement = work.element.enclosingElement; | |
| 166 return builder.buildFactory(classElement, work.element); | |
| 167 } | |
| 168 } | |
| 169 | |
| 170 /** | |
| 171 * Keeps track of locals (including parameters and phis) when building. The | |
| 172 * 'this' reference is treated as parameter and hence handled by this class, | |
| 173 * too. | |
| 174 */ | |
| 175 class LocalsHandler { | |
| 176 /** | |
| 177 * The values of locals that can be directly accessed (without redirections | |
| 178 * to boxes or closure-fields). | |
| 179 */ | |
| 180 Map<Element, HInstruction> directLocals; | |
| 181 Map<Element, Element> redirectionMapping; | |
| 182 SsaBuilder builder; | |
| 183 ClosureData closureData; | |
| 184 | |
| 185 LocalsHandler(this.builder) | |
| 186 : directLocals = new Map<Element, HInstruction>(), | |
| 187 redirectionMapping = new Map<Element, Element>(); | |
| 188 | |
| 189 /** | |
| 190 * Creates a new [LocalsHandler] based on [other]. We only need to | |
| 191 * copy the [directLocals], since the other fields can be shared | |
| 192 * throughout the AST visit. | |
| 193 */ | |
| 194 LocalsHandler.from(LocalsHandler other) | |
| 195 : directLocals = new Map<Element, HInstruction>.from(other.directLocals), | |
| 196 redirectionMapping = other.redirectionMapping, | |
| 197 builder = other.builder, | |
| 198 closureData = other.closureData; | |
| 199 | |
| 200 /** | |
| 201 * Redirects accesses from element [from] to element [to]. The [to] element | |
| 202 * must be a boxed variable or a variable that is stored in a closure-field. | |
| 203 */ | |
| 204 void redirectElement(Element from, Element to) { | |
| 205 assert(redirectionMapping[from] === null); | |
| 206 redirectionMapping[from] = to; | |
| 207 assert(isStoredInClosureField(from) || isBoxed(from)); | |
| 208 } | |
| 209 | |
| 210 HInstruction createBox() { | |
| 211 // TODO(floitsch): Clean up this hack. Should we create a box-object by | |
| 212 // just creating an empty object literal? | |
| 213 HInstruction box = new HForeign(const LiteralDartString("{}"), | |
| 214 const LiteralDartString('Object'), | |
| 215 <HInstruction>[]); | |
| 216 builder.add(box); | |
| 217 return box; | |
| 218 } | |
| 219 | |
| 220 /** | |
| 221 * If the scope (function or loop) [node] has captured variables then this | |
| 222 * method creates a box and sets up the redirections. | |
| 223 */ | |
| 224 void enterScope(Node node) { | |
| 225 // See if any variable in the top-scope of the function is captured. If yes | |
| 226 // we need to create a box-object. | |
| 227 ClosureScope scopeData = closureData.capturingScopes[node]; | |
| 228 if (scopeData !== null) { | |
| 229 // The scope has captured variables. Create a box. | |
| 230 // TODO(floitsch): Clean up this hack. Should we create a box-object by | |
| 231 // just creating an empty object literal? | |
| 232 HInstruction box = createBox(); | |
| 233 // Add the box to the known locals. | |
| 234 directLocals[scopeData.boxElement] = box; | |
| 235 // Make sure that accesses to the boxed locals go into the box. We also | |
| 236 // need to make sure that parameters are copied into the box if necessary. | |
| 237 scopeData.capturedVariableMapping.forEach((Element from, Element to) { | |
| 238 // The [from] can only be a parameter for function-scopes and not | |
| 239 // loop scopes. | |
| 240 if (from.kind == ElementKind.PARAMETER) { | |
| 241 // Store the captured parameter in the box. Get the current value | |
| 242 // before we put the redirection in place. | |
| 243 HInstruction instruction = readLocal(from); | |
| 244 redirectElement(from, to); | |
| 245 // Now that the redirection is set up, the update to the local will | |
| 246 // write the parameter value into the box. | |
| 247 updateLocal(from, instruction); | |
| 248 } else { | |
| 249 redirectElement(from, to); | |
| 250 } | |
| 251 }); | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 /** | |
| 256 * Replaces the current box with a new box and copies over the given list | |
| 257 * of elements from the old box into the new box. | |
| 258 */ | |
| 259 void updateCaptureBox(Element boxElement, List<Element> toBeCopiedElements) { | |
| 260 // Create a new box and copy over the values from the old box into the | |
| 261 // new one. | |
| 262 HInstruction oldBox = readLocal(boxElement); | |
| 263 HInstruction newBox = createBox(); | |
| 264 for (Element boxedVariable in toBeCopiedElements) { | |
| 265 // [readLocal] uses the [boxElement] to find its box. By replacing it | |
| 266 // behind its back we can still get to the old values. | |
| 267 updateLocal(boxElement, oldBox); | |
| 268 HInstruction oldValue = readLocal(boxedVariable); | |
| 269 updateLocal(boxElement, newBox); | |
| 270 updateLocal(boxedVariable, oldValue); | |
| 271 } | |
| 272 updateLocal(boxElement, newBox); | |
| 273 } | |
| 274 | |
| 275 void startFunction(FunctionElement function, | |
| 276 FunctionExpression node) { | |
| 277 | |
| 278 ClosureTranslator translator = | |
| 279 new ClosureTranslator(builder.compiler, builder.elements); | |
| 280 closureData = translator.translate(node); | |
| 281 | |
| 282 FunctionParameters params = function.computeParameters(builder.compiler); | |
| 283 params.forEachParameter((Element element) { | |
| 284 HParameterValue parameter = new HParameterValue(element); | |
| 285 builder.add(parameter); | |
| 286 directLocals[element] = parameter; | |
| 287 }); | |
| 288 if (closureData.thisElement !== null) { | |
| 289 // Once closures have been mapped to classes their instance members might | |
| 290 // not have any thisElement if the closure was created inside a static | |
| 291 // context. | |
| 292 assert(function.isInstanceMember() || function.isGenerativeConstructor()); | |
| 293 // We have to introduce 'this' before we enter the scope, since it might | |
| 294 // need to be copied into a box (if it is captured). This is similar | |
| 295 // to all other parameters that are introduced. | |
| 296 HInstruction thisInstruction = new HThis(); | |
| 297 builder.add(thisInstruction); | |
| 298 directLocals[closureData.thisElement] = thisInstruction; | |
| 299 } | |
| 300 | |
| 301 enterScope(node); | |
| 302 | |
| 303 // If the freeVariableMapping is not empty, then this function was a | |
| 304 // nested closure that captures variables. Redirect the captured | |
| 305 // variables to fields in the closure. | |
| 306 closureData.freeVariableMapping.forEach((Element from, Element to) { | |
| 307 redirectElement(from, to); | |
| 308 }); | |
| 309 if (closureData.isClosure()) { | |
| 310 // Inside closure redirect references to itself to [:this:]. | |
| 311 HInstruction thisInstruction = new HThis(); | |
| 312 builder.add(thisInstruction); | |
| 313 updateLocal(closureData.closureElement, thisInstruction); | |
| 314 } | |
| 315 } | |
| 316 | |
| 317 bool hasValueForDirectLocal(Element element) { | |
| 318 assert(element !== null); | |
| 319 assert(isAccessedDirectly(element)); | |
| 320 return directLocals[element] !== null; | |
| 321 } | |
| 322 | |
| 323 /** | |
| 324 * Returns true if the local can be accessed directly. Boxed variables or | |
| 325 * captured variables that are stored in the closure-field return [false]. | |
| 326 */ | |
| 327 bool isAccessedDirectly(Element element) { | |
| 328 assert(element !== null); | |
| 329 return redirectionMapping[element] === null | |
| 330 && !closureData.usedVariablesInTry.contains(element); | |
| 331 } | |
| 332 | |
| 333 bool isStoredInClosureField(Element element) { | |
| 334 assert(element !== null); | |
| 335 if (isAccessedDirectly(element)) return false; | |
| 336 Element redirectTarget = redirectionMapping[element]; | |
| 337 if (redirectTarget == null) return false; | |
| 338 if (redirectTarget.enclosingElement.kind == ElementKind.CLASS) { | |
| 339 assert(redirectTarget is ClosureFieldElement); | |
| 340 return true; | |
| 341 } | |
| 342 return false; | |
| 343 } | |
| 344 | |
| 345 bool isBoxed(Element element) { | |
| 346 if (isAccessedDirectly(element)) return false; | |
| 347 if (isStoredInClosureField(element)) return false; | |
| 348 return redirectionMapping[element] !== null; | |
| 349 } | |
| 350 | |
| 351 bool isUsedInTry(Element element) { | |
| 352 return closureData.usedVariablesInTry.contains(element); | |
| 353 } | |
| 354 | |
| 355 /** | |
| 356 * Returns an [HInstruction] for the given element. If the element is | |
| 357 * boxed or stored in a closure then the method generates code to retrieve | |
| 358 * the value. | |
| 359 */ | |
| 360 HInstruction readLocal(Element element) { | |
| 361 if (isAccessedDirectly(element)) { | |
| 362 if (directLocals[element] == null) { | |
| 363 builder.compiler.internalError("Cannot find value $element", | |
| 364 element: element); | |
| 365 } | |
| 366 return directLocals[element]; | |
| 367 } else if (isStoredInClosureField(element)) { | |
| 368 Element redirect = redirectionMapping[element]; | |
| 369 // We must not use the [LocalsHandler.readThis()] since that could | |
| 370 // point to a captured this which would be stored in a closure-field | |
| 371 // itself. | |
| 372 HInstruction receiver = new HThis(); | |
| 373 builder.add(receiver); | |
| 374 HInstruction fieldGet = new HFieldGet(redirect, receiver); | |
| 375 builder.add(fieldGet); | |
| 376 return fieldGet; | |
| 377 } else if (isBoxed(element)) { | |
| 378 Element redirect = redirectionMapping[element]; | |
| 379 // In the function that declares the captured variable the box is | |
| 380 // accessed as direct local. Inside the nested closure the box is | |
| 381 // accessed through a closure-field. | |
| 382 // Calling [readLocal] makes sure we generate the correct code to get | |
| 383 // the box. | |
| 384 assert(redirect.enclosingElement.kind == ElementKind.VARIABLE); | |
| 385 HInstruction box = readLocal(redirect.enclosingElement); | |
| 386 HInstruction lookup = new HFieldGet(redirect, box); | |
| 387 builder.add(lookup); | |
| 388 return lookup; | |
| 389 } else { | |
| 390 assert(isUsedInTry(element)); | |
| 391 HInstruction variable = new HFieldGet.fromActivation(element); | |
| 392 builder.add(variable); | |
| 393 return variable; | |
| 394 } | |
| 395 } | |
| 396 | |
| 397 HInstruction readThis() { | |
| 398 return readLocal(closureData.thisElement); | |
| 399 } | |
| 400 | |
| 401 /** | |
| 402 * Sets the [element] to [value]. If the element is boxed or stored in a | |
| 403 * closure then the method generates code to set the value. | |
| 404 */ | |
| 405 void updateLocal(Element element, HInstruction value) { | |
| 406 if (isAccessedDirectly(element)) { | |
| 407 directLocals[element] = value; | |
| 408 } else if (isStoredInClosureField(element)) { | |
| 409 Element redirect = redirectionMapping[element]; | |
| 410 // We must not use the [LocalsHandler.readThis()] since that could | |
| 411 // point to a captured this which would be stored in a closure-field | |
| 412 // itself. | |
| 413 HInstruction receiver = new HThis(); | |
| 414 builder.add(receiver); | |
| 415 builder.add(new HFieldSet(redirect, receiver, value)); | |
| 416 } else if (isBoxed(element)) { | |
| 417 Element redirect = redirectionMapping[element]; | |
| 418 // The box itself could be captured, or be local. A local variable that | |
| 419 // is captured will be boxed, but the box itself will be a local. | |
| 420 // Inside the closure the box is stored in a closure-field and cannot | |
| 421 // be accessed directly. | |
| 422 assert(redirect.enclosingElement.kind == ElementKind.VARIABLE); | |
| 423 HInstruction box = readLocal(redirect.enclosingElement); | |
| 424 builder.add(new HFieldSet(redirect, box, value)); | |
| 425 } else { | |
| 426 assert(isUsedInTry(element)); | |
| 427 builder.add(new HFieldSet.fromActivation(element,value)); | |
| 428 } | |
| 429 } | |
| 430 | |
| 431 /** | |
| 432 * This function must be called before visiting any children of the loop. In | |
| 433 * particular it needs to be called before executing the initializers. | |
| 434 * | |
| 435 * The [LocalsHandler] will make the boxes and updates at the right moment. | |
| 436 * The builder just needs to call [enterLoopBody] and [enterLoopUpdates] (for | |
| 437 * [For] loops) at the correct places. For phi-handling [beginLoopHeader] and | |
| 438 * [endLoop] must also be called. | |
| 439 * | |
| 440 * The correct place for the box depends on the given loop. In most cases | |
| 441 * the box will be created when entering the loop-body: while, do-while, and | |
| 442 * for-in (assuming the call to [:next:] is inside the body) can always be | |
| 443 * constructed this way. | |
| 444 * | |
| 445 * Things are slightly more complicated for [For] loops. If no declared | |
| 446 * loop variable is boxed then the loop-body approach works here too. If a | |
| 447 * loop-variable is boxed we need to introduce a new box for the | |
| 448 * loop-variable before we enter the initializer so that the initializer | |
| 449 * writes the values into the box. In any case we need to create the box | |
| 450 * before the condition since the condition could box the variable. | |
| 451 * Since the first box is created outside the actual loop we have a second | |
| 452 * location where a box is created: just before the updates. This is | |
| 453 * necessary since updates are considered to be part of the next iteration | |
| 454 * (and can again capture variables). | |
| 455 * | |
| 456 * For example the following Dart code prints 1 3 -- 3 4. | |
| 457 * | |
| 458 * var fs = []; | |
| 459 * for (var i = 0; i < 3; (f() { fs.add(f); print(i); i++; })()) { | |
| 460 * i++; | |
| 461 * } | |
| 462 * print("--"); | |
| 463 * for (var i = 0; i < 2; i++) fs[i](); | |
| 464 * | |
| 465 * We solve this by emitting the following code (only for [For] loops): | |
| 466 * <Create box> <== move the first box creation outside the loop. | |
| 467 * <initializer>; | |
| 468 * loop-entry: | |
| 469 * if (!<condition>) goto loop-exit; | |
| 470 * <body> | |
| 471 * <update box> // create a new box and copy the captured loop-variables. | |
| 472 * <updates> | |
| 473 * goto loop-entry; | |
| 474 * loop-exit: | |
| 475 */ | |
| 476 void startLoop(Node node) { | |
| 477 ClosureScope scopeData = closureData.capturingScopes[node]; | |
| 478 if (scopeData == null) return; | |
| 479 if (scopeData.hasBoxedLoopVariables()) { | |
| 480 // If there are boxed loop variables then we set up the box and | |
| 481 // redirections already now. This way the initializer can write its | |
| 482 // values into the box. | |
| 483 // For other loops the box will be created when entering the body. | |
| 484 enterScope(node); | |
| 485 } | |
| 486 } | |
| 487 | |
| 488 void beginLoopHeader(Node node, HBasicBlock loopEntry) { | |
| 489 // Create a copy because we modify the map while iterating over | |
| 490 // it. | |
| 491 Map<Element, HInstruction> saved = | |
| 492 new Map<Element, HInstruction>.from(directLocals); | |
| 493 | |
| 494 // Create phis for all elements in the definitions environment. | |
| 495 saved.forEach((Element element, HInstruction instruction) { | |
| 496 // We know 'this' cannot be modified. | |
| 497 if (element !== closureData.thisElement) { | |
| 498 HPhi phi = new HPhi.singleInput(element, instruction); | |
| 499 loopEntry.addPhi(phi); | |
| 500 directLocals[element] = phi; | |
| 501 } else { | |
| 502 directLocals[element] = instruction; | |
| 503 } | |
| 504 }); | |
| 505 } | |
| 506 | |
| 507 void enterLoopBody(Node node) { | |
| 508 ClosureScope scopeData = closureData.capturingScopes[node]; | |
| 509 if (scopeData == null) return; | |
| 510 // If there are no declared boxed loop variables then we did not create the | |
| 511 // box before the initializer and we have to create the box now. | |
| 512 if (!scopeData.hasBoxedLoopVariables()) { | |
| 513 enterScope(node); | |
| 514 } | |
| 515 } | |
| 516 | |
| 517 void enterLoopUpdates(Loop node) { | |
| 518 // If there are declared boxed loop variables then the updates might have | |
| 519 // access to the box and we must switch to a new box before executing the | |
| 520 // updates. | |
| 521 // In all other cases a new box will be created when entering the body of | |
| 522 // the next iteration. | |
| 523 ClosureScope scopeData = closureData.capturingScopes[node]; | |
| 524 if (scopeData == null) return; | |
| 525 if (scopeData.hasBoxedLoopVariables()) { | |
| 526 updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables); | |
| 527 } | |
| 528 } | |
| 529 | |
| 530 void endLoop(HBasicBlock loopEntry) { | |
| 531 loopEntry.forEachPhi((HPhi phi) { | |
| 532 Element element = phi.element; | |
| 533 HInstruction postLoopDefinition = directLocals[element]; | |
| 534 phi.addInput(postLoopDefinition); | |
| 535 }); | |
| 536 } | |
| 537 | |
| 538 /** | |
| 539 * Merge [otherLocals] into this locals handler, creating phi-nodes when | |
| 540 * there is a conflict. | |
| 541 * If a phi node is necessary, it will use the otherLocals instruction as the | |
| 542 * first input, and this handler's instruction as the second. | |
| 543 * NOTICE: This means that the predecessor corresponding to [otherLocals] | |
| 544 * should be the first predecessor of the current block, and the one | |
| 545 * corresponding to this locals handler should be the second. | |
| 546 */ | |
| 547 void mergeWith(LocalsHandler otherLocals, HBasicBlock joinBlock) { | |
| 548 // If an element is in one map but not the other we can safely | |
| 549 // ignore it. It means that a variable was declared in the | |
| 550 // block. Since variable declarations are scoped the declared | |
| 551 // variable cannot be alive outside the block. Note: this is only | |
| 552 // true for nodes where we do joins. | |
| 553 Map<Element, HInstruction> joinedLocals = new Map<Element, HInstruction>(); | |
| 554 otherLocals.directLocals.forEach((element, instruction) { | |
| 555 // We know 'this' cannot be modified. | |
| 556 if (element === closureData.thisElement) { | |
| 557 assert(directLocals[element] == instruction); | |
| 558 joinedLocals[element] = instruction; | |
| 559 } else { | |
| 560 HInstruction mine = directLocals[element]; | |
| 561 if (mine === null) return; | |
| 562 if (instruction === mine) { | |
| 563 joinedLocals[element] = instruction; | |
| 564 } else { | |
| 565 HInstruction phi = | |
| 566 new HPhi.manyInputs(element, <HInstruction>[instruction, mine]); | |
| 567 joinBlock.addPhi(phi); | |
| 568 joinedLocals[element] = phi; | |
| 569 } | |
| 570 } | |
| 571 }); | |
| 572 directLocals = joinedLocals; | |
| 573 } | |
| 574 | |
| 575 /** | |
| 576 * The current localsHandler is not used for its values, only for its | |
| 577 * declared variables. This is a way to exclude local values from the | |
| 578 * result when they are no longer in scope. | |
| 579 * Returns the new LocalsHandler to use (may not be [this]). | |
| 580 */ | |
| 581 LocalsHandler mergeMultiple(List<LocalsHandler> locals, | |
| 582 HBasicBlock joinBlock) { | |
| 583 assert(locals.length > 0); | |
| 584 if (locals.length == 1) return locals[0]; | |
| 585 Map<Element, HInstruction> joinedLocals = new Map<Element,HInstruction>(); | |
| 586 HInstruction thisValue = null; | |
| 587 directLocals.forEach((Element element, HInstruction instruction) { | |
| 588 if (element !== closureData.thisElement) { | |
| 589 HPhi phi = new HPhi.noInputs(element); | |
| 590 joinedLocals[element] = phi; | |
| 591 joinBlock.addPhi(phi); | |
| 592 } else { | |
| 593 // We know that "this" never changes, if it's there. | |
| 594 // Save it for later. While merging, there is no phi for "this", | |
| 595 // so we don't have to special case it in the merge loop. | |
| 596 thisValue = instruction; | |
| 597 } | |
| 598 }); | |
| 599 for (LocalsHandler local in locals) { | |
| 600 local.directLocals.forEach((Element element, HInstruction instruction) { | |
| 601 HPhi phi = joinedLocals[element]; | |
| 602 if (phi !== null) { | |
| 603 phi.addInput(instruction); | |
| 604 } | |
| 605 }); | |
| 606 } | |
| 607 if (thisValue !== null) { | |
| 608 // If there was a "this" for the scope, add it to the new locals. | |
| 609 joinedLocals[closureData.thisElement] = thisValue; | |
| 610 } | |
| 611 directLocals = joinedLocals; | |
| 612 return this; | |
| 613 } | |
| 614 } | |
| 615 | |
| 616 | |
| 617 // Represents a single break/continue instruction. | |
| 618 class JumpHandlerEntry { | |
| 619 final HGoto jumpInstruction; | |
| 620 final LocalsHandler locals; | |
| 621 bool isBreak() => jumpInstruction is HBreak; | |
| 622 bool isContinue() => jumpInstruction is HContinue; | |
| 623 JumpHandlerEntry(this.jumpInstruction, this.locals); | |
| 624 } | |
| 625 | |
| 626 | |
| 627 interface JumpHandler default JumpHandlerImpl { | |
| 628 JumpHandler(SsaBuilder builder, TargetElement target); | |
| 629 void generateBreak([LabelElement label]); | |
| 630 void generateContinue([LabelElement label]); | |
| 631 void forEachBreak(void action(HBreak instruction, LocalsHandler locals)); | |
| 632 void forEachContinue(void action(HBreak instruction, LocalsHandler locals)); | |
| 633 void close(); | |
| 634 List<LabelElement> labels(); | |
| 635 } | |
| 636 | |
| 637 // Insert break handler used to avoid null checks when a target isn't | |
| 638 // used as the target of a break, and therefore doesn't need a break | |
| 639 // handler associated with it. | |
| 640 class NullJumpHandler implements JumpHandler { | |
| 641 const NullJumpHandler(); | |
| 642 void generateBreak([LabelElement label]) { unreachable(); } | |
| 643 void generateContinue([LabelElement label]) { unreachable(); } | |
| 644 void forEachBreak(Function ignored) { } | |
| 645 void forEachContinue(Function ignored) { } | |
| 646 void close() { } | |
| 647 List<LabelElement> labels() => const <LabelElement>[]; | |
| 648 } | |
| 649 | |
| 650 // Records breaks until a target block is available. | |
| 651 // Breaks are always forward jumps. | |
| 652 // Continues in loops are implemented as breaks of the body. | |
| 653 // Continues in switches is currently not handled. | |
| 654 class JumpHandlerImpl implements JumpHandler { | |
| 655 final SsaBuilder builder; | |
| 656 final TargetElement target; | |
| 657 final List<JumpHandlerEntry> jumps; | |
| 658 | |
| 659 JumpHandlerImpl(SsaBuilder builder, this.target) | |
| 660 : this.builder = builder, | |
| 661 jumps = <JumpHandlerEntry>[] { | |
| 662 assert(builder.jumpTargets[target] === null); | |
| 663 builder.jumpTargets[target] = this; | |
| 664 } | |
| 665 | |
| 666 void generateBreak([LabelElement label]) { | |
| 667 HInstruction breakInstruction; | |
| 668 if (label === null) { | |
| 669 breakInstruction = new HBreak(target); | |
| 670 } else { | |
| 671 breakInstruction = new HBreak.toLabel(label); | |
| 672 } | |
| 673 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); | |
| 674 builder.close(breakInstruction); | |
| 675 jumps.add(new JumpHandlerEntry(breakInstruction, locals)); | |
| 676 } | |
| 677 | |
| 678 void generateContinue([LabelElement label]) { | |
| 679 HInstruction continueInstruction; | |
| 680 if (label === null) { | |
| 681 continueInstruction = new HContinue(target); | |
| 682 } else { | |
| 683 continueInstruction = new HContinue.toLabel(label); | |
| 684 } | |
| 685 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); | |
| 686 builder.close(continueInstruction); | |
| 687 jumps.add(new JumpHandlerEntry(continueInstruction, locals)); | |
| 688 } | |
| 689 | |
| 690 void forEachBreak(Function action) { | |
| 691 for (JumpHandlerEntry entry in jumps) { | |
| 692 if (entry.isBreak()) action(entry.jumpInstruction, entry.locals); | |
| 693 } | |
| 694 } | |
| 695 | |
| 696 void forEachContinue(Function action) { | |
| 697 for (JumpHandlerEntry entry in jumps) { | |
| 698 if (entry.isContinue()) action(entry.jumpInstruction, entry.locals); | |
| 699 } | |
| 700 } | |
| 701 | |
| 702 void close() { | |
| 703 // The mapping from TargetElement to JumpHandler is no longer needed. | |
| 704 builder.jumpTargets.remove(target); | |
| 705 } | |
| 706 | |
| 707 List<LabelElement> labels() { | |
| 708 List<LabelElement> result = null; | |
| 709 for (LabelElement element in target.labels) { | |
| 710 if (result === null) result = <LabelElement>[]; | |
| 711 result.add(element); | |
| 712 } | |
| 713 return (result === null) ? const <LabelElement>[] : result; | |
| 714 } | |
| 715 } | |
| 716 | |
| 717 class SsaBuilder implements Visitor { | |
| 718 final Compiler compiler; | |
| 719 TreeElements elements; | |
| 720 final Interceptors interceptors; | |
| 721 final WorkItem work; | |
| 722 bool methodInterceptionEnabled; | |
| 723 HGraph graph; | |
| 724 LocalsHandler localsHandler; | |
| 725 HInstruction rethrowableException; | |
| 726 | |
| 727 Map<TargetElement, JumpHandler> jumpTargets; | |
| 728 | |
| 729 // We build the Ssa graph by simulating a stack machine. | |
| 730 List<HInstruction> stack; | |
| 731 | |
| 732 // The current block to add instructions to. Might be null, if we are | |
| 733 // visiting dead code. | |
| 734 HBasicBlock current; | |
| 735 // The most recently opened block. Has the same value as [current] while | |
| 736 // the block is open, but unlike [current], it isn't cleared when the current | |
| 737 // block is closed. | |
| 738 HBasicBlock lastOpenedBlock; | |
| 739 | |
| 740 LibraryElement get currentLibrary() => work.element.getLibrary(); | |
| 741 | |
| 742 SsaBuilder(Compiler compiler, WorkItem work) | |
| 743 : this.compiler = compiler, | |
| 744 this.work = work, | |
| 745 interceptors = compiler.builder.interceptors, | |
| 746 methodInterceptionEnabled = true, | |
| 747 elements = work.resolutionTree, | |
| 748 graph = new HGraph(), | |
| 749 stack = new List<HInstruction>(), | |
| 750 jumpTargets = new Map<TargetElement, JumpHandler>() { | |
| 751 localsHandler = new LocalsHandler(this); | |
| 752 } | |
| 753 | |
| 754 void disableMethodInterception() { | |
| 755 assert(methodInterceptionEnabled); | |
| 756 methodInterceptionEnabled = false; | |
| 757 } | |
| 758 | |
| 759 void enableMethodInterception() { | |
| 760 assert(!methodInterceptionEnabled); | |
| 761 methodInterceptionEnabled = true; | |
| 762 } | |
| 763 | |
| 764 HGraph buildMethod(FunctionElement functionElement) { | |
| 765 FunctionExpression function = functionElement.parseNode(compiler); | |
| 766 openFunction(functionElement, function); | |
| 767 function.body.accept(this); | |
| 768 return closeFunction(); | |
| 769 } | |
| 770 | |
| 771 /** | |
| 772 * Returns the constructor body associated with the given constructor or | |
| 773 * creates a new constructor body, if none can be found. | |
| 774 */ | |
| 775 ConstructorBodyElement getConstructorBody(ClassElement classElement, | |
| 776 FunctionElement constructor) { | |
| 777 assert(constructor.kind === ElementKind.GENERATIVE_CONSTRUCTOR); | |
| 778 ConstructorBodyElement bodyElement; | |
| 779 for (Link<Element> backendMembers = classElement.backendMembers; | |
| 780 !backendMembers.isEmpty(); | |
| 781 backendMembers = backendMembers.tail) { | |
| 782 Element backendMember = backendMembers.head; | |
| 783 if (backendMember.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { | |
| 784 ConstructorBodyElement body = backendMember; | |
| 785 if (body.constructor == constructor) { | |
| 786 bodyElement = backendMember; | |
| 787 break; | |
| 788 } | |
| 789 } | |
| 790 } | |
| 791 if (bodyElement === null) { | |
| 792 bodyElement = new ConstructorBodyElement(constructor); | |
| 793 TreeElements treeElements = | |
| 794 compiler.resolver.resolveMethodElement(constructor); | |
| 795 compiler.enqueue(new WorkItem.toCodegen(bodyElement, treeElements)); | |
| 796 classElement.backendMembers = | |
| 797 classElement.backendMembers.prepend(bodyElement); | |
| 798 } | |
| 799 assert(bodyElement.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY); | |
| 800 return bodyElement; | |
| 801 } | |
| 802 | |
| 803 /** | |
| 804 * Run through the initializers and inline all field initializers. Recursively | |
| 805 * inlines super initializers. | |
| 806 * | |
| 807 * The constructors of the inlined initializers is added to [constructors] | |
| 808 * with sub constructors having a lower index than super constructors. | |
| 809 */ | |
| 810 void inlineInitializers(FunctionElement constructor, | |
| 811 List<FunctionElement> constructors, | |
| 812 Map<Element, HInstruction> fieldValues) { | |
| 813 TreeElements oldElements = elements; | |
| 814 constructors.addLast(constructor); | |
| 815 bool initializedSuper = false; | |
| 816 elements = compiler.resolver.resolveMethodElement(constructor); | |
| 817 FunctionExpression functionNode = constructor.parseNode(compiler); | |
| 818 | |
| 819 if (functionNode.initializers !== null) { | |
| 820 Link<Node> initializers = functionNode.initializers.nodes; | |
| 821 for (Link<Node> link = initializers; !link.isEmpty(); link = link.tail) { | |
| 822 assert(link.head is Send); | |
| 823 if (link.head is !SendSet) { | |
| 824 // A super initializer or constructor redirection. | |
| 825 Send call = link.head; | |
| 826 assert(Initializers.isSuperConstructorCall(call) || | |
| 827 Initializers.isConstructorRedirect(call)); | |
| 828 FunctionElement nextConstructor = elements[call]; | |
| 829 // Visit arguments and map the corresponding parameter value to | |
| 830 // the resulting HInstruction value. | |
| 831 List<HInstruction> arguments = new List<HInstruction>(); | |
| 832 addStaticSendArgumentsToList(call, nextConstructor, arguments); | |
| 833 int index = 0; | |
| 834 FunctionParameters parameters = | |
| 835 nextConstructor.computeParameters(compiler); | |
| 836 parameters.forEachParameter((Element parameter) { | |
| 837 HInstruction argument = arguments[index++]; | |
| 838 localsHandler.updateLocal(parameter, argument); | |
| 839 // Don't forget to update the field, if the parameter is of the | |
| 840 // form [:this.x:]. | |
| 841 if (parameter.kind == ElementKind.FIELD_PARAMETER) { | |
| 842 FieldParameterElement fieldParameterElement = parameter; | |
| 843 fieldValues[fieldParameterElement.fieldElement] = argument; | |
| 844 } | |
| 845 }); | |
| 846 inlineInitializers(nextConstructor, constructors, fieldValues); | |
| 847 initializedSuper = true; | |
| 848 } else { | |
| 849 // A field initializer. | |
| 850 SendSet init = link.head; | |
| 851 Link<Node> arguments = init.arguments; | |
| 852 assert(!arguments.isEmpty() && arguments.tail.isEmpty()); | |
| 853 visit(arguments.head); | |
| 854 fieldValues[elements[init]] = pop(); | |
| 855 } | |
| 856 } | |
| 857 } | |
| 858 | |
| 859 if (!initializedSuper) { | |
| 860 // No super initializer found. Try to find the default constructor if | |
| 861 // the class is not Object. | |
| 862 ClassElement enclosingClass = constructor.enclosingElement; | |
| 863 ClassElement superClass = enclosingClass.superclass; | |
| 864 if (enclosingClass != compiler.objectClass) { | |
| 865 assert(superClass !== null); | |
| 866 assert(superClass.isResolved); | |
| 867 FunctionElement nextConstructor = | |
| 868 superClass.lookupConstructor(superClass.name); | |
| 869 if (nextConstructor === null) { | |
| 870 compiler.internalError("no default constructor available"); | |
| 871 } | |
| 872 inlineInitializers(nextConstructor, constructors, fieldValues); | |
| 873 } | |
| 874 } | |
| 875 | |
| 876 elements = oldElements; | |
| 877 } | |
| 878 | |
| 879 /** | |
| 880 * Build the factory function corresponding to the constructor | |
| 881 * [functionElement]: | |
| 882 * - Initialize fields with the values of the field initializers of the | |
| 883 * current constructor and super constructors or constructors redirected | |
| 884 * to, starting from the current constructor. | |
| 885 * - Call the the constructor bodies, starting from the constructor(s) in the | |
| 886 * super class(es). | |
| 887 */ | |
| 888 HGraph buildFactory(ClassElement classElement, | |
| 889 FunctionElement functionElement) { | |
| 890 FunctionExpression function = functionElement.parseNode(compiler); | |
| 891 // Note that constructors (like any other static function) do not need | |
| 892 // to deal with optional arguments. It is the callers job to provide all | |
| 893 // arguments as if they were positional. | |
| 894 | |
| 895 // The initializer list could contain closures. | |
| 896 openFunction(functionElement, function); | |
| 897 | |
| 898 Map<Element, HInstruction> fieldValues = new Map<Element, HInstruction>(); | |
| 899 FunctionParameters parameters = functionElement.computeParameters(compiler); | |
| 900 parameters.forEachParameter((Element element) { | |
| 901 if (element.kind == ElementKind.FIELD_PARAMETER) { | |
| 902 // If the [element] is a field-parameter (such as [:this.x:] then | |
| 903 // initialize the field element with its value. | |
| 904 FieldParameterElement fieldParameterElement = element; | |
| 905 HInstruction parameterValue = localsHandler.readLocal(element); | |
| 906 fieldValues[fieldParameterElement.fieldElement] = parameterValue; | |
| 907 } | |
| 908 }); | |
| 909 | |
| 910 final Map<FunctionElement, TreeElements> constructorElements = | |
| 911 compiler.resolver.constructorElements; | |
| 912 List<FunctionElement> constructors = new List<FunctionElement>(); | |
| 913 | |
| 914 // Analyze the constructor and all referenced constructors and collect | |
| 915 // initializers and constructor bodies. | |
| 916 inlineInitializers(functionElement, constructors, fieldValues); | |
| 917 | |
| 918 // Call the JavaScript constructor with the fields as argument. | |
| 919 // TODO(floitsch,karlklose): move this code to ClassElement and share with | |
| 920 // the emitter. | |
| 921 List<HInstruction> constructorArguments = <HInstruction>[]; | |
| 922 ClassElement element = classElement; | |
| 923 while (element != null) { | |
| 924 for (Element member in element.members) { | |
| 925 if (member.isInstanceMember() && member.kind == ElementKind.FIELD) { | |
| 926 HInstruction value = fieldValues[member]; | |
| 927 if (value === null) { | |
| 928 // The field has no value in the initializer list. Initialize it | |
| 929 // with the declaration-site constant (if any). | |
| 930 Constant fieldValue = | |
| 931 compiler.constantHandler.compileVariable(member); | |
| 932 value = graph.addConstant(fieldValue); | |
| 933 } | |
| 934 constructorArguments.add(value); | |
| 935 } | |
| 936 } | |
| 937 element = element.superclass; | |
| 938 } | |
| 939 HForeignNew newObject = new HForeignNew(classElement, constructorArguments); | |
| 940 add(newObject); | |
| 941 // Generate calls to the constructor bodies. | |
| 942 for (int index = constructors.length - 1; index >= 0; index--) { | |
| 943 FunctionElement constructor = constructors[index]; | |
| 944 // TODO(floitsch): find better way to detect that constructor body is | |
| 945 // empty. | |
| 946 if (constructor is SynthesizedConstructorElement) continue; | |
| 947 ConstructorBodyElement body = getConstructorBody(classElement, | |
| 948 constructor); | |
| 949 List bodyCallInputs = <HInstruction>[]; | |
| 950 bodyCallInputs.add(newObject); | |
| 951 body.functionParameters.forEachParameter((parameter) { | |
| 952 bodyCallInputs.add(localsHandler.readLocal(parameter)); | |
| 953 }); | |
| 954 // TODO(ahe): The constructor name is statically resolved. See | |
| 955 // SsaCodeGenerator.visitInvokeDynamicMethod. Is there a cleaner | |
| 956 // way to do this? | |
| 957 SourceString methodName = new SourceString(compiler.namer.getName(body)); | |
| 958 add(new HInvokeDynamicMethod(null, methodName, bodyCallInputs)); | |
| 959 } | |
| 960 close(new HReturn(newObject)).addSuccessor(graph.exit); | |
| 961 return closeFunction(); | |
| 962 } | |
| 963 | |
| 964 void openFunction(FunctionElement functionElement, | |
| 965 FunctionExpression node) { | |
| 966 HBasicBlock block = graph.addNewBlock(); | |
| 967 open(graph.entry); | |
| 968 | |
| 969 localsHandler.startFunction(functionElement, node); | |
| 970 close(new HGoto()).addSuccessor(block); | |
| 971 | |
| 972 open(block); | |
| 973 } | |
| 974 | |
| 975 HGraph closeFunction() { | |
| 976 // TODO(kasperl): Make this goto an implicit return. | |
| 977 if (!isAborted()) close(new HGoto()).addSuccessor(graph.exit); | |
| 978 graph.finalize(); | |
| 979 return graph; | |
| 980 } | |
| 981 | |
| 982 HBasicBlock addNewBlock() { | |
| 983 HBasicBlock block = graph.addNewBlock(); | |
| 984 // If adding a new block during building of an expression, it is due to | |
| 985 // conditional expressions or short-circuit logical operators. | |
| 986 return block; | |
| 987 } | |
| 988 | |
| 989 void open(HBasicBlock block) { | |
| 990 block.open(); | |
| 991 current = block; | |
| 992 lastOpenedBlock = block; | |
| 993 } | |
| 994 | |
| 995 HBasicBlock close(HControlFlow end) { | |
| 996 HBasicBlock result = current; | |
| 997 current.close(end); | |
| 998 current = null; | |
| 999 return result; | |
| 1000 } | |
| 1001 | |
| 1002 void goto(HBasicBlock from, HBasicBlock to) { | |
| 1003 from.close(new HGoto()); | |
| 1004 from.addSuccessor(to); | |
| 1005 } | |
| 1006 | |
| 1007 bool isAborted() { | |
| 1008 return current === null; | |
| 1009 } | |
| 1010 | |
| 1011 void add(HInstruction instruction) { | |
| 1012 current.add(instruction); | |
| 1013 } | |
| 1014 | |
| 1015 void push(HInstruction instruction) { | |
| 1016 add(instruction); | |
| 1017 stack.add(instruction); | |
| 1018 } | |
| 1019 | |
| 1020 HInstruction pop() { | |
| 1021 return stack.removeLast(); | |
| 1022 } | |
| 1023 | |
| 1024 HBoolify popBoolified() { | |
| 1025 HBoolify boolified = new HBoolify(pop()); | |
| 1026 add(boolified); | |
| 1027 return boolified; | |
| 1028 } | |
| 1029 | |
| 1030 void visit(Node node) { | |
| 1031 if (node !== null) node.accept(this); | |
| 1032 } | |
| 1033 | |
| 1034 visitBlock(Block node) { | |
| 1035 for (Link<Node> link = node.statements.nodes; | |
| 1036 !link.isEmpty(); | |
| 1037 link = link.tail) { | |
| 1038 visit(link.head); | |
| 1039 if (isAborted()) { | |
| 1040 // The block has been aborted by a return or a throw. | |
| 1041 if (!stack.isEmpty()) compiler.cancel('non-empty instruction stack'); | |
| 1042 return; | |
| 1043 } | |
| 1044 } | |
| 1045 assert(!current.isClosed()); | |
| 1046 if (!stack.isEmpty()) compiler.cancel('non-empty instruction stack'); | |
| 1047 } | |
| 1048 | |
| 1049 visitClassNode(ClassNode node) { | |
| 1050 unreachable(); | |
| 1051 } | |
| 1052 | |
| 1053 visitExpressionStatement(ExpressionStatement node) { | |
| 1054 visit(node.expression); | |
| 1055 pop(); | |
| 1056 } | |
| 1057 | |
| 1058 /** | |
| 1059 * Creates a new loop-header block. The previous [current] block | |
| 1060 * is closed with an [HGoto] and replaced by the newly created block. | |
| 1061 * Also notifies the locals handler that we're entering a loop. | |
| 1062 */ | |
| 1063 JumpHandler beginLoopHeader(Node node) { | |
| 1064 assert(!isAborted()); | |
| 1065 HBasicBlock previousBlock = close(new HGoto()); | |
| 1066 | |
| 1067 JumpHandler jumpHandler = createJumpHandler(node); | |
| 1068 HBasicBlock loopEntry = graph.addNewLoopHeaderBlock(jumpHandler.labels()); | |
| 1069 previousBlock.addSuccessor(loopEntry); | |
| 1070 open(loopEntry); | |
| 1071 | |
| 1072 localsHandler.beginLoopHeader(node, loopEntry); | |
| 1073 return jumpHandler; | |
| 1074 } | |
| 1075 | |
| 1076 /** | |
| 1077 * Ends the loop: | |
| 1078 * - creates a new block and adds it as successor to the [branchBlock]. | |
| 1079 * - opens the new block (setting as [current]). | |
| 1080 * - notifies the locals handler that we're exiting a loop. | |
| 1081 */ | |
| 1082 void endLoop(HBasicBlock loopEntry, | |
| 1083 HBasicBlock branchBlock, | |
| 1084 JumpHandler jumpHandler, | |
| 1085 LocalsHandler savedLocals) { | |
| 1086 HBasicBlock loopExitBlock = addNewBlock(); | |
| 1087 assert(branchBlock.successors.length == 1); | |
| 1088 List<LocalsHandler> breakLocals = <LocalsHandler>[]; | |
| 1089 jumpHandler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) { | |
| 1090 breakInstruction.block.addSuccessor(loopExitBlock); | |
| 1091 breakLocals.add(locals); | |
| 1092 }); | |
| 1093 branchBlock.addSuccessor(loopExitBlock); | |
| 1094 open(loopExitBlock); | |
| 1095 localsHandler.endLoop(loopEntry); | |
| 1096 if (!breakLocals.isEmpty()) { | |
| 1097 breakLocals.add(savedLocals); | |
| 1098 localsHandler = savedLocals.mergeMultiple(breakLocals, loopExitBlock); | |
| 1099 } else { | |
| 1100 localsHandler = savedLocals; | |
| 1101 } | |
| 1102 } | |
| 1103 | |
| 1104 // For while loops, initializer and update are null. | |
| 1105 // The condition function must return a boolean result. | |
| 1106 // None of the functions must leave anything on the stack. | |
| 1107 handleLoop(Node loop, | |
| 1108 void initialize(), | |
| 1109 HInstruction condition(), | |
| 1110 void update(), | |
| 1111 void body()) { | |
| 1112 // Generate: | |
| 1113 // <initializer> | |
| 1114 // loop-entry: | |
| 1115 // if (!<condition>) goto loop-exit; | |
| 1116 // <body> | |
| 1117 // <updates> | |
| 1118 // goto loop-entry; | |
| 1119 // loop-exit: | |
| 1120 | |
| 1121 localsHandler.startLoop(loop); | |
| 1122 | |
| 1123 // The initializer. | |
| 1124 initialize(); | |
| 1125 assert(!isAborted()); | |
| 1126 | |
| 1127 JumpHandler jumpHandler = beginLoopHeader(loop); | |
| 1128 HBasicBlock conditionBlock = current; | |
| 1129 | |
| 1130 HInstruction conditionInstruction = condition(); | |
| 1131 HBasicBlock conditionExitBlock = | |
| 1132 close(new HLoopBranch(conditionInstruction)); | |
| 1133 | |
| 1134 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
| 1135 | |
| 1136 // The body. | |
| 1137 HBasicBlock beginBodyBlock = addNewBlock(); | |
| 1138 conditionExitBlock.addSuccessor(beginBodyBlock); | |
| 1139 open(beginBodyBlock); | |
| 1140 | |
| 1141 localsHandler.enterLoopBody(loop); | |
| 1142 hackAroundPossiblyAbortingBody(loop, body); | |
| 1143 | |
| 1144 SubGraph bodyGraph = new SubGraph(beginBodyBlock, current); | |
| 1145 HBasicBlock bodyBlock = close(new HGoto()); | |
| 1146 | |
| 1147 // Update. | |
| 1148 // We create an update block, even when we are in a while loop. There the | |
| 1149 // update block is the jump-target for continue statements. We could avoid | |
| 1150 // the creation if there is no continue, but for now we always create it. | |
| 1151 HBasicBlock updateBlock = addNewBlock(); | |
| 1152 | |
| 1153 List<LocalsHandler> continueLocals = <LocalsHandler>[]; | |
| 1154 jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) { | |
| 1155 instruction.block.addSuccessor(updateBlock); | |
| 1156 continueLocals.add(locals); | |
| 1157 }); | |
| 1158 bodyBlock.addSuccessor(updateBlock); | |
| 1159 continueLocals.add(localsHandler); | |
| 1160 | |
| 1161 open(updateBlock); | |
| 1162 | |
| 1163 localsHandler = localsHandler.mergeMultiple(continueLocals, updateBlock); | |
| 1164 | |
| 1165 HLabeledBlockInformation labelInfo; | |
| 1166 List<LabelElement> labels = jumpHandler.labels(); | |
| 1167 TargetElement target = elements[loop]; | |
| 1168 if (!labels.isEmpty()) { | |
| 1169 beginBodyBlock.labeledBlockInformation = | |
| 1170 new HLabeledBlockInformation(bodyGraph, updateBlock, | |
| 1171 jumpHandler.labels(), isContinue: true); | |
| 1172 } else if (target !== null && target.isContinueTarget) { | |
| 1173 beginBodyBlock.labeledBlockInformation = | |
| 1174 new HLabeledBlockInformation.implicit(bodyGraph, updateBlock, | |
| 1175 target, isContinue: true); | |
| 1176 } | |
| 1177 | |
| 1178 localsHandler.enterLoopUpdates(loop); | |
| 1179 | |
| 1180 update(); | |
| 1181 | |
| 1182 updateBlock = close(new HGoto()); | |
| 1183 // The back-edge completing the cycle. | |
| 1184 updateBlock.addSuccessor(conditionBlock); | |
| 1185 conditionBlock.postProcessLoopHeader(); | |
| 1186 | |
| 1187 endLoop(conditionBlock, conditionExitBlock, jumpHandler, savedLocals); | |
| 1188 } | |
| 1189 | |
| 1190 visitFor(For node) { | |
| 1191 assert(node.body !== null); | |
| 1192 void buildInitializer() { | |
| 1193 if (node.initializer === null) return; | |
| 1194 Node initializer = node.initializer; | |
| 1195 if (initializer !== null) { | |
| 1196 visit(initializer); | |
| 1197 if (initializer.asExpression() !== null) { | |
| 1198 pop(); | |
| 1199 } | |
| 1200 } | |
| 1201 } | |
| 1202 HInstruction buildCondition() { | |
| 1203 if (node.condition === null) { | |
| 1204 return graph.addConstantBool(true); | |
| 1205 } | |
| 1206 visit(node.condition); | |
| 1207 return popBoolified(); | |
| 1208 } | |
| 1209 void buildUpdate() { | |
| 1210 for (Expression expression in node.update) { | |
| 1211 visit(expression); | |
| 1212 assert(!isAborted()); | |
| 1213 // The result of the update instruction isn't used, and can just | |
| 1214 // be dropped. | |
| 1215 HInstruction updateInstruction = pop(); | |
| 1216 } | |
| 1217 } | |
| 1218 void buildBody() { | |
| 1219 visit(node.body); | |
| 1220 } | |
| 1221 handleLoop(node, buildInitializer, buildCondition, buildUpdate, buildBody); | |
| 1222 } | |
| 1223 | |
| 1224 visitWhile(While node) { | |
| 1225 HInstruction buildCondition() { | |
| 1226 visit(node.condition); | |
| 1227 return popBoolified(); | |
| 1228 } | |
| 1229 handleLoop(node, | |
| 1230 () {}, | |
| 1231 buildCondition, | |
| 1232 () {}, | |
| 1233 () { visit(node.body); }); | |
| 1234 } | |
| 1235 | |
| 1236 visitDoWhile(DoWhile node) { | |
| 1237 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
| 1238 localsHandler.startLoop(node); | |
| 1239 JumpHandler jumpHandler = beginLoopHeader(node); | |
| 1240 HBasicBlock loopEntryBlock = current; | |
| 1241 HBasicBlock bodyEntryBlock = current; | |
| 1242 TargetElement target = elements[node]; | |
| 1243 bool hasContinues = target !== null && target.isContinueTarget; | |
| 1244 if (hasContinues) { | |
| 1245 // Add extra block to hang labels on. | |
| 1246 // It doesn't currently work if they are on the same block as the | |
| 1247 // HLoopInfo. The handling of HLabeledBlockInformation will visit a | |
| 1248 // SubGraph that starts at the same block again, so the HLoopInfo is | |
| 1249 // either handled twice, or it's handled after the labeled block info, | |
| 1250 // both of which generate the wrong code. | |
| 1251 // Using a separate block is just a simple workaround. | |
| 1252 bodyEntryBlock = graph.addNewBlock(); | |
| 1253 goto(current, bodyEntryBlock); | |
| 1254 open(bodyEntryBlock); | |
| 1255 } | |
| 1256 localsHandler.enterLoopBody(node); | |
| 1257 hackAroundPossiblyAbortingBody(node, () { visit(node.body); }); | |
| 1258 | |
| 1259 // If there are no continues we could avoid the creation of the condition | |
| 1260 // block. This could also lead to a block having multiple entries and exits. | |
| 1261 HBasicBlock bodyExitBlock = close(new HGoto()); | |
| 1262 HBasicBlock conditionBlock = addNewBlock(); | |
| 1263 | |
| 1264 List<LocalsHandler> continueLocals = <LocalsHandler>[]; | |
| 1265 jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) { | |
| 1266 instruction.block.addSuccessor(conditionBlock); | |
| 1267 continueLocals.add(locals); | |
| 1268 }); | |
| 1269 bodyExitBlock.addSuccessor(conditionBlock); | |
| 1270 if (!continueLocals.isEmpty()) { | |
| 1271 continueLocals.add(localsHandler); | |
| 1272 localsHandler = savedLocals.mergeMultiple(continueLocals, conditionBlock); | |
| 1273 SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock); | |
| 1274 List<LabelElement> labels = jumpHandler.labels(); | |
| 1275 if (!labels.isEmpty()) { | |
| 1276 bodyEntryBlock.labeledBlockInformation = | |
| 1277 new HLabeledBlockInformation(bodyGraph, | |
| 1278 conditionBlock, | |
| 1279 labels, | |
| 1280 isContinue: true); | |
| 1281 } else { | |
| 1282 bodyEntryBlock.labeledBlockInformation = | |
| 1283 new HLabeledBlockInformation.implicit(bodyGraph, | |
| 1284 conditionBlock, | |
| 1285 target, | |
| 1286 isContinue: true); | |
| 1287 } | |
| 1288 } | |
| 1289 open(conditionBlock); | |
| 1290 | |
| 1291 visit(node.condition); | |
| 1292 assert(!isAborted()); | |
| 1293 conditionBlock = close(new HLoopBranch(popBoolified(), | |
| 1294 HLoopBranch.DO_WHILE_LOOP)); | |
| 1295 | |
| 1296 conditionBlock.addSuccessor(loopEntryBlock); // The back-edge. | |
| 1297 loopEntryBlock.postProcessLoopHeader(); | |
| 1298 | |
| 1299 endLoop(loopEntryBlock, conditionBlock, jumpHandler, localsHandler); | |
| 1300 jumpHandler.close(); | |
| 1301 } | |
| 1302 | |
| 1303 visitFunctionExpression(FunctionExpression node) { | |
| 1304 ClosureData nestedClosureData = compiler.builder.closureDataCache[node]; | |
| 1305 if (nestedClosureData === null) { | |
| 1306 // TODO(floitsch): we can only assume that the reason for not having a | |
| 1307 // closure data here is, because the function is inside an initializer. | |
| 1308 compiler.unimplemented("Closures inside initializers", node: node); | |
| 1309 } | |
| 1310 assert(nestedClosureData !== null); | |
| 1311 assert(nestedClosureData.closureClassElement !== null); | |
| 1312 ClassElement closureClassElement = | |
| 1313 nestedClosureData.closureClassElement; | |
| 1314 FunctionElement callElement = nestedClosureData.callElement; | |
| 1315 compiler.enqueue(new WorkItem.toCodegen(callElement, elements)); | |
| 1316 compiler.registerInstantiatedClass(closureClassElement); | |
| 1317 assert(closureClassElement.members.isEmpty()); | |
| 1318 | |
| 1319 List<HInstruction> capturedVariables = <HInstruction>[]; | |
| 1320 for (Element member in closureClassElement.backendMembers) { | |
| 1321 // The backendMembers also contains the call method(s). We are only | |
| 1322 // interested in the fields. | |
| 1323 if (member.kind == ElementKind.FIELD) { | |
| 1324 Element capturedLocal = nestedClosureData.capturedFieldMapping[member]; | |
| 1325 assert(capturedLocal != null); | |
| 1326 capturedVariables.add(localsHandler.readLocal(capturedLocal)); | |
| 1327 } | |
| 1328 } | |
| 1329 | |
| 1330 push(new HForeignNew(closureClassElement, capturedVariables)); | |
| 1331 } | |
| 1332 | |
| 1333 visitFunctionDeclaration(FunctionDeclaration node) { | |
| 1334 visit(node.function); | |
| 1335 localsHandler.updateLocal(elements[node], pop()); | |
| 1336 } | |
| 1337 | |
| 1338 visitIdentifier(Identifier node) { | |
| 1339 if (node.isThis()) { | |
| 1340 stack.add(localsHandler.readThis()); | |
| 1341 } else { | |
| 1342 compiler.internalError("SsaBuilder.visitIdentifier on non-this", | |
| 1343 node: node); | |
| 1344 } | |
| 1345 } | |
| 1346 | |
| 1347 visitIf(If node) { | |
| 1348 visit(node.condition); | |
| 1349 Function visitElse; | |
| 1350 if (node.elsePart != null) { | |
| 1351 visitElse = () { | |
| 1352 visit(node.elsePart); | |
| 1353 }; | |
| 1354 } | |
| 1355 handleIf(() => visit(node.thenPart), visitElse); | |
| 1356 } | |
| 1357 | |
| 1358 void handleIf(void visitThen(), void visitElse()) { | |
| 1359 bool hasElse = visitElse != null; | |
| 1360 HIf condition = new HIf(popBoolified(), hasElse); | |
| 1361 HBasicBlock conditionBlock = close(condition); | |
| 1362 | |
| 1363 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
| 1364 | |
| 1365 // The then part. | |
| 1366 HBasicBlock thenBlock = addNewBlock(); | |
| 1367 conditionBlock.addSuccessor(thenBlock); | |
| 1368 open(thenBlock); | |
| 1369 visitThen(); | |
| 1370 SubGraph thenGraph = new SubGraph(thenBlock, lastOpenedBlock); | |
| 1371 thenBlock = current; | |
| 1372 | |
| 1373 // Reset the locals state to the state after the condition and keep the | |
| 1374 // current state in [thenLocals]. | |
| 1375 LocalsHandler thenLocals = localsHandler; | |
| 1376 | |
| 1377 // Now the else part. | |
| 1378 localsHandler = savedLocals; | |
| 1379 HBasicBlock elseBlock = null; | |
| 1380 SubGraph elseGraph = null; | |
| 1381 if (hasElse) { | |
| 1382 elseBlock = addNewBlock(); | |
| 1383 conditionBlock.addSuccessor(elseBlock); | |
| 1384 open(elseBlock); | |
| 1385 visitElse(); | |
| 1386 elseGraph = new SubGraph(elseBlock, lastOpenedBlock); | |
| 1387 elseBlock = current; | |
| 1388 } | |
| 1389 | |
| 1390 HBasicBlock joinBlock = null; | |
| 1391 if (thenBlock !== null || elseBlock !== null || !hasElse) { | |
| 1392 joinBlock = addNewBlock(); | |
| 1393 if (thenBlock !== null) goto(thenBlock, joinBlock); | |
| 1394 if (elseBlock !== null) goto(elseBlock, joinBlock); | |
| 1395 else if (!hasElse) conditionBlock.addSuccessor(joinBlock); | |
| 1396 // If the join block has two predecessors we have to merge the | |
| 1397 // locals. The current locals is what either the | |
| 1398 // condition or the else block left us with, so we merge that | |
| 1399 // with the set of locals we got after visiting the then | |
| 1400 // part of the if. | |
| 1401 open(joinBlock); | |
| 1402 if (joinBlock.predecessors.length == 2) { | |
| 1403 localsHandler.mergeWith(thenLocals, joinBlock); | |
| 1404 } else if (thenBlock !== null) { | |
| 1405 // The only predecessor is the then branch. | |
| 1406 localsHandler = thenLocals; | |
| 1407 } | |
| 1408 } | |
| 1409 condition.blockInformation = | |
| 1410 new HIfBlockInformation(condition, thenGraph, elseGraph, joinBlock); | |
| 1411 } | |
| 1412 | |
| 1413 void visitLogicalAndOr(Send node, Operator op) { | |
| 1414 handleLogicalAndOr(() { visit(node.receiver); }, | |
| 1415 () { visit(node.argumentsNode); }, | |
| 1416 isAnd: (const SourceString("&&") == op.source)); | |
| 1417 } | |
| 1418 | |
| 1419 | |
| 1420 void handleLogicalAndOr(void left(), void right(), [bool isAnd = true]) { | |
| 1421 // x && y is transformed into: | |
| 1422 // t0 = boolify(x); | |
| 1423 // if (t0) t1 = boolify(y); | |
| 1424 // result = phi(t0, t1); | |
| 1425 // | |
| 1426 // x || y is transformed into: | |
| 1427 // t0 = boolify(x); | |
| 1428 // if (not(t0)) t1 = boolify(y); | |
| 1429 // result = phi(t0, t1); | |
| 1430 left(); | |
| 1431 HInstruction boolifiedLeft = popBoolified(); | |
| 1432 HInstruction condition; | |
| 1433 if (isAnd) { | |
| 1434 condition = boolifiedLeft; | |
| 1435 } else { | |
| 1436 condition = new HNot(boolifiedLeft); | |
| 1437 add(condition); | |
| 1438 } | |
| 1439 HIf branch = new HIf(condition, false); | |
| 1440 HBasicBlock leftBlock = close(branch); | |
| 1441 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
| 1442 | |
| 1443 HBasicBlock rightBlock = addNewBlock(); | |
| 1444 leftBlock.addSuccessor(rightBlock); | |
| 1445 open(rightBlock); | |
| 1446 | |
| 1447 right(); | |
| 1448 HInstruction boolifiedRight = popBoolified(); | |
| 1449 SubGraph rightGraph = new SubGraph(rightBlock, current); | |
| 1450 rightBlock = close(new HGoto()); | |
| 1451 | |
| 1452 HBasicBlock joinBlock = addNewBlock(); | |
| 1453 leftBlock.addSuccessor(joinBlock); | |
| 1454 rightBlock.addSuccessor(joinBlock); | |
| 1455 open(joinBlock); | |
| 1456 | |
| 1457 branch.blockInformation = | |
| 1458 new HIfBlockInformation(branch, rightGraph, null, joinBlock); | |
| 1459 | |
| 1460 localsHandler.mergeWith(savedLocals, joinBlock); | |
| 1461 HPhi result = new HPhi.manyInputs(null, | |
| 1462 <HInstruction>[boolifiedLeft, boolifiedRight]); | |
| 1463 joinBlock.addPhi(result); | |
| 1464 stack.add(result); | |
| 1465 } | |
| 1466 | |
| 1467 void visitLogicalNot(Send node) { | |
| 1468 assert(node.argumentsNode is Prefix); | |
| 1469 visit(node.receiver); | |
| 1470 HNot not = new HNot(popBoolified()); | |
| 1471 push(not); | |
| 1472 } | |
| 1473 | |
| 1474 void visitUnary(Send node, Operator op) { | |
| 1475 assert(node.argumentsNode is Prefix); | |
| 1476 visit(node.receiver); | |
| 1477 assert(op.token.kind !== PLUS_TOKEN); | |
| 1478 HInstruction operand = pop(); | |
| 1479 | |
| 1480 HInstruction target = | |
| 1481 new HStatic(interceptors.getPrefixOperatorInterceptor(op)); | |
| 1482 add(target); | |
| 1483 HInvokeUnary result; | |
| 1484 switch (op.source.stringValue) { | |
| 1485 case "-": result = new HNegate(target, operand); break; | |
| 1486 case "~": result = new HBitNot(target, operand); break; | |
| 1487 default: unreachable(); | |
| 1488 } | |
| 1489 // See if we can constant-fold right away. This avoids rewrites later on. | |
| 1490 if (operand is HConstant) { | |
| 1491 HConstant constant = operand; | |
| 1492 Constant folded = result.operation.fold(constant.constant); | |
| 1493 if (folded !== null) { | |
| 1494 stack.add(graph.addConstant(folded)); | |
| 1495 return; | |
| 1496 } | |
| 1497 } | |
| 1498 push(result); | |
| 1499 } | |
| 1500 | |
| 1501 void visitBinary(HInstruction left, Operator op, HInstruction right) { | |
| 1502 Element element = interceptors.getOperatorInterceptor(op); | |
| 1503 assert(element != null); | |
| 1504 HInstruction target = new HStatic(element); | |
| 1505 add(target); | |
| 1506 switch (op.source.stringValue) { | |
| 1507 case "+": | |
| 1508 case "++": | |
| 1509 case "+=": | |
| 1510 push(new HAdd(target, left, right)); | |
| 1511 break; | |
| 1512 case "-": | |
| 1513 case "--": | |
| 1514 case "-=": | |
| 1515 push(new HSubtract(target, left, right)); | |
| 1516 break; | |
| 1517 case "*": | |
| 1518 case "*=": | |
| 1519 push(new HMultiply(target, left, right)); | |
| 1520 break; | |
| 1521 case "/": | |
| 1522 case "/=": | |
| 1523 push(new HDivide(target, left, right)); | |
| 1524 break; | |
| 1525 case "~/": | |
| 1526 case "~/=": | |
| 1527 push(new HTruncatingDivide(target, left, right)); | |
| 1528 break; | |
| 1529 case "%": | |
| 1530 case "%=": | |
| 1531 push(new HModulo(target, left, right)); | |
| 1532 break; | |
| 1533 case "<<": | |
| 1534 case "<<=": | |
| 1535 push(new HShiftLeft(target, left, right)); | |
| 1536 break; | |
| 1537 case ">>": | |
| 1538 case ">>=": | |
| 1539 push(new HShiftRight(target, left, right)); | |
| 1540 break; | |
| 1541 case "|": | |
| 1542 case "|=": | |
| 1543 push(new HBitOr(target, left, right)); | |
| 1544 break; | |
| 1545 case "&": | |
| 1546 case "&=": | |
| 1547 push(new HBitAnd(target, left, right)); | |
| 1548 break; | |
| 1549 case "^": | |
| 1550 case "^=": | |
| 1551 push(new HBitXor(target, left, right)); | |
| 1552 break; | |
| 1553 case "==": | |
| 1554 push(new HEquals(target, left, right)); | |
| 1555 break; | |
| 1556 case "===": | |
| 1557 push(new HIdentity(target, left, right)); | |
| 1558 break; | |
| 1559 case "!==": | |
| 1560 HIdentity eq = new HIdentity(target, left, right); | |
| 1561 add(eq); | |
| 1562 push(new HNot(eq)); | |
| 1563 break; | |
| 1564 case "<": | |
| 1565 push(new HLess(target, left, right)); | |
| 1566 break; | |
| 1567 case "<=": | |
| 1568 push(new HLessEqual(target, left, right)); | |
| 1569 break; | |
| 1570 case ">": | |
| 1571 push(new HGreater(target, left, right)); | |
| 1572 break; | |
| 1573 case ">=": | |
| 1574 push(new HGreaterEqual(target, left, right)); | |
| 1575 break; | |
| 1576 case "!=": | |
| 1577 HEquals eq = new HEquals(target, left, right); | |
| 1578 add(eq); | |
| 1579 HBoolify bl = new HBoolify(eq); | |
| 1580 add(bl); | |
| 1581 push(new HNot(bl)); | |
| 1582 break; | |
| 1583 default: compiler.unimplemented("SsaBuilder.visitBinary"); | |
| 1584 } | |
| 1585 } | |
| 1586 | |
| 1587 void generateGetter(Send send, Element element) { | |
| 1588 Selector selector = elements.getSelector(send); | |
| 1589 if (Elements.isStaticOrTopLevelField(element)) { | |
| 1590 push(new HStatic(element)); | |
| 1591 if (element.kind == ElementKind.GETTER) { | |
| 1592 push(new HInvokeStatic(selector, <HInstruction>[pop()])); | |
| 1593 } | |
| 1594 } else if (Elements.isInstanceSend(send, elements)) { | |
| 1595 HInstruction receiver; | |
| 1596 if (send.receiver == null) { | |
| 1597 receiver = localsHandler.readThis(); | |
| 1598 } else { | |
| 1599 visit(send.receiver); | |
| 1600 receiver = pop(); | |
| 1601 } | |
| 1602 SourceString getterName = send.selector.asIdentifier().source; | |
| 1603 Element staticInterceptor = null; | |
| 1604 if (methodInterceptionEnabled) { | |
| 1605 staticInterceptor = interceptors.getStaticGetInterceptor(getterName); | |
| 1606 } | |
| 1607 if (staticInterceptor != null) { | |
| 1608 HStatic target = new HStatic(staticInterceptor); | |
| 1609 add(target); | |
| 1610 List<HInstruction> inputs = <HInstruction>[target, receiver]; | |
| 1611 push(new HInvokeInterceptor(selector, getterName, true, inputs)); | |
| 1612 } else { | |
| 1613 push(new HInvokeDynamicGetter(selector, null, getterName, receiver)); | |
| 1614 } | |
| 1615 } else if (Elements.isStaticOrTopLevelFunction(element)) { | |
| 1616 push(new HStatic(element)); | |
| 1617 compiler.registerGetOfStaticFunction(element); | |
| 1618 } else { | |
| 1619 stack.add(localsHandler.readLocal(element)); | |
| 1620 } | |
| 1621 } | |
| 1622 | |
| 1623 void generateSetter(SendSet send, Element element, HInstruction value) { | |
| 1624 Selector selector = elements.getSelector(send); | |
| 1625 if (Elements.isStaticOrTopLevelField(element)) { | |
| 1626 if (element.kind == ElementKind.SETTER) { | |
| 1627 HStatic target = new HStatic(element); | |
| 1628 add(target); | |
| 1629 add(new HInvokeStatic(selector, <HInstruction>[target, value])); | |
| 1630 } else { | |
| 1631 add(new HStaticStore(element, value)); | |
| 1632 } | |
| 1633 stack.add(value); | |
| 1634 } else if (element === null || Elements.isInstanceField(element)) { | |
| 1635 SourceString dartSetterName = send.selector.asIdentifier().source; | |
| 1636 HInstruction receiver; | |
| 1637 if (send.receiver == null) { | |
| 1638 receiver = localsHandler.readThis(); | |
| 1639 } else { | |
| 1640 visit(send.receiver); | |
| 1641 receiver = pop(); | |
| 1642 } | |
| 1643 Element staticInterceptor = null; | |
| 1644 if (methodInterceptionEnabled) { | |
| 1645 staticInterceptor = | |
| 1646 interceptors.getStaticSetInterceptor(dartSetterName); | |
| 1647 } | |
| 1648 if (staticInterceptor != null) { | |
| 1649 HStatic target = new HStatic(staticInterceptor); | |
| 1650 add(target); | |
| 1651 List<HInstruction> inputs = <HInstruction>[target, receiver, value]; | |
| 1652 add(new HInvokeInterceptor(selector, dartSetterName, false, inputs)); | |
| 1653 } else { | |
| 1654 add(new HInvokeDynamicSetter(selector, null, dartSetterName, | |
| 1655 receiver, value)); | |
| 1656 } | |
| 1657 stack.add(value); | |
| 1658 } else { | |
| 1659 localsHandler.updateLocal(element, value); | |
| 1660 stack.add(value); | |
| 1661 } | |
| 1662 } | |
| 1663 | |
| 1664 visitOperatorSend(node) { | |
| 1665 assert(node.selector is Operator); | |
| 1666 Operator op = node.selector; | |
| 1667 if (const SourceString("[]") == op.source) { | |
| 1668 HStatic target = new HStatic(interceptors.getIndexInterceptor()); | |
| 1669 add(target); | |
| 1670 visit(node.receiver); | |
| 1671 HInstruction receiver = pop(); | |
| 1672 visit(node.argumentsNode); | |
| 1673 HInstruction index = pop(); | |
| 1674 push(new HIndex(target, receiver, index)); | |
| 1675 } else if (const SourceString("&&") == op.source || | |
| 1676 const SourceString("||") == op.source) { | |
| 1677 visitLogicalAndOr(node, op); | |
| 1678 } else if (const SourceString("!") == op.source) { | |
| 1679 visitLogicalNot(node); | |
| 1680 } else if (node.argumentsNode is Prefix) { | |
| 1681 visitUnary(node, op); | |
| 1682 } else if (const SourceString("is") == op.source) { | |
| 1683 visit(node.receiver); | |
| 1684 HInstruction expression = pop(); | |
| 1685 Node argument = node.arguments.head; | |
| 1686 TypeAnnotation type = argument.asTypeAnnotation(); | |
| 1687 bool isNot = false; | |
| 1688 // TODO(ngeoffray): Duplicating pattern in resolver. We should | |
| 1689 // add a new kind of node. | |
| 1690 if (type == null) { | |
| 1691 type = argument.asSend().receiver; | |
| 1692 isNot = true; | |
| 1693 } | |
| 1694 HInstruction instruction = new HIs(elements[type], expression); | |
| 1695 if (isNot) { | |
| 1696 add(instruction); | |
| 1697 instruction = new HNot(instruction); | |
| 1698 } | |
| 1699 push(instruction); | |
| 1700 } else { | |
| 1701 visit(node.receiver); | |
| 1702 visit(node.argumentsNode); | |
| 1703 var right = pop(); | |
| 1704 var left = pop(); | |
| 1705 visitBinary(left, op, right); | |
| 1706 } | |
| 1707 } | |
| 1708 | |
| 1709 void addDynamicSendArgumentsToList(Send node, List<HInstruction> list) { | |
| 1710 Selector selector = elements.getSelector(node); | |
| 1711 if (selector.namedArgumentCount == 0) { | |
| 1712 addGenericSendArgumentsToList(node.arguments, list); | |
| 1713 } else { | |
| 1714 // Visit positional arguments and add them to the list. | |
| 1715 Link<Node> arguments = node.arguments; | |
| 1716 int positionalArgumentCount = selector.positionalArgumentCount; | |
| 1717 for (int i = 0; | |
| 1718 i < positionalArgumentCount; | |
| 1719 arguments = arguments.tail, i++) { | |
| 1720 visit(arguments.head); | |
| 1721 list.add(pop()); | |
| 1722 } | |
| 1723 | |
| 1724 // Visit named arguments and add them into a temporary map. | |
| 1725 Map<SourceString, HInstruction> instructions = | |
| 1726 new Map<SourceString, HInstruction>(); | |
| 1727 List<SourceString> namedArguments = selector.namedArguments; | |
| 1728 int nameIndex = 0; | |
| 1729 for (; !arguments.isEmpty(); arguments = arguments.tail) { | |
| 1730 visit(arguments.head); | |
| 1731 instructions[namedArguments[nameIndex++]] = pop(); | |
| 1732 } | |
| 1733 | |
| 1734 // Iterate through the named arguments to add them to the list | |
| 1735 // of instructions, in an order that can be shared with | |
| 1736 // selectors with the same named arguments. | |
| 1737 List<SourceString> orderedNames = selector.getOrderedNamedArguments(); | |
| 1738 for (SourceString name in orderedNames) { | |
| 1739 list.add(instructions[name]); | |
| 1740 } | |
| 1741 } | |
| 1742 } | |
| 1743 | |
| 1744 void addStaticSendArgumentsToList(Send node, | |
| 1745 FunctionElement element, | |
| 1746 List<HInstruction> list) { | |
| 1747 HInstruction compileArgument(Node argument) { | |
| 1748 visit(argument); | |
| 1749 return pop(); | |
| 1750 } | |
| 1751 | |
| 1752 HInstruction compileConstant(Element constantElement) { | |
| 1753 Constant constant = compiler.compileVariable(constantElement); | |
| 1754 return graph.addConstant(constant); | |
| 1755 } | |
| 1756 | |
| 1757 Selector selector = elements.getSelector(node); | |
| 1758 FunctionParameters parameters = element.computeParameters(compiler); | |
| 1759 bool succeeded = selector.addSendArgumentsToList(node, list, parameters, | |
| 1760 compileArgument, | |
| 1761 compileConstant); | |
| 1762 if (!succeeded) { | |
| 1763 // TODO(ngeoffray): Match the VM behavior and throw an | |
| 1764 // exception at runtime. | |
| 1765 compiler.cancel('Unimplemented non-matching static call', node: node); | |
| 1766 } | |
| 1767 } | |
| 1768 | |
| 1769 void addGenericSendArgumentsToList(Link<Node> link, List<HInstruction> list) { | |
| 1770 for (; !link.isEmpty(); link = link.tail) { | |
| 1771 visit(link.head); | |
| 1772 list.add(pop()); | |
| 1773 } | |
| 1774 } | |
| 1775 | |
| 1776 visitDynamicSend(Send node) { | |
| 1777 Selector selector = elements.getSelector(node); | |
| 1778 var inputs = <HInstruction>[]; | |
| 1779 | |
| 1780 SourceString dartMethodName; | |
| 1781 bool isNotEquals = false; | |
| 1782 if (node.isIndex && !node.arguments.tail.isEmpty()) { | |
| 1783 dartMethodName = Elements.constructOperatorName( | |
| 1784 const SourceString('operator'), | |
| 1785 const SourceString('[]=')); | |
| 1786 } else if (node.selector.asOperator() != null) { | |
| 1787 SourceString name = node.selector.asIdentifier().source; | |
| 1788 isNotEquals = name.stringValue === '!='; | |
| 1789 dartMethodName = Elements.constructOperatorName( | |
| 1790 const SourceString('operator'), | |
| 1791 name, | |
| 1792 node.argumentsNode is Prefix); | |
| 1793 } else { | |
| 1794 dartMethodName = node.selector.asIdentifier().source; | |
| 1795 } | |
| 1796 | |
| 1797 Element interceptor = null; | |
| 1798 if (methodInterceptionEnabled && node.receiver !== null) { | |
| 1799 interceptor = interceptors.getStaticInterceptor(dartMethodName, | |
| 1800 node.argumentCount()); | |
| 1801 } | |
| 1802 if (interceptor != null) { | |
| 1803 HStatic target = new HStatic(interceptor); | |
| 1804 add(target); | |
| 1805 inputs.add(target); | |
| 1806 visit(node.receiver); | |
| 1807 inputs.add(pop()); | |
| 1808 addGenericSendArgumentsToList(node.arguments, inputs); | |
| 1809 push(new HInvokeInterceptor(selector, dartMethodName, false, inputs)); | |
| 1810 return; | |
| 1811 } | |
| 1812 | |
| 1813 if (node.receiver === null) { | |
| 1814 inputs.add(localsHandler.readThis()); | |
| 1815 } else { | |
| 1816 visit(node.receiver); | |
| 1817 inputs.add(pop()); | |
| 1818 } | |
| 1819 | |
| 1820 addDynamicSendArgumentsToList(node, inputs); | |
| 1821 | |
| 1822 // The first entry in the inputs list is the receiver. | |
| 1823 push(new HInvokeDynamicMethod(selector, dartMethodName, inputs)); | |
| 1824 | |
| 1825 if (isNotEquals) { | |
| 1826 HNot not = new HNot(popBoolified()); | |
| 1827 push(not); | |
| 1828 } | |
| 1829 } | |
| 1830 | |
| 1831 visitClosureSend(Send node) { | |
| 1832 Selector selector = elements.getSelector(node); | |
| 1833 assert(node.receiver === null); | |
| 1834 Element element = elements[node]; | |
| 1835 HInstruction closureTarget; | |
| 1836 if (element === null) { | |
| 1837 visit(node.selector); | |
| 1838 closureTarget = pop(); | |
| 1839 } else { | |
| 1840 assert(Elements.isLocal(element)); | |
| 1841 closureTarget = localsHandler.readLocal(element); | |
| 1842 } | |
| 1843 var inputs = <HInstruction>[]; | |
| 1844 inputs.add(closureTarget); | |
| 1845 addDynamicSendArgumentsToList(node, inputs); | |
| 1846 push(new HInvokeClosure(selector, inputs)); | |
| 1847 } | |
| 1848 | |
| 1849 void handleForeignJs(Send node) { | |
| 1850 Link<Node> link = node.arguments; | |
| 1851 // If the invoke is on foreign code, don't visit the first | |
| 1852 // argument, which is the type, and the second argument, | |
| 1853 // which is the foreign code. | |
| 1854 if (link.isEmpty() || link.isEmpty()) { | |
| 1855 compiler.cancel('At least two arguments expected', node: node.arguments); | |
| 1856 } | |
| 1857 link = link.tail.tail; | |
| 1858 List<HInstruction> inputs = <HInstruction>[]; | |
| 1859 addGenericSendArgumentsToList(link, inputs); | |
| 1860 Node type = node.arguments.head; | |
| 1861 Node literal = node.arguments.tail.head; | |
| 1862 if (literal is !StringNode || literal.dynamic.isInterpolation) { | |
| 1863 compiler.cancel('JS code must be a string literal', node: literal); | |
| 1864 } | |
| 1865 if (type is !LiteralString) { | |
| 1866 compiler.cancel( | |
| 1867 'The type of a JS expression must be a string literal', node: type); | |
| 1868 } | |
| 1869 push(new HForeign( | |
| 1870 literal.dynamic.dartString, type.dynamic.dartString, inputs)); | |
| 1871 } | |
| 1872 | |
| 1873 void handleForeignUnintercepted(Send node) { | |
| 1874 Link<Node> link = node.arguments; | |
| 1875 if (!link.tail.isEmpty()) { | |
| 1876 compiler.cancel( | |
| 1877 'More than one expression in UNINTERCEPTED()', node: node); | |
| 1878 } | |
| 1879 Expression expression = link.head; | |
| 1880 disableMethodInterception(); | |
| 1881 visit(expression); | |
| 1882 enableMethodInterception(); | |
| 1883 } | |
| 1884 | |
| 1885 void handleForeignJsHasEquals(Send node) { | |
| 1886 List<HInstruction> inputs = <HInstruction>[]; | |
| 1887 if (!node.arguments.tail.isEmpty()) { | |
| 1888 compiler.cancel( | |
| 1889 'More than one expression in JS_HAS_EQUALS()', node: node); | |
| 1890 } | |
| 1891 addGenericSendArgumentsToList(node.arguments, inputs); | |
| 1892 String name = compiler.namer.instanceMethodName( | |
| 1893 currentLibrary, Namer.OPERATOR_EQUALS, 1); | |
| 1894 push(new HForeign(new DartString.literal('!!#.$name'), | |
| 1895 const LiteralDartString('bool'), | |
| 1896 inputs)); | |
| 1897 } | |
| 1898 | |
| 1899 void handleForeignJsCurrentIsolate(Send node) { | |
| 1900 if (!node.arguments.isEmpty()) { | |
| 1901 compiler.cancel( | |
| 1902 'Too many arguments to JS_CURRENT_ISOLATE', node: node); | |
| 1903 } | |
| 1904 | |
| 1905 if (!compiler.hasIsolateSupport()) { | |
| 1906 // If the isolate library is not used, we just generate code | |
| 1907 // to fetch the Leg's current isolate. | |
| 1908 String name = compiler.namer.CURRENT_ISOLATE; | |
| 1909 push(new HForeign(new DartString.literal(name), | |
| 1910 const LiteralDartString('var'), | |
| 1911 <HInstruction>[])); | |
| 1912 } else { | |
| 1913 // Call a helper method from the isolate library. The isolate | |
| 1914 // library uses its own isolate structure, that encapsulates | |
| 1915 // Leg's isolate. | |
| 1916 Element element = compiler.isolateLibrary.find( | |
| 1917 const SourceString('_currentIsolate')); | |
| 1918 if (element === null) { | |
| 1919 compiler.cancel( | |
| 1920 'Isolate library and compiler mismatch', node: node); | |
| 1921 } | |
| 1922 HStatic target = new HStatic(element); | |
| 1923 add(target); | |
| 1924 push(new HInvokeStatic(Selector.INVOCATION_0, | |
| 1925 <HInstruction>[target])); | |
| 1926 } | |
| 1927 } | |
| 1928 | |
| 1929 void handleForeignJsCallInIsolate(Send node) { | |
| 1930 Link<Node> link = node.arguments; | |
| 1931 if (!compiler.hasIsolateSupport()) { | |
| 1932 // If the isolate library is not used, we just invoke the | |
| 1933 // closure. | |
| 1934 visit(link.tail.head); | |
| 1935 push(new HInvokeClosure(Selector.INVOCATION_0, | |
| 1936 <HInstruction>[pop()])); | |
| 1937 } else { | |
| 1938 // Call a helper method from the isolate library. | |
| 1939 Element element = compiler.isolateLibrary.find( | |
| 1940 const SourceString('_callInIsolate')); | |
| 1941 if (element === null) { | |
| 1942 compiler.cancel( | |
| 1943 'Isolate library and compiler mismatch', node: node); | |
| 1944 } | |
| 1945 HStatic target = new HStatic(element); | |
| 1946 add(target); | |
| 1947 List<HInstruction> inputs = <HInstruction>[target]; | |
| 1948 addGenericSendArgumentsToList(link, inputs); | |
| 1949 push(new HInvokeStatic(Selector.INVOCATION_0, inputs)); | |
| 1950 } | |
| 1951 } | |
| 1952 | |
| 1953 void handleForeignDartClosureToJs(Send node) { | |
| 1954 if (node.arguments.isEmpty() || !node.arguments.tail.isEmpty()) { | |
| 1955 compiler.cancel('Exactly one argument required', node: node.arguments); | |
| 1956 } | |
| 1957 Node closure = node.arguments.head; | |
| 1958 Element element = elements[closure]; | |
| 1959 if (!Elements.isStaticOrTopLevelFunction(element)) { | |
| 1960 compiler.cancel( | |
| 1961 'JS_TO_CLOSURE requires a static or top-level method', | |
| 1962 node: closure); | |
| 1963 } | |
| 1964 FunctionElement function = element; | |
| 1965 FunctionParameters parameters = element.computeParameters(compiler); | |
| 1966 if (parameters.optionalParameterCount !== 0) { | |
| 1967 compiler.cancel( | |
| 1968 'JS_TO_CLOSURE does not handle closure with optional parameters', | |
| 1969 node: closure); | |
| 1970 } | |
| 1971 visit(closure); | |
| 1972 List<HInstruction> inputs = <HInstruction>[pop()]; | |
| 1973 String invocationName = compiler.namer.closureInvocationName( | |
| 1974 new Selector(SelectorKind.INVOCATION, | |
| 1975 parameters.requiredParameterCount)); | |
| 1976 push(new HForeign(new DartString.literal('#.$invocationName'), | |
| 1977 const LiteralDartString('var'), | |
| 1978 inputs)); | |
| 1979 } | |
| 1980 | |
| 1981 handleForeignSend(Send node) { | |
| 1982 Element element = elements[node]; | |
| 1983 if (element.name == const SourceString('JS')) { | |
| 1984 handleForeignJs(node); | |
| 1985 } else if (element.name == const SourceString('UNINTERCEPTED')) { | |
| 1986 handleForeignUnintercepted(node); | |
| 1987 } else if (element.name == const SourceString('JS_HAS_EQUALS')) { | |
| 1988 handleForeignJsHasEquals(node); | |
| 1989 } else if (element.name == const SourceString('JS_CURRENT_ISOLATE')) { | |
| 1990 handleForeignJsCurrentIsolate(node); | |
| 1991 } else if (element.name == const SourceString('JS_CALL_IN_ISOLATE')) { | |
| 1992 handleForeignJsCallInIsolate(node); | |
| 1993 } else if (element.name == const SourceString('DART_CLOSURE_TO_JS')) { | |
| 1994 handleForeignDartClosureToJs(node); | |
| 1995 } else if (element.name == const SourceString('native')) { | |
| 1996 native.handleSsaNative(this, node); | |
| 1997 } else { | |
| 1998 throw "Unknown foreign: ${node.selector}"; | |
| 1999 } | |
| 2000 } | |
| 2001 | |
| 2002 visitSuperSend(Send node) { | |
| 2003 Selector selector = elements.getSelector(node); | |
| 2004 Element element = elements[node]; | |
| 2005 if (element === null) { | |
| 2006 ClassElement cls = work.element.getEnclosingClass(); | |
| 2007 element = cls.lookupSuperMember(Compiler.NO_SUCH_METHOD); | |
| 2008 HStatic target = new HStatic(element); | |
| 2009 add(target); | |
| 2010 HInstruction self = localsHandler.readThis(); | |
| 2011 Identifier identifier = node.selector.asIdentifier(); | |
| 2012 String name = identifier.source.slowToString(); | |
| 2013 // TODO(ahe): Add the arguments to this list. | |
| 2014 push(new HLiteralList([], true)); | |
| 2015 var inputs = <HInstruction>[ | |
| 2016 target, | |
| 2017 self, | |
| 2018 graph.addConstantString(new DartString.literal(name)), | |
| 2019 pop()]; | |
| 2020 push(new HInvokeSuper(const Selector(SelectorKind.INVOCATION, 2), | |
| 2021 inputs)); | |
| 2022 return; | |
| 2023 } | |
| 2024 HInstruction target = new HStatic(element); | |
| 2025 HInstruction context = localsHandler.readThis(); | |
| 2026 add(target); | |
| 2027 var inputs = <HInstruction>[target, context]; | |
| 2028 if (element.kind == ElementKind.FUNCTION || | |
| 2029 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) { | |
| 2030 addStaticSendArgumentsToList(node, element, inputs); | |
| 2031 push(new HInvokeSuper(selector, inputs)); | |
| 2032 } else { | |
| 2033 target = new HInvokeSuper(Selector.GETTER, inputs); | |
| 2034 add(target); | |
| 2035 inputs = <HInstruction>[target]; | |
| 2036 addDynamicSendArgumentsToList(node, inputs); | |
| 2037 push(new HInvokeClosure(selector, inputs)); | |
| 2038 } | |
| 2039 } | |
| 2040 | |
| 2041 visitStaticSend(Send node) { | |
| 2042 Selector selector = elements.getSelector(node); | |
| 2043 Element element = elements[node]; | |
| 2044 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR) { | |
| 2045 compiler.resolver.resolveMethodElement(element); | |
| 2046 FunctionElement functionElement = element; | |
| 2047 element = functionElement.defaultImplementation; | |
| 2048 } | |
| 2049 HInstruction target = new HStatic(element); | |
| 2050 add(target); | |
| 2051 var inputs = <HInstruction>[]; | |
| 2052 inputs.add(target); | |
| 2053 if (element.kind == ElementKind.FUNCTION || | |
| 2054 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) { | |
| 2055 addStaticSendArgumentsToList(node, element, inputs); | |
| 2056 push(new HInvokeStatic(selector, inputs)); | |
| 2057 } else { | |
| 2058 if (element.kind == ElementKind.GETTER) { | |
| 2059 target = new HInvokeStatic(Selector.GETTER, inputs); | |
| 2060 add(target); | |
| 2061 inputs = <HInstruction>[target]; | |
| 2062 } | |
| 2063 addDynamicSendArgumentsToList(node, inputs); | |
| 2064 push(new HInvokeClosure(selector, inputs)); | |
| 2065 } | |
| 2066 } | |
| 2067 | |
| 2068 visitSend(Send node) { | |
| 2069 if (node.isSuperCall) { | |
| 2070 if (node.isPropertyAccess) { | |
| 2071 compiler.unimplemented('super property read', node: node); | |
| 2072 } | |
| 2073 visitSuperSend(node); | |
| 2074 } else if (node.selector is Operator && methodInterceptionEnabled) { | |
| 2075 visitOperatorSend(node); | |
| 2076 } else if (node.isPropertyAccess) { | |
| 2077 generateGetter(node, elements[node]); | |
| 2078 } else if (Elements.isClosureSend(node, elements)) { | |
| 2079 visitClosureSend(node); | |
| 2080 } else { | |
| 2081 Element element = elements[node]; | |
| 2082 if (element === null) { | |
| 2083 // Example: f() with 'f' unbound. | |
| 2084 // This can only happen inside an instance method. | |
| 2085 visitDynamicSend(node); | |
| 2086 } else if (element.kind == ElementKind.CLASS) { | |
| 2087 compiler.internalError("Cannot generate code for send", node: node); | |
| 2088 } else if (element.isInstanceMember()) { | |
| 2089 // Example: f() with 'f' bound to instance method. | |
| 2090 visitDynamicSend(node); | |
| 2091 } else if (element.kind === ElementKind.FOREIGN) { | |
| 2092 handleForeignSend(node); | |
| 2093 } else if (!element.isInstanceMember()) { | |
| 2094 // Example: A.f() or f() with 'f' bound to a static function. | |
| 2095 // Also includes new A() or new A.named() which is treated like a | |
| 2096 // static call to a factory. | |
| 2097 visitStaticSend(node); | |
| 2098 } else { | |
| 2099 compiler.internalError("Cannot generate code for send", node: node); | |
| 2100 } | |
| 2101 } | |
| 2102 } | |
| 2103 | |
| 2104 visitNewExpression(NewExpression node) { | |
| 2105 if (node.isConst()) { | |
| 2106 ConstantHandler handler = compiler.constantHandler; | |
| 2107 Constant constant = handler.compileNodeWithDefinitions(node, elements); | |
| 2108 stack.add(graph.addConstant(constant)); | |
| 2109 } else { | |
| 2110 visitSend(node.send); | |
| 2111 } | |
| 2112 } | |
| 2113 | |
| 2114 visitSendSet(SendSet node) { | |
| 2115 Operator op = node.assignmentOperator; | |
| 2116 if (node.isSuperCall) { | |
| 2117 compiler.unimplemented('super property store', node: node); | |
| 2118 } else if (node.isIndex) { | |
| 2119 if (!methodInterceptionEnabled) { | |
| 2120 assert(op.source.stringValue === '='); | |
| 2121 visitDynamicSend(node); | |
| 2122 } else { | |
| 2123 HStatic target = new HStatic( | |
| 2124 interceptors.getIndexAssignmentInterceptor()); | |
| 2125 add(target); | |
| 2126 visit(node.receiver); | |
| 2127 HInstruction receiver = pop(); | |
| 2128 visit(node.argumentsNode); | |
| 2129 if (const SourceString("=") == op.source) { | |
| 2130 HInstruction value = pop(); | |
| 2131 HInstruction index = pop(); | |
| 2132 add(new HIndexAssign(target, receiver, index, value)); | |
| 2133 stack.add(value); | |
| 2134 } else { | |
| 2135 HInstruction value; | |
| 2136 HInstruction index; | |
| 2137 bool isCompoundAssignment = op.source.stringValue.endsWith('='); | |
| 2138 // Compound assignments are considered as being prefix. | |
| 2139 bool isPrefix = !node.isPostfix; | |
| 2140 Element getter = elements[node.selector]; | |
| 2141 if (isCompoundAssignment) { | |
| 2142 value = pop(); | |
| 2143 index = pop(); | |
| 2144 } else { | |
| 2145 index = pop(); | |
| 2146 value = graph.addConstantInt(1); | |
| 2147 } | |
| 2148 HStatic indexMethod = new HStatic(interceptors.getIndexInterceptor()); | |
| 2149 add(indexMethod); | |
| 2150 HInstruction left = new HIndex(indexMethod, receiver, index); | |
| 2151 add(left); | |
| 2152 Element opElement = elements[op]; | |
| 2153 visitBinary(left, op, value); | |
| 2154 value = pop(); | |
| 2155 HInstruction assign = new HIndexAssign( | |
| 2156 target, receiver, index, value); | |
| 2157 add(assign); | |
| 2158 if (isPrefix) { | |
| 2159 stack.add(value); | |
| 2160 } else { | |
| 2161 stack.add(left); | |
| 2162 } | |
| 2163 } | |
| 2164 } | |
| 2165 } else if (const SourceString("=") == op.source) { | |
| 2166 Element element = elements[node]; | |
| 2167 Link<Node> link = node.arguments; | |
| 2168 assert(!link.isEmpty() && link.tail.isEmpty()); | |
| 2169 visit(link.head); | |
| 2170 HInstruction value = pop(); | |
| 2171 generateSetter(node, element, value); | |
| 2172 } else if (op.source.stringValue === "is") { | |
| 2173 compiler.internalError("is-operator as SendSet", node: op); | |
| 2174 } else { | |
| 2175 assert(const SourceString("++") == op.source || | |
| 2176 const SourceString("--") == op.source || | |
| 2177 node.assignmentOperator.source.stringValue.endsWith("=")); | |
| 2178 Element element = elements[node]; | |
| 2179 bool isCompoundAssignment = !node.arguments.isEmpty(); | |
| 2180 bool isPrefix = !node.isPostfix; // Compound assignments are prefix. | |
| 2181 generateGetter(node, elements[node.selector]); | |
| 2182 HInstruction left = pop(); | |
| 2183 HInstruction right; | |
| 2184 if (isCompoundAssignment) { | |
| 2185 visit(node.argumentsNode); | |
| 2186 right = pop(); | |
| 2187 } else { | |
| 2188 right = graph.addConstantInt(1); | |
| 2189 } | |
| 2190 visitBinary(left, op, right); | |
| 2191 HInstruction operation = pop(); | |
| 2192 assert(operation !== null); | |
| 2193 generateSetter(node, element, operation); | |
| 2194 if (!isPrefix) { | |
| 2195 pop(); | |
| 2196 stack.add(left); | |
| 2197 } | |
| 2198 } | |
| 2199 } | |
| 2200 | |
| 2201 void visitLiteralInt(LiteralInt node) { | |
| 2202 stack.add(graph.addConstantInt(node.value)); | |
| 2203 } | |
| 2204 | |
| 2205 void visitLiteralDouble(LiteralDouble node) { | |
| 2206 stack.add(graph.addConstantDouble(node.value)); | |
| 2207 } | |
| 2208 | |
| 2209 void visitLiteralBool(LiteralBool node) { | |
| 2210 stack.add(graph.addConstantBool(node.value)); | |
| 2211 } | |
| 2212 | |
| 2213 void visitLiteralString(LiteralString node) { | |
| 2214 stack.add(graph.addConstantString(node.dartString)); | |
| 2215 } | |
| 2216 | |
| 2217 void visitStringJuxtaposition(StringJuxtaposition node) { | |
| 2218 if (!node.isInterpolation) { | |
| 2219 // This is a simple string with no interpolations. | |
| 2220 stack.add(graph.addConstantString(node.dartString)); | |
| 2221 return; | |
| 2222 } | |
| 2223 int offset = node.getBeginToken().charOffset; | |
| 2224 StringBuilderVisitor stringBuilder = | |
| 2225 new StringBuilderVisitor(this, offset); | |
| 2226 stringBuilder.visit(node); | |
| 2227 stack.add(stringBuilder.result()); | |
| 2228 } | |
| 2229 | |
| 2230 void visitLiteralNull(LiteralNull node) { | |
| 2231 stack.add(graph.addConstantNull()); | |
| 2232 } | |
| 2233 | |
| 2234 visitNodeList(NodeList node) { | |
| 2235 for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) { | |
| 2236 if (isAborted()) { | |
| 2237 compiler.reportWarning(link.head, 'dead code'); | |
| 2238 } else { | |
| 2239 visit(link.head); | |
| 2240 } | |
| 2241 } | |
| 2242 } | |
| 2243 | |
| 2244 void visitParenthesizedExpression(ParenthesizedExpression node) { | |
| 2245 visit(node.expression); | |
| 2246 } | |
| 2247 | |
| 2248 visitOperator(Operator node) { | |
| 2249 // Operators are intercepted in their surrounding Send nodes. | |
| 2250 unreachable(); | |
| 2251 } | |
| 2252 | |
| 2253 visitReturn(Return node) { | |
| 2254 HInstruction value; | |
| 2255 if (node.expression === null) { | |
| 2256 value = graph.addConstantNull(); | |
| 2257 } else { | |
| 2258 visit(node.expression); | |
| 2259 value = pop(); | |
| 2260 } | |
| 2261 close(new HReturn(value)).addSuccessor(graph.exit); | |
| 2262 } | |
| 2263 | |
| 2264 visitThrow(Throw node) { | |
| 2265 if (node.expression === null) { | |
| 2266 HInstruction exception = rethrowableException; | |
| 2267 if (exception === null) { | |
| 2268 exception = graph.addConstantNull(); | |
| 2269 compiler.reportError(node, | |
| 2270 'throw without expression outside catch block'); | |
| 2271 } | |
| 2272 close(new HThrow(exception, isRethrow: true)); | |
| 2273 } else { | |
| 2274 visit(node.expression); | |
| 2275 close(new HThrow(pop())); | |
| 2276 } | |
| 2277 } | |
| 2278 | |
| 2279 visitTypeAnnotation(TypeAnnotation node) { | |
| 2280 compiler.internalError('visiting type annotation in SSA builder', | |
| 2281 node: node); | |
| 2282 } | |
| 2283 | |
| 2284 visitVariableDefinitions(VariableDefinitions node) { | |
| 2285 for (Link<Node> link = node.definitions.nodes; | |
| 2286 !link.isEmpty(); | |
| 2287 link = link.tail) { | |
| 2288 Node definition = link.head; | |
| 2289 if (definition is Identifier) { | |
| 2290 HInstruction initialValue = graph.addConstantNull(); | |
| 2291 localsHandler.updateLocal(elements[definition], initialValue); | |
| 2292 } else { | |
| 2293 assert(definition is SendSet); | |
| 2294 visitSendSet(definition); | |
| 2295 pop(); // Discard value. | |
| 2296 } | |
| 2297 } | |
| 2298 } | |
| 2299 | |
| 2300 visitLiteralList(LiteralList node) { | |
| 2301 List<HInstruction> inputs = <HInstruction>[]; | |
| 2302 for (Link<Node> link = node.elements.nodes; | |
| 2303 !link.isEmpty(); | |
| 2304 link = link.tail) { | |
| 2305 visit(link.head); | |
| 2306 inputs.add(pop()); | |
| 2307 } | |
| 2308 push(new HLiteralList(inputs, node.isConst())); | |
| 2309 } | |
| 2310 | |
| 2311 visitConditional(Conditional node) { | |
| 2312 visit(node.condition); | |
| 2313 HIf condition = new HIf(popBoolified(), true); | |
| 2314 HBasicBlock conditionBlock = close(condition); | |
| 2315 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
| 2316 | |
| 2317 HBasicBlock thenBlock = addNewBlock(); | |
| 2318 conditionBlock.addSuccessor(thenBlock); | |
| 2319 open(thenBlock); | |
| 2320 visit(node.thenExpression); | |
| 2321 HInstruction thenInstruction = pop(); | |
| 2322 SubGraph thenGraph = new SubGraph(thenBlock, current); | |
| 2323 thenBlock = close(new HGoto()); | |
| 2324 LocalsHandler thenLocals = localsHandler; | |
| 2325 localsHandler = savedLocals; | |
| 2326 | |
| 2327 HBasicBlock elseBlock = addNewBlock(); | |
| 2328 conditionBlock.addSuccessor(elseBlock); | |
| 2329 open(elseBlock); | |
| 2330 visit(node.elseExpression); | |
| 2331 HInstruction elseInstruction = pop(); | |
| 2332 SubGraph elseGraph = new SubGraph(elseBlock, current); | |
| 2333 elseBlock = close(new HGoto()); | |
| 2334 | |
| 2335 HBasicBlock joinBlock = addNewBlock(); | |
| 2336 thenBlock.addSuccessor(joinBlock); | |
| 2337 elseBlock.addSuccessor(joinBlock); | |
| 2338 condition.blockInformation = | |
| 2339 new HIfBlockInformation(condition, thenGraph, elseGraph, joinBlock); | |
| 2340 open(joinBlock); | |
| 2341 | |
| 2342 localsHandler.mergeWith(thenLocals, joinBlock); | |
| 2343 HPhi phi = new HPhi.manyInputs(null, | |
| 2344 <HInstruction>[thenInstruction, elseInstruction]); | |
| 2345 joinBlock.addPhi(phi); | |
| 2346 stack.add(phi); | |
| 2347 } | |
| 2348 | |
| 2349 visitStringInterpolation(StringInterpolation node) { | |
| 2350 int offset = node.getBeginToken().charOffset; | |
| 2351 StringBuilderVisitor stringBuilder = | |
| 2352 new StringBuilderVisitor(this, offset); | |
| 2353 stringBuilder.visit(node); | |
| 2354 stack.add(stringBuilder.result()); | |
| 2355 } | |
| 2356 | |
| 2357 visitStringInterpolationPart(StringInterpolationPart node) { | |
| 2358 // The parts are iterated in visitStringInterpolation. | |
| 2359 unreachable(); | |
| 2360 } | |
| 2361 | |
| 2362 visitEmptyStatement(EmptyStatement node) { | |
| 2363 // Do nothing, empty statement. | |
| 2364 } | |
| 2365 | |
| 2366 visitModifiers(Modifiers node) { | |
| 2367 compiler.unimplemented('SsaBuilder.visitModifiers', node: node); | |
| 2368 } | |
| 2369 | |
| 2370 visitBreakStatement(BreakStatement node) { | |
| 2371 work.allowSpeculativeOptimization = false; | |
| 2372 assert(!isAborted()); | |
| 2373 TargetElement target = elements[node]; | |
| 2374 assert(target !== null); | |
| 2375 JumpHandler handler = jumpTargets[target]; | |
| 2376 assert(handler !== null); | |
| 2377 if (node.target === null) { | |
| 2378 handler.generateBreak(); | |
| 2379 } else { | |
| 2380 LabelElement label = elements[node.target]; | |
| 2381 handler.generateBreak(label); | |
| 2382 } | |
| 2383 } | |
| 2384 | |
| 2385 visitContinueStatement(ContinueStatement node) { | |
| 2386 work.allowSpeculativeOptimization = false; | |
| 2387 TargetElement target = elements[node]; | |
| 2388 assert(target !== null); | |
| 2389 JumpHandler handler = jumpTargets[target]; | |
| 2390 assert(handler !== null); | |
| 2391 if (node.target === null) { | |
| 2392 handler.generateContinue(); | |
| 2393 } else { | |
| 2394 LabelElement label = elements[node.target]; | |
| 2395 handler.generateContinue(label); | |
| 2396 } | |
| 2397 } | |
| 2398 | |
| 2399 /** | |
| 2400 * Creates a [JumpHandler] for a statement. The node must be a jump | |
| 2401 * target. If there are no breaks or continues targeting the statement, | |
| 2402 * a special "null handler" is returned. | |
| 2403 */ | |
| 2404 JumpHandler createJumpHandler(Statement node) { | |
| 2405 TargetElement element = elements[node]; | |
| 2406 if (element === null || element.statement !== node) { | |
| 2407 // No breaks or continues to this node. | |
| 2408 return const NullJumpHandler(); | |
| 2409 } | |
| 2410 return new JumpHandler(this, element); | |
| 2411 } | |
| 2412 | |
| 2413 visitForInStatement(ForInStatement node) { | |
| 2414 // Generate a structure equivalent to: | |
| 2415 // Iterator<E> $iter = <iterable>.iterator() | |
| 2416 // while ($iter.hasNext()) { | |
| 2417 // E <declaredIdentifier> = $iter.next(); | |
| 2418 // <body> | |
| 2419 // } | |
| 2420 | |
| 2421 // All the generated calls are to zero-argument functions. | |
| 2422 Selector selector = Selector.INVOCATION_0; | |
| 2423 // The iterator is shared between initializer, condition and body. | |
| 2424 HInstruction iterator; | |
| 2425 void buildInitializer() { | |
| 2426 SourceString iteratorName = const SourceString("iterator"); | |
| 2427 Element interceptor = interceptors.getStaticInterceptor(iteratorName, 0); | |
| 2428 assert(interceptor != null); | |
| 2429 HStatic target = new HStatic(interceptor); | |
| 2430 add(target); | |
| 2431 visit(node.expression); | |
| 2432 List<HInstruction> inputs = <HInstruction>[target, pop()]; | |
| 2433 iterator = new HInvokeInterceptor(selector, iteratorName, false, inputs); | |
| 2434 add(iterator); | |
| 2435 } | |
| 2436 HInstruction buildCondition() { | |
| 2437 push(new HInvokeDynamicMethod( | |
| 2438 selector, const SourceString('hasNext'), <HInstruction>[iterator])); | |
| 2439 return popBoolified(); | |
| 2440 } | |
| 2441 void buildBody() { | |
| 2442 push(new HInvokeDynamicMethod( | |
| 2443 selector, const SourceString('next'), <HInstruction>[iterator])); | |
| 2444 | |
| 2445 Element variable; | |
| 2446 if (node.declaredIdentifier.asSend() !== null) { | |
| 2447 variable = elements[node.declaredIdentifier]; | |
| 2448 } else { | |
| 2449 assert(node.declaredIdentifier.asVariableDefinitions() !== null); | |
| 2450 VariableDefinitions variableDefinitions = node.declaredIdentifier; | |
| 2451 variable = elements[variableDefinitions.definitions.nodes.head]; | |
| 2452 } | |
| 2453 localsHandler.updateLocal(variable, pop()); | |
| 2454 | |
| 2455 visit(node.body); | |
| 2456 } | |
| 2457 handleLoop(node, buildInitializer, buildCondition, () {}, buildBody); | |
| 2458 } | |
| 2459 | |
| 2460 visitLabeledStatement(LabeledStatement node) { | |
| 2461 Statement body = node.getBody(); | |
| 2462 if (body is Loop || body is SwitchStatement) { | |
| 2463 // Loops and switches handle their own labels. | |
| 2464 visit(body); | |
| 2465 return; | |
| 2466 } | |
| 2467 // Non-loop statements can only be break targets, not continue targets. | |
| 2468 TargetElement targetElement = elements[body]; | |
| 2469 if (targetElement === null || targetElement.statement !== body) { | |
| 2470 // Labeled statements with no element on the body have no breaks. | |
| 2471 // A different target statement only happens if the body is itself | |
| 2472 // a break or continue for a different target. In that case, this | |
| 2473 // label is also always unused. | |
| 2474 visit(body); | |
| 2475 return; | |
| 2476 } | |
| 2477 LocalsHandler beforeLocals = new LocalsHandler.from(localsHandler); | |
| 2478 assert(targetElement.isBreakTarget); | |
| 2479 JumpHandler handler = new JumpHandler(this, targetElement); | |
| 2480 // Introduce a new basic block. | |
| 2481 HBasicBlock entryBlock = graph.addNewBlock(); | |
| 2482 goto(current, entryBlock); | |
| 2483 open(entryBlock); | |
| 2484 hackAroundPossiblyAbortingBody(node, () { visit(body); }); | |
| 2485 SubGraph bodyGraph = new SubGraph(entryBlock, lastOpenedBlock); | |
| 2486 | |
| 2487 HBasicBlock joinBlock = graph.addNewBlock(); | |
| 2488 List<LocalsHandler> breakLocals = <LocalsHandler>[]; | |
| 2489 handler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) { | |
| 2490 breakInstruction.block.addSuccessor(joinBlock); | |
| 2491 breakLocals.add(locals); | |
| 2492 }); | |
| 2493 bool hasBreak = breakLocals.length > 0; | |
| 2494 if (!isAborted()) { | |
| 2495 goto(current, joinBlock); | |
| 2496 breakLocals.add(localsHandler); | |
| 2497 } | |
| 2498 open(joinBlock); | |
| 2499 localsHandler = beforeLocals.mergeMultiple(breakLocals, joinBlock); | |
| 2500 | |
| 2501 if (hasBreak) { | |
| 2502 // There was at least one reachable break, so the label is needed. | |
| 2503 HLabeledBlockInformation blockInfo = | |
| 2504 new HLabeledBlockInformation(bodyGraph, joinBlock, handler.labels()); | |
| 2505 entryBlock.labeledBlockInformation = blockInfo; | |
| 2506 } | |
| 2507 handler.close(); | |
| 2508 } | |
| 2509 | |
| 2510 visitLiteralMap(LiteralMap node) { | |
| 2511 List<HInstruction> inputs = <HInstruction>[]; | |
| 2512 for (Link<Node> link = node.entries.nodes; | |
| 2513 !link.isEmpty(); | |
| 2514 link = link.tail) { | |
| 2515 visit(link.head); | |
| 2516 inputs.addLast(pop()); | |
| 2517 inputs.addLast(pop()); | |
| 2518 } | |
| 2519 HLiteralList keyValuePairs = new HLiteralList(inputs, node.isConst()); | |
| 2520 HStatic mapMaker = new HStatic(interceptors.getMapMaker()); | |
| 2521 add(keyValuePairs); | |
| 2522 add(mapMaker); | |
| 2523 inputs = <HInstruction>[mapMaker, keyValuePairs]; | |
| 2524 push(new HInvokeStatic(Selector.INVOCATION_1, inputs)); | |
| 2525 } | |
| 2526 | |
| 2527 visitLiteralMapEntry(LiteralMapEntry node) { | |
| 2528 visit(node.value); | |
| 2529 visit(node.key); | |
| 2530 } | |
| 2531 | |
| 2532 visitNamedArgument(NamedArgument node) { | |
| 2533 visit(node.expression); | |
| 2534 } | |
| 2535 | |
| 2536 visitSwitchStatement(SwitchStatement node) { | |
| 2537 work.allowSpeculativeOptimization = false; | |
| 2538 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
| 2539 HBasicBlock startBlock = graph.addNewBlock(); | |
| 2540 goto(current, startBlock); | |
| 2541 open(startBlock); | |
| 2542 visit(node.expression); | |
| 2543 HInstruction expression = pop(); | |
| 2544 if (node.cases.isEmpty()) { | |
| 2545 return; | |
| 2546 } | |
| 2547 Link<Node> cases = node.cases.nodes; | |
| 2548 | |
| 2549 JumpHandler jumpHandler = createJumpHandler(node); | |
| 2550 | |
| 2551 buildSwitchCases(cases, expression); | |
| 2552 | |
| 2553 HBasicBlock lastBlock = lastOpenedBlock; | |
| 2554 | |
| 2555 // Create merge block for break targets. | |
| 2556 HBasicBlock joinBlock = new HBasicBlock(); | |
| 2557 List<LocalsHandler> caseLocals = <LocalsHandler>[]; | |
| 2558 jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) { | |
| 2559 instruction.block.addSuccessor(joinBlock); | |
| 2560 caseLocals.add(locals); | |
| 2561 }); | |
| 2562 if (!isAborted()) { | |
| 2563 // The current flow is only aborted if the switch has a default that | |
| 2564 // aborts (all previous cases must abort, and if there is no default, | |
| 2565 // it's possible to miss all the cases). | |
| 2566 caseLocals.add(localsHandler); | |
| 2567 goto(current, joinBlock); | |
| 2568 } | |
| 2569 if (caseLocals.length != 0) { | |
| 2570 graph.addBlock(joinBlock); | |
| 2571 open(joinBlock); | |
| 2572 if (caseLocals.length == 1) { | |
| 2573 localsHandler = caseLocals[0]; | |
| 2574 } else { | |
| 2575 localsHandler = savedLocals.mergeMultiple(caseLocals, joinBlock); | |
| 2576 } | |
| 2577 } else { | |
| 2578 // The joinblock is not used. | |
| 2579 joinBlock = null; | |
| 2580 } | |
| 2581 startBlock.labeledBlockInformation = new HLabeledBlockInformation.implicit( | |
| 2582 new SubGraph(startBlock, lastBlock), | |
| 2583 joinBlock, | |
| 2584 elements[node]); | |
| 2585 jumpHandler.close(); | |
| 2586 } | |
| 2587 | |
| 2588 | |
| 2589 // Recursively build an if/else structure to match the cases. | |
| 2590 buildSwitchCases(Link<Node> cases, HInstruction expression) { | |
| 2591 SwitchCase node = cases.head; | |
| 2592 | |
| 2593 // Called for the statements on all but the last case block. | |
| 2594 // Ensures that a user expecting a fallthrough gets an error. | |
| 2595 void visitStatementsAndAbort() { | |
| 2596 visit(node.statements); | |
| 2597 if (!isAborted()) { | |
| 2598 compiler.reportWarning(node, 'Missing break at end of switch case'); | |
| 2599 Element element = | |
| 2600 compiler.findHelper(const SourceString("getFallThroughError")); | |
| 2601 push(new HStatic(element)); | |
| 2602 HInstruction error = new HInvokeStatic( | |
| 2603 Selector.INVOCATION_0, <HInstruction>[pop()]); | |
| 2604 add(error); | |
| 2605 close(new HThrow(error)); | |
| 2606 } | |
| 2607 } | |
| 2608 | |
| 2609 Link<Node> expressions = node.expressions.nodes; | |
| 2610 if (expressions.isEmpty()) { | |
| 2611 // Default case with no expressions. | |
| 2612 if (!node.isDefaultCase) { | |
| 2613 compiler.internalError("Case with no expression and not default", | |
| 2614 node: node); | |
| 2615 } | |
| 2616 visit(node.statements); | |
| 2617 // This must be the final case (otherwise "default" would be invalid), | |
| 2618 // so we don't need to check for fallthrough. | |
| 2619 return; | |
| 2620 } | |
| 2621 | |
| 2622 // Recursively build the test conditions. Leaves the result on the | |
| 2623 // expression stack. | |
| 2624 void buildTests(Link<Node> expressions) { | |
| 2625 // Build comparison for one case expression. | |
| 2626 void left() { | |
| 2627 Element equalsHelper = interceptors.getEqualsInterceptor(); | |
| 2628 HInstruction target = new HStatic(equalsHelper); | |
| 2629 add(target); | |
| 2630 visit(expressions.head); | |
| 2631 push(new HEquals(target, pop(), expression)); | |
| 2632 } | |
| 2633 | |
| 2634 // If this is the last expression, just return it. | |
| 2635 if (expressions.tail.isEmpty()) { | |
| 2636 left(); | |
| 2637 return; | |
| 2638 } | |
| 2639 | |
| 2640 void right() { | |
| 2641 buildTests(expressions.tail); | |
| 2642 } | |
| 2643 handleLogicalAndOr(left, right, isAnd: false); | |
| 2644 } | |
| 2645 | |
| 2646 buildTests(expressions); | |
| 2647 HInstruction result = popBoolified(); | |
| 2648 | |
| 2649 if (node.isDefaultCase) { | |
| 2650 // Don't actually use the condition result. | |
| 2651 // This must be final case, so don't check for abort. | |
| 2652 visit(node.statements); | |
| 2653 } else { | |
| 2654 stack.add(result); | |
| 2655 if (cases.tail.isEmpty()) { | |
| 2656 handleIf(() { visit(node.statements); }, null); | |
| 2657 } else { | |
| 2658 handleIf(() { visitStatementsAndAbort(); }, | |
| 2659 () { buildSwitchCases(cases.tail, expression); }); | |
| 2660 } | |
| 2661 } | |
| 2662 } | |
| 2663 | |
| 2664 visitSwitchCase(SwitchCase node) { | |
| 2665 unreachable(); | |
| 2666 } | |
| 2667 | |
| 2668 visitTryStatement(TryStatement node) { | |
| 2669 work.allowSpeculativeOptimization = false; | |
| 2670 HBasicBlock enterBlock = graph.addNewBlock(); | |
| 2671 close(new HGoto()).addSuccessor(enterBlock); | |
| 2672 open(enterBlock); | |
| 2673 HTry tryInstruction = new HTry(); | |
| 2674 List<HBasicBlock> blocks = <HBasicBlock>[]; | |
| 2675 blocks.add(close(tryInstruction)); | |
| 2676 | |
| 2677 HBasicBlock tryBody = graph.addNewBlock(); | |
| 2678 enterBlock.addSuccessor(tryBody); | |
| 2679 open(tryBody); | |
| 2680 visit(node.tryBlock); | |
| 2681 if (!isAborted()) blocks.add(close(new HGoto())); | |
| 2682 | |
| 2683 if (!node.catchBlocks.isEmpty()) { | |
| 2684 HBasicBlock block = graph.addNewBlock(); | |
| 2685 enterBlock.addSuccessor(block); | |
| 2686 open(block); | |
| 2687 // Note that the name of this element is irrelevant. | |
| 2688 Element element = new Element( | |
| 2689 const SourceString('exception'), ElementKind.PARAMETER, work.element); | |
| 2690 HParameterValue exception = new HParameterValue(element); | |
| 2691 add(exception); | |
| 2692 HInstruction oldRethrowableException = rethrowableException; | |
| 2693 rethrowableException = exception; | |
| 2694 push(new HStatic(interceptors.getExceptionUnwrapper())); | |
| 2695 List<HInstruction> inputs = <HInstruction>[pop(), exception]; | |
| 2696 HInvokeStatic unwrappedException = | |
| 2697 new HInvokeStatic(Selector.INVOCATION_1, inputs); | |
| 2698 add(unwrappedException); | |
| 2699 tryInstruction.exception = exception; | |
| 2700 | |
| 2701 Link<Node> link = node.catchBlocks.nodes; | |
| 2702 | |
| 2703 void pushCondition(CatchBlock catchBlock) { | |
| 2704 VariableDefinitions declaration = catchBlock.formals.nodes.head; | |
| 2705 HInstruction condition = null; | |
| 2706 if (declaration.type == null) { | |
| 2707 condition = graph.addConstantBool(true); | |
| 2708 stack.add(condition); | |
| 2709 } else { | |
| 2710 Element typeElement = elements[declaration.type]; | |
| 2711 if (typeElement == null) { | |
| 2712 compiler.cancel('Catch with unresolved type', node: catchBlock); | |
| 2713 } | |
| 2714 condition = new HIs(typeElement, unwrappedException, nullOk: true); | |
| 2715 push(condition); | |
| 2716 } | |
| 2717 } | |
| 2718 | |
| 2719 void visitThen() { | |
| 2720 CatchBlock catchBlock = link.head; | |
| 2721 link = link.tail; | |
| 2722 localsHandler.updateLocal(elements[catchBlock.exception], | |
| 2723 unwrappedException); | |
| 2724 Node trace = catchBlock.trace; | |
| 2725 if (trace != null) { | |
| 2726 push(new HStatic(interceptors.getTraceFromException())); | |
| 2727 HInstruction traceInstruction = new HInvokeStatic( | |
| 2728 Selector.INVOCATION_1, <HInstruction>[pop(), exception]); | |
| 2729 add(traceInstruction); | |
| 2730 localsHandler.updateLocal(elements[trace], traceInstruction); | |
| 2731 } | |
| 2732 visit(catchBlock); | |
| 2733 } | |
| 2734 | |
| 2735 void visitElse() { | |
| 2736 if (link.isEmpty()) { | |
| 2737 close(new HThrow(exception, isRethrow: true)); | |
| 2738 } else { | |
| 2739 CatchBlock newBlock = link.head; | |
| 2740 pushCondition(newBlock); | |
| 2741 handleIf(visitThen, visitElse); | |
| 2742 } | |
| 2743 } | |
| 2744 | |
| 2745 CatchBlock firstBlock = link.head; | |
| 2746 pushCondition(firstBlock); | |
| 2747 handleIf(visitThen, visitElse); | |
| 2748 if (!isAborted()) blocks.add(close(new HGoto())); | |
| 2749 rethrowableException = oldRethrowableException; | |
| 2750 } | |
| 2751 | |
| 2752 if (node.finallyBlock != null) { | |
| 2753 HBasicBlock finallyBlock = graph.addNewBlock(); | |
| 2754 enterBlock.addSuccessor(finallyBlock); | |
| 2755 open(finallyBlock); | |
| 2756 visit(node.finallyBlock); | |
| 2757 if (!isAborted()) blocks.add(close(new HGoto())); | |
| 2758 tryInstruction.finallyBlock = finallyBlock; | |
| 2759 } | |
| 2760 | |
| 2761 HBasicBlock exitBlock = graph.addNewBlock(); | |
| 2762 | |
| 2763 for (HBasicBlock block in blocks) { | |
| 2764 block.addSuccessor(exitBlock); | |
| 2765 } | |
| 2766 | |
| 2767 open(exitBlock); | |
| 2768 } | |
| 2769 | |
| 2770 visitScriptTag(ScriptTag node) { | |
| 2771 compiler.unimplemented('SsaBuilder.visitScriptTag', node: node); | |
| 2772 } | |
| 2773 | |
| 2774 visitCatchBlock(CatchBlock node) { | |
| 2775 visit(node.block); | |
| 2776 } | |
| 2777 | |
| 2778 visitTypedef(Typedef node) { | |
| 2779 compiler.unimplemented('SsaBuilder.visitTypedef', node: node); | |
| 2780 } | |
| 2781 | |
| 2782 visitTypeVariable(TypeVariable node) { | |
| 2783 compiler.internalError('SsaBuilder.visitTypeVariable'); | |
| 2784 } | |
| 2785 | |
| 2786 generateUnimplemented(String reason, [bool isExpression = false]) { | |
| 2787 DartString string = new DartString.literal(reason); | |
| 2788 HInstruction message = graph.addConstantString(string); | |
| 2789 | |
| 2790 // Normally, we would call [close] here. However, then we hit | |
| 2791 // another unimplemented feature: aborting loop body. Simply | |
| 2792 // calling [add] does not work as it asserts that the instruction | |
| 2793 // isn't a control flow instruction. So we inline parts of [add]. | |
| 2794 current.addAfter(current.last, new HThrow(message)); | |
| 2795 if (isExpression) { | |
| 2796 stack.add(graph.addConstantNull()); | |
| 2797 } | |
| 2798 } | |
| 2799 | |
| 2800 /** HACK HACK HACK */ | |
| 2801 void hackAroundPossiblyAbortingBody(Node statement, void body()) { | |
| 2802 stack.add(graph.addConstantBool(true)); | |
| 2803 buildBody() { | |
| 2804 // TODO(lrn): Make sure to take continue into account. | |
| 2805 body(); | |
| 2806 if (isAborted()) { | |
| 2807 compiler.reportWarning(statement, "aborting loop body"); | |
| 2808 } | |
| 2809 } | |
| 2810 handleIf(buildBody, null); | |
| 2811 } | |
| 2812 } | |
| 2813 | |
| 2814 /** | |
| 2815 * Visitor that handles generation of string literals (LiteralString, | |
| 2816 * StringInterpolation), and otherwise delegates to the given visitor for | |
| 2817 * non-literal subexpressions. | |
| 2818 * TODO(lrn): Consider whether to handle compile time constant int/boolean | |
| 2819 * expressions as well. | |
| 2820 */ | |
| 2821 class StringBuilderVisitor extends AbstractVisitor { | |
| 2822 final SsaBuilder builder; | |
| 2823 | |
| 2824 /** | |
| 2825 * Offset used for the synthetic operator token used by concat. | |
| 2826 * Can probably be removed when we stop using String.operator+. | |
| 2827 */ | |
| 2828 final int offset; | |
| 2829 | |
| 2830 /** | |
| 2831 * Used to collect concatenated string literals into a single literal | |
| 2832 * instead of introducing unnecessary concatenations. | |
| 2833 */ | |
| 2834 DartString literalAccumulator = const LiteralDartString(""); | |
| 2835 | |
| 2836 /** | |
| 2837 * The string value generated so far (not including that which is still | |
| 2838 * in [literalAccumulator]). | |
| 2839 */ | |
| 2840 HInstruction prefix = null; | |
| 2841 | |
| 2842 StringBuilderVisitor(this.builder, this.offset); | |
| 2843 | |
| 2844 void visit(Node node) { | |
| 2845 node.accept(this); | |
| 2846 } | |
| 2847 | |
| 2848 visitNode(Node node) { | |
| 2849 compiler.internalError('unexpected node', node: node); | |
| 2850 } | |
| 2851 | |
| 2852 void visitExpression(Node node) { | |
| 2853 flushLiterals(); | |
| 2854 node.accept(builder); | |
| 2855 HInstruction asString = buildToString(node, builder.pop()); | |
| 2856 prefix = concat(prefix, asString); | |
| 2857 } | |
| 2858 | |
| 2859 void visitLiteralNull(LiteralNull node) { | |
| 2860 addLiteral(const LiteralDartString("null")); | |
| 2861 } | |
| 2862 | |
| 2863 void visitLiteralInt(LiteralInt node) { | |
| 2864 addLiteral(new DartString.literal(node.value.toString())); | |
| 2865 } | |
| 2866 | |
| 2867 void visitLiteralDouble(LiteralDouble node) { | |
| 2868 addLiteral(new DartString.literal(node.value.toString())); | |
| 2869 } | |
| 2870 | |
| 2871 void visitLiteralBool(LiteralBool node) { | |
| 2872 addLiteral(node.value ? const LiteralDartString("true") | |
| 2873 : const LiteralDartString("false")); | |
| 2874 } | |
| 2875 | |
| 2876 void visitStringInterpolation(StringInterpolation node) { | |
| 2877 node.visitChildren(this); | |
| 2878 } | |
| 2879 | |
| 2880 void visitStringInterpolationPart(StringInterpolationPart node) { | |
| 2881 visit(node.expression); | |
| 2882 visit(node.string); | |
| 2883 } | |
| 2884 | |
| 2885 void visitLiteralString(LiteralString node) { | |
| 2886 addLiteral(node.dartString); | |
| 2887 } | |
| 2888 | |
| 2889 void visitStringJuxtaposition(StringJuxtaposition node) { | |
| 2890 node.visitChildren(this); | |
| 2891 } | |
| 2892 | |
| 2893 void visitNodeList(NodeList node) { | |
| 2894 node.visitChildren(this); | |
| 2895 } | |
| 2896 | |
| 2897 /** | |
| 2898 * Add another literal string to the literalAccumulator. | |
| 2899 */ | |
| 2900 void addLiteral(DartString dartString) { | |
| 2901 literalAccumulator = new DartString.concat(literalAccumulator, dartString); | |
| 2902 } | |
| 2903 | |
| 2904 /** | |
| 2905 * Combine the strings in [literalAccumulator] into the prefix instruction. | |
| 2906 * After this, the [literalAccumulator] is empty and [prefix] is non-null. | |
| 2907 */ | |
| 2908 void flushLiterals() { | |
| 2909 if (literalAccumulator.isEmpty()) { | |
| 2910 if (prefix === null) { | |
| 2911 prefix = builder.graph.addConstantString(literalAccumulator); | |
| 2912 } | |
| 2913 return; | |
| 2914 } | |
| 2915 HInstruction string = builder.graph.addConstantString(literalAccumulator); | |
| 2916 literalAccumulator = new DartString.empty(); | |
| 2917 if (prefix !== null) { | |
| 2918 prefix = concat(prefix, string); | |
| 2919 } else { | |
| 2920 prefix = string; | |
| 2921 } | |
| 2922 } | |
| 2923 | |
| 2924 HInstruction concat(HInstruction left, HInstruction right) { | |
| 2925 SourceString dartMethodName = const SourceString("concat"); | |
| 2926 if (!builder.methodInterceptionEnabled) { | |
| 2927 builder.compiler.internalError( | |
| 2928 "Using string interpolations in non-intercepted code.", | |
| 2929 instruction: right); | |
| 2930 } | |
| 2931 Element interceptor = | |
| 2932 builder.interceptors.getStaticInterceptor(dartMethodName, 1); | |
| 2933 if (interceptor === null) { | |
| 2934 builder.compiler.internalError( | |
| 2935 "concat not intercepted.", instruction: left); | |
| 2936 } | |
| 2937 HStatic target = new HStatic(interceptor); | |
| 2938 builder.add(target); | |
| 2939 builder.push(new HInvokeInterceptor(Selector.INVOCATION_1, | |
| 2940 dartMethodName, | |
| 2941 false, | |
| 2942 <HInstruction>[target, left, right])); | |
| 2943 return builder.pop(); | |
| 2944 } | |
| 2945 | |
| 2946 HInstruction buildToString(Node node, HInstruction input) { | |
| 2947 SourceString dartMethodName = const SourceString("toString"); | |
| 2948 if (!builder.methodInterceptionEnabled) { | |
| 2949 builder.compiler.internalError( | |
| 2950 "Using string interpolations in non-intercepted code.", node: node); | |
| 2951 } | |
| 2952 Element interceptor = | |
| 2953 builder.interceptors.getStaticInterceptor(dartMethodName, 0); | |
| 2954 if (interceptor === null) { | |
| 2955 builder.compiler.internalError( | |
| 2956 "toString not intercepted.", node: node); | |
| 2957 } | |
| 2958 HStatic target = new HStatic(interceptor); | |
| 2959 builder.add(target); | |
| 2960 builder.push(new HInvokeInterceptor(Selector.INVOCATION_0, | |
| 2961 dartMethodName, | |
| 2962 false, | |
| 2963 <HInstruction>[target, input])); | |
| 2964 return builder.pop(); | |
| 2965 } | |
| 2966 | |
| 2967 HInstruction result() { | |
| 2968 flushLiterals(); | |
| 2969 return prefix; | |
| 2970 } | |
| 2971 } | |
| OLD | NEW |