Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(595)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/inferrer/container_tracer.dart

Issue 25000002: Revert r27992: some browser tests fail. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698