| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 part of type_graph_inferrer; | 5 library container_tracer; |
| 6 |
| 7 import '../dart2jslib.dart' hide Selector, TypedSelector; |
| 8 import '../elements/elements.dart'; |
| 9 import '../tree/tree.dart'; |
| 10 import '../universe/universe.dart'; |
| 11 import '../util/util.dart' show Link; |
| 12 import 'simple_types_inferrer.dart' |
| 13 show InferrerEngine, InferrerVisitor, LocalsHandler, TypeMaskSystem; |
| 14 import '../types/types.dart'; |
| 15 import 'inferrer_visitor.dart'; |
| 6 | 16 |
| 7 /** | 17 /** |
| 8 * A set of selector names that [List] implements, that we know do not | 18 * A set of selector names that [List] implements, that we know do not |
| 9 * change the element type of the list, or let the list escape to code | 19 * change the element type of the list, or let the list escape to code |
| 10 * that might change the element type. | 20 * that might change the element type. |
| 11 */ | 21 */ |
| 12 Set<String> okSelectorsSet = new Set<String>.from( | 22 Set<String> okSelectorsSet = new Set<String>.from( |
| 13 const <String>[ | 23 const <String>[ |
| 14 // From Object. | 24 // From Object. |
| 15 '==', | 25 '==', |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 119 'getRange', | 129 'getRange', |
| 120 'asMap', | 130 'asMap', |
| 121 | 131 |
| 122 // From JSArray. | 132 // From JSArray. |
| 123 'checkMutable', | 133 'checkMutable', |
| 124 'checkGrowable', | 134 'checkGrowable', |
| 125 ]); | 135 ]); |
| 126 | 136 |
| 127 bool _VERBOSE = false; | 137 bool _VERBOSE = false; |
| 128 | 138 |
| 129 class ContainerTracerVisitor implements TypeInformationVisitor { | 139 class InferrerEngineForContainerTracer |
| 130 final ContainerTypeInformation container; | 140 implements MinimalInferrerEngine<TypeMask> { |
| 131 final TypeGraphInferrerEngine inferrer; | |
| 132 final Compiler compiler; | 141 final Compiler compiler; |
| 133 | 142 |
| 134 // The set of [TypeInformation] where the traced container could | 143 InferrerEngineForContainerTracer(this.compiler); |
| 135 // flow in, and operations done on them. | 144 |
| 136 final Set<TypeInformation> allUsers = new Set<TypeInformation>(); | 145 TypeMask typeOfElement(Element element) { |
| 137 | 146 return compiler.typesTask.getGuaranteedTypeOfElement(element); |
| 138 // The list of found assignments to the container. | 147 } |
| 139 final List<TypeInformation> assignments = <TypeInformation>[]; | 148 |
| 140 | 149 TypeMask returnTypeOfElement(Element element) { |
| 141 bool enableLengthTracking = true; | 150 return compiler.typesTask.getGuaranteedReturnTypeOfElement(element); |
| 151 } |
| 152 |
| 153 TypeMask returnTypeOfSelector(Selector selector) { |
| 154 return compiler.typesTask.getGuaranteedTypeOfSelector(selector); |
| 155 } |
| 156 |
| 157 TypeMask typeOfNode(Node node) { |
| 158 return compiler.typesTask.getGuaranteedTypeOfNode(null, node); |
| 159 } |
| 160 |
| 161 Iterable<Element> getCallersOf(Element element) { |
| 162 return compiler.typesTask.typesInferrer.getCallersOf(element); |
| 163 } |
| 164 |
| 165 void recordTypeOfNonFinalField(Node node, |
| 166 Element field, |
| 167 TypeMask type) {} |
| 168 } |
| 169 |
| 170 /** |
| 171 * Global analysis phase that traces container instantiations in order to |
| 172 * find their element type. |
| 173 */ |
| 174 class ContainerTracer extends CompilerTask { |
| 175 ContainerTracer(Compiler compiler) : super(compiler); |
| 176 |
| 177 String get name => 'List tracer'; |
| 178 |
| 179 bool analyze() { |
| 180 measure(() { |
| 181 if (compiler.disableTypeInference) return; |
| 182 TypesInferrer inferrer = compiler.typesTask.typesInferrer; |
| 183 InferrerEngineForContainerTracer engine = |
| 184 new InferrerEngineForContainerTracer(compiler); |
| 185 |
| 186 // Walk over all created [ContainerTypeMask]. |
| 187 inferrer.containerTypes.forEach((ContainerTypeMask mask) { |
| 188 // The element type has already been set for const containers. |
| 189 if (mask.elementType != null) return; |
| 190 new TracerForConcreteContainer(mask, this, compiler, engine).run(); |
| 191 }); |
| 192 }); |
| 193 } |
| 194 } |
| 195 |
| 196 /** |
| 197 * A tracer for a specific container. |
| 198 */ |
| 199 class TracerForConcreteContainer { |
| 200 final Compiler compiler; |
| 201 final ContainerTracer tracer; |
| 202 final InferrerEngineForContainerTracer inferrer; |
| 203 final ContainerTypeMask mask; |
| 204 |
| 205 final Node analyzedNode; |
| 206 final Element startElement; |
| 207 |
| 208 final List<Element> workList = <Element>[]; |
| 209 |
| 210 /** |
| 211 * A set of elements where this list might escape. |
| 212 */ |
| 213 final Set<Element> escapingElements = new Set<Element>(); |
| 214 |
| 215 /** |
| 216 * A set of selectors that both use and update the list, for example |
| 217 * [: list[0]++; :] or [: list[0] |= 42; :]. |
| 218 */ |
| 219 final Set<Selector> constraints = new Set<Selector>(); |
| 220 |
| 221 /** |
| 222 * A cache of setters that were already seen. Caching these |
| 223 * selectors avoid the filtering done in [addSettersToAnalysis]. |
| 224 */ |
| 225 final Set<Selector> seenSetterSelectors = new Set<Selector>(); |
| 226 |
| 227 static const int MAX_ANALYSIS_COUNT = 11; |
| 228 |
| 229 TypeMask potentialType; |
| 230 int potentialLength; |
| 231 bool isLengthTrackingDisabled = false; |
| 142 bool continueAnalyzing = true; | 232 bool continueAnalyzing = true; |
| 143 | 233 |
| 144 static const int MAX_ANALYSIS_COUNT = 11; | 234 TracerForConcreteContainer(ContainerTypeMask mask, |
| 145 final Set<Element> analyzedElements = new Set<Element>(); | 235 this.tracer, |
| 146 | 236 this.compiler, |
| 147 ContainerTracerVisitor(this.container, inferrer) | 237 this.inferrer) |
| 148 : this.inferrer = inferrer, this.compiler = inferrer.compiler; | 238 : analyzedNode = mask.allocationNode, |
| 239 startElement = mask.allocationElement, |
| 240 this.mask = mask; |
| 149 | 241 |
| 150 void run() { | 242 void run() { |
| 151 // Add the assignments found at allocation site. | 243 int analysisCount = 0; |
| 152 assignments.addAll(container.elementType.assignments); | 244 workList.add(startElement); |
| 153 | |
| 154 // Collect the [TypeInformation] where the container can flow in, | |
| 155 // as well as the operations done on all these [TypeInformation]s. | |
| 156 List<TypeInformation> workList = <TypeInformation>[]; | |
| 157 allUsers.add(container); | |
| 158 workList.add(container); | |
| 159 while (!workList.isEmpty) { | 245 while (!workList.isEmpty) { |
| 160 TypeInformation user = workList.removeLast(); | 246 if (workList.length + analysisCount > MAX_ANALYSIS_COUNT) { |
| 161 user.users.forEach((TypeInformation info) { | |
| 162 if (allUsers.contains(info)) return; | |
| 163 allUsers.add(info); | |
| 164 analyzedElements.add(info.owner); | |
| 165 if (info.reachedBy(user, inferrer)) { | |
| 166 workList.add(info); | |
| 167 } | |
| 168 }); | |
| 169 if (analyzedElements.length > MAX_ANALYSIS_COUNT) { | |
| 170 bailout('Too many users'); | 247 bailout('Too many users'); |
| 171 break; | 248 break; |
| 172 } | 249 } |
| 173 } | 250 Element currentElement = workList.removeLast().implementation; |
| 174 | 251 new ContainerTracerVisitor(currentElement, this).run(); |
| 175 if (continueAnalyzing) { | 252 if (!continueAnalyzing) break; |
| 176 for (TypeInformation info in allUsers) { | 253 analysisCount++; |
| 177 info.accept(this); | 254 } |
| 178 if (!continueAnalyzing) break; | 255 |
| 179 } | 256 if (!continueAnalyzing) { |
| 180 } | 257 if (mask.forwardTo == compiler.typesTask.fixedListType) { |
| 181 | 258 mask.length = potentialLength; |
| 182 ContainerTypeMask mask = container.type; | 259 } |
| 183 if (!enableLengthTracking | 260 mask.elementType = compiler.typesTask.dynamicType; |
| 184 && (mask.forwardTo != compiler.typesTask.fixedListType)) { | 261 return; |
| 185 mask.length = null; | 262 } |
| 186 } | 263 |
| 187 | 264 // [potentialType] can be null if we did not find any instruction |
| 188 TypeMask result = continueAnalyzing | 265 // that adds elements to the list. |
| 189 ? inferrer.types.computeTypeMask(assignments) | 266 if (potentialType == null) { |
| 190 : inferrer.types.dynamicType.type; | 267 if (_VERBOSE) { |
| 191 | 268 print('Found empty type for $analyzedNode $startElement'); |
| 192 mask.elementType = result; | 269 } |
| 270 mask.elementType = new TypeMask.nonNullEmpty(); |
| 271 return; |
| 272 } |
| 273 |
| 274 // Walk over the found constraints and update the type according |
| 275 // to the selectors of these constraints. |
| 276 for (Selector constraint in constraints) { |
| 277 assert(constraint.isOperator()); |
| 278 constraint = new TypedSelector(potentialType, constraint); |
| 279 potentialType = potentialType.union( |
| 280 inferrer.returnTypeOfSelector(constraint), compiler); |
| 281 } |
| 193 if (_VERBOSE) { | 282 if (_VERBOSE) { |
| 194 print('$result and ${mask.length} ' | 283 print('$potentialType and $potentialLength ' |
| 195 'for ${mask.allocationNode} ${mask.allocationElement}'); | 284 'for $analyzedNode $startElement'); |
| 196 } | 285 } |
| 197 } | 286 mask.elementType = potentialType; |
| 198 | 287 mask.length = potentialLength; |
| 199 void bailout(String reason) { | 288 } |
| 289 |
| 290 void disableLengthTracking() { |
| 291 if (mask.forwardTo == compiler.typesTask.fixedListType) { |
| 292 // Bogus update to a fixed list. |
| 293 return; |
| 294 } |
| 295 isLengthTrackingDisabled = true; |
| 296 potentialLength = null; |
| 297 } |
| 298 |
| 299 void setPotentialLength(int value) { |
| 300 if (isLengthTrackingDisabled) return; |
| 301 potentialLength = value; |
| 302 } |
| 303 |
| 304 void unionPotentialTypeWith(TypeMask newType) { |
| 305 assert(newType != null); |
| 306 potentialType = potentialType == null |
| 307 ? newType |
| 308 : newType.union(potentialType, compiler); |
| 309 if (potentialType == compiler.typesTask.dynamicType) { |
| 310 bailout('Moved to dynamic'); |
| 311 } |
| 312 } |
| 313 |
| 314 void addEscapingElement(element) { |
| 315 element = element.implementation; |
| 316 if (escapingElements.contains(element)) return; |
| 317 escapingElements.add(element); |
| 318 if (element.isField() || element.isGetter() || element.isFunction()) { |
| 319 for (Element e in inferrer.getCallersOf(element)) { |
| 320 addElementToAnalysis(e); |
| 321 } |
| 322 } else if (element.isParameter()) { |
| 323 addElementToAnalysis(element.enclosingElement); |
| 324 } else if (element.isFieldParameter()) { |
| 325 addEscapingElement(element.fieldElement); |
| 326 } |
| 327 } |
| 328 |
| 329 void addSettersToAnalysis(Selector selector) { |
| 330 assert(selector.isSetter()); |
| 331 if (seenSetterSelectors.contains(selector)) return; |
| 332 seenSetterSelectors.add(selector); |
| 333 for (var e in compiler.world.allFunctions.filter(selector)) { |
| 334 e = e.implementation; |
| 335 if (e.isField()) { |
| 336 addEscapingElement(e); |
| 337 } else { |
| 338 FunctionSignature signature = e.computeSignature(compiler); |
| 339 signature.forEachRequiredParameter((Element e) { |
| 340 addEscapingElement(e); |
| 341 }); |
| 342 } |
| 343 } |
| 344 } |
| 345 |
| 346 void addElementToAnalysis(Element element) { |
| 347 workList.add(element); |
| 348 } |
| 349 |
| 350 TypeMask bailout(String reason) { |
| 200 if (_VERBOSE) { | 351 if (_VERBOSE) { |
| 201 ContainerTypeMask mask = container.type; | 352 print('Bailout on $analyzedNode $startElement because of $reason'); |
| 202 print('Bailing out on ${mask.allocationNode} ${mask.allocationElement} ' | |
| 203 'because: $reason'); | |
| 204 } | 353 } |
| 205 continueAnalyzing = false; | 354 continueAnalyzing = false; |
| 206 enableLengthTracking = false; | 355 return compiler.typesTask.dynamicType; |
| 207 } | 356 } |
| 208 | 357 |
| 209 visitNarrowTypeInformation(NarrowTypeInformation info) {} | 358 bool couldBeTheList(resolved) { |
| 210 visitPhiElementTypeInformation(PhiElementTypeInformation info) {} | 359 if (resolved is Selector) { |
| 211 visitElementInContainerTypeInformation( | 360 return escapingElements.any((e) { |
| 212 ElementInContainerTypeInformation info) {} | 361 return e.isInstanceMember() && resolved.applies(e, compiler); |
| 213 visitContainerTypeInformation(ContainerTypeInformation info) {} | 362 }); |
| 214 visitConcreteTypeInformation(ConcreteTypeInformation info) {} | 363 } else if (resolved is Node) { |
| 215 | 364 return analyzedNode == resolved; |
| 216 visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { | 365 } else { |
| 217 bailout('Passed to a closure'); | 366 assert(resolved is Element); |
| 218 } | 367 return escapingElements.contains(resolved); |
| 219 | 368 } |
| 220 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { | 369 } |
| 221 analyzedElements.add(info.caller); | 370 |
| 222 Element called = info.calledElement; | 371 void recordConstraint(Selector selector) { |
| 223 if (called.isForeign(compiler) && called.name == const SourceString('JS')) { | 372 constraints.add(selector); |
| 224 bailout('Used in JS ${info.call}'); | 373 } |
| 225 } | 374 } |
| 226 } | 375 |
| 227 | 376 class ContainerTracerVisitor |
| 228 visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { | 377 extends InferrerVisitor<TypeMask, InferrerEngineForContainerTracer> { |
| 229 Selector selector = info.selector; | 378 final Element analyzedElement; |
| 379 final TracerForConcreteContainer tracer; |
| 380 final bool visitingClosure; |
| 381 |
| 382 ContainerTracerVisitor(element, tracer, [LocalsHandler<TypeMask> locals]) |
| 383 : super(element, tracer.inferrer, new TypeMaskSystem(tracer.compiler), |
| 384 tracer.compiler, locals), |
| 385 this.analyzedElement = element, |
| 386 this.tracer = tracer, |
| 387 visitingClosure = locals != null; |
| 388 |
| 389 bool escaping = false; |
| 390 bool visitingInitializers = false; |
| 391 |
| 392 void run() { |
| 393 compiler.withCurrentElement(analyzedElement, () { |
| 394 visit(analyzedElement.parseNode(compiler)); |
| 395 }); |
| 396 } |
| 397 |
| 398 /** |
| 399 * Executes [f] and returns whether it triggered the list to escape. |
| 400 */ |
| 401 bool visitAndCatchEscaping(Function f) { |
| 402 bool oldEscaping = escaping; |
| 403 escaping = false; |
| 404 f(); |
| 405 bool foundEscaping = escaping; |
| 406 escaping = oldEscaping; |
| 407 return foundEscaping; |
| 408 } |
| 409 |
| 410 /** |
| 411 * Visits the [arguments] of [callee], and records the parameters |
| 412 * that could hold the container as escaping. |
| 413 * |
| 414 * Returns whether the container escaped. |
| 415 */ |
| 416 bool visitArguments(Link<Node> arguments, /* Element or Selector */ callee) { |
| 417 List<int> indices = []; |
| 418 int index = 0; |
| 419 for (Node node in arguments) { |
| 420 if (visitAndCatchEscaping(() { visit(node); })) { |
| 421 indices.add(index); |
| 422 } |
| 423 index++; |
| 424 } |
| 425 if (!indices.isEmpty) { |
| 426 Iterable<Element> callees; |
| 427 if (callee is Element) { |
| 428 // No need to go further, we know the call will throw. |
| 429 if (callee.isErroneous()) return false; |
| 430 callees = [callee]; |
| 431 } else { |
| 432 assert(callee is Selector); |
| 433 callees = compiler.world.allFunctions.filter(callee); |
| 434 } |
| 435 for (var e in callees) { |
| 436 e = e.implementation; |
| 437 if (e.isField()) { |
| 438 tracer.bailout('Passed to a closure'); |
| 439 break; |
| 440 } |
| 441 FunctionSignature signature = e.computeSignature(compiler); |
| 442 index = 0; |
| 443 int parameterIndex = 0; |
| 444 signature.forEachRequiredParameter((Element parameter) { |
| 445 if (index < indices.length && indices[index] == parameterIndex) { |
| 446 tracer.addEscapingElement(parameter); |
| 447 index++; |
| 448 } |
| 449 parameterIndex++; |
| 450 }); |
| 451 if (index != indices.length) { |
| 452 tracer.bailout('Used in a named parameter or closure'); |
| 453 } |
| 454 } |
| 455 return true; |
| 456 } else { |
| 457 return false; |
| 458 } |
| 459 } |
| 460 |
| 461 TypeMask visitFunctionExpression(FunctionExpression node) { |
| 462 FunctionElement function = elements[node]; |
| 463 if (function != analyzedElement) { |
| 464 // Visiting a closure. |
| 465 LocalsHandler closureLocals = new LocalsHandler<TypeMask>.from( |
| 466 locals, node, useOtherTryBlock: false); |
| 467 new ContainerTracerVisitor(function, tracer, closureLocals).run(); |
| 468 return types.functionType; |
| 469 } else { |
| 470 // Visiting [analyzedElement]. |
| 471 FunctionSignature signature = function.computeSignature(compiler); |
| 472 signature.forEachParameter((element) { |
| 473 locals.update(element, inferrer.typeOfElement(element), node); |
| 474 }); |
| 475 visitingInitializers = true; |
| 476 visit(node.initializers); |
| 477 visitingInitializers = false; |
| 478 visit(node.body); |
| 479 return null; |
| 480 } |
| 481 } |
| 482 |
| 483 TypeMask visitLiteralList(LiteralList node) { |
| 484 if (node.isConst()) { |
| 485 return inferrer.typeOfNode(node); |
| 486 } |
| 487 if (tracer.couldBeTheList(node)) { |
| 488 escaping = true; |
| 489 int length = 0; |
| 490 for (Node element in node.elements.nodes) { |
| 491 tracer.unionPotentialTypeWith(visit(element)); |
| 492 length++; |
| 493 } |
| 494 tracer.setPotentialLength(length); |
| 495 } else { |
| 496 node.visitChildren(this); |
| 497 } |
| 498 return types.growableListType; |
| 499 } |
| 500 |
| 501 TypeMask visitSendSet(SendSet node) { |
| 502 bool isReceiver = visitAndCatchEscaping(() { |
| 503 visit(node.receiver); |
| 504 }); |
| 505 return handleSendSet(node, isReceiver); |
| 506 } |
| 507 |
| 508 TypeMask handleSendSet(SendSet node, bool isReceiver) { |
| 509 TypeMask rhsType; |
| 510 TypeMask indexType; |
| 511 |
| 512 Selector getterSelector = |
| 513 elements.getGetterSelectorInComplexSendSet(node); |
| 514 Selector operatorSelector = |
| 515 elements.getOperatorSelectorInComplexSendSet(node); |
| 516 Selector setterSelector = elements.getSelector(node); |
| 517 |
| 518 String op = node.assignmentOperator.source.stringValue; |
| 519 bool isIncrementOrDecrement = op == '++' || op == '--'; |
| 520 bool isIndexEscaping = false; |
| 521 bool isValueEscaping = false; |
| 522 if (isIncrementOrDecrement) { |
| 523 rhsType = types.intType; |
| 524 if (node.isIndex) { |
| 525 isIndexEscaping = visitAndCatchEscaping(() { |
| 526 indexType = visit(node.arguments.head); |
| 527 }); |
| 528 } |
| 529 } else if (node.isIndex) { |
| 530 isIndexEscaping = visitAndCatchEscaping(() { |
| 531 indexType = visit(node.arguments.head); |
| 532 }); |
| 533 isValueEscaping = visitAndCatchEscaping(() { |
| 534 rhsType = visit(node.arguments.tail.head); |
| 535 }); |
| 536 } else { |
| 537 isValueEscaping = visitAndCatchEscaping(() { |
| 538 rhsType = visit(node.arguments.head); |
| 539 }); |
| 540 } |
| 541 |
| 542 Element element = elements[node]; |
| 543 |
| 544 if (node.isIndex) { |
| 545 if (isReceiver) { |
| 546 if (op == '=') { |
| 547 tracer.unionPotentialTypeWith(rhsType); |
| 548 } else { |
| 549 tracer.recordConstraint(operatorSelector); |
| 550 } |
| 551 } else if (isIndexEscaping || isValueEscaping) { |
| 552 // If the index or value is escaping, iterate over all |
| 553 // potential targets, and mark their parameter as escaping. |
| 554 for (var e in compiler.world.allFunctions.filter(setterSelector)) { |
| 555 e = e.implementation; |
| 556 FunctionSignature signature = e.computeSignature(compiler); |
| 557 int index = 0; |
| 558 signature.forEachRequiredParameter((Element parameter) { |
| 559 if (index == 0 && isIndexEscaping) { |
| 560 tracer.addEscapingElement(parameter); |
| 561 } |
| 562 if (index == 1 && isValueEscaping) { |
| 563 tracer.addEscapingElement(parameter); |
| 564 } |
| 565 index++; |
| 566 }); |
| 567 } |
| 568 } |
| 569 } else if (isReceiver) { |
| 570 if (setterSelector.name == const SourceString('length')) { |
| 571 tracer.disableLengthTracking(); |
| 572 tracer.unionPotentialTypeWith(compiler.typesTask.nullType); |
| 573 } |
| 574 } else if (isValueEscaping) { |
| 575 if (element != null |
| 576 && element.isField() |
| 577 && setterSelector == null |
| 578 && !visitingInitializers) { |
| 579 // Initializer at declaration of a field. |
| 580 assert(analyzedElement.isField()); |
| 581 tracer.addEscapingElement(analyzedElement); |
| 582 } else if (element != null |
| 583 && (!element.isInstanceMember() || visitingInitializers)) { |
| 584 // A local, a static element, or a field in an initializer. |
| 585 tracer.addEscapingElement(element); |
| 586 } else { |
| 587 tracer.addSettersToAnalysis(setterSelector); |
| 588 } |
| 589 } |
| 590 |
| 591 TypeMask result; |
| 592 if (node.isPostfix) { |
| 593 // We don't check if [getterSelector] could be the container because |
| 594 // a list++ will always throw. |
| 595 result = inferrer.returnTypeOfSelector(getterSelector); |
| 596 } else if (op != '=') { |
| 597 // We don't check if [getterSelector] could be the container because |
| 598 // a list += 42 will always throw. |
| 599 result = inferrer.returnTypeOfSelector(operatorSelector); |
| 600 } else { |
| 601 if (isValueEscaping) { |
| 602 escaping = true; |
| 603 } |
| 604 result = rhsType; |
| 605 } |
| 606 |
| 607 if (Elements.isLocal(element)) { |
| 608 locals.update(element, result, node); |
| 609 } |
| 610 |
| 611 return result; |
| 612 } |
| 613 |
| 614 TypeMask visitSuperSend(Send node) { |
| 615 Element element = elements[node]; |
| 616 if (!node.isPropertyAccess) { |
| 617 visitArguments(node.arguments, element); |
| 618 } |
| 619 |
| 620 if (tracer.couldBeTheList(element)) { |
| 621 escaping = true; |
| 622 } |
| 623 |
| 624 if (element.isField()) { |
| 625 return inferrer.typeOfElement(element); |
| 626 } else if (element.isFunction()) { |
| 627 return inferrer.returnTypeOfElement(element); |
| 628 } else { |
| 629 return types.dynamicType; |
| 630 } |
| 631 } |
| 632 |
| 633 TypeMask visitStaticSend(Send node) { |
| 634 Element element = elements[node]; |
| 635 |
| 636 if (Elements.isGrowableListConstructorCall(element, node, compiler)) { |
| 637 visitArguments(node.arguments, element); |
| 638 if (tracer.couldBeTheList(node)) { |
| 639 escaping = true; |
| 640 } |
| 641 return inferrer.typeOfNode(node); |
| 642 } else if (Elements.isFixedListConstructorCall(element, node, compiler)) { |
| 643 visitArguments(node.arguments, element); |
| 644 if (tracer.couldBeTheList(node)) { |
| 645 tracer.unionPotentialTypeWith(types.nullType); |
| 646 escaping = true; |
| 647 LiteralInt length = node.arguments.head.asLiteralInt(); |
| 648 if (length != null) { |
| 649 tracer.setPotentialLength(length.value); |
| 650 } |
| 651 } |
| 652 return inferrer.typeOfNode(node); |
| 653 } else if (Elements.isFilledListConstructorCall(element, node, compiler)) { |
| 654 if (tracer.couldBeTheList(node)) { |
| 655 escaping = true; |
| 656 visit(node.arguments.head); |
| 657 TypeMask fillWithType = visit(node.arguments.tail.head); |
| 658 tracer.unionPotentialTypeWith(fillWithType); |
| 659 LiteralInt length = node.arguments.head.asLiteralInt(); |
| 660 if (length != null) { |
| 661 tracer.setPotentialLength(length.value); |
| 662 } |
| 663 } else { |
| 664 visitArguments(node.arguments, element); |
| 665 } |
| 666 return inferrer.typeOfNode(node); |
| 667 } |
| 668 |
| 669 bool isEscaping = visitArguments(node.arguments, element); |
| 670 |
| 671 if (element.isForeign(compiler)) { |
| 672 if (isEscaping) return tracer.bailout('Used in a JS'); |
| 673 } |
| 674 |
| 675 if (tracer.couldBeTheList(element)) { |
| 676 escaping = true; |
| 677 } |
| 678 |
| 679 if (element.isFunction() || element.isConstructor()) { |
| 680 return inferrer.returnTypeOfElement(element); |
| 681 } else { |
| 682 // Closure call or unresolved. |
| 683 return types.dynamicType; |
| 684 } |
| 685 } |
| 686 |
| 687 TypeMask visitGetterSend(Send node) { |
| 688 Element element = elements[node]; |
| 689 Selector selector = elements.getSelector(node); |
| 690 if (Elements.isStaticOrTopLevelField(element)) { |
| 691 if (tracer.couldBeTheList(element)) { |
| 692 escaping = true; |
| 693 } |
| 694 return inferrer.typeOfElement(element); |
| 695 } else if (Elements.isInstanceSend(node, elements)) { |
| 696 return visitDynamicSend(node); |
| 697 } else if (Elements.isStaticOrTopLevelFunction(element)) { |
| 698 return types.functionType; |
| 699 } else if (Elements.isErroneousElement(element)) { |
| 700 return types.dynamicType; |
| 701 } else if (Elements.isLocal(element)) { |
| 702 if (tracer.couldBeTheList(element)) { |
| 703 escaping = true; |
| 704 } |
| 705 return locals.use(element); |
| 706 } else { |
| 707 node.visitChildren(this); |
| 708 return types.dynamicType; |
| 709 } |
| 710 } |
| 711 |
| 712 TypeMask visitClosureSend(Send node) { |
| 713 assert(node.receiver == null); |
| 714 visit(node.selector); |
| 715 bool isEscaping = |
| 716 visitArguments(node.arguments, elements.getSelector(node)); |
| 717 |
| 718 if (isEscaping) return tracer.bailout('Passed to a closure'); |
| 719 return types.dynamicType; |
| 720 } |
| 721 |
| 722 TypeMask visitDynamicSend(Send node) { |
| 723 bool isReceiver = visitAndCatchEscaping(() { |
| 724 visit(node.receiver); |
| 725 }); |
| 726 return handleDynamicSend(node, isReceiver); |
| 727 } |
| 728 |
| 729 TypeMask handleDynamicSend(Send node, bool isReceiver) { |
| 730 Selector selector = elements.getSelector(node); |
| 230 String selectorName = selector.name.slowToString(); | 731 String selectorName = selector.name.slowToString(); |
| 231 if (allUsers.contains(info.receiver)) { | 732 if (isReceiver && !okSelectorsSet.contains(selectorName)) { |
| 232 if (!okSelectorsSet.contains(selectorName)) { | 733 if (selector.isCall() |
| 233 if (selector.isCall()) { | 734 && (selectorName == 'add' || selectorName == 'insert')) { |
| 234 int positionalLength = info.arguments.positional.length; | 735 TypeMask argumentType; |
| 235 if (selectorName == 'add') { | 736 if (node.arguments.isEmpty |
| 236 if (positionalLength == 1) { | 737 || (selectorName == 'insert' && node.arguments.tail.isEmpty)) { |
| 237 assignments.add(info.arguments.positional[0]); | 738 return tracer.bailout('Invalid "add" or "insert" call on a list'); |
| 238 } | 739 } |
| 239 } else if (selectorName == 'insert') { | 740 bool isEscaping = visitAndCatchEscaping(() { |
| 240 if (positionalLength == 2) { | 741 argumentType = visit(node.arguments.head); |
| 241 assignments.add(info.arguments.positional[1]); | 742 if (selectorName == 'insert') { |
| 242 } | 743 argumentType = visit(node.arguments.tail.head); |
| 243 } else { | |
| 244 bailout('Used in a not-ok selector'); | |
| 245 return; | |
| 246 } | 744 } |
| 247 } else if (selector.isIndexSet()) { | 745 }); |
| 248 assignments.add(info.arguments.positional[1]); | 746 if (isEscaping) { |
| 249 } else if (!selector.isIndex()) { | 747 return tracer.bailout('List containing itself'); |
| 250 bailout('Used in a not-ok selector'); | 748 } |
| 251 return; | 749 tracer.unionPotentialTypeWith(argumentType); |
| 252 } | 750 } else { |
| 253 } | 751 return tracer.bailout('Send with the node as receiver $node'); |
| 254 if (!doNotChangeLengthSelectorsSet.contains(selectorName)) { | 752 } |
| 255 enableLengthTracking = false; | 753 } else if (!node.isPropertyAccess) { |
| 256 } | 754 visitArguments(node.arguments, selector); |
| 257 if (selectorName == 'length' && selector.isSetter()) { | 755 } |
| 258 enableLengthTracking = false; | 756 if (isReceiver && !doNotChangeLengthSelectorsSet.contains(selectorName)) { |
| 259 assignments.add(inferrer.types.nullType); | 757 tracer.disableLengthTracking(); |
| 260 } | 758 } |
| 261 } else if (selector.isCall() | 759 if (tracer.couldBeTheList(selector)) { |
| 262 && !info.targets.every((element) => element.isFunction())) { | 760 escaping = true; |
| 263 bailout('Passed to a closure'); | 761 } |
| 264 return; | 762 return inferrer.returnTypeOfSelector(selector); |
| 265 } | 763 } |
| 266 } | 764 |
| 267 | 765 TypeMask visitReturn(Return node) { |
| 268 bool isClosure(Element element) { | 766 if (node.expression == null) { |
| 269 if (!element.isFunction()) return false; | 767 return types.nullType; |
| 270 Element outermost = element.getOutermostEnclosingMemberOrTopLevel(); | 768 } |
| 271 return outermost.declaration != element.declaration; | 769 |
| 272 } | 770 TypeMask type; |
| 273 | 771 bool isEscaping = visitAndCatchEscaping(() { |
| 274 visitElementTypeInformation(ElementTypeInformation info) { | 772 type = visit(node.expression); |
| 275 if (isClosure(info.element)) { | 773 }); |
| 276 bailout('Returned from a closure'); | 774 |
| 277 } | 775 if (isEscaping) { |
| 776 if (visitingClosure) { |
| 777 tracer.bailout('Return from closure'); |
| 778 } else { |
| 779 tracer.addEscapingElement(analyzedElement); |
| 780 } |
| 781 } |
| 782 return type; |
| 783 } |
| 784 |
| 785 TypeMask visitForIn(ForIn node) { |
| 786 visit(node.expression); |
| 787 Selector iteratorSelector = elements.getIteratorSelector(node); |
| 788 Selector currentSelector = elements.getCurrentSelector(node); |
| 789 |
| 790 TypeMask iteratorType = inferrer.returnTypeOfSelector(iteratorSelector); |
| 791 TypeMask currentType = inferrer.returnTypeOfSelector(currentSelector); |
| 792 |
| 793 // We nullify the type in case there is no element in the |
| 794 // iterable. |
| 795 currentType = currentType.nullable(); |
| 796 |
| 797 Node identifier = node.declaredIdentifier; |
| 798 Element element = elements[identifier]; |
| 799 if (Elements.isLocal(element)) { |
| 800 locals.update(element, currentType, node); |
| 801 } |
| 802 |
| 803 return handleLoop(node, () { |
| 804 visit(node.body); |
| 805 }); |
| 278 } | 806 } |
| 279 } | 807 } |
| OLD | NEW |