OLD | NEW |
| (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 } | |
OLD | NEW |