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

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

Issue 24994003: Re-apply "Move the container tracer to the call graph inferrer.". (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 library container_tracer; 5 part of type_graph_inferrer;
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';
16 6
17 /** 7 /**
18 * A set of selector names that [List] implements, that we know do not 8 * A set of selector names that [List] implements, that we know do not
19 * change the element type of the list, or let the list escape to code 9 * change the element type of the list, or let the list escape to code
20 * that might change the element type. 10 * that might change the element type.
21 */ 11 */
22 Set<String> okSelectorsSet = new Set<String>.from( 12 Set<String> okSelectorsSet = new Set<String>.from(
23 const <String>[ 13 const <String>[
24 // From Object. 14 // From Object.
25 '==', 15 '==',
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 'getRange', 119 'getRange',
130 'asMap', 120 'asMap',
131 121
132 // From JSArray. 122 // From JSArray.
133 'checkMutable', 123 'checkMutable',
134 'checkGrowable', 124 'checkGrowable',
135 ]); 125 ]);
136 126
137 bool _VERBOSE = false; 127 bool _VERBOSE = false;
138 128
139 class InferrerEngineForContainerTracer 129 class ContainerTracerVisitor implements TypeInformationVisitor {
140 implements MinimalInferrerEngine<TypeMask> { 130 final ContainerTypeInformation container;
131 final TypeGraphInferrerEngine inferrer;
141 final Compiler compiler; 132 final Compiler compiler;
142 133
143 InferrerEngineForContainerTracer(this.compiler); 134 // The set of [TypeInformation] where the traced container could
135 // flow in, and operations done on them.
136 final Set<TypeInformation> allUsers = new Set<TypeInformation>();
144 137
145 TypeMask typeOfElement(Element element) { 138 // The list of found assignments to the container.
146 return compiler.typesTask.getGuaranteedTypeOfElement(element); 139 final List<TypeInformation> assignments = <TypeInformation>[];
147 }
148 140
149 TypeMask returnTypeOfElement(Element element) { 141 bool enableLengthTracking = true;
150 return compiler.typesTask.getGuaranteedReturnTypeOfElement(element); 142 bool continueAnalyzing = true;
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 143
227 static const int MAX_ANALYSIS_COUNT = 11; 144 static const int MAX_ANALYSIS_COUNT = 11;
145 final Set<Element> analyzedElements = new Set<Element>();
228 146
229 TypeMask potentialType; 147 ContainerTracerVisitor(this.container, inferrer)
230 int potentialLength; 148 : this.inferrer = inferrer, this.compiler = inferrer.compiler;
231 bool isLengthTrackingDisabled = false;
232 bool continueAnalyzing = true;
233
234 TracerForConcreteContainer(ContainerTypeMask mask,
235 this.tracer,
236 this.compiler,
237 this.inferrer)
238 : analyzedNode = mask.allocationNode,
239 startElement = mask.allocationElement,
240 this.mask = mask;
241 149
242 void run() { 150 void run() {
243 int analysisCount = 0; 151 // Add the assignments found at allocation site.
244 workList.add(startElement); 152 assignments.addAll(container.elementType.assignments);
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);
245 while (!workList.isEmpty) { 159 while (!workList.isEmpty) {
246 if (workList.length + analysisCount > MAX_ANALYSIS_COUNT) { 160 TypeInformation user = workList.removeLast();
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) {
247 bailout('Too many users'); 170 bailout('Too many users');
248 break; 171 break;
249 } 172 }
250 Element currentElement = workList.removeLast().implementation;
251 new ContainerTracerVisitor(currentElement, this).run();
252 if (!continueAnalyzing) break;
253 analysisCount++;
254 } 173 }
255 174
256 if (!continueAnalyzing) { 175 if (continueAnalyzing) {
257 if (mask.forwardTo == compiler.typesTask.fixedListType) { 176 for (TypeInformation info in allUsers) {
258 mask.length = potentialLength; 177 info.accept(this);
178 if (!continueAnalyzing) break;
259 } 179 }
260 mask.elementType = compiler.typesTask.dynamicType;
261 return;
262 } 180 }
263 181
264 // [potentialType] can be null if we did not find any instruction 182 ContainerTypeMask mask = container.type;
265 // that adds elements to the list. 183 if (!enableLengthTracking
266 if (potentialType == null) { 184 && (mask.forwardTo != compiler.typesTask.fixedListType)) {
267 if (_VERBOSE) { 185 mask.length = null;
268 print('Found empty type for $analyzedNode $startElement');
269 }
270 mask.elementType = new TypeMask.nonNullEmpty();
271 return;
272 } 186 }
273 187
274 // Walk over the found constraints and update the type according 188 TypeMask result = continueAnalyzing
275 // to the selectors of these constraints. 189 ? inferrer.types.computeTypeMask(assignments)
276 for (Selector constraint in constraints) { 190 : inferrer.types.dynamicType.type;
277 assert(constraint.isOperator()); 191
278 constraint = new TypedSelector(potentialType, constraint); 192 mask.elementType = result;
279 potentialType = potentialType.union(
280 inferrer.returnTypeOfSelector(constraint), compiler);
281 }
282 if (_VERBOSE) { 193 if (_VERBOSE) {
283 print('$potentialType and $potentialLength ' 194 print('$result and ${mask.length} '
284 'for $analyzedNode $startElement'); 195 'for ${mask.allocationNode} ${mask.allocationElement}');
285 }
286 mask.elementType = potentialType;
287 mask.length = potentialLength;
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 } 196 }
312 } 197 }
313 198
314 void addEscapingElement(element) { 199 void bailout(String reason) {
315 element = element.implementation; 200 if (_VERBOSE) {
316 if (escapingElements.contains(element)) return; 201 ContainerTypeMask mask = container.type;
317 escapingElements.add(element); 202 print('Bailing out on ${mask.allocationNode} ${mask.allocationElement} '
318 if (element.isField() || element.isGetter() || element.isFunction()) { 203 'because: $reason');
319 for (Element e in inferrer.getCallersOf(element)) { 204 }
320 addElementToAnalysis(e); 205 continueAnalyzing = false;
321 } 206 enableLengthTracking = false;
322 } else if (element.isParameter()) { 207 }
323 addElementToAnalysis(element.enclosingElement); 208
324 } else if (element.isFieldParameter()) { 209 visitNarrowTypeInformation(NarrowTypeInformation info) {}
325 addEscapingElement(element.fieldElement); 210 visitPhiElementTypeInformation(PhiElementTypeInformation info) {}
211 visitElementInContainerTypeInformation(
212 ElementInContainerTypeInformation info) {}
213 visitContainerTypeInformation(ContainerTypeInformation info) {}
214 visitConcreteTypeInformation(ConcreteTypeInformation info) {}
215
216 visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) {
217 bailout('Passed to a closure');
218 }
219
220 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) {
221 analyzedElements.add(info.caller);
222 Element called = info.calledElement;
223 if (called.isForeign(compiler) && called.name == const SourceString('JS')) {
224 bailout('Used in JS ${info.call}');
326 } 225 }
327 } 226 }
328 227
329 void addSettersToAnalysis(Selector selector) { 228 visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) {
330 assert(selector.isSetter()); 229 Selector selector = info.selector;
331 if (seenSetterSelectors.contains(selector)) return; 230 String selectorName = selector.name.slowToString();
332 seenSetterSelectors.add(selector); 231 if (allUsers.contains(info.receiver)) {
333 for (var e in compiler.world.allFunctions.filter(selector)) { 232 if (!okSelectorsSet.contains(selectorName)) {
334 e = e.implementation; 233 if (selector.isCall()) {
335 if (e.isField()) { 234 int positionalLength = info.arguments.positional.length;
336 addEscapingElement(e); 235 if (selectorName == 'add') {
337 } else { 236 if (positionalLength == 1) {
338 FunctionSignature signature = e.computeSignature(compiler); 237 assignments.add(info.arguments.positional[0]);
339 signature.forEachRequiredParameter((Element e) { 238 }
340 addEscapingElement(e); 239 } else if (selectorName == 'insert') {
341 }); 240 if (positionalLength == 2) {
241 assignments.add(info.arguments.positional[1]);
242 }
243 } else {
244 bailout('Used in a not-ok selector');
245 return;
246 }
247 } else if (selector.isIndexSet()) {
248 assignments.add(info.arguments.positional[1]);
249 } else if (!selector.isIndex()) {
250 bailout('Used in a not-ok selector');
251 return;
252 }
342 } 253 }
254 if (!doNotChangeLengthSelectorsSet.contains(selectorName)) {
255 enableLengthTracking = false;
256 }
257 if (selectorName == 'length' && selector.isSetter()) {
258 enableLengthTracking = false;
259 assignments.add(inferrer.types.nullType);
260 }
261 } else if (selector.isCall()
262 && !info.targets.every((element) => element.isFunction())) {
263 bailout('Passed to a closure');
264 return;
343 } 265 }
344 } 266 }
345 267
346 void addElementToAnalysis(Element element) { 268 bool isClosure(Element element) {
347 workList.add(element); 269 if (!element.isFunction()) return false;
270 Element outermost = element.getOutermostEnclosingMemberOrTopLevel();
271 return outermost.declaration != element.declaration;
348 } 272 }
349 273
350 TypeMask bailout(String reason) { 274 visitElementTypeInformation(ElementTypeInformation info) {
351 if (_VERBOSE) { 275 if (isClosure(info.element)) {
352 print('Bailout on $analyzedNode $startElement because of $reason'); 276 bailout('Returned from a closure');
353 }
354 continueAnalyzing = false;
355 return compiler.typesTask.dynamicType;
356 }
357
358 bool couldBeTheList(resolved) {
359 if (resolved is Selector) {
360 return escapingElements.any((e) {
361 return e.isInstanceMember() && resolved.applies(e, compiler);
362 });
363 } else if (resolved is Node) {
364 return analyzedNode == resolved;
365 } else {
366 assert(resolved is Element);
367 return escapingElements.contains(resolved);
368 } 277 }
369 } 278 }
370
371 void recordConstraint(Selector selector) {
372 constraints.add(selector);
373 }
374 } 279 }
375
376 class ContainerTracerVisitor
377 extends InferrerVisitor<TypeMask, InferrerEngineForContainerTracer> {
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);
731 String selectorName = selector.name.slowToString();
732 if (isReceiver && !okSelectorsSet.contains(selectorName)) {
733 if (selector.isCall()
734 && (selectorName == 'add' || selectorName == 'insert')) {
735 TypeMask argumentType;
736 if (node.arguments.isEmpty
737 || (selectorName == 'insert' && node.arguments.tail.isEmpty)) {
738 return tracer.bailout('Invalid "add" or "insert" call on a list');
739 }
740 bool isEscaping = visitAndCatchEscaping(() {
741 argumentType = visit(node.arguments.head);
742 if (selectorName == 'insert') {
743 argumentType = visit(node.arguments.tail.head);
744 }
745 });
746 if (isEscaping) {
747 return tracer.bailout('List containing itself');
748 }
749 tracer.unionPotentialTypeWith(argumentType);
750 } else {
751 return tracer.bailout('Send with the node as receiver $node');
752 }
753 } else if (!node.isPropertyAccess) {
754 visitArguments(node.arguments, selector);
755 }
756 if (isReceiver && !doNotChangeLengthSelectorsSet.contains(selectorName)) {
757 tracer.disableLengthTracking();
758 }
759 if (tracer.couldBeTheList(selector)) {
760 escaping = true;
761 }
762 return inferrer.returnTypeOfSelector(selector);
763 }
764
765 TypeMask visitReturn(Return node) {
766 if (node.expression == null) {
767 return types.nullType;
768 }
769
770 TypeMask type;
771 bool isEscaping = visitAndCatchEscaping(() {
772 type = visit(node.expression);
773 });
774
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 });
806 }
807 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698