| 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 part of ssa; | |
| 6 | |
| 7 /// A synthetic local variable only used with the SSA graph. | |
| 8 /// | |
| 9 /// For instance used for holding return value of function or the exception of a | |
| 10 /// try-catch statement. | |
| 11 class SyntheticLocal extends Local { | |
| 12 final String name; | |
| 13 final ExecutableElement executableContext; | |
| 14 | |
| 15 SyntheticLocal(this.name, this.executableContext); | |
| 16 } | |
| 17 | |
| 18 class SsaBuilderTask extends CompilerTask { | |
| 19 final CodeEmitterTask emitter; | |
| 20 final JavaScriptBackend backend; | |
| 21 | |
| 22 String get name => 'SSA builder'; | |
| 23 | |
| 24 SsaBuilderTask(JavaScriptBackend backend) | |
| 25 : emitter = backend.emitter, | |
| 26 backend = backend, | |
| 27 super(backend.compiler); | |
| 28 | |
| 29 HGraph build(CodegenWorkItem work) { | |
| 30 return measure(() { | |
| 31 Element element = work.element.implementation; | |
| 32 return compiler.withCurrentElement(element, () { | |
| 33 HInstruction.idCounter = 0; | |
| 34 SsaBuilder builder = | |
| 35 new SsaBuilder(backend, work, emitter.nativeEmitter); | |
| 36 HGraph graph; | |
| 37 ElementKind kind = element.kind; | |
| 38 if (kind == ElementKind.GENERATIVE_CONSTRUCTOR) { | |
| 39 graph = compileConstructor(builder, work); | |
| 40 } else if (kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY || | |
| 41 kind == ElementKind.FUNCTION || | |
| 42 kind == ElementKind.GETTER || | |
| 43 kind == ElementKind.SETTER) { | |
| 44 graph = builder.buildMethod(element); | |
| 45 } else if (kind == ElementKind.FIELD) { | |
| 46 if (element.isInstanceMember) { | |
| 47 assert(compiler.enableTypeAssertions); | |
| 48 graph = builder.buildCheckedSetter(element); | |
| 49 } else { | |
| 50 graph = builder.buildLazyInitializer(element); | |
| 51 } | |
| 52 } else { | |
| 53 compiler.internalError(element, 'Unexpected element kind $kind.'); | |
| 54 } | |
| 55 assert(graph.isValid()); | |
| 56 if (!identical(kind, ElementKind.FIELD)) { | |
| 57 FunctionElement function = element; | |
| 58 FunctionSignature signature = function.functionSignature; | |
| 59 signature.forEachOptionalParameter((ParameterElement parameter) { | |
| 60 // This ensures the default value will be computed. | |
| 61 ConstantValue constant = | |
| 62 backend.constants.getConstantForVariable(parameter).value; | |
| 63 CodegenRegistry registry = work.registry; | |
| 64 registry.registerCompileTimeConstant(constant); | |
| 65 }); | |
| 66 } | |
| 67 if (compiler.tracer.isEnabled) { | |
| 68 String name; | |
| 69 if (element.isClassMember) { | |
| 70 String className = element.enclosingClass.name; | |
| 71 String memberName = element.name; | |
| 72 name = "$className.$memberName"; | |
| 73 if (element.isGenerativeConstructorBody) { | |
| 74 name = "$name (body)"; | |
| 75 } | |
| 76 } else { | |
| 77 name = "${element.name}"; | |
| 78 } | |
| 79 compiler.tracer.traceCompilation( | |
| 80 name, work.compilationContext); | |
| 81 compiler.tracer.traceGraph('builder', graph); | |
| 82 } | |
| 83 return graph; | |
| 84 }); | |
| 85 }); | |
| 86 } | |
| 87 | |
| 88 HGraph compileConstructor(SsaBuilder builder, CodegenWorkItem work) { | |
| 89 return builder.buildFactory(work.element); | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 /** | |
| 94 * Keeps track of locals (including parameters and phis) when building. The | |
| 95 * 'this' reference is treated as parameter and hence handled by this class, | |
| 96 * too. | |
| 97 */ | |
| 98 class LocalsHandler { | |
| 99 /** | |
| 100 * The values of locals that can be directly accessed (without redirections | |
| 101 * to boxes or closure-fields). | |
| 102 * | |
| 103 * [directLocals] is iterated, so it is "insertion ordered" to make the | |
| 104 * iteration order a function only of insertions and not a function of | |
| 105 * e.g. Element hash codes. I'd prefer to use a SortedMap but some elements | |
| 106 * don't have source locations for [Elements.compareByPosition]. | |
| 107 */ | |
| 108 Map<Local, HInstruction> directLocals = | |
| 109 new Map<Local, HInstruction>(); | |
| 110 Map<Local, CapturedVariable> redirectionMapping = | |
| 111 new Map<Local, CapturedVariable>(); | |
| 112 SsaBuilder builder; | |
| 113 ClosureClassMap closureData; | |
| 114 Map<TypeVariableType, TypeVariableLocal> typeVariableLocals = | |
| 115 new Map<TypeVariableType, TypeVariableLocal>(); | |
| 116 final ExecutableElement executableContext; | |
| 117 | |
| 118 /// The class that defines the current type environment or null if no type | |
| 119 /// variables are in scope. | |
| 120 ClassElement get contextClass => executableContext.contextClass; | |
| 121 | |
| 122 LocalsHandler(this.builder, this.executableContext); | |
| 123 | |
| 124 /// Substituted type variables occurring in [type] into the context of | |
| 125 /// [contextClass]. | |
| 126 DartType substInContext(DartType type) { | |
| 127 if (contextClass != null) { | |
| 128 ClassElement typeContext = Types.getClassContext(type); | |
| 129 if (typeContext != null) { | |
| 130 type = type.substByContext( | |
| 131 contextClass.asInstanceOf(typeContext)); | |
| 132 } | |
| 133 } | |
| 134 return type; | |
| 135 } | |
| 136 | |
| 137 get typesTask => builder.compiler.typesTask; | |
| 138 | |
| 139 /** | |
| 140 * Creates a new [LocalsHandler] based on [other]. We only need to | |
| 141 * copy the [directLocals], since the other fields can be shared | |
| 142 * throughout the AST visit. | |
| 143 */ | |
| 144 LocalsHandler.from(LocalsHandler other) | |
| 145 : directLocals = new Map<Local, HInstruction>.from(other.directLocals), | |
| 146 redirectionMapping = other.redirectionMapping, | |
| 147 executableContext = other.executableContext, | |
| 148 builder = other.builder, | |
| 149 closureData = other.closureData; | |
| 150 | |
| 151 /** | |
| 152 * Redirects accesses from element [from] to element [to]. The [to] element | |
| 153 * must be a boxed variable or a variable that is stored in a closure-field. | |
| 154 */ | |
| 155 void redirectElement(Local from, CapturedVariable to) { | |
| 156 assert(redirectionMapping[from] == null); | |
| 157 redirectionMapping[from] = to; | |
| 158 assert(isStoredInClosureField(from) || isBoxed(from)); | |
| 159 } | |
| 160 | |
| 161 HInstruction createBox() { | |
| 162 // TODO(floitsch): Clean up this hack. Should we create a box-object by | |
| 163 // just creating an empty object literal? | |
| 164 JavaScriptBackend backend = builder.backend; | |
| 165 HInstruction box = new HForeign(js.js.parseForeignJS('{}'), | |
| 166 backend.nonNullType, | |
| 167 <HInstruction>[]); | |
| 168 builder.add(box); | |
| 169 return box; | |
| 170 } | |
| 171 | |
| 172 /** | |
| 173 * If the scope (function or loop) [node] has captured variables then this | |
| 174 * method creates a box and sets up the redirections. | |
| 175 */ | |
| 176 void enterScope(ast.Node node, Element element) { | |
| 177 // See if any variable in the top-scope of the function is captured. If yes | |
| 178 // we need to create a box-object. | |
| 179 ClosureScope scopeData = closureData.capturingScopes[node]; | |
| 180 if (scopeData == null) return; | |
| 181 HInstruction box; | |
| 182 // The scope has captured variables. | |
| 183 if (element != null && element.isGenerativeConstructorBody) { | |
| 184 // The box is passed as a parameter to a generative | |
| 185 // constructor body. | |
| 186 JavaScriptBackend backend = builder.backend; | |
| 187 box = builder.addParameter(scopeData.boxElement, backend.nonNullType); | |
| 188 } else { | |
| 189 box = createBox(); | |
| 190 } | |
| 191 // Add the box to the known locals. | |
| 192 directLocals[scopeData.boxElement] = box; | |
| 193 // Make sure that accesses to the boxed locals go into the box. We also | |
| 194 // need to make sure that parameters are copied into the box if necessary. | |
| 195 scopeData.forEachCapturedVariable( | |
| 196 (LocalVariableElement from, BoxFieldElement to) { | |
| 197 // The [from] can only be a parameter for function-scopes and not | |
| 198 // loop scopes. | |
| 199 if (from.isParameter && !element.isGenerativeConstructorBody) { | |
| 200 // Now that the redirection is set up, the update to the local will | |
| 201 // write the parameter value into the box. | |
| 202 // Store the captured parameter in the box. Get the current value | |
| 203 // before we put the redirection in place. | |
| 204 // We don't need to update the local for a generative | |
| 205 // constructor body, because it receives a box that already | |
| 206 // contains the updates as the last parameter. | |
| 207 HInstruction instruction = readLocal(from); | |
| 208 redirectElement(from, to); | |
| 209 updateLocal(from, instruction); | |
| 210 } else { | |
| 211 redirectElement(from, to); | |
| 212 } | |
| 213 }); | |
| 214 } | |
| 215 | |
| 216 /** | |
| 217 * Replaces the current box with a new box and copies over the given list | |
| 218 * of elements from the old box into the new box. | |
| 219 */ | |
| 220 void updateCaptureBox(BoxLocal boxElement, | |
| 221 List<LocalVariableElement> toBeCopiedElements) { | |
| 222 // Create a new box and copy over the values from the old box into the | |
| 223 // new one. | |
| 224 HInstruction oldBox = readLocal(boxElement); | |
| 225 HInstruction newBox = createBox(); | |
| 226 for (LocalVariableElement boxedVariable in toBeCopiedElements) { | |
| 227 // [readLocal] uses the [boxElement] to find its box. By replacing it | |
| 228 // behind its back we can still get to the old values. | |
| 229 updateLocal(boxElement, oldBox); | |
| 230 HInstruction oldValue = readLocal(boxedVariable); | |
| 231 updateLocal(boxElement, newBox); | |
| 232 updateLocal(boxedVariable, oldValue); | |
| 233 } | |
| 234 updateLocal(boxElement, newBox); | |
| 235 } | |
| 236 | |
| 237 /** | |
| 238 * Documentation wanted -- johnniwinther | |
| 239 * | |
| 240 * Invariant: [function] must be an implementation element. | |
| 241 */ | |
| 242 void startFunction(Element element, ast.Node node) { | |
| 243 assert(invariant(element, element.isImplementation)); | |
| 244 Compiler compiler = builder.compiler; | |
| 245 closureData = compiler.closureToClassMapper.computeClosureToClassMapping( | |
| 246 element, node, builder.elements); | |
| 247 | |
| 248 if (element is FunctionElement) { | |
| 249 FunctionElement functionElement = element; | |
| 250 FunctionSignature params = functionElement.functionSignature; | |
| 251 ClosureScope scopeData = closureData.capturingScopes[node]; | |
| 252 params.orderedForEachParameter((ParameterElement parameterElement) { | |
| 253 if (element.isGenerativeConstructorBody) { | |
| 254 if (scopeData != null && | |
| 255 scopeData.isCapturedVariable(parameterElement)) { | |
| 256 // The parameter will be a field in the box passed as the | |
| 257 // last parameter. So no need to have it. | |
| 258 return; | |
| 259 } | |
| 260 } | |
| 261 HInstruction parameter = builder.addParameter( | |
| 262 parameterElement, | |
| 263 TypeMaskFactory.inferredTypeForElement(parameterElement, compiler)); | |
| 264 builder.parameters[parameterElement] = parameter; | |
| 265 directLocals[parameterElement] = parameter; | |
| 266 }); | |
| 267 } | |
| 268 | |
| 269 enterScope(node, element); | |
| 270 | |
| 271 // If the freeVariableMapping is not empty, then this function was a | |
| 272 // nested closure that captures variables. Redirect the captured | |
| 273 // variables to fields in the closure. | |
| 274 closureData.forEachFreeVariable((Local from, CapturedVariable to) { | |
| 275 redirectElement(from, to); | |
| 276 }); | |
| 277 JavaScriptBackend backend = compiler.backend; | |
| 278 if (closureData.isClosure) { | |
| 279 // Inside closure redirect references to itself to [:this:]. | |
| 280 HThis thisInstruction = new HThis(closureData.thisLocal, | |
| 281 backend.nonNullType); | |
| 282 builder.graph.thisInstruction = thisInstruction; | |
| 283 builder.graph.entry.addAtEntry(thisInstruction); | |
| 284 updateLocal(closureData.closureElement, thisInstruction); | |
| 285 } else if (element.isInstanceMember) { | |
| 286 // Once closures have been mapped to classes their instance members might | |
| 287 // not have any thisElement if the closure was created inside a static | |
| 288 // context. | |
| 289 HThis thisInstruction = new HThis( | |
| 290 closureData.thisLocal, builder.getTypeOfThis()); | |
| 291 builder.graph.thisInstruction = thisInstruction; | |
| 292 builder.graph.entry.addAtEntry(thisInstruction); | |
| 293 directLocals[closureData.thisLocal] = thisInstruction; | |
| 294 } | |
| 295 | |
| 296 // If this method is an intercepted method, add the extra | |
| 297 // parameter to it, that is the actual receiver for intercepted | |
| 298 // classes, or the same as [:this:] for non-intercepted classes. | |
| 299 ClassElement cls = element.enclosingClass; | |
| 300 | |
| 301 // When the class extends a native class, the instance is pre-constructed | |
| 302 // and passed to the generative constructor factory function as a parameter. | |
| 303 // Instead of allocating and initializing the object, the constructor | |
| 304 // 'upgrades' the native subclass object by initializing the Dart fields. | |
| 305 bool isNativeUpgradeFactory = element.isGenerativeConstructor | |
| 306 && Elements.isNativeOrExtendsNative(cls); | |
| 307 if (backend.isInterceptedMethod(element)) { | |
| 308 bool isInterceptorClass = backend.isInterceptorClass(cls.declaration); | |
| 309 String name = isInterceptorClass ? 'receiver' : '_'; | |
| 310 SyntheticLocal parameter = new SyntheticLocal(name, executableContext); | |
| 311 HParameterValue value = | |
| 312 new HParameterValue(parameter, builder.getTypeOfThis()); | |
| 313 builder.graph.explicitReceiverParameter = value; | |
| 314 builder.graph.entry.addAfter( | |
| 315 directLocals[closureData.thisLocal], value); | |
| 316 if (isInterceptorClass) { | |
| 317 // Only use the extra parameter in intercepted classes. | |
| 318 directLocals[closureData.thisLocal] = value; | |
| 319 } | |
| 320 } else if (isNativeUpgradeFactory) { | |
| 321 SyntheticLocal parameter = | |
| 322 new SyntheticLocal('receiver', executableContext); | |
| 323 // Unlike `this`, receiver is nullable since direct calls to generative | |
| 324 // constructor call the constructor with `null`. | |
| 325 ClassWorld classWorld = compiler.world; | |
| 326 HParameterValue value = | |
| 327 new HParameterValue(parameter, new TypeMask.exact(cls, classWorld)); | |
| 328 builder.graph.explicitReceiverParameter = value; | |
| 329 builder.graph.entry.addAtEntry(value); | |
| 330 } | |
| 331 } | |
| 332 | |
| 333 /** | |
| 334 * Returns true if the local can be accessed directly. Boxed variables or | |
| 335 * captured variables that are stored in the closure-field return [:false:]. | |
| 336 */ | |
| 337 bool isAccessedDirectly(Local local) { | |
| 338 assert(local != null); | |
| 339 return !redirectionMapping.containsKey(local) | |
| 340 && !closureData.usedVariablesInTry.contains(local); | |
| 341 } | |
| 342 | |
| 343 bool isStoredInClosureField(Local local) { | |
| 344 assert(local != null); | |
| 345 if (isAccessedDirectly(local)) return false; | |
| 346 CapturedVariable redirectTarget = redirectionMapping[local]; | |
| 347 if (redirectTarget == null) return false; | |
| 348 return redirectTarget is ClosureFieldElement; | |
| 349 } | |
| 350 | |
| 351 bool isBoxed(Local local) { | |
| 352 if (isAccessedDirectly(local)) return false; | |
| 353 if (isStoredInClosureField(local)) return false; | |
| 354 return redirectionMapping.containsKey(local); | |
| 355 } | |
| 356 | |
| 357 bool isUsedInTry(Local local) { | |
| 358 return closureData.usedVariablesInTry.contains(local); | |
| 359 } | |
| 360 | |
| 361 /** | |
| 362 * Returns an [HInstruction] for the given element. If the element is | |
| 363 * boxed or stored in a closure then the method generates code to retrieve | |
| 364 * the value. | |
| 365 */ | |
| 366 HInstruction readLocal(Local local) { | |
| 367 if (isAccessedDirectly(local)) { | |
| 368 if (directLocals[local] == null) { | |
| 369 if (local is TypeVariableElement) { | |
| 370 builder.compiler.internalError(builder.compiler.currentElement, | |
| 371 "Runtime type information not available for $local."); | |
| 372 } else { | |
| 373 builder.compiler.internalError(local, | |
| 374 "Cannot find value $local."); | |
| 375 } | |
| 376 } | |
| 377 return directLocals[local]; | |
| 378 } else if (isStoredInClosureField(local)) { | |
| 379 ClosureFieldElement redirect = redirectionMapping[local]; | |
| 380 HInstruction receiver = readLocal(closureData.closureElement); | |
| 381 TypeMask type = local is BoxLocal | |
| 382 ? builder.backend.nonNullType | |
| 383 : builder.getTypeOfCapturedVariable(redirect); | |
| 384 HInstruction fieldGet = new HFieldGet(redirect, receiver, type); | |
| 385 builder.add(fieldGet); | |
| 386 return fieldGet; | |
| 387 } else if (isBoxed(local)) { | |
| 388 BoxFieldElement redirect = redirectionMapping[local]; | |
| 389 // In the function that declares the captured variable the box is | |
| 390 // accessed as direct local. Inside the nested closure the box is | |
| 391 // accessed through a closure-field. | |
| 392 // Calling [readLocal] makes sure we generate the correct code to get | |
| 393 // the box. | |
| 394 HInstruction box = readLocal(redirect.box); | |
| 395 HInstruction lookup = new HFieldGet( | |
| 396 redirect, box, builder.getTypeOfCapturedVariable(redirect)); | |
| 397 builder.add(lookup); | |
| 398 return lookup; | |
| 399 } else { | |
| 400 assert(isUsedInTry(local)); | |
| 401 HLocalValue localValue = getLocal(local); | |
| 402 HInstruction instruction = new HLocalGet( | |
| 403 local, localValue, builder.backend.dynamicType); | |
| 404 builder.add(instruction); | |
| 405 return instruction; | |
| 406 } | |
| 407 } | |
| 408 | |
| 409 HInstruction readThis() { | |
| 410 HInstruction res = readLocal(closureData.thisLocal); | |
| 411 if (res.instructionType == null) { | |
| 412 res.instructionType = builder.getTypeOfThis(); | |
| 413 } | |
| 414 return res; | |
| 415 } | |
| 416 | |
| 417 HLocalValue getLocal(Local local) { | |
| 418 // If the element is a parameter, we already have a | |
| 419 // HParameterValue for it. We cannot create another one because | |
| 420 // it could then have another name than the real parameter. And | |
| 421 // the other one would not know it is just a copy of the real | |
| 422 // parameter. | |
| 423 if (local is ParameterElement) return builder.parameters[local]; | |
| 424 | |
| 425 return builder.activationVariables.putIfAbsent(local, () { | |
| 426 JavaScriptBackend backend = builder.backend; | |
| 427 HLocalValue localValue = new HLocalValue(local, backend.nonNullType); | |
| 428 builder.graph.entry.addAtExit(localValue); | |
| 429 return localValue; | |
| 430 }); | |
| 431 } | |
| 432 | |
| 433 Local getTypeVariableAsLocal(TypeVariableType type) { | |
| 434 return typeVariableLocals.putIfAbsent(type, () { | |
| 435 return new TypeVariableLocal(type, executableContext); | |
| 436 }); | |
| 437 } | |
| 438 | |
| 439 /** | |
| 440 * Sets the [element] to [value]. If the element is boxed or stored in a | |
| 441 * closure then the method generates code to set the value. | |
| 442 */ | |
| 443 void updateLocal(Local local, HInstruction value) { | |
| 444 assert(!isStoredInClosureField(local)); | |
| 445 if (isAccessedDirectly(local)) { | |
| 446 directLocals[local] = value; | |
| 447 } else if (isBoxed(local)) { | |
| 448 BoxFieldElement redirect = redirectionMapping[local]; | |
| 449 // The box itself could be captured, or be local. A local variable that | |
| 450 // is captured will be boxed, but the box itself will be a local. | |
| 451 // Inside the closure the box is stored in a closure-field and cannot | |
| 452 // be accessed directly. | |
| 453 HInstruction box = readLocal(redirect.box); | |
| 454 builder.add(new HFieldSet(redirect, box, value)); | |
| 455 } else { | |
| 456 assert(isUsedInTry(local)); | |
| 457 HLocalValue localValue = getLocal(local); | |
| 458 builder.add(new HLocalSet(local, localValue, value)); | |
| 459 } | |
| 460 } | |
| 461 | |
| 462 /** | |
| 463 * This function, startLoop, must be called before visiting any children of | |
| 464 * the loop. In particular it needs to be called before executing the | |
| 465 * initializers. | |
| 466 * | |
| 467 * The [LocalsHandler] will make the boxes and updates at the right moment. | |
| 468 * The builder just needs to call [enterLoopBody] and [enterLoopUpdates] | |
| 469 * (for [ast.For] loops) at the correct places. For phi-handling | |
| 470 * [beginLoopHeader] and [endLoop] must also be called. | |
| 471 * | |
| 472 * The correct place for the box depends on the given loop. In most cases | |
| 473 * the box will be created when entering the loop-body: while, do-while, and | |
| 474 * for-in (assuming the call to [:next:] is inside the body) can always be | |
| 475 * constructed this way. | |
| 476 * | |
| 477 * Things are slightly more complicated for [ast.For] loops. If no declared | |
| 478 * loop variable is boxed then the loop-body approach works here too. If a | |
| 479 * loop-variable is boxed we need to introduce a new box for the | |
| 480 * loop-variable before we enter the initializer so that the initializer | |
| 481 * writes the values into the box. In any case we need to create the box | |
| 482 * before the condition since the condition could box the variable. | |
| 483 * Since the first box is created outside the actual loop we have a second | |
| 484 * location where a box is created: just before the updates. This is | |
| 485 * necessary since updates are considered to be part of the next iteration | |
| 486 * (and can again capture variables). | |
| 487 * | |
| 488 * For example the following Dart code prints 1 3 -- 3 4. | |
| 489 * | |
| 490 * var fs = []; | |
| 491 * for (var i = 0; i < 3; (f() { fs.add(f); print(i); i++; })()) { | |
| 492 * i++; | |
| 493 * } | |
| 494 * print("--"); | |
| 495 * for (var i = 0; i < 2; i++) fs[i](); | |
| 496 * | |
| 497 * We solve this by emitting the following code (only for [ast.For] loops): | |
| 498 * <Create box> <== move the first box creation outside the loop. | |
| 499 * <initializer>; | |
| 500 * loop-entry: | |
| 501 * if (!<condition>) goto loop-exit; | |
| 502 * <body> | |
| 503 * <update box> // create a new box and copy the captured loop-variables. | |
| 504 * <updates> | |
| 505 * goto loop-entry; | |
| 506 * loop-exit: | |
| 507 */ | |
| 508 void startLoop(ast.Node node) { | |
| 509 ClosureScope scopeData = closureData.capturingScopes[node]; | |
| 510 if (scopeData == null) return; | |
| 511 if (scopeData.hasBoxedLoopVariables()) { | |
| 512 // If there are boxed loop variables then we set up the box and | |
| 513 // redirections already now. This way the initializer can write its | |
| 514 // values into the box. | |
| 515 // For other loops the box will be created when entering the body. | |
| 516 enterScope(node, null); | |
| 517 } | |
| 518 } | |
| 519 | |
| 520 /** | |
| 521 * Create phis at the loop entry for local variables (ready for the values | |
| 522 * from the back edge). Populate the phis with the current values. | |
| 523 */ | |
| 524 void beginLoopHeader(HBasicBlock loopEntry) { | |
| 525 // Create a copy because we modify the map while iterating over it. | |
| 526 Map<Local, HInstruction> savedDirectLocals = | |
| 527 new Map<Local, HInstruction>.from(directLocals); | |
| 528 | |
| 529 JavaScriptBackend backend = builder.backend; | |
| 530 // Create phis for all elements in the definitions environment. | |
| 531 savedDirectLocals.forEach((Local local, | |
| 532 HInstruction instruction) { | |
| 533 if (isAccessedDirectly(local)) { | |
| 534 // We know 'this' cannot be modified. | |
| 535 if (local != closureData.thisLocal) { | |
| 536 HPhi phi = new HPhi.singleInput( | |
| 537 local, instruction, backend.dynamicType); | |
| 538 loopEntry.addPhi(phi); | |
| 539 directLocals[local] = phi; | |
| 540 } else { | |
| 541 directLocals[local] = instruction; | |
| 542 } | |
| 543 } | |
| 544 }); | |
| 545 } | |
| 546 | |
| 547 void enterLoopBody(ast.Node node) { | |
| 548 ClosureScope scopeData = closureData.capturingScopes[node]; | |
| 549 if (scopeData == null) return; | |
| 550 // If there are no declared boxed loop variables then we did not create the | |
| 551 // box before the initializer and we have to create the box now. | |
| 552 if (!scopeData.hasBoxedLoopVariables()) { | |
| 553 enterScope(node, null); | |
| 554 } | |
| 555 } | |
| 556 | |
| 557 void enterLoopUpdates(ast.Node node) { | |
| 558 // If there are declared boxed loop variables then the updates might have | |
| 559 // access to the box and we must switch to a new box before executing the | |
| 560 // updates. | |
| 561 // In all other cases a new box will be created when entering the body of | |
| 562 // the next iteration. | |
| 563 ClosureScope scopeData = closureData.capturingScopes[node]; | |
| 564 if (scopeData == null) return; | |
| 565 if (scopeData.hasBoxedLoopVariables()) { | |
| 566 updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables); | |
| 567 } | |
| 568 } | |
| 569 | |
| 570 /** | |
| 571 * Goes through the phis created in beginLoopHeader entry and adds the | |
| 572 * input from the back edge (from the current value of directLocals) to them. | |
| 573 */ | |
| 574 void endLoop(HBasicBlock loopEntry) { | |
| 575 // If the loop has an aborting body, we don't update the loop | |
| 576 // phis. | |
| 577 if (loopEntry.predecessors.length == 1) return; | |
| 578 loopEntry.forEachPhi((HPhi phi) { | |
| 579 Local element = phi.sourceElement; | |
| 580 HInstruction postLoopDefinition = directLocals[element]; | |
| 581 phi.addInput(postLoopDefinition); | |
| 582 }); | |
| 583 } | |
| 584 | |
| 585 /** | |
| 586 * Merge [otherLocals] into this locals handler, creating phi-nodes when | |
| 587 * there is a conflict. | |
| 588 * If a phi node is necessary, it will use this handler's instruction as the | |
| 589 * first input, and the otherLocals instruction as the second. | |
| 590 */ | |
| 591 void mergeWith(LocalsHandler otherLocals, HBasicBlock joinBlock) { | |
| 592 // If an element is in one map but not the other we can safely | |
| 593 // ignore it. It means that a variable was declared in the | |
| 594 // block. Since variable declarations are scoped the declared | |
| 595 // variable cannot be alive outside the block. Note: this is only | |
| 596 // true for nodes where we do joins. | |
| 597 Map<Local, HInstruction> joinedLocals = | |
| 598 new Map<Local, HInstruction>(); | |
| 599 JavaScriptBackend backend = builder.backend; | |
| 600 otherLocals.directLocals.forEach((Local local, | |
| 601 HInstruction instruction) { | |
| 602 // We know 'this' cannot be modified. | |
| 603 if (local == closureData.thisLocal) { | |
| 604 assert(directLocals[local] == instruction); | |
| 605 joinedLocals[local] = instruction; | |
| 606 } else { | |
| 607 HInstruction mine = directLocals[local]; | |
| 608 if (mine == null) return; | |
| 609 if (identical(instruction, mine)) { | |
| 610 joinedLocals[local] = instruction; | |
| 611 } else { | |
| 612 HInstruction phi = new HPhi.manyInputs( | |
| 613 local, <HInstruction>[mine, instruction], backend.dynamicType); | |
| 614 joinBlock.addPhi(phi); | |
| 615 joinedLocals[local] = phi; | |
| 616 } | |
| 617 } | |
| 618 }); | |
| 619 directLocals = joinedLocals; | |
| 620 } | |
| 621 | |
| 622 /** | |
| 623 * When control flow merges, this method can be used to merge several | |
| 624 * localsHandlers into a new one using phis. The new localsHandler is | |
| 625 * returned. Unless it is also in the list, the current localsHandler is not | |
| 626 * used for its values, only for its declared variables. This is a way to | |
| 627 * exclude local values from the result when they are no longer in scope. | |
| 628 */ | |
| 629 LocalsHandler mergeMultiple(List<LocalsHandler> localsHandlers, | |
| 630 HBasicBlock joinBlock) { | |
| 631 assert(localsHandlers.length > 0); | |
| 632 if (localsHandlers.length == 1) return localsHandlers[0]; | |
| 633 Map<Local, HInstruction> joinedLocals = | |
| 634 new Map<Local, HInstruction>(); | |
| 635 HInstruction thisValue = null; | |
| 636 JavaScriptBackend backend = builder.backend; | |
| 637 directLocals.forEach((Local local, HInstruction instruction) { | |
| 638 if (local != closureData.thisLocal) { | |
| 639 HPhi phi = new HPhi.noInputs(local, backend.dynamicType); | |
| 640 joinedLocals[local] = phi; | |
| 641 joinBlock.addPhi(phi); | |
| 642 } else { | |
| 643 // We know that "this" never changes, if it's there. | |
| 644 // Save it for later. While merging, there is no phi for "this", | |
| 645 // so we don't have to special case it in the merge loop. | |
| 646 thisValue = instruction; | |
| 647 } | |
| 648 }); | |
| 649 for (LocalsHandler handler in localsHandlers) { | |
| 650 handler.directLocals.forEach((Local local, | |
| 651 HInstruction instruction) { | |
| 652 HPhi phi = joinedLocals[local]; | |
| 653 if (phi != null) { | |
| 654 phi.addInput(instruction); | |
| 655 } | |
| 656 }); | |
| 657 } | |
| 658 if (thisValue != null) { | |
| 659 // If there was a "this" for the scope, add it to the new locals. | |
| 660 joinedLocals[closureData.thisLocal] = thisValue; | |
| 661 } | |
| 662 | |
| 663 // Remove locals that are not in all handlers. | |
| 664 directLocals = new Map<Local, HInstruction>(); | |
| 665 joinedLocals.forEach((Local local, | |
| 666 HInstruction instruction) { | |
| 667 if (local != closureData.thisLocal | |
| 668 && instruction.inputs.length != localsHandlers.length) { | |
| 669 joinBlock.removePhi(instruction); | |
| 670 } else { | |
| 671 directLocals[local] = instruction; | |
| 672 } | |
| 673 }); | |
| 674 return this; | |
| 675 } | |
| 676 } | |
| 677 | |
| 678 | |
| 679 // Represents a single break/continue instruction. | |
| 680 class JumpHandlerEntry { | |
| 681 final HJump jumpInstruction; | |
| 682 final LocalsHandler locals; | |
| 683 bool isBreak() => jumpInstruction is HBreak; | |
| 684 bool isContinue() => jumpInstruction is HContinue; | |
| 685 JumpHandlerEntry(this.jumpInstruction, this.locals); | |
| 686 } | |
| 687 | |
| 688 | |
| 689 abstract class JumpHandler { | |
| 690 factory JumpHandler(SsaBuilder builder, JumpTarget target) { | |
| 691 return new TargetJumpHandler(builder, target); | |
| 692 } | |
| 693 void generateBreak([LabelDefinition label]); | |
| 694 void generateContinue([LabelDefinition label]); | |
| 695 void forEachBreak(void action(HBreak instruction, LocalsHandler locals)); | |
| 696 void forEachContinue(void action(HContinue instruction, | |
| 697 LocalsHandler locals)); | |
| 698 bool hasAnyContinue(); | |
| 699 bool hasAnyBreak(); | |
| 700 void close(); | |
| 701 final JumpTarget target; | |
| 702 List<LabelDefinition> labels(); | |
| 703 } | |
| 704 | |
| 705 // Insert break handler used to avoid null checks when a target isn't | |
| 706 // used as the target of a break, and therefore doesn't need a break | |
| 707 // handler associated with it. | |
| 708 class NullJumpHandler implements JumpHandler { | |
| 709 final Compiler compiler; | |
| 710 | |
| 711 NullJumpHandler(this.compiler); | |
| 712 | |
| 713 void generateBreak([LabelDefinition label]) { | |
| 714 compiler.internalError(CURRENT_ELEMENT_SPANNABLE, | |
| 715 'NullJumpHandler.generateBreak should not be called.'); | |
| 716 } | |
| 717 | |
| 718 void generateContinue([LabelDefinition label]) { | |
| 719 compiler.internalError(CURRENT_ELEMENT_SPANNABLE, | |
| 720 'NullJumpHandler.generateContinue should not be called.'); | |
| 721 } | |
| 722 | |
| 723 void forEachBreak(Function ignored) { } | |
| 724 void forEachContinue(Function ignored) { } | |
| 725 void close() { } | |
| 726 bool hasAnyContinue() => false; | |
| 727 bool hasAnyBreak() => false; | |
| 728 | |
| 729 List<LabelDefinition> labels() => const <LabelDefinition>[]; | |
| 730 JumpTarget get target => null; | |
| 731 } | |
| 732 | |
| 733 // Records breaks until a target block is available. | |
| 734 // Breaks are always forward jumps. | |
| 735 // Continues in loops are implemented as breaks of the body. | |
| 736 // Continues in switches is currently not handled. | |
| 737 class TargetJumpHandler implements JumpHandler { | |
| 738 final SsaBuilder builder; | |
| 739 final JumpTarget target; | |
| 740 final List<JumpHandlerEntry> jumps; | |
| 741 | |
| 742 TargetJumpHandler(SsaBuilder builder, this.target) | |
| 743 : this.builder = builder, | |
| 744 jumps = <JumpHandlerEntry>[] { | |
| 745 assert(builder.jumpTargets[target] == null); | |
| 746 builder.jumpTargets[target] = this; | |
| 747 } | |
| 748 | |
| 749 void generateBreak([LabelDefinition label]) { | |
| 750 HInstruction breakInstruction; | |
| 751 if (label == null) { | |
| 752 breakInstruction = new HBreak(target); | |
| 753 } else { | |
| 754 breakInstruction = new HBreak.toLabel(label); | |
| 755 } | |
| 756 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); | |
| 757 builder.close(breakInstruction); | |
| 758 jumps.add(new JumpHandlerEntry(breakInstruction, locals)); | |
| 759 } | |
| 760 | |
| 761 void generateContinue([LabelDefinition label]) { | |
| 762 HInstruction continueInstruction; | |
| 763 if (label == null) { | |
| 764 continueInstruction = new HContinue(target); | |
| 765 } else { | |
| 766 continueInstruction = new HContinue.toLabel(label); | |
| 767 // Switch case continue statements must be handled by the | |
| 768 // [SwitchCaseJumpHandler]. | |
| 769 assert(label.target.statement is! ast.SwitchCase); | |
| 770 } | |
| 771 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); | |
| 772 builder.close(continueInstruction); | |
| 773 jumps.add(new JumpHandlerEntry(continueInstruction, locals)); | |
| 774 } | |
| 775 | |
| 776 void forEachBreak(Function action) { | |
| 777 for (JumpHandlerEntry entry in jumps) { | |
| 778 if (entry.isBreak()) action(entry.jumpInstruction, entry.locals); | |
| 779 } | |
| 780 } | |
| 781 | |
| 782 void forEachContinue(Function action) { | |
| 783 for (JumpHandlerEntry entry in jumps) { | |
| 784 if (entry.isContinue()) action(entry.jumpInstruction, entry.locals); | |
| 785 } | |
| 786 } | |
| 787 | |
| 788 bool hasAnyContinue() { | |
| 789 for (JumpHandlerEntry entry in jumps) { | |
| 790 if (entry.isContinue()) return true; | |
| 791 } | |
| 792 return false; | |
| 793 } | |
| 794 | |
| 795 bool hasAnyBreak() { | |
| 796 for (JumpHandlerEntry entry in jumps) { | |
| 797 if (entry.isBreak()) return true; | |
| 798 } | |
| 799 return false; | |
| 800 } | |
| 801 | |
| 802 void close() { | |
| 803 // The mapping from TargetElement to JumpHandler is no longer needed. | |
| 804 builder.jumpTargets.remove(target); | |
| 805 } | |
| 806 | |
| 807 List<LabelDefinition> labels() { | |
| 808 List<LabelDefinition> result = null; | |
| 809 for (LabelDefinition element in target.labels) { | |
| 810 if (result == null) result = <LabelDefinition>[]; | |
| 811 result.add(element); | |
| 812 } | |
| 813 return (result == null) ? const <LabelDefinition>[] : result; | |
| 814 } | |
| 815 } | |
| 816 | |
| 817 /// Special [JumpHandler] implementation used to handle continue statements | |
| 818 /// targeting switch cases. | |
| 819 class SwitchCaseJumpHandler extends TargetJumpHandler { | |
| 820 /// Map from switch case targets to indices used to encode the flow of the | |
| 821 /// switch case loop. | |
| 822 final Map<JumpTarget, int> targetIndexMap = new Map<JumpTarget, int>(); | |
| 823 | |
| 824 SwitchCaseJumpHandler(SsaBuilder builder, | |
| 825 JumpTarget target, | |
| 826 ast.SwitchStatement node) | |
| 827 : super(builder, target) { | |
| 828 // The switch case indices must match those computed in | |
| 829 // [SsaFromAstMixin.buildSwitchCaseConstants]. | |
| 830 // Switch indices are 1-based so we can bypass the synthetic loop when no | |
| 831 // cases match simply by branching on the index (which defaults to null). | |
| 832 int switchIndex = 1; | |
| 833 for (ast.SwitchCase switchCase in node.cases) { | |
| 834 for (ast.Node labelOrCase in switchCase.labelsAndCases) { | |
| 835 ast.Node label = labelOrCase.asLabel(); | |
| 836 if (label != null) { | |
| 837 LabelDefinition labelElement = | |
| 838 builder.elements.getLabelDefinition(label); | |
| 839 if (labelElement != null && labelElement.isContinueTarget) { | |
| 840 JumpTarget continueTarget = labelElement.target; | |
| 841 targetIndexMap[continueTarget] = switchIndex; | |
| 842 assert(builder.jumpTargets[continueTarget] == null); | |
| 843 builder.jumpTargets[continueTarget] = this; | |
| 844 } | |
| 845 } | |
| 846 } | |
| 847 switchIndex++; | |
| 848 } | |
| 849 } | |
| 850 | |
| 851 void generateBreak([LabelDefinition label]) { | |
| 852 if (label == null) { | |
| 853 // Creates a special break instruction for the synthetic loop generated | |
| 854 // for a switch statement with continue statements. See | |
| 855 // [SsaFromAstMixin.buildComplexSwitchStatement] for detail. | |
| 856 | |
| 857 HInstruction breakInstruction = | |
| 858 new HBreak(target, breakSwitchContinueLoop: true); | |
| 859 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); | |
| 860 builder.close(breakInstruction); | |
| 861 jumps.add(new JumpHandlerEntry(breakInstruction, locals)); | |
| 862 } else { | |
| 863 super.generateBreak(label); | |
| 864 } | |
| 865 } | |
| 866 | |
| 867 bool isContinueToSwitchCase(LabelDefinition label) { | |
| 868 return label != null && targetIndexMap.containsKey(label.target); | |
| 869 } | |
| 870 | |
| 871 void generateContinue([LabelDefinition label]) { | |
| 872 if (isContinueToSwitchCase(label)) { | |
| 873 // Creates the special instructions 'label = i; continue l;' used in | |
| 874 // switch statements with continue statements. See | |
| 875 // [SsaFromAstMixin.buildComplexSwitchStatement] for detail. | |
| 876 | |
| 877 assert(label != null); | |
| 878 HInstruction value = builder.graph.addConstantInt( | |
| 879 targetIndexMap[label.target], | |
| 880 builder.compiler); | |
| 881 builder.localsHandler.updateLocal(target, value); | |
| 882 | |
| 883 assert(label.target.labels.contains(label)); | |
| 884 HInstruction continueInstruction = new HContinue(target); | |
| 885 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); | |
| 886 builder.close(continueInstruction); | |
| 887 jumps.add(new JumpHandlerEntry(continueInstruction, locals)); | |
| 888 } else { | |
| 889 super.generateContinue(label); | |
| 890 } | |
| 891 } | |
| 892 | |
| 893 void close() { | |
| 894 // The mapping from TargetElement to JumpHandler is no longer needed. | |
| 895 for (JumpTarget target in targetIndexMap.keys) { | |
| 896 builder.jumpTargets.remove(target); | |
| 897 } | |
| 898 super.close(); | |
| 899 } | |
| 900 } | |
| 901 | |
| 902 /** | |
| 903 * This class builds SSA nodes for functions represented in AST. | |
| 904 */ | |
| 905 class SsaBuilder extends ResolvedVisitor { | |
| 906 final Compiler compiler; | |
| 907 final JavaScriptBackend backend; | |
| 908 final ConstantSystem constantSystem; | |
| 909 final CodegenWorkItem work; | |
| 910 final RuntimeTypes rti; | |
| 911 | |
| 912 /* This field is used by the native handler. */ | |
| 913 final NativeEmitter nativeEmitter; | |
| 914 | |
| 915 final HGraph graph = new HGraph(); | |
| 916 | |
| 917 /** | |
| 918 * The current block to add instructions to. Might be null, if we are | |
| 919 * visiting dead code, but see [isReachable]. | |
| 920 */ | |
| 921 HBasicBlock _current; | |
| 922 | |
| 923 HBasicBlock get current => _current; | |
| 924 | |
| 925 void set current(c) { | |
| 926 isReachable = c != null; | |
| 927 _current = c; | |
| 928 } | |
| 929 | |
| 930 /** | |
| 931 * The most recently opened block. Has the same value as [current] while | |
| 932 * the block is open, but unlike [current], it isn't cleared when the | |
| 933 * current block is closed. | |
| 934 */ | |
| 935 HBasicBlock lastOpenedBlock; | |
| 936 | |
| 937 /** | |
| 938 * Indicates whether the current block is dead (because it has a throw or a | |
| 939 * return further up). If this is false, then [current] may be null. If the | |
| 940 * block is dead then it may also be aborted, but for simplicity we only | |
| 941 * abort on statement boundaries, not in the middle of expressions. See | |
| 942 * isAborted. | |
| 943 */ | |
| 944 bool isReachable = true; | |
| 945 | |
| 946 /** | |
| 947 * True if we are visiting the expression of a throw statement; we assume this | |
| 948 * is a slow path. | |
| 949 */ | |
| 950 bool inExpressionOfThrow = false; | |
| 951 | |
| 952 /** | |
| 953 * The loop nesting is consulted when inlining a function invocation in | |
| 954 * [tryInlineMethod]. The inlining heuristics take this information into | |
| 955 * account. | |
| 956 */ | |
| 957 int loopNesting = 0; | |
| 958 | |
| 959 /** | |
| 960 * This stack contains declaration elements of the functions being built | |
| 961 * or inlined by this builder. | |
| 962 */ | |
| 963 final List<Element> sourceElementStack = <Element>[]; | |
| 964 | |
| 965 LocalsHandler localsHandler; | |
| 966 | |
| 967 HInstruction rethrowableException; | |
| 968 | |
| 969 HParameterValue lastAddedParameter; | |
| 970 | |
| 971 Map<ParameterElement, HInstruction> parameters = | |
| 972 <ParameterElement, HInstruction>{}; | |
| 973 | |
| 974 Map<JumpTarget, JumpHandler> jumpTargets = <JumpTarget, JumpHandler>{}; | |
| 975 | |
| 976 /** | |
| 977 * Variables stored in the current activation. These variables are | |
| 978 * being updated in try/catch blocks, and should be | |
| 979 * accessed indirectly through [HLocalGet] and [HLocalSet]. | |
| 980 */ | |
| 981 Map<Local, HLocalValue> activationVariables = | |
| 982 <Local, HLocalValue>{}; | |
| 983 | |
| 984 // We build the Ssa graph by simulating a stack machine. | |
| 985 List<HInstruction> stack = <HInstruction>[]; | |
| 986 | |
| 987 SsaBuilder(JavaScriptBackend backend, | |
| 988 CodegenWorkItem work, | |
| 989 this.nativeEmitter) | |
| 990 : this.compiler = backend.compiler, | |
| 991 this.backend = backend, | |
| 992 this.constantSystem = backend.constantSystem, | |
| 993 this.work = work, | |
| 994 this.rti = backend.rti, | |
| 995 super(work.resolutionTree) { | |
| 996 localsHandler = new LocalsHandler(this, work.element); | |
| 997 sourceElementStack.add(work.element); | |
| 998 } | |
| 999 | |
| 1000 CodegenRegistry get registry => work.registry; | |
| 1001 | |
| 1002 /// Returns the current source element. | |
| 1003 /// | |
| 1004 /// The returned element is a declaration element. | |
| 1005 // TODO(johnniwinther): Check that all usages of sourceElement agree on | |
| 1006 // implementation/declaration distinction. | |
| 1007 Element get sourceElement => sourceElementStack.last; | |
| 1008 | |
| 1009 HBasicBlock addNewBlock() { | |
| 1010 HBasicBlock block = graph.addNewBlock(); | |
| 1011 // If adding a new block during building of an expression, it is due to | |
| 1012 // conditional expressions or short-circuit logical operators. | |
| 1013 return block; | |
| 1014 } | |
| 1015 | |
| 1016 void open(HBasicBlock block) { | |
| 1017 block.open(); | |
| 1018 current = block; | |
| 1019 lastOpenedBlock = block; | |
| 1020 } | |
| 1021 | |
| 1022 HBasicBlock close(HControlFlow end) { | |
| 1023 HBasicBlock result = current; | |
| 1024 current.close(end); | |
| 1025 current = null; | |
| 1026 return result; | |
| 1027 } | |
| 1028 | |
| 1029 HBasicBlock closeAndGotoExit(HControlFlow end) { | |
| 1030 HBasicBlock result = current; | |
| 1031 current.close(end); | |
| 1032 current = null; | |
| 1033 result.addSuccessor(graph.exit); | |
| 1034 return result; | |
| 1035 } | |
| 1036 | |
| 1037 void goto(HBasicBlock from, HBasicBlock to) { | |
| 1038 from.close(new HGoto()); | |
| 1039 from.addSuccessor(to); | |
| 1040 } | |
| 1041 | |
| 1042 bool isAborted() { | |
| 1043 return current == null; | |
| 1044 } | |
| 1045 | |
| 1046 /** | |
| 1047 * Creates a new block, transitions to it from any current block, and | |
| 1048 * opens the new block. | |
| 1049 */ | |
| 1050 HBasicBlock openNewBlock() { | |
| 1051 HBasicBlock newBlock = addNewBlock(); | |
| 1052 if (!isAborted()) goto(current, newBlock); | |
| 1053 open(newBlock); | |
| 1054 return newBlock; | |
| 1055 } | |
| 1056 | |
| 1057 void add(HInstruction instruction) { | |
| 1058 current.add(instruction); | |
| 1059 } | |
| 1060 | |
| 1061 void addWithPosition(HInstruction instruction, ast.Node node) { | |
| 1062 add(attachPosition(instruction, node)); | |
| 1063 } | |
| 1064 | |
| 1065 SourceFile currentSourceFile() { | |
| 1066 return sourceElement.implementation.compilationUnit.script.file; | |
| 1067 } | |
| 1068 | |
| 1069 void checkValidSourceFileLocation( | |
| 1070 SourceFileLocation location, SourceFile sourceFile, int offset) { | |
| 1071 if (!location.isValid()) { | |
| 1072 throw MessageKind.INVALID_SOURCE_FILE_LOCATION.message( | |
| 1073 {'offset': offset, | |
| 1074 'fileName': sourceFile.filename, | |
| 1075 'length': sourceFile.length}); | |
| 1076 } | |
| 1077 } | |
| 1078 | |
| 1079 /** | |
| 1080 * Returns a complete argument list for a call of [function]. | |
| 1081 */ | |
| 1082 List<HInstruction> completeSendArgumentsList( | |
| 1083 FunctionElement function, | |
| 1084 Selector selector, | |
| 1085 List<HInstruction> providedArguments, | |
| 1086 ast.Node currentNode) { | |
| 1087 assert(invariant(function, function.isImplementation)); | |
| 1088 assert(providedArguments != null); | |
| 1089 | |
| 1090 bool isInstanceMember = function.isInstanceMember; | |
| 1091 // For static calls, [providedArguments] is complete, default arguments | |
| 1092 // have been included if necessary, see [addStaticSendArgumentsToList]. | |
| 1093 if (!isInstanceMember | |
| 1094 || currentNode == null // In erroneous code, currentNode can be null. | |
| 1095 || providedArgumentsKnownToBeComplete(currentNode) | |
| 1096 || function.isGenerativeConstructorBody | |
| 1097 || selector.isGetter) { | |
| 1098 // For these cases, the provided argument list is known to be complete. | |
| 1099 return providedArguments; | |
| 1100 } else { | |
| 1101 return completeDynamicSendArgumentsList( | |
| 1102 selector, function, providedArguments); | |
| 1103 } | |
| 1104 } | |
| 1105 | |
| 1106 /** | |
| 1107 * Returns a complete argument list for a dynamic call of [function]. The | |
| 1108 * initial argument list [providedArguments], created by | |
| 1109 * [addDynamicSendArgumentsToList], does not include values for default | |
| 1110 * arguments used in the call. The reason is that the target function (which | |
| 1111 * defines the defaults) is not known. | |
| 1112 * | |
| 1113 * However, inlining can only be performed when the target function can be | |
| 1114 * resolved statically. The defaults can therefore be included at this point. | |
| 1115 * | |
| 1116 * The [providedArguments] list contains first all positional arguments, then | |
| 1117 * the provided named arguments (the named arguments that are defined in the | |
| 1118 * [selector]) in a specific order (see [addDynamicSendArgumentsToList]). | |
| 1119 */ | |
| 1120 List<HInstruction> completeDynamicSendArgumentsList( | |
| 1121 Selector selector, | |
| 1122 FunctionElement function, | |
| 1123 List<HInstruction> providedArguments) { | |
| 1124 assert(selector.applies(function, compiler.world)); | |
| 1125 FunctionSignature signature = function.functionSignature; | |
| 1126 List<HInstruction> compiledArguments = new List<HInstruction>( | |
| 1127 signature.parameterCount + 1); // Plus one for receiver. | |
| 1128 | |
| 1129 compiledArguments[0] = providedArguments[0]; // Receiver. | |
| 1130 int index = 1; | |
| 1131 for (; index <= signature.requiredParameterCount; index++) { | |
| 1132 compiledArguments[index] = providedArguments[index]; | |
| 1133 } | |
| 1134 if (!signature.optionalParametersAreNamed) { | |
| 1135 signature.forEachOptionalParameter((element) { | |
| 1136 if (index < providedArguments.length) { | |
| 1137 compiledArguments[index] = providedArguments[index]; | |
| 1138 } else { | |
| 1139 compiledArguments[index] = | |
| 1140 handleConstantForOptionalParameter(element); | |
| 1141 } | |
| 1142 index++; | |
| 1143 }); | |
| 1144 } else { | |
| 1145 /* Example: | |
| 1146 * void foo(a, {b, d, c}) | |
| 1147 * foo(0, d = 1, b = 2) | |
| 1148 * | |
| 1149 * providedArguments = [0, 2, 1] | |
| 1150 * selectorArgumentNames = [b, d] | |
| 1151 * signature.orderedOptionalParameters = [b, c, d] | |
| 1152 * | |
| 1153 * For each parameter name in the signature, if the argument name matches | |
| 1154 * we use the next provided argument, otherwise we get the default. | |
| 1155 */ | |
| 1156 List<String> selectorArgumentNames = selector.getOrderedNamedArguments(); | |
| 1157 int namedArgumentIndex = 0; | |
| 1158 int firstProvidedNamedArgument = index; | |
| 1159 signature.orderedOptionalParameters.forEach((element) { | |
| 1160 if (namedArgumentIndex < selectorArgumentNames.length && | |
| 1161 element.name == selectorArgumentNames[namedArgumentIndex]) { | |
| 1162 // The named argument was provided in the function invocation. | |
| 1163 compiledArguments[index] = providedArguments[ | |
| 1164 firstProvidedNamedArgument + namedArgumentIndex++]; | |
| 1165 } else { | |
| 1166 compiledArguments[index] = | |
| 1167 handleConstantForOptionalParameter(element); | |
| 1168 } | |
| 1169 index++; | |
| 1170 }); | |
| 1171 } | |
| 1172 return compiledArguments; | |
| 1173 } | |
| 1174 | |
| 1175 /** | |
| 1176 * Try to inline [element] within the currect context of the builder. The | |
| 1177 * insertion point is the state of the builder. | |
| 1178 */ | |
| 1179 bool tryInlineMethod(Element element, | |
| 1180 Selector selector, | |
| 1181 List<HInstruction> providedArguments, | |
| 1182 ast.Node currentNode) { | |
| 1183 // TODO(johnniwinther): Register this on the [registry]. Currently the | |
| 1184 // [CodegenRegistry] calls the enqueuer, but [element] should _not_ be | |
| 1185 // enqueued. | |
| 1186 backend.registerStaticUse(element, compiler.enqueuer.codegen); | |
| 1187 | |
| 1188 // Ensure that [element] is an implementation element. | |
| 1189 element = element.implementation; | |
| 1190 FunctionElement function = element; | |
| 1191 bool insideLoop = loopNesting > 0 || graph.calledInLoop; | |
| 1192 | |
| 1193 // Bail out early if the inlining decision is in the cache and we can't | |
| 1194 // inline (no need to check the hard constraints). | |
| 1195 bool cachedCanBeInlined = | |
| 1196 backend.inlineCache.canInline(function, insideLoop: insideLoop); | |
| 1197 if (cachedCanBeInlined == false) return false; | |
| 1198 | |
| 1199 bool meetsHardConstraints() { | |
| 1200 // Don't inline from one output unit to another. If something is deferred | |
| 1201 // it is to save space in the loading code. | |
| 1202 if (!compiler.deferredLoadTask | |
| 1203 .inSameOutputUnit(element,compiler.currentElement)) { | |
| 1204 return false; | |
| 1205 } | |
| 1206 if (compiler.disableInlining) return false; | |
| 1207 | |
| 1208 assert(selector != null | |
| 1209 || Elements.isStaticOrTopLevel(element) | |
| 1210 || element.isGenerativeConstructorBody); | |
| 1211 if (selector != null && !selector.applies(function, compiler.world)) { | |
| 1212 return false; | |
| 1213 } | |
| 1214 | |
| 1215 // Don't inline operator== methods if the parameter can be null. | |
| 1216 if (element.name == '==') { | |
| 1217 if (element.enclosingClass != compiler.objectClass | |
| 1218 && providedArguments[1].canBeNull()) { | |
| 1219 return false; | |
| 1220 } | |
| 1221 } | |
| 1222 | |
| 1223 // Generative constructors of native classes should not be called directly | |
| 1224 // and have an extra argument that causes problems with inlining. | |
| 1225 if (element.isGenerativeConstructor | |
| 1226 && Elements.isNativeOrExtendsNative(element.enclosingClass)) { | |
| 1227 return false; | |
| 1228 } | |
| 1229 | |
| 1230 // A generative constructor body is not seen by global analysis, | |
| 1231 // so we should not query for its type. | |
| 1232 if (!element.isGenerativeConstructorBody) { | |
| 1233 // Don't inline if the return type was inferred to be non-null empty. | |
| 1234 // This means that the function always throws an exception. | |
| 1235 TypeMask returnType = | |
| 1236 compiler.typesTask.getGuaranteedReturnTypeOfElement(element); | |
| 1237 if (returnType != null | |
| 1238 && returnType.isEmpty | |
| 1239 && !returnType.isNullable) { | |
| 1240 isReachable = false; | |
| 1241 return false; | |
| 1242 } | |
| 1243 } | |
| 1244 | |
| 1245 return true; | |
| 1246 } | |
| 1247 | |
| 1248 bool heuristicSayGoodToGo() { | |
| 1249 // Don't inline recursivly | |
| 1250 if (inliningStack.any((entry) => entry.function == function)) { | |
| 1251 return false; | |
| 1252 } | |
| 1253 | |
| 1254 if (inExpressionOfThrow) return false; | |
| 1255 | |
| 1256 if (element.isSynthesized) return true; | |
| 1257 | |
| 1258 if (cachedCanBeInlined == true) return cachedCanBeInlined; | |
| 1259 | |
| 1260 if (backend.functionsToAlwaysInline.contains(function)) { | |
| 1261 // Inline this function regardless of it's size. | |
| 1262 assert(InlineWeeder.canBeInlined(function.node, -1, false, | |
| 1263 allowLoops: true)); | |
| 1264 return true; | |
| 1265 } | |
| 1266 | |
| 1267 int numParameters = function.functionSignature.parameterCount; | |
| 1268 int maxInliningNodes; | |
| 1269 bool useMaxInliningNodes = true; | |
| 1270 if (insideLoop) { | |
| 1271 maxInliningNodes = InlineWeeder.INLINING_NODES_INSIDE_LOOP + | |
| 1272 InlineWeeder.INLINING_NODES_INSIDE_LOOP_ARG_FACTOR * numParameters; | |
| 1273 } else { | |
| 1274 maxInliningNodes = InlineWeeder.INLINING_NODES_OUTSIDE_LOOP + | |
| 1275 InlineWeeder.INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR * numParameters; | |
| 1276 } | |
| 1277 | |
| 1278 // If a method is called only once, and all the methods in the | |
| 1279 // inlining stack are called only once as well, we know we will | |
| 1280 // save on output size by inlining this method. | |
| 1281 TypesInferrer inferrer = compiler.typesTask.typesInferrer; | |
| 1282 if (inferrer.isCalledOnce(element) && allInlinedFunctionsCalledOnce) { | |
| 1283 useMaxInliningNodes = false; | |
| 1284 } | |
| 1285 bool canInline; | |
| 1286 ast.FunctionExpression functionNode = function.node; | |
| 1287 canInline = InlineWeeder.canBeInlined( | |
| 1288 functionNode, maxInliningNodes, useMaxInliningNodes); | |
| 1289 if (canInline) { | |
| 1290 backend.inlineCache.markAsInlinable(element, insideLoop: insideLoop); | |
| 1291 } else { | |
| 1292 backend.inlineCache.markAsNonInlinable(element, insideLoop: insideLoop); | |
| 1293 } | |
| 1294 return canInline; | |
| 1295 } | |
| 1296 | |
| 1297 void doInlining() { | |
| 1298 // Add an explicit null check on the receiver before doing the | |
| 1299 // inlining. We use [element] to get the same name in the | |
| 1300 // NoSuchMethodError message as if we had called it. | |
| 1301 if (element.isInstanceMember | |
| 1302 && !element.isGenerativeConstructorBody | |
| 1303 && (selector.mask == null || selector.mask.isNullable)) { | |
| 1304 addWithPosition( | |
| 1305 new HFieldGet(null, providedArguments[0], backend.dynamicType, | |
| 1306 isAssignable: false), | |
| 1307 currentNode); | |
| 1308 } | |
| 1309 List<HInstruction> compiledArguments = completeSendArgumentsList( | |
| 1310 function, selector, providedArguments, currentNode); | |
| 1311 enterInlinedMethod(function, currentNode, compiledArguments); | |
| 1312 inlinedFrom(function, () { | |
| 1313 if (!isReachable) { | |
| 1314 emitReturn(graph.addConstantNull(compiler), null); | |
| 1315 } else { | |
| 1316 doInline(function); | |
| 1317 } | |
| 1318 }); | |
| 1319 leaveInlinedMethod(); | |
| 1320 } | |
| 1321 | |
| 1322 if (meetsHardConstraints() && heuristicSayGoodToGo()) { | |
| 1323 doInlining(); | |
| 1324 registry.registerInlining( | |
| 1325 element, | |
| 1326 compiler.currentElement); | |
| 1327 return true; | |
| 1328 } | |
| 1329 | |
| 1330 return false; | |
| 1331 } | |
| 1332 | |
| 1333 bool get allInlinedFunctionsCalledOnce { | |
| 1334 return inliningStack.isEmpty || inliningStack.last.allFunctionsCalledOnce; | |
| 1335 } | |
| 1336 | |
| 1337 inlinedFrom(Element element, f()) { | |
| 1338 assert(element is FunctionElement || element is VariableElement); | |
| 1339 return compiler.withCurrentElement(element, () { | |
| 1340 // The [sourceElementStack] contains declaration elements. | |
| 1341 sourceElementStack.add(element.declaration); | |
| 1342 var result = f(); | |
| 1343 sourceElementStack.removeLast(); | |
| 1344 return result; | |
| 1345 }); | |
| 1346 } | |
| 1347 | |
| 1348 HInstruction handleConstantForOptionalParameter(Element parameter) { | |
| 1349 ConstantExpression constant = | |
| 1350 backend.constants.getConstantForVariable(parameter); | |
| 1351 assert(invariant(parameter, constant != null, | |
| 1352 message: 'No constant computed for $parameter')); | |
| 1353 return graph.addConstant(constant.value, compiler); | |
| 1354 } | |
| 1355 | |
| 1356 Element get currentNonClosureClass { | |
| 1357 ClassElement cls = sourceElement.enclosingClass; | |
| 1358 if (cls != null && cls.isClosure) { | |
| 1359 var closureClass = cls; | |
| 1360 return closureClass.methodElement.enclosingClass; | |
| 1361 } else { | |
| 1362 return cls; | |
| 1363 } | |
| 1364 } | |
| 1365 | |
| 1366 /** | |
| 1367 * Returns whether this builder is building code for [element]. | |
| 1368 */ | |
| 1369 bool isBuildingFor(Element element) { | |
| 1370 return work.element == element; | |
| 1371 } | |
| 1372 | |
| 1373 /// A stack of [DartType]s the have been seen during inlining of factory | |
| 1374 /// constructors. These types are preserved in [HInvokeStatic]s and | |
| 1375 /// [HForeignNews] inside the inline code and registered during code | |
| 1376 /// generation for these nodes. | |
| 1377 // TODO(karlklose): consider removing this and keeping the (substituted) | |
| 1378 // types of the type variables in an environment (like the [LocalsHandler]). | |
| 1379 final List<DartType> currentInlinedInstantiations = <DartType>[]; | |
| 1380 | |
| 1381 final List<AstInliningState> inliningStack = <AstInliningState>[]; | |
| 1382 | |
| 1383 Local returnLocal; | |
| 1384 DartType returnType; | |
| 1385 | |
| 1386 bool inTryStatement = false; | |
| 1387 | |
| 1388 ConstantValue getConstantForNode(ast.Node node) { | |
| 1389 ConstantExpression constant = | |
| 1390 backend.constants.getConstantForNode(node, elements); | |
| 1391 assert(invariant(node, constant != null, | |
| 1392 message: 'No constant computed for $node')); | |
| 1393 return constant.value; | |
| 1394 } | |
| 1395 | |
| 1396 HInstruction addConstant(ast.Node node) { | |
| 1397 return graph.addConstant(getConstantForNode(node), compiler); | |
| 1398 } | |
| 1399 | |
| 1400 bool isLazilyInitialized(VariableElement element) { | |
| 1401 ConstantExpression initialValue = | |
| 1402 backend.constants.getConstantForVariable(element); | |
| 1403 return initialValue == null; | |
| 1404 } | |
| 1405 | |
| 1406 TypeMask cachedTypeOfThis; | |
| 1407 | |
| 1408 TypeMask getTypeOfThis() { | |
| 1409 TypeMask result = cachedTypeOfThis; | |
| 1410 if (result == null) { | |
| 1411 ThisLocal local = localsHandler.closureData.thisLocal; | |
| 1412 ClassElement cls = local.enclosingClass; | |
| 1413 ClassWorld classWorld = compiler.world; | |
| 1414 if (classWorld.isUsedAsMixin(cls)) { | |
| 1415 // If the enclosing class is used as a mixin, [:this:] can be | |
| 1416 // of the class that mixins the enclosing class. These two | |
| 1417 // classes do not have a subclass relationship, so, for | |
| 1418 // simplicity, we mark the type as an interface type. | |
| 1419 result = new TypeMask.nonNullSubtype(cls.declaration, compiler.world); | |
| 1420 } else { | |
| 1421 result = new TypeMask.nonNullSubclass(cls.declaration, compiler.world); | |
| 1422 } | |
| 1423 cachedTypeOfThis = result; | |
| 1424 } | |
| 1425 return result; | |
| 1426 } | |
| 1427 | |
| 1428 Map<Element, TypeMask> cachedTypesOfCapturedVariables = | |
| 1429 new Map<Element, TypeMask>(); | |
| 1430 | |
| 1431 TypeMask getTypeOfCapturedVariable(Element element) { | |
| 1432 assert(element.isField); | |
| 1433 return cachedTypesOfCapturedVariables.putIfAbsent(element, () { | |
| 1434 return TypeMaskFactory.inferredTypeForElement(element, compiler); | |
| 1435 }); | |
| 1436 } | |
| 1437 | |
| 1438 /** | |
| 1439 * Documentation wanted -- johnniwinther | |
| 1440 * | |
| 1441 * Invariant: [functionElement] must be an implementation element. | |
| 1442 */ | |
| 1443 HGraph buildMethod(FunctionElement functionElement) { | |
| 1444 assert(invariant(functionElement, functionElement.isImplementation)); | |
| 1445 graph.calledInLoop = compiler.world.isCalledInLoop(functionElement); | |
| 1446 ast.FunctionExpression function = functionElement.node; | |
| 1447 assert(function != null); | |
| 1448 assert(!function.modifiers.isExternal); | |
| 1449 assert(elements.getFunctionDefinition(function) != null); | |
| 1450 openFunction(functionElement, function); | |
| 1451 String name = functionElement.name; | |
| 1452 // If [functionElement] is `operator==` we explicitely add a null check at | |
| 1453 // the beginning of the method. This is to avoid having call sites do the | |
| 1454 // null check. | |
| 1455 if (name == '==') { | |
| 1456 if (!backend.operatorEqHandlesNullArgument(functionElement)) { | |
| 1457 handleIf( | |
| 1458 function, | |
| 1459 () { | |
| 1460 HParameterValue parameter = parameters.values.first; | |
| 1461 push(new HIdentity( | |
| 1462 parameter, graph.addConstantNull(compiler), null, | |
| 1463 backend.boolType)); | |
| 1464 }, | |
| 1465 () { | |
| 1466 closeAndGotoExit(new HReturn( | |
| 1467 graph.addConstantBool(false, compiler))); | |
| 1468 }, | |
| 1469 null); | |
| 1470 } | |
| 1471 } | |
| 1472 function.body.accept(this); | |
| 1473 return closeFunction(); | |
| 1474 } | |
| 1475 | |
| 1476 HGraph buildCheckedSetter(VariableElement field) { | |
| 1477 openFunction(field, field.node); | |
| 1478 HInstruction thisInstruction = localsHandler.readThis(); | |
| 1479 // Use dynamic type because the type computed by the inferrer is | |
| 1480 // narrowed to the type annotation. | |
| 1481 HInstruction parameter = new HParameterValue(field, backend.dynamicType); | |
| 1482 // Add the parameter as the last instruction of the entry block. | |
| 1483 // If the method is intercepted, we want the actual receiver | |
| 1484 // to be the first parameter. | |
| 1485 graph.entry.addBefore(graph.entry.last, parameter); | |
| 1486 HInstruction value = | |
| 1487 potentiallyCheckType(parameter, field.type); | |
| 1488 add(new HFieldSet(field, thisInstruction, value)); | |
| 1489 return closeFunction(); | |
| 1490 } | |
| 1491 | |
| 1492 HGraph buildLazyInitializer(VariableElement variable) { | |
| 1493 ast.Node node = variable.node; | |
| 1494 openFunction(variable, node); | |
| 1495 assert(variable.initializer != null); | |
| 1496 visit(variable.initializer); | |
| 1497 HInstruction value = pop(); | |
| 1498 value = potentiallyCheckType(value, variable.type); | |
| 1499 closeAndGotoExit(new HReturn(value)); | |
| 1500 return closeFunction(); | |
| 1501 } | |
| 1502 | |
| 1503 /** | |
| 1504 * Returns the constructor body associated with the given constructor or | |
| 1505 * creates a new constructor body, if none can be found. | |
| 1506 * | |
| 1507 * Returns [:null:] if the constructor does not have a body. | |
| 1508 */ | |
| 1509 ConstructorBodyElement getConstructorBody(FunctionElement constructor) { | |
| 1510 assert(constructor.isGenerativeConstructor); | |
| 1511 assert(invariant(constructor, constructor.isImplementation)); | |
| 1512 if (constructor.isSynthesized) return null; | |
| 1513 ast.FunctionExpression node = constructor.node; | |
| 1514 // If we know the body doesn't have any code, we don't generate it. | |
| 1515 if (!node.hasBody()) return null; | |
| 1516 if (node.hasEmptyBody()) return null; | |
| 1517 ClassElement classElement = constructor.enclosingClass; | |
| 1518 ConstructorBodyElement bodyElement; | |
| 1519 classElement.forEachBackendMember((Element backendMember) { | |
| 1520 if (backendMember.isGenerativeConstructorBody) { | |
| 1521 ConstructorBodyElement body = backendMember; | |
| 1522 if (body.constructor == constructor) { | |
| 1523 // TODO(kasperl): Find a way of stopping the iteration | |
| 1524 // through the backend members. | |
| 1525 bodyElement = backendMember; | |
| 1526 } | |
| 1527 } | |
| 1528 }); | |
| 1529 if (bodyElement == null) { | |
| 1530 bodyElement = new ConstructorBodyElementX(constructor); | |
| 1531 classElement.addBackendMember(bodyElement); | |
| 1532 | |
| 1533 if (constructor.isPatch) { | |
| 1534 // Create origin body element for patched constructors. | |
| 1535 ConstructorBodyElementX patch = bodyElement; | |
| 1536 ConstructorBodyElementX origin = | |
| 1537 new ConstructorBodyElementX(constructor.origin); | |
| 1538 origin.applyPatch(patch); | |
| 1539 classElement.origin.addBackendMember(bodyElement.origin); | |
| 1540 } | |
| 1541 } | |
| 1542 assert(bodyElement.isGenerativeConstructorBody); | |
| 1543 return bodyElement; | |
| 1544 } | |
| 1545 | |
| 1546 HParameterValue addParameter(Entity parameter, TypeMask type) { | |
| 1547 assert(inliningStack.isEmpty); | |
| 1548 HParameterValue result = new HParameterValue(parameter, type); | |
| 1549 if (lastAddedParameter == null) { | |
| 1550 graph.entry.addBefore(graph.entry.first, result); | |
| 1551 } else { | |
| 1552 graph.entry.addAfter(lastAddedParameter, result); | |
| 1553 } | |
| 1554 lastAddedParameter = result; | |
| 1555 return result; | |
| 1556 } | |
| 1557 | |
| 1558 /** | |
| 1559 * This method sets up the local state of the builder for inlining [function]. | |
| 1560 * The arguments of the function are inserted into the [localsHandler]. | |
| 1561 * | |
| 1562 * When inlining a function, [:return:] statements are not emitted as | |
| 1563 * [HReturn] instructions. Instead, the value of a synthetic element is | |
| 1564 * updated in the [localsHandler]. This function creates such an element and | |
| 1565 * stores it in the [returnLocal] field. | |
| 1566 */ | |
| 1567 void setupStateForInlining(FunctionElement function, | |
| 1568 List<HInstruction> compiledArguments) { | |
| 1569 localsHandler = new LocalsHandler(this, function); | |
| 1570 localsHandler.closureData = | |
| 1571 compiler.closureToClassMapper.computeClosureToClassMapping( | |
| 1572 function, function.node, elements); | |
| 1573 returnLocal = new SyntheticLocal("result", function); | |
| 1574 localsHandler.updateLocal(returnLocal, | |
| 1575 graph.addConstantNull(compiler)); | |
| 1576 | |
| 1577 inTryStatement = false; // TODO(lry): why? Document. | |
| 1578 | |
| 1579 int argumentIndex = 0; | |
| 1580 if (function.isInstanceMember) { | |
| 1581 localsHandler.updateLocal(localsHandler.closureData.thisLocal, | |
| 1582 compiledArguments[argumentIndex++]); | |
| 1583 } | |
| 1584 | |
| 1585 FunctionSignature signature = function.functionSignature; | |
| 1586 signature.orderedForEachParameter((ParameterElement parameter) { | |
| 1587 HInstruction argument = compiledArguments[argumentIndex++]; | |
| 1588 localsHandler.updateLocal(parameter, argument); | |
| 1589 }); | |
| 1590 | |
| 1591 ClassElement enclosing = function.enclosingClass; | |
| 1592 if ((function.isConstructor || function.isGenerativeConstructorBody) | |
| 1593 && backend.classNeedsRti(enclosing)) { | |
| 1594 enclosing.typeVariables.forEach((TypeVariableType typeVariable) { | |
| 1595 HInstruction argument = compiledArguments[argumentIndex++]; | |
| 1596 localsHandler.updateLocal( | |
| 1597 localsHandler.getTypeVariableAsLocal(typeVariable), argument); | |
| 1598 }); | |
| 1599 } | |
| 1600 assert(argumentIndex == compiledArguments.length); | |
| 1601 | |
| 1602 elements = function.resolvedAst.elements; | |
| 1603 assert(elements != null); | |
| 1604 returnType = signature.type.returnType; | |
| 1605 stack = <HInstruction>[]; | |
| 1606 | |
| 1607 insertTraceCall(function); | |
| 1608 } | |
| 1609 | |
| 1610 void restoreState(AstInliningState state) { | |
| 1611 localsHandler = state.oldLocalsHandler; | |
| 1612 returnLocal = state.oldReturnLocal; | |
| 1613 inTryStatement = state.inTryStatement; | |
| 1614 elements = state.oldElements; | |
| 1615 returnType = state.oldReturnType; | |
| 1616 assert(stack.isEmpty); | |
| 1617 stack = state.oldStack; | |
| 1618 } | |
| 1619 | |
| 1620 /** | |
| 1621 * Run this builder on the body of the [function] to be inlined. | |
| 1622 */ | |
| 1623 void visitInlinedFunction(FunctionElement function) { | |
| 1624 potentiallyCheckInlinedParameterTypes(function); | |
| 1625 if (function.isGenerativeConstructor) { | |
| 1626 buildFactory(function); | |
| 1627 } else { | |
| 1628 ast.FunctionExpression functionNode = function.node; | |
| 1629 functionNode.body.accept(this); | |
| 1630 } | |
| 1631 } | |
| 1632 | |
| 1633 | |
| 1634 addInlinedInstantiation(DartType type) { | |
| 1635 if (type != null) { | |
| 1636 currentInlinedInstantiations.add(type); | |
| 1637 } | |
| 1638 } | |
| 1639 | |
| 1640 removeInlinedInstantiation(DartType type) { | |
| 1641 if (type != null) { | |
| 1642 currentInlinedInstantiations.removeLast(); | |
| 1643 } | |
| 1644 } | |
| 1645 | |
| 1646 bool providedArgumentsKnownToBeComplete(ast.Node currentNode) { | |
| 1647 /* When inlining the iterator methods generated for a [:for-in:] loop, the | |
| 1648 * [currentNode] is the [ForIn] tree. The compiler-generated iterator | |
| 1649 * invocations are known to have fully specified argument lists, no default | |
| 1650 * arguments are used. See invocations of [pushInvokeDynamic] in | |
| 1651 * [visitForIn]. | |
| 1652 */ | |
| 1653 return currentNode.asForIn() != null; | |
| 1654 } | |
| 1655 | |
| 1656 /** | |
| 1657 * In checked mode, generate type tests for the parameters of the inlined | |
| 1658 * function. | |
| 1659 */ | |
| 1660 void potentiallyCheckInlinedParameterTypes(FunctionElement function) { | |
| 1661 if (!compiler.enableTypeAssertions) return; | |
| 1662 | |
| 1663 FunctionSignature signature = function.functionSignature; | |
| 1664 signature.orderedForEachParameter((ParameterElement parameter) { | |
| 1665 HInstruction argument = localsHandler.readLocal(parameter); | |
| 1666 potentiallyCheckType(argument, parameter.type); | |
| 1667 }); | |
| 1668 } | |
| 1669 | |
| 1670 /** | |
| 1671 * Documentation wanted -- johnniwinther | |
| 1672 * | |
| 1673 * Invariant: [constructors] must contain only implementation elements. | |
| 1674 */ | |
| 1675 void inlineSuperOrRedirect(FunctionElement callee, | |
| 1676 List<HInstruction> compiledArguments, | |
| 1677 List<FunctionElement> constructors, | |
| 1678 Map<Element, HInstruction> fieldValues, | |
| 1679 FunctionElement caller) { | |
| 1680 callee = callee.implementation; | |
| 1681 compiler.withCurrentElement(callee, () { | |
| 1682 constructors.add(callee); | |
| 1683 ClassElement enclosingClass = callee.enclosingClass; | |
| 1684 if (backend.classNeedsRti(enclosingClass)) { | |
| 1685 // If [enclosingClass] needs RTI, we have to give a value to its | |
| 1686 // type parameters. | |
| 1687 ClassElement currentClass = caller.enclosingClass; | |
| 1688 // For a super constructor call, the type is the supertype of | |
| 1689 // [currentClass]. For a redirecting constructor, the type is | |
| 1690 // the current type. [InterfaceType.asInstanceOf] takes care | |
| 1691 // of both. | |
| 1692 InterfaceType type = currentClass.thisType.asInstanceOf(enclosingClass); | |
| 1693 type = localsHandler.substInContext(type); | |
| 1694 List<DartType> arguments = type.typeArguments; | |
| 1695 List<DartType> typeVariables = enclosingClass.typeVariables; | |
| 1696 if (!type.isRaw) { | |
| 1697 assert(arguments.length == typeVariables.length); | |
| 1698 Iterator<DartType> variables = typeVariables.iterator; | |
| 1699 type.typeArguments.forEach((DartType argument) { | |
| 1700 variables.moveNext(); | |
| 1701 TypeVariableType typeVariable = variables.current; | |
| 1702 localsHandler.updateLocal( | |
| 1703 localsHandler.getTypeVariableAsLocal(typeVariable), | |
| 1704 analyzeTypeArgument(argument)); | |
| 1705 }); | |
| 1706 } else { | |
| 1707 // If the supertype is a raw type, we need to set to null the | |
| 1708 // type variables. | |
| 1709 for (TypeVariableType variable in typeVariables) { | |
| 1710 localsHandler.updateLocal( | |
| 1711 localsHandler.getTypeVariableAsLocal(variable), | |
| 1712 graph.addConstantNull(compiler)); | |
| 1713 } | |
| 1714 } | |
| 1715 } | |
| 1716 | |
| 1717 // For redirecting constructors, the fields have already been | |
| 1718 // initialized by the caller. | |
| 1719 if (callee.enclosingClass != caller.enclosingClass) { | |
| 1720 inlinedFrom(callee, () { | |
| 1721 buildFieldInitializers(callee.enclosingElement.implementation, | |
| 1722 fieldValues); | |
| 1723 }); | |
| 1724 } | |
| 1725 | |
| 1726 int index = 0; | |
| 1727 FunctionSignature params = callee.functionSignature; | |
| 1728 params.orderedForEachParameter((ParameterElement parameter) { | |
| 1729 HInstruction argument = compiledArguments[index++]; | |
| 1730 // Because we are inlining the initializer, we must update | |
| 1731 // what was given as parameter. This will be used in case | |
| 1732 // there is a parameter check expression in the initializer. | |
| 1733 parameters[parameter] = argument; | |
| 1734 localsHandler.updateLocal(parameter, argument); | |
| 1735 // Don't forget to update the field, if the parameter is of the | |
| 1736 // form [:this.x:]. | |
| 1737 if (parameter.isInitializingFormal) { | |
| 1738 InitializingFormalElement fieldParameterElement = parameter; | |
| 1739 fieldValues[fieldParameterElement.fieldElement] = argument; | |
| 1740 } | |
| 1741 }); | |
| 1742 | |
| 1743 // Build the initializers in the context of the new constructor. | |
| 1744 TreeElements oldElements = elements; | |
| 1745 ResolvedAst resolvedAst = callee.resolvedAst; | |
| 1746 elements = resolvedAst.elements; | |
| 1747 ClosureClassMap oldClosureData = localsHandler.closureData; | |
| 1748 ast.Node node = resolvedAst.node; | |
| 1749 ClosureClassMap newClosureData = | |
| 1750 compiler.closureToClassMapper.computeClosureToClassMapping( | |
| 1751 callee, node, elements); | |
| 1752 localsHandler.closureData = newClosureData; | |
| 1753 localsHandler.enterScope(node, callee); | |
| 1754 buildInitializers(callee, constructors, fieldValues); | |
| 1755 localsHandler.closureData = oldClosureData; | |
| 1756 elements = oldElements; | |
| 1757 }); | |
| 1758 } | |
| 1759 | |
| 1760 /** | |
| 1761 * Run through the initializers and inline all field initializers. Recursively | |
| 1762 * inlines super initializers. | |
| 1763 * | |
| 1764 * The constructors of the inlined initializers is added to [constructors] | |
| 1765 * with sub constructors having a lower index than super constructors. | |
| 1766 * | |
| 1767 * Invariant: The [constructor] and elements in [constructors] must all be | |
| 1768 * implementation elements. | |
| 1769 */ | |
| 1770 void buildInitializers(ConstructorElement constructor, | |
| 1771 List<FunctionElement> constructors, | |
| 1772 Map<Element, HInstruction> fieldValues) { | |
| 1773 assert(invariant(constructor, constructor.isImplementation)); | |
| 1774 if (constructor.isSynthesized) { | |
| 1775 List<HInstruction> arguments = <HInstruction>[]; | |
| 1776 HInstruction compileArgument(ParameterElement parameter) { | |
| 1777 return localsHandler.readLocal(parameter); | |
| 1778 } | |
| 1779 | |
| 1780 Element target = constructor.definingConstructor.implementation; | |
| 1781 bool match = Selector.addForwardingElementArgumentsToList( | |
| 1782 constructor, | |
| 1783 arguments, | |
| 1784 target, | |
| 1785 compileArgument, | |
| 1786 handleConstantForOptionalParameter, | |
| 1787 compiler.world); | |
| 1788 if (!match) { | |
| 1789 // If this fails, the selector we constructed for the call to a | |
| 1790 // forwarding constructor in a mixin application did not match the | |
| 1791 // constructor (which, for example, may happen when the libraries are | |
| 1792 // not compatible for private names, see issue 20394). | |
| 1793 compiler.internalError(constructor, | |
| 1794 'forwarding constructor call does not match'); | |
| 1795 } | |
| 1796 inlineSuperOrRedirect( | |
| 1797 target, | |
| 1798 arguments, | |
| 1799 constructors, | |
| 1800 fieldValues, | |
| 1801 constructor); | |
| 1802 return; | |
| 1803 } | |
| 1804 ast.FunctionExpression functionNode = constructor.node; | |
| 1805 | |
| 1806 bool foundSuperOrRedirect = false; | |
| 1807 if (functionNode.initializers != null) { | |
| 1808 Link<ast.Node> initializers = functionNode.initializers.nodes; | |
| 1809 for (Link<ast.Node> link = initializers; !link.isEmpty; link = link.tail)
{ | |
| 1810 assert(link.head is ast.Send); | |
| 1811 if (link.head is !ast.SendSet) { | |
| 1812 // A super initializer or constructor redirection. | |
| 1813 foundSuperOrRedirect = true; | |
| 1814 ast.Send call = link.head; | |
| 1815 assert(ast.Initializers.isSuperConstructorCall(call) || | |
| 1816 ast.Initializers.isConstructorRedirect(call)); | |
| 1817 FunctionElement target = elements[call].implementation; | |
| 1818 Selector selector = elements.getSelector(call); | |
| 1819 Link<ast.Node> arguments = call.arguments; | |
| 1820 List<HInstruction> compiledArguments = new List<HInstruction>(); | |
| 1821 inlinedFrom(constructor, () { | |
| 1822 addStaticSendArgumentsToList(selector, | |
| 1823 arguments, | |
| 1824 target, | |
| 1825 compiledArguments); | |
| 1826 }); | |
| 1827 inlineSuperOrRedirect(target, | |
| 1828 compiledArguments, | |
| 1829 constructors, | |
| 1830 fieldValues, | |
| 1831 constructor); | |
| 1832 } else { | |
| 1833 // A field initializer. | |
| 1834 ast.SendSet init = link.head; | |
| 1835 Link<ast.Node> arguments = init.arguments; | |
| 1836 assert(!arguments.isEmpty && arguments.tail.isEmpty); | |
| 1837 inlinedFrom(constructor, () { | |
| 1838 visit(arguments.head); | |
| 1839 }); | |
| 1840 fieldValues[elements[init]] = pop(); | |
| 1841 } | |
| 1842 } | |
| 1843 } | |
| 1844 | |
| 1845 if (!foundSuperOrRedirect) { | |
| 1846 // No super initializer found. Try to find the default constructor if | |
| 1847 // the class is not Object. | |
| 1848 ClassElement enclosingClass = constructor.enclosingClass; | |
| 1849 ClassElement superClass = enclosingClass.superclass; | |
| 1850 if (!enclosingClass.isObject) { | |
| 1851 assert(superClass != null); | |
| 1852 assert(superClass.resolutionState == STATE_DONE); | |
| 1853 Selector selector = | |
| 1854 new Selector.callDefaultConstructor(enclosingClass.library); | |
| 1855 // TODO(johnniwinther): Should we find injected constructors as well? | |
| 1856 FunctionElement target = superClass.lookupConstructor(selector); | |
| 1857 if (target == null) { | |
| 1858 compiler.internalError(superClass, | |
| 1859 "No default constructor available."); | |
| 1860 } | |
| 1861 List<HInstruction> arguments = <HInstruction>[]; | |
| 1862 selector.addArgumentsToList(const Link<ast.Node>(), | |
| 1863 arguments, | |
| 1864 target.implementation, | |
| 1865 null, | |
| 1866 handleConstantForOptionalParameter, | |
| 1867 compiler.world); | |
| 1868 inlineSuperOrRedirect(target, | |
| 1869 arguments, | |
| 1870 constructors, | |
| 1871 fieldValues, | |
| 1872 constructor); | |
| 1873 } | |
| 1874 } | |
| 1875 } | |
| 1876 | |
| 1877 /** | |
| 1878 * Run through the fields of [cls] and add their potential | |
| 1879 * initializers. | |
| 1880 * | |
| 1881 * Invariant: [classElement] must be an implementation element. | |
| 1882 */ | |
| 1883 void buildFieldInitializers(ClassElement classElement, | |
| 1884 Map<Element, HInstruction> fieldValues) { | |
| 1885 assert(invariant(classElement, classElement.isImplementation)); | |
| 1886 classElement.forEachInstanceField( | |
| 1887 (ClassElement enclosingClass, VariableElement member) { | |
| 1888 compiler.withCurrentElement(member, () { | |
| 1889 TreeElements definitions = member.treeElements; | |
| 1890 ast.Node node = member.node; | |
| 1891 ast.Expression initializer = member.initializer; | |
| 1892 if (initializer == null) { | |
| 1893 // Unassigned fields of native classes are not initialized to | |
| 1894 // prevent overwriting pre-initialized native properties. | |
| 1895 if (!Elements.isNativeOrExtendsNative(classElement)) { | |
| 1896 fieldValues[member] = graph.addConstantNull(compiler); | |
| 1897 } | |
| 1898 } else { | |
| 1899 ast.Node right = initializer; | |
| 1900 TreeElements savedElements = elements; | |
| 1901 elements = definitions; | |
| 1902 // In case the field initializer uses closures, run the | |
| 1903 // closure to class mapper. | |
| 1904 compiler.closureToClassMapper.computeClosureToClassMapping( | |
| 1905 member, node, elements); | |
| 1906 inlinedFrom(member, () => right.accept(this)); | |
| 1907 elements = savedElements; | |
| 1908 fieldValues[member] = pop(); | |
| 1909 } | |
| 1910 }); | |
| 1911 }); | |
| 1912 } | |
| 1913 | |
| 1914 /** | |
| 1915 * Build the factory function corresponding to the constructor | |
| 1916 * [functionElement]: | |
| 1917 * - Initialize fields with the values of the field initializers of the | |
| 1918 * current constructor and super constructors or constructors redirected | |
| 1919 * to, starting from the current constructor. | |
| 1920 * - Call the constructor bodies, starting from the constructor(s) in the | |
| 1921 * super class(es). | |
| 1922 */ | |
| 1923 HGraph buildFactory(FunctionElement functionElement) { | |
| 1924 functionElement = functionElement.implementation; | |
| 1925 ClassElement classElement = | |
| 1926 functionElement.enclosingClass.implementation; | |
| 1927 bool isNativeUpgradeFactory = | |
| 1928 Elements.isNativeOrExtendsNative(classElement); | |
| 1929 ast.FunctionExpression function = functionElement.node; | |
| 1930 // Note that constructors (like any other static function) do not need | |
| 1931 // to deal with optional arguments. It is the callers job to provide all | |
| 1932 // arguments as if they were positional. | |
| 1933 | |
| 1934 if (inliningStack.isEmpty) { | |
| 1935 // The initializer list could contain closures. | |
| 1936 openFunction(functionElement, function); | |
| 1937 } | |
| 1938 | |
| 1939 Map<Element, HInstruction> fieldValues = new Map<Element, HInstruction>(); | |
| 1940 | |
| 1941 // Compile the possible initialization code for local fields and | |
| 1942 // super fields. | |
| 1943 buildFieldInitializers(classElement, fieldValues); | |
| 1944 | |
| 1945 // Compile field-parameters such as [:this.x:]. | |
| 1946 FunctionSignature params = functionElement.functionSignature; | |
| 1947 params.orderedForEachParameter((ParameterElement parameter) { | |
| 1948 if (parameter.isInitializingFormal) { | |
| 1949 // If the [element] is a field-parameter then | |
| 1950 // initialize the field element with its value. | |
| 1951 InitializingFormalElement fieldParameter = parameter; | |
| 1952 HInstruction parameterValue = | |
| 1953 localsHandler.readLocal(fieldParameter); | |
| 1954 fieldValues[fieldParameter.fieldElement] = parameterValue; | |
| 1955 } | |
| 1956 }); | |
| 1957 | |
| 1958 // Analyze the constructor and all referenced constructors and collect | |
| 1959 // initializers and constructor bodies. | |
| 1960 List<FunctionElement> constructors = <FunctionElement>[functionElement]; | |
| 1961 buildInitializers(functionElement, constructors, fieldValues); | |
| 1962 | |
| 1963 // Call the JavaScript constructor with the fields as argument. | |
| 1964 List<HInstruction> constructorArguments = <HInstruction>[]; | |
| 1965 List<Element> fields = <Element>[]; | |
| 1966 | |
| 1967 classElement.forEachInstanceField( | |
| 1968 (ClassElement enclosingClass, VariableElement member) { | |
| 1969 HInstruction value = fieldValues[member]; | |
| 1970 if (value == null) { | |
| 1971 // Uninitialized native fields are pre-initialized by the native | |
| 1972 // implementation. | |
| 1973 assert(isNativeUpgradeFactory); | |
| 1974 } else { | |
| 1975 fields.add(member); | |
| 1976 DartType type = localsHandler.substInContext(member.type); | |
| 1977 constructorArguments.add(potentiallyCheckType(value, type)); | |
| 1978 } | |
| 1979 }, | |
| 1980 includeSuperAndInjectedMembers: true); | |
| 1981 | |
| 1982 InterfaceType type = classElement.thisType; | |
| 1983 TypeMask ssaType = | |
| 1984 new TypeMask.nonNullExact(classElement.declaration, compiler.world); | |
| 1985 List<DartType> instantiatedTypes; | |
| 1986 addInlinedInstantiation(type); | |
| 1987 if (!currentInlinedInstantiations.isEmpty) { | |
| 1988 instantiatedTypes = new List<DartType>.from(currentInlinedInstantiations); | |
| 1989 } | |
| 1990 | |
| 1991 HInstruction newObject; | |
| 1992 if (!isNativeUpgradeFactory) { | |
| 1993 newObject = new HForeignNew(classElement, | |
| 1994 ssaType, | |
| 1995 constructorArguments, | |
| 1996 instantiatedTypes); | |
| 1997 add(newObject); | |
| 1998 } else { | |
| 1999 // Bulk assign to the initialized fields. | |
| 2000 newObject = graph.explicitReceiverParameter; | |
| 2001 // Null guard ensures an error if we are being called from an explicit | |
| 2002 // 'new' of the constructor instead of via an upgrade. It is optimized out | |
| 2003 // if there are field initializers. | |
| 2004 add(new HFieldGet( | |
| 2005 null, newObject, backend.dynamicType, isAssignable: false)); | |
| 2006 for (int i = 0; i < fields.length; i++) { | |
| 2007 add(new HFieldSet(fields[i], newObject, constructorArguments[i])); | |
| 2008 } | |
| 2009 } | |
| 2010 removeInlinedInstantiation(type); | |
| 2011 // Create the runtime type information, if needed. | |
| 2012 if (backend.classNeedsRti(classElement)) { | |
| 2013 // Read the values of the type arguments and create a list to set on the | |
| 2014 // newly create object. We can identify the case where the new list | |
| 2015 // would be of the form: | |
| 2016 // [getTypeArgumentByIndex(this, 0), .., getTypeArgumentByIndex(this, k)] | |
| 2017 // and k is the number of type arguments of this. If this is the case, | |
| 2018 // we can simply copy the list from this. | |
| 2019 | |
| 2020 // These locals are modified by [isIndexedTypeArgumentGet]. | |
| 2021 HThis source; // The source of the type arguments. | |
| 2022 bool allIndexed = true; | |
| 2023 int expectedIndex = 0; | |
| 2024 ClassElement contextClass; // The class of `this`. | |
| 2025 int remainingTypeVariables; // The number of 'remaining type variables' | |
| 2026 // of `this`. | |
| 2027 | |
| 2028 /// Helper to identify instructions that read a type variable without | |
| 2029 /// substitution (that is, directly use the index). These instructions | |
| 2030 /// are of the form: | |
| 2031 /// HInvokeStatic(getTypeArgumentByIndex, this, index) | |
| 2032 /// | |
| 2033 /// Return `true` if [instruction] is of that form and the index is the | |
| 2034 /// next index in the sequence (held in [expectedIndex]). | |
| 2035 bool isIndexedTypeArgumentGet(HInstruction instruction) { | |
| 2036 if (instruction is! HInvokeStatic) return false; | |
| 2037 HInvokeStatic invoke = instruction; | |
| 2038 if (invoke.element != backend.getGetTypeArgumentByIndex()) { | |
| 2039 return false; | |
| 2040 } | |
| 2041 HConstant index = invoke.inputs[1]; | |
| 2042 HInstruction newSource = invoke.inputs[0]; | |
| 2043 if (newSource is! HThis) { | |
| 2044 return false; | |
| 2045 } | |
| 2046 if (source == null) { | |
| 2047 // This is the first match. Extract the context class for the type | |
| 2048 // variables and get the list of type variables to keep track of how | |
| 2049 // many arguments we need to process. | |
| 2050 source = newSource; | |
| 2051 contextClass = source.sourceElement.enclosingClass; | |
| 2052 remainingTypeVariables = contextClass.typeVariables.length; | |
| 2053 } else { | |
| 2054 assert(source == newSource); | |
| 2055 } | |
| 2056 // If there are no more type variables, then there are more type | |
| 2057 // arguments for the new object than the source has, and it can't be | |
| 2058 // a copy. Otherwise remove one argument. | |
| 2059 if (remainingTypeVariables == 0) return false; | |
| 2060 remainingTypeVariables--; | |
| 2061 // Check that the index is the one we expect. | |
| 2062 IntConstantValue constant = index.constant; | |
| 2063 return constant.primitiveValue == expectedIndex++; | |
| 2064 } | |
| 2065 | |
| 2066 List<HInstruction> typeArguments = <HInstruction>[]; | |
| 2067 classElement.typeVariables.forEach((TypeVariableType typeVariable) { | |
| 2068 HInstruction argument = localsHandler.readLocal( | |
| 2069 localsHandler.getTypeVariableAsLocal(typeVariable)); | |
| 2070 if (allIndexed && !isIndexedTypeArgumentGet(argument)) { | |
| 2071 allIndexed = false; | |
| 2072 } | |
| 2073 typeArguments.add(argument); | |
| 2074 }); | |
| 2075 | |
| 2076 if (source != null && allIndexed && remainingTypeVariables == 0) { | |
| 2077 copyRuntimeTypeInfo(source, newObject); | |
| 2078 } else { | |
| 2079 newObject = | |
| 2080 callSetRuntimeTypeInfo(classElement, typeArguments, newObject); | |
| 2081 } | |
| 2082 } | |
| 2083 | |
| 2084 // Generate calls to the constructor bodies. | |
| 2085 HInstruction interceptor = null; | |
| 2086 for (int index = constructors.length - 1; index >= 0; index--) { | |
| 2087 FunctionElement constructor = constructors[index]; | |
| 2088 assert(invariant(functionElement, constructor.isImplementation)); | |
| 2089 ConstructorBodyElement body = getConstructorBody(constructor); | |
| 2090 if (body == null) continue; | |
| 2091 | |
| 2092 List bodyCallInputs = <HInstruction>[]; | |
| 2093 if (isNativeUpgradeFactory) { | |
| 2094 if (interceptor == null) { | |
| 2095 ConstantValue constant = | |
| 2096 new InterceptorConstantValue(classElement.thisType); | |
| 2097 interceptor = graph.addConstant(constant, compiler); | |
| 2098 } | |
| 2099 bodyCallInputs.add(interceptor); | |
| 2100 } | |
| 2101 bodyCallInputs.add(newObject); | |
| 2102 ResolvedAst resolvedAst = constructor.resolvedAst; | |
| 2103 TreeElements elements = resolvedAst.elements; | |
| 2104 ast.Node node = resolvedAst.node; | |
| 2105 ClosureClassMap parameterClosureData = | |
| 2106 compiler.closureToClassMapper.getMappingForNestedFunction(node); | |
| 2107 | |
| 2108 FunctionSignature functionSignature = body.functionSignature; | |
| 2109 // Provide the parameters to the generative constructor body. | |
| 2110 functionSignature.orderedForEachParameter((ParameterElement parameter) { | |
| 2111 // If [parameter] is boxed, it will be a field in the box passed as the | |
| 2112 // last parameter. So no need to directly pass it. | |
| 2113 if (!localsHandler.isBoxed(parameter)) { | |
| 2114 bodyCallInputs.add(localsHandler.readLocal(parameter)); | |
| 2115 } | |
| 2116 }); | |
| 2117 | |
| 2118 ClassElement currentClass = constructor.enclosingClass; | |
| 2119 if (backend.classNeedsRti(currentClass)) { | |
| 2120 // If [currentClass] needs RTI, we add the type variables as | |
| 2121 // parameters of the generative constructor body. | |
| 2122 currentClass.typeVariables.forEach((TypeVariableType argument) { | |
| 2123 // TODO(johnniwinther): Substitute [argument] with | |
| 2124 // `localsHandler.substInContext(argument)`. | |
| 2125 bodyCallInputs.add(localsHandler.readLocal( | |
| 2126 localsHandler.getTypeVariableAsLocal(argument))); | |
| 2127 }); | |
| 2128 } | |
| 2129 | |
| 2130 // If there are locals that escape (ie mutated in closures), we | |
| 2131 // pass the box to the constructor. | |
| 2132 ClosureScope scopeData = parameterClosureData.capturingScopes[node]; | |
| 2133 if (scopeData != null) { | |
| 2134 bodyCallInputs.add(localsHandler.readLocal(scopeData.boxElement)); | |
| 2135 } | |
| 2136 | |
| 2137 if (!isNativeUpgradeFactory && // TODO(13836): Fix inlining. | |
| 2138 tryInlineMethod(body, null, bodyCallInputs, function)) { | |
| 2139 pop(); | |
| 2140 } else { | |
| 2141 HInvokeConstructorBody invoke = new HInvokeConstructorBody( | |
| 2142 body.declaration, bodyCallInputs, backend.nonNullType); | |
| 2143 invoke.sideEffects = | |
| 2144 compiler.world.getSideEffectsOfElement(constructor); | |
| 2145 add(invoke); | |
| 2146 } | |
| 2147 } | |
| 2148 if (inliningStack.isEmpty) { | |
| 2149 closeAndGotoExit(new HReturn(newObject)); | |
| 2150 return closeFunction(); | |
| 2151 } else { | |
| 2152 localsHandler.updateLocal(returnLocal, newObject); | |
| 2153 return null; | |
| 2154 } | |
| 2155 } | |
| 2156 | |
| 2157 /** | |
| 2158 * Documentation wanted -- johnniwinther | |
| 2159 * | |
| 2160 * Invariant: [functionElement] must be the implementation element. | |
| 2161 */ | |
| 2162 void openFunction(Element element, ast.Node node) { | |
| 2163 assert(invariant(element, element.isImplementation)); | |
| 2164 HBasicBlock block = graph.addNewBlock(); | |
| 2165 open(graph.entry); | |
| 2166 | |
| 2167 localsHandler.startFunction(element, node); | |
| 2168 close(new HGoto()).addSuccessor(block); | |
| 2169 | |
| 2170 open(block); | |
| 2171 | |
| 2172 // Add the type parameters of the class as parameters of this method. This | |
| 2173 // must be done before adding the normal parameters, because their types | |
| 2174 // may contain references to type variables. | |
| 2175 var enclosing = element.enclosingElement; | |
| 2176 if ((element.isConstructor || element.isGenerativeConstructorBody) | |
| 2177 && backend.classNeedsRti(enclosing)) { | |
| 2178 enclosing.typeVariables.forEach((TypeVariableType typeVariable) { | |
| 2179 HParameterValue param = addParameter( | |
| 2180 typeVariable.element, backend.nonNullType); | |
| 2181 localsHandler.directLocals[ | |
| 2182 localsHandler.getTypeVariableAsLocal(typeVariable)] = param; | |
| 2183 }); | |
| 2184 } | |
| 2185 | |
| 2186 if (element is FunctionElement) { | |
| 2187 FunctionElement functionElement = element; | |
| 2188 FunctionSignature signature = functionElement.functionSignature; | |
| 2189 | |
| 2190 // Put the type checks in the first successor of the entry, | |
| 2191 // because that is where the type guards will also be inserted. | |
| 2192 // This way we ensure that a type guard will dominate the type | |
| 2193 // check. | |
| 2194 ClosureScope scopeData = | |
| 2195 localsHandler.closureData.capturingScopes[node]; | |
| 2196 signature.orderedForEachParameter((ParameterElement parameterElement) { | |
| 2197 if (element.isGenerativeConstructorBody) { | |
| 2198 if (scopeData != null && | |
| 2199 scopeData.isCapturedVariable(parameterElement)) { | |
| 2200 // The parameter will be a field in the box passed as the | |
| 2201 // last parameter. So no need to have it. | |
| 2202 return; | |
| 2203 } | |
| 2204 } | |
| 2205 HInstruction newParameter = | |
| 2206 localsHandler.directLocals[parameterElement]; | |
| 2207 if (!element.isConstructor || | |
| 2208 !(element as ConstructorElement).isRedirectingFactory) { | |
| 2209 // Redirection factories must not check their argument types. | |
| 2210 // Example: | |
| 2211 // | |
| 2212 // class A { | |
| 2213 // A(String foo) = A.b; | |
| 2214 // A(int foo) { print(foo); } | |
| 2215 // } | |
| 2216 // main() { | |
| 2217 // new A(499); // valid even in checked mode. | |
| 2218 // new A("foo"); // invalid in checked mode. | |
| 2219 // | |
| 2220 // Only the final target is allowed to check for the argument types. | |
| 2221 newParameter = | |
| 2222 potentiallyCheckType(newParameter, parameterElement.type); | |
| 2223 } | |
| 2224 localsHandler.directLocals[parameterElement] = newParameter; | |
| 2225 }); | |
| 2226 | |
| 2227 returnType = signature.type.returnType; | |
| 2228 } else { | |
| 2229 // Otherwise it is a lazy initializer which does not have parameters. | |
| 2230 assert(element is VariableElement); | |
| 2231 } | |
| 2232 | |
| 2233 insertTraceCall(element); | |
| 2234 } | |
| 2235 | |
| 2236 insertTraceCall(Element element) { | |
| 2237 if (JavaScriptBackend.TRACE_CALLS) { | |
| 2238 if (element == backend.traceHelper) return; | |
| 2239 n(e) => e == null ? '' : e.name; | |
| 2240 String name = "${n(element.library)}:${n(element.enclosingClass)}." | |
| 2241 "${n(element)}"; | |
| 2242 HConstant nameConstant = addConstantString(name); | |
| 2243 add(new HInvokeStatic(backend.traceHelper, | |
| 2244 <HInstruction>[nameConstant], | |
| 2245 backend.dynamicType)); | |
| 2246 } | |
| 2247 } | |
| 2248 | |
| 2249 /// Check that [type] is valid in the context of `localsHandler.contextClass`. | |
| 2250 /// This should only be called in assertions. | |
| 2251 bool assertTypeInContext(DartType type, [Spannable spannable]) { | |
| 2252 return invariant(spannable == null ? CURRENT_ELEMENT_SPANNABLE : spannable, | |
| 2253 () { | |
| 2254 ClassElement contextClass = Types.getClassContext(type); | |
| 2255 return contextClass == null || | |
| 2256 contextClass == localsHandler.contextClass; | |
| 2257 }, | |
| 2258 message: "Type '$type' is not valid context of " | |
| 2259 "${localsHandler.contextClass}."); | |
| 2260 } | |
| 2261 | |
| 2262 /// Build a [HTypeConversion] for convertion [original] to type [type]. | |
| 2263 /// | |
| 2264 /// Invariant: [type] must be valid in the context. | |
| 2265 /// See [LocalsHandler.substInContext]. | |
| 2266 HInstruction buildTypeConversion(HInstruction original, | |
| 2267 DartType type, | |
| 2268 int kind) { | |
| 2269 if (type == null) return original; | |
| 2270 type = type.unalias(compiler); | |
| 2271 assert(assertTypeInContext(type, original)); | |
| 2272 if (type.isInterfaceType && !type.treatAsRaw) { | |
| 2273 TypeMask subtype = new TypeMask.subtype(type.element, compiler.world); | |
| 2274 HInstruction representations = buildTypeArgumentRepresentations(type); | |
| 2275 add(representations); | |
| 2276 return new HTypeConversion.withTypeRepresentation(type, kind, subtype, | |
| 2277 original, representations); | |
| 2278 } else if (type.isTypeVariable) { | |
| 2279 TypeMask subtype = original.instructionType; | |
| 2280 HInstruction typeVariable = addTypeVariableReference(type); | |
| 2281 return new HTypeConversion.withTypeRepresentation(type, kind, subtype, | |
| 2282 original, typeVariable); | |
| 2283 } else if (type.isFunctionType) { | |
| 2284 String name = kind == HTypeConversion.CAST_TYPE_CHECK | |
| 2285 ? '_asCheck' : '_assertCheck'; | |
| 2286 | |
| 2287 List arguments = [buildFunctionType(type), original]; | |
| 2288 pushInvokeDynamic( | |
| 2289 null, | |
| 2290 new Selector.call(name, backend.jsHelperLibrary, 1), | |
| 2291 arguments); | |
| 2292 | |
| 2293 return new HTypeConversion(type, kind, original.instructionType, pop()); | |
| 2294 } else { | |
| 2295 return original.convertType(compiler, type, kind); | |
| 2296 } | |
| 2297 } | |
| 2298 | |
| 2299 HInstruction potentiallyBuildTypeHint(HInstruction original, DartType type) { | |
| 2300 if (!compiler.trustTypeAnnotations || type == null) return original; | |
| 2301 type = localsHandler.substInContext(type); | |
| 2302 if (!type.isInterfaceType) return original; | |
| 2303 TypeMask mask = new TypeMask.subtype(type.element, compiler.world); | |
| 2304 var result = new HTypeKnown.pinned(mask, original); | |
| 2305 return result; | |
| 2306 } | |
| 2307 | |
| 2308 HInstruction potentiallyCheckType(HInstruction original, DartType type, | |
| 2309 { int kind: HTypeConversion.CHECKED_MODE_CHECK }) { | |
| 2310 if (!compiler.enableTypeAssertions) return original; | |
| 2311 type = localsHandler.substInContext(type); | |
| 2312 HInstruction other = buildTypeConversion(original, type, kind); | |
| 2313 if (other != original) add(other); | |
| 2314 registry.registerIsCheck(type); | |
| 2315 return other; | |
| 2316 } | |
| 2317 | |
| 2318 void assertIsSubtype(ast.Node node, DartType subtype, DartType supertype, | |
| 2319 String message) { | |
| 2320 HInstruction subtypeInstruction = | |
| 2321 analyzeTypeArgument(localsHandler.substInContext(subtype)); | |
| 2322 HInstruction supertypeInstruction = | |
| 2323 analyzeTypeArgument(localsHandler.substInContext(supertype)); | |
| 2324 HInstruction messageInstruction = | |
| 2325 graph.addConstantString(new ast.DartString.literal(message), compiler); | |
| 2326 Element element = backend.getAssertIsSubtype(); | |
| 2327 var inputs = <HInstruction>[subtypeInstruction, supertypeInstruction, | |
| 2328 messageInstruction]; | |
| 2329 HInstruction assertIsSubtype = new HInvokeStatic( | |
| 2330 element, inputs, subtypeInstruction.instructionType); | |
| 2331 registry.registerTypeVariableBoundsSubtypeCheck(subtype, supertype); | |
| 2332 add(assertIsSubtype); | |
| 2333 } | |
| 2334 | |
| 2335 HGraph closeFunction() { | |
| 2336 // TODO(kasperl): Make this goto an implicit return. | |
| 2337 if (!isAborted()) closeAndGotoExit(new HGoto()); | |
| 2338 graph.finalize(); | |
| 2339 return graph; | |
| 2340 } | |
| 2341 | |
| 2342 void push(HInstruction instruction) { | |
| 2343 add(instruction); | |
| 2344 stack.add(instruction); | |
| 2345 } | |
| 2346 | |
| 2347 void pushWithPosition(HInstruction instruction, ast.Node node) { | |
| 2348 push(attachPosition(instruction, node)); | |
| 2349 } | |
| 2350 | |
| 2351 HInstruction pop() { | |
| 2352 return stack.removeLast(); | |
| 2353 } | |
| 2354 | |
| 2355 void dup() { | |
| 2356 stack.add(stack.last); | |
| 2357 } | |
| 2358 | |
| 2359 HInstruction popBoolified() { | |
| 2360 HInstruction value = pop(); | |
| 2361 if (compiler.enableTypeAssertions) { | |
| 2362 return potentiallyCheckType( | |
| 2363 value, | |
| 2364 compiler.boolClass.rawType, | |
| 2365 kind: HTypeConversion.BOOLEAN_CONVERSION_CHECK); | |
| 2366 } | |
| 2367 HInstruction result = new HBoolify(value, backend.boolType); | |
| 2368 add(result); | |
| 2369 return result; | |
| 2370 } | |
| 2371 | |
| 2372 HInstruction attachPosition(HInstruction target, ast.Node node) { | |
| 2373 if (node != null) { | |
| 2374 target.sourcePosition = sourceFileLocationForBeginToken(node); | |
| 2375 } | |
| 2376 return target; | |
| 2377 } | |
| 2378 | |
| 2379 SourceFileLocation sourceFileLocationForBeginToken(ast.Node node) => | |
| 2380 sourceFileLocationForToken(node, node.getBeginToken()); | |
| 2381 | |
| 2382 SourceFileLocation sourceFileLocationForEndToken(ast.Node node) => | |
| 2383 sourceFileLocationForToken(node, node.getEndToken()); | |
| 2384 | |
| 2385 SourceFileLocation sourceFileLocationForToken(ast.Node node, Token token) { | |
| 2386 SourceFile sourceFile = currentSourceFile(); | |
| 2387 SourceFileLocation location = | |
| 2388 new TokenSourceFileLocation(sourceFile, token, sourceElement.name); | |
| 2389 checkValidSourceFileLocation(location, sourceFile, token.charOffset); | |
| 2390 return location; | |
| 2391 } | |
| 2392 | |
| 2393 void visit(ast.Node node) { | |
| 2394 if (node != null) node.accept(this); | |
| 2395 } | |
| 2396 | |
| 2397 visitBlock(ast.Block node) { | |
| 2398 assert(!isAborted()); | |
| 2399 if (!isReachable) return; // This can only happen when inlining. | |
| 2400 for (Link<ast.Node> link = node.statements.nodes; | |
| 2401 !link.isEmpty; | |
| 2402 link = link.tail) { | |
| 2403 visit(link.head); | |
| 2404 if (!isReachable) { | |
| 2405 // The block has been aborted by a return or a throw. | |
| 2406 if (!stack.isEmpty) { | |
| 2407 compiler.internalError(node, 'Non-empty instruction stack.'); | |
| 2408 } | |
| 2409 return; | |
| 2410 } | |
| 2411 } | |
| 2412 assert(!current.isClosed()); | |
| 2413 if (!stack.isEmpty) { | |
| 2414 compiler.internalError(node, 'Non-empty instruction stack.'); | |
| 2415 } | |
| 2416 } | |
| 2417 | |
| 2418 visitClassNode(ast.ClassNode node) { | |
| 2419 compiler.internalError(node, | |
| 2420 'SsaBuilder.visitClassNode should not be called.'); | |
| 2421 } | |
| 2422 | |
| 2423 visitThrowExpression(ast.Expression expression) { | |
| 2424 bool old = inExpressionOfThrow; | |
| 2425 try { | |
| 2426 inExpressionOfThrow = true; | |
| 2427 visit(expression); | |
| 2428 } finally { | |
| 2429 inExpressionOfThrow = old; | |
| 2430 } | |
| 2431 } | |
| 2432 | |
| 2433 visitExpressionStatement(ast.ExpressionStatement node) { | |
| 2434 if (!isReachable) return; | |
| 2435 ast.Throw throwExpression = node.expression.asThrow(); | |
| 2436 if (throwExpression != null && inliningStack.isEmpty) { | |
| 2437 visitThrowExpression(throwExpression.expression); | |
| 2438 handleInTryStatement(); | |
| 2439 closeAndGotoExit(new HThrow(pop())); | |
| 2440 } else { | |
| 2441 visit(node.expression); | |
| 2442 pop(); | |
| 2443 } | |
| 2444 } | |
| 2445 | |
| 2446 /** | |
| 2447 * Creates a new loop-header block. The previous [current] block | |
| 2448 * is closed with an [HGoto] and replaced by the newly created block. | |
| 2449 * Also notifies the locals handler that we're entering a loop. | |
| 2450 */ | |
| 2451 JumpHandler beginLoopHeader(ast.Node node) { | |
| 2452 assert(!isAborted()); | |
| 2453 HBasicBlock previousBlock = close(new HGoto()); | |
| 2454 | |
| 2455 JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: true); | |
| 2456 HBasicBlock loopEntry = graph.addNewLoopHeaderBlock( | |
| 2457 jumpHandler.target, | |
| 2458 jumpHandler.labels()); | |
| 2459 previousBlock.addSuccessor(loopEntry); | |
| 2460 open(loopEntry); | |
| 2461 | |
| 2462 localsHandler.beginLoopHeader(loopEntry); | |
| 2463 return jumpHandler; | |
| 2464 } | |
| 2465 | |
| 2466 /** | |
| 2467 * Ends the loop: | |
| 2468 * - creates a new block and adds it as successor to the [branchExitBlock] and | |
| 2469 * any blocks that end in break. | |
| 2470 * - opens the new block (setting as [current]). | |
| 2471 * - notifies the locals handler that we're exiting a loop. | |
| 2472 * [savedLocals] are the locals from the end of the loop condition. | |
| 2473 * [branchExitBlock] is the exit (branching) block of the condition. Generally | |
| 2474 * this is not the top of the loop, since this would lead to critical edges. | |
| 2475 * It is null for degenerate do-while loops that have | |
| 2476 * no back edge because they abort (throw/return/break in the body and have | |
| 2477 * no continues). | |
| 2478 */ | |
| 2479 void endLoop(HBasicBlock loopEntry, | |
| 2480 HBasicBlock branchExitBlock, | |
| 2481 JumpHandler jumpHandler, | |
| 2482 LocalsHandler savedLocals) { | |
| 2483 HBasicBlock loopExitBlock = addNewBlock(); | |
| 2484 | |
| 2485 List<LocalsHandler> breakHandlers = <LocalsHandler>[]; | |
| 2486 // Collect data for the successors and the phis at each break. | |
| 2487 jumpHandler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) { | |
| 2488 breakInstruction.block.addSuccessor(loopExitBlock); | |
| 2489 breakHandlers.add(locals); | |
| 2490 }); | |
| 2491 | |
| 2492 // The exit block is a successor of the loop condition if it is reached. | |
| 2493 // We don't add the successor in the case of a while/for loop that aborts | |
| 2494 // because the caller of endLoop will be wiring up a special empty else | |
| 2495 // block instead. | |
| 2496 if (branchExitBlock != null) { | |
| 2497 branchExitBlock.addSuccessor(loopExitBlock); | |
| 2498 } | |
| 2499 // Update the phis at the loop entry with the current values of locals. | |
| 2500 localsHandler.endLoop(loopEntry); | |
| 2501 | |
| 2502 // Start generating code for the exit block. | |
| 2503 open(loopExitBlock); | |
| 2504 | |
| 2505 // Create a new localsHandler for the loopExitBlock with the correct phis. | |
| 2506 if (!breakHandlers.isEmpty) { | |
| 2507 if (branchExitBlock != null) { | |
| 2508 // Add the values of the locals at the end of the condition block to | |
| 2509 // the phis. These are the values that flow to the exit if the | |
| 2510 // condition fails. | |
| 2511 breakHandlers.add(savedLocals); | |
| 2512 } | |
| 2513 localsHandler = savedLocals.mergeMultiple(breakHandlers, loopExitBlock); | |
| 2514 } else { | |
| 2515 localsHandler = savedLocals; | |
| 2516 } | |
| 2517 } | |
| 2518 | |
| 2519 HSubGraphBlockInformation wrapStatementGraph(SubGraph statements) { | |
| 2520 if (statements == null) return null; | |
| 2521 return new HSubGraphBlockInformation(statements); | |
| 2522 } | |
| 2523 | |
| 2524 HSubExpressionBlockInformation wrapExpressionGraph(SubExpression expression) { | |
| 2525 if (expression == null) return null; | |
| 2526 return new HSubExpressionBlockInformation(expression); | |
| 2527 } | |
| 2528 | |
| 2529 // For while loops, initializer and update are null. | |
| 2530 // The condition function must return a boolean result. | |
| 2531 // None of the functions must leave anything on the stack. | |
| 2532 void handleLoop(ast.Node loop, | |
| 2533 void initialize(), | |
| 2534 HInstruction condition(), | |
| 2535 void update(), | |
| 2536 void body()) { | |
| 2537 // Generate: | |
| 2538 // <initializer> | |
| 2539 // loop-entry: | |
| 2540 // if (!<condition>) goto loop-exit; | |
| 2541 // <body> | |
| 2542 // <updates> | |
| 2543 // goto loop-entry; | |
| 2544 // loop-exit: | |
| 2545 | |
| 2546 localsHandler.startLoop(loop); | |
| 2547 | |
| 2548 // The initializer. | |
| 2549 SubExpression initializerGraph = null; | |
| 2550 HBasicBlock startBlock; | |
| 2551 if (initialize != null) { | |
| 2552 HBasicBlock initializerBlock = openNewBlock(); | |
| 2553 startBlock = initializerBlock; | |
| 2554 initialize(); | |
| 2555 assert(!isAborted()); | |
| 2556 initializerGraph = | |
| 2557 new SubExpression(initializerBlock, current); | |
| 2558 } | |
| 2559 | |
| 2560 loopNesting++; | |
| 2561 JumpHandler jumpHandler = beginLoopHeader(loop); | |
| 2562 HLoopInformation loopInfo = current.loopInformation; | |
| 2563 HBasicBlock conditionBlock = current; | |
| 2564 if (startBlock == null) startBlock = conditionBlock; | |
| 2565 | |
| 2566 HInstruction conditionInstruction = condition(); | |
| 2567 HBasicBlock conditionEndBlock = | |
| 2568 close(new HLoopBranch(conditionInstruction)); | |
| 2569 SubExpression conditionExpression = | |
| 2570 new SubExpression(conditionBlock, conditionEndBlock); | |
| 2571 | |
| 2572 // Save the values of the local variables at the end of the condition | |
| 2573 // block. These are the values that will flow to the loop exit if the | |
| 2574 // condition fails. | |
| 2575 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
| 2576 | |
| 2577 // The body. | |
| 2578 HBasicBlock beginBodyBlock = addNewBlock(); | |
| 2579 conditionEndBlock.addSuccessor(beginBodyBlock); | |
| 2580 open(beginBodyBlock); | |
| 2581 | |
| 2582 localsHandler.enterLoopBody(loop); | |
| 2583 body(); | |
| 2584 | |
| 2585 SubGraph bodyGraph = new SubGraph(beginBodyBlock, lastOpenedBlock); | |
| 2586 HBasicBlock bodyBlock = current; | |
| 2587 if (current != null) close(new HGoto()); | |
| 2588 | |
| 2589 SubExpression updateGraph; | |
| 2590 | |
| 2591 bool loopIsDegenerate = !jumpHandler.hasAnyContinue() && bodyBlock == null; | |
| 2592 if (!loopIsDegenerate) { | |
| 2593 // Update. | |
| 2594 // We create an update block, even when we are in a while loop. There the | |
| 2595 // update block is the jump-target for continue statements. We could avoid | |
| 2596 // the creation if there is no continue, but for now we always create it. | |
| 2597 HBasicBlock updateBlock = addNewBlock(); | |
| 2598 | |
| 2599 List<LocalsHandler> continueHandlers = <LocalsHandler>[]; | |
| 2600 jumpHandler.forEachContinue((HContinue instruction, | |
| 2601 LocalsHandler locals) { | |
| 2602 instruction.block.addSuccessor(updateBlock); | |
| 2603 continueHandlers.add(locals); | |
| 2604 }); | |
| 2605 | |
| 2606 | |
| 2607 if (bodyBlock != null) { | |
| 2608 continueHandlers.add(localsHandler); | |
| 2609 bodyBlock.addSuccessor(updateBlock); | |
| 2610 } | |
| 2611 | |
| 2612 open(updateBlock); | |
| 2613 localsHandler = | |
| 2614 continueHandlers[0].mergeMultiple(continueHandlers, updateBlock); | |
| 2615 | |
| 2616 HLabeledBlockInformation labelInfo; | |
| 2617 List<LabelDefinition> labels = jumpHandler.labels(); | |
| 2618 JumpTarget target = elements.getTargetDefinition(loop); | |
| 2619 if (!labels.isEmpty) { | |
| 2620 beginBodyBlock.setBlockFlow( | |
| 2621 new HLabeledBlockInformation( | |
| 2622 new HSubGraphBlockInformation(bodyGraph), | |
| 2623 jumpHandler.labels(), | |
| 2624 isContinue: true), | |
| 2625 updateBlock); | |
| 2626 } else if (target != null && target.isContinueTarget) { | |
| 2627 beginBodyBlock.setBlockFlow( | |
| 2628 new HLabeledBlockInformation.implicit( | |
| 2629 new HSubGraphBlockInformation(bodyGraph), | |
| 2630 target, | |
| 2631 isContinue: true), | |
| 2632 updateBlock); | |
| 2633 } | |
| 2634 | |
| 2635 localsHandler.enterLoopUpdates(loop); | |
| 2636 | |
| 2637 update(); | |
| 2638 | |
| 2639 HBasicBlock updateEndBlock = close(new HGoto()); | |
| 2640 // The back-edge completing the cycle. | |
| 2641 updateEndBlock.addSuccessor(conditionBlock); | |
| 2642 updateGraph = new SubExpression(updateBlock, updateEndBlock); | |
| 2643 | |
| 2644 // Avoid a critical edge from the condition to the loop-exit body. | |
| 2645 HBasicBlock conditionExitBlock = addNewBlock(); | |
| 2646 open(conditionExitBlock); | |
| 2647 close(new HGoto()); | |
| 2648 conditionEndBlock.addSuccessor(conditionExitBlock); | |
| 2649 | |
| 2650 endLoop(conditionBlock, conditionExitBlock, jumpHandler, savedLocals); | |
| 2651 | |
| 2652 conditionBlock.postProcessLoopHeader(); | |
| 2653 HLoopBlockInformation info = | |
| 2654 new HLoopBlockInformation( | |
| 2655 HLoopBlockInformation.loopType(loop), | |
| 2656 wrapExpressionGraph(initializerGraph), | |
| 2657 wrapExpressionGraph(conditionExpression), | |
| 2658 wrapStatementGraph(bodyGraph), | |
| 2659 wrapExpressionGraph(updateGraph), | |
| 2660 conditionBlock.loopInformation.target, | |
| 2661 conditionBlock.loopInformation.labels, | |
| 2662 sourceFileLocationForBeginToken(loop), | |
| 2663 sourceFileLocationForEndToken(loop)); | |
| 2664 | |
| 2665 startBlock.setBlockFlow(info, current); | |
| 2666 loopInfo.loopBlockInformation = info; | |
| 2667 } else { | |
| 2668 // The body of the for/while loop always aborts, so there is no back edge. | |
| 2669 // We turn the code into: | |
| 2670 // if (condition) { | |
| 2671 // body; | |
| 2672 // } else { | |
| 2673 // // We always create an empty else block to avoid critical edges. | |
| 2674 // } | |
| 2675 // | |
| 2676 // If there is any break in the body, we attach a synthetic | |
| 2677 // label to the if. | |
| 2678 HBasicBlock elseBlock = addNewBlock(); | |
| 2679 open(elseBlock); | |
| 2680 close(new HGoto()); | |
| 2681 // Pass the elseBlock as the branchBlock, because that's the block we go | |
| 2682 // to just before leaving the 'loop'. | |
| 2683 endLoop(conditionBlock, elseBlock, jumpHandler, savedLocals); | |
| 2684 | |
| 2685 SubGraph elseGraph = new SubGraph(elseBlock, elseBlock); | |
| 2686 // Remove the loop information attached to the header. | |
| 2687 conditionBlock.loopInformation = null; | |
| 2688 | |
| 2689 // Remove the [HLoopBranch] instruction and replace it with | |
| 2690 // [HIf]. | |
| 2691 HInstruction condition = conditionEndBlock.last.inputs[0]; | |
| 2692 conditionEndBlock.addAtExit(new HIf(condition)); | |
| 2693 conditionEndBlock.addSuccessor(elseBlock); | |
| 2694 conditionEndBlock.remove(conditionEndBlock.last); | |
| 2695 HIfBlockInformation info = | |
| 2696 new HIfBlockInformation( | |
| 2697 wrapExpressionGraph(conditionExpression), | |
| 2698 wrapStatementGraph(bodyGraph), | |
| 2699 wrapStatementGraph(elseGraph)); | |
| 2700 | |
| 2701 conditionEndBlock.setBlockFlow(info, current); | |
| 2702 HIf ifBlock = conditionEndBlock.last; | |
| 2703 ifBlock.blockInformation = conditionEndBlock.blockFlow; | |
| 2704 | |
| 2705 // If the body has any break, attach a synthesized label to the | |
| 2706 // if block. | |
| 2707 if (jumpHandler.hasAnyBreak()) { | |
| 2708 JumpTarget target = elements.getTargetDefinition(loop); | |
| 2709 LabelDefinition label = target.addLabel(null, 'loop'); | |
| 2710 label.setBreakTarget(); | |
| 2711 SubGraph labelGraph = new SubGraph(conditionBlock, current); | |
| 2712 HLabeledBlockInformation labelInfo = new HLabeledBlockInformation( | |
| 2713 new HSubGraphBlockInformation(labelGraph), | |
| 2714 <LabelDefinition>[label]); | |
| 2715 | |
| 2716 conditionBlock.setBlockFlow(labelInfo, current); | |
| 2717 | |
| 2718 jumpHandler.forEachBreak((HBreak breakInstruction, _) { | |
| 2719 HBasicBlock block = breakInstruction.block; | |
| 2720 block.addAtExit(new HBreak.toLabel(label)); | |
| 2721 block.remove(breakInstruction); | |
| 2722 }); | |
| 2723 } | |
| 2724 } | |
| 2725 jumpHandler.close(); | |
| 2726 loopNesting--; | |
| 2727 } | |
| 2728 | |
| 2729 visitFor(ast.For node) { | |
| 2730 assert(isReachable); | |
| 2731 assert(node.body != null); | |
| 2732 void buildInitializer() { | |
| 2733 ast.Node initializer = node.initializer; | |
| 2734 if (initializer == null) return; | |
| 2735 visit(initializer); | |
| 2736 if (initializer.asExpression() != null) { | |
| 2737 pop(); | |
| 2738 } | |
| 2739 } | |
| 2740 HInstruction buildCondition() { | |
| 2741 if (node.condition == null) { | |
| 2742 return graph.addConstantBool(true, compiler); | |
| 2743 } | |
| 2744 visit(node.condition); | |
| 2745 return popBoolified(); | |
| 2746 } | |
| 2747 void buildUpdate() { | |
| 2748 for (ast.Expression expression in node.update) { | |
| 2749 visit(expression); | |
| 2750 assert(!isAborted()); | |
| 2751 // The result of the update instruction isn't used, and can just | |
| 2752 // be dropped. | |
| 2753 HInstruction updateInstruction = pop(); | |
| 2754 } | |
| 2755 } | |
| 2756 void buildBody() { | |
| 2757 visit(node.body); | |
| 2758 } | |
| 2759 handleLoop(node, buildInitializer, buildCondition, buildUpdate, buildBody); | |
| 2760 } | |
| 2761 | |
| 2762 visitWhile(ast.While node) { | |
| 2763 assert(isReachable); | |
| 2764 HInstruction buildCondition() { | |
| 2765 visit(node.condition); | |
| 2766 return popBoolified(); | |
| 2767 } | |
| 2768 handleLoop(node, | |
| 2769 () {}, | |
| 2770 buildCondition, | |
| 2771 () {}, | |
| 2772 () { visit(node.body); }); | |
| 2773 } | |
| 2774 | |
| 2775 visitDoWhile(ast.DoWhile node) { | |
| 2776 assert(isReachable); | |
| 2777 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
| 2778 localsHandler.startLoop(node); | |
| 2779 loopNesting++; | |
| 2780 JumpHandler jumpHandler = beginLoopHeader(node); | |
| 2781 HLoopInformation loopInfo = current.loopInformation; | |
| 2782 HBasicBlock loopEntryBlock = current; | |
| 2783 HBasicBlock bodyEntryBlock = current; | |
| 2784 JumpTarget target = elements.getTargetDefinition(node); | |
| 2785 bool hasContinues = target != null && target.isContinueTarget; | |
| 2786 if (hasContinues) { | |
| 2787 // Add extra block to hang labels on. | |
| 2788 // It doesn't currently work if they are on the same block as the | |
| 2789 // HLoopInfo. The handling of HLabeledBlockInformation will visit a | |
| 2790 // SubGraph that starts at the same block again, so the HLoopInfo is | |
| 2791 // either handled twice, or it's handled after the labeled block info, | |
| 2792 // both of which generate the wrong code. | |
| 2793 // Using a separate block is just a simple workaround. | |
| 2794 bodyEntryBlock = openNewBlock(); | |
| 2795 } | |
| 2796 localsHandler.enterLoopBody(node); | |
| 2797 visit(node.body); | |
| 2798 | |
| 2799 // If there are no continues we could avoid the creation of the condition | |
| 2800 // block. This could also lead to a block having multiple entries and exits. | |
| 2801 HBasicBlock bodyExitBlock; | |
| 2802 bool isAbortingBody = false; | |
| 2803 if (current != null) { | |
| 2804 bodyExitBlock = close(new HGoto()); | |
| 2805 } else { | |
| 2806 isAbortingBody = true; | |
| 2807 bodyExitBlock = lastOpenedBlock; | |
| 2808 } | |
| 2809 | |
| 2810 SubExpression conditionExpression; | |
| 2811 bool loopIsDegenerate = isAbortingBody && !hasContinues; | |
| 2812 if (!loopIsDegenerate) { | |
| 2813 HBasicBlock conditionBlock = addNewBlock(); | |
| 2814 | |
| 2815 List<LocalsHandler> continueHandlers = <LocalsHandler>[]; | |
| 2816 jumpHandler.forEachContinue((HContinue instruction, | |
| 2817 LocalsHandler locals) { | |
| 2818 instruction.block.addSuccessor(conditionBlock); | |
| 2819 continueHandlers.add(locals); | |
| 2820 }); | |
| 2821 | |
| 2822 if (!isAbortingBody) { | |
| 2823 bodyExitBlock.addSuccessor(conditionBlock); | |
| 2824 } | |
| 2825 | |
| 2826 if (!continueHandlers.isEmpty) { | |
| 2827 if (!isAbortingBody) continueHandlers.add(localsHandler); | |
| 2828 localsHandler = | |
| 2829 savedLocals.mergeMultiple(continueHandlers, conditionBlock); | |
| 2830 SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock); | |
| 2831 List<LabelDefinition> labels = jumpHandler.labels(); | |
| 2832 HSubGraphBlockInformation bodyInfo = | |
| 2833 new HSubGraphBlockInformation(bodyGraph); | |
| 2834 HLabeledBlockInformation info; | |
| 2835 if (!labels.isEmpty) { | |
| 2836 info = new HLabeledBlockInformation(bodyInfo, labels, | |
| 2837 isContinue: true); | |
| 2838 } else { | |
| 2839 info = new HLabeledBlockInformation.implicit(bodyInfo, target, | |
| 2840 isContinue: true); | |
| 2841 } | |
| 2842 bodyEntryBlock.setBlockFlow(info, conditionBlock); | |
| 2843 } | |
| 2844 open(conditionBlock); | |
| 2845 | |
| 2846 visit(node.condition); | |
| 2847 assert(!isAborted()); | |
| 2848 HInstruction conditionInstruction = popBoolified(); | |
| 2849 HBasicBlock conditionEndBlock = close( | |
| 2850 new HLoopBranch(conditionInstruction, HLoopBranch.DO_WHILE_LOOP)); | |
| 2851 | |
| 2852 HBasicBlock avoidCriticalEdge = addNewBlock(); | |
| 2853 conditionEndBlock.addSuccessor(avoidCriticalEdge); | |
| 2854 open(avoidCriticalEdge); | |
| 2855 close(new HGoto()); | |
| 2856 avoidCriticalEdge.addSuccessor(loopEntryBlock); // The back-edge. | |
| 2857 | |
| 2858 conditionExpression = | |
| 2859 new SubExpression(conditionBlock, conditionEndBlock); | |
| 2860 | |
| 2861 // Avoid a critical edge from the condition to the loop-exit body. | |
| 2862 HBasicBlock conditionExitBlock = addNewBlock(); | |
| 2863 open(conditionExitBlock); | |
| 2864 close(new HGoto()); | |
| 2865 conditionEndBlock.addSuccessor(conditionExitBlock); | |
| 2866 | |
| 2867 endLoop(loopEntryBlock, conditionExitBlock, jumpHandler, localsHandler); | |
| 2868 | |
| 2869 loopEntryBlock.postProcessLoopHeader(); | |
| 2870 SubGraph bodyGraph = new SubGraph(loopEntryBlock, bodyExitBlock); | |
| 2871 HLoopBlockInformation loopBlockInfo = | |
| 2872 new HLoopBlockInformation( | |
| 2873 HLoopBlockInformation.DO_WHILE_LOOP, | |
| 2874 null, | |
| 2875 wrapExpressionGraph(conditionExpression), | |
| 2876 wrapStatementGraph(bodyGraph), | |
| 2877 null, | |
| 2878 loopEntryBlock.loopInformation.target, | |
| 2879 loopEntryBlock.loopInformation.labels, | |
| 2880 sourceFileLocationForBeginToken(node), | |
| 2881 sourceFileLocationForEndToken(node)); | |
| 2882 loopEntryBlock.setBlockFlow(loopBlockInfo, current); | |
| 2883 loopInfo.loopBlockInformation = loopBlockInfo; | |
| 2884 } else { | |
| 2885 // Since the loop has no back edge, we remove the loop information on the | |
| 2886 // header. | |
| 2887 loopEntryBlock.loopInformation = null; | |
| 2888 | |
| 2889 if (jumpHandler.hasAnyBreak()) { | |
| 2890 // Null branchBlock because the body of the do-while loop always aborts, | |
| 2891 // so we never get to the condition. | |
| 2892 endLoop(loopEntryBlock, null, jumpHandler, localsHandler); | |
| 2893 | |
| 2894 // Since the body of the loop has a break, we attach a synthesized label | |
| 2895 // to the body. | |
| 2896 SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock); | |
| 2897 JumpTarget target = elements.getTargetDefinition(node); | |
| 2898 LabelDefinition label = target.addLabel(null, 'loop'); | |
| 2899 label.setBreakTarget(); | |
| 2900 HLabeledBlockInformation info = new HLabeledBlockInformation( | |
| 2901 new HSubGraphBlockInformation(bodyGraph), <LabelDefinition>[label]); | |
| 2902 loopEntryBlock.setBlockFlow(info, current); | |
| 2903 jumpHandler.forEachBreak((HBreak breakInstruction, _) { | |
| 2904 HBasicBlock block = breakInstruction.block; | |
| 2905 block.addAtExit(new HBreak.toLabel(label)); | |
| 2906 block.remove(breakInstruction); | |
| 2907 }); | |
| 2908 } | |
| 2909 } | |
| 2910 jumpHandler.close(); | |
| 2911 loopNesting--; | |
| 2912 } | |
| 2913 | |
| 2914 visitFunctionExpression(ast.FunctionExpression node) { | |
| 2915 ClosureClassMap nestedClosureData = | |
| 2916 compiler.closureToClassMapper.getMappingForNestedFunction(node); | |
| 2917 assert(nestedClosureData != null); | |
| 2918 assert(nestedClosureData.closureClassElement != null); | |
| 2919 ClosureClassElement closureClassElement = | |
| 2920 nestedClosureData.closureClassElement; | |
| 2921 FunctionElement callElement = nestedClosureData.callElement; | |
| 2922 // TODO(ahe): This should be registered in codegen, not here. | |
| 2923 // TODO(johnniwinther): Is [registerStaticUse] equivalent to | |
| 2924 // [addToWorkList]? | |
| 2925 registry.registerStaticUse(callElement); | |
| 2926 // TODO(ahe): This should be registered in codegen, not here. | |
| 2927 registry.registerInstantiatedClass(closureClassElement); | |
| 2928 | |
| 2929 List<HInstruction> capturedVariables = <HInstruction>[]; | |
| 2930 closureClassElement.closureFields.forEach((ClosureFieldElement field) { | |
| 2931 Local capturedLocal = | |
| 2932 nestedClosureData.getLocalVariableForClosureField(field); | |
| 2933 assert(capturedLocal != null); | |
| 2934 capturedVariables.add(localsHandler.readLocal(capturedLocal)); | |
| 2935 }); | |
| 2936 | |
| 2937 TypeMask type = | |
| 2938 new TypeMask.nonNullExact(compiler.functionClass, compiler.world); | |
| 2939 push(new HForeignNew(closureClassElement, type, capturedVariables)); | |
| 2940 | |
| 2941 Element methodElement = nestedClosureData.closureElement; | |
| 2942 if (compiler.backend.methodNeedsRti(methodElement)) { | |
| 2943 registry.registerClosureWithFreeTypeVariables(methodElement); | |
| 2944 } | |
| 2945 } | |
| 2946 | |
| 2947 visitFunctionDeclaration(ast.FunctionDeclaration node) { | |
| 2948 assert(isReachable); | |
| 2949 visit(node.function); | |
| 2950 LocalFunctionElement localFunction = | |
| 2951 elements.getFunctionDefinition(node.function); | |
| 2952 localsHandler.updateLocal(localFunction, pop()); | |
| 2953 } | |
| 2954 | |
| 2955 visitIdentifier(ast.Identifier node) { | |
| 2956 if (node.isThis()) { | |
| 2957 stack.add(localsHandler.readThis()); | |
| 2958 } else { | |
| 2959 compiler.internalError(node, | |
| 2960 "SsaFromAstMixin.visitIdentifier on non-this."); | |
| 2961 } | |
| 2962 } | |
| 2963 | |
| 2964 visitIf(ast.If node) { | |
| 2965 assert(isReachable); | |
| 2966 handleIf(node, | |
| 2967 () => visit(node.condition), | |
| 2968 () => visit(node.thenPart), | |
| 2969 node.elsePart != null ? () => visit(node.elsePart) : null); | |
| 2970 } | |
| 2971 | |
| 2972 void handleIf(ast.Node diagnosticNode, | |
| 2973 void visitCondition(), void visitThen(), void visitElse()) { | |
| 2974 SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, diagnosticNode); | |
| 2975 branchBuilder.handleIf(visitCondition, visitThen, visitElse); | |
| 2976 } | |
| 2977 | |
| 2978 void visitLogicalAndOr(ast.Send node, ast.Operator op) { | |
| 2979 SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, node); | |
| 2980 branchBuilder.handleLogicalAndOrWithLeftNode( | |
| 2981 node.receiver, | |
| 2982 () { visit(node.argumentsNode); }, | |
| 2983 isAnd: ("&&" == op.source)); | |
| 2984 } | |
| 2985 | |
| 2986 void visitLogicalNot(ast.Send node) { | |
| 2987 assert(node.argumentsNode is ast.Prefix); | |
| 2988 visit(node.receiver); | |
| 2989 HNot not = new HNot(popBoolified(), backend.boolType); | |
| 2990 pushWithPosition(not, node); | |
| 2991 } | |
| 2992 | |
| 2993 void visitUnary(ast.Send node, ast.Operator op) { | |
| 2994 assert(node.argumentsNode is ast.Prefix); | |
| 2995 visit(node.receiver); | |
| 2996 assert(!identical(op.token.kind, PLUS_TOKEN)); | |
| 2997 HInstruction operand = pop(); | |
| 2998 | |
| 2999 // See if we can constant-fold right away. This avoids rewrites later on. | |
| 3000 if (operand is HConstant) { | |
| 3001 UnaryOperation operation = constantSystem.lookupUnary(op.source); | |
| 3002 HConstant constant = operand; | |
| 3003 ConstantValue folded = operation.fold(constant.constant); | |
| 3004 if (folded != null) { | |
| 3005 stack.add(graph.addConstant(folded, compiler)); | |
| 3006 return; | |
| 3007 } | |
| 3008 } | |
| 3009 | |
| 3010 pushInvokeDynamic(node, elements.getSelector(node), [operand]); | |
| 3011 } | |
| 3012 | |
| 3013 void visitBinary(HInstruction left, | |
| 3014 ast.Operator op, | |
| 3015 HInstruction right, | |
| 3016 Selector selector, | |
| 3017 ast.Send send) { | |
| 3018 switch (op.source) { | |
| 3019 case "===": | |
| 3020 pushWithPosition( | |
| 3021 new HIdentity(left, right, null, backend.boolType), op); | |
| 3022 return; | |
| 3023 case "!==": | |
| 3024 HIdentity eq = new HIdentity(left, right, null, backend.boolType); | |
| 3025 add(eq); | |
| 3026 pushWithPosition(new HNot(eq, backend.boolType), op); | |
| 3027 return; | |
| 3028 } | |
| 3029 | |
| 3030 pushInvokeDynamic(send, selector, [left, right], location: op); | |
| 3031 if (op.source == '!=') { | |
| 3032 pushWithPosition(new HNot(popBoolified(), backend.boolType), op); | |
| 3033 } | |
| 3034 } | |
| 3035 | |
| 3036 HInstruction generateInstanceSendReceiver(ast.Send send) { | |
| 3037 assert(Elements.isInstanceSend(send, elements)); | |
| 3038 if (send.receiver == null) { | |
| 3039 return localsHandler.readThis(); | |
| 3040 } | |
| 3041 visit(send.receiver); | |
| 3042 return pop(); | |
| 3043 } | |
| 3044 | |
| 3045 String noSuchMethodTargetSymbolString(ErroneousElement error, | |
| 3046 [String prefix]) { | |
| 3047 String result = error.name; | |
| 3048 if (prefix == "set") return "$result="; | |
| 3049 return result; | |
| 3050 } | |
| 3051 | |
| 3052 /** | |
| 3053 * Returns a set of interceptor classes that contain the given | |
| 3054 * [selector]. | |
| 3055 */ | |
| 3056 void generateInstanceGetterWithCompiledReceiver(ast.Send send, | |
| 3057 Selector selector, | |
| 3058 HInstruction receiver) { | |
| 3059 assert(Elements.isInstanceSend(send, elements)); | |
| 3060 assert(selector.isGetter); | |
| 3061 pushInvokeDynamic(send, selector, [receiver]); | |
| 3062 } | |
| 3063 | |
| 3064 /// Inserts a call to checkDeferredIsLoaded if the send has a prefix that | |
| 3065 /// resolves to a deferred library. | |
| 3066 void generateIsDeferredLoadedCheckIfNeeded(ast.Send node) { | |
| 3067 DeferredLoadTask deferredTask = compiler.deferredLoadTask; | |
| 3068 PrefixElement prefixElement = | |
| 3069 deferredTask.deferredPrefixElement(node, elements); | |
| 3070 if (prefixElement != null) { | |
| 3071 String loadId = | |
| 3072 deferredTask.importDeferName[prefixElement.deferredImport]; | |
| 3073 HInstruction loadIdConstant = addConstantString(loadId); | |
| 3074 String uri = prefixElement.deferredImport.uri.dartString.slowToString(); | |
| 3075 HInstruction uriConstant = addConstantString(uri); | |
| 3076 Element helper = backend.getCheckDeferredIsLoaded(); | |
| 3077 pushInvokeStatic(node, helper, [loadIdConstant, uriConstant]); | |
| 3078 pop(); | |
| 3079 } | |
| 3080 } | |
| 3081 | |
| 3082 void generateGetter(ast.Send send, Element element) { | |
| 3083 if (element != null && element.isForeign(backend)) { | |
| 3084 visitForeignGetter(send); | |
| 3085 } else if (Elements.isStaticOrTopLevelField(element)) { | |
| 3086 ConstantExpression constant; | |
| 3087 if (element.isField && !element.isAssignable) { | |
| 3088 // A static final or const. Get its constant value and inline it if | |
| 3089 // the value can be compiled eagerly. | |
| 3090 constant = backend.constants.getConstantForVariable(element); | |
| 3091 } | |
| 3092 if (constant != null) { | |
| 3093 ConstantValue value = constant.value; | |
| 3094 HConstant instruction; | |
| 3095 // Constants that are referred via a deferred prefix should be referred | |
| 3096 // by reference. | |
| 3097 PrefixElement prefix = compiler.deferredLoadTask | |
| 3098 .deferredPrefixElement(send, elements); | |
| 3099 if (prefix != null) { | |
| 3100 instruction = graph.addDeferredConstant(value, prefix, compiler); | |
| 3101 } else { | |
| 3102 instruction = graph.addConstant(value, compiler); | |
| 3103 } | |
| 3104 stack.add(instruction); | |
| 3105 // The inferrer may have found a better type than the constant | |
| 3106 // handler in the case of lists, because the constant handler | |
| 3107 // does not look at elements in the list. | |
| 3108 TypeMask type = | |
| 3109 TypeMaskFactory.inferredTypeForElement(element, compiler); | |
| 3110 if (!type.containsAll(compiler.world) && | |
| 3111 !instruction.isConstantNull()) { | |
| 3112 // TODO(13429): The inferrer should know that an element | |
| 3113 // cannot be null. | |
| 3114 instruction.instructionType = type.nonNullable(); | |
| 3115 } | |
| 3116 } else if (element.isField && isLazilyInitialized(element)) { | |
| 3117 HInstruction instruction = new HLazyStatic( | |
| 3118 element, | |
| 3119 TypeMaskFactory.inferredTypeForElement(element, compiler)); | |
| 3120 push(instruction); | |
| 3121 } else { | |
| 3122 if (element.isGetter) { | |
| 3123 pushInvokeStatic(send, element, <HInstruction>[]); | |
| 3124 } else { | |
| 3125 // TODO(5346): Try to avoid the need for calling [declaration] before | |
| 3126 // creating an [HStatic]. | |
| 3127 HInstruction instruction = new HStatic( | |
| 3128 element.declaration, | |
| 3129 TypeMaskFactory.inferredTypeForElement(element, compiler)); | |
| 3130 push(instruction); | |
| 3131 } | |
| 3132 } | |
| 3133 } else if (Elements.isInstanceSend(send, elements)) { | |
| 3134 HInstruction receiver = generateInstanceSendReceiver(send); | |
| 3135 generateInstanceGetterWithCompiledReceiver( | |
| 3136 send, elements.getSelector(send), receiver); | |
| 3137 } else if (Elements.isStaticOrTopLevelFunction(element)) { | |
| 3138 // TODO(5346): Try to avoid the need for calling [declaration] before | |
| 3139 // creating an [HStatic]. | |
| 3140 push(new HStatic(element.declaration, backend.nonNullType)); | |
| 3141 // TODO(ahe): This should be registered in codegen. | |
| 3142 registry.registerGetOfStaticFunction(element.declaration); | |
| 3143 } else if (Elements.isErroneousElement(element)) { | |
| 3144 // An erroneous element indicates an unresolved static getter. | |
| 3145 generateThrowNoSuchMethod(send, | |
| 3146 noSuchMethodTargetSymbolString(element, 'get'), | |
| 3147 argumentNodes: const Link<ast.Node>()); | |
| 3148 } else { | |
| 3149 LocalElement local = element; | |
| 3150 stack.add(localsHandler.readLocal(local)); | |
| 3151 } | |
| 3152 } | |
| 3153 | |
| 3154 void generateInstanceSetterWithCompiledReceiver(ast.Send send, | |
| 3155 HInstruction receiver, | |
| 3156 HInstruction value, | |
| 3157 {Selector selector, | |
| 3158 ast.Node location}) { | |
| 3159 assert(send == null || Elements.isInstanceSend(send, elements)); | |
| 3160 if (selector == null) { | |
| 3161 assert(send != null); | |
| 3162 selector = elements.getSelector(send); | |
| 3163 } | |
| 3164 if (location == null) { | |
| 3165 assert(send != null); | |
| 3166 location = send; | |
| 3167 } | |
| 3168 assert(selector.isSetter); | |
| 3169 pushInvokeDynamic(location, selector, [receiver, value]); | |
| 3170 pop(); | |
| 3171 stack.add(value); | |
| 3172 } | |
| 3173 | |
| 3174 void generateNonInstanceSetter(ast.SendSet send, | |
| 3175 Element element, | |
| 3176 HInstruction value, | |
| 3177 {ast.Node location}) { | |
| 3178 assert(send == null || !Elements.isInstanceSend(send, elements)); | |
| 3179 if (location == null) { | |
| 3180 assert(send != null); | |
| 3181 location = send; | |
| 3182 } | |
| 3183 if (Elements.isStaticOrTopLevelField(element)) { | |
| 3184 if (element.isSetter) { | |
| 3185 pushInvokeStatic(location, element, <HInstruction>[value]); | |
| 3186 pop(); | |
| 3187 } else { | |
| 3188 VariableElement field = element; | |
| 3189 value = | |
| 3190 potentiallyCheckType(value, field.type); | |
| 3191 addWithPosition(new HStaticStore(element, value), location); | |
| 3192 } | |
| 3193 stack.add(value); | |
| 3194 } else if (Elements.isErroneousElement(element)) { | |
| 3195 List<HInstruction> arguments = | |
| 3196 send == null ? const <HInstruction>[] : <HInstruction>[value]; | |
| 3197 // An erroneous element indicates an unresolved static setter. | |
| 3198 generateThrowNoSuchMethod(location, | |
| 3199 noSuchMethodTargetSymbolString(element, 'set'), | |
| 3200 argumentValues: arguments); | |
| 3201 } else { | |
| 3202 stack.add(value); | |
| 3203 LocalElement local = element; | |
| 3204 // If the value does not already have a name, give it here. | |
| 3205 if (value.sourceElement == null) { | |
| 3206 value.sourceElement = local; | |
| 3207 } | |
| 3208 HInstruction checked = | |
| 3209 potentiallyCheckType(value, local.type); | |
| 3210 if (!identical(checked, value)) { | |
| 3211 pop(); | |
| 3212 stack.add(checked); | |
| 3213 } | |
| 3214 HInstruction trusted = | |
| 3215 potentiallyBuildTypeHint(checked, local.type); | |
| 3216 if (!identical(trusted, checked)) { | |
| 3217 pop(); | |
| 3218 push(trusted); | |
| 3219 } | |
| 3220 | |
| 3221 localsHandler.updateLocal(local, trusted); | |
| 3222 } | |
| 3223 } | |
| 3224 | |
| 3225 HInstruction invokeInterceptor(HInstruction receiver) { | |
| 3226 HInterceptor interceptor = new HInterceptor(receiver, backend.nonNullType); | |
| 3227 add(interceptor); | |
| 3228 return interceptor; | |
| 3229 } | |
| 3230 | |
| 3231 HForeign createForeign(js.Template code, | |
| 3232 TypeMask type, | |
| 3233 List<HInstruction> inputs) { | |
| 3234 return new HForeign(code, type, inputs); | |
| 3235 } | |
| 3236 | |
| 3237 HLiteralList buildLiteralList(List<HInstruction> inputs) { | |
| 3238 return new HLiteralList(inputs, backend.extendableArrayType); | |
| 3239 } | |
| 3240 | |
| 3241 // TODO(karlklose): change construction of the representations to be GVN'able | |
| 3242 // (dartbug.com/7182). | |
| 3243 HInstruction buildTypeArgumentRepresentations(DartType type) { | |
| 3244 // Compute the representation of the type arguments, including access | |
| 3245 // to the runtime type information for type variables as instructions. | |
| 3246 if (type.isTypeVariable) { | |
| 3247 return buildLiteralList(<HInstruction>[addTypeVariableReference(type)]); | |
| 3248 } else { | |
| 3249 assert(type.element.isClass); | |
| 3250 InterfaceType interface = type; | |
| 3251 List<HInstruction> inputs = <HInstruction>[]; | |
| 3252 bool first = true; | |
| 3253 List<String> templates = <String>[]; | |
| 3254 for (DartType argument in interface.typeArguments) { | |
| 3255 templates.add(rti.getTypeRepresentationWithHashes(argument, (variable) { | |
| 3256 HInstruction runtimeType = addTypeVariableReference(variable); | |
| 3257 inputs.add(runtimeType); | |
| 3258 })); | |
| 3259 } | |
| 3260 String template = '[${templates.join(', ')}]'; | |
| 3261 // TODO(sra): This is a fresh template each time. We can't let the | |
| 3262 // template manager build them. | |
| 3263 js.Template code = js.js.uncachedExpressionTemplate(template); | |
| 3264 HInstruction representation = | |
| 3265 createForeign(code, backend.readableArrayType, inputs); | |
| 3266 return representation; | |
| 3267 } | |
| 3268 } | |
| 3269 | |
| 3270 visitOperatorSend(ast.Send node) { | |
| 3271 ast.Operator op = node.selector; | |
| 3272 if ("[]" == op.source) { | |
| 3273 visitDynamicSend(node); | |
| 3274 } else if ("&&" == op.source || | |
| 3275 "||" == op.source) { | |
| 3276 visitLogicalAndOr(node, op); | |
| 3277 } else if ("!" == op.source) { | |
| 3278 visitLogicalNot(node); | |
| 3279 } else if (node.argumentsNode is ast.Prefix) { | |
| 3280 visitUnary(node, op); | |
| 3281 } else if ("is" == op.source) { | |
| 3282 visitIsSend(node); | |
| 3283 } else if ("as" == op.source) { | |
| 3284 visit(node.receiver); | |
| 3285 HInstruction expression = pop(); | |
| 3286 DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast); | |
| 3287 if (type.isMalformed) { | |
| 3288 ErroneousElement element = type.element; | |
| 3289 generateTypeError(node, element.message); | |
| 3290 } else { | |
| 3291 HInstruction converted = buildTypeConversion( | |
| 3292 expression, | |
| 3293 localsHandler.substInContext(type), | |
| 3294 HTypeConversion.CAST_TYPE_CHECK); | |
| 3295 if (converted != expression) add(converted); | |
| 3296 stack.add(converted); | |
| 3297 } | |
| 3298 } else { | |
| 3299 visit(node.receiver); | |
| 3300 visit(node.argumentsNode); | |
| 3301 var right = pop(); | |
| 3302 var left = pop(); | |
| 3303 visitBinary(left, op, right, elements.getSelector(node), node); | |
| 3304 } | |
| 3305 } | |
| 3306 | |
| 3307 void visitIsSend(ast.Send node) { | |
| 3308 visit(node.receiver); | |
| 3309 HInstruction expression = pop(); | |
| 3310 bool isNot = node.isIsNotCheck; | |
| 3311 DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast); | |
| 3312 HInstruction instruction = buildIsNode(node, type, expression); | |
| 3313 if (isNot) { | |
| 3314 add(instruction); | |
| 3315 instruction = new HNot(instruction, backend.boolType); | |
| 3316 } | |
| 3317 push(instruction); | |
| 3318 } | |
| 3319 | |
| 3320 HInstruction buildIsNode(ast.Node node, | |
| 3321 DartType type, | |
| 3322 HInstruction expression) { | |
| 3323 type = localsHandler.substInContext(type).unalias(compiler); | |
| 3324 if (type.isFunctionType) { | |
| 3325 List arguments = [buildFunctionType(type), expression]; | |
| 3326 pushInvokeDynamic( | |
| 3327 node, new Selector.call('_isTest', backend.jsHelperLibrary, 1), | |
| 3328 arguments); | |
| 3329 return new HIs.compound(type, expression, pop(), backend.boolType); | |
| 3330 } else if (type.isTypeVariable) { | |
| 3331 HInstruction runtimeType = addTypeVariableReference(type); | |
| 3332 Element helper = backend.getCheckSubtypeOfRuntimeType(); | |
| 3333 List<HInstruction> inputs = <HInstruction>[expression, runtimeType]; | |
| 3334 pushInvokeStatic(null, helper, inputs, backend.boolType); | |
| 3335 HInstruction call = pop(); | |
| 3336 return new HIs.variable(type, expression, call, backend.boolType); | |
| 3337 } else if (RuntimeTypes.hasTypeArguments(type)) { | |
| 3338 ClassElement element = type.element; | |
| 3339 Element helper = backend.getCheckSubtype(); | |
| 3340 HInstruction representations = | |
| 3341 buildTypeArgumentRepresentations(type); | |
| 3342 add(representations); | |
| 3343 String operator = backend.namer.operatorIs(element); | |
| 3344 HInstruction isFieldName = addConstantString(operator); | |
| 3345 HInstruction asFieldName = compiler.world.hasAnyStrictSubtype(element) | |
| 3346 ? addConstantString(backend.namer.substitutionName(element)) | |
| 3347 : graph.addConstantNull(compiler); | |
| 3348 List<HInstruction> inputs = <HInstruction>[expression, | |
| 3349 isFieldName, | |
| 3350 representations, | |
| 3351 asFieldName]; | |
| 3352 pushInvokeStatic(node, helper, inputs, backend.boolType); | |
| 3353 HInstruction call = pop(); | |
| 3354 return new HIs.compound(type, expression, call, backend.boolType); | |
| 3355 } else if (type.isMalformed) { | |
| 3356 ErroneousElement element = type.element; | |
| 3357 generateTypeError(node, element.message); | |
| 3358 HInstruction call = pop(); | |
| 3359 return new HIs.compound(type, expression, call, backend.boolType); | |
| 3360 } else { | |
| 3361 if (backend.hasDirectCheckFor(type)) { | |
| 3362 return new HIs.direct(type, expression, backend.boolType); | |
| 3363 } | |
| 3364 // TODO(johnniwinther): Avoid interceptor if unneeded. | |
| 3365 return new HIs.raw( | |
| 3366 type, expression, invokeInterceptor(expression), backend.boolType); | |
| 3367 } | |
| 3368 } | |
| 3369 | |
| 3370 HInstruction buildFunctionType(FunctionType type) { | |
| 3371 type.accept(new TypeBuilder(compiler.world), this); | |
| 3372 return pop(); | |
| 3373 } | |
| 3374 | |
| 3375 void addDynamicSendArgumentsToList(ast.Send node, List<HInstruction> list) { | |
| 3376 Selector selector = elements.getSelector(node); | |
| 3377 if (selector.namedArgumentCount == 0) { | |
| 3378 addGenericSendArgumentsToList(node.arguments, list); | |
| 3379 } else { | |
| 3380 // Visit positional arguments and add them to the list. | |
| 3381 Link<ast.Node> arguments = node.arguments; | |
| 3382 int positionalArgumentCount = selector.positionalArgumentCount; | |
| 3383 for (int i = 0; | |
| 3384 i < positionalArgumentCount; | |
| 3385 arguments = arguments.tail, i++) { | |
| 3386 visit(arguments.head); | |
| 3387 list.add(pop()); | |
| 3388 } | |
| 3389 | |
| 3390 // Visit named arguments and add them into a temporary map. | |
| 3391 Map<String, HInstruction> instructions = | |
| 3392 new Map<String, HInstruction>(); | |
| 3393 List<String> namedArguments = selector.namedArguments; | |
| 3394 int nameIndex = 0; | |
| 3395 for (; !arguments.isEmpty; arguments = arguments.tail) { | |
| 3396 visit(arguments.head); | |
| 3397 instructions[namedArguments[nameIndex++]] = pop(); | |
| 3398 } | |
| 3399 | |
| 3400 // Iterate through the named arguments to add them to the list | |
| 3401 // of instructions, in an order that can be shared with | |
| 3402 // selectors with the same named arguments. | |
| 3403 List<String> orderedNames = selector.getOrderedNamedArguments(); | |
| 3404 for (String name in orderedNames) { | |
| 3405 list.add(instructions[name]); | |
| 3406 } | |
| 3407 } | |
| 3408 } | |
| 3409 | |
| 3410 /** | |
| 3411 * Returns true if the arguments were compatible with the function signature. | |
| 3412 * | |
| 3413 * Invariant: [element] must be an implementation element. | |
| 3414 */ | |
| 3415 bool addStaticSendArgumentsToList(Selector selector, | |
| 3416 Link<ast.Node> arguments, | |
| 3417 FunctionElement element, | |
| 3418 List<HInstruction> list) { | |
| 3419 assert(invariant(element, element.isImplementation)); | |
| 3420 | |
| 3421 HInstruction compileArgument(ast.Node argument) { | |
| 3422 visit(argument); | |
| 3423 return pop(); | |
| 3424 } | |
| 3425 | |
| 3426 return selector.addArgumentsToList(arguments, | |
| 3427 list, | |
| 3428 element, | |
| 3429 compileArgument, | |
| 3430 handleConstantForOptionalParameter, | |
| 3431 compiler.world); | |
| 3432 } | |
| 3433 | |
| 3434 void addGenericSendArgumentsToList(Link<ast.Node> link, List<HInstruction> lis
t) { | |
| 3435 for (; !link.isEmpty; link = link.tail) { | |
| 3436 visit(link.head); | |
| 3437 list.add(pop()); | |
| 3438 } | |
| 3439 } | |
| 3440 | |
| 3441 visitDynamicSend(ast.Send node) { | |
| 3442 Selector selector = elements.getSelector(node); | |
| 3443 | |
| 3444 List<HInstruction> inputs = <HInstruction>[]; | |
| 3445 HInstruction receiver = generateInstanceSendReceiver(node); | |
| 3446 inputs.add(receiver); | |
| 3447 addDynamicSendArgumentsToList(node, inputs); | |
| 3448 | |
| 3449 pushInvokeDynamic(node, selector, inputs); | |
| 3450 if (selector.isSetter || selector.isIndexSet) { | |
| 3451 pop(); | |
| 3452 stack.add(inputs.last); | |
| 3453 } | |
| 3454 } | |
| 3455 | |
| 3456 visitClosureSend(ast.Send node) { | |
| 3457 Selector selector = elements.getSelector(node); | |
| 3458 assert(node.receiver == null); | |
| 3459 Element element = elements[node]; | |
| 3460 HInstruction closureTarget; | |
| 3461 if (element == null) { | |
| 3462 visit(node.selector); | |
| 3463 closureTarget = pop(); | |
| 3464 } else { | |
| 3465 LocalElement local = element; | |
| 3466 closureTarget = localsHandler.readLocal(local); | |
| 3467 } | |
| 3468 var inputs = <HInstruction>[]; | |
| 3469 inputs.add(closureTarget); | |
| 3470 addDynamicSendArgumentsToList(node, inputs); | |
| 3471 Selector closureSelector = new Selector.callClosureFrom(selector); | |
| 3472 pushWithPosition( | |
| 3473 new HInvokeClosure(closureSelector, inputs, backend.dynamicType), | |
| 3474 node); | |
| 3475 } | |
| 3476 | |
| 3477 void handleForeignJs(ast.Send node) { | |
| 3478 Link<ast.Node> link = node.arguments; | |
| 3479 // If the invoke is on foreign code, don't visit the first | |
| 3480 // argument, which is the type, and the second argument, | |
| 3481 // which is the foreign code. | |
| 3482 if (link.isEmpty || link.tail.isEmpty) { | |
| 3483 compiler.internalError(node.argumentsNode, | |
| 3484 'At least two arguments expected.'); | |
| 3485 } | |
| 3486 native.NativeBehavior nativeBehavior = | |
| 3487 compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node); | |
| 3488 | |
| 3489 List<HInstruction> inputs = <HInstruction>[]; | |
| 3490 addGenericSendArgumentsToList(link.tail.tail, inputs); | |
| 3491 | |
| 3492 TypeMask ssaType = | |
| 3493 TypeMaskFactory.fromNativeBehavior(nativeBehavior, compiler); | |
| 3494 | |
| 3495 if (nativeBehavior.codeTemplate.isExpression) { | |
| 3496 push(new HForeign(nativeBehavior.codeTemplate, ssaType, inputs, | |
| 3497 effects: nativeBehavior.sideEffects, | |
| 3498 nativeBehavior: nativeBehavior)); | |
| 3499 } else { | |
| 3500 push(new HForeign(nativeBehavior.codeTemplate, ssaType, inputs, | |
| 3501 isStatement: true, | |
| 3502 effects: nativeBehavior.sideEffects, | |
| 3503 nativeBehavior: nativeBehavior, | |
| 3504 canThrow: true)); | |
| 3505 } | |
| 3506 } | |
| 3507 | |
| 3508 void handleJsStringConcat(ast.Send node) { | |
| 3509 List<HInstruction> inputs = <HInstruction>[]; | |
| 3510 addGenericSendArgumentsToList(node.arguments, inputs); | |
| 3511 if (inputs.length != 2) { | |
| 3512 compiler.internalError(node.argumentsNode, 'Two arguments expected.'); | |
| 3513 } | |
| 3514 push(new HStringConcat(inputs[0], inputs[1], node, backend.stringType)); | |
| 3515 } | |
| 3516 | |
| 3517 void handleForeignJsCurrentIsolateContext(ast.Send node) { | |
| 3518 if (!node.arguments.isEmpty) { | |
| 3519 compiler.internalError(node, | |
| 3520 'Too many arguments to JS_CURRENT_ISOLATE_CONTEXT.'); | |
| 3521 } | |
| 3522 | |
| 3523 if (!compiler.hasIsolateSupport) { | |
| 3524 // If the isolate library is not used, we just generate code | |
| 3525 // to fetch the current isolate. | |
| 3526 String name = backend.namer.currentIsolate; | |
| 3527 push(new HForeign(js.js.parseForeignJS(name), | |
| 3528 backend.dynamicType, | |
| 3529 <HInstruction>[])); | |
| 3530 } else { | |
| 3531 // Call a helper method from the isolate library. The isolate | |
| 3532 // library uses its own isolate structure, that encapsulates | |
| 3533 // Leg's isolate. | |
| 3534 Element element = backend.isolateHelperLibrary.find('_currentIsolate'); | |
| 3535 if (element == null) { | |
| 3536 compiler.internalError(node, | |
| 3537 'Isolate library and compiler mismatch.'); | |
| 3538 } | |
| 3539 pushInvokeStatic(null, element, [], backend.dynamicType); | |
| 3540 } | |
| 3541 } | |
| 3542 | |
| 3543 void handleForeingJsGetFlag(ast.Send node) { | |
| 3544 List<ast.Node> arguments = node.arguments.toList(); | |
| 3545 ast.Node argument; | |
| 3546 switch (arguments.length) { | |
| 3547 case 0: | |
| 3548 compiler.reportError( | |
| 3549 node, MessageKind.GENERIC, | |
| 3550 {'text': 'Error: Expected one argument to JS_GET_FLAG.'}); | |
| 3551 return; | |
| 3552 case 1: | |
| 3553 argument = arguments[0]; | |
| 3554 break; | |
| 3555 default: | |
| 3556 for (int i = 1; i < arguments.length; i++) { | |
| 3557 compiler.reportError( | |
| 3558 arguments[i], MessageKind.GENERIC, | |
| 3559 {'text': 'Error: Extra argument to JS_GET_FLAG.'}); | |
| 3560 } | |
| 3561 return; | |
| 3562 } | |
| 3563 ast.LiteralString string = argument.asLiteralString(); | |
| 3564 if (string == null) { | |
| 3565 compiler.reportError( | |
| 3566 argument, MessageKind.GENERIC, | |
| 3567 {'text': 'Error: Expected a literal string.'}); | |
| 3568 } | |
| 3569 String name = string.dartString.slowToString(); | |
| 3570 bool value = false; | |
| 3571 if (name == 'MUST_RETAIN_METADATA') { | |
| 3572 value = backend.mustRetainMetadata; | |
| 3573 } else { | |
| 3574 compiler.reportError( | |
| 3575 node, MessageKind.GENERIC, | |
| 3576 {'text': 'Error: Unknown internal flag "$name".'}); | |
| 3577 } | |
| 3578 stack.add(graph.addConstantBool(value, compiler)); | |
| 3579 } | |
| 3580 | |
| 3581 void handleForeignJsGetName(ast.Send node) { | |
| 3582 List<ast.Node> arguments = node.arguments.toList(); | |
| 3583 ast.Node argument; | |
| 3584 switch (arguments.length) { | |
| 3585 case 0: | |
| 3586 compiler.reportError( | |
| 3587 node, MessageKind.GENERIC, | |
| 3588 {'text': 'Error: Expected one argument to JS_GET_NAME.'}); | |
| 3589 return; | |
| 3590 case 1: | |
| 3591 argument = arguments[0]; | |
| 3592 break; | |
| 3593 default: | |
| 3594 for (int i = 1; i < arguments.length; i++) { | |
| 3595 compiler.reportError( | |
| 3596 arguments[i], MessageKind.GENERIC, | |
| 3597 {'text': 'Error: Extra argument to JS_GET_NAME.'}); | |
| 3598 } | |
| 3599 return; | |
| 3600 } | |
| 3601 ast.LiteralString string = argument.asLiteralString(); | |
| 3602 if (string == null) { | |
| 3603 compiler.reportError( | |
| 3604 argument, MessageKind.GENERIC, | |
| 3605 {'text': 'Error: Expected a literal string.'}); | |
| 3606 } | |
| 3607 stack.add( | |
| 3608 addConstantString( | |
| 3609 backend.namer.getNameForJsGetName( | |
| 3610 argument, string.dartString.slowToString()))); | |
| 3611 } | |
| 3612 | |
| 3613 void handleForeignJsEmbeddedGlobal(ast.Send node) { | |
| 3614 List<ast.Node> arguments = node.arguments.toList(); | |
| 3615 ast.Node globalNameNode; | |
| 3616 switch (arguments.length) { | |
| 3617 case 0: | |
| 3618 case 1: | |
| 3619 compiler.reportError( | |
| 3620 node, MessageKind.GENERIC, | |
| 3621 {'text': 'Error: Expected two arguments to JS_EMBEDDED_GLOBAL.'}); | |
| 3622 return; | |
| 3623 case 2: | |
| 3624 // The type has been extracted earlier. We are only interested in the | |
| 3625 // name in this function. | |
| 3626 globalNameNode = arguments[1]; | |
| 3627 break; | |
| 3628 default: | |
| 3629 for (int i = 2; i < arguments.length; i++) { | |
| 3630 compiler.reportError( | |
| 3631 arguments[i], MessageKind.GENERIC, | |
| 3632 {'text': 'Error: Extra argument to JS_EMBEDDED_GLOBAL.'}); | |
| 3633 } | |
| 3634 return; | |
| 3635 } | |
| 3636 visit(arguments[1]); | |
| 3637 HInstruction globalNameHNode = pop(); | |
| 3638 if (!globalNameHNode.isConstantString()) { | |
| 3639 compiler.reportError( | |
| 3640 arguments[1], MessageKind.GENERIC, | |
| 3641 {'text': 'Error: Expected String as second argument ' | |
| 3642 'to JS_EMBEDDED_GLOBAL.'}); | |
| 3643 return; | |
| 3644 } | |
| 3645 HConstant hConstant = globalNameHNode; | |
| 3646 StringConstantValue constant = hConstant.constant; | |
| 3647 String globalName = constant.primitiveValue.slowToString(); | |
| 3648 js.Template expr = js.js.expressionTemplateYielding( | |
| 3649 backend.emitter.generateEmbeddedGlobalAccess(globalName)); | |
| 3650 native.NativeBehavior nativeBehavior = | |
| 3651 compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node); | |
| 3652 TypeMask ssaType = | |
| 3653 TypeMaskFactory.fromNativeBehavior(nativeBehavior, compiler); | |
| 3654 push(new HForeign(expr, ssaType, const [])); | |
| 3655 } | |
| 3656 | |
| 3657 void handleJsInterceptorConstant(ast.Send node) { | |
| 3658 // Single argument must be a TypeConstant which is converted into a | |
| 3659 // InterceptorConstant. | |
| 3660 if (!node.arguments.isEmpty && node.arguments.tail.isEmpty) { | |
| 3661 ast.Node argument = node.arguments.head; | |
| 3662 visit(argument); | |
| 3663 HInstruction argumentInstruction = pop(); | |
| 3664 if (argumentInstruction is HConstant) { | |
| 3665 ConstantValue argumentConstant = argumentInstruction.constant; | |
| 3666 if (argumentConstant is TypeConstantValue) { | |
| 3667 ConstantValue constant = | |
| 3668 new InterceptorConstantValue(argumentConstant.representedType); | |
| 3669 HInstruction instruction = graph.addConstant(constant, compiler); | |
| 3670 stack.add(instruction); | |
| 3671 return; | |
| 3672 } | |
| 3673 } | |
| 3674 } | |
| 3675 compiler.reportError(node, | |
| 3676 MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT); | |
| 3677 stack.add(graph.addConstantNull(compiler)); | |
| 3678 } | |
| 3679 | |
| 3680 void handleForeignJsCallInIsolate(ast.Send node) { | |
| 3681 Link<ast.Node> link = node.arguments; | |
| 3682 if (!compiler.hasIsolateSupport) { | |
| 3683 // If the isolate library is not used, we just invoke the | |
| 3684 // closure. | |
| 3685 visit(link.tail.head); | |
| 3686 Selector selector = new Selector.callClosure(0); | |
| 3687 push(new HInvokeClosure(selector, | |
| 3688 <HInstruction>[pop()], | |
| 3689 backend.dynamicType)); | |
| 3690 } else { | |
| 3691 // Call a helper method from the isolate library. | |
| 3692 Element element = backend.isolateHelperLibrary.find('_callInIsolate'); | |
| 3693 if (element == null) { | |
| 3694 compiler.internalError(node, | |
| 3695 'Isolate library and compiler mismatch.'); | |
| 3696 } | |
| 3697 List<HInstruction> inputs = <HInstruction>[]; | |
| 3698 addGenericSendArgumentsToList(link, inputs); | |
| 3699 pushInvokeStatic(node, element, inputs, backend.dynamicType); | |
| 3700 } | |
| 3701 } | |
| 3702 | |
| 3703 FunctionSignature handleForeignRawFunctionRef(ast.Send node, String name) { | |
| 3704 if (node.arguments.isEmpty || !node.arguments.tail.isEmpty) { | |
| 3705 compiler.internalError(node.argumentsNode, | |
| 3706 '"$name" requires exactly one argument.'); | |
| 3707 } | |
| 3708 ast.Node closure = node.arguments.head; | |
| 3709 Element element = elements[closure]; | |
| 3710 if (!Elements.isStaticOrTopLevelFunction(element)) { | |
| 3711 compiler.internalError(closure, | |
| 3712 '"$name" requires a static or top-level method.'); | |
| 3713 } | |
| 3714 FunctionElement function = element; | |
| 3715 // TODO(johnniwinther): Try to eliminate the need to distinguish declaration | |
| 3716 // and implementation signatures. Currently it is need because the | |
| 3717 // signatures have different elements for parameters. | |
| 3718 FunctionElement implementation = function.implementation; | |
| 3719 FunctionSignature params = implementation.functionSignature; | |
| 3720 if (params.optionalParameterCount != 0) { | |
| 3721 compiler.internalError(closure, | |
| 3722 '"$name" does not handle closure with optional parameters.'); | |
| 3723 } | |
| 3724 | |
| 3725 registry.registerStaticUse(element); | |
| 3726 push(new HForeign(js.js.expressionTemplateYielding( | |
| 3727 backend.namer.elementAccess(element)), | |
| 3728 backend.dynamicType, | |
| 3729 <HInstruction>[])); | |
| 3730 return params; | |
| 3731 } | |
| 3732 | |
| 3733 void handleForeignDartClosureToJs(ast.Send node, String name) { | |
| 3734 // TODO(ahe): This implements DART_CLOSURE_TO_JS and should probably take | |
| 3735 // care to wrap the closure in another closure that saves the current | |
| 3736 // isolate. | |
| 3737 handleForeignRawFunctionRef(node, name); | |
| 3738 } | |
| 3739 | |
| 3740 void handleForeignSetCurrentIsolate(ast.Send node) { | |
| 3741 if (node.arguments.isEmpty || !node.arguments.tail.isEmpty) { | |
| 3742 compiler.internalError(node.argumentsNode, | |
| 3743 'Exactly one argument required.'); | |
| 3744 } | |
| 3745 visit(node.arguments.head); | |
| 3746 String isolateName = backend.namer.currentIsolate; | |
| 3747 SideEffects sideEffects = new SideEffects.empty(); | |
| 3748 sideEffects.setAllSideEffects(); | |
| 3749 push(new HForeign(js.js.parseForeignJS("$isolateName = #"), | |
| 3750 backend.dynamicType, | |
| 3751 <HInstruction>[pop()], | |
| 3752 effects: sideEffects)); | |
| 3753 } | |
| 3754 | |
| 3755 void handleForeignCreateIsolate(ast.Send node) { | |
| 3756 if (!node.arguments.isEmpty) { | |
| 3757 compiler.internalError(node.argumentsNode, 'Too many arguments.'); | |
| 3758 } | |
| 3759 String constructorName = backend.namer.isolateName; | |
| 3760 push(new HForeign(js.js.parseForeignJS("new $constructorName()"), | |
| 3761 backend.dynamicType, | |
| 3762 <HInstruction>[])); | |
| 3763 } | |
| 3764 | |
| 3765 void handleForeignDartObjectJsConstructorFunction(ast.Send node) { | |
| 3766 if (!node.arguments.isEmpty) { | |
| 3767 compiler.internalError(node.argumentsNode, 'Too many arguments.'); | |
| 3768 } | |
| 3769 push(new HForeign(js.js.expressionTemplateYielding( | |
| 3770 backend.namer.elementAccess(compiler.objectClass)), | |
| 3771 backend.dynamicType, | |
| 3772 <HInstruction>[])); | |
| 3773 } | |
| 3774 | |
| 3775 void handleForeignJsCurrentIsolate(ast.Send node) { | |
| 3776 if (!node.arguments.isEmpty) { | |
| 3777 compiler.internalError(node.argumentsNode, 'Too many arguments.'); | |
| 3778 } | |
| 3779 push(new HForeign(js.js.parseForeignJS(backend.namer.currentIsolate), | |
| 3780 backend.dynamicType, | |
| 3781 <HInstruction>[])); | |
| 3782 } | |
| 3783 | |
| 3784 visitForeignSend(ast.Send node) { | |
| 3785 Selector selector = elements.getSelector(node); | |
| 3786 String name = selector.name; | |
| 3787 if (name == 'JS') { | |
| 3788 handleForeignJs(node); | |
| 3789 } else if (name == 'JS_CURRENT_ISOLATE_CONTEXT') { | |
| 3790 handleForeignJsCurrentIsolateContext(node); | |
| 3791 } else if (name == 'JS_CALL_IN_ISOLATE') { | |
| 3792 handleForeignJsCallInIsolate(node); | |
| 3793 } else if (name == 'DART_CLOSURE_TO_JS') { | |
| 3794 handleForeignDartClosureToJs(node, 'DART_CLOSURE_TO_JS'); | |
| 3795 } else if (name == 'RAW_DART_FUNCTION_REF') { | |
| 3796 handleForeignRawFunctionRef(node, 'RAW_DART_FUNCTION_REF'); | |
| 3797 } else if (name == 'JS_SET_CURRENT_ISOLATE') { | |
| 3798 handleForeignSetCurrentIsolate(node); | |
| 3799 } else if (name == 'JS_CREATE_ISOLATE') { | |
| 3800 handleForeignCreateIsolate(node); | |
| 3801 } else if (name == 'JS_OPERATOR_IS_PREFIX') { | |
| 3802 stack.add(addConstantString(backend.namer.operatorIsPrefix())); | |
| 3803 } else if (name == 'JS_OBJECT_CLASS_NAME') { | |
| 3804 String name = backend.namer.getRuntimeTypeName(compiler.objectClass); | |
| 3805 stack.add(addConstantString(name)); | |
| 3806 } else if (name == 'JS_NULL_CLASS_NAME') { | |
| 3807 String name = backend.namer.getRuntimeTypeName(compiler.nullClass); | |
| 3808 stack.add(addConstantString(name)); | |
| 3809 } else if (name == 'JS_FUNCTION_CLASS_NAME') { | |
| 3810 String name = backend.namer.getRuntimeTypeName(compiler.functionClass); | |
| 3811 stack.add(addConstantString(name)); | |
| 3812 } else if (name == 'JS_OPERATOR_AS_PREFIX') { | |
| 3813 stack.add(addConstantString(backend.namer.operatorAsPrefix())); | |
| 3814 } else if (name == 'JS_SIGNATURE_NAME') { | |
| 3815 stack.add(addConstantString(backend.namer.operatorSignature())); | |
| 3816 } else if (name == 'JS_FUNCTION_TYPE_TAG') { | |
| 3817 stack.add(addConstantString(backend.namer.functionTypeTag())); | |
| 3818 } else if (name == 'JS_FUNCTION_TYPE_VOID_RETURN_TAG') { | |
| 3819 stack.add(addConstantString(backend.namer.functionTypeVoidReturnTag())); | |
| 3820 } else if (name == 'JS_FUNCTION_TYPE_RETURN_TYPE_TAG') { | |
| 3821 stack.add(addConstantString(backend.namer.functionTypeReturnTypeTag())); | |
| 3822 } else if (name == | |
| 3823 'JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG') { | |
| 3824 stack.add(addConstantString( | |
| 3825 backend.namer.functionTypeRequiredParametersTag())); | |
| 3826 } else if (name == | |
| 3827 'JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG') { | |
| 3828 stack.add(addConstantString( | |
| 3829 backend.namer.functionTypeOptionalParametersTag())); | |
| 3830 } else if (name == | |
| 3831 'JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG') { | |
| 3832 stack.add(addConstantString( | |
| 3833 backend.namer.functionTypeNamedParametersTag())); | |
| 3834 } else if (name == 'JS_DART_OBJECT_CONSTRUCTOR') { | |
| 3835 handleForeignDartObjectJsConstructorFunction(node); | |
| 3836 } else if (name == 'JS_IS_INDEXABLE_FIELD_NAME') { | |
| 3837 Element element = backend.findHelper('JavaScriptIndexingBehavior'); | |
| 3838 stack.add(addConstantString(backend.namer.operatorIs(element))); | |
| 3839 } else if (name == 'JS_CURRENT_ISOLATE') { | |
| 3840 handleForeignJsCurrentIsolate(node); | |
| 3841 } else if (name == 'JS_GET_NAME') { | |
| 3842 handleForeignJsGetName(node); | |
| 3843 } else if (name == 'JS_EMBEDDED_GLOBAL') { | |
| 3844 handleForeignJsEmbeddedGlobal(node); | |
| 3845 } else if (name == 'JS_GET_FLAG') { | |
| 3846 handleForeingJsGetFlag(node); | |
| 3847 } else if (name == 'JS_EFFECT') { | |
| 3848 stack.add(graph.addConstantNull(compiler)); | |
| 3849 } else if (name == 'JS_INTERCEPTOR_CONSTANT') { | |
| 3850 handleJsInterceptorConstant(node); | |
| 3851 } else if (name == 'JS_STRING_CONCAT') { | |
| 3852 handleJsStringConcat(node); | |
| 3853 } else { | |
| 3854 throw "Unknown foreign: ${selector}"; | |
| 3855 } | |
| 3856 } | |
| 3857 | |
| 3858 visitForeignGetter(ast.Send node) { | |
| 3859 Element element = elements[node]; | |
| 3860 // Until now we only handle these as getters. | |
| 3861 invariant(node, element.isDeferredLoaderGetter); | |
| 3862 FunctionElement deferredLoader = element; | |
| 3863 Element loadFunction = compiler.loadLibraryFunction; | |
| 3864 PrefixElement prefixElement = deferredLoader.enclosingElement; | |
| 3865 String loadId = compiler.deferredLoadTask | |
| 3866 .importDeferName[prefixElement.deferredImport]; | |
| 3867 var inputs = [graph.addConstantString( | |
| 3868 new ast.DartString.literal(loadId), compiler)]; | |
| 3869 push(new HInvokeStatic(loadFunction, inputs, backend.nonNullType, | |
| 3870 targetCanThrow: false)); | |
| 3871 } | |
| 3872 | |
| 3873 generateSuperNoSuchMethodSend(ast.Send node, | |
| 3874 Selector selector, | |
| 3875 List<HInstruction> arguments) { | |
| 3876 String name = selector.name; | |
| 3877 | |
| 3878 ClassElement cls = currentNonClosureClass; | |
| 3879 Element element = cls.lookupSuperMember(Compiler.NO_SUCH_METHOD); | |
| 3880 if (compiler.enabledInvokeOn | |
| 3881 && element.enclosingElement.declaration != compiler.objectClass) { | |
| 3882 // Register the call as dynamic if [noSuchMethod] on the super | |
| 3883 // class is _not_ the default implementation from [Object], in | |
| 3884 // case the [noSuchMethod] implementation calls | |
| 3885 // [JSInvocationMirror._invokeOn]. | |
| 3886 registry.registerSelectorUse(selector.asUntyped); | |
| 3887 } | |
| 3888 String publicName = name; | |
| 3889 if (selector.isSetter) publicName += '='; | |
| 3890 | |
| 3891 ConstantValue nameConstant = constantSystem.createString( | |
| 3892 new ast.DartString.literal(publicName)); | |
| 3893 | |
| 3894 String internalName = backend.namer.invocationName(selector); | |
| 3895 ConstantValue internalNameConstant = | |
| 3896 constantSystem.createString(new ast.DartString.literal(internalName)); | |
| 3897 | |
| 3898 Element createInvocationMirror = backend.getCreateInvocationMirror(); | |
| 3899 var argumentsInstruction = buildLiteralList(arguments); | |
| 3900 add(argumentsInstruction); | |
| 3901 | |
| 3902 var argumentNames = new List<HInstruction>(); | |
| 3903 for (String argumentName in selector.namedArguments) { | |
| 3904 ConstantValue argumentNameConstant = | |
| 3905 constantSystem.createString(new ast.DartString.literal(argumentName)); | |
| 3906 argumentNames.add(graph.addConstant(argumentNameConstant, compiler)); | |
| 3907 } | |
| 3908 var argumentNamesInstruction = buildLiteralList(argumentNames); | |
| 3909 add(argumentNamesInstruction); | |
| 3910 | |
| 3911 ConstantValue kindConstant = | |
| 3912 constantSystem.createInt(selector.invocationMirrorKind); | |
| 3913 | |
| 3914 pushInvokeStatic(null, | |
| 3915 createInvocationMirror, | |
| 3916 [graph.addConstant(nameConstant, compiler), | |
| 3917 graph.addConstant(internalNameConstant, compiler), | |
| 3918 graph.addConstant(kindConstant, compiler), | |
| 3919 argumentsInstruction, | |
| 3920 argumentNamesInstruction], | |
| 3921 backend.dynamicType); | |
| 3922 | |
| 3923 var inputs = <HInstruction>[pop()]; | |
| 3924 push(buildInvokeSuper(compiler.noSuchMethodSelector, element, inputs)); | |
| 3925 } | |
| 3926 | |
| 3927 visitSuperSend(ast.Send node) { | |
| 3928 Selector selector = elements.getSelector(node); | |
| 3929 Element element = elements[node]; | |
| 3930 if (Elements.isUnresolved(element)) { | |
| 3931 List<HInstruction> arguments = <HInstruction>[]; | |
| 3932 if (!node.isPropertyAccess) { | |
| 3933 addGenericSendArgumentsToList(node.arguments, arguments); | |
| 3934 } | |
| 3935 return generateSuperNoSuchMethodSend(node, selector, arguments); | |
| 3936 } | |
| 3937 List<HInstruction> inputs = <HInstruction>[]; | |
| 3938 if (node.isPropertyAccess) { | |
| 3939 push(buildInvokeSuper(selector, element, inputs)); | |
| 3940 } else if (element.isFunction || element.isGenerativeConstructor) { | |
| 3941 if (selector.applies(element, compiler.world)) { | |
| 3942 // TODO(5347): Try to avoid the need for calling [implementation] before | |
| 3943 // calling [addStaticSendArgumentsToList]. | |
| 3944 FunctionElement function = element.implementation; | |
| 3945 bool succeeded = addStaticSendArgumentsToList(selector, node.arguments, | |
| 3946 function, inputs); | |
| 3947 assert(succeeded); | |
| 3948 push(buildInvokeSuper(selector, element, inputs)); | |
| 3949 } else if (element.isGenerativeConstructor) { | |
| 3950 generateWrongArgumentCountError(node, element, node.arguments); | |
| 3951 } else { | |
| 3952 addGenericSendArgumentsToList(node.arguments, inputs); | |
| 3953 generateSuperNoSuchMethodSend(node, selector, inputs); | |
| 3954 } | |
| 3955 } else { | |
| 3956 HInstruction target = buildInvokeSuper(selector, element, inputs); | |
| 3957 add(target); | |
| 3958 inputs = <HInstruction>[target]; | |
| 3959 addDynamicSendArgumentsToList(node, inputs); | |
| 3960 Selector closureSelector = new Selector.callClosureFrom(selector); | |
| 3961 push(new HInvokeClosure(closureSelector, inputs, backend.dynamicType)); | |
| 3962 } | |
| 3963 } | |
| 3964 | |
| 3965 bool needsSubstitutionForTypeVariableAccess(ClassElement cls) { | |
| 3966 ClassWorld classWorld = compiler.world; | |
| 3967 if (classWorld.isUsedAsMixin(cls)) return true; | |
| 3968 | |
| 3969 Iterable<ClassElement> subclasses = compiler.world.strictSubclassesOf(cls); | |
| 3970 return subclasses.any((ClassElement subclass) { | |
| 3971 return !rti.isTrivialSubstitution(subclass, cls); | |
| 3972 }); | |
| 3973 } | |
| 3974 | |
| 3975 /** | |
| 3976 * Generate code to extract the type arguments from the object, substitute | |
| 3977 * them as an instance of the type we are testing against (if necessary), and | |
| 3978 * extract the type argument by the index of the variable in the list of type | |
| 3979 * variables for that class. | |
| 3980 */ | |
| 3981 HInstruction readTypeVariable(ClassElement cls, | |
| 3982 TypeVariableElement variable) { | |
| 3983 assert(sourceElement.isInstanceMember); | |
| 3984 | |
| 3985 HInstruction target = localsHandler.readThis(); | |
| 3986 HConstant index = graph.addConstantInt( | |
| 3987 RuntimeTypes.getTypeVariableIndex(variable), | |
| 3988 compiler); | |
| 3989 | |
| 3990 if (needsSubstitutionForTypeVariableAccess(cls)) { | |
| 3991 // TODO(ahe): Creating a string here is unfortunate. It is slow (due to | |
| 3992 // string concatenation in the implementation), and may prevent | |
| 3993 // segmentation of '$'. | |
| 3994 String substitutionNameString = backend.namer.getNameForRti(cls); | |
| 3995 HInstruction substitutionName = graph.addConstantString( | |
| 3996 new ast.LiteralDartString(substitutionNameString), compiler); | |
| 3997 pushInvokeStatic(null, | |
| 3998 backend.getGetRuntimeTypeArgument(), | |
| 3999 [target, substitutionName, index], | |
| 4000 backend.dynamicType); | |
| 4001 } else { | |
| 4002 pushInvokeStatic(null, backend.getGetTypeArgumentByIndex(), | |
| 4003 [target, index], | |
| 4004 backend.dynamicType); | |
| 4005 } | |
| 4006 return pop(); | |
| 4007 } | |
| 4008 | |
| 4009 // TODO(karlklose): this is needed to avoid a bug where the resolved type is | |
| 4010 // not stored on a type annotation in the closure translator. Remove when | |
| 4011 // fixed. | |
| 4012 bool hasDirectLocal(Local local) { | |
| 4013 return !localsHandler.isAccessedDirectly(local) || | |
| 4014 localsHandler.directLocals[local] != null; | |
| 4015 } | |
| 4016 | |
| 4017 /** | |
| 4018 * Helper to create an instruction that gets the value of a type variable. | |
| 4019 */ | |
| 4020 HInstruction addTypeVariableReference(TypeVariableType type) { | |
| 4021 assert(assertTypeInContext(type)); | |
| 4022 Element member = sourceElement; | |
| 4023 bool isClosure = member.enclosingElement.isClosure; | |
| 4024 if (isClosure) { | |
| 4025 ClosureClassElement closureClass = member.enclosingElement; | |
| 4026 member = closureClass.methodElement; | |
| 4027 member = member.outermostEnclosingMemberOrTopLevel; | |
| 4028 } | |
| 4029 bool isInConstructorContext = member.isConstructor || | |
| 4030 member.isGenerativeConstructorBody; | |
| 4031 Local typeVariableLocal = localsHandler.getTypeVariableAsLocal(type); | |
| 4032 if (isClosure) { | |
| 4033 if (member.isFactoryConstructor || | |
| 4034 (isInConstructorContext && hasDirectLocal(typeVariableLocal))) { | |
| 4035 // The type variable is used from a closure in a factory constructor. | |
| 4036 // The value of the type argument is stored as a local on the closure | |
| 4037 // itself. | |
| 4038 return localsHandler.readLocal(typeVariableLocal); | |
| 4039 } else if (member.isFunction || | |
| 4040 member.isGetter || | |
| 4041 member.isSetter || | |
| 4042 isInConstructorContext) { | |
| 4043 // The type variable is stored on the "enclosing object" and needs to be | |
| 4044 // accessed using the this-reference in the closure. | |
| 4045 return readTypeVariable(member.enclosingClass, type.element); | |
| 4046 } else { | |
| 4047 assert(member.isField); | |
| 4048 // The type variable is stored in a parameter of the method. | |
| 4049 return localsHandler.readLocal(typeVariableLocal); | |
| 4050 } | |
| 4051 } else if (isInConstructorContext || | |
| 4052 // When [member] is a field, we can be either | |
| 4053 // generating a checked setter or inlining its | |
| 4054 // initializer in a constructor. An initializer is | |
| 4055 // never built standalone, so [isBuildingFor] will | |
| 4056 // always return true when seeing one. | |
| 4057 (member.isField && !isBuildingFor(member))) { | |
| 4058 // The type variable is stored in a parameter of the method. | |
| 4059 return localsHandler.readLocal(typeVariableLocal); | |
| 4060 } else if (member.isInstanceMember) { | |
| 4061 // The type variable is stored on the object. | |
| 4062 return readTypeVariable(member.enclosingClass, | |
| 4063 type.element); | |
| 4064 } else { | |
| 4065 // TODO(ngeoffray): Match the VM behavior and throw an | |
| 4066 // exception at runtime. | |
| 4067 compiler.internalError(type.element, | |
| 4068 'Unimplemented unresolved type variable.'); | |
| 4069 return null; | |
| 4070 } | |
| 4071 } | |
| 4072 | |
| 4073 HInstruction analyzeTypeArgument(DartType argument) { | |
| 4074 assert(assertTypeInContext(argument)); | |
| 4075 if (argument.treatAsDynamic) { | |
| 4076 // Represent [dynamic] as [null]. | |
| 4077 return graph.addConstantNull(compiler); | |
| 4078 } | |
| 4079 | |
| 4080 if (argument.isTypeVariable) { | |
| 4081 return addTypeVariableReference(argument); | |
| 4082 } | |
| 4083 | |
| 4084 List<HInstruction> inputs = <HInstruction>[]; | |
| 4085 | |
| 4086 String template = rti.getTypeRepresentationWithHashes(argument, (variable) { | |
| 4087 inputs.add(addTypeVariableReference(variable)); | |
| 4088 }); | |
| 4089 | |
| 4090 js.Template code = js.js.uncachedExpressionTemplate(template); | |
| 4091 HInstruction result = createForeign(code, backend.stringType, inputs); | |
| 4092 add(result); | |
| 4093 return result; | |
| 4094 } | |
| 4095 | |
| 4096 HInstruction handleListConstructor(InterfaceType type, | |
| 4097 ast.Node currentNode, | |
| 4098 HInstruction newObject) { | |
| 4099 if (!backend.classNeedsRti(type.element) || type.treatAsRaw) { | |
| 4100 return newObject; | |
| 4101 } | |
| 4102 List<HInstruction> inputs = <HInstruction>[]; | |
| 4103 type = localsHandler.substInContext(type); | |
| 4104 type.typeArguments.forEach((DartType argument) { | |
| 4105 inputs.add(analyzeTypeArgument(argument)); | |
| 4106 }); | |
| 4107 // TODO(15489): Register at codegen. | |
| 4108 registry.registerInstantiatedType(type); | |
| 4109 return callSetRuntimeTypeInfo(type.element, inputs, newObject); | |
| 4110 } | |
| 4111 | |
| 4112 void copyRuntimeTypeInfo(HInstruction source, HInstruction target) { | |
| 4113 Element copyHelper = backend.getCopyTypeArguments(); | |
| 4114 pushInvokeStatic(null, copyHelper, [source, target]); | |
| 4115 pop(); | |
| 4116 } | |
| 4117 | |
| 4118 HInstruction callSetRuntimeTypeInfo(ClassElement element, | |
| 4119 List<HInstruction> rtiInputs, | |
| 4120 HInstruction newObject) { | |
| 4121 if (!backend.classNeedsRti(element) || element.typeVariables.isEmpty) { | |
| 4122 return newObject; | |
| 4123 } | |
| 4124 | |
| 4125 HInstruction typeInfo = buildLiteralList(rtiInputs); | |
| 4126 add(typeInfo); | |
| 4127 | |
| 4128 // Set the runtime type information on the object. | |
| 4129 Element typeInfoSetterElement = backend.getSetRuntimeTypeInfo(); | |
| 4130 pushInvokeStatic( | |
| 4131 null, | |
| 4132 typeInfoSetterElement, | |
| 4133 <HInstruction>[newObject, typeInfo], | |
| 4134 backend.dynamicType); | |
| 4135 | |
| 4136 // The new object will now be referenced through the | |
| 4137 // `setRuntimeTypeInfo` call. We therefore set the type of that | |
| 4138 // instruction to be of the object's type. | |
| 4139 assert(stack.last is HInvokeStatic || stack.last == newObject); | |
| 4140 stack.last.instructionType = newObject.instructionType; | |
| 4141 return pop(); | |
| 4142 } | |
| 4143 | |
| 4144 handleNewSend(ast.NewExpression node) { | |
| 4145 ast.Send send = node.send; | |
| 4146 generateIsDeferredLoadedCheckIfNeeded(send); | |
| 4147 | |
| 4148 bool isFixedList = false; | |
| 4149 bool isFixedListConstructorCall = | |
| 4150 Elements.isFixedListConstructorCall(elements[send], send, compiler); | |
| 4151 bool isGrowableListConstructorCall = | |
| 4152 Elements.isGrowableListConstructorCall(elements[send], send, compiler); | |
| 4153 | |
| 4154 TypeMask computeType(element) { | |
| 4155 Element originalElement = elements[send]; | |
| 4156 if (isFixedListConstructorCall | |
| 4157 || Elements.isFilledListConstructorCall( | |
| 4158 originalElement, send, compiler)) { | |
| 4159 isFixedList = true; | |
| 4160 TypeMask inferred = | |
| 4161 TypeMaskFactory.inferredForNode(sourceElement, send, compiler); | |
| 4162 return inferred.containsAll(compiler.world) | |
| 4163 ? backend.fixedArrayType | |
| 4164 : inferred; | |
| 4165 } else if (isGrowableListConstructorCall) { | |
| 4166 TypeMask inferred = | |
| 4167 TypeMaskFactory.inferredForNode(sourceElement, send, compiler); | |
| 4168 return inferred.containsAll(compiler.world) | |
| 4169 ? backend.extendableArrayType | |
| 4170 : inferred; | |
| 4171 } else if (Elements.isConstructorOfTypedArraySubclass( | |
| 4172 originalElement, compiler)) { | |
| 4173 isFixedList = true; | |
| 4174 TypeMask inferred = | |
| 4175 TypeMaskFactory.inferredForNode(sourceElement, send, compiler); | |
| 4176 ClassElement cls = element.enclosingClass; | |
| 4177 assert(cls.thisType.element.isNative); | |
| 4178 return inferred.containsAll(compiler.world) | |
| 4179 ? new TypeMask.nonNullExact(cls.thisType.element, compiler.world) | |
| 4180 : inferred; | |
| 4181 } else if (element.isGenerativeConstructor) { | |
| 4182 ClassElement cls = element.enclosingClass; | |
| 4183 return new TypeMask.nonNullExact(cls.thisType.element, compiler.world); | |
| 4184 } else { | |
| 4185 return TypeMaskFactory.inferredReturnTypeForElement( | |
| 4186 originalElement, compiler); | |
| 4187 } | |
| 4188 } | |
| 4189 | |
| 4190 Element constructor = elements[send]; | |
| 4191 Selector selector = elements.getSelector(send); | |
| 4192 ConstructorElement constructorDeclaration = constructor; | |
| 4193 ConstructorElement constructorImplementation = constructor.implementation; | |
| 4194 constructor = constructorImplementation.effectiveTarget; | |
| 4195 | |
| 4196 final bool isSymbolConstructor = | |
| 4197 constructorDeclaration == compiler.symbolConstructor; | |
| 4198 final bool isJSArrayTypedConstructor = | |
| 4199 constructorDeclaration == backend.jsArrayTypedConstructor; | |
| 4200 | |
| 4201 if (isSymbolConstructor) { | |
| 4202 constructor = compiler.symbolValidatedConstructor; | |
| 4203 assert(invariant(send, constructor != null, | |
| 4204 message: 'Constructor Symbol.validated is missing')); | |
| 4205 selector = compiler.symbolValidatedConstructorSelector; | |
| 4206 assert(invariant(send, selector != null, | |
| 4207 message: 'Constructor Symbol.validated is missing')); | |
| 4208 } | |
| 4209 | |
| 4210 bool isRedirected = constructorDeclaration.isRedirectingFactory; | |
| 4211 InterfaceType type = elements.getType(node); | |
| 4212 InterfaceType expectedType = | |
| 4213 constructorDeclaration.computeEffectiveTargetType(type); | |
| 4214 expectedType = localsHandler.substInContext(expectedType); | |
| 4215 | |
| 4216 if (checkTypeVariableBounds(node, type)) return; | |
| 4217 | |
| 4218 var inputs = <HInstruction>[]; | |
| 4219 if (constructor.isGenerativeConstructor && | |
| 4220 Elements.isNativeOrExtendsNative(constructor.enclosingClass)) { | |
| 4221 // Native class generative constructors take a pre-constructed object. | |
| 4222 inputs.add(graph.addConstantNull(compiler)); | |
| 4223 } | |
| 4224 // TODO(5347): Try to avoid the need for calling [implementation] before | |
| 4225 // calling [addStaticSendArgumentsToList]. | |
| 4226 bool succeeded = addStaticSendArgumentsToList(selector, send.arguments, | |
| 4227 constructor.implementation, | |
| 4228 inputs); | |
| 4229 if (!succeeded) { | |
| 4230 generateWrongArgumentCountError(send, constructor, send.arguments); | |
| 4231 return; | |
| 4232 } | |
| 4233 | |
| 4234 if (constructor.isFactoryConstructor && | |
| 4235 !expectedType.typeArguments.isEmpty) { | |
| 4236 registry.registerFactoryWithTypeArguments(); | |
| 4237 } | |
| 4238 | |
| 4239 TypeMask elementType = computeType(constructor); | |
| 4240 if (isFixedListConstructorCall) { | |
| 4241 if (!inputs[0].isNumber(compiler)) { | |
| 4242 HTypeConversion conversion = new HTypeConversion( | |
| 4243 null, HTypeConversion.ARGUMENT_TYPE_CHECK, backend.numType, | |
| 4244 inputs[0], null); | |
| 4245 add(conversion); | |
| 4246 inputs[0] = conversion; | |
| 4247 } | |
| 4248 js.Template code = js.js.parseForeignJS('Array(#)'); | |
| 4249 var behavior = new native.NativeBehavior(); | |
| 4250 behavior.typesReturned.add(expectedType); | |
| 4251 // The allocation can throw only if the given length is a double | |
| 4252 // or negative. | |
| 4253 bool canThrow = true; | |
| 4254 if (inputs[0].isInteger(compiler) && inputs[0] is HConstant) { | |
| 4255 var constant = inputs[0]; | |
| 4256 if (constant.constant.primitiveValue >= 0) canThrow = false; | |
| 4257 } | |
| 4258 HForeign foreign = new HForeign( | |
| 4259 code, elementType, inputs, nativeBehavior: behavior, | |
| 4260 canThrow: canThrow); | |
| 4261 push(foreign); | |
| 4262 TypesInferrer inferrer = compiler.typesTask.typesInferrer; | |
| 4263 if (inferrer.isFixedArrayCheckedForGrowable(send)) { | |
| 4264 js.Template code = js.js.parseForeignJS(r'#.fixed$length = init'); | |
| 4265 // We set the instruction as [canThrow] to avoid it being dead code. | |
| 4266 // We need a finer grained side effect. | |
| 4267 add(new HForeign( | |
| 4268 code, backend.nullType, [stack.last], canThrow: true)); | |
| 4269 } | |
| 4270 } else if (isGrowableListConstructorCall) { | |
| 4271 push(buildLiteralList(<HInstruction>[])); | |
| 4272 stack.last.instructionType = elementType; | |
| 4273 } else { | |
| 4274 ClassElement cls = constructor.enclosingClass; | |
| 4275 if (cls.isAbstract && constructor.isGenerativeConstructor) { | |
| 4276 generateAbstractClassInstantiationError(send, cls.name); | |
| 4277 return; | |
| 4278 } | |
| 4279 potentiallyAddTypeArguments(inputs, cls, expectedType); | |
| 4280 | |
| 4281 addInlinedInstantiation(expectedType); | |
| 4282 pushInvokeStatic(node, constructor, inputs, elementType); | |
| 4283 removeInlinedInstantiation(expectedType); | |
| 4284 } | |
| 4285 HInstruction newInstance = stack.last; | |
| 4286 if (isFixedList) { | |
| 4287 // Overwrite the element type, in case the allocation site has | |
| 4288 // been inlined. | |
| 4289 newInstance.instructionType = elementType; | |
| 4290 JavaScriptItemCompilationContext context = work.compilationContext; | |
| 4291 context.allocatedFixedLists.add(newInstance); | |
| 4292 } | |
| 4293 | |
| 4294 // The List constructor forwards to a Dart static method that does | |
| 4295 // not know about the type argument. Therefore we special case | |
| 4296 // this constructor to have the setRuntimeTypeInfo called where | |
| 4297 // the 'new' is done. | |
| 4298 if (backend.classNeedsRti(compiler.listClass) && | |
| 4299 (isFixedListConstructorCall || isGrowableListConstructorCall || | |
| 4300 isJSArrayTypedConstructor)) { | |
| 4301 newInstance = handleListConstructor(type, send, pop()); | |
| 4302 stack.add(newInstance); | |
| 4303 } | |
| 4304 | |
| 4305 // Finally, if we called a redirecting factory constructor, check the type. | |
| 4306 if (isRedirected) { | |
| 4307 HInstruction checked = potentiallyCheckType(newInstance, type); | |
| 4308 if (checked != newInstance) { | |
| 4309 pop(); | |
| 4310 stack.add(checked); | |
| 4311 } | |
| 4312 } | |
| 4313 } | |
| 4314 | |
| 4315 void potentiallyAddTypeArguments(List<HInstruction> inputs, ClassElement cls, | |
| 4316 InterfaceType expectedType) { | |
| 4317 if (!backend.classNeedsRti(cls)) return; | |
| 4318 assert(expectedType.typeArguments.isEmpty || | |
| 4319 cls.typeVariables.length == expectedType.typeArguments.length); | |
| 4320 expectedType.typeArguments.forEach((DartType argument) { | |
| 4321 inputs.add(analyzeTypeArgument(argument)); | |
| 4322 }); | |
| 4323 } | |
| 4324 | |
| 4325 /// In checked mode checks the [type] of [node] to be well-bounded. The method | |
| 4326 /// returns [:true:] if an error can be statically determined. | |
| 4327 bool checkTypeVariableBounds(ast.NewExpression node, InterfaceType type) { | |
| 4328 if (!compiler.enableTypeAssertions) return false; | |
| 4329 | |
| 4330 Map<DartType, Set<DartType>> seenChecksMap = | |
| 4331 new Map<DartType, Set<DartType>>(); | |
| 4332 bool definitelyFails = false; | |
| 4333 | |
| 4334 addTypeVariableBoundCheck(GenericType instance, | |
| 4335 DartType typeArgument, | |
| 4336 TypeVariableType typeVariable, | |
| 4337 DartType bound) { | |
| 4338 if (definitelyFails) return; | |
| 4339 | |
| 4340 int subtypeRelation = compiler.types.computeSubtypeRelation(typeArgument,
bound); | |
| 4341 if (subtypeRelation == Types.IS_SUBTYPE) return; | |
| 4342 | |
| 4343 String message = | |
| 4344 "Can't create an instance of malbounded type '$type': " | |
| 4345 "'${typeArgument}' is not a subtype of bound '${bound}' for " | |
| 4346 "type variable '${typeVariable}' of type " | |
| 4347 "${type == instance | |
| 4348 ? "'${type.element.thisType}'" | |
| 4349 : "'${instance.element.thisType}' on the supertype " | |
| 4350 "'${instance}' of '${type}'" | |
| 4351 }."; | |
| 4352 if (subtypeRelation == Types.NOT_SUBTYPE) { | |
| 4353 generateTypeError(node, message); | |
| 4354 definitelyFails = true; | |
| 4355 return; | |
| 4356 } else if (subtypeRelation == Types.MAYBE_SUBTYPE) { | |
| 4357 Set<DartType> seenChecks = | |
| 4358 seenChecksMap.putIfAbsent(typeArgument, () => new Set<DartType>()); | |
| 4359 if (!seenChecks.contains(bound)) { | |
| 4360 seenChecks.add(bound); | |
| 4361 assertIsSubtype(node, typeArgument, bound, message); | |
| 4362 } | |
| 4363 } | |
| 4364 } | |
| 4365 | |
| 4366 compiler.types.checkTypeVariableBounds(type, addTypeVariableBoundCheck); | |
| 4367 if (definitelyFails) { | |
| 4368 return true; | |
| 4369 } | |
| 4370 for (InterfaceType supertype in type.element.allSupertypes) { | |
| 4371 DartType instance = type.asInstanceOf(supertype.element); | |
| 4372 compiler.types.checkTypeVariableBounds(instance, | |
| 4373 addTypeVariableBoundCheck); | |
| 4374 if (definitelyFails) { | |
| 4375 return true; | |
| 4376 } | |
| 4377 } | |
| 4378 return false; | |
| 4379 } | |
| 4380 | |
| 4381 visitAssert(node) { | |
| 4382 if (!compiler.enableUserAssertions) { | |
| 4383 stack.add(graph.addConstantNull(compiler)); | |
| 4384 return; | |
| 4385 } | |
| 4386 // TODO(johnniwinther): Don't handle assert like a regular static call. | |
| 4387 // It breaks the selector name check since the assert helper method cannot | |
| 4388 // be called `assert` and therefore does not match the selector like a | |
| 4389 // regular method. | |
| 4390 visitStaticSend(node); | |
| 4391 } | |
| 4392 | |
| 4393 visitStaticSend(ast.Send node) { | |
| 4394 Selector selector = elements.getSelector(node); | |
| 4395 Element element = elements[node]; | |
| 4396 if (elements.isAssert(node)) { | |
| 4397 element = backend.assertMethod; | |
| 4398 } | |
| 4399 if (element.isForeign(backend) && element.isFunction) { | |
| 4400 visitForeignSend(node); | |
| 4401 return; | |
| 4402 } | |
| 4403 if (element.isErroneous) { | |
| 4404 // An erroneous element indicates that the funciton could not be resolved | |
| 4405 // (a warning has been issued). | |
| 4406 generateThrowNoSuchMethod(node, | |
| 4407 noSuchMethodTargetSymbolString(element), | |
| 4408 argumentNodes: node.arguments); | |
| 4409 return; | |
| 4410 } | |
| 4411 invariant(element, !element.isGenerativeConstructor); | |
| 4412 generateIsDeferredLoadedCheckIfNeeded(node); | |
| 4413 if (element.isFunction) { | |
| 4414 var inputs = <HInstruction>[]; | |
| 4415 // TODO(5347): Try to avoid the need for calling [implementation] before | |
| 4416 // calling [addStaticSendArgumentsToList]. | |
| 4417 bool succeeded = addStaticSendArgumentsToList(selector, node.arguments, | |
| 4418 element.implementation, | |
| 4419 inputs); | |
| 4420 if (!succeeded) { | |
| 4421 generateWrongArgumentCountError(node, element, node.arguments); | |
| 4422 return; | |
| 4423 } | |
| 4424 | |
| 4425 if (element == compiler.identicalFunction) { | |
| 4426 pushWithPosition( | |
| 4427 new HIdentity(inputs[0], inputs[1], null, backend.boolType), node); | |
| 4428 return; | |
| 4429 } | |
| 4430 | |
| 4431 pushInvokeStatic(node, element, inputs); | |
| 4432 } else { | |
| 4433 generateGetter(node, element); | |
| 4434 List<HInstruction> inputs = <HInstruction>[pop()]; | |
| 4435 addDynamicSendArgumentsToList(node, inputs); | |
| 4436 Selector closureSelector = new Selector.callClosureFrom(selector); | |
| 4437 pushWithPosition( | |
| 4438 new HInvokeClosure(closureSelector, inputs, backend.dynamicType), | |
| 4439 node); | |
| 4440 } | |
| 4441 } | |
| 4442 | |
| 4443 HConstant addConstantString(String string) { | |
| 4444 ast.DartString dartString = new ast.DartString.literal(string); | |
| 4445 ConstantValue constant = constantSystem.createString(dartString); | |
| 4446 return graph.addConstant(constant, compiler); | |
| 4447 } | |
| 4448 | |
| 4449 visitTypePrefixSend(ast.Send node) { | |
| 4450 compiler.internalError(node, "visitTypePrefixSend should not be called."); | |
| 4451 } | |
| 4452 | |
| 4453 visitTypeLiteralSend(ast.Send node) { | |
| 4454 DartType type = elements.getTypeLiteralType(node); | |
| 4455 if (type.isInterfaceType || type.isTypedef || type.isDynamic) { | |
| 4456 // TODO(karlklose): add type representation | |
| 4457 if (node.isCall) { | |
| 4458 // The node itself is not a constant but we register the selector (the | |
| 4459 // identifier that refers to the class/typedef) as a constant. | |
| 4460 stack.add(addConstant(node.selector)); | |
| 4461 } else { | |
| 4462 stack.add(addConstant(node)); | |
| 4463 } | |
| 4464 } else if (type.isTypeVariable) { | |
| 4465 type = localsHandler.substInContext(type); | |
| 4466 HInstruction value = analyzeTypeArgument(type); | |
| 4467 pushInvokeStatic(node, | |
| 4468 backend.getRuntimeTypeToString(), | |
| 4469 [value], | |
| 4470 backend.stringType); | |
| 4471 pushInvokeStatic(node, | |
| 4472 backend.getCreateRuntimeType(), | |
| 4473 [pop()]); | |
| 4474 } else { | |
| 4475 internalError('unexpected type kind ${type.kind}', node: node); | |
| 4476 } | |
| 4477 if (node.isCall) { | |
| 4478 // This send is of the form 'e(...)', where e is resolved to a type | |
| 4479 // reference. We create a regular closure call on the result of the type | |
| 4480 // reference instead of creating a NoSuchMethodError to avoid pulling it | |
| 4481 // in if it is not used (e.g., in a try/catch). | |
| 4482 HInstruction target = pop(); | |
| 4483 Selector selector = elements.getSelector(node); | |
| 4484 List<HInstruction> inputs = <HInstruction>[target]; | |
| 4485 addDynamicSendArgumentsToList(node, inputs); | |
| 4486 Selector closureSelector = new Selector.callClosureFrom(selector); | |
| 4487 push(new HInvokeClosure(closureSelector, inputs, backend.dynamicType)); | |
| 4488 } | |
| 4489 } | |
| 4490 | |
| 4491 visitGetterSend(ast.Send node) { | |
| 4492 generateIsDeferredLoadedCheckIfNeeded(node); | |
| 4493 generateGetter(node, elements[node]); | |
| 4494 } | |
| 4495 | |
| 4496 // TODO(antonm): migrate rest of SsaFromAstMixin to internalError. | |
| 4497 internalError(String reason, {ast.Node node}) { | |
| 4498 compiler.internalError(node, reason); | |
| 4499 } | |
| 4500 | |
| 4501 void generateError(ast.Node node, String message, Element helper) { | |
| 4502 HInstruction errorMessage = addConstantString(message); | |
| 4503 pushInvokeStatic(node, helper, [errorMessage]); | |
| 4504 } | |
| 4505 | |
| 4506 void generateRuntimeError(ast.Node node, String message) { | |
| 4507 generateError(node, message, backend.getThrowRuntimeError()); | |
| 4508 } | |
| 4509 | |
| 4510 void generateTypeError(ast.Node node, String message) { | |
| 4511 generateError(node, message, backend.getThrowTypeError()); | |
| 4512 } | |
| 4513 | |
| 4514 void generateAbstractClassInstantiationError(ast.Node node, String message) { | |
| 4515 generateError(node, | |
| 4516 message, | |
| 4517 backend.getThrowAbstractClassInstantiationError()); | |
| 4518 } | |
| 4519 | |
| 4520 void generateThrowNoSuchMethod(ast.Node diagnosticNode, | |
| 4521 String methodName, | |
| 4522 {Link<ast.Node> argumentNodes, | |
| 4523 List<HInstruction> argumentValues, | |
| 4524 List<String> existingArguments}) { | |
| 4525 Element helper = backend.getThrowNoSuchMethod(); | |
| 4526 ConstantValue receiverConstant = | |
| 4527 constantSystem.createString(new ast.DartString.empty()); | |
| 4528 HInstruction receiver = graph.addConstant(receiverConstant, compiler); | |
| 4529 ast.DartString dartString = new ast.DartString.literal(methodName); | |
| 4530 ConstantValue nameConstant = constantSystem.createString(dartString); | |
| 4531 HInstruction name = graph.addConstant(nameConstant, compiler); | |
| 4532 if (argumentValues == null) { | |
| 4533 argumentValues = <HInstruction>[]; | |
| 4534 argumentNodes.forEach((argumentNode) { | |
| 4535 visit(argumentNode); | |
| 4536 HInstruction value = pop(); | |
| 4537 argumentValues.add(value); | |
| 4538 }); | |
| 4539 } | |
| 4540 HInstruction arguments = buildLiteralList(argumentValues); | |
| 4541 add(arguments); | |
| 4542 HInstruction existingNamesList; | |
| 4543 if (existingArguments != null) { | |
| 4544 List<HInstruction> existingNames = <HInstruction>[]; | |
| 4545 for (String name in existingArguments) { | |
| 4546 HInstruction nameConstant = | |
| 4547 graph.addConstantString(new ast.DartString.literal(name), compiler); | |
| 4548 existingNames.add(nameConstant); | |
| 4549 } | |
| 4550 existingNamesList = buildLiteralList(existingNames); | |
| 4551 add(existingNamesList); | |
| 4552 } else { | |
| 4553 existingNamesList = graph.addConstantNull(compiler); | |
| 4554 } | |
| 4555 pushInvokeStatic(diagnosticNode, | |
| 4556 helper, | |
| 4557 [receiver, name, arguments, existingNamesList]); | |
| 4558 } | |
| 4559 | |
| 4560 /** | |
| 4561 * Generate code to throw a [NoSuchMethodError] exception for calling a | |
| 4562 * method with a wrong number of arguments or mismatching named optional | |
| 4563 * arguments. | |
| 4564 */ | |
| 4565 void generateWrongArgumentCountError(ast.Node diagnosticNode, | |
| 4566 FunctionElement function, | |
| 4567 Link<ast.Node> argumentNodes) { | |
| 4568 List<String> existingArguments = <String>[]; | |
| 4569 FunctionSignature signature = function.functionSignature; | |
| 4570 signature.forEachParameter((Element parameter) { | |
| 4571 existingArguments.add(parameter.name); | |
| 4572 }); | |
| 4573 generateThrowNoSuchMethod(diagnosticNode, | |
| 4574 function.name, | |
| 4575 argumentNodes: argumentNodes, | |
| 4576 existingArguments: existingArguments); | |
| 4577 } | |
| 4578 | |
| 4579 visitNewExpression(ast.NewExpression node) { | |
| 4580 Element element = elements[node.send]; | |
| 4581 final bool isSymbolConstructor = element == compiler.symbolConstructor; | |
| 4582 if (!Elements.isErroneousElement(element)) { | |
| 4583 ConstructorElement function = element; | |
| 4584 element = function.effectiveTarget; | |
| 4585 } | |
| 4586 if (Elements.isErroneousElement(element)) { | |
| 4587 ErroneousElement error = element; | |
| 4588 if (error.messageKind == MessageKind.CANNOT_FIND_CONSTRUCTOR) { | |
| 4589 generateThrowNoSuchMethod( | |
| 4590 node.send, | |
| 4591 noSuchMethodTargetSymbolString(error, 'constructor'), | |
| 4592 argumentNodes: node.send.arguments); | |
| 4593 } else { | |
| 4594 Message message = error.messageKind.message(error.messageArguments); | |
| 4595 generateRuntimeError(node.send, message.toString()); | |
| 4596 } | |
| 4597 } else if (node.isConst) { | |
| 4598 stack.add(addConstant(node)); | |
| 4599 if (isSymbolConstructor) { | |
| 4600 ConstructedConstantValue symbol = getConstantForNode(node); | |
| 4601 StringConstantValue stringConstant = symbol.fields.single; | |
| 4602 String nameString = stringConstant.toDartString().slowToString(); | |
| 4603 registry.registerConstSymbol(nameString); | |
| 4604 } | |
| 4605 } else { | |
| 4606 handleNewSend(node); | |
| 4607 } | |
| 4608 } | |
| 4609 | |
| 4610 void pushInvokeDynamic(ast.Node node, | |
| 4611 Selector selector, | |
| 4612 List<HInstruction> arguments, | |
| 4613 {ast.Node location}) { | |
| 4614 if (location == null) location = node; | |
| 4615 | |
| 4616 // We prefer to not inline certain operations on indexables, | |
| 4617 // because the constant folder will handle them better and turn | |
| 4618 // them into simpler instructions that allow further | |
| 4619 // optimizations. | |
| 4620 bool isOptimizableOperationOnIndexable(Selector selector, Element element) { | |
| 4621 bool isLength = selector.isGetter | |
| 4622 && selector.name == "length"; | |
| 4623 if (isLength || selector.isIndex) { | |
| 4624 TypeMask type = new TypeMask.nonNullExact( | |
| 4625 element.enclosingClass.declaration, compiler.world); | |
| 4626 return type.satisfies(backend.jsIndexableClass, compiler.world); | |
| 4627 } else if (selector.isIndexSet) { | |
| 4628 TypeMask type = new TypeMask.nonNullExact( | |
| 4629 element.enclosingClass.declaration, compiler.world); | |
| 4630 return type.satisfies(backend.jsMutableIndexableClass, compiler.world); | |
| 4631 } else { | |
| 4632 return false; | |
| 4633 } | |
| 4634 } | |
| 4635 | |
| 4636 bool isOptimizableOperation(Selector selector, Element element) { | |
| 4637 ClassElement cls = element.enclosingClass; | |
| 4638 if (isOptimizableOperationOnIndexable(selector, element)) return true; | |
| 4639 if (!backend.interceptedClasses.contains(cls)) return false; | |
| 4640 if (selector.isOperator) return true; | |
| 4641 if (selector.isSetter) return true; | |
| 4642 if (selector.isIndex) return true; | |
| 4643 if (selector.isIndexSet) return true; | |
| 4644 if (element == backend.jsArrayAdd | |
| 4645 || element == backend.jsArrayRemoveLast | |
| 4646 || element == backend.jsStringSplit) { | |
| 4647 return true; | |
| 4648 } | |
| 4649 return false; | |
| 4650 } | |
| 4651 | |
| 4652 Element element = compiler.world.locateSingleElement(selector); | |
| 4653 if (element != null | |
| 4654 && !element.isField | |
| 4655 && !(element.isGetter && selector.isCall) | |
| 4656 && !(element.isFunction && selector.isGetter) | |
| 4657 && !isOptimizableOperation(selector, element)) { | |
| 4658 if (tryInlineMethod(element, selector, arguments, node)) { | |
| 4659 return; | |
| 4660 } | |
| 4661 } | |
| 4662 | |
| 4663 HInstruction receiver = arguments[0]; | |
| 4664 List<HInstruction> inputs = <HInstruction>[]; | |
| 4665 bool isIntercepted = backend.isInterceptedSelector(selector); | |
| 4666 if (isIntercepted) { | |
| 4667 inputs.add(invokeInterceptor(receiver)); | |
| 4668 } | |
| 4669 inputs.addAll(arguments); | |
| 4670 TypeMask type = TypeMaskFactory.inferredTypeForSelector(selector, compiler); | |
| 4671 if (selector.isGetter) { | |
| 4672 pushWithPosition( | |
| 4673 new HInvokeDynamicGetter(selector, null, inputs, type), | |
| 4674 location); | |
| 4675 } else if (selector.isSetter) { | |
| 4676 pushWithPosition( | |
| 4677 new HInvokeDynamicSetter(selector, null, inputs, type), | |
| 4678 location); | |
| 4679 } else { | |
| 4680 pushWithPosition( | |
| 4681 new HInvokeDynamicMethod(selector, inputs, type, isIntercepted), | |
| 4682 location); | |
| 4683 } | |
| 4684 } | |
| 4685 | |
| 4686 void pushInvokeStatic(ast.Node location, | |
| 4687 Element element, | |
| 4688 List<HInstruction> arguments, | |
| 4689 [TypeMask type]) { | |
| 4690 if (tryInlineMethod(element, null, arguments, location)) { | |
| 4691 return; | |
| 4692 } | |
| 4693 | |
| 4694 if (type == null) { | |
| 4695 type = TypeMaskFactory.inferredReturnTypeForElement(element, compiler); | |
| 4696 } | |
| 4697 bool targetCanThrow = !compiler.world.getCannotThrow(element); | |
| 4698 // TODO(5346): Try to avoid the need for calling [declaration] before | |
| 4699 // creating an [HInvokeStatic]. | |
| 4700 HInvokeStatic instruction = new HInvokeStatic( | |
| 4701 element.declaration, arguments, type, targetCanThrow: targetCanThrow); | |
| 4702 if (!currentInlinedInstantiations.isEmpty) { | |
| 4703 instruction.instantiatedTypes = new List<DartType>.from( | |
| 4704 currentInlinedInstantiations); | |
| 4705 } | |
| 4706 instruction.sideEffects = compiler.world.getSideEffectsOfElement(element); | |
| 4707 if (location == null) { | |
| 4708 push(instruction); | |
| 4709 } else { | |
| 4710 pushWithPosition(instruction, location); | |
| 4711 } | |
| 4712 } | |
| 4713 | |
| 4714 HInstruction buildInvokeSuper(Selector selector, | |
| 4715 Element element, | |
| 4716 List<HInstruction> arguments) { | |
| 4717 HInstruction receiver = localsHandler.readThis(); | |
| 4718 // TODO(5346): Try to avoid the need for calling [declaration] before | |
| 4719 // creating an [HStatic]. | |
| 4720 List<HInstruction> inputs = <HInstruction>[]; | |
| 4721 if (backend.isInterceptedSelector(selector) && | |
| 4722 // Fields don't need an interceptor; consider generating HFieldGet/Set | |
| 4723 // instead. | |
| 4724 element.kind != ElementKind.FIELD) { | |
| 4725 inputs.add(invokeInterceptor(receiver)); | |
| 4726 } | |
| 4727 inputs.add(receiver); | |
| 4728 inputs.addAll(arguments); | |
| 4729 TypeMask type; | |
| 4730 if (!element.isGetter && selector.isGetter) { | |
| 4731 type = TypeMaskFactory.inferredTypeForElement(element, compiler); | |
| 4732 } else { | |
| 4733 type = TypeMaskFactory.inferredReturnTypeForElement(element, compiler); | |
| 4734 } | |
| 4735 HInstruction instruction = new HInvokeSuper( | |
| 4736 element, | |
| 4737 currentNonClosureClass, | |
| 4738 selector, | |
| 4739 inputs, | |
| 4740 type, | |
| 4741 isSetter: selector.isSetter || selector.isIndexSet); | |
| 4742 instruction.sideEffects = compiler.world.getSideEffectsOfSelector(selector); | |
| 4743 return instruction; | |
| 4744 } | |
| 4745 | |
| 4746 void handleComplexOperatorSend(ast.SendSet node, | |
| 4747 HInstruction receiver, | |
| 4748 Link<ast.Node> arguments) { | |
| 4749 HInstruction rhs; | |
| 4750 if (node.isPrefix || node.isPostfix) { | |
| 4751 rhs = graph.addConstantInt(1, compiler); | |
| 4752 } else { | |
| 4753 visit(arguments.head); | |
| 4754 assert(arguments.tail.isEmpty); | |
| 4755 rhs = pop(); | |
| 4756 } | |
| 4757 visitBinary(receiver, node.assignmentOperator, rhs, | |
| 4758 elements.getOperatorSelectorInComplexSendSet(node), node); | |
| 4759 } | |
| 4760 | |
| 4761 visitSendSet(ast.SendSet node) { | |
| 4762 generateIsDeferredLoadedCheckIfNeeded(node); | |
| 4763 Element element = elements[node]; | |
| 4764 if (!Elements.isUnresolved(element) && element.impliesType) { | |
| 4765 ast.Identifier selector = node.selector; | |
| 4766 generateThrowNoSuchMethod(node, selector.source, | |
| 4767 argumentNodes: node.arguments); | |
| 4768 return; | |
| 4769 } | |
| 4770 ast.Operator op = node.assignmentOperator; | |
| 4771 if (node.isSuperCall) { | |
| 4772 HInstruction result; | |
| 4773 List<HInstruction> setterInputs = <HInstruction>[]; | |
| 4774 if (identical(node.assignmentOperator.source, '=')) { | |
| 4775 addDynamicSendArgumentsToList(node, setterInputs); | |
| 4776 result = setterInputs.last; | |
| 4777 } else { | |
| 4778 Element getter = elements[node.selector]; | |
| 4779 List<HInstruction> getterInputs = <HInstruction>[]; | |
| 4780 Link<ast.Node> arguments = node.arguments; | |
| 4781 if (node.isIndex) { | |
| 4782 // If node is of the from [:super.foo[0] += 2:], the send has | |
| 4783 // two arguments: the index and the left hand side. We get | |
| 4784 // the index and add it as input of the getter and the | |
| 4785 // setter. | |
| 4786 visit(arguments.head); | |
| 4787 arguments = arguments.tail; | |
| 4788 HInstruction index = pop(); | |
| 4789 getterInputs.add(index); | |
| 4790 setterInputs.add(index); | |
| 4791 } | |
| 4792 HInstruction getterInstruction; | |
| 4793 Selector getterSelector = | |
| 4794 elements.getGetterSelectorInComplexSendSet(node); | |
| 4795 if (Elements.isUnresolved(getter)) { | |
| 4796 generateSuperNoSuchMethodSend( | |
| 4797 node, | |
| 4798 getterSelector, | |
| 4799 getterInputs); | |
| 4800 getterInstruction = pop(); | |
| 4801 } else { | |
| 4802 getterInstruction = buildInvokeSuper( | |
| 4803 getterSelector, getter, getterInputs); | |
| 4804 add(getterInstruction); | |
| 4805 } | |
| 4806 handleComplexOperatorSend(node, getterInstruction, arguments); | |
| 4807 setterInputs.add(pop()); | |
| 4808 | |
| 4809 if (node.isPostfix) { | |
| 4810 result = getterInstruction; | |
| 4811 } else { | |
| 4812 result = setterInputs.last; | |
| 4813 } | |
| 4814 } | |
| 4815 Selector setterSelector = elements.getSelector(node); | |
| 4816 if (Elements.isUnresolved(element) | |
| 4817 || !setterSelector.applies(element, compiler.world)) { | |
| 4818 generateSuperNoSuchMethodSend( | |
| 4819 node, setterSelector, setterInputs); | |
| 4820 pop(); | |
| 4821 } else { | |
| 4822 add(buildInvokeSuper(setterSelector, element, setterInputs)); | |
| 4823 } | |
| 4824 stack.add(result); | |
| 4825 } else if (node.isIndex) { | |
| 4826 if ("=" == op.source) { | |
| 4827 visitDynamicSend(node); | |
| 4828 } else { | |
| 4829 visit(node.receiver); | |
| 4830 HInstruction receiver = pop(); | |
| 4831 Link<ast.Node> arguments = node.arguments; | |
| 4832 HInstruction index; | |
| 4833 if (node.isIndex) { | |
| 4834 visit(arguments.head); | |
| 4835 arguments = arguments.tail; | |
| 4836 index = pop(); | |
| 4837 } | |
| 4838 | |
| 4839 pushInvokeDynamic( | |
| 4840 node, | |
| 4841 elements.getGetterSelectorInComplexSendSet(node), | |
| 4842 [receiver, index]); | |
| 4843 HInstruction getterInstruction = pop(); | |
| 4844 | |
| 4845 handleComplexOperatorSend(node, getterInstruction, arguments); | |
| 4846 HInstruction value = pop(); | |
| 4847 | |
| 4848 pushInvokeDynamic( | |
| 4849 node, elements.getSelector(node), [receiver, index, value]); | |
| 4850 pop(); | |
| 4851 | |
| 4852 if (node.isPostfix) { | |
| 4853 stack.add(getterInstruction); | |
| 4854 } else { | |
| 4855 stack.add(value); | |
| 4856 } | |
| 4857 } | |
| 4858 } else if ("=" == op.source) { | |
| 4859 Link<ast.Node> link = node.arguments; | |
| 4860 assert(!link.isEmpty && link.tail.isEmpty); | |
| 4861 if (Elements.isInstanceSend(node, elements)) { | |
| 4862 HInstruction receiver = generateInstanceSendReceiver(node); | |
| 4863 visit(link.head); | |
| 4864 generateInstanceSetterWithCompiledReceiver(node, receiver, pop()); | |
| 4865 } else { | |
| 4866 visit(link.head); | |
| 4867 generateNonInstanceSetter(node, element, pop()); | |
| 4868 } | |
| 4869 } else if (identical(op.source, "is")) { | |
| 4870 compiler.internalError(op, "is-operator as SendSet."); | |
| 4871 } else { | |
| 4872 assert("++" == op.source || "--" == op.source || | |
| 4873 node.assignmentOperator.source.endsWith("=")); | |
| 4874 | |
| 4875 // [receiver] is only used if the node is an instance send. | |
| 4876 HInstruction receiver = null; | |
| 4877 Element getter = elements[node.selector]; | |
| 4878 | |
| 4879 if (!Elements.isUnresolved(getter) && getter.impliesType) { | |
| 4880 ast.Identifier selector = node.selector; | |
| 4881 generateThrowNoSuchMethod(node, selector.source, | |
| 4882 argumentNodes: node.arguments); | |
| 4883 return; | |
| 4884 } else if (Elements.isInstanceSend(node, elements)) { | |
| 4885 receiver = generateInstanceSendReceiver(node); | |
| 4886 generateInstanceGetterWithCompiledReceiver( | |
| 4887 node, elements.getGetterSelectorInComplexSendSet(node), receiver); | |
| 4888 } else { | |
| 4889 generateGetter(node, getter); | |
| 4890 } | |
| 4891 HInstruction getterInstruction = pop(); | |
| 4892 handleComplexOperatorSend(node, getterInstruction, node.arguments); | |
| 4893 HInstruction value = pop(); | |
| 4894 assert(value != null); | |
| 4895 if (Elements.isInstanceSend(node, elements)) { | |
| 4896 assert(receiver != null); | |
| 4897 generateInstanceSetterWithCompiledReceiver(node, receiver, value); | |
| 4898 } else { | |
| 4899 assert(receiver == null); | |
| 4900 generateNonInstanceSetter(node, element, value); | |
| 4901 } | |
| 4902 if (node.isPostfix) { | |
| 4903 pop(); | |
| 4904 stack.add(getterInstruction); | |
| 4905 } | |
| 4906 } | |
| 4907 } | |
| 4908 | |
| 4909 void visitLiteralInt(ast.LiteralInt node) { | |
| 4910 stack.add(graph.addConstantInt(node.value, compiler)); | |
| 4911 } | |
| 4912 | |
| 4913 void visitLiteralDouble(ast.LiteralDouble node) { | |
| 4914 stack.add(graph.addConstantDouble(node.value, compiler)); | |
| 4915 } | |
| 4916 | |
| 4917 void visitLiteralBool(ast.LiteralBool node) { | |
| 4918 stack.add(graph.addConstantBool(node.value, compiler)); | |
| 4919 } | |
| 4920 | |
| 4921 void visitLiteralString(ast.LiteralString node) { | |
| 4922 stack.add(graph.addConstantString(node.dartString, compiler)); | |
| 4923 } | |
| 4924 | |
| 4925 void visitLiteralSymbol(ast.LiteralSymbol node) { | |
| 4926 stack.add(addConstant(node)); | |
| 4927 registry.registerConstSymbol(node.slowNameString); | |
| 4928 } | |
| 4929 | |
| 4930 void visitStringJuxtaposition(ast.StringJuxtaposition node) { | |
| 4931 if (!node.isInterpolation) { | |
| 4932 // This is a simple string with no interpolations. | |
| 4933 stack.add(graph.addConstantString(node.dartString, compiler)); | |
| 4934 return; | |
| 4935 } | |
| 4936 StringBuilderVisitor stringBuilder = new StringBuilderVisitor(this, node); | |
| 4937 stringBuilder.visit(node); | |
| 4938 stack.add(stringBuilder.result); | |
| 4939 } | |
| 4940 | |
| 4941 void visitLiteralNull(ast.LiteralNull node) { | |
| 4942 stack.add(graph.addConstantNull(compiler)); | |
| 4943 } | |
| 4944 | |
| 4945 visitNodeList(ast.NodeList node) { | |
| 4946 for (Link<ast.Node> link = node.nodes; !link.isEmpty; link = link.tail) { | |
| 4947 if (isAborted()) { | |
| 4948 compiler.reportWarning(link.head, | |
| 4949 MessageKind.GENERIC, {'text': 'dead code'}); | |
| 4950 } else { | |
| 4951 visit(link.head); | |
| 4952 } | |
| 4953 } | |
| 4954 } | |
| 4955 | |
| 4956 void visitParenthesizedExpression(ast.ParenthesizedExpression node) { | |
| 4957 visit(node.expression); | |
| 4958 } | |
| 4959 | |
| 4960 visitOperator(ast.Operator node) { | |
| 4961 // Operators are intercepted in their surrounding Send nodes. | |
| 4962 compiler.internalError(node, | |
| 4963 'SsaBuilder.visitOperator should not be called.'); | |
| 4964 } | |
| 4965 | |
| 4966 visitCascade(ast.Cascade node) { | |
| 4967 visit(node.expression); | |
| 4968 // Remove the result and reveal the duplicated receiver on the stack. | |
| 4969 pop(); | |
| 4970 } | |
| 4971 | |
| 4972 visitCascadeReceiver(ast.CascadeReceiver node) { | |
| 4973 visit(node.expression); | |
| 4974 dup(); | |
| 4975 } | |
| 4976 | |
| 4977 void handleInTryStatement() { | |
| 4978 if (!inTryStatement) return; | |
| 4979 HBasicBlock block = close(new HExitTry()); | |
| 4980 HBasicBlock newBlock = graph.addNewBlock(); | |
| 4981 block.addSuccessor(newBlock); | |
| 4982 open(newBlock); | |
| 4983 } | |
| 4984 | |
| 4985 visitRethrow(ast.Rethrow node) { | |
| 4986 HInstruction exception = rethrowableException; | |
| 4987 if (exception == null) { | |
| 4988 exception = graph.addConstantNull(compiler); | |
| 4989 compiler.internalError(node, | |
| 4990 'rethrowableException should not be null.'); | |
| 4991 } | |
| 4992 handleInTryStatement(); | |
| 4993 closeAndGotoExit(new HThrow(exception, isRethrow: true)); | |
| 4994 } | |
| 4995 | |
| 4996 visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) { | |
| 4997 ConstructorElement targetConstructor = | |
| 4998 elements.getRedirectingTargetConstructor(node).implementation; | |
| 4999 ConstructorElement redirectingConstructor = sourceElement.implementation; | |
| 5000 List<HInstruction> inputs = <HInstruction>[]; | |
| 5001 FunctionSignature targetSignature = targetConstructor.functionSignature; | |
| 5002 FunctionSignature redirectingSignature = | |
| 5003 redirectingConstructor.functionSignature; | |
| 5004 redirectingSignature.forEachRequiredParameter((ParameterElement element) { | |
| 5005 inputs.add(localsHandler.readLocal(element)); | |
| 5006 }); | |
| 5007 List<Element> targetOptionals = | |
| 5008 targetSignature.orderedOptionalParameters; | |
| 5009 List<Element> redirectingOptionals = | |
| 5010 redirectingSignature.orderedOptionalParameters; | |
| 5011 int i = 0; | |
| 5012 for (; i < redirectingOptionals.length; i++) { | |
| 5013 ParameterElement parameter = redirectingOptionals[i]; | |
| 5014 inputs.add(localsHandler.readLocal(parameter)); | |
| 5015 } | |
| 5016 for (; i < targetOptionals.length; i++) { | |
| 5017 inputs.add(handleConstantForOptionalParameter(targetOptionals[i])); | |
| 5018 } | |
| 5019 ClassElement targetClass = targetConstructor.enclosingClass; | |
| 5020 if (backend.classNeedsRti(targetClass)) { | |
| 5021 ClassElement cls = redirectingConstructor.enclosingClass; | |
| 5022 InterfaceType targetType = | |
| 5023 redirectingConstructor.computeEffectiveTargetType(cls.thisType); | |
| 5024 targetType = localsHandler.substInContext(targetType); | |
| 5025 targetType.typeArguments.forEach((DartType argument) { | |
| 5026 inputs.add(analyzeTypeArgument(argument)); | |
| 5027 }); | |
| 5028 } | |
| 5029 pushInvokeStatic(node, targetConstructor, inputs); | |
| 5030 HInstruction value = pop(); | |
| 5031 emitReturn(value, node); | |
| 5032 } | |
| 5033 | |
| 5034 visitReturn(ast.Return node) { | |
| 5035 if (identical(node.beginToken.stringValue, 'native')) { | |
| 5036 native.handleSsaNative(this, node.expression); | |
| 5037 return; | |
| 5038 } | |
| 5039 HInstruction value; | |
| 5040 if (node.expression == null) { | |
| 5041 value = graph.addConstantNull(compiler); | |
| 5042 } else { | |
| 5043 visit(node.expression); | |
| 5044 value = pop(); | |
| 5045 value = potentiallyCheckType(value, returnType); | |
| 5046 } | |
| 5047 | |
| 5048 handleInTryStatement(); | |
| 5049 emitReturn(value, node); | |
| 5050 } | |
| 5051 | |
| 5052 visitThrow(ast.Throw node) { | |
| 5053 visitThrowExpression(node.expression); | |
| 5054 if (isReachable) { | |
| 5055 handleInTryStatement(); | |
| 5056 push(new HThrowExpression(pop())); | |
| 5057 isReachable = false; | |
| 5058 } | |
| 5059 } | |
| 5060 | |
| 5061 visitYield(ast.Yield node) { | |
| 5062 // Dummy implementation. | |
| 5063 visit(node.expression); | |
| 5064 pop(); | |
| 5065 } | |
| 5066 | |
| 5067 visitAwait(ast.Await node) { | |
| 5068 // Dummy implementation. | |
| 5069 visit(node.expression); | |
| 5070 } | |
| 5071 | |
| 5072 visitTypeAnnotation(ast.TypeAnnotation node) { | |
| 5073 compiler.internalError(node, | |
| 5074 'Visiting type annotation in SSA builder.'); | |
| 5075 } | |
| 5076 | |
| 5077 visitVariableDefinitions(ast.VariableDefinitions node) { | |
| 5078 assert(isReachable); | |
| 5079 for (Link<ast.Node> link = node.definitions.nodes; | |
| 5080 !link.isEmpty; | |
| 5081 link = link.tail) { | |
| 5082 ast.Node definition = link.head; | |
| 5083 if (definition is ast.Identifier) { | |
| 5084 HInstruction initialValue = graph.addConstantNull(compiler); | |
| 5085 LocalElement local = elements[definition]; | |
| 5086 localsHandler.updateLocal(local, initialValue); | |
| 5087 } else { | |
| 5088 assert(definition is ast.SendSet); | |
| 5089 visitSendSet(definition); | |
| 5090 pop(); // Discard value. | |
| 5091 } | |
| 5092 } | |
| 5093 } | |
| 5094 | |
| 5095 HInstruction setRtiIfNeeded(HInstruction object, ast.Node node) { | |
| 5096 InterfaceType type = localsHandler.substInContext(elements.getType(node)); | |
| 5097 if (!backend.classNeedsRti(type.element) || type.treatAsRaw) { | |
| 5098 return object; | |
| 5099 } | |
| 5100 List<HInstruction> arguments = <HInstruction>[]; | |
| 5101 for (DartType argument in type.typeArguments) { | |
| 5102 arguments.add(analyzeTypeArgument(argument)); | |
| 5103 } | |
| 5104 // TODO(15489): Register at codegen. | |
| 5105 registry.registerInstantiatedType(type); | |
| 5106 return callSetRuntimeTypeInfo(type.element, arguments, object); | |
| 5107 } | |
| 5108 | |
| 5109 visitLiteralList(ast.LiteralList node) { | |
| 5110 HInstruction instruction; | |
| 5111 | |
| 5112 if (node.isConst) { | |
| 5113 instruction = addConstant(node); | |
| 5114 } else { | |
| 5115 List<HInstruction> inputs = <HInstruction>[]; | |
| 5116 for (Link<ast.Node> link = node.elements.nodes; | |
| 5117 !link.isEmpty; | |
| 5118 link = link.tail) { | |
| 5119 visit(link.head); | |
| 5120 inputs.add(pop()); | |
| 5121 } | |
| 5122 instruction = buildLiteralList(inputs); | |
| 5123 add(instruction); | |
| 5124 instruction = setRtiIfNeeded(instruction, node); | |
| 5125 } | |
| 5126 | |
| 5127 TypeMask type = | |
| 5128 TypeMaskFactory.inferredForNode(sourceElement, node, compiler); | |
| 5129 if (!type.containsAll(compiler.world)) instruction.instructionType = type; | |
| 5130 stack.add(instruction); | |
| 5131 } | |
| 5132 | |
| 5133 visitConditional(ast.Conditional node) { | |
| 5134 SsaBranchBuilder brancher = new SsaBranchBuilder(this, node); | |
| 5135 brancher.handleConditional(() => visit(node.condition), | |
| 5136 () => visit(node.thenExpression), | |
| 5137 () => visit(node.elseExpression)); | |
| 5138 } | |
| 5139 | |
| 5140 visitStringInterpolation(ast.StringInterpolation node) { | |
| 5141 StringBuilderVisitor stringBuilder = new StringBuilderVisitor(this, node); | |
| 5142 stringBuilder.visit(node); | |
| 5143 stack.add(stringBuilder.result); | |
| 5144 } | |
| 5145 | |
| 5146 visitStringInterpolationPart(ast.StringInterpolationPart node) { | |
| 5147 // The parts are iterated in visitStringInterpolation. | |
| 5148 compiler.internalError(node, | |
| 5149 'SsaBuilder.visitStringInterpolation should not be called.'); | |
| 5150 } | |
| 5151 | |
| 5152 visitEmptyStatement(ast.EmptyStatement node) { | |
| 5153 // Do nothing, empty statement. | |
| 5154 } | |
| 5155 | |
| 5156 visitModifiers(ast.Modifiers node) { | |
| 5157 compiler.unimplemented(node, 'SsaFromAstMixin.visitModifiers.'); | |
| 5158 } | |
| 5159 | |
| 5160 visitBreakStatement(ast.BreakStatement node) { | |
| 5161 assert(!isAborted()); | |
| 5162 handleInTryStatement(); | |
| 5163 JumpTarget target = elements.getTargetOf(node); | |
| 5164 assert(target != null); | |
| 5165 JumpHandler handler = jumpTargets[target]; | |
| 5166 assert(handler != null); | |
| 5167 if (node.target == null) { | |
| 5168 handler.generateBreak(); | |
| 5169 } else { | |
| 5170 LabelDefinition label = elements.getTargetLabel(node); | |
| 5171 handler.generateBreak(label); | |
| 5172 } | |
| 5173 } | |
| 5174 | |
| 5175 visitContinueStatement(ast.ContinueStatement node) { | |
| 5176 handleInTryStatement(); | |
| 5177 JumpTarget target = elements.getTargetOf(node); | |
| 5178 assert(target != null); | |
| 5179 JumpHandler handler = jumpTargets[target]; | |
| 5180 assert(handler != null); | |
| 5181 if (node.target == null) { | |
| 5182 handler.generateContinue(); | |
| 5183 } else { | |
| 5184 LabelDefinition label = elements.getTargetLabel(node); | |
| 5185 assert(label != null); | |
| 5186 handler.generateContinue(label); | |
| 5187 } | |
| 5188 } | |
| 5189 | |
| 5190 /** | |
| 5191 * Creates a [JumpHandler] for a statement. The node must be a jump | |
| 5192 * target. If there are no breaks or continues targeting the statement, | |
| 5193 * a special "null handler" is returned. | |
| 5194 * | |
| 5195 * [isLoopJump] is [:true:] when the jump handler is for a loop. This is used | |
| 5196 * to distinguish the synthetized loop created for a switch statement with | |
| 5197 * continue statements from simple switch statements. | |
| 5198 */ | |
| 5199 JumpHandler createJumpHandler(ast.Statement node, {bool isLoopJump}) { | |
| 5200 JumpTarget element = elements.getTargetDefinition(node); | |
| 5201 if (element == null || !identical(element.statement, node)) { | |
| 5202 // No breaks or continues to this node. | |
| 5203 return new NullJumpHandler(compiler); | |
| 5204 } | |
| 5205 if (isLoopJump && node is ast.SwitchStatement) { | |
| 5206 // Create a special jump handler for loops created for switch statements | |
| 5207 // with continue statements. | |
| 5208 return new SwitchCaseJumpHandler(this, element, node); | |
| 5209 } | |
| 5210 return new JumpHandler(this, element); | |
| 5211 } | |
| 5212 | |
| 5213 visitForIn(ast.ForIn node) { | |
| 5214 // Generate a structure equivalent to: | |
| 5215 // Iterator<E> $iter = <iterable>.iterator; | |
| 5216 // while ($iter.moveNext()) { | |
| 5217 // E <declaredIdentifier> = $iter.current; | |
| 5218 // <body> | |
| 5219 // } | |
| 5220 | |
| 5221 // The iterator is shared between initializer, condition and body. | |
| 5222 HInstruction iterator; | |
| 5223 void buildInitializer() { | |
| 5224 Selector selector = elements.getIteratorSelector(node); | |
| 5225 visit(node.expression); | |
| 5226 HInstruction receiver = pop(); | |
| 5227 pushInvokeDynamic(node, selector, [receiver]); | |
| 5228 iterator = pop(); | |
| 5229 } | |
| 5230 HInstruction buildCondition() { | |
| 5231 Selector selector = elements.getMoveNextSelector(node); | |
| 5232 pushInvokeDynamic(node, selector, [iterator]); | |
| 5233 return popBoolified(); | |
| 5234 } | |
| 5235 void buildBody() { | |
| 5236 Selector call = elements.getCurrentSelector(node); | |
| 5237 pushInvokeDynamic(node, call, [iterator]); | |
| 5238 | |
| 5239 ast.Node identifier = node.declaredIdentifier; | |
| 5240 Element variable = elements.getForInVariable(node); | |
| 5241 Selector selector = elements.getSelector(identifier); | |
| 5242 | |
| 5243 HInstruction value = pop(); | |
| 5244 if (identifier.asSend() != null | |
| 5245 && Elements.isInstanceSend(identifier, elements)) { | |
| 5246 HInstruction receiver = generateInstanceSendReceiver(identifier); | |
| 5247 assert(receiver != null); | |
| 5248 generateInstanceSetterWithCompiledReceiver( | |
| 5249 null, | |
| 5250 receiver, | |
| 5251 value, | |
| 5252 selector: selector, | |
| 5253 location: identifier); | |
| 5254 } else { | |
| 5255 generateNonInstanceSetter(null, variable, value, location: identifier); | |
| 5256 } | |
| 5257 pop(); // Pop the value pushed by the setter call. | |
| 5258 | |
| 5259 visit(node.body); | |
| 5260 } | |
| 5261 handleLoop(node, buildInitializer, buildCondition, () {}, buildBody); | |
| 5262 } | |
| 5263 | |
| 5264 visitLabel(ast.Label node) { | |
| 5265 compiler.internalError(node, 'SsaFromAstMixin.visitLabel.'); | |
| 5266 } | |
| 5267 | |
| 5268 visitLabeledStatement(ast.LabeledStatement node) { | |
| 5269 ast.Statement body = node.statement; | |
| 5270 if (body is ast.Loop | |
| 5271 || body is ast.SwitchStatement | |
| 5272 || Elements.isUnusedLabel(node, elements)) { | |
| 5273 // Loops and switches handle their own labels. | |
| 5274 visit(body); | |
| 5275 return; | |
| 5276 } | |
| 5277 JumpTarget targetElement = elements.getTargetDefinition(body); | |
| 5278 LocalsHandler beforeLocals = new LocalsHandler.from(localsHandler); | |
| 5279 assert(targetElement.isBreakTarget); | |
| 5280 JumpHandler handler = new JumpHandler(this, targetElement); | |
| 5281 // Introduce a new basic block. | |
| 5282 HBasicBlock entryBlock = openNewBlock(); | |
| 5283 visit(body); | |
| 5284 SubGraph bodyGraph = new SubGraph(entryBlock, lastOpenedBlock); | |
| 5285 | |
| 5286 HBasicBlock joinBlock = graph.addNewBlock(); | |
| 5287 List<LocalsHandler> breakHandlers = <LocalsHandler>[]; | |
| 5288 handler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) { | |
| 5289 breakInstruction.block.addSuccessor(joinBlock); | |
| 5290 breakHandlers.add(locals); | |
| 5291 }); | |
| 5292 bool hasBreak = breakHandlers.length > 0; | |
| 5293 if (!isAborted()) { | |
| 5294 goto(current, joinBlock); | |
| 5295 breakHandlers.add(localsHandler); | |
| 5296 } | |
| 5297 open(joinBlock); | |
| 5298 localsHandler = beforeLocals.mergeMultiple(breakHandlers, joinBlock); | |
| 5299 | |
| 5300 if (hasBreak) { | |
| 5301 // There was at least one reachable break, so the label is needed. | |
| 5302 entryBlock.setBlockFlow( | |
| 5303 new HLabeledBlockInformation(new HSubGraphBlockInformation(bodyGraph), | |
| 5304 handler.labels()), | |
| 5305 joinBlock); | |
| 5306 } | |
| 5307 handler.close(); | |
| 5308 } | |
| 5309 | |
| 5310 visitLiteralMap(ast.LiteralMap node) { | |
| 5311 if (node.isConst) { | |
| 5312 stack.add(addConstant(node)); | |
| 5313 return; | |
| 5314 } | |
| 5315 List<HInstruction> listInputs = <HInstruction>[]; | |
| 5316 for (Link<ast.Node> link = node.entries.nodes; | |
| 5317 !link.isEmpty; | |
| 5318 link = link.tail) { | |
| 5319 visit(link.head); | |
| 5320 listInputs.add(pop()); | |
| 5321 listInputs.add(pop()); | |
| 5322 } | |
| 5323 | |
| 5324 ConstructorElement constructor; | |
| 5325 List<HInstruction> inputs = <HInstruction>[]; | |
| 5326 | |
| 5327 if (listInputs.isEmpty) { | |
| 5328 constructor = backend.mapLiteralConstructorEmpty; | |
| 5329 } else { | |
| 5330 constructor = backend.mapLiteralConstructor; | |
| 5331 HLiteralList keyValuePairs = buildLiteralList(listInputs); | |
| 5332 add(keyValuePairs); | |
| 5333 inputs.add(keyValuePairs); | |
| 5334 } | |
| 5335 | |
| 5336 assert(constructor.isFactoryConstructor); | |
| 5337 | |
| 5338 ConstructorElement functionElement = constructor; | |
| 5339 constructor = functionElement.effectiveTarget; | |
| 5340 | |
| 5341 InterfaceType type = elements.getType(node); | |
| 5342 InterfaceType expectedType = | |
| 5343 functionElement.computeEffectiveTargetType(type); | |
| 5344 expectedType = localsHandler.substInContext(expectedType); | |
| 5345 | |
| 5346 if (constructor.isFactoryConstructor) { | |
| 5347 registry.registerFactoryWithTypeArguments(); | |
| 5348 } | |
| 5349 | |
| 5350 ClassElement cls = constructor.enclosingClass; | |
| 5351 | |
| 5352 if (backend.classNeedsRti(cls)) { | |
| 5353 List<DartType> typeVariable = cls.typeVariables; | |
| 5354 expectedType.typeArguments.forEach((DartType argument) { | |
| 5355 inputs.add(analyzeTypeArgument(argument)); | |
| 5356 }); | |
| 5357 } | |
| 5358 | |
| 5359 // The instruction type will always be a subtype of the mapLiteralClass, but | |
| 5360 // type inference might discover a more specific type, or find nothing (in | |
| 5361 // dart2js unit tests). | |
| 5362 TypeMask mapType = | |
| 5363 new TypeMask.nonNullSubtype(backend.mapLiteralClass, compiler.world); | |
| 5364 TypeMask returnTypeMask = TypeMaskFactory.inferredReturnTypeForElement( | |
| 5365 constructor, compiler); | |
| 5366 TypeMask instructionType = | |
| 5367 mapType.intersection(returnTypeMask, compiler.world); | |
| 5368 | |
| 5369 addInlinedInstantiation(expectedType); | |
| 5370 pushInvokeStatic(node, constructor, inputs, instructionType); | |
| 5371 removeInlinedInstantiation(expectedType); | |
| 5372 } | |
| 5373 | |
| 5374 visitLiteralMapEntry(ast.LiteralMapEntry node) { | |
| 5375 visit(node.value); | |
| 5376 visit(node.key); | |
| 5377 } | |
| 5378 | |
| 5379 visitNamedArgument(ast.NamedArgument node) { | |
| 5380 visit(node.expression); | |
| 5381 } | |
| 5382 | |
| 5383 Map<ast.CaseMatch, ConstantValue> buildSwitchCaseConstants( | |
| 5384 ast.SwitchStatement node) { | |
| 5385 | |
| 5386 Map<ast.CaseMatch, ConstantValue> constants = | |
| 5387 new Map<ast.CaseMatch, ConstantValue>(); | |
| 5388 for (ast.SwitchCase switchCase in node.cases) { | |
| 5389 for (ast.Node labelOrCase in switchCase.labelsAndCases) { | |
| 5390 if (labelOrCase is ast.CaseMatch) { | |
| 5391 ast.CaseMatch match = labelOrCase; | |
| 5392 ConstantValue constant = getConstantForNode(match.expression); | |
| 5393 constants[labelOrCase] = constant; | |
| 5394 } | |
| 5395 } | |
| 5396 } | |
| 5397 return constants; | |
| 5398 } | |
| 5399 | |
| 5400 visitSwitchStatement(ast.SwitchStatement node) { | |
| 5401 Map<ast.CaseMatch, ConstantValue> constants = | |
| 5402 buildSwitchCaseConstants(node); | |
| 5403 | |
| 5404 // The switch case indices must match those computed in | |
| 5405 // [SwitchCaseJumpHandler]. | |
| 5406 bool hasContinue = false; | |
| 5407 Map<ast.SwitchCase, int> caseIndex = new Map<ast.SwitchCase, int>(); | |
| 5408 int switchIndex = 1; | |
| 5409 bool hasDefault = false; | |
| 5410 for (ast.SwitchCase switchCase in node.cases) { | |
| 5411 for (ast.Node labelOrCase in switchCase.labelsAndCases) { | |
| 5412 ast.Node label = labelOrCase.asLabel(); | |
| 5413 if (label != null) { | |
| 5414 LabelDefinition labelElement = elements.getLabelDefinition(label); | |
| 5415 if (labelElement != null && labelElement.isContinueTarget) { | |
| 5416 hasContinue = true; | |
| 5417 } | |
| 5418 } | |
| 5419 } | |
| 5420 if (switchCase.isDefaultCase) { | |
| 5421 hasDefault = true; | |
| 5422 } | |
| 5423 caseIndex[switchCase] = switchIndex; | |
| 5424 switchIndex++; | |
| 5425 } | |
| 5426 if (!hasContinue) { | |
| 5427 // If the switch statement has no switch cases targeted by continue | |
| 5428 // statements we encode the switch statement directly. | |
| 5429 buildSimpleSwitchStatement(node, constants); | |
| 5430 } else { | |
| 5431 buildComplexSwitchStatement(node, constants, caseIndex, hasDefault); | |
| 5432 } | |
| 5433 } | |
| 5434 | |
| 5435 /** | |
| 5436 * Builds a simple switch statement which does not handle uses of continue | |
| 5437 * statements to labeled switch cases. | |
| 5438 */ | |
| 5439 void buildSimpleSwitchStatement(ast.SwitchStatement node, | |
| 5440 Map<ast.CaseMatch, ConstantValue> constants) { | |
| 5441 JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: false); | |
| 5442 HInstruction buildExpression() { | |
| 5443 visit(node.expression); | |
| 5444 return pop(); | |
| 5445 } | |
| 5446 Iterable<ConstantValue> getConstants(ast.SwitchCase switchCase) { | |
| 5447 List<ConstantValue> constantList = <ConstantValue>[]; | |
| 5448 for (ast.Node labelOrCase in switchCase.labelsAndCases) { | |
| 5449 if (labelOrCase is ast.CaseMatch) { | |
| 5450 constantList.add(constants[labelOrCase]); | |
| 5451 } | |
| 5452 } | |
| 5453 return constantList; | |
| 5454 } | |
| 5455 bool isDefaultCase(ast.SwitchCase switchCase) { | |
| 5456 return switchCase.isDefaultCase; | |
| 5457 } | |
| 5458 void buildSwitchCase(ast.SwitchCase node) { | |
| 5459 visit(node.statements); | |
| 5460 } | |
| 5461 handleSwitch(node, | |
| 5462 jumpHandler, | |
| 5463 buildExpression, | |
| 5464 node.cases, | |
| 5465 getConstants, | |
| 5466 isDefaultCase, | |
| 5467 buildSwitchCase); | |
| 5468 jumpHandler.close(); | |
| 5469 } | |
| 5470 | |
| 5471 /** | |
| 5472 * Builds a switch statement that can handle arbitrary uses of continue | |
| 5473 * statements to labeled switch cases. | |
| 5474 */ | |
| 5475 void buildComplexSwitchStatement(ast.SwitchStatement node, | |
| 5476 Map<ast.CaseMatch, ConstantValue> constants, | |
| 5477 Map<ast.SwitchCase, int> caseIndex, | |
| 5478 bool hasDefault) { | |
| 5479 // If the switch statement has switch cases targeted by continue | |
| 5480 // statements we create the following encoding: | |
| 5481 // | |
| 5482 // switch (e) { | |
| 5483 // l_1: case e0: s_1; break; | |
| 5484 // l_2: case e1: s_2; continue l_i; | |
| 5485 // ... | |
| 5486 // l_n: default: s_n; continue l_j; | |
| 5487 // } | |
| 5488 // | |
| 5489 // is encoded as | |
| 5490 // | |
| 5491 // var target; | |
| 5492 // switch (e) { | |
| 5493 // case e1: target = 1; break; | |
| 5494 // case e2: target = 2; break; | |
| 5495 // ... | |
| 5496 // default: target = n; break; | |
| 5497 // } | |
| 5498 // l: while (true) { | |
| 5499 // switch (target) { | |
| 5500 // case 1: s_1; break l; | |
| 5501 // case 2: s_2; target = i; continue l; | |
| 5502 // ... | |
| 5503 // case n: s_n; target = j; continue l; | |
| 5504 // } | |
| 5505 // } | |
| 5506 | |
| 5507 JumpTarget switchTarget = elements.getTargetDefinition(node); | |
| 5508 HInstruction initialValue = graph.addConstantNull(compiler); | |
| 5509 localsHandler.updateLocal(switchTarget, initialValue); | |
| 5510 | |
| 5511 JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: false); | |
| 5512 var switchCases = node.cases; | |
| 5513 if (!hasDefault) { | |
| 5514 // Use [:null:] as the marker for a synthetic default clause. | |
| 5515 // The synthetic default is added because otherwise, there would be no | |
| 5516 // good place to give a default value to the local. | |
| 5517 switchCases = node.cases.nodes.toList()..add(null); | |
| 5518 } | |
| 5519 HInstruction buildExpression() { | |
| 5520 visit(node.expression); | |
| 5521 return pop(); | |
| 5522 } | |
| 5523 Iterable<ConstantValue> getConstants(ast.SwitchCase switchCase) { | |
| 5524 List<ConstantValue> constantList = <ConstantValue>[]; | |
| 5525 if (switchCase != null) { | |
| 5526 for (ast.Node labelOrCase in switchCase.labelsAndCases) { | |
| 5527 if (labelOrCase is ast.CaseMatch) { | |
| 5528 constantList.add(constants[labelOrCase]); | |
| 5529 } | |
| 5530 } | |
| 5531 } | |
| 5532 return constantList; | |
| 5533 } | |
| 5534 bool isDefaultCase(ast.SwitchCase switchCase) { | |
| 5535 return switchCase == null || switchCase.isDefaultCase; | |
| 5536 } | |
| 5537 void buildSwitchCase(ast.SwitchCase switchCase) { | |
| 5538 if (switchCase != null) { | |
| 5539 // Generate 'target = i; break;' for switch case i. | |
| 5540 int index = caseIndex[switchCase]; | |
| 5541 HInstruction value = graph.addConstantInt(index, compiler); | |
| 5542 localsHandler.updateLocal(switchTarget, value); | |
| 5543 } else { | |
| 5544 // Generate synthetic default case 'target = null; break;'. | |
| 5545 HInstruction value = graph.addConstantNull(compiler); | |
| 5546 localsHandler.updateLocal(switchTarget, value); | |
| 5547 } | |
| 5548 jumpTargets[switchTarget].generateBreak(); | |
| 5549 } | |
| 5550 handleSwitch(node, | |
| 5551 jumpHandler, | |
| 5552 buildExpression, | |
| 5553 switchCases, | |
| 5554 getConstants, | |
| 5555 isDefaultCase, | |
| 5556 buildSwitchCase); | |
| 5557 jumpHandler.close(); | |
| 5558 | |
| 5559 HInstruction buildCondition() => | |
| 5560 graph.addConstantBool(true, compiler); | |
| 5561 | |
| 5562 void buildSwitch() { | |
| 5563 HInstruction buildExpression() { | |
| 5564 return localsHandler.readLocal(switchTarget); | |
| 5565 } | |
| 5566 Iterable<ConstantValue> getConstants(ast.SwitchCase switchCase) { | |
| 5567 return <ConstantValue>[constantSystem.createInt(caseIndex[switchCase])]; | |
| 5568 } | |
| 5569 void buildSwitchCase(ast.SwitchCase switchCase) { | |
| 5570 visit(switchCase.statements); | |
| 5571 if (!isAborted()) { | |
| 5572 // Ensure that we break the loop if the case falls through. (This | |
| 5573 // is only possible for the last case.) | |
| 5574 jumpTargets[switchTarget].generateBreak(); | |
| 5575 } | |
| 5576 } | |
| 5577 // Pass a [NullJumpHandler] because the target for the contained break | |
| 5578 // is not the generated switch statement but instead the loop generated | |
| 5579 // in the call to [handleLoop] below. | |
| 5580 handleSwitch(node, | |
| 5581 new NullJumpHandler(compiler), | |
| 5582 buildExpression, node.cases, getConstants, | |
| 5583 (_) => false, // No case is default. | |
| 5584 buildSwitchCase); | |
| 5585 } | |
| 5586 | |
| 5587 void buildLoop() { | |
| 5588 handleLoop(node, | |
| 5589 () {}, | |
| 5590 buildCondition, | |
| 5591 () {}, | |
| 5592 buildSwitch); | |
| 5593 } | |
| 5594 | |
| 5595 if (hasDefault) { | |
| 5596 buildLoop(); | |
| 5597 } else { | |
| 5598 // If the switch statement has no default case, surround the loop with | |
| 5599 // a test of the target. | |
| 5600 void buildCondition() { | |
| 5601 js.Template code = js.js.parseForeignJS('#'); | |
| 5602 push(createForeign(code, | |
| 5603 backend.boolType, | |
| 5604 [localsHandler.readLocal(switchTarget)])); | |
| 5605 } | |
| 5606 handleIf(node, buildCondition, buildLoop, () => {}); | |
| 5607 } | |
| 5608 } | |
| 5609 | |
| 5610 /** | |
| 5611 * Creates a switch statement. | |
| 5612 * | |
| 5613 * [jumpHandler] is the [JumpHandler] for the created switch statement. | |
| 5614 * [buildExpression] creates the switch expression. | |
| 5615 * [switchCases] must be either an [Iterable] of [ast.SwitchCase] nodes or | |
| 5616 * a [Link] or a [ast.NodeList] of [ast.SwitchCase] nodes. | |
| 5617 * [getConstants] returns the set of constants for a switch case. | |
| 5618 * [isDefaultCase] returns [:true:] if the provided switch case should be | |
| 5619 * considered default for the created switch statement. | |
| 5620 * [buildSwitchCase] creates the statements for the switch case. | |
| 5621 */ | |
| 5622 void handleSwitch( | |
| 5623 ast.Node errorNode, | |
| 5624 JumpHandler jumpHandler, | |
| 5625 HInstruction buildExpression(), | |
| 5626 var switchCases, | |
| 5627 Iterable<ConstantValue> getConstants(ast.SwitchCase switchCase), | |
| 5628 bool isDefaultCase(ast.SwitchCase switchCase), | |
| 5629 void buildSwitchCase(ast.SwitchCase switchCase)) { | |
| 5630 | |
| 5631 Map<ast.CaseMatch, ConstantValue> constants = | |
| 5632 new Map<ast.CaseMatch, ConstantValue>(); | |
| 5633 | |
| 5634 HBasicBlock expressionStart = openNewBlock(); | |
| 5635 HInstruction expression = buildExpression(); | |
| 5636 if (switchCases.isEmpty) { | |
| 5637 return; | |
| 5638 } | |
| 5639 | |
| 5640 HSwitch switchInstruction = new HSwitch(<HInstruction>[expression]); | |
| 5641 HBasicBlock expressionEnd = close(switchInstruction); | |
| 5642 LocalsHandler savedLocals = localsHandler; | |
| 5643 | |
| 5644 List<HStatementInformation> statements = <HStatementInformation>[]; | |
| 5645 bool hasDefault = false; | |
| 5646 Element getFallThroughErrorElement = backend.getFallThroughError(); | |
| 5647 HasNextIterator<ast.Node> caseIterator = | |
| 5648 new HasNextIterator<ast.Node>(switchCases.iterator); | |
| 5649 while (caseIterator.hasNext) { | |
| 5650 ast.SwitchCase switchCase = caseIterator.next(); | |
| 5651 HBasicBlock block = graph.addNewBlock(); | |
| 5652 for (ConstantValue constant in getConstants(switchCase)) { | |
| 5653 HConstant hConstant = graph.addConstant(constant, compiler); | |
| 5654 switchInstruction.inputs.add(hConstant); | |
| 5655 hConstant.usedBy.add(switchInstruction); | |
| 5656 expressionEnd.addSuccessor(block); | |
| 5657 } | |
| 5658 | |
| 5659 if (isDefaultCase(switchCase)) { | |
| 5660 // An HSwitch has n inputs and n+1 successors, the last being the | |
| 5661 // default case. | |
| 5662 expressionEnd.addSuccessor(block); | |
| 5663 hasDefault = true; | |
| 5664 } | |
| 5665 open(block); | |
| 5666 localsHandler = new LocalsHandler.from(savedLocals); | |
| 5667 buildSwitchCase(switchCase); | |
| 5668 if (!isAborted()) { | |
| 5669 if (caseIterator.hasNext) { | |
| 5670 pushInvokeStatic(switchCase, getFallThroughErrorElement, []); | |
| 5671 HInstruction error = pop(); | |
| 5672 closeAndGotoExit(new HThrow(error)); | |
| 5673 } else if (!isDefaultCase(switchCase)) { | |
| 5674 // If there is no default, we will add one later to avoid | |
| 5675 // the critical edge. So we generate a break statement to make | |
| 5676 // sure the last case does not fall through to the default case. | |
| 5677 jumpHandler.generateBreak(); | |
| 5678 } | |
| 5679 } | |
| 5680 statements.add( | |
| 5681 new HSubGraphBlockInformation(new SubGraph(block, lastOpenedBlock))); | |
| 5682 } | |
| 5683 | |
| 5684 // Add a join-block if necessary. | |
| 5685 // We create [joinBlock] early, and then go through the cases that might | |
| 5686 // want to jump to it. In each case, if we add [joinBlock] as a successor | |
| 5687 // of another block, we also add an element to [caseHandlers] that is used | |
| 5688 // to create the phis in [joinBlock]. | |
| 5689 // If we never jump to the join block, [caseHandlers] will stay empty, and | |
| 5690 // the join block is never added to the graph. | |
| 5691 HBasicBlock joinBlock = new HBasicBlock(); | |
| 5692 List<LocalsHandler> caseHandlers = <LocalsHandler>[]; | |
| 5693 jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) { | |
| 5694 instruction.block.addSuccessor(joinBlock); | |
| 5695 caseHandlers.add(locals); | |
| 5696 }); | |
| 5697 jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) { | |
| 5698 assert(invariant(errorNode, false, | |
| 5699 message: 'Continue cannot target a switch.')); | |
| 5700 }); | |
| 5701 if (!isAborted()) { | |
| 5702 current.close(new HGoto()); | |
| 5703 lastOpenedBlock.addSuccessor(joinBlock); | |
| 5704 caseHandlers.add(localsHandler); | |
| 5705 } | |
| 5706 if (!hasDefault) { | |
| 5707 // Always create a default case, to avoid a critical edge in the | |
| 5708 // graph. | |
| 5709 HBasicBlock defaultCase = addNewBlock(); | |
| 5710 expressionEnd.addSuccessor(defaultCase); | |
| 5711 open(defaultCase); | |
| 5712 close(new HGoto()); | |
| 5713 defaultCase.addSuccessor(joinBlock); | |
| 5714 caseHandlers.add(savedLocals); | |
| 5715 statements.add(new HSubGraphBlockInformation(new SubGraph( | |
| 5716 defaultCase, defaultCase))); | |
| 5717 } | |
| 5718 assert(caseHandlers.length == joinBlock.predecessors.length); | |
| 5719 if (caseHandlers.length != 0) { | |
| 5720 graph.addBlock(joinBlock); | |
| 5721 open(joinBlock); | |
| 5722 if (caseHandlers.length == 1) { | |
| 5723 localsHandler = caseHandlers[0]; | |
| 5724 } else { | |
| 5725 localsHandler = savedLocals.mergeMultiple(caseHandlers, joinBlock); | |
| 5726 } | |
| 5727 } else { | |
| 5728 // The joinblock is not used. | |
| 5729 joinBlock = null; | |
| 5730 } | |
| 5731 | |
| 5732 HSubExpressionBlockInformation expressionInfo = | |
| 5733 new HSubExpressionBlockInformation(new SubExpression(expressionStart, | |
| 5734 expressionEnd)); | |
| 5735 expressionStart.setBlockFlow( | |
| 5736 new HSwitchBlockInformation(expressionInfo, | |
| 5737 statements, | |
| 5738 jumpHandler.target, | |
| 5739 jumpHandler.labels()), | |
| 5740 joinBlock); | |
| 5741 | |
| 5742 jumpHandler.close(); | |
| 5743 } | |
| 5744 | |
| 5745 visitSwitchCase(ast.SwitchCase node) { | |
| 5746 compiler.internalError(node, 'SsaFromAstMixin.visitSwitchCase.'); | |
| 5747 } | |
| 5748 | |
| 5749 visitCaseMatch(ast.CaseMatch node) { | |
| 5750 compiler.internalError(node, 'SsaFromAstMixin.visitCaseMatch.'); | |
| 5751 } | |
| 5752 | |
| 5753 visitTryStatement(ast.TryStatement node) { | |
| 5754 // Save the current locals. The catch block and the finally block | |
| 5755 // must not reuse the existing locals handler. None of the variables | |
| 5756 // that have been defined in the body-block will be used, but for | |
| 5757 // loops we will add (unnecessary) phis that will reference the body | |
| 5758 // variables. This makes it look as if the variables were used | |
| 5759 // in a non-dominated block. | |
| 5760 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); | |
| 5761 HBasicBlock enterBlock = openNewBlock(); | |
| 5762 HTry tryInstruction = new HTry(); | |
| 5763 close(tryInstruction); | |
| 5764 bool oldInTryStatement = inTryStatement; | |
| 5765 inTryStatement = true; | |
| 5766 | |
| 5767 HBasicBlock startTryBlock; | |
| 5768 HBasicBlock endTryBlock; | |
| 5769 HBasicBlock startCatchBlock; | |
| 5770 HBasicBlock endCatchBlock; | |
| 5771 HBasicBlock startFinallyBlock; | |
| 5772 HBasicBlock endFinallyBlock; | |
| 5773 | |
| 5774 startTryBlock = graph.addNewBlock(); | |
| 5775 open(startTryBlock); | |
| 5776 visit(node.tryBlock); | |
| 5777 // We use a [HExitTry] instead of a [HGoto] for the try block | |
| 5778 // because it will have multiple successors: the join block, and | |
| 5779 // the catch or finally block. | |
| 5780 if (!isAborted()) endTryBlock = close(new HExitTry()); | |
| 5781 SubGraph bodyGraph = new SubGraph(startTryBlock, lastOpenedBlock); | |
| 5782 SubGraph catchGraph = null; | |
| 5783 HLocalValue exception = null; | |
| 5784 | |
| 5785 if (!node.catchBlocks.isEmpty) { | |
| 5786 localsHandler = new LocalsHandler.from(savedLocals); | |
| 5787 startCatchBlock = graph.addNewBlock(); | |
| 5788 open(startCatchBlock); | |
| 5789 // Note that the name of this local is irrelevant. | |
| 5790 SyntheticLocal local = | |
| 5791 new SyntheticLocal('exception', localsHandler.executableContext); | |
| 5792 exception = new HLocalValue(local, backend.nonNullType); | |
| 5793 add(exception); | |
| 5794 HInstruction oldRethrowableException = rethrowableException; | |
| 5795 rethrowableException = exception; | |
| 5796 | |
| 5797 pushInvokeStatic(node, backend.getExceptionUnwrapper(), [exception]); | |
| 5798 HInvokeStatic unwrappedException = pop(); | |
| 5799 tryInstruction.exception = exception; | |
| 5800 Link<ast.Node> link = node.catchBlocks.nodes; | |
| 5801 | |
| 5802 void pushCondition(ast.CatchBlock catchBlock) { | |
| 5803 if (catchBlock.onKeyword != null) { | |
| 5804 DartType type = elements.getType(catchBlock.type); | |
| 5805 if (type == null) { | |
| 5806 compiler.internalError(catchBlock.type, 'On with no type.'); | |
| 5807 } | |
| 5808 HInstruction condition = | |
| 5809 buildIsNode(catchBlock.type, type, unwrappedException); | |
| 5810 push(condition); | |
| 5811 } else { | |
| 5812 ast.VariableDefinitions declaration = catchBlock.formals.nodes.head; | |
| 5813 HInstruction condition = null; | |
| 5814 if (declaration.type == null) { | |
| 5815 condition = graph.addConstantBool(true, compiler); | |
| 5816 stack.add(condition); | |
| 5817 } else { | |
| 5818 // TODO(aprelev@gmail.com): Once old catch syntax is removed | |
| 5819 // "if" condition above and this "else" branch should be deleted as | |
| 5820 // type of declared variable won't matter for the catch | |
| 5821 // condition. | |
| 5822 DartType type = elements.getType(declaration.type); | |
| 5823 if (type == null) { | |
| 5824 compiler.internalError(catchBlock, 'Catch with unresolved type.'); | |
| 5825 } | |
| 5826 condition = buildIsNode(declaration.type, type, unwrappedException); | |
| 5827 push(condition); | |
| 5828 } | |
| 5829 } | |
| 5830 } | |
| 5831 | |
| 5832 void visitThen() { | |
| 5833 ast.CatchBlock catchBlock = link.head; | |
| 5834 link = link.tail; | |
| 5835 if (catchBlock.exception != null) { | |
| 5836 LocalVariableElement exceptionVariable = | |
| 5837 elements[catchBlock.exception]; | |
| 5838 localsHandler.updateLocal(exceptionVariable, | |
| 5839 unwrappedException); | |
| 5840 } | |
| 5841 ast.Node trace = catchBlock.trace; | |
| 5842 if (trace != null) { | |
| 5843 pushInvokeStatic(trace, backend.getTraceFromException(), [exception]); | |
| 5844 HInstruction traceInstruction = pop(); | |
| 5845 LocalVariableElement traceVariable = elements[trace]; | |
| 5846 localsHandler.updateLocal(traceVariable, traceInstruction); | |
| 5847 } | |
| 5848 visit(catchBlock); | |
| 5849 } | |
| 5850 | |
| 5851 void visitElse() { | |
| 5852 if (link.isEmpty) { | |
| 5853 closeAndGotoExit(new HThrow(exception, isRethrow: true)); | |
| 5854 } else { | |
| 5855 ast.CatchBlock newBlock = link.head; | |
| 5856 handleIf(node, | |
| 5857 () { pushCondition(newBlock); }, | |
| 5858 visitThen, visitElse); | |
| 5859 } | |
| 5860 } | |
| 5861 | |
| 5862 ast.CatchBlock firstBlock = link.head; | |
| 5863 handleIf(node, () { pushCondition(firstBlock); }, visitThen, visitElse); | |
| 5864 if (!isAborted()) endCatchBlock = close(new HGoto()); | |
| 5865 | |
| 5866 rethrowableException = oldRethrowableException; | |
| 5867 tryInstruction.catchBlock = startCatchBlock; | |
| 5868 catchGraph = new SubGraph(startCatchBlock, lastOpenedBlock); | |
| 5869 } | |
| 5870 | |
| 5871 SubGraph finallyGraph = null; | |
| 5872 if (node.finallyBlock != null) { | |
| 5873 localsHandler = new LocalsHandler.from(savedLocals); | |
| 5874 startFinallyBlock = graph.addNewBlock(); | |
| 5875 open(startFinallyBlock); | |
| 5876 visit(node.finallyBlock); | |
| 5877 if (!isAborted()) endFinallyBlock = close(new HGoto()); | |
| 5878 tryInstruction.finallyBlock = startFinallyBlock; | |
| 5879 finallyGraph = new SubGraph(startFinallyBlock, lastOpenedBlock); | |
| 5880 } | |
| 5881 | |
| 5882 HBasicBlock exitBlock = graph.addNewBlock(); | |
| 5883 | |
| 5884 addOptionalSuccessor(b1, b2) { if (b2 != null) b1.addSuccessor(b2); } | |
| 5885 addExitTrySuccessor(successor) { | |
| 5886 if (successor == null) return; | |
| 5887 // Iterate over all blocks created inside this try/catch, and | |
| 5888 // attach successor information to blocks that end with | |
| 5889 // [HExitTry]. | |
| 5890 for (int i = startTryBlock.id; i < successor.id; i++) { | |
| 5891 HBasicBlock block = graph.blocks[i]; | |
| 5892 var last = block.last; | |
| 5893 if (last is HExitTry) { | |
| 5894 block.addSuccessor(successor); | |
| 5895 } | |
| 5896 } | |
| 5897 } | |
| 5898 | |
| 5899 // Setup all successors. The entry block that contains the [HTry] | |
| 5900 // has 1) the body, 2) the catch, 3) the finally, and 4) the exit | |
| 5901 // blocks as successors. | |
| 5902 enterBlock.addSuccessor(startTryBlock); | |
| 5903 addOptionalSuccessor(enterBlock, startCatchBlock); | |
| 5904 addOptionalSuccessor(enterBlock, startFinallyBlock); | |
| 5905 enterBlock.addSuccessor(exitBlock); | |
| 5906 | |
| 5907 // The body has either the catch or the finally block as successor. | |
| 5908 if (endTryBlock != null) { | |
| 5909 assert(startCatchBlock != null || startFinallyBlock != null); | |
| 5910 endTryBlock.addSuccessor( | |
| 5911 startCatchBlock != null ? startCatchBlock : startFinallyBlock); | |
| 5912 endTryBlock.addSuccessor(exitBlock); | |
| 5913 } | |
| 5914 | |
| 5915 // The catch block has either the finally or the exit block as | |
| 5916 // successor. | |
| 5917 if (endCatchBlock != null) { | |
| 5918 endCatchBlock.addSuccessor( | |
| 5919 startFinallyBlock != null ? startFinallyBlock : exitBlock); | |
| 5920 } | |
| 5921 | |
| 5922 // The finally block has the exit block as successor. | |
| 5923 if (endFinallyBlock != null) { | |
| 5924 endFinallyBlock.addSuccessor(exitBlock); | |
| 5925 } | |
| 5926 | |
| 5927 // If a block inside try/catch aborts (eg with a return statement), | |
| 5928 // we explicitely mark this block a predecessor of the catch | |
| 5929 // block and the finally block. | |
| 5930 addExitTrySuccessor(startCatchBlock); | |
| 5931 addExitTrySuccessor(startFinallyBlock); | |
| 5932 | |
| 5933 // Use the locals handler not altered by the catch and finally | |
| 5934 // blocks. | |
| 5935 localsHandler = savedLocals; | |
| 5936 open(exitBlock); | |
| 5937 enterBlock.setBlockFlow( | |
| 5938 new HTryBlockInformation( | |
| 5939 wrapStatementGraph(bodyGraph), | |
| 5940 exception, | |
| 5941 wrapStatementGraph(catchGraph), | |
| 5942 wrapStatementGraph(finallyGraph)), | |
| 5943 exitBlock); | |
| 5944 inTryStatement = oldInTryStatement; | |
| 5945 } | |
| 5946 | |
| 5947 visitCatchBlock(ast.CatchBlock node) { | |
| 5948 visit(node.block); | |
| 5949 } | |
| 5950 | |
| 5951 visitTypedef(ast.Typedef node) { | |
| 5952 compiler.unimplemented(node, 'SsaFromAstMixin.visitTypedef.'); | |
| 5953 } | |
| 5954 | |
| 5955 visitTypeVariable(ast.TypeVariable node) { | |
| 5956 compiler.internalError(node, 'SsaFromAstMixin.visitTypeVariable.'); | |
| 5957 } | |
| 5958 | |
| 5959 /** | |
| 5960 * This method is invoked before inlining the body of [function] into this | |
| 5961 * [SsaBuilder]. | |
| 5962 */ | |
| 5963 void enterInlinedMethod(FunctionElement function, | |
| 5964 ast.Node _, | |
| 5965 List<HInstruction> compiledArguments) { | |
| 5966 TypesInferrer inferrer = compiler.typesTask.typesInferrer; | |
| 5967 AstInliningState state = new AstInliningState( | |
| 5968 function, returnLocal, returnType, elements, stack, localsHandler, | |
| 5969 inTryStatement, | |
| 5970 allInlinedFunctionsCalledOnce && inferrer.isCalledOnce(function)); | |
| 5971 inliningStack.add(state); | |
| 5972 | |
| 5973 // Setting up the state of the (AST) builder is performed even when the | |
| 5974 // inlined function is in IR, because the irInliner uses the [returnElement] | |
| 5975 // of the AST builder. | |
| 5976 setupStateForInlining(function, compiledArguments); | |
| 5977 } | |
| 5978 | |
| 5979 void leaveInlinedMethod() { | |
| 5980 HInstruction result = localsHandler.readLocal(returnLocal); | |
| 5981 AstInliningState state = inliningStack.removeLast(); | |
| 5982 restoreState(state); | |
| 5983 stack.add(result); | |
| 5984 } | |
| 5985 | |
| 5986 void doInline(FunctionElement function) { | |
| 5987 visitInlinedFunction(function); | |
| 5988 } | |
| 5989 | |
| 5990 void emitReturn(HInstruction value, ast.Node node) { | |
| 5991 if (inliningStack.isEmpty) { | |
| 5992 closeAndGotoExit(attachPosition(new HReturn(value), node)); | |
| 5993 } else { | |
| 5994 localsHandler.updateLocal(returnLocal, value); | |
| 5995 } | |
| 5996 } | |
| 5997 } | |
| 5998 | |
| 5999 /** | |
| 6000 * Visitor that handles generation of string literals (LiteralString, | |
| 6001 * StringInterpolation), and otherwise delegates to the given visitor for | |
| 6002 * non-literal subexpressions. | |
| 6003 */ | |
| 6004 class StringBuilderVisitor extends ast.Visitor { | |
| 6005 final SsaBuilder builder; | |
| 6006 final ast.Node diagnosticNode; | |
| 6007 | |
| 6008 /** | |
| 6009 * The string value generated so far. | |
| 6010 */ | |
| 6011 HInstruction result = null; | |
| 6012 | |
| 6013 StringBuilderVisitor(this.builder, this.diagnosticNode); | |
| 6014 | |
| 6015 Compiler get compiler => builder.compiler; | |
| 6016 | |
| 6017 void visit(ast.Node node) { | |
| 6018 node.accept(this); | |
| 6019 } | |
| 6020 | |
| 6021 visitNode(ast.Node node) { | |
| 6022 builder.compiler.internalError(node, 'Unexpected node.'); | |
| 6023 } | |
| 6024 | |
| 6025 void visitExpression(ast.Node node) { | |
| 6026 node.accept(builder); | |
| 6027 HInstruction expression = builder.pop(); | |
| 6028 | |
| 6029 // We want to use HStringify when: | |
| 6030 // 1. The value is known to be a primitive type, because it might get | |
| 6031 // constant-folded and codegen has some tricks with JavaScript | |
| 6032 // conversions. | |
| 6033 // 2. The value can be primitive, because the library stringifier has | |
| 6034 // fast-path code for most primitives. | |
| 6035 if (expression.canBePrimitive(compiler)) { | |
| 6036 append(stringify(node, expression)); | |
| 6037 return; | |
| 6038 } | |
| 6039 | |
| 6040 // If the `toString` method is guaranteed to return a string we can call it | |
| 6041 // directly. | |
| 6042 Selector selector = | |
| 6043 new TypedSelector(expression.instructionType, | |
| 6044 new Selector.call('toString', null, 0), compiler.world); | |
| 6045 TypeMask type = TypeMaskFactory.inferredTypeForSelector(selector, compiler); | |
| 6046 if (type.containsOnlyString(compiler.world)) { | |
| 6047 builder.pushInvokeDynamic(node, selector, <HInstruction>[expression]); | |
| 6048 append(builder.pop()); | |
| 6049 return; | |
| 6050 } | |
| 6051 | |
| 6052 append(stringify(node, expression)); | |
| 6053 } | |
| 6054 | |
| 6055 void visitStringInterpolation(ast.StringInterpolation node) { | |
| 6056 node.visitChildren(this); | |
| 6057 } | |
| 6058 | |
| 6059 void visitStringInterpolationPart(ast.StringInterpolationPart node) { | |
| 6060 visit(node.expression); | |
| 6061 visit(node.string); | |
| 6062 } | |
| 6063 | |
| 6064 void visitStringJuxtaposition(ast.StringJuxtaposition node) { | |
| 6065 node.visitChildren(this); | |
| 6066 } | |
| 6067 | |
| 6068 void visitNodeList(ast.NodeList node) { | |
| 6069 node.visitChildren(this); | |
| 6070 } | |
| 6071 | |
| 6072 void append(HInstruction expression) { | |
| 6073 result = (result == null) ? expression : concat(result, expression); | |
| 6074 } | |
| 6075 | |
| 6076 HInstruction concat(HInstruction left, HInstruction right) { | |
| 6077 HInstruction instruction = new HStringConcat( | |
| 6078 left, right, diagnosticNode, builder.backend.stringType); | |
| 6079 builder.add(instruction); | |
| 6080 return instruction; | |
| 6081 } | |
| 6082 | |
| 6083 HInstruction stringify(ast.Node node, HInstruction expression) { | |
| 6084 HInstruction instruction = | |
| 6085 new HStringify(expression, node, builder.backend.stringType); | |
| 6086 builder.add(instruction); | |
| 6087 return instruction; | |
| 6088 } | |
| 6089 } | |
| 6090 | |
| 6091 /** | |
| 6092 * This class visits the method that is a candidate for inlining and | |
| 6093 * finds whether it is too difficult to inline. | |
| 6094 */ | |
| 6095 // TODO(karlklose): refactor to make it possible to distinguish between | |
| 6096 // implementation restrictions (for example, we *can't* inline multiple returns) | |
| 6097 // and heuristics (we *shouldn't* inline large functions). | |
| 6098 class InlineWeeder extends ast.Visitor { | |
| 6099 // Invariant: *INSIDE_LOOP* > *OUTSIDE_LOOP* | |
| 6100 static const INLINING_NODES_OUTSIDE_LOOP = 18; | |
| 6101 static const INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR = 3; | |
| 6102 static const INLINING_NODES_INSIDE_LOOP = 42; | |
| 6103 static const INLINING_NODES_INSIDE_LOOP_ARG_FACTOR = 4; | |
| 6104 | |
| 6105 bool seenReturn = false; | |
| 6106 bool tooDifficult = false; | |
| 6107 int nodeCount = 0; | |
| 6108 final int maxInliningNodes; | |
| 6109 final bool useMaxInliningNodes; | |
| 6110 final bool allowLoops; | |
| 6111 | |
| 6112 InlineWeeder(this.maxInliningNodes, | |
| 6113 this.useMaxInliningNodes, | |
| 6114 this.allowLoops); | |
| 6115 | |
| 6116 static bool canBeInlined(ast.FunctionExpression functionExpression, | |
| 6117 int maxInliningNodes, | |
| 6118 bool useMaxInliningNodes, | |
| 6119 {bool allowLoops: false}) { | |
| 6120 InlineWeeder weeder = | |
| 6121 new InlineWeeder(maxInliningNodes, useMaxInliningNodes, allowLoops); | |
| 6122 weeder.visit(functionExpression.initializers); | |
| 6123 weeder.visit(functionExpression.body); | |
| 6124 return !weeder.tooDifficult; | |
| 6125 } | |
| 6126 | |
| 6127 bool registerNode() { | |
| 6128 if (!useMaxInliningNodes) return true; | |
| 6129 if (nodeCount++ > maxInliningNodes) { | |
| 6130 tooDifficult = true; | |
| 6131 return false; | |
| 6132 } else { | |
| 6133 return true; | |
| 6134 } | |
| 6135 } | |
| 6136 | |
| 6137 void visit(ast.Node node) { | |
| 6138 if (node != null) node.accept(this); | |
| 6139 } | |
| 6140 | |
| 6141 void visitNode(ast.Node node) { | |
| 6142 if (!registerNode()) return; | |
| 6143 if (seenReturn) { | |
| 6144 tooDifficult = true; | |
| 6145 } else { | |
| 6146 node.visitChildren(this); | |
| 6147 } | |
| 6148 } | |
| 6149 | |
| 6150 void visitFunctionExpression(ast.Node node) { | |
| 6151 if (!registerNode()) return; | |
| 6152 tooDifficult = true; | |
| 6153 } | |
| 6154 | |
| 6155 void visitFunctionDeclaration(ast.Node node) { | |
| 6156 if (!registerNode()) return; | |
| 6157 tooDifficult = true; | |
| 6158 } | |
| 6159 | |
| 6160 void visitSend(ast.Send node) { | |
| 6161 if (!registerNode()) return; | |
| 6162 node.visitChildren(this); | |
| 6163 } | |
| 6164 | |
| 6165 visitLoop(ast.Node node) { | |
| 6166 // It's actually not difficult to inline a method with a loop, but | |
| 6167 // our measurements show that it's currently better to not inline a | |
| 6168 // method that contains a loop. | |
| 6169 if (!allowLoops) tooDifficult = true; | |
| 6170 } | |
| 6171 | |
| 6172 void visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) { | |
| 6173 if (!registerNode()) return; | |
| 6174 tooDifficult = true; | |
| 6175 } | |
| 6176 | |
| 6177 void visitRethrow(ast.Rethrow node) { | |
| 6178 if (!registerNode()) return; | |
| 6179 tooDifficult = true; | |
| 6180 } | |
| 6181 | |
| 6182 void visitReturn(ast.Return node) { | |
| 6183 if (!registerNode()) return; | |
| 6184 if (seenReturn | |
| 6185 || identical(node.beginToken.stringValue, 'native')) { | |
| 6186 tooDifficult = true; | |
| 6187 return; | |
| 6188 } | |
| 6189 node.visitChildren(this); | |
| 6190 seenReturn = true; | |
| 6191 } | |
| 6192 | |
| 6193 void visitTryStatement(ast.Node node) { | |
| 6194 if (!registerNode()) return; | |
| 6195 tooDifficult = true; | |
| 6196 } | |
| 6197 | |
| 6198 void visitThrow(ast.Throw node) { | |
| 6199 if (!registerNode()) return; | |
| 6200 // For now, we don't want to handle throw after a return even if | |
| 6201 // it is in an "if". | |
| 6202 if (seenReturn) { | |
| 6203 tooDifficult = true; | |
| 6204 } else { | |
| 6205 node.visitChildren(this); | |
| 6206 } | |
| 6207 } | |
| 6208 } | |
| 6209 | |
| 6210 abstract class InliningState { | |
| 6211 /** | |
| 6212 * Invariant: [function] must be an implementation element. | |
| 6213 */ | |
| 6214 final FunctionElement function; | |
| 6215 | |
| 6216 InliningState(this.function) { | |
| 6217 assert(function.isImplementation); | |
| 6218 } | |
| 6219 } | |
| 6220 | |
| 6221 class AstInliningState extends InliningState { | |
| 6222 final Local oldReturnLocal; | |
| 6223 final DartType oldReturnType; | |
| 6224 final TreeElements oldElements; | |
| 6225 final List<HInstruction> oldStack; | |
| 6226 final LocalsHandler oldLocalsHandler; | |
| 6227 final bool inTryStatement; | |
| 6228 final bool allFunctionsCalledOnce; | |
| 6229 | |
| 6230 AstInliningState(FunctionElement function, | |
| 6231 this.oldReturnLocal, | |
| 6232 this.oldReturnType, | |
| 6233 this.oldElements, | |
| 6234 this.oldStack, | |
| 6235 this.oldLocalsHandler, | |
| 6236 this.inTryStatement, | |
| 6237 this.allFunctionsCalledOnce) | |
| 6238 : super(function); | |
| 6239 } | |
| 6240 | |
| 6241 class SsaBranch { | |
| 6242 final SsaBranchBuilder branchBuilder; | |
| 6243 final HBasicBlock block; | |
| 6244 LocalsHandler startLocals; | |
| 6245 LocalsHandler exitLocals; | |
| 6246 SubGraph graph; | |
| 6247 | |
| 6248 SsaBranch(this.branchBuilder) : block = new HBasicBlock(); | |
| 6249 } | |
| 6250 | |
| 6251 class SsaBranchBuilder { | |
| 6252 final SsaBuilder builder; | |
| 6253 final ast.Node diagnosticNode; | |
| 6254 | |
| 6255 SsaBranchBuilder(this.builder, [this.diagnosticNode]); | |
| 6256 | |
| 6257 Compiler get compiler => builder.compiler; | |
| 6258 | |
| 6259 void checkNotAborted() { | |
| 6260 if (builder.isAborted()) { | |
| 6261 compiler.unimplemented(diagnosticNode, "aborted control flow"); | |
| 6262 } | |
| 6263 } | |
| 6264 | |
| 6265 void buildCondition(void visitCondition(), | |
| 6266 SsaBranch conditionBranch, | |
| 6267 SsaBranch thenBranch, | |
| 6268 SsaBranch elseBranch) { | |
| 6269 startBranch(conditionBranch); | |
| 6270 visitCondition(); | |
| 6271 checkNotAborted(); | |
| 6272 assert(identical(builder.current, builder.lastOpenedBlock)); | |
| 6273 HInstruction conditionValue = builder.popBoolified(); | |
| 6274 HIf branch = new HIf(conditionValue); | |
| 6275 HBasicBlock conditionExitBlock = builder.current; | |
| 6276 builder.close(branch); | |
| 6277 conditionBranch.exitLocals = builder.localsHandler; | |
| 6278 conditionExitBlock.addSuccessor(thenBranch.block); | |
| 6279 conditionExitBlock.addSuccessor(elseBranch.block); | |
| 6280 bool conditionBranchLocalsCanBeReused = | |
| 6281 mergeLocals(conditionBranch, thenBranch, mayReuseFromLocals: true); | |
| 6282 mergeLocals(conditionBranch, elseBranch, | |
| 6283 mayReuseFromLocals: conditionBranchLocalsCanBeReused); | |
| 6284 | |
| 6285 conditionBranch.graph = | |
| 6286 new SubExpression(conditionBranch.block, conditionExitBlock); | |
| 6287 } | |
| 6288 | |
| 6289 /** | |
| 6290 * Returns true if the locals of the [fromBranch] may be reused. A [:true:] | |
| 6291 * return value implies that [mayReuseFromLocals] was set to [:true:]. | |
| 6292 */ | |
| 6293 bool mergeLocals(SsaBranch fromBranch, SsaBranch toBranch, | |
| 6294 {bool mayReuseFromLocals}) { | |
| 6295 LocalsHandler fromLocals = fromBranch.exitLocals; | |
| 6296 if (toBranch.startLocals == null) { | |
| 6297 if (mayReuseFromLocals) { | |
| 6298 toBranch.startLocals = fromLocals; | |
| 6299 return false; | |
| 6300 } else { | |
| 6301 toBranch.startLocals = new LocalsHandler.from(fromLocals); | |
| 6302 return true; | |
| 6303 } | |
| 6304 } else { | |
| 6305 toBranch.startLocals.mergeWith(fromLocals, toBranch.block); | |
| 6306 return true; | |
| 6307 } | |
| 6308 } | |
| 6309 | |
| 6310 void startBranch(SsaBranch branch) { | |
| 6311 builder.graph.addBlock(branch.block); | |
| 6312 builder.localsHandler = branch.startLocals; | |
| 6313 builder.open(branch.block); | |
| 6314 } | |
| 6315 | |
| 6316 HInstruction buildBranch(SsaBranch branch, | |
| 6317 void visitBranch(), | |
| 6318 SsaBranch joinBranch, | |
| 6319 bool isExpression) { | |
| 6320 startBranch(branch); | |
| 6321 visitBranch(); | |
| 6322 branch.graph = new SubGraph(branch.block, builder.lastOpenedBlock); | |
| 6323 branch.exitLocals = builder.localsHandler; | |
| 6324 if (!builder.isAborted()) { | |
| 6325 builder.goto(builder.current, joinBranch.block); | |
| 6326 mergeLocals(branch, joinBranch, mayReuseFromLocals: true); | |
| 6327 } | |
| 6328 if (isExpression) { | |
| 6329 checkNotAborted(); | |
| 6330 return builder.pop(); | |
| 6331 } | |
| 6332 return null; | |
| 6333 } | |
| 6334 | |
| 6335 handleIf(void visitCondition(), void visitThen(), void visitElse()) { | |
| 6336 if (visitElse == null) { | |
| 6337 // Make sure to have an else part to avoid a critical edge. A | |
| 6338 // critical edge is an edge that connects a block with multiple | |
| 6339 // successors to a block with multiple predecessors. We avoid | |
| 6340 // such edges because they prevent inserting copies during code | |
| 6341 // generation of phi instructions. | |
| 6342 visitElse = () {}; | |
| 6343 } | |
| 6344 | |
| 6345 _handleDiamondBranch(visitCondition, visitThen, visitElse, false); | |
| 6346 } | |
| 6347 | |
| 6348 handleConditional(void visitCondition(), void visitThen(), void visitElse()) { | |
| 6349 assert(visitElse != null); | |
| 6350 _handleDiamondBranch(visitCondition, visitThen, visitElse, true); | |
| 6351 } | |
| 6352 | |
| 6353 void handleLogicalAndOr(void left(), void right(), {bool isAnd}) { | |
| 6354 // x && y is transformed into: | |
| 6355 // t0 = boolify(x); | |
| 6356 // if (t0) { | |
| 6357 // t1 = boolify(y); | |
| 6358 // } | |
| 6359 // result = phi(t1, false); | |
| 6360 // | |
| 6361 // x || y is transformed into: | |
| 6362 // t0 = boolify(x); | |
| 6363 // if (not(t0)) { | |
| 6364 // t1 = boolify(y); | |
| 6365 // } | |
| 6366 // result = phi(t1, true); | |
| 6367 HInstruction boolifiedLeft; | |
| 6368 HInstruction boolifiedRight; | |
| 6369 | |
| 6370 void visitCondition() { | |
| 6371 left(); | |
| 6372 boolifiedLeft = builder.popBoolified(); | |
| 6373 builder.stack.add(boolifiedLeft); | |
| 6374 if (!isAnd) { | |
| 6375 builder.push(new HNot(builder.pop(), builder.backend.boolType)); | |
| 6376 } | |
| 6377 } | |
| 6378 | |
| 6379 void visitThen() { | |
| 6380 right(); | |
| 6381 boolifiedRight = builder.popBoolified(); | |
| 6382 } | |
| 6383 | |
| 6384 handleIf(visitCondition, visitThen, null); | |
| 6385 HConstant notIsAnd = | |
| 6386 builder.graph.addConstantBool(!isAnd, builder.compiler); | |
| 6387 JavaScriptBackend backend = builder.backend; | |
| 6388 HPhi result = new HPhi.manyInputs(null, | |
| 6389 <HInstruction>[boolifiedRight, notIsAnd], | |
| 6390 backend.dynamicType); | |
| 6391 builder.current.addPhi(result); | |
| 6392 builder.stack.add(result); | |
| 6393 } | |
| 6394 | |
| 6395 void handleLogicalAndOrWithLeftNode(ast.Node left, | |
| 6396 void visitRight(), | |
| 6397 {bool isAnd}) { | |
| 6398 // This method is similar to [handleLogicalAndOr] but optimizes the case | |
| 6399 // where left is a logical "and" or logical "or". | |
| 6400 // | |
| 6401 // For example (x && y) && z is transformed into x && (y && z): | |
| 6402 // t0 = boolify(x); | |
| 6403 // if (t0) { | |
| 6404 // t1 = boolify(y); | |
| 6405 // if (t1) { | |
| 6406 // t2 = boolify(z); | |
| 6407 // } | |
| 6408 // t3 = phi(t2, false); | |
| 6409 // } | |
| 6410 // result = phi(t3, false); | |
| 6411 | |
| 6412 ast.Send send = left.asSend(); | |
| 6413 if (send != null && | |
| 6414 (isAnd ? send.isLogicalAnd : send.isLogicalOr)) { | |
| 6415 ast.Node newLeft = send.receiver; | |
| 6416 Link<ast.Node> link = send.argumentsNode.nodes; | |
| 6417 assert(link.tail.isEmpty); | |
| 6418 ast.Node middle = link.head; | |
| 6419 handleLogicalAndOrWithLeftNode( | |
| 6420 newLeft, | |
| 6421 () => handleLogicalAndOrWithLeftNode(middle, visitRight, | |
| 6422 isAnd: isAnd), | |
| 6423 isAnd: isAnd); | |
| 6424 } else { | |
| 6425 handleLogicalAndOr(() => builder.visit(left), visitRight, isAnd: isAnd); | |
| 6426 } | |
| 6427 } | |
| 6428 | |
| 6429 void _handleDiamondBranch(void visitCondition(), | |
| 6430 void visitThen(), | |
| 6431 void visitElse(), | |
| 6432 bool isExpression) { | |
| 6433 SsaBranch conditionBranch = new SsaBranch(this); | |
| 6434 SsaBranch thenBranch = new SsaBranch(this); | |
| 6435 SsaBranch elseBranch = new SsaBranch(this); | |
| 6436 SsaBranch joinBranch = new SsaBranch(this); | |
| 6437 | |
| 6438 conditionBranch.startLocals = builder.localsHandler; | |
| 6439 builder.goto(builder.current, conditionBranch.block); | |
| 6440 | |
| 6441 buildCondition(visitCondition, conditionBranch, thenBranch, elseBranch); | |
| 6442 HInstruction thenValue = | |
| 6443 buildBranch(thenBranch, visitThen, joinBranch, isExpression); | |
| 6444 HInstruction elseValue = | |
| 6445 buildBranch(elseBranch, visitElse, joinBranch, isExpression); | |
| 6446 | |
| 6447 if (isExpression) { | |
| 6448 assert(thenValue != null && elseValue != null); | |
| 6449 JavaScriptBackend backend = builder.backend; | |
| 6450 HPhi phi = new HPhi.manyInputs( | |
| 6451 null, <HInstruction>[thenValue, elseValue], backend.dynamicType); | |
| 6452 joinBranch.block.addPhi(phi); | |
| 6453 builder.stack.add(phi); | |
| 6454 } | |
| 6455 | |
| 6456 HBasicBlock thenBlock = thenBranch.block; | |
| 6457 HBasicBlock elseBlock = elseBranch.block; | |
| 6458 HBasicBlock joinBlock; | |
| 6459 // If at least one branch did not abort, open the joinBranch. | |
| 6460 if (!joinBranch.block.predecessors.isEmpty) { | |
| 6461 startBranch(joinBranch); | |
| 6462 joinBlock = joinBranch.block; | |
| 6463 } | |
| 6464 | |
| 6465 HIfBlockInformation info = | |
| 6466 new HIfBlockInformation( | |
| 6467 new HSubExpressionBlockInformation(conditionBranch.graph), | |
| 6468 new HSubGraphBlockInformation(thenBranch.graph), | |
| 6469 new HSubGraphBlockInformation(elseBranch.graph)); | |
| 6470 | |
| 6471 HBasicBlock conditionStartBlock = conditionBranch.block; | |
| 6472 conditionStartBlock.setBlockFlow(info, joinBlock); | |
| 6473 SubGraph conditionGraph = conditionBranch.graph; | |
| 6474 HIf branch = conditionGraph.end.last; | |
| 6475 assert(branch is HIf); | |
| 6476 branch.blockInformation = conditionStartBlock.blockFlow; | |
| 6477 } | |
| 6478 } | |
| 6479 | |
| 6480 class TypeBuilder implements DartTypeVisitor<dynamic, SsaBuilder> { | |
| 6481 final ClassWorld classWorld; | |
| 6482 | |
| 6483 TypeBuilder(this.classWorld); | |
| 6484 | |
| 6485 void visitType(DartType type, _) { | |
| 6486 throw 'Internal error $type'; | |
| 6487 } | |
| 6488 | |
| 6489 void visitVoidType(VoidType type, SsaBuilder builder) { | |
| 6490 ClassElement cls = builder.backend.findHelper('VoidRuntimeType'); | |
| 6491 builder.push(new HVoidType(type, new TypeMask.exact(cls, classWorld))); | |
| 6492 } | |
| 6493 | |
| 6494 void visitTypeVariableType(TypeVariableType type, | |
| 6495 SsaBuilder builder) { | |
| 6496 ClassElement cls = builder.backend.findHelper('RuntimeType'); | |
| 6497 TypeMask instructionType = new TypeMask.subclass(cls, classWorld); | |
| 6498 if (!builder.sourceElement.enclosingElement.isClosure && | |
| 6499 builder.sourceElement.isInstanceMember) { | |
| 6500 HInstruction receiver = builder.localsHandler.readThis(); | |
| 6501 builder.push(new HReadTypeVariable(type, receiver, instructionType)); | |
| 6502 } else { | |
| 6503 builder.push( | |
| 6504 new HReadTypeVariable.noReceiver( | |
| 6505 type, builder.addTypeVariableReference(type), instructionType)); | |
| 6506 } | |
| 6507 } | |
| 6508 | |
| 6509 void visitFunctionType(FunctionType type, SsaBuilder builder) { | |
| 6510 type.returnType.accept(this, builder); | |
| 6511 HInstruction returnType = builder.pop(); | |
| 6512 List<HInstruction> inputs = <HInstruction>[returnType]; | |
| 6513 | |
| 6514 for (DartType parameter in type.parameterTypes) { | |
| 6515 parameter.accept(this, builder); | |
| 6516 inputs.add(builder.pop()); | |
| 6517 } | |
| 6518 | |
| 6519 for (DartType parameter in type.optionalParameterTypes) { | |
| 6520 parameter.accept(this, builder); | |
| 6521 inputs.add(builder.pop()); | |
| 6522 } | |
| 6523 | |
| 6524 List<DartType> namedParameterTypes = type.namedParameterTypes; | |
| 6525 List<String> names = type.namedParameters; | |
| 6526 for (int index = 0; index < names.length; index++) { | |
| 6527 ast.DartString dartString = new ast.DartString.literal(names[index]); | |
| 6528 inputs.add( | |
| 6529 builder.graph.addConstantString(dartString, builder.compiler)); | |
| 6530 namedParameterTypes[index].accept(this, builder); | |
| 6531 inputs.add(builder.pop()); | |
| 6532 } | |
| 6533 | |
| 6534 ClassElement cls = builder.backend.findHelper('RuntimeFunctionType'); | |
| 6535 builder.push(new HFunctionType(inputs, type, | |
| 6536 new TypeMask.exact(cls, classWorld))); | |
| 6537 } | |
| 6538 | |
| 6539 void visitMalformedType(MalformedType type, SsaBuilder builder) { | |
| 6540 visitDynamicType(const DynamicType(), builder); | |
| 6541 } | |
| 6542 | |
| 6543 void visitStatementType(StatementType type, SsaBuilder builder) { | |
| 6544 throw 'not implemented visitStatementType($type)'; | |
| 6545 } | |
| 6546 | |
| 6547 void visitGenericType(GenericType type, SsaBuilder builder) { | |
| 6548 throw 'not implemented visitGenericType($type)'; | |
| 6549 } | |
| 6550 | |
| 6551 void visitInterfaceType(InterfaceType type, SsaBuilder builder) { | |
| 6552 List<HInstruction> inputs = <HInstruction>[]; | |
| 6553 for (DartType typeArgument in type.typeArguments) { | |
| 6554 typeArgument.accept(this, builder); | |
| 6555 inputs.add(builder.pop()); | |
| 6556 } | |
| 6557 ClassElement cls; | |
| 6558 if (type.typeArguments.isEmpty) { | |
| 6559 cls = builder.backend.findHelper('RuntimeTypePlain'); | |
| 6560 } else { | |
| 6561 cls = builder.backend.findHelper('RuntimeTypeGeneric'); | |
| 6562 } | |
| 6563 builder.push(new HInterfaceType(inputs, type, | |
| 6564 new TypeMask.exact(cls, classWorld))); | |
| 6565 } | |
| 6566 | |
| 6567 void visitTypedefType(TypedefType type, SsaBuilder builder) { | |
| 6568 DartType unaliased = type.unalias(builder.compiler); | |
| 6569 if (unaliased is TypedefType) throw 'unable to unalias $type'; | |
| 6570 unaliased.accept(this, builder); | |
| 6571 } | |
| 6572 | |
| 6573 void visitDynamicType(DynamicType type, SsaBuilder builder) { | |
| 6574 JavaScriptBackend backend = builder.compiler.backend; | |
| 6575 ClassElement cls = backend.findHelper('DynamicRuntimeType'); | |
| 6576 builder.push(new HDynamicType(type, new TypeMask.exact(cls, classWorld))); | |
| 6577 } | |
| 6578 } | |
| OLD | NEW |