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

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

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library type_graph_inferrer;
6
7 import 'dart:collection' show Queue, IterableBase;
8
9 import '../constants/expressions.dart';
10 import '../constants/values.dart';
11 import '../cps_ir/cps_ir_nodes.dart' as cps_ir
12 show Node;
13 import '../dart_types.dart'
14 show DartType,
15 FunctionType,
16 InterfaceType,
17 TypeKind;
18 import '../dart2jslib.dart'
19 show ClassWorld,
20 Compiler,
21 Constant,
22 FunctionConstant,
23 invariant,
24 TreeElementMapping;
25 import '../elements/elements.dart';
26 import '../native/native.dart' as native;
27 import '../tree/tree.dart' as ast
28 show DartString,
29 Node,
30 TryStatement;
31 import '../types/types.dart'
32 show ContainerTypeMask,
33 DictionaryTypeMask,
34 MapTypeMask,
35 TypeMask,
36 TypesInferrer,
37 ValueTypeMask;
38 import '../universe/universe.dart'
39 show Selector,
40 SideEffects,
41 TypedSelector;
42 import '../util/util.dart'
43 show ImmutableEmptySet,
44 Setlet,
45 Spannable;
46
47 import 'inferrer_visitor.dart'
48 show ArgumentsTypes,
49 TypeSystem;
50 import 'simple_types_inferrer.dart';
51
52 part 'closure_tracer.dart';
53 part 'list_tracer.dart';
54 part 'map_tracer.dart';
55 part 'node_tracer.dart';
56 part 'type_graph_nodes.dart';
57
58 bool _VERBOSE = false;
59 bool _PRINT_SUMMARY = false;
60 final _ANOMALY_WARN = false;
61
62 class TypeInformationSystem extends TypeSystem<TypeInformation> {
63 final Compiler compiler;
64 final ClassWorld classWorld;
65
66 /// [ElementTypeInformation]s for elements.
67 final Map<Element, TypeInformation> typeInformations =
68 new Map<Element, TypeInformation>();
69
70 /// [ListTypeInformation] for allocated lists.
71 final Map<ast.Node, TypeInformation> allocatedLists =
72 new Map<ast.Node, TypeInformation>();
73
74 /// [MapTypeInformation] for allocated Maps.
75 final Map<ast.Node, TypeInformation> allocatedMaps =
76 new Map<ast.Node, TypeInformation>();
77
78 /// Closures found during the analysis.
79 final Set<TypeInformation> allocatedClosures = new Set<TypeInformation>();
80
81 /// Cache of [ConcreteTypeInformation].
82 final Map<TypeMask, TypeInformation> concreteTypes =
83 new Map<TypeMask, TypeInformation>();
84
85 /// List of [TypeInformation]s allocated inside method bodies (calls,
86 /// narrowing, phis, and containers).
87 final List<TypeInformation> allocatedTypes = <TypeInformation>[];
88
89 TypeInformationSystem(Compiler compiler)
90 : this.compiler = compiler,
91 this.classWorld = compiler.world {
92 nonNullEmptyType = getConcreteTypeFor(const TypeMask.nonNullEmpty());
93 }
94
95 /// Used to group [TypeInformation] nodes by the element that triggered their
96 /// creation.
97 MemberTypeInformation _currentMember = null;
98 MemberTypeInformation get currentMember => _currentMember;
99
100 void withMember(MemberElement element, action) {
101 assert(invariant(element, _currentMember == null,
102 message: "Already constructing graph for $_currentMember."));
103 _currentMember = getInferredTypeOf(element);
104 action();
105 _currentMember = null;
106 }
107
108 TypeInformation nullTypeCache;
109 TypeInformation get nullType {
110 if (nullTypeCache != null) return nullTypeCache;
111 return nullTypeCache = getConcreteTypeFor(compiler.typesTask.nullType);
112 }
113
114 TypeInformation intTypeCache;
115 TypeInformation get intType {
116 if (intTypeCache != null) return intTypeCache;
117 return intTypeCache = getConcreteTypeFor(compiler.typesTask.intType);
118 }
119
120 TypeInformation uint32TypeCache;
121 TypeInformation get uint32Type {
122 if (uint32TypeCache != null) return uint32TypeCache;
123 return uint32TypeCache = getConcreteTypeFor(compiler.typesTask.uint32Type);
124 }
125
126 TypeInformation uint31TypeCache;
127 TypeInformation get uint31Type {
128 if (uint31TypeCache != null) return uint31TypeCache;
129 return uint31TypeCache = getConcreteTypeFor(compiler.typesTask.uint31Type);
130 }
131
132 TypeInformation positiveIntTypeCache;
133 TypeInformation get positiveIntType {
134 if (positiveIntTypeCache != null) return positiveIntTypeCache;
135 return positiveIntTypeCache =
136 getConcreteTypeFor(compiler.typesTask.positiveIntType);
137 }
138
139 TypeInformation doubleTypeCache;
140 TypeInformation get doubleType {
141 if (doubleTypeCache != null) return doubleTypeCache;
142 return doubleTypeCache = getConcreteTypeFor(compiler.typesTask.doubleType);
143 }
144
145 TypeInformation numTypeCache;
146 TypeInformation get numType {
147 if (numTypeCache != null) return numTypeCache;
148 return numTypeCache = getConcreteTypeFor(compiler.typesTask.numType);
149 }
150
151 TypeInformation boolTypeCache;
152 TypeInformation get boolType {
153 if (boolTypeCache != null) return boolTypeCache;
154 return boolTypeCache = getConcreteTypeFor(compiler.typesTask.boolType);
155 }
156
157 TypeInformation functionTypeCache;
158 TypeInformation get functionType {
159 if (functionTypeCache != null) return functionTypeCache;
160 return functionTypeCache =
161 getConcreteTypeFor(compiler.typesTask.functionType);
162 }
163
164 TypeInformation listTypeCache;
165 TypeInformation get listType {
166 if (listTypeCache != null) return listTypeCache;
167 return listTypeCache = getConcreteTypeFor(compiler.typesTask.listType);
168 }
169
170 TypeInformation constListTypeCache;
171 TypeInformation get constListType {
172 if (constListTypeCache != null) return constListTypeCache;
173 return constListTypeCache =
174 getConcreteTypeFor(compiler.typesTask.constListType);
175 }
176
177 TypeInformation fixedListTypeCache;
178 TypeInformation get fixedListType {
179 if (fixedListTypeCache != null) return fixedListTypeCache;
180 return fixedListTypeCache =
181 getConcreteTypeFor(compiler.typesTask.fixedListType);
182 }
183
184 TypeInformation growableListTypeCache;
185 TypeInformation get growableListType {
186 if (growableListTypeCache != null) return growableListTypeCache;
187 return growableListTypeCache =
188 getConcreteTypeFor(compiler.typesTask.growableListType);
189 }
190
191 TypeInformation mapTypeCache;
192 TypeInformation get mapType {
193 if (mapTypeCache != null) return mapTypeCache;
194 return mapTypeCache = getConcreteTypeFor(compiler.typesTask.mapType);
195 }
196
197 TypeInformation constMapTypeCache;
198 TypeInformation get constMapType {
199 if (constMapTypeCache != null) return constMapTypeCache;
200 return constMapTypeCache =
201 getConcreteTypeFor(compiler.typesTask.constMapType);
202 }
203
204 TypeInformation stringTypeCache;
205 TypeInformation get stringType {
206 if (stringTypeCache != null) return stringTypeCache;
207 return stringTypeCache = getConcreteTypeFor(compiler.typesTask.stringType);
208 }
209
210 TypeInformation typeTypeCache;
211 TypeInformation get typeType {
212 if (typeTypeCache != null) return typeTypeCache;
213 return typeTypeCache = getConcreteTypeFor(compiler.typesTask.typeType);
214 }
215
216 TypeInformation dynamicTypeCache;
217 TypeInformation get dynamicType {
218 if (dynamicTypeCache != null) return dynamicTypeCache;
219 return dynamicTypeCache =
220 getConcreteTypeFor(compiler.typesTask.dynamicType);
221 }
222
223 TypeInformation nonNullEmptyType;
224
225 TypeInformation stringLiteralType(ast.DartString value) {
226 return new StringLiteralTypeInformation(
227 value, compiler.typesTask.stringType);
228 }
229
230 TypeInformation computeLUB(TypeInformation firstType,
231 TypeInformation secondType) {
232 if (firstType == null) return secondType;
233 if (firstType == secondType) return firstType;
234 if (firstType == nonNullEmptyType) return secondType;
235 if (secondType == nonNullEmptyType) return firstType;
236 if (firstType == dynamicType || secondType == dynamicType) {
237 return dynamicType;
238 }
239 return getConcreteTypeFor(
240 firstType.type.union(secondType.type, classWorld));
241 }
242
243 bool selectorNeedsUpdate(TypeInformation info, Selector selector)
244 {
245 return info.type != selector.mask;
246 }
247
248 TypeInformation refineReceiver(Selector selector, TypeInformation receiver) {
249 if (receiver.type.isExact) return receiver;
250 TypeMask otherType = compiler.world.allFunctions.receiverType(selector);
251 // If this is refining to nullable subtype of `Object` just return
252 // the receiver. We know the narrowing is useless.
253 if (otherType.isNullable && otherType.containsAll(classWorld)) {
254 return receiver;
255 }
256 assert(TypeMask.assertIsNormalized(otherType, classWorld));
257 TypeInformation newType = new NarrowTypeInformation(receiver, otherType);
258 allocatedTypes.add(newType);
259 return newType;
260 }
261
262 TypeInformation narrowType(TypeInformation type,
263 DartType annotation,
264 {bool isNullable: true}) {
265 if (annotation.treatAsDynamic) return type;
266 if (annotation.isVoid) return nullType;
267 if (annotation.element == classWorld.objectClass && isNullable) return type;
268 TypeMask otherType;
269 if (annotation.isTypedef || annotation.isFunctionType) {
270 otherType = functionType.type;
271 } else if (annotation.isTypeVariable) {
272 // TODO(ngeoffray): Narrow to bound.
273 return type;
274 } else {
275 assert(annotation.isInterfaceType);
276 otherType = annotation.element == classWorld.objectClass
277 ? dynamicType.type.nonNullable()
278 : new TypeMask.nonNullSubtype(annotation.element, classWorld);
279 }
280 if (isNullable) otherType = otherType.nullable();
281 if (type.type.isExact) {
282 return type;
283 } else {
284 assert(TypeMask.assertIsNormalized(otherType, classWorld));
285 TypeInformation newType = new NarrowTypeInformation(type, otherType);
286 allocatedTypes.add(newType);
287 return newType;
288 }
289 }
290
291 ElementTypeInformation getInferredTypeOf(Element element) {
292 element = element.implementation;
293 return typeInformations.putIfAbsent(element, () {
294 return new ElementTypeInformation(element, this);
295 });
296 }
297
298 ConcreteTypeInformation getConcreteTypeFor(TypeMask mask) {
299 assert(mask != null);
300 return concreteTypes.putIfAbsent(mask, () {
301 return new ConcreteTypeInformation(mask);
302 });
303 }
304
305 String getInferredSignatureOf(FunctionElement function) {
306 ElementTypeInformation info = getInferredTypeOf(function);
307 FunctionElement impl = function.implementation;
308 FunctionSignature signature = impl.functionSignature;
309 var res = "";
310 signature.forEachParameter((Element parameter) {
311 TypeInformation type = getInferredTypeOf(parameter);
312 res += "${res.isEmpty ? '(' : ', '}${type.type} ${parameter.name}";
313 });
314 res += ") -> ${info.type}";
315 return res;
316 }
317
318 TypeInformation nonNullSubtype(ClassElement type) {
319 return getConcreteTypeFor(
320 new TypeMask.nonNullSubtype(type.declaration, classWorld));
321 }
322
323 TypeInformation nonNullSubclass(ClassElement type) {
324 return getConcreteTypeFor(
325 new TypeMask.nonNullSubclass(type.declaration, classWorld));
326 }
327
328 TypeInformation nonNullExact(ClassElement type) {
329 return getConcreteTypeFor(
330 new TypeMask.nonNullExact(type.declaration, classWorld));
331 }
332
333 TypeInformation nonNullEmpty() {
334 return nonNullEmptyType;
335 }
336
337 bool isNull(TypeInformation type) {
338 return type == nullType;
339 }
340
341 TypeInformation allocateList(TypeInformation type,
342 ast.Node node,
343 Element enclosing,
344 [TypeInformation elementType, int length]) {
345 bool isTypedArray =
346 compiler.typedDataClass != null &&
347 classWorld.isInstantiated(compiler.typedDataClass) &&
348 type.type.satisfies(compiler.typedDataClass, classWorld);
349 bool isConst = (type.type == compiler.typesTask.constListType);
350 bool isFixed = (type.type == compiler.typesTask.fixedListType) ||
351 isConst ||
352 isTypedArray;
353 bool isElementInferred = isConst || isTypedArray;
354
355 int inferredLength = isFixed ? length : null;
356 TypeMask elementTypeMask = isElementInferred
357 ? elementType.type
358 : dynamicType.type;
359 ContainerTypeMask mask = new ContainerTypeMask(
360 type.type, node, enclosing, elementTypeMask, inferredLength);
361 ElementInContainerTypeInformation element =
362 new ElementInContainerTypeInformation(currentMember, elementType);
363 element.inferred = isElementInferred;
364
365 allocatedTypes.add(element);
366 return allocatedLists[node] =
367 new ListTypeInformation(currentMember, mask, element, length);
368 }
369
370 TypeInformation allocateClosure(ast.Node node, Element element) {
371 TypeInformation result =
372 new ClosureTypeInformation(currentMember, node, element);
373 allocatedClosures.add(result);
374 return result;
375 }
376
377 TypeInformation allocateMap(ConcreteTypeInformation type,
378 ast.Node node,
379 Element element,
380 [List<TypeInformation> keyTypes,
381 List<TypeInformation> valueTypes]) {
382 assert(keyTypes.length == valueTypes.length);
383 bool isFixed = (type.type == compiler.typesTask.constMapType);
384
385 TypeMask keyType, valueType;
386 if (isFixed) {
387 keyType = keyTypes.fold(nonNullEmptyType.type,
388 (type, info) => type.union(info.type, classWorld));
389 valueType = valueTypes.fold(nonNullEmptyType.type,
390 (type, info) => type.union(info.type, classWorld));
391 } else {
392 keyType = valueType = dynamicType.type;
393 }
394 MapTypeMask mask = new MapTypeMask(type.type,
395 node,
396 element,
397 keyType,
398 valueType);
399
400 TypeInformation keyTypeInfo =
401 new KeyInMapTypeInformation(currentMember, null);
402 TypeInformation valueTypeInfo =
403 new ValueInMapTypeInformation(currentMember, null);
404 allocatedTypes.add(keyTypeInfo);
405 allocatedTypes.add(valueTypeInfo);
406
407 MapTypeInformation map =
408 new MapTypeInformation(currentMember, mask, keyTypeInfo, valueTypeInfo);
409
410 for (int i = 0; i < keyTypes.length; ++i) {
411 TypeInformation newType =
412 map.addEntryAssignment(keyTypes[i], valueTypes[i], true);
413 if (newType != null) allocatedTypes.add(newType);
414 }
415
416 // Shortcut: If we already have a first approximation of the key/value type,
417 // start propagating it early.
418 if (isFixed) map.markAsInferred();
419
420 allocatedMaps[node] = map;
421 return map;
422 }
423
424 Selector newTypedSelector(TypeInformation info, Selector selector) {
425 // Only type the selector if [info] is concrete, because the other
426 // kinds of [TypeInformation] have the empty type at this point of
427 // analysis.
428 return info.isConcrete
429 ? new TypedSelector(info.type, selector, classWorld)
430 : selector;
431 }
432
433 TypeInformation allocateDiamondPhi(TypeInformation firstInput,
434 TypeInformation secondInput) {
435 PhiElementTypeInformation result =
436 new PhiElementTypeInformation(currentMember, null, false, null);
437 result.addAssignment(firstInput);
438 result.addAssignment(secondInput);
439 allocatedTypes.add(result);
440 return result;
441 }
442
443 PhiElementTypeInformation _addPhi(ast.Node node,
444 Local variable,
445 inputType,
446 bool isLoop) {
447 PhiElementTypeInformation result =
448 new PhiElementTypeInformation(currentMember, node, isLoop, variable);
449 allocatedTypes.add(result);
450 result.addAssignment(inputType);
451 return result;
452 }
453
454 PhiElementTypeInformation allocatePhi(ast.Node node,
455 Local variable,
456 inputType) {
457 // Check if [inputType] is a phi for a local updated in
458 // the try/catch block [node]. If it is, no need to allocate a new
459 // phi.
460 if (inputType is PhiElementTypeInformation &&
461 inputType.branchNode == node &&
462 inputType.branchNode is ast.TryStatement) {
463 return inputType;
464 }
465 return _addPhi(node, variable, inputType, false);
466 }
467
468 PhiElementTypeInformation allocateLoopPhi(ast.Node node,
469 Local variable,
470 inputType) {
471 return _addPhi(node, variable, inputType, true);
472 }
473
474 TypeInformation simplifyPhi(ast.Node node,
475 Local variable,
476 PhiElementTypeInformation phiType) {
477 assert(phiType.branchNode == node);
478 if (phiType.assignments.length == 1) return phiType.assignments.first;
479 return phiType;
480 }
481
482 PhiElementTypeInformation addPhiInput(Local variable,
483 PhiElementTypeInformation phiType,
484 TypeInformation newType) {
485 phiType.addAssignment(newType);
486 return phiType;
487 }
488
489 TypeMask computeTypeMask(Iterable<TypeInformation> assignments) {
490 return joinTypeMasks(assignments.map((e) => e.type));
491 }
492
493 TypeMask joinTypeMasks(Iterable<TypeMask> masks) {
494 TypeMask newType = const TypeMask.nonNullEmpty();
495 for (TypeMask mask in masks) {
496 newType = newType.union(mask, classWorld);
497 }
498 return newType.containsAll(classWorld) ? dynamicType.type : newType;
499 }
500 }
501
502 /**
503 * A work queue for the inferrer. It filters out nodes that are tagged as
504 * [TypeInformation.doNotEnqueue], as well as ensures through
505 * [TypeInformation.inQueue] that a node is in the queue only once at
506 * a time.
507 */
508 class WorkQueue {
509 final Queue<TypeInformation> queue = new Queue<TypeInformation>();
510
511 void add(TypeInformation element) {
512 if (element.doNotEnqueue) return;
513 if (element.inQueue) return;
514 queue.addLast(element);
515 element.inQueue = true;
516 }
517
518 void addAll(Iterable<TypeInformation> all) {
519 all.forEach(add);
520 }
521
522 TypeInformation remove() {
523 TypeInformation element = queue.removeFirst();
524 element.inQueue = false;
525 return element;
526 }
527
528 bool get isEmpty => queue.isEmpty;
529
530 int get length => queue.length;
531 }
532
533 /**
534 * An inferencing engine that computes a call graph of
535 * [TypeInformation] nodes by visiting the AST of the application, and
536 * then does the inferencing on the graph.
537 *
538 */
539 class TypeGraphInferrerEngine
540 extends InferrerEngine<TypeInformation, TypeInformationSystem> {
541 final Map<Element, TypeInformation> defaultTypeOfParameter =
542 new Map<Element, TypeInformation>();
543 final List<CallSiteTypeInformation> allocatedCalls =
544 <CallSiteTypeInformation>[];
545 final WorkQueue workQueue = new WorkQueue();
546 final Element mainElement;
547 final Set<Element> analyzedElements = new Set<Element>();
548
549 /// The maximum number of times we allow a node in the graph to
550 /// change types. If a node reaches that limit, we give up
551 /// inferencing on it and give it the dynamic type.
552 final int MAX_CHANGE_COUNT = 6;
553
554 int overallRefineCount = 0;
555 int addedInGraph = 0;
556
557 TypeGraphInferrerEngine(Compiler compiler, this.mainElement)
558 : super(compiler, new TypeInformationSystem(compiler));
559
560 /**
561 * A set of selector names that [List] implements, that we know return
562 * their element type.
563 */
564 final Set<Selector> _returnsListElementTypeSet = new Set<Selector>.from(
565 <Selector>[
566 new Selector.getter('first', null),
567 new Selector.getter('last', null),
568 new Selector.getter('single', null),
569 new Selector.call('singleWhere', null, 1),
570 new Selector.call('elementAt', null, 1),
571 new Selector.index(),
572 new Selector.call('removeAt', null, 1),
573 new Selector.call('removeLast', null, 0)
574 ]);
575
576 bool returnsListElementType(Selector selector) {
577 return (selector.mask != null) &&
578 selector.mask.isContainer &&
579 _returnsListElementTypeSet.contains(selector.asUntyped);
580 }
581
582 bool returnsMapValueType(Selector selector) {
583 return (selector.mask != null) &&
584 selector.mask.isMap &&
585 selector.isIndex;
586 }
587
588 void analyzeListAndEnqueue(ListTypeInformation info) {
589 if (info.analyzed) return;
590 info.analyzed = true;
591
592 ListTracerVisitor tracer = new ListTracerVisitor(info, this);
593 bool succeeded = tracer.run();
594 if (!succeeded) return;
595
596 info.bailedOut = false;
597 info.elementType.inferred = true;
598 TypeMask fixedListType = compiler.typesTask.fixedListType;
599 if (info.originalType.forwardTo == fixedListType) {
600 info.checksGrowable = tracer.callsGrowableMethod;
601 }
602 tracer.assignments.forEach(info.elementType.addAssignment);
603 // Enqueue the list for later refinement
604 workQueue.add(info);
605 workQueue.add(info.elementType);
606 }
607
608 void analyzeMapAndEnqueue(MapTypeInformation info) {
609 if (info.analyzed) return;
610 info.analyzed = true;
611 MapTracerVisitor tracer = new MapTracerVisitor(info, this);
612
613 bool succeeded = tracer.run();
614 if (!succeeded) return;
615
616 info.bailedOut = false;
617 for (int i = 0; i < tracer.keyAssignments.length; ++i) {
618 TypeInformation newType = info.addEntryAssignment(
619 tracer.keyAssignments[i], tracer.valueAssignments[i]);
620 if (newType != null) workQueue.add(newType);
621 }
622 for (TypeInformation map in tracer.mapAssignments) {
623 workQueue.addAll(info.addMapAssignment(map));
624 }
625
626 info.markAsInferred();
627 workQueue.add(info.keyType);
628 workQueue.add(info.valueType);
629 workQueue.addAll(info.typeInfoMap.values);
630 workQueue.add(info);
631 }
632
633 void runOverAllElements() {
634 if (compiler.disableTypeInference) return;
635 if (compiler.verbose) {
636 compiler.progress.reset();
637 }
638 sortResolvedElements().forEach((Element element) {
639 if (compiler.shouldPrintProgress) {
640 compiler.log('Added $addedInGraph elements in inferencing graph.');
641 compiler.progress.reset();
642 }
643 // This also forces the creation of the [ElementTypeInformation] to ensure
644 // it is in the graph.
645 types.withMember(element, () => analyze(element, null));
646 });
647 compiler.log('Added $addedInGraph elements in inferencing graph.');
648
649 buildWorkQueue();
650 refine();
651
652 // Try to infer element types of lists and compute their escape information.
653 types.allocatedLists.values.forEach((ListTypeInformation info) {
654 analyzeListAndEnqueue(info);
655 });
656
657 // Try to infer the key and value types for maps and compute the values'
658 // escape information.
659 types.allocatedMaps.values.forEach((MapTypeInformation info) {
660 analyzeMapAndEnqueue(info);
661 });
662
663 Set<FunctionElement> bailedOutOn = new Set<FunctionElement>();
664
665 // Trace closures to potentially infer argument types.
666 types.allocatedClosures.forEach((info) {
667 void trace(Iterable<FunctionElement> elements,
668 ClosureTracerVisitor tracer) {
669 tracer.run();
670 if (!tracer.continueAnalyzing) {
671 elements.forEach((FunctionElement e) {
672 compiler.world.registerMightBePassedToApply(e);
673 if (_VERBOSE) print("traced closure $e as ${true} (bail)");
674 e.functionSignature.forEachParameter((parameter) {
675 types.getInferredTypeOf(parameter).giveUp(
676 this,
677 clearAssignments: false);
678 });
679 });
680 bailedOutOn.addAll(elements);
681 return;
682 }
683 elements
684 .where((e) => !bailedOutOn.contains(e))
685 .forEach((FunctionElement e) {
686 e.functionSignature.forEachParameter((parameter) {
687 var info = types.getInferredTypeOf(parameter);
688 info.maybeResume();
689 workQueue.add(info);
690 });
691 if (tracer.tracedType.mightBePassedToFunctionApply) {
692 compiler.world.registerMightBePassedToApply(e);
693 };
694 if (_VERBOSE) {
695 print("traced closure $e as "
696 "${compiler.world.getMightBePassedToApply(e)}");
697 }
698 });
699 }
700 if (info is ClosureTypeInformation) {
701 Iterable<FunctionElement> elements = [info.element];
702 trace(elements, new ClosureTracerVisitor(elements, info, this));
703 } else if (info is CallSiteTypeInformation) {
704 // We only are interested in functions here, as other targets
705 // of this closure call are not a root to trace but an intermediate
706 // for some other function.
707 Iterable<FunctionElement> elements = info.callees
708 .where((e) => e.isFunction);
709 trace(elements, new ClosureTracerVisitor(elements, info, this));
710 } else {
711 assert(info is ElementTypeInformation);
712 trace([info.element],
713 new StaticTearOffClosureTracerVisitor(info.element, info, this));
714 }
715 });
716
717 // Reset all nodes that use lists/maps that have been inferred, as well
718 // as nodes that use elements fetched from these lists/maps. The
719 // workset for a new run of the analysis will be these nodes.
720 Set<TypeInformation> seenTypes = new Set<TypeInformation>();
721 while (!workQueue.isEmpty) {
722 TypeInformation info = workQueue.remove();
723 if (seenTypes.contains(info)) continue;
724 // If the node cannot be reset, we do not need to update its users either.
725 if (!info.reset(this)) continue;
726 seenTypes.add(info);
727 workQueue.addAll(info.users);
728 }
729
730 workQueue.addAll(seenTypes);
731 refine();
732
733 if (_PRINT_SUMMARY) {
734 types.allocatedLists.values.forEach((ListTypeInformation info) {
735 print('${info.type} '
736 'for ${info.originalType.allocationNode} '
737 'at ${info.originalType.allocationElement} '
738 'after ${info.refineCount}');
739 });
740 types.allocatedMaps.values.forEach((MapTypeInformation info) {
741 print('${info.type} '
742 'for ${info.originalType.allocationNode} '
743 'at ${info.originalType.allocationElement} '
744 'after ${info.refineCount}');
745 });
746 types.allocatedClosures.forEach((TypeInformation info) {
747 if (info is ElementTypeInformation) {
748 print('${types.getInferredSignatureOf(info.element)} for '
749 '${info.element}');
750 } else if (info is ClosureTypeInformation) {
751 print('${types.getInferredSignatureOf(info.element)} for '
752 '${info.element}');
753 } else if (info is DynamicCallSiteTypeInformation) {
754 for (Element target in info.targets) {
755 if (target is FunctionElement) {
756 print('${types.getInferredSignatureOf(target)} for ${target}');
757 } else {
758 print('${types.getInferredTypeOf(target).type} for ${target}');
759 }
760 }
761 } else {
762 print('${info.type} for some unknown kind of closure');
763 }
764 });
765 analyzedElements.forEach((Element elem) {
766 TypeInformation type = types.getInferredTypeOf(elem);
767 print('${elem} :: ${type} from ${type.assignments} ');
768 });
769 }
770
771 compiler.log('Inferred $overallRefineCount types.');
772
773 processLoopInformation();
774 }
775
776 void analyze(Element element, ArgumentsTypes arguments) {
777 element = element.implementation;
778 if (analyzedElements.contains(element)) return;
779 analyzedElements.add(element);
780
781 SimpleTypeInferrerVisitor visitor =
782 new SimpleTypeInferrerVisitor(element, compiler, this);
783 TypeInformation type;
784 compiler.withCurrentElement(element, () {
785 type = visitor.run();
786 });
787 addedInGraph++;
788
789 if (element.isField) {
790 VariableElement fieldElement = element;
791 ast.Node node = fieldElement.node;
792 if (element.isFinal || element.isConst) {
793 // If [element] is final and has an initializer, we record
794 // the inferred type.
795 if (fieldElement.initializer != null) {
796 if (type is! ListTypeInformation && type is! MapTypeInformation) {
797 // For non-container types, the constant handler does
798 // constant folding that could give more precise results.
799 ConstantExpression constant = compiler.backend.constants
800 .getConstantForVariable(element);
801 if (constant != null) {
802 ConstantValue value = constant.value;
803 if (value.isFunction) {
804 FunctionConstantValue functionConstant = value;
805 type = types.allocateClosure(node, functionConstant.element);
806 } else {
807 // Although we might find a better type, we have to keep
808 // the old type around to ensure that we get a complete view
809 // of the type graph and do not drop any flow edges.
810 TypeMask refinedType = value.computeMask(compiler);
811 assert(TypeMask.assertIsNormalized(refinedType, classWorld));
812 type = new NarrowTypeInformation(type, refinedType);
813 types.allocatedTypes.add(type);
814 }
815 }
816 }
817 recordType(element, type);
818 } else if (!element.isInstanceMember) {
819 recordType(element, types.nullType);
820 }
821 } else if (fieldElement.initializer == null) {
822 // Only update types of static fields if there is no
823 // assignment. Instance fields are dealt with in the constructor.
824 if (Elements.isStaticOrTopLevelField(element)) {
825 recordTypeOfNonFinalField(node, element, type);
826 }
827 } else {
828 recordTypeOfNonFinalField(node, element, type);
829 }
830 if (Elements.isStaticOrTopLevelField(element) &&
831 fieldElement.initializer != null &&
832 !element.isConst) {
833 var argument = fieldElement.initializer;
834 // TODO(13429): We could do better here by using the
835 // constant handler to figure out if it's a lazy field or not.
836 if (argument.asSend() != null ||
837 (argument.asNewExpression() != null && !argument.isConst)) {
838 recordType(element, types.nullType);
839 }
840 }
841 } else {
842 recordReturnType(element, type);
843 }
844 }
845
846 void processLoopInformation() {
847 allocatedCalls.forEach((info) {
848 if (!info.inLoop) return;
849 if (info is StaticCallSiteTypeInformation) {
850 compiler.world.addFunctionCalledInLoop(info.calledElement);
851 } else if (info.selector.mask != null &&
852 !info.selector.mask.containsAll(compiler.world)) {
853 // For instance methods, we only register a selector called in a
854 // loop if it is a typed selector, to avoid marking too many
855 // methods as being called from within a loop. This cuts down
856 // on the code bloat.
857 info.targets.forEach(compiler.world.addFunctionCalledInLoop);
858 }
859 });
860 }
861
862 void refine() {
863 while (!workQueue.isEmpty) {
864 if (compiler.shouldPrintProgress) {
865 compiler.log('Inferred $overallRefineCount types.');
866 compiler.progress.reset();
867 }
868 TypeInformation info = workQueue.remove();
869 TypeMask oldType = info.type;
870 TypeMask newType = info.refine(this);
871 // Check that refinement has not accidentially changed the type.
872 assert(oldType == info.type);
873 if (info.abandonInferencing) info.doNotEnqueue = true;
874 if ((info.type = newType) != oldType) {
875 overallRefineCount++;
876 info.refineCount++;
877 if (info.refineCount > MAX_CHANGE_COUNT) {
878 if (_ANOMALY_WARN) {
879 print("ANOMALY WARNING: max refinement reached for $info");
880 }
881 info.giveUp(this);
882 info.type = info.refine(this);
883 info.doNotEnqueue = true;
884 }
885 workQueue.addAll(info.users);
886 if (info.hasStableType(this)) {
887 info.stabilize(this);
888 }
889 }
890 }
891 }
892
893 void buildWorkQueue() {
894 workQueue.addAll(types.typeInformations.values);
895 workQueue.addAll(types.allocatedTypes);
896 workQueue.addAll(types.allocatedClosures);
897 workQueue.addAll(allocatedCalls);
898 }
899
900 /**
901 * Update the assignments to parameters in the graph. [remove] tells
902 * wheter assignments must be added or removed. If [init] is false,
903 * parameters are added to the work queue.
904 */
905 void updateParameterAssignments(TypeInformation caller,
906 Element callee,
907 ArgumentsTypes arguments,
908 Selector selector,
909 {bool remove, bool addToQueue: true}) {
910 if (callee.name == Compiler.NO_SUCH_METHOD) return;
911 if (callee.isField) {
912 if (selector.isSetter) {
913 ElementTypeInformation info = types.getInferredTypeOf(callee);
914 if (remove) {
915 info.removeAssignment(arguments.positional[0]);
916 } else {
917 info.addAssignment(arguments.positional[0]);
918 }
919 if (addToQueue) workQueue.add(info);
920 }
921 } else if (callee.isGetter) {
922 return;
923 } else if (selector != null && selector.isGetter) {
924 // We are tearing a function off and thus create a closure.
925 assert(callee.isFunction);
926 MemberTypeInformation info = types.getInferredTypeOf(callee);
927 if (remove) {
928 info.closurizedCount--;
929 } else {
930 info.closurizedCount++;
931 if (Elements.isStaticOrTopLevel(callee)) {
932 types.allocatedClosures.add(info);
933 } else {
934 // We add the call-site type information here so that we
935 // can benefit from further refinement of the selector.
936 types.allocatedClosures.add(caller);
937 }
938 FunctionElement function = callee.implementation;
939 FunctionSignature signature = function.functionSignature;
940 signature.forEachParameter((Element parameter) {
941 ParameterTypeInformation info = types.getInferredTypeOf(parameter);
942 info.tagAsTearOffClosureParameter(this);
943 if (addToQueue) workQueue.add(info);
944 });
945 }
946 } else {
947 FunctionElement function = callee.implementation;
948 FunctionSignature signature = function.functionSignature;
949 int parameterIndex = 0;
950 bool visitingRequiredParameter = true;
951 signature.forEachParameter((Element parameter) {
952 if (signature.hasOptionalParameters &&
953 parameter == signature.firstOptionalParameter) {
954 visitingRequiredParameter = false;
955 }
956 TypeInformation type = visitingRequiredParameter
957 ? arguments.positional[parameterIndex]
958 : signature.optionalParametersAreNamed
959 ? arguments.named[parameter.name]
960 : parameterIndex < arguments.positional.length
961 ? arguments.positional[parameterIndex]
962 : null;
963 if (type == null) type = getDefaultTypeOfParameter(parameter);
964 TypeInformation info = types.getInferredTypeOf(parameter);
965 if (remove) {
966 info.removeAssignment(type);
967 } else {
968 info.addAssignment(type);
969 }
970 parameterIndex++;
971 if (addToQueue) workQueue.add(info);
972 });
973 }
974 }
975
976 /**
977 * Sets the type of a parameter's default value to [type]. If the global
978 * mapping in [defaultTypeOfParameter] already contains a type, it must be
979 * a [PlaceholderTypeInformation], which will be replaced. All its uses are
980 * updated.
981 */
982 void setDefaultTypeOfParameter(ParameterElement parameter,
983 TypeInformation type) {
984 assert(parameter.functionDeclaration.isImplementation);
985 TypeInformation existing = defaultTypeOfParameter[parameter];
986 defaultTypeOfParameter[parameter] = type;
987 TypeInformation info = types.getInferredTypeOf(parameter);
988 if (existing != null && existing is PlaceholderTypeInformation) {
989 // Replace references to [existing] to use [type] instead.
990 if (parameter.functionDeclaration.isInstanceMember) {
991 ParameterAssignments assignments = info.assignments;
992 assignments.replace(existing, type);
993 type.addUser(info);
994 } else {
995 List<TypeInformation> assignments = info.assignments;
996 for (int i = 0; i < assignments.length; i++) {
997 if (assignments[i] == existing) {
998 assignments[i] = type;
999 type.addUser(info);
1000 }
1001 }
1002 }
1003 } else {
1004 assert(existing == null);
1005 }
1006 }
1007
1008 /**
1009 * Returns the [TypeInformation] node for the default value of a parameter.
1010 * If this is queried before it is set by [setDefaultTypeOfParameter], a
1011 * [PlaceholderTypeInformation] is returned, which will later be replaced
1012 * by the actual node when [setDefaultTypeOfParameter] is called.
1013 *
1014 * Invariant: After graph construction, no [PlaceholderTypeInformation] nodes
1015 * should be present and a default type for each parameter should
1016 * exist.
1017 */
1018 TypeInformation getDefaultTypeOfParameter(Element parameter) {
1019 return defaultTypeOfParameter.putIfAbsent(parameter, () {
1020 return new PlaceholderTypeInformation(types.currentMember);
1021 });
1022 }
1023
1024 /**
1025 * Helper to inspect the [TypeGraphInferrer]'s state. To be removed by
1026 * TODO(johnniwinther) once synthetic parameters get their own default
1027 * values.
1028 */
1029 bool hasAlreadyComputedTypeOfParameterDefault(Element parameter) {
1030 TypeInformation seen = defaultTypeOfParameter[parameter];
1031 return (seen != null && seen is! PlaceholderTypeInformation);
1032 }
1033
1034 TypeInformation typeOfElement(Element element) {
1035 if (element is FunctionElement) return types.functionType;
1036 return types.getInferredTypeOf(element);
1037 }
1038
1039 TypeInformation returnTypeOfElement(Element element) {
1040 if (element is !FunctionElement) return types.dynamicType;
1041 return types.getInferredTypeOf(element);
1042 }
1043
1044 void recordTypeOfFinalField(Spannable node,
1045 Element analyzed,
1046 Element element,
1047 TypeInformation type) {
1048 types.getInferredTypeOf(element).addAssignment(type);
1049 }
1050
1051 void recordTypeOfNonFinalField(Spannable node,
1052 Element element,
1053 TypeInformation type) {
1054 types.getInferredTypeOf(element).addAssignment(type);
1055 }
1056
1057 void recordType(Element element, TypeInformation type) {
1058 types.getInferredTypeOf(element).addAssignment(type);
1059 }
1060
1061 void recordReturnType(Element element, TypeInformation type) {
1062 TypeInformation info = types.getInferredTypeOf(element);
1063 if (element.name == '==') {
1064 // Even if x.== doesn't return a bool, 'x == null' evaluates to 'false'.
1065 info.addAssignment(types.boolType);
1066 }
1067 // TODO(ngeoffray): Clean up. We do these checks because
1068 // [SimpleTypesInferrer] deals with two different inferrers.
1069 if (type == null) return;
1070 if (info.assignments.isEmpty) info.addAssignment(type);
1071 }
1072
1073 TypeInformation addReturnTypeFor(Element element,
1074 TypeInformation unused,
1075 TypeInformation newType) {
1076 TypeInformation type = types.getInferredTypeOf(element);
1077 // TODO(ngeoffray): Clean up. We do this check because
1078 // [SimpleTypesInferrer] deals with two different inferrers.
1079 if (element.isGenerativeConstructor) return type;
1080 type.addAssignment(newType);
1081 return type;
1082 }
1083
1084 TypeInformation registerCalledElement(Spannable node,
1085 Selector selector,
1086 Element caller,
1087 Element callee,
1088 ArgumentsTypes arguments,
1089 SideEffects sideEffects,
1090 bool inLoop) {
1091 CallSiteTypeInformation info = new StaticCallSiteTypeInformation(
1092 types.currentMember, node, caller, callee, selector, arguments,
1093 inLoop);
1094 info.addToGraph(this);
1095 allocatedCalls.add(info);
1096 updateSideEffects(sideEffects, selector, callee);
1097 return info;
1098 }
1099
1100 TypeInformation registerCalledSelector(ast.Node node,
1101 Selector selector,
1102 TypeInformation receiverType,
1103 Element caller,
1104 ArgumentsTypes arguments,
1105 SideEffects sideEffects,
1106 bool inLoop) {
1107 if (selector.isClosureCall) {
1108 return registerCalledClosure(
1109 node, selector, receiverType, caller, arguments, sideEffects, inLoop);
1110 }
1111
1112 compiler.world.allFunctions.filter(selector).forEach((callee) {
1113 updateSideEffects(sideEffects, selector, callee);
1114 });
1115
1116 CallSiteTypeInformation info = new DynamicCallSiteTypeInformation(
1117 types.currentMember, node, caller, selector, receiverType, arguments,
1118 inLoop);
1119
1120 info.addToGraph(this);
1121 allocatedCalls.add(info);
1122 return info;
1123 }
1124
1125 TypeInformation registerCalledClosure(ast.Node node,
1126 Selector selector,
1127 TypeInformation closure,
1128 Element caller,
1129 ArgumentsTypes arguments,
1130 SideEffects sideEffects,
1131 bool inLoop) {
1132 sideEffects.setDependsOnSomething();
1133 sideEffects.setAllSideEffects();
1134 CallSiteTypeInformation info = new ClosureCallSiteTypeInformation(
1135 types.currentMember, node, caller, selector, closure, arguments,
1136 inLoop);
1137 info.addToGraph(this);
1138 allocatedCalls.add(info);
1139 return info;
1140 }
1141
1142 // Sorts the resolved elements by size. We do this for this inferrer
1143 // to get the same results for [ListTracer] compared to the
1144 // [SimpleTypesInferrer].
1145 Iterable<Element> sortResolvedElements() {
1146 int max = 0;
1147 Map<int, Setlet<Element>> methodSizes = new Map<int, Setlet<Element>>();
1148 compiler.enqueuer.resolution.resolvedElements.forEach((AstElement element) {
1149 // TODO(ngeoffray): Not sure why the resolver would put a null
1150 // mapping.
1151 if (!compiler.enqueuer.resolution.hasBeenResolved(element)) return;
1152 TreeElementMapping mapping = element.resolvedAst.elements;
1153 element = element.implementation;
1154 if (element.impliesType) return;
1155 assert(invariant(element,
1156 element.isField ||
1157 element.isFunction ||
1158 element.isGenerativeConstructor ||
1159 element.isGetter ||
1160 element.isSetter,
1161 message: 'Unexpected element kind: ${element.kind}'));
1162 if (element.isAbstract) return;
1163 // Put the other operators in buckets by length, later to be added in
1164 // length order.
1165 int length = mapping.getSelectorCount();
1166 max = length > max ? length : max;
1167 Setlet<Element> set = methodSizes.putIfAbsent(
1168 length, () => new Setlet<Element>());
1169 set.add(element);
1170 });
1171
1172 List<Element> result = <Element>[];
1173 for (int i = 0; i <= max; i++) {
1174 Setlet<Element> set = methodSizes[i];
1175 if (set != null) result.addAll(set);
1176 }
1177 return result;
1178 }
1179
1180 void clear() {
1181 allocatedCalls.clear();
1182 defaultTypeOfParameter.clear();
1183 types.typeInformations.values.forEach((info) => info.clear());
1184 types.allocatedTypes.clear();
1185 types.concreteTypes.clear();
1186 types.allocatedClosures.clear();
1187 analyzedElements.clear();
1188 generativeConstructorsExposingThis.clear();
1189 }
1190
1191 Iterable<Element> getCallersOf(Element element) {
1192 if (compiler.disableTypeInference) {
1193 throw new UnsupportedError(
1194 "Cannot query the type inferrer when type inference is disabled.");
1195 }
1196 MemberTypeInformation info = types.getInferredTypeOf(element);
1197 return info.callers;
1198 }
1199
1200 /**
1201 * Returns the type of [element] when being called with [selector].
1202 */
1203 TypeInformation typeOfElementWithSelector(Element element,
1204 Selector selector) {
1205 if (element.name == Compiler.NO_SUCH_METHOD &&
1206 selector.name != element.name) {
1207 // An invocation can resolve to a [noSuchMethod], in which case
1208 // we get the return type of [noSuchMethod].
1209 return returnTypeOfElement(element);
1210 } else if (selector.isGetter) {
1211 if (element.isFunction) {
1212 // [functionType] is null if the inferrer did not run.
1213 return types.functionType == null
1214 ? types.dynamicType
1215 : types.functionType;
1216 } else if (element.isField) {
1217 return typeOfElement(element);
1218 } else if (Elements.isUnresolved(element)) {
1219 return types.dynamicType;
1220 } else {
1221 assert(element.isGetter);
1222 return returnTypeOfElement(element);
1223 }
1224 } else if (element.isGetter || element.isField) {
1225 assert(selector.isCall || selector.isSetter);
1226 return types.dynamicType;
1227 } else {
1228 return returnTypeOfElement(element);
1229 }
1230 }
1231
1232 void recordCapturedLocalRead(Local local) {}
1233
1234 void recordLocalUpdate(Local local, TypeInformation type) {}
1235 }
1236
1237 class TypeGraphInferrer implements TypesInferrer {
1238 TypeGraphInferrerEngine inferrer;
1239 final Compiler compiler;
1240 TypeGraphInferrer(Compiler this.compiler);
1241
1242 String get name => 'Graph inferrer';
1243
1244 void analyzeMain(Element main) {
1245 inferrer = new TypeGraphInferrerEngine(compiler, main);
1246 inferrer.runOverAllElements();
1247 }
1248
1249 TypeMask getReturnTypeOfElement(Element element) {
1250 if (compiler.disableTypeInference) return compiler.typesTask.dynamicType;
1251 // Currently, closure calls return dynamic.
1252 if (element is! FunctionElement) return compiler.typesTask.dynamicType;
1253 return inferrer.types.getInferredTypeOf(element).type;
1254 }
1255
1256 TypeMask getTypeOfElement(Element element) {
1257 if (compiler.disableTypeInference) return compiler.typesTask.dynamicType;
1258 // The inferrer stores the return type for a function, so we have to
1259 // be careful to not return it here.
1260 if (element is FunctionElement) return compiler.typesTask.functionType;
1261 return inferrer.types.getInferredTypeOf(element).type;
1262 }
1263
1264 TypeMask getTypeOfNode(Element owner, ast.Node node) {
1265 if (compiler.disableTypeInference) return compiler.typesTask.dynamicType;
1266 return inferrer.types.allocatedLists[node].type;
1267 }
1268
1269 bool isFixedArrayCheckedForGrowable(ast.Node node) {
1270 if (compiler.disableTypeInference) return true;
1271 ListTypeInformation info = inferrer.types.allocatedLists[node];
1272 return info.checksGrowable;
1273 }
1274
1275 TypeMask getTypeOfSelector(Selector selector) {
1276 if (compiler.disableTypeInference) return compiler.typesTask.dynamicType;
1277 // Bailout for closure calls. We're not tracking types of
1278 // closures.
1279 if (selector.isClosureCall) return compiler.typesTask.dynamicType;
1280 if (selector.isSetter || selector.isIndexSet) {
1281 return compiler.typesTask.dynamicType;
1282 }
1283 if (inferrer.returnsListElementType(selector)) {
1284 ContainerTypeMask mask = selector.mask;
1285 TypeMask elementType = mask.elementType;
1286 return elementType == null ? compiler.typesTask.dynamicType : elementType;
1287 }
1288 if (inferrer.returnsMapValueType(selector)) {
1289 MapTypeMask mask = selector.mask;
1290 TypeMask valueType = mask.valueType;
1291 return valueType == null ? compiler.typesTask.dynamicType
1292 : valueType;
1293 }
1294
1295 TypeMask result = const TypeMask.nonNullEmpty();
1296 Iterable<Element> elements = compiler.world.allFunctions.filter(selector);
1297 for (Element element in elements) {
1298 TypeMask type =
1299 inferrer.typeOfElementWithSelector(element, selector).type;
1300 result = result.union(type, compiler.world);
1301 }
1302 return result;
1303 }
1304
1305 Iterable<Element> getCallersOf(Element element) {
1306 if (compiler.disableTypeInference) {
1307 throw new UnsupportedError(
1308 "Cannot query the type inferrer when type inference is disabled.");
1309 }
1310 return inferrer.getCallersOf(element);
1311 }
1312
1313 bool isCalledOnce(Element element) {
1314 if (compiler.disableTypeInference) return false;
1315 MemberTypeInformation info = inferrer.types.getInferredTypeOf(element);
1316 return info.isCalledOnce();
1317 }
1318
1319 void clear() {
1320 inferrer.clear();
1321 }
1322 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698