| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import 'dart:collection'; | 5 import 'dart:collection'; |
| 6 | 6 |
| 7 import 'package:js_runtime/shared/embedded_names.dart'; | 7 import 'package:js_runtime/shared/embedded_names.dart'; |
| 8 | 8 |
| 9 import '../closure.dart'; | 9 import '../closure.dart'; |
| 10 import '../common.dart'; | 10 import '../common.dart'; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 32 import '../resolution/tree_elements.dart' show TreeElements; | 32 import '../resolution/tree_elements.dart' show TreeElements; |
| 33 import '../tree/tree.dart' as ast; | 33 import '../tree/tree.dart' as ast; |
| 34 import '../types/types.dart'; | 34 import '../types/types.dart'; |
| 35 import '../universe/call_structure.dart' show CallStructure; | 35 import '../universe/call_structure.dart' show CallStructure; |
| 36 import '../universe/selector.dart' show Selector; | 36 import '../universe/selector.dart' show Selector; |
| 37 import '../universe/side_effects.dart' show SideEffects; | 37 import '../universe/side_effects.dart' show SideEffects; |
| 38 import '../universe/use.dart' show DynamicUse, StaticUse, TypeUse; | 38 import '../universe/use.dart' show DynamicUse, StaticUse, TypeUse; |
| 39 import '../util/util.dart'; | 39 import '../util/util.dart'; |
| 40 import '../world.dart' show ClassWorld; | 40 import '../world.dart' show ClassWorld; |
| 41 import 'graph_builder.dart'; | 41 import 'graph_builder.dart'; |
| 42 import 'locals_handler.dart'; |
| 42 import 'nodes.dart'; | 43 import 'nodes.dart'; |
| 43 import 'optimize.dart'; | 44 import 'optimize.dart'; |
| 44 import 'types.dart'; | 45 import 'types.dart'; |
| 45 | 46 |
| 46 /// A synthetic local variable only used with the SSA graph. | 47 /// A synthetic local variable only used with the SSA graph. |
| 47 /// | 48 /// |
| 48 /// For instance used for holding return value of function or the exception of a | 49 /// For instance used for holding return value of function or the exception of a |
| 49 /// try-catch statement. | 50 /// try-catch statement. |
| 50 class SyntheticLocal extends Local { | 51 class SyntheticLocal extends Local { |
| 51 final String name; | 52 final String name; |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 116 } | 117 } |
| 117 compiler.tracer.traceCompilation(name, work.compilationContext); | 118 compiler.tracer.traceCompilation(name, work.compilationContext); |
| 118 compiler.tracer.traceGraph('builder', graph); | 119 compiler.tracer.traceGraph('builder', graph); |
| 119 } | 120 } |
| 120 return graph; | 121 return graph; |
| 121 }); | 122 }); |
| 122 }); | 123 }); |
| 123 } | 124 } |
| 124 } | 125 } |
| 125 | 126 |
| 126 /** | |
| 127 * Keeps track of locals (including parameters and phis) when building. The | |
| 128 * 'this' reference is treated as parameter and hence handled by this class, | |
| 129 * too. | |
| 130 */ | |
| 131 class LocalsHandler { | |
| 132 /** | |
| 133 * The values of locals that can be directly accessed (without redirections | |
| 134 * to boxes or closure-fields). | |
| 135 * | |
| 136 * [directLocals] is iterated, so it is "insertion ordered" to make the | |
| 137 * iteration order a function only of insertions and not a function of | |
| 138 * e.g. Element hash codes. I'd prefer to use a SortedMap but some elements | |
| 139 * don't have source locations for [Elements.compareByPosition]. | |
| 140 */ | |
| 141 Map<Local, HInstruction> directLocals = new Map<Local, HInstruction>(); | |
| 142 Map<Local, CapturedVariable> redirectionMapping = | |
| 143 new Map<Local, CapturedVariable>(); | |
| 144 SsaBuilder builder; | |
| 145 ClosureClassMap closureData; | |
| 146 Map<TypeVariableType, TypeVariableLocal> typeVariableLocals = | |
| 147 new Map<TypeVariableType, TypeVariableLocal>(); | |
| 148 final ExecutableElement executableContext; | |
| 149 | |
| 150 /// The class that defines the current type environment or null if no type | |
| 151 /// variables are in scope. | |
| 152 ClassElement get contextClass => executableContext.contextClass; | |
| 153 | |
| 154 /// The type of the current instance, if concrete. | |
| 155 /// | |
| 156 /// This allows for handling fixed type argument in case of inlining. For | |
| 157 /// instance, checking `'foo'` against `String` instead of `T` in `main`: | |
| 158 /// | |
| 159 /// class Foo<T> { | |
| 160 /// T field; | |
| 161 /// Foo(this.field); | |
| 162 /// } | |
| 163 /// main() { | |
| 164 /// new Foo<String>('foo'); | |
| 165 /// } | |
| 166 /// | |
| 167 /// [instanceType] is not used if it contains type variables, since these | |
| 168 /// might not be in scope or from the current instance. | |
| 169 /// | |
| 170 final InterfaceType instanceType; | |
| 171 | |
| 172 SourceInformationBuilder get sourceInformationBuilder { | |
| 173 return builder.sourceInformationBuilder; | |
| 174 } | |
| 175 | |
| 176 LocalsHandler( | |
| 177 this.builder, this.executableContext, InterfaceType instanceType) | |
| 178 : this.instanceType = instanceType == null || | |
| 179 instanceType.containsTypeVariables ? null : instanceType; | |
| 180 | |
| 181 /// Substituted type variables occurring in [type] into the context of | |
| 182 /// [contextClass]. | |
| 183 DartType substInContext(DartType type) { | |
| 184 if (contextClass != null) { | |
| 185 ClassElement typeContext = Types.getClassContext(type); | |
| 186 if (typeContext != null) { | |
| 187 type = type.substByContext(contextClass.asInstanceOf(typeContext)); | |
| 188 } | |
| 189 } | |
| 190 if (instanceType != null) { | |
| 191 type = type.substByContext(instanceType); | |
| 192 } | |
| 193 return type; | |
| 194 } | |
| 195 | |
| 196 get typesTask => builder.compiler.typesTask; | |
| 197 | |
| 198 /** | |
| 199 * Creates a new [LocalsHandler] based on [other]. We only need to | |
| 200 * copy the [directLocals], since the other fields can be shared | |
| 201 * throughout the AST visit. | |
| 202 */ | |
| 203 LocalsHandler.from(LocalsHandler other) | |
| 204 : directLocals = new Map<Local, HInstruction>.from(other.directLocals), | |
| 205 redirectionMapping = other.redirectionMapping, | |
| 206 executableContext = other.executableContext, | |
| 207 instanceType = other.instanceType, | |
| 208 builder = other.builder, | |
| 209 closureData = other.closureData; | |
| 210 | |
| 211 /** | |
| 212 * Redirects accesses from element [from] to element [to]. The [to] element | |
| 213 * must be a boxed variable or a variable that is stored in a closure-field. | |
| 214 */ | |
| 215 void redirectElement(Local from, CapturedVariable to) { | |
| 216 assert(redirectionMapping[from] == null); | |
| 217 redirectionMapping[from] = to; | |
| 218 assert(isStoredInClosureField(from) || isBoxed(from)); | |
| 219 } | |
| 220 | |
| 221 HInstruction createBox() { | |
| 222 // TODO(floitsch): Clean up this hack. Should we create a box-object by | |
| 223 // just creating an empty object literal? | |
| 224 JavaScriptBackend backend = builder.backend; | |
| 225 HInstruction box = new HForeignCode( | |
| 226 js.js.parseForeignJS('{}'), backend.nonNullType, <HInstruction>[], | |
| 227 nativeBehavior: native.NativeBehavior.PURE_ALLOCATION); | |
| 228 builder.add(box); | |
| 229 return box; | |
| 230 } | |
| 231 | |
| 232 /** | |
| 233 * If the scope (function or loop) [node] has captured variables then this | |
| 234 * method creates a box and sets up the redirections. | |
| 235 */ | |
| 236 void enterScope(ast.Node node, Element element) { | |
| 237 // See if any variable in the top-scope of the function is captured. If yes | |
| 238 // we need to create a box-object. | |
| 239 ClosureScope scopeData = closureData.capturingScopes[node]; | |
| 240 if (scopeData == null) return; | |
| 241 HInstruction box; | |
| 242 // The scope has captured variables. | |
| 243 if (element != null && element.isGenerativeConstructorBody) { | |
| 244 // The box is passed as a parameter to a generative | |
| 245 // constructor body. | |
| 246 JavaScriptBackend backend = builder.backend; | |
| 247 box = builder.addParameter(scopeData.boxElement, backend.nonNullType); | |
| 248 } else { | |
| 249 box = createBox(); | |
| 250 } | |
| 251 // Add the box to the known locals. | |
| 252 directLocals[scopeData.boxElement] = box; | |
| 253 // Make sure that accesses to the boxed locals go into the box. We also | |
| 254 // need to make sure that parameters are copied into the box if necessary. | |
| 255 scopeData.forEachCapturedVariable( | |
| 256 (LocalVariableElement from, BoxFieldElement to) { | |
| 257 // The [from] can only be a parameter for function-scopes and not | |
| 258 // loop scopes. | |
| 259 if (from.isRegularParameter && !element.isGenerativeConstructorBody) { | |
| 260 // Now that the redirection is set up, the update to the local will | |
| 261 // write the parameter value into the box. | |
| 262 // Store the captured parameter in the box. Get the current value | |
| 263 // before we put the redirection in place. | |
| 264 // We don't need to update the local for a generative | |
| 265 // constructor body, because it receives a box that already | |
| 266 // contains the updates as the last parameter. | |
| 267 HInstruction instruction = readLocal(from); | |
| 268 redirectElement(from, to); | |
| 269 updateLocal(from, instruction); | |
| 270 } else { | |
| 271 redirectElement(from, to); | |
| 272 } | |
| 273 }); | |
| 274 } | |
| 275 | |
| 276 /** | |
| 277 * Replaces the current box with a new box and copies over the given list | |
| 278 * of elements from the old box into the new box. | |
| 279 */ | |
| 280 void updateCaptureBox( | |
| 281 BoxLocal boxElement, List<LocalVariableElement> toBeCopiedElements) { | |
| 282 // Create a new box and copy over the values from the old box into the | |
| 283 // new one. | |
| 284 HInstruction oldBox = readLocal(boxElement); | |
| 285 HInstruction newBox = createBox(); | |
| 286 for (LocalVariableElement boxedVariable in toBeCopiedElements) { | |
| 287 // [readLocal] uses the [boxElement] to find its box. By replacing it | |
| 288 // behind its back we can still get to the old values. | |
| 289 updateLocal(boxElement, oldBox); | |
| 290 HInstruction oldValue = readLocal(boxedVariable); | |
| 291 updateLocal(boxElement, newBox); | |
| 292 updateLocal(boxedVariable, oldValue); | |
| 293 } | |
| 294 updateLocal(boxElement, newBox); | |
| 295 } | |
| 296 | |
| 297 /** | |
| 298 * Documentation wanted -- johnniwinther | |
| 299 * | |
| 300 * Invariant: [function] must be an implementation element. | |
| 301 */ | |
| 302 void startFunction(AstElement element, ast.Node node) { | |
| 303 assert(invariant(element, element.isImplementation)); | |
| 304 Compiler compiler = builder.compiler; | |
| 305 closureData = compiler.closureToClassMapper | |
| 306 .computeClosureToClassMapping(element.resolvedAst); | |
| 307 | |
| 308 if (element is FunctionElement) { | |
| 309 FunctionElement functionElement = element; | |
| 310 FunctionSignature params = functionElement.functionSignature; | |
| 311 ClosureScope scopeData = closureData.capturingScopes[node]; | |
| 312 params.orderedForEachParameter((ParameterElement parameterElement) { | |
| 313 if (element.isGenerativeConstructorBody) { | |
| 314 if (scopeData != null && | |
| 315 scopeData.isCapturedVariable(parameterElement)) { | |
| 316 // The parameter will be a field in the box passed as the | |
| 317 // last parameter. So no need to have it. | |
| 318 return; | |
| 319 } | |
| 320 } | |
| 321 HInstruction parameter = builder.addParameter(parameterElement, | |
| 322 TypeMaskFactory.inferredTypeForElement(parameterElement, compiler)); | |
| 323 builder.parameters[parameterElement] = parameter; | |
| 324 directLocals[parameterElement] = parameter; | |
| 325 }); | |
| 326 } | |
| 327 | |
| 328 enterScope(node, element); | |
| 329 | |
| 330 // If the freeVariableMapping is not empty, then this function was a | |
| 331 // nested closure that captures variables. Redirect the captured | |
| 332 // variables to fields in the closure. | |
| 333 closureData.forEachFreeVariable((Local from, CapturedVariable to) { | |
| 334 redirectElement(from, to); | |
| 335 }); | |
| 336 JavaScriptBackend backend = compiler.backend; | |
| 337 if (closureData.isClosure) { | |
| 338 // Inside closure redirect references to itself to [:this:]. | |
| 339 HThis thisInstruction = | |
| 340 new HThis(closureData.thisLocal, backend.nonNullType); | |
| 341 builder.graph.thisInstruction = thisInstruction; | |
| 342 builder.graph.entry.addAtEntry(thisInstruction); | |
| 343 updateLocal(closureData.closureElement, thisInstruction); | |
| 344 } else if (element.isInstanceMember) { | |
| 345 // Once closures have been mapped to classes their instance members might | |
| 346 // not have any thisElement if the closure was created inside a static | |
| 347 // context. | |
| 348 HThis thisInstruction = | |
| 349 new HThis(closureData.thisLocal, builder.getTypeOfThis()); | |
| 350 builder.graph.thisInstruction = thisInstruction; | |
| 351 builder.graph.entry.addAtEntry(thisInstruction); | |
| 352 directLocals[closureData.thisLocal] = thisInstruction; | |
| 353 } | |
| 354 | |
| 355 // If this method is an intercepted method, add the extra | |
| 356 // parameter to it, that is the actual receiver for intercepted | |
| 357 // classes, or the same as [:this:] for non-intercepted classes. | |
| 358 ClassElement cls = element.enclosingClass; | |
| 359 | |
| 360 // When the class extends a native class, the instance is pre-constructed | |
| 361 // and passed to the generative constructor factory function as a parameter. | |
| 362 // Instead of allocating and initializing the object, the constructor | |
| 363 // 'upgrades' the native subclass object by initializing the Dart fields. | |
| 364 bool isNativeUpgradeFactory = | |
| 365 element.isGenerativeConstructor && backend.isNativeOrExtendsNative(cls); | |
| 366 if (backend.isInterceptedMethod(element)) { | |
| 367 bool isInterceptorClass = backend.isInterceptorClass(cls.declaration); | |
| 368 String name = isInterceptorClass ? 'receiver' : '_'; | |
| 369 SyntheticLocal parameter = new SyntheticLocal(name, executableContext); | |
| 370 HParameterValue value = | |
| 371 new HParameterValue(parameter, builder.getTypeOfThis()); | |
| 372 builder.graph.explicitReceiverParameter = value; | |
| 373 builder.graph.entry.addAfter(directLocals[closureData.thisLocal], value); | |
| 374 if (builder.lastAddedParameter == null) { | |
| 375 // If this is the first parameter inserted, make sure it stays first. | |
| 376 builder.lastAddedParameter = value; | |
| 377 } | |
| 378 if (isInterceptorClass) { | |
| 379 // Only use the extra parameter in intercepted classes. | |
| 380 directLocals[closureData.thisLocal] = value; | |
| 381 } | |
| 382 } else if (isNativeUpgradeFactory) { | |
| 383 SyntheticLocal parameter = | |
| 384 new SyntheticLocal('receiver', executableContext); | |
| 385 // Unlike `this`, receiver is nullable since direct calls to generative | |
| 386 // constructor call the constructor with `null`. | |
| 387 ClassWorld classWorld = compiler.world; | |
| 388 HParameterValue value = | |
| 389 new HParameterValue(parameter, new TypeMask.exact(cls, classWorld)); | |
| 390 builder.graph.explicitReceiverParameter = value; | |
| 391 builder.graph.entry.addAtEntry(value); | |
| 392 } | |
| 393 } | |
| 394 | |
| 395 /** | |
| 396 * Returns true if the local can be accessed directly. Boxed variables or | |
| 397 * captured variables that are stored in the closure-field return [:false:]. | |
| 398 */ | |
| 399 bool isAccessedDirectly(Local local) { | |
| 400 assert(local != null); | |
| 401 return !redirectionMapping.containsKey(local) && | |
| 402 !closureData.variablesUsedInTryOrGenerator.contains(local); | |
| 403 } | |
| 404 | |
| 405 bool isStoredInClosureField(Local local) { | |
| 406 assert(local != null); | |
| 407 if (isAccessedDirectly(local)) return false; | |
| 408 CapturedVariable redirectTarget = redirectionMapping[local]; | |
| 409 if (redirectTarget == null) return false; | |
| 410 return redirectTarget is ClosureFieldElement; | |
| 411 } | |
| 412 | |
| 413 bool isBoxed(Local local) { | |
| 414 if (isAccessedDirectly(local)) return false; | |
| 415 if (isStoredInClosureField(local)) return false; | |
| 416 return redirectionMapping.containsKey(local); | |
| 417 } | |
| 418 | |
| 419 bool isUsedInTryOrGenerator(Local local) { | |
| 420 return closureData.variablesUsedInTryOrGenerator.contains(local); | |
| 421 } | |
| 422 | |
| 423 /** | |
| 424 * Returns an [HInstruction] for the given element. If the element is | |
| 425 * boxed or stored in a closure then the method generates code to retrieve | |
| 426 * the value. | |
| 427 */ | |
| 428 HInstruction readLocal(Local local, {SourceInformation sourceInformation}) { | |
| 429 if (isAccessedDirectly(local)) { | |
| 430 if (directLocals[local] == null) { | |
| 431 if (local is TypeVariableElement) { | |
| 432 builder.reporter.internalError(builder.compiler.currentElement, | |
| 433 "Runtime type information not available for $local."); | |
| 434 } else { | |
| 435 builder.reporter.internalError( | |
| 436 local, "Cannot find value $local in ${directLocals.keys}."); | |
| 437 } | |
| 438 } | |
| 439 HInstruction value = directLocals[local]; | |
| 440 if (sourceInformation != null) { | |
| 441 value = new HRef(value, sourceInformation); | |
| 442 builder.add(value); | |
| 443 } | |
| 444 return value; | |
| 445 } else if (isStoredInClosureField(local)) { | |
| 446 ClosureFieldElement redirect = redirectionMapping[local]; | |
| 447 HInstruction receiver = readLocal(closureData.closureElement); | |
| 448 TypeMask type = local is BoxLocal | |
| 449 ? builder.backend.nonNullType | |
| 450 : builder.getTypeOfCapturedVariable(redirect); | |
| 451 HInstruction fieldGet = new HFieldGet(redirect, receiver, type); | |
| 452 builder.add(fieldGet); | |
| 453 return fieldGet..sourceInformation = sourceInformation; | |
| 454 } else if (isBoxed(local)) { | |
| 455 BoxFieldElement redirect = redirectionMapping[local]; | |
| 456 // In the function that declares the captured variable the box is | |
| 457 // accessed as direct local. Inside the nested closure the box is | |
| 458 // accessed through a closure-field. | |
| 459 // Calling [readLocal] makes sure we generate the correct code to get | |
| 460 // the box. | |
| 461 HInstruction box = readLocal(redirect.box); | |
| 462 HInstruction lookup = new HFieldGet( | |
| 463 redirect, box, builder.getTypeOfCapturedVariable(redirect)); | |
| 464 builder.add(lookup); | |
| 465 return lookup..sourceInformation = sourceInformation; | |
| 466 } else { | |
| 467 assert(isUsedInTryOrGenerator(local)); | |
| 468 HLocalValue localValue = getLocal(local); | |
| 469 HInstruction instruction = new HLocalGet( | |
| 470 local, localValue, builder.backend.dynamicType, sourceInformation); | |
| 471 builder.add(instruction); | |
| 472 return instruction; | |
| 473 } | |
| 474 } | |
| 475 | |
| 476 HInstruction readThis() { | |
| 477 HInstruction res = readLocal(closureData.thisLocal); | |
| 478 if (res.instructionType == null) { | |
| 479 res.instructionType = builder.getTypeOfThis(); | |
| 480 } | |
| 481 return res; | |
| 482 } | |
| 483 | |
| 484 HLocalValue getLocal(Local local, {SourceInformation sourceInformation}) { | |
| 485 // If the element is a parameter, we already have a | |
| 486 // HParameterValue for it. We cannot create another one because | |
| 487 // it could then have another name than the real parameter. And | |
| 488 // the other one would not know it is just a copy of the real | |
| 489 // parameter. | |
| 490 if (local is ParameterElement) { | |
| 491 assert(invariant(local, builder.parameters.containsKey(local), | |
| 492 message: "No local value for parameter $local in " | |
| 493 "${builder.parameters}.")); | |
| 494 return builder.parameters[local]; | |
| 495 } | |
| 496 | |
| 497 return builder.activationVariables.putIfAbsent(local, () { | |
| 498 JavaScriptBackend backend = builder.backend; | |
| 499 HLocalValue localValue = new HLocalValue(local, backend.nonNullType) | |
| 500 ..sourceInformation = sourceInformation; | |
| 501 builder.graph.entry.addAtExit(localValue); | |
| 502 return localValue; | |
| 503 }); | |
| 504 } | |
| 505 | |
| 506 Local getTypeVariableAsLocal(TypeVariableType type) { | |
| 507 return typeVariableLocals.putIfAbsent(type, () { | |
| 508 return new TypeVariableLocal(type, executableContext); | |
| 509 }); | |
| 510 } | |
| 511 | |
| 512 /** | |
| 513 * Sets the [element] to [value]. If the element is boxed or stored in a | |
| 514 * closure then the method generates code to set the value. | |
| 515 */ | |
| 516 void updateLocal(Local local, HInstruction value, | |
| 517 {SourceInformation sourceInformation}) { | |
| 518 if (value is HRef) { | |
| 519 HRef ref = value; | |
| 520 value = ref.value; | |
| 521 } | |
| 522 assert(!isStoredInClosureField(local)); | |
| 523 if (isAccessedDirectly(local)) { | |
| 524 directLocals[local] = value; | |
| 525 } else if (isBoxed(local)) { | |
| 526 BoxFieldElement redirect = redirectionMapping[local]; | |
| 527 // The box itself could be captured, or be local. A local variable that | |
| 528 // is captured will be boxed, but the box itself will be a local. | |
| 529 // Inside the closure the box is stored in a closure-field and cannot | |
| 530 // be accessed directly. | |
| 531 HInstruction box = readLocal(redirect.box); | |
| 532 builder.add(new HFieldSet(redirect, box, value) | |
| 533 ..sourceInformation = sourceInformation); | |
| 534 } else { | |
| 535 assert(isUsedInTryOrGenerator(local)); | |
| 536 HLocalValue localValue = getLocal(local); | |
| 537 builder.add(new HLocalSet(local, localValue, value) | |
| 538 ..sourceInformation = sourceInformation); | |
| 539 } | |
| 540 } | |
| 541 | |
| 542 /** | |
| 543 * This function, startLoop, must be called before visiting any children of | |
| 544 * the loop. In particular it needs to be called before executing the | |
| 545 * initializers. | |
| 546 * | |
| 547 * The [LocalsHandler] will make the boxes and updates at the right moment. | |
| 548 * The builder just needs to call [enterLoopBody] and [enterLoopUpdates] | |
| 549 * (for [ast.For] loops) at the correct places. For phi-handling | |
| 550 * [beginLoopHeader] and [endLoop] must also be called. | |
| 551 * | |
| 552 * The correct place for the box depends on the given loop. In most cases | |
| 553 * the box will be created when entering the loop-body: while, do-while, and | |
| 554 * for-in (assuming the call to [:next:] is inside the body) can always be | |
| 555 * constructed this way. | |
| 556 * | |
| 557 * Things are slightly more complicated for [ast.For] loops. If no declared | |
| 558 * loop variable is boxed then the loop-body approach works here too. If a | |
| 559 * loop-variable is boxed we need to introduce a new box for the | |
| 560 * loop-variable before we enter the initializer so that the initializer | |
| 561 * writes the values into the box. In any case we need to create the box | |
| 562 * before the condition since the condition could box the variable. | |
| 563 * Since the first box is created outside the actual loop we have a second | |
| 564 * location where a box is created: just before the updates. This is | |
| 565 * necessary since updates are considered to be part of the next iteration | |
| 566 * (and can again capture variables). | |
| 567 * | |
| 568 * For example the following Dart code prints 1 3 -- 3 4. | |
| 569 * | |
| 570 * var fs = []; | |
| 571 * for (var i = 0; i < 3; (f() { fs.add(f); print(i); i++; })()) { | |
| 572 * i++; | |
| 573 * } | |
| 574 * print("--"); | |
| 575 * for (var i = 0; i < 2; i++) fs[i](); | |
| 576 * | |
| 577 * We solve this by emitting the following code (only for [ast.For] loops): | |
| 578 * <Create box> <== move the first box creation outside the loop. | |
| 579 * <initializer>; | |
| 580 * loop-entry: | |
| 581 * if (!<condition>) goto loop-exit; | |
| 582 * <body> | |
| 583 * <update box> // create a new box and copy the captured loop-variables. | |
| 584 * <updates> | |
| 585 * goto loop-entry; | |
| 586 * loop-exit: | |
| 587 */ | |
| 588 void startLoop(ast.Node node) { | |
| 589 ClosureScope scopeData = closureData.capturingScopes[node]; | |
| 590 if (scopeData == null) return; | |
| 591 if (scopeData.hasBoxedLoopVariables()) { | |
| 592 // If there are boxed loop variables then we set up the box and | |
| 593 // redirections already now. This way the initializer can write its | |
| 594 // values into the box. | |
| 595 // For other loops the box will be created when entering the body. | |
| 596 enterScope(node, null); | |
| 597 } | |
| 598 } | |
| 599 | |
| 600 /** | |
| 601 * Create phis at the loop entry for local variables (ready for the values | |
| 602 * from the back edge). Populate the phis with the current values. | |
| 603 */ | |
| 604 void beginLoopHeader(HBasicBlock loopEntry) { | |
| 605 // Create a copy because we modify the map while iterating over it. | |
| 606 Map<Local, HInstruction> savedDirectLocals = | |
| 607 new Map<Local, HInstruction>.from(directLocals); | |
| 608 | |
| 609 JavaScriptBackend backend = builder.backend; | |
| 610 // Create phis for all elements in the definitions environment. | |
| 611 savedDirectLocals.forEach((Local local, HInstruction instruction) { | |
| 612 if (isAccessedDirectly(local)) { | |
| 613 // We know 'this' cannot be modified. | |
| 614 if (local != closureData.thisLocal) { | |
| 615 HPhi phi = | |
| 616 new HPhi.singleInput(local, instruction, backend.dynamicType); | |
| 617 loopEntry.addPhi(phi); | |
| 618 directLocals[local] = phi; | |
| 619 } else { | |
| 620 directLocals[local] = instruction; | |
| 621 } | |
| 622 } | |
| 623 }); | |
| 624 } | |
| 625 | |
| 626 void enterLoopBody(ast.Node node) { | |
| 627 ClosureScope scopeData = closureData.capturingScopes[node]; | |
| 628 if (scopeData == null) return; | |
| 629 // If there are no declared boxed loop variables then we did not create the | |
| 630 // box before the initializer and we have to create the box now. | |
| 631 if (!scopeData.hasBoxedLoopVariables()) { | |
| 632 enterScope(node, null); | |
| 633 } | |
| 634 } | |
| 635 | |
| 636 void enterLoopUpdates(ast.Node node) { | |
| 637 // If there are declared boxed loop variables then the updates might have | |
| 638 // access to the box and we must switch to a new box before executing the | |
| 639 // updates. | |
| 640 // In all other cases a new box will be created when entering the body of | |
| 641 // the next iteration. | |
| 642 ClosureScope scopeData = closureData.capturingScopes[node]; | |
| 643 if (scopeData == null) return; | |
| 644 if (scopeData.hasBoxedLoopVariables()) { | |
| 645 updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables); | |
| 646 } | |
| 647 } | |
| 648 | |
| 649 /** | |
| 650 * Goes through the phis created in beginLoopHeader entry and adds the | |
| 651 * input from the back edge (from the current value of directLocals) to them. | |
| 652 */ | |
| 653 void endLoop(HBasicBlock loopEntry) { | |
| 654 // If the loop has an aborting body, we don't update the loop | |
| 655 // phis. | |
| 656 if (loopEntry.predecessors.length == 1) return; | |
| 657 loopEntry.forEachPhi((HPhi phi) { | |
| 658 Local element = phi.sourceElement; | |
| 659 HInstruction postLoopDefinition = directLocals[element]; | |
| 660 phi.addInput(postLoopDefinition); | |
| 661 }); | |
| 662 } | |
| 663 | |
| 664 /** | |
| 665 * Merge [otherLocals] into this locals handler, creating phi-nodes when | |
| 666 * there is a conflict. | |
| 667 * If a phi node is necessary, it will use this handler's instruction as the | |
| 668 * first input, and the otherLocals instruction as the second. | |
| 669 */ | |
| 670 void mergeWith(LocalsHandler otherLocals, HBasicBlock joinBlock) { | |
| 671 // If an element is in one map but not the other we can safely | |
| 672 // ignore it. It means that a variable was declared in the | |
| 673 // block. Since variable declarations are scoped the declared | |
| 674 // variable cannot be alive outside the block. Note: this is only | |
| 675 // true for nodes where we do joins. | |
| 676 Map<Local, HInstruction> joinedLocals = new Map<Local, HInstruction>(); | |
| 677 JavaScriptBackend backend = builder.backend; | |
| 678 otherLocals.directLocals.forEach((Local local, HInstruction instruction) { | |
| 679 // We know 'this' cannot be modified. | |
| 680 if (local == closureData.thisLocal) { | |
| 681 assert(directLocals[local] == instruction); | |
| 682 joinedLocals[local] = instruction; | |
| 683 } else { | |
| 684 HInstruction mine = directLocals[local]; | |
| 685 if (mine == null) return; | |
| 686 if (identical(instruction, mine)) { | |
| 687 joinedLocals[local] = instruction; | |
| 688 } else { | |
| 689 HInstruction phi = new HPhi.manyInputs( | |
| 690 local, <HInstruction>[mine, instruction], backend.dynamicType); | |
| 691 joinBlock.addPhi(phi); | |
| 692 joinedLocals[local] = phi; | |
| 693 } | |
| 694 } | |
| 695 }); | |
| 696 directLocals = joinedLocals; | |
| 697 } | |
| 698 | |
| 699 /** | |
| 700 * When control flow merges, this method can be used to merge several | |
| 701 * localsHandlers into a new one using phis. The new localsHandler is | |
| 702 * returned. Unless it is also in the list, the current localsHandler is not | |
| 703 * used for its values, only for its declared variables. This is a way to | |
| 704 * exclude local values from the result when they are no longer in scope. | |
| 705 */ | |
| 706 LocalsHandler mergeMultiple( | |
| 707 List<LocalsHandler> localsHandlers, HBasicBlock joinBlock) { | |
| 708 assert(localsHandlers.length > 0); | |
| 709 if (localsHandlers.length == 1) return localsHandlers[0]; | |
| 710 Map<Local, HInstruction> joinedLocals = new Map<Local, HInstruction>(); | |
| 711 HInstruction thisValue = null; | |
| 712 JavaScriptBackend backend = builder.backend; | |
| 713 directLocals.forEach((Local local, HInstruction instruction) { | |
| 714 if (local != closureData.thisLocal) { | |
| 715 HPhi phi = new HPhi.noInputs(local, backend.dynamicType); | |
| 716 joinedLocals[local] = phi; | |
| 717 joinBlock.addPhi(phi); | |
| 718 } else { | |
| 719 // We know that "this" never changes, if it's there. | |
| 720 // Save it for later. While merging, there is no phi for "this", | |
| 721 // so we don't have to special case it in the merge loop. | |
| 722 thisValue = instruction; | |
| 723 } | |
| 724 }); | |
| 725 for (LocalsHandler handler in localsHandlers) { | |
| 726 handler.directLocals.forEach((Local local, HInstruction instruction) { | |
| 727 HPhi phi = joinedLocals[local]; | |
| 728 if (phi != null) { | |
| 729 phi.addInput(instruction); | |
| 730 } | |
| 731 }); | |
| 732 } | |
| 733 if (thisValue != null) { | |
| 734 // If there was a "this" for the scope, add it to the new locals. | |
| 735 joinedLocals[closureData.thisLocal] = thisValue; | |
| 736 } | |
| 737 | |
| 738 // Remove locals that are not in all handlers. | |
| 739 directLocals = new Map<Local, HInstruction>(); | |
| 740 joinedLocals.forEach((Local local, HInstruction instruction) { | |
| 741 if (local != closureData.thisLocal && | |
| 742 instruction.inputs.length != localsHandlers.length) { | |
| 743 joinBlock.removePhi(instruction); | |
| 744 } else { | |
| 745 directLocals[local] = instruction; | |
| 746 } | |
| 747 }); | |
| 748 return this; | |
| 749 } | |
| 750 } | |
| 751 | |
| 752 // Represents a single break/continue instruction. | 127 // Represents a single break/continue instruction. |
| 753 class JumpHandlerEntry { | 128 class JumpHandlerEntry { |
| 754 final HJump jumpInstruction; | 129 final HJump jumpInstruction; |
| 755 final LocalsHandler locals; | 130 final LocalsHandler locals; |
| 756 bool isBreak() => jumpInstruction is HBreak; | 131 bool isBreak() => jumpInstruction is HBreak; |
| 757 bool isContinue() => jumpInstruction is HContinue; | 132 bool isContinue() => jumpInstruction is HContinue; |
| 758 JumpHandlerEntry(this.jumpInstruction, this.locals); | 133 JumpHandlerEntry(this.jumpInstruction, this.locals); |
| 759 } | 134 } |
| 760 | 135 |
| 761 abstract class JumpHandler { | 136 abstract class JumpHandler { |
| (...skipping 271 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1033 /** | 408 /** |
| 1034 * This stack contains declaration elements of the functions being built | 409 * This stack contains declaration elements of the functions being built |
| 1035 * or inlined by this builder. | 410 * or inlined by this builder. |
| 1036 */ | 411 */ |
| 1037 final List<Element> sourceElementStack = <Element>[]; | 412 final List<Element> sourceElementStack = <Element>[]; |
| 1038 | 413 |
| 1039 LocalsHandler localsHandler; | 414 LocalsHandler localsHandler; |
| 1040 | 415 |
| 1041 HInstruction rethrowableException; | 416 HInstruction rethrowableException; |
| 1042 | 417 |
| 1043 HParameterValue lastAddedParameter; | |
| 1044 | |
| 1045 Map<ParameterElement, HInstruction> parameters = | |
| 1046 <ParameterElement, HInstruction>{}; | |
| 1047 | |
| 1048 Map<JumpTarget, JumpHandler> jumpTargets = <JumpTarget, JumpHandler>{}; | 418 Map<JumpTarget, JumpHandler> jumpTargets = <JumpTarget, JumpHandler>{}; |
| 1049 | 419 |
| 1050 /** | |
| 1051 * Variables stored in the current activation. These variables are | |
| 1052 * being updated in try/catch blocks, and should be | |
| 1053 * accessed indirectly through [HLocalGet] and [HLocalSet]. | |
| 1054 */ | |
| 1055 Map<Local, HLocalValue> activationVariables = <Local, HLocalValue>{}; | |
| 1056 | |
| 1057 // We build the Ssa graph by simulating a stack machine. | 420 // We build the Ssa graph by simulating a stack machine. |
| 1058 List<HInstruction> stack = <HInstruction>[]; | 421 List<HInstruction> stack = <HInstruction>[]; |
| 1059 | 422 |
| 1060 /// Returns `true` if the current element is an `async` function. | 423 /// Returns `true` if the current element is an `async` function. |
| 1061 bool get isBuildingAsyncFunction { | 424 bool get isBuildingAsyncFunction { |
| 1062 Element element = sourceElement; | 425 Element element = sourceElement; |
| 1063 return (element is FunctionElement && | 426 return (element is FunctionElement && |
| 1064 element.asyncMarker == AsyncMarker.ASYNC); | 427 element.asyncMarker == AsyncMarker.ASYNC); |
| 1065 } | 428 } |
| 1066 | 429 |
| 1067 // TODO(sigmund): make most args optional | 430 // TODO(sigmund): make most args optional |
| 1068 SsaBuilder( | 431 SsaBuilder( |
| 1069 this.target, | 432 this.target, |
| 1070 this.resolvedAst, | 433 this.resolvedAst, |
| 1071 this.context, | 434 this.context, |
| 1072 this.registry, | 435 this.registry, |
| 1073 JavaScriptBackend backend, | 436 JavaScriptBackend backend, |
| 1074 this.nativeEmitter, | 437 this.nativeEmitter, |
| 1075 SourceInformationStrategy sourceInformationFactory) | 438 SourceInformationStrategy sourceInformationFactory) |
| 1076 : this.compiler = backend.compiler, | 439 : this.compiler = backend.compiler, |
| 1077 this.infoReporter = backend.compiler.dumpInfoTask, | 440 this.infoReporter = backend.compiler.dumpInfoTask, |
| 1078 this.backend = backend, | 441 this.backend = backend, |
| 1079 this.constantSystem = backend.constantSystem, | 442 this.constantSystem = backend.constantSystem, |
| 1080 this.rti = backend.rti { | 443 this.rti = backend.rti { |
| 1081 assert(target.isImplementation); | 444 assert(target.isImplementation); |
| 1082 graph.element = target; | 445 graph.element = target; |
| 1083 localsHandler = new LocalsHandler(this, target, null); | |
| 1084 sourceElementStack.add(target); | 446 sourceElementStack.add(target); |
| 1085 sourceInformationBuilder = | 447 sourceInformationBuilder = |
| 1086 sourceInformationFactory.createBuilderForContext(resolvedAst); | 448 sourceInformationFactory.createBuilderForContext(resolvedAst); |
| 1087 graph.sourceInformation = | 449 graph.sourceInformation = |
| 1088 sourceInformationBuilder.buildVariableDeclaration(); | 450 sourceInformationBuilder.buildVariableDeclaration(); |
| 451 localsHandler = new LocalsHandler(this, target, null, compiler); |
| 1089 } | 452 } |
| 1090 | 453 |
| 1091 BackendHelpers get helpers => backend.helpers; | 454 BackendHelpers get helpers => backend.helpers; |
| 1092 | 455 |
| 1093 RuntimeTypesEncoder get rtiEncoder => backend.rtiEncoder; | 456 RuntimeTypesEncoder get rtiEncoder => backend.rtiEncoder; |
| 1094 | 457 |
| 1095 DiagnosticReporter get reporter => compiler.reporter; | 458 DiagnosticReporter get reporter => compiler.reporter; |
| 1096 | 459 |
| 1097 CoreClasses get coreClasses => compiler.coreClasses; | 460 CoreClasses get coreClasses => compiler.coreClasses; |
| 1098 | 461 |
| (...skipping 382 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1481 sourceElementStack.add(element.declaration); | 844 sourceElementStack.add(element.declaration); |
| 1482 var result = f(); | 845 var result = f(); |
| 1483 sourceInformationBuilder = oldSourceInformationBuilder; | 846 sourceInformationBuilder = oldSourceInformationBuilder; |
| 1484 sourceElementStack.removeLast(); | 847 sourceElementStack.removeLast(); |
| 1485 return result; | 848 return result; |
| 1486 }); | 849 }); |
| 1487 } | 850 } |
| 1488 | 851 |
| 1489 /** | 852 /** |
| 1490 * Return null so it is simple to remove the optional parameters completely | 853 * Return null so it is simple to remove the optional parameters completely |
| 1491 * from interop methods to match JavaScript semantics for ommitted arguments. | 854 * from interop methods to match JavaScript semantics for omitted arguments. |
| 1492 */ | 855 */ |
| 1493 HInstruction handleConstantForOptionalParameterJsInterop(Element parameter) => | 856 HInstruction handleConstantForOptionalParameterJsInterop(Element parameter) => |
| 1494 null; | 857 null; |
| 1495 | 858 |
| 1496 HInstruction handleConstantForOptionalParameter(ParameterElement parameter) { | 859 HInstruction handleConstantForOptionalParameter(ParameterElement parameter) { |
| 1497 ConstantValue constantValue = | 860 ConstantValue constantValue = |
| 1498 backend.constants.getConstantValue(parameter.constant); | 861 backend.constants.getConstantValue(parameter.constant); |
| 1499 assert(invariant(parameter, constantValue != null, | 862 assert(invariant(parameter, constantValue != null, |
| 1500 message: 'No constant computed for $parameter')); | 863 message: 'No constant computed for $parameter')); |
| 1501 return graph.addConstant(constantValue, compiler); | 864 return graph.addConstant(constantValue, compiler); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 1531 backend.constants.getConstantValueForNode(node, elements); | 894 backend.constants.getConstantValueForNode(node, elements); |
| 1532 assert(invariant(node, constantValue != null, | 895 assert(invariant(node, constantValue != null, |
| 1533 message: 'No constant computed for $node')); | 896 message: 'No constant computed for $node')); |
| 1534 return constantValue; | 897 return constantValue; |
| 1535 } | 898 } |
| 1536 | 899 |
| 1537 HInstruction addConstant(ast.Node node) { | 900 HInstruction addConstant(ast.Node node) { |
| 1538 return graph.addConstant(getConstantForNode(node), compiler); | 901 return graph.addConstant(getConstantForNode(node), compiler); |
| 1539 } | 902 } |
| 1540 | 903 |
| 1541 TypeMask cachedTypeOfThis; | |
| 1542 | |
| 1543 TypeMask getTypeOfThis() { | |
| 1544 TypeMask result = cachedTypeOfThis; | |
| 1545 if (result == null) { | |
| 1546 ThisLocal local = localsHandler.closureData.thisLocal; | |
| 1547 ClassElement cls = local.enclosingClass; | |
| 1548 ClassWorld classWorld = compiler.world; | |
| 1549 if (classWorld.isUsedAsMixin(cls)) { | |
| 1550 // If the enclosing class is used as a mixin, [:this:] can be | |
| 1551 // of the class that mixins the enclosing class. These two | |
| 1552 // classes do not have a subclass relationship, so, for | |
| 1553 // simplicity, we mark the type as an interface type. | |
| 1554 result = new TypeMask.nonNullSubtype(cls.declaration, compiler.world); | |
| 1555 } else { | |
| 1556 result = new TypeMask.nonNullSubclass(cls.declaration, compiler.world); | |
| 1557 } | |
| 1558 cachedTypeOfThis = result; | |
| 1559 } | |
| 1560 return result; | |
| 1561 } | |
| 1562 | |
| 1563 Map<Element, TypeMask> cachedTypesOfCapturedVariables = | |
| 1564 new Map<Element, TypeMask>(); | |
| 1565 | |
| 1566 TypeMask getTypeOfCapturedVariable(Element element) { | |
| 1567 assert(element.isField); | |
| 1568 return cachedTypesOfCapturedVariables.putIfAbsent(element, () { | |
| 1569 return TypeMaskFactory.inferredTypeForElement(element, compiler); | |
| 1570 }); | |
| 1571 } | |
| 1572 | |
| 1573 /** | 904 /** |
| 1574 * Documentation wanted -- johnniwinther | 905 * Documentation wanted -- johnniwinther |
| 1575 * | 906 * |
| 1576 * Invariant: [functionElement] must be an implementation element. | 907 * Invariant: [functionElement] must be an implementation element. |
| 1577 */ | 908 */ |
| 1578 HGraph buildMethod(FunctionElement functionElement) { | 909 HGraph buildMethod(FunctionElement functionElement) { |
| 1579 assert(invariant(functionElement, functionElement.isImplementation)); | 910 assert(invariant(functionElement, functionElement.isImplementation)); |
| 1580 graph.calledInLoop = compiler.world.isCalledInLoop(functionElement); | 911 graph.calledInLoop = compiler.world.isCalledInLoop(functionElement); |
| 1581 ast.FunctionExpression function = resolvedAst.node; | 912 ast.FunctionExpression function = resolvedAst.node; |
| 1582 assert(function != null); | 913 assert(function != null); |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1709 ConstructorBodyElementX origin = new ConstructorBodyElementX( | 1040 ConstructorBodyElementX origin = new ConstructorBodyElementX( |
| 1710 constructorResolvedAst, constructor.origin); | 1041 constructorResolvedAst, constructor.origin); |
| 1711 origin.applyPatch(patch); | 1042 origin.applyPatch(patch); |
| 1712 classElement.origin.addBackendMember(bodyElement.origin); | 1043 classElement.origin.addBackendMember(bodyElement.origin); |
| 1713 } | 1044 } |
| 1714 } | 1045 } |
| 1715 assert(bodyElement.isGenerativeConstructorBody); | 1046 assert(bodyElement.isGenerativeConstructorBody); |
| 1716 return bodyElement; | 1047 return bodyElement; |
| 1717 } | 1048 } |
| 1718 | 1049 |
| 1719 HParameterValue addParameter(Entity parameter, TypeMask type) { | |
| 1720 assert(inliningStack.isEmpty); | |
| 1721 HParameterValue result = new HParameterValue(parameter, type); | |
| 1722 if (lastAddedParameter == null) { | |
| 1723 graph.entry.addBefore(graph.entry.first, result); | |
| 1724 } else { | |
| 1725 graph.entry.addAfter(lastAddedParameter, result); | |
| 1726 } | |
| 1727 lastAddedParameter = result; | |
| 1728 return result; | |
| 1729 } | |
| 1730 | |
| 1731 /** | 1050 /** |
| 1732 * This method sets up the local state of the builder for inlining [function]. | 1051 * This method sets up the local state of the builder for inlining [function]. |
| 1733 * The arguments of the function are inserted into the [localsHandler]. | 1052 * The arguments of the function are inserted into the [localsHandler]. |
| 1734 * | 1053 * |
| 1735 * When inlining a function, [:return:] statements are not emitted as | 1054 * When inlining a function, [:return:] statements are not emitted as |
| 1736 * [HReturn] instructions. Instead, the value of a synthetic element is | 1055 * [HReturn] instructions. Instead, the value of a synthetic element is |
| 1737 * updated in the [localsHandler]. This function creates such an element and | 1056 * updated in the [localsHandler]. This function creates such an element and |
| 1738 * stores it in the [returnLocal] field. | 1057 * stores it in the [returnLocal] field. |
| 1739 */ | 1058 */ |
| 1740 void setupStateForInlining( | 1059 void setupStateForInlining( |
| 1741 FunctionElement function, List<HInstruction> compiledArguments, | 1060 FunctionElement function, List<HInstruction> compiledArguments, |
| 1742 {InterfaceType instanceType}) { | 1061 {InterfaceType instanceType}) { |
| 1743 ResolvedAst resolvedAst = function.resolvedAst; | 1062 ResolvedAst resolvedAst = function.resolvedAst; |
| 1744 assert(resolvedAst != null); | 1063 assert(resolvedAst != null); |
| 1745 localsHandler = new LocalsHandler(this, function, instanceType); | 1064 localsHandler = new LocalsHandler(this, function, instanceType, compiler); |
| 1746 localsHandler.closureData = | 1065 localsHandler.closureData = |
| 1747 compiler.closureToClassMapper.computeClosureToClassMapping(resolvedAst); | 1066 compiler.closureToClassMapper.computeClosureToClassMapping(resolvedAst); |
| 1748 returnLocal = new SyntheticLocal("result", function); | 1067 returnLocal = new SyntheticLocal("result", function); |
| 1749 localsHandler.updateLocal(returnLocal, graph.addConstantNull(compiler)); | 1068 localsHandler.updateLocal(returnLocal, graph.addConstantNull(compiler)); |
| 1750 | 1069 |
| 1751 inTryStatement = false; // TODO(lry): why? Document. | 1070 inTryStatement = false; // TODO(lry): why? Document. |
| 1752 | 1071 |
| 1753 int argumentIndex = 0; | 1072 int argumentIndex = 0; |
| 1754 if (function.isInstanceMember) { | 1073 if (function.isInstanceMember) { |
| 1755 localsHandler.updateLocal(localsHandler.closureData.thisLocal, | 1074 localsHandler.updateLocal(localsHandler.closureData.thisLocal, |
| (...skipping 3777 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 5533 | 4852 |
| 5534 var nativeBehavior = new native.NativeBehavior() | 4853 var nativeBehavior = new native.NativeBehavior() |
| 5535 ..sideEffects.setAllSideEffects(); | 4854 ..sideEffects.setAllSideEffects(); |
| 5536 | 4855 |
| 5537 DartType type = element.isConstructor | 4856 DartType type = element.isConstructor |
| 5538 ? element.enclosingClass.thisType | 4857 ? element.enclosingClass.thisType |
| 5539 : element.type.returnType; | 4858 : element.type.returnType; |
| 5540 // Native behavior effects here are similar to native/behavior.dart. | 4859 // Native behavior effects here are similar to native/behavior.dart. |
| 5541 // The return type is dynamic if we don't trust js-interop type | 4860 // The return type is dynamic if we don't trust js-interop type |
| 5542 // declarations. | 4861 // declarations. |
| 5543 nativeBehavior.typesReturned.add(compiler | 4862 nativeBehavior.typesReturned.add( |
| 5544 .options.trustJSInteropTypeAnnotations ? type : const DynamicType()); | 4863 compiler.options.trustJSInteropTypeAnnotations |
| 4864 ? type |
| 4865 : const DynamicType()); |
| 5545 | 4866 |
| 5546 // The allocation effects include the declared type if it is native (which | 4867 // The allocation effects include the declared type if it is native (which |
| 5547 // includes js interop types). | 4868 // includes js interop types). |
| 5548 if (type.element != null && backend.isNative(type.element)) { | 4869 if (type.element != null && backend.isNative(type.element)) { |
| 5549 nativeBehavior.typesInstantiated.add(type); | 4870 nativeBehavior.typesInstantiated.add(type); |
| 5550 } | 4871 } |
| 5551 | 4872 |
| 5552 // It also includes any other JS interop type if we don't trust the | 4873 // It also includes any other JS interop type if we don't trust the |
| 5553 // annotation or if is declared too broad. | 4874 // annotation or if is declared too broad. |
| 5554 if (!compiler.options.trustJSInteropTypeAnnotations || | 4875 if (!compiler.options.trustJSInteropTypeAnnotations || |
| (...skipping 2968 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8523 const _LoopTypeVisitor(); | 7844 const _LoopTypeVisitor(); |
| 8524 int visitNode(ast.Node node) => HLoopBlockInformation.NOT_A_LOOP; | 7845 int visitNode(ast.Node node) => HLoopBlockInformation.NOT_A_LOOP; |
| 8525 int visitWhile(ast.While node) => HLoopBlockInformation.WHILE_LOOP; | 7846 int visitWhile(ast.While node) => HLoopBlockInformation.WHILE_LOOP; |
| 8526 int visitFor(ast.For node) => HLoopBlockInformation.FOR_LOOP; | 7847 int visitFor(ast.For node) => HLoopBlockInformation.FOR_LOOP; |
| 8527 int visitDoWhile(ast.DoWhile node) => HLoopBlockInformation.DO_WHILE_LOOP; | 7848 int visitDoWhile(ast.DoWhile node) => HLoopBlockInformation.DO_WHILE_LOOP; |
| 8528 int visitAsyncForIn(ast.AsyncForIn node) => HLoopBlockInformation.FOR_IN_LOOP; | 7849 int visitAsyncForIn(ast.AsyncForIn node) => HLoopBlockInformation.FOR_IN_LOOP; |
| 8529 int visitSyncForIn(ast.SyncForIn node) => HLoopBlockInformation.FOR_IN_LOOP; | 7850 int visitSyncForIn(ast.SyncForIn node) => HLoopBlockInformation.FOR_IN_LOOP; |
| 8530 int visitSwitchStatement(ast.SwitchStatement node) => | 7851 int visitSwitchStatement(ast.SwitchStatement node) => |
| 8531 HLoopBlockInformation.SWITCH_CONTINUE_LOOP; | 7852 HLoopBlockInformation.SWITCH_CONTINUE_LOOP; |
| 8532 } | 7853 } |
| OLD | NEW |