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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/inferrer/type_graph_nodes.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 part of type_graph_inferrer;
6
7 /**
8 * Common class for all nodes in the graph. The current nodes are:
9 *
10 * - Concrete types
11 * - Elements
12 * - Call sites
13 * - Narrowing instructions
14 * - Phi instructions
15 * - Containers (for lists)
16 * - Type of the element in a container
17 *
18 * A node has a set of assignments and users. Assignments are used to
19 * compute the type of the node ([TypeInformation.computeType]). Users are
20 * added to the inferrer's work queue when the type of the node
21 * changes.
22 */
23 abstract class TypeInformation {
24 Set<TypeInformation> users;
25 var /* List|ParameterAssignments */ _assignments;
26
27 /// The type the inferrer has found for this [TypeInformation].
28 /// Initially empty.
29 TypeMask type = const TypeMask.nonNullEmpty();
30
31 /// The graph node of the member this [TypeInformation] node belongs to.
32 final MemberTypeInformation context;
33
34 /// The element this [TypeInformation] node belongs to.
35 MemberElement get contextMember => context == null ? null : context.element;
36
37 Iterable<TypeInformation> get assignments => _assignments;
38
39 /// We abandon inference in certain cases (complex cyclic flow, native
40 /// behaviours, etc.). In some case, we might resume inference in the
41 /// closure tracer, which is handled by checking whether [assignments] has
42 /// been set to [STOP_TRACKING_ASSIGNMENTS_MARKER].
43 bool abandonInferencing = false;
44 bool get mightResume =>
45 !identical(assignments, STOP_TRACKING_ASSIGNMENTS_MARKER);
46
47 /// Number of times this [TypeInformation] has changed type.
48 int refineCount = 0;
49
50 /// Whether this [TypeInformation] is currently in the inferrer's
51 /// work queue.
52 bool inQueue = false;
53
54 /// Used to disable enqueueing of type informations where we know that their
55 /// type will not change for other reasons than being stable. For example,
56 /// if inference is disabled for a type and it is hardwired to dynamic, this
57 /// is set to true to spare recomputing dynamic again and again. Changing this
58 /// to false should never change inference outcome, just make is slower.
59 bool doNotEnqueue = false;
60
61 /// Whether this [TypeInformation] has a stable [type] that will not
62 /// change.
63 bool isStable = false;
64
65 // TypeInformations are unique.
66 static int staticHashCode = 0;
67 final int hashCode = staticHashCode++;
68
69 bool get isConcrete => false;
70
71 TypeInformation(this.context) : _assignments = <TypeInformation>[],
72 users = new Setlet<TypeInformation>();
73
74 TypeInformation.noAssignments(this.context)
75 : _assignments = const <TypeInformation>[],
76 users = new Setlet<TypeInformation>();
77
78 TypeInformation.untracked()
79 : _assignments = const <TypeInformation>[],
80 users = const ImmutableEmptySet(),
81 context = null;
82
83 TypeInformation.withAssignments(this.context, this._assignments)
84 : users = new Setlet<TypeInformation>();
85
86 void addUser(TypeInformation user) {
87 assert(!user.isConcrete);
88 users.add(user);
89 }
90
91 void removeUser(TypeInformation user) {
92 assert(!user.isConcrete);
93 users.remove(user);
94 }
95
96 // The below is not a compile time constant to make it differentiable
97 // from other empty lists of [TypeInformation].
98 static final STOP_TRACKING_ASSIGNMENTS_MARKER = new List<TypeInformation>(0);
99
100 bool areAssignmentsTracked() {
101 return assignments != STOP_TRACKING_ASSIGNMENTS_MARKER;
102 }
103
104 void addAssignment(TypeInformation assignment) {
105 // Cheap one-level cycle detection.
106 if (assignment == this) return;
107 if (areAssignmentsTracked()) {
108 _assignments.add(assignment);
109 }
110 // Even if we abandon inferencing on this [TypeInformation] we
111 // need to collect the users, so that phases that track where
112 // elements flow in still work.
113 assignment.addUser(this);
114 }
115
116 void removeAssignment(TypeInformation assignment) {
117 if (!abandonInferencing || mightResume) {
118 _assignments.remove(assignment);
119 }
120 // We can have multiple assignments of the same [TypeInformation].
121 if (!assignments.contains(assignment)) {
122 assignment.removeUser(this);
123 }
124 }
125
126 TypeMask refine(TypeGraphInferrerEngine inferrer) {
127 return abandonInferencing ? safeType(inferrer) : computeType(inferrer);
128 }
129
130 /**
131 * Computes a new type for this [TypeInformation] node depending on its
132 * potentially updated inputs.
133 */
134 TypeMask computeType(TypeGraphInferrerEngine inferrer);
135
136 /**
137 * Returns an approximation for this [TypeInformation] node that is always
138 * safe to use. Used when abandoning inference on a node.
139 */
140 TypeMask safeType(TypeGraphInferrerEngine inferrer) {
141 return inferrer.types.dynamicType.type;
142 }
143
144 void giveUp(TypeGraphInferrerEngine inferrer, {bool clearAssignments: true}) {
145 abandonInferencing = true;
146 // Do not remove [this] as a user of nodes in [assignments],
147 // because our tracing analysis could be interested in tracing
148 // this node.
149 if (clearAssignments) _assignments = STOP_TRACKING_ASSIGNMENTS_MARKER;
150 // Do not remove users because our tracing analysis could be
151 // interested in tracing the users of this node.
152 }
153
154 void clear() {
155 _assignments = STOP_TRACKING_ASSIGNMENTS_MARKER;
156 users = const ImmutableEmptySet();
157 }
158
159 /// Reset the analysis of this node by making its type empty.
160
161 bool reset(TypeGraphInferrerEngine inferrer) {
162 if (abandonInferencing) return false;
163 type = const TypeMask.nonNullEmpty();
164 refineCount = 0;
165 return true;
166 }
167
168 accept(TypeInformationVisitor visitor);
169
170 /// The [Element] where this [TypeInformation] was created. May be
171 /// for some [TypeInformation] nodes, where we do not need to store
172 /// the information.
173 Element get owner => null;
174
175 /// Returns whether the type cannot change after it has been
176 /// inferred.
177 bool hasStableType(TypeGraphInferrerEngine inferrer) {
178 return !mightResume && assignments.every((e) => e.isStable);
179 }
180
181 void removeAndClearReferences(TypeGraphInferrerEngine inferrer) {
182 assignments.forEach((info) { info.removeUser(this); });
183 }
184
185 void stabilize(TypeGraphInferrerEngine inferrer) {
186 removeAndClearReferences(inferrer);
187 // Do not remove users because the tracing analysis could be interested
188 // in tracing the users of this node.
189 _assignments = STOP_TRACKING_ASSIGNMENTS_MARKER;
190 abandonInferencing = true;
191 isStable = true;
192 }
193
194 void maybeResume() {
195 if (!mightResume) return;
196 abandonInferencing = false;
197 doNotEnqueue = false;
198 }
199 }
200
201 abstract class ApplyableTypeInformation implements TypeInformation {
202 bool mightBePassedToFunctionApply = false;
203 }
204
205 /**
206 * Marker node used only during tree construction but not during actual type
207 * refinement.
208 *
209 * Currently, this is used to give a type to an optional parameter even before
210 * the corresponding default expression has been analyzed. See
211 * [getDefaultTypeOfParameter] and [setDefaultTypeOfParameter] for details.
212 */
213 class PlaceholderTypeInformation extends TypeInformation {
214 PlaceholderTypeInformation(MemberTypeInformation context) : super(context);
215
216 void accept(TypeInformationVisitor visitor) {
217 throw new UnsupportedError("Cannot visit placeholder");
218 }
219
220 TypeMask computeType(TypeGraphInferrerEngine inferrer) {
221 throw new UnsupportedError("Cannot refine placeholder");
222 }
223
224 toString() => "Placeholder [$hashCode]";
225 }
226
227 /**
228 * Parameters of instance functions behave differently than other
229 * elements because the inferrer may remove assignments. This happens
230 * when the receiver of a dynamic call site can be refined
231 * to a type where we know more about which instance method is being
232 * called.
233 */
234 class ParameterAssignments extends IterableBase<TypeInformation> {
235 final Map<TypeInformation, int> assignments =
236 new Map<TypeInformation, int>();
237
238 void remove(TypeInformation info) {
239 int existing = assignments[info];
240 if (existing == null) return;
241 if (existing == 1) {
242 assignments.remove(info);
243 } else {
244 assignments[info] = existing - 1;
245 }
246 }
247
248 void add(TypeInformation info) {
249 int existing = assignments[info];
250 if (existing == null) {
251 assignments[info] = 1;
252 } else {
253 assignments[info] = existing + 1;
254 }
255 }
256
257 void replace(TypeInformation old, TypeInformation replacement) {
258 int existing = assignments[old];
259 if (existing != null) {
260 int other = assignments[replacement];
261 if (other != null) existing += other;
262 assignments[replacement] = existing;
263 assignments.remove(old);
264 }
265 }
266
267 Iterator<TypeInformation> get iterator => assignments.keys.iterator;
268 Iterable<TypeInformation> where(Function f) => assignments.keys.where(f);
269
270 bool contains(TypeInformation info) => assignments.containsKey(info);
271
272 String toString() => assignments.keys.toList().toString();
273 }
274
275 /**
276 * A node representing a resolved element of the program. The kind of
277 * elements that need an [ElementTypeInformation] are:
278 *
279 * - Functions (including getters and setters)
280 * - Constructors (factory or generative)
281 * - Fields
282 * - Parameters
283 * - Local variables mutated in closures
284 *
285 * The [ElementTypeInformation] of a function and a constructor is its
286 * return type.
287 *
288 * Note that a few elements of these kinds must be treated specially,
289 * and they are dealt in [ElementTypeInformation.handleSpecialCases]:
290 *
291 * - Parameters of closures, [noSuchMethod] and [call] instance
292 * methods: we currently do not infer types for those.
293 *
294 * - Fields and parameters being assigned by synthesized calls done by
295 * the backend: we do not know what types the backend will use.
296 *
297 * - Native functions and fields: because native methods contain no Dart
298 * code, and native fields do not have Dart assignments, we just
299 * trust their type annotation.
300 *
301 */
302 abstract class ElementTypeInformation extends TypeInformation {
303 final Element element;
304
305 /// Marker to disable inference for closures in [handleSpecialCases].
306 bool disableInferenceForClosures = true;
307
308 factory ElementTypeInformation(Element element, TypeInformationSystem types) {
309 if (element.isParameter || element.isInitializingFormal) {
310 ParameterElement parameter = element;
311 if (parameter.functionDeclaration.isInstanceMember) {
312 return new ParameterTypeInformation._instanceMember(element, types);
313 }
314 return new ParameterTypeInformation._internal(element, types);
315 }
316 return new MemberTypeInformation._internal(element);
317 }
318
319 ElementTypeInformation._internal(MemberTypeInformation context, this.element)
320 : super(context);
321 ElementTypeInformation._withAssignments(MemberTypeInformation context,
322 this.element, assignments)
323 : super.withAssignments(context, assignments);
324 }
325
326 /**
327 * A node representing members in the broadest sense:
328 *
329 * - Functions
330 * - Constructors
331 * - Fields (also synthetic ones due to closures)
332 * - Local functions (closures)
333 *
334 * These should never be created directly but instead are constructed by
335 * the [ElementTypeInformation] factory.
336 */
337 class MemberTypeInformation extends ElementTypeInformation
338 with ApplyableTypeInformation {
339 TypedElement get element => super.element;
340
341 /**
342 * If [element] is a function, [closurizedCount] is the number of
343 * times it is closurized. The value gets updated while infering.
344 */
345 int closurizedCount = 0;
346
347 /**
348 * This map contains the callers of [element]. It stores all unique call sites
349 * to enable counting the global number of call sites of [element].
350 *
351 * A call site is either an AST [ast.Node], a [cps_ir.Node] or in the case of
352 * synthesized calls, an [Element] (see uses of [synthesizeForwardingCall]
353 * in [SimpleTypeInferrerVisitor]).
354 */
355 final Map<Element, Setlet<Spannable>> _callers = new Map<Element, Setlet>();
356
357 MemberTypeInformation._internal(Element element)
358 : super._internal(null, element);
359
360 void addCall(Element caller, Spannable node) {
361 assert(node is ast.Node || node is cps_ir.Node || node is Element);
362 _callers.putIfAbsent(caller, () => new Setlet()).add(node);
363 }
364
365 void removeCall(Element caller, node) {
366 Setlet calls = _callers[caller];
367 if (calls == null) return;
368 calls.remove(node);
369 if (calls.isEmpty) {
370 _callers.remove(caller);
371 }
372 }
373
374 Iterable<Element> get callers => _callers.keys;
375
376 bool isCalledOnce() {
377 int count = 0;
378 for (var set in _callers.values) {
379 count += set.length;
380 if (count > 1) return false;
381 }
382 return count == 1;
383 }
384
385 bool get isClosurized => closurizedCount > 0;
386
387 // Closurized methods never become stable to ensure that the information in
388 // [users] is accurate. The inference stops tracking users for stable types.
389 // Note that we only override the getter, the setter will still modify the
390 // state of the [isStable] field inhertied from [TypeInformation].
391 bool get isStable => super.isStable && !isClosurized;
392
393 TypeMask handleSpecialCases(TypeGraphInferrerEngine inferrer) {
394 if (element.isField &&
395 !inferrer.compiler.backend.canBeUsedForGlobalOptimizations(element)) {
396 // Do not infer types for fields being assigned by synthesized calls.
397 giveUp(inferrer);
398 return safeType(inferrer);
399 }
400 if (inferrer.isNativeElement(element)) {
401 // Use the type annotation as the type for native elements. We
402 // also give up on inferring to make sure this element never
403 // goes in the work queue.
404 giveUp(inferrer);
405 if (element.isField) {
406 return inferrer.typeOfNativeBehavior(
407 native.NativeBehavior.ofFieldLoad(element, inferrer.compiler)).type;
408 } else {
409 assert(element.isFunction ||
410 element.isGetter ||
411 element.isSetter);
412 TypedElement typedElement = element;
413 var elementType = typedElement.type;
414 if (elementType.kind != TypeKind.FUNCTION) {
415 return safeType(inferrer);
416 } else {
417 return inferrer.typeOfNativeBehavior(
418 native.NativeBehavior.ofMethod(element, inferrer.compiler)).type;
419 }
420 }
421 }
422
423 Compiler compiler = inferrer.compiler;
424 if (element.declaration == compiler.intEnvironment) {
425 giveUp(inferrer);
426 return compiler.typesTask.intType.nullable();
427 } else if (element.declaration == compiler.boolEnvironment) {
428 giveUp(inferrer);
429 return compiler.typesTask.boolType.nullable();
430 } else if (element.declaration == compiler.stringEnvironment) {
431 giveUp(inferrer);
432 return compiler.typesTask.stringType.nullable();
433 }
434 return null;
435 }
436
437 TypeMask potentiallyNarrowType(TypeMask mask,
438 TypeGraphInferrerEngine inferrer) {
439 Compiler compiler = inferrer.compiler;
440 if (!compiler.trustTypeAnnotations && !compiler.enableTypeAssertions) {
441 return mask;
442 }
443 if (element.isGenerativeConstructor ||
444 element.isSetter) {
445 return mask;
446 }
447 if (element.isField) {
448 return new TypeMaskSystem(compiler).narrowType(mask, element.type);
449 }
450 assert(element.isFunction ||
451 element.isGetter ||
452 element.isFactoryConstructor);
453
454 FunctionType type = element.type;
455 return new TypeMaskSystem(compiler).narrowType(mask, type.returnType);
456 }
457
458 TypeMask computeType(TypeGraphInferrerEngine inferrer) {
459 TypeMask special = handleSpecialCases(inferrer);
460 if (special != null) return potentiallyNarrowType(special, inferrer);
461 return potentiallyNarrowType(
462 inferrer.types.computeTypeMask(assignments), inferrer);
463 }
464
465 TypeMask safeType(TypeGraphInferrerEngine inferrer) {
466 return potentiallyNarrowType(super.safeType(inferrer), inferrer);
467 }
468
469 String toString() => 'MemberElement $element $type';
470
471 accept(TypeInformationVisitor visitor) {
472 return visitor.visitMemberTypeInformation(this);
473 }
474
475 Element get owner => element.outermostEnclosingMemberOrTopLevel;
476
477 bool hasStableType(TypeGraphInferrerEngine inferrer) {
478 // The number of assignments of non-final fields is
479 // not stable. Therefore such a field cannot be stable.
480 if (element.isField && !(element.isConst || element.isFinal)) {
481 return false;
482 }
483
484 if (element.isFunction) return false;
485
486 return super.hasStableType(inferrer);
487 }
488 }
489
490 /**
491 * A node representing parameters:
492 *
493 * - Parameters
494 * - Initializing formals
495 *
496 * These should never be created directly but instead are constructed by
497 * the [ElementTypeInformation] factory.
498 */
499 class ParameterTypeInformation extends ElementTypeInformation {
500 ParameterElement get element => super.element;
501
502 ParameterTypeInformation._internal(ParameterElement element,
503 TypeInformationSystem types)
504 : super._internal(types.getInferredTypeOf(element.functionDeclaration),
505 element) {
506 assert(!element.functionDeclaration.isInstanceMember);
507 }
508
509 ParameterTypeInformation._instanceMember(ParameterElement element,
510 TypeInformationSystem types)
511 : super._withAssignments(
512 types.getInferredTypeOf(element.functionDeclaration),
513 element,
514 new ParameterAssignments()) {
515 assert(element.functionDeclaration.isInstanceMember);
516 }
517
518 bool isTearOffClosureParameter = false;
519
520 void tagAsTearOffClosureParameter(TypeGraphInferrerEngine inferrer) {
521 assert(element.isParameter);
522 isTearOffClosureParameter = true;
523 }
524
525 // TODO(herhut): Cleanup into one conditional.
526 TypeMask handleSpecialCases(TypeGraphInferrerEngine inferrer) {
527 if (!inferrer.compiler.backend.canBeUsedForGlobalOptimizations(element)) {
528 // Do not infer types for fields and parameters being assigned
529 // by synthesized calls.
530 giveUp(inferrer);
531 return safeType(inferrer);
532 }
533
534 // The below do not apply to parameters of constructors, so skip
535 // initializing formals.
536 if (element.isInitializingFormal) return null;
537
538 FunctionElement function = element.functionDeclaration;
539 if ((isTearOffClosureParameter || function.isLocal) &&
540 disableInferenceForClosures) {
541 // Do not infer types for parameters of closures. We do not
542 // clear the assignments in case the closure is successfully
543 // traced.
544 giveUp(inferrer, clearAssignments: false);
545 return safeType(inferrer);
546 }
547 if (function.isInstanceMember &&
548 (function.name == Compiler.NO_SUCH_METHOD ||
549 (function.name == Compiler.CALL_OPERATOR_NAME &&
550 disableInferenceForClosures))) {
551 // Do not infer types for parameters of [noSuchMethod] and
552 // [call] instance methods.
553 giveUp(inferrer);
554 return safeType(inferrer);
555 }
556 if (function == inferrer.mainElement) {
557 // The implicit call to main is not seen by the inferrer,
558 // therefore we explicitly set the type of its parameters as
559 // dynamic.
560 // TODO(14566): synthesize a call instead to get the exact
561 // types.
562 giveUp(inferrer);
563 return safeType(inferrer);
564 }
565
566 return null;
567 }
568
569 TypeMask potentiallyNarrowType(TypeMask mask,
570 TypeGraphInferrerEngine inferrer) {
571 Compiler compiler = inferrer.compiler;
572 if (!compiler.trustTypeAnnotations) {
573 return mask;
574 }
575 // When type assertions are enabled (aka checked mode), we have to always
576 // ignore type annotations to ensure that the checks are actually inserted
577 // into the function body and retained until runtime.
578 assert(!compiler.enableTypeAssertions);
579 return new TypeMaskSystem(compiler).narrowType(mask, element.type);
580 }
581
582 TypeMask computeType(TypeGraphInferrerEngine inferrer) {
583 TypeMask special = handleSpecialCases(inferrer);
584 if (special != null) return special;
585 return potentiallyNarrowType(inferrer.types.computeTypeMask(assignments),
586 inferrer);
587 }
588
589 TypeMask safeType(TypeGraphInferrerEngine inferrer) {
590 return potentiallyNarrowType(super.safeType(inferrer), inferrer);
591 }
592
593 bool hasStableType(TypeGraphInferrerEngine inferrer) {
594 // The number of assignments of parameters of instance methods is
595 // not stable. Therefore such a parameter cannot be stable.
596 if (element.functionDeclaration.isInstanceMember) {
597 return false;
598 }
599 return super.hasStableType(inferrer);
600 }
601
602 accept(TypeInformationVisitor visitor) {
603 return visitor.visitParameterTypeInformation(this);
604 }
605
606 String toString() => 'ParameterElement $element $type';
607 }
608
609 /**
610 * A [CallSiteTypeInformation] is a call found in the AST, or a
611 * synthesized call for implicit calls in Dart (such as forwarding
612 * factories). The [call] field is a [ast.Node] for the former, and an
613 * [Element] for the latter.
614 *
615 * In the inferrer graph, [CallSiteTypeInformation] nodes do not have
616 * any assignment. They rely on the [caller] field for static calls,
617 * and [selector] and [receiver] fields for dynamic calls.
618 */
619 abstract class CallSiteTypeInformation extends TypeInformation
620 with ApplyableTypeInformation {
621 final Spannable call;
622 final Element caller;
623 final Selector selector;
624 final ArgumentsTypes arguments;
625 final bool inLoop;
626
627 CallSiteTypeInformation(
628 MemberTypeInformation context,
629 this.call,
630 this.caller,
631 this.selector,
632 this.arguments,
633 this.inLoop) : super.noAssignments(context);
634
635 String toString() => 'Call site $call $type';
636
637 /// Add [this] to the graph being computed by [engine].
638 void addToGraph(TypeGraphInferrerEngine engine);
639
640 /// Return an iterable over the targets of this call.
641 Iterable<Element> get callees;
642
643 Element get owner => caller;
644 }
645
646 class StaticCallSiteTypeInformation extends CallSiteTypeInformation {
647 final Element calledElement;
648
649 StaticCallSiteTypeInformation(
650 MemberTypeInformation context,
651 Spannable call,
652 Element enclosing,
653 this.calledElement,
654 Selector selector,
655 ArgumentsTypes arguments,
656 bool inLoop)
657 : super(context, call, enclosing, selector, arguments, inLoop);
658
659 void addToGraph(TypeGraphInferrerEngine inferrer) {
660 MemberTypeInformation callee =
661 inferrer.types.getInferredTypeOf(calledElement);
662 callee.addCall(caller, call);
663 callee.addUser(this);
664 if (arguments != null) {
665 arguments.forEach((info) => info.addUser(this));
666 }
667 inferrer.updateParameterAssignments(
668 this, calledElement, arguments, selector, remove: false,
669 addToQueue: false);
670 }
671
672 bool get isSynthesized {
673 // Some calls do not have a corresponding node, for example
674 // fowarding factory constructors, or synthesized super
675 // constructor calls. We synthesize these calls but do
676 // not create a selector for them.
677 return selector == null;
678 }
679
680 TypeMask computeType(TypeGraphInferrerEngine inferrer) {
681 if (isSynthesized) {
682 assert(arguments != null);
683 return inferrer.types.getInferredTypeOf(calledElement).type;
684 } else {
685 return inferrer.typeOfElementWithSelector(calledElement, selector).type;
686 }
687 }
688
689 Iterable<Element> get callees => [calledElement.implementation];
690
691 accept(TypeInformationVisitor visitor) {
692 return visitor.visitStaticCallSiteTypeInformation(this);
693 }
694
695 bool hasStableType(TypeGraphInferrerEngine inferrer) {
696 return inferrer.types.getInferredTypeOf(calledElement).isStable &&
697 (arguments == null || arguments.every((info) => info.isStable)) &&
698 super.hasStableType(inferrer);
699 }
700
701 void removeAndClearReferences(TypeGraphInferrerEngine inferrer) {
702 ElementTypeInformation callee =
703 inferrer.types.getInferredTypeOf(calledElement);
704 callee.removeUser(this);
705 if (arguments != null) {
706 arguments.forEach((info) => info.removeUser(this));
707 }
708 super.removeAndClearReferences(inferrer);
709 }
710 }
711
712 class DynamicCallSiteTypeInformation extends CallSiteTypeInformation {
713 final TypeInformation receiver;
714 /// Cached targets of this call.
715 Iterable<Element> targets;
716
717 DynamicCallSiteTypeInformation(
718 MemberTypeInformation context,
719 Spannable call,
720 Element enclosing,
721 Selector selector,
722 this.receiver,
723 ArgumentsTypes arguments,
724 bool inLoop)
725 : super(context, call, enclosing, selector, arguments, inLoop);
726
727 void addToGraph(TypeGraphInferrerEngine inferrer) {
728 assert(receiver != null);
729 Selector typedSelector = computeTypedSelector(inferrer);
730 targets = inferrer.compiler.world.allFunctions.filter(typedSelector);
731 receiver.addUser(this);
732 if (arguments != null) {
733 arguments.forEach((info) => info.addUser(this));
734 }
735 for (Element element in targets) {
736 MemberTypeInformation callee = inferrer.types.getInferredTypeOf(element);
737 callee.addCall(caller, call);
738 callee.addUser(this);
739 inferrer.updateParameterAssignments(
740 this, element, arguments, typedSelector, remove: false,
741 addToQueue: false);
742 }
743 }
744
745 Iterable<Element> get callees => targets.map((e) => e.implementation);
746
747 Selector computeTypedSelector(TypeGraphInferrerEngine inferrer) {
748 TypeMask receiverType = receiver.type;
749
750 if (selector.mask != receiverType) {
751 return receiverType == inferrer.compiler.typesTask.dynamicType
752 ? selector.asUntyped
753 : new TypedSelector(receiverType, selector, inferrer.classWorld);
754 } else {
755 return selector;
756 }
757 }
758
759 bool get targetsIncludeNoSuchMethod {
760 return targets.any((Element e) {
761 return e is FunctionElement &&
762 e.isInstanceMember &&
763 e.name == Compiler.NO_SUCH_METHOD;
764 });
765 }
766
767 /**
768 * We optimize certain operations on the [int] class because we know
769 * more about their return type than the actual Dart code. For
770 * example, we know int + int returns an int. The Dart code for
771 * [int.operator+] only says it returns a [num].
772 */
773 TypeInformation handleIntrisifiedSelector(Selector selector,
774 TypeGraphInferrerEngine inferrer) {
775 ClassWorld classWorld = inferrer.classWorld;
776 if (!classWorld.backend.intImplementation.isResolved) return null;
777 TypeMask emptyType = const TypeMask.nonNullEmpty();
778 if (selector.mask == null) return null;
779 if (!selector.mask.containsOnlyInt(classWorld)) {
780 return null;
781 }
782 if (!selector.isCall && !selector.isOperator) return null;
783 if (!arguments.named.isEmpty) return null;
784 if (arguments.positional.length > 1) return null;
785
786 ClassElement uint31Implementation = classWorld.backend.uint31Implementation;
787 bool isInt(info) => info.type.containsOnlyInt(classWorld);
788 bool isEmpty(info) => info.type == emptyType;
789 bool isUInt31(info) {
790 return info.type.satisfies(uint31Implementation, classWorld);
791 }
792 bool isPositiveInt(info) {
793 return info.type.satisfies(
794 classWorld.backend.positiveIntImplementation, classWorld);
795 }
796
797 String name = selector.name;
798 // We are optimizing for the cases that are not expressed in the
799 // Dart code, for example:
800 // int + int -> int
801 // uint31 | uint31 -> uint31
802 if (name == '*' || name == '+' || name == '%' || name == 'remainder' ||
803 name == '~/') {
804 if (isPositiveInt(receiver) &&
805 arguments.hasOnePositionalArgumentThatMatches(isPositiveInt)) {
806 return inferrer.types.positiveIntType;
807 } else if (arguments.hasOnePositionalArgumentThatMatches(isInt)) {
808 return inferrer.types.intType;
809 } else if (arguments.hasOnePositionalArgumentThatMatches(isEmpty)) {
810 return inferrer.types.nonNullEmptyType;
811 } else {
812 return null;
813 }
814 } else if (name == '|' || name == '^') {
815 if (isUInt31(receiver) &&
816 arguments.hasOnePositionalArgumentThatMatches(isUInt31)) {
817 return inferrer.types.uint31Type;
818 }
819 } else if (name == '>>') {
820 if (isUInt31(receiver)) {
821 return inferrer.types.uint31Type;
822 }
823 } else if (name == '&') {
824 if (isUInt31(receiver) ||
825 arguments.hasOnePositionalArgumentThatMatches(isUInt31)) {
826 return inferrer.types.uint31Type;
827 }
828 } else if (name == 'unary-') {
829 // The receiver being an int, the return value will also be an
830 // int.
831 return inferrer.types.intType;
832 } else if (name == '-') {
833 if (arguments.hasOnePositionalArgumentThatMatches(isInt)) {
834 return inferrer.types.intType;
835 } else if (arguments.hasOnePositionalArgumentThatMatches(isEmpty)) {
836 return inferrer.types.nonNullEmptyType;
837 }
838 return null;
839 } else if (name == 'abs') {
840 return arguments.hasNoArguments() ? inferrer.types.positiveIntType : null;
841 }
842 return null;
843 }
844
845 TypeMask computeType(TypeGraphInferrerEngine inferrer) {
846 Iterable<Element> oldTargets = targets;
847 Selector typedSelector = computeTypedSelector(inferrer);
848 inferrer.updateSelectorInTree(caller, call, typedSelector);
849
850 Compiler compiler = inferrer.compiler;
851 Selector selectorToUse = typedSelector.extendIfReachesAll(compiler);
852 bool canReachAll = compiler.enabledInvokeOn &&
853 (selectorToUse != typedSelector);
854
855 // If this call could potentially reach all methods that satisfy
856 // the untyped selector (through noSuchMethod's `Invocation`
857 // and a call to `delegate`), we iterate over all these methods to
858 // update their parameter types.
859 targets = compiler.world.allFunctions.filter(selectorToUse);
860 Iterable<Element> typedTargets = canReachAll
861 ? compiler.world.allFunctions.filter(typedSelector)
862 : targets;
863
864 // Walk over the found targets, and compute the joined union type mask
865 // for all these targets.
866 TypeMask newType = inferrer.types.joinTypeMasks(targets.map((element) {
867 if (!oldTargets.contains(element)) {
868 MemberTypeInformation callee =
869 inferrer.types.getInferredTypeOf(element);
870 callee.addCall(caller, call);
871 callee.addUser(this);
872 inferrer.updateParameterAssignments(
873 this, element, arguments, typedSelector, remove: false,
874 addToQueue: true);
875 }
876
877 // If [canReachAll] is true, then we are iterating over all
878 // targets that satisfy the untyped selector. We skip the return
879 // type of the targets that can only be reached through
880 // `Invocation.delegate`. Note that the `noSuchMethod` targets
881 // are included in [typedTargets].
882 if (canReachAll && !typedTargets.contains(element)) {
883 return const TypeMask.nonNullEmpty();
884 }
885
886 if (inferrer.returnsListElementType(typedSelector)) {
887 ContainerTypeMask mask = receiver.type;
888 return mask.elementType;
889 } else if (inferrer.returnsMapValueType(typedSelector)) {
890 if (typedSelector.mask.isDictionary &&
891 arguments.positional[0].type.isValue) {
892 DictionaryTypeMask mask = typedSelector.mask;
893 ValueTypeMask arg = arguments.positional[0].type;
894 String key = arg.value;
895 if (mask.typeMap.containsKey(key)) {
896 if (_VERBOSE) {
897 print("Dictionary lookup for $key yields ${mask.typeMap[key]}.");
898 }
899 return mask.typeMap[key];
900 } else {
901 // The typeMap is precise, so if we do not find the key, the lookup
902 // will be [null] at runtime.
903 if (_VERBOSE) {
904 print("Dictionary lookup for $key yields [null].");
905 }
906 return inferrer.types.nullType.type;
907 }
908 }
909 MapTypeMask mask = typedSelector.mask;
910 if (_VERBOSE) {
911 print("Map lookup for $typedSelector yields ${mask.valueType}.");
912 }
913 return mask.valueType;
914 } else {
915 TypeInformation info =
916 handleIntrisifiedSelector(typedSelector, inferrer);
917 if (info != null) return info.type;
918 return inferrer.typeOfElementWithSelector(element, typedSelector).type;
919 }
920 }));
921
922 // Walk over the old targets, and remove calls that cannot happen
923 // anymore.
924 oldTargets.forEach((element) {
925 if (!targets.contains(element)) {
926 MemberTypeInformation callee =
927 inferrer.types.getInferredTypeOf(element);
928 callee.removeCall(caller, call);
929 callee.removeUser(this);
930 inferrer.updateParameterAssignments(
931 this, element, arguments, typedSelector, remove: true,
932 addToQueue: true);
933 }
934 });
935
936 return newType;
937 }
938
939 void giveUp(TypeGraphInferrerEngine inferrer, {bool clearAssignments: true}) {
940 if (!abandonInferencing) {
941 inferrer.updateSelectorInTree(caller, call, selector);
942 Iterable<Element> oldTargets = targets;
943 targets = inferrer.compiler.world.allFunctions.filter(selector);
944 for (Element element in targets) {
945 if (!oldTargets.contains(element)) {
946 MemberTypeInformation callee =
947 inferrer.types.getInferredTypeOf(element);
948 callee.addCall(caller, call);
949 inferrer.updateParameterAssignments(
950 this, element, arguments, selector, remove: false,
951 addToQueue: true);
952 }
953 }
954 }
955 super.giveUp(inferrer, clearAssignments: clearAssignments);
956 }
957
958 void removeAndClearReferences(TypeGraphInferrerEngine inferrer) {
959 for (Element element in targets) {
960 ElementTypeInformation callee = inferrer.types.getInferredTypeOf(element);
961 callee.removeUser(this);
962 }
963 if (arguments != null) {
964 arguments.forEach((info) => info.removeUser(this));
965 }
966 super.removeAndClearReferences(inferrer);
967 }
968
969 String toString() => 'Call site $call on ${receiver.type} $type';
970
971 accept(TypeInformationVisitor visitor) {
972 return visitor.visitDynamicCallSiteTypeInformation(this);
973 }
974
975 bool hasStableType(TypeGraphInferrerEngine inferrer) {
976 return receiver.isStable &&
977 targets.every(
978 (element) => inferrer.types.getInferredTypeOf(element).isStable) &&
979 (arguments == null || arguments.every((info) => info.isStable)) &&
980 super.hasStableType(inferrer);
981 }
982 }
983
984 class ClosureCallSiteTypeInformation extends CallSiteTypeInformation {
985 final TypeInformation closure;
986
987 ClosureCallSiteTypeInformation(
988 MemberTypeInformation context,
989 Spannable call,
990 Element enclosing,
991 Selector selector,
992 this.closure,
993 ArgumentsTypes arguments,
994 bool inLoop)
995 : super(context, call, enclosing, selector, arguments, inLoop);
996
997 void addToGraph(TypeGraphInferrerEngine inferrer) {
998 arguments.forEach((info) => info.addUser(this));
999 closure.addUser(this);
1000 }
1001
1002 TypeMask computeType(TypeGraphInferrerEngine inferrer) => safeType(inferrer);
1003
1004 Iterable<Element> get callees {
1005 throw new UnsupportedError("Cannot compute callees of a closure call.");
1006 }
1007
1008 String toString() => 'Closure call $call on $closure';
1009
1010 accept(TypeInformationVisitor visitor) {
1011 return visitor.visitClosureCallSiteTypeInformation(this);
1012 }
1013
1014 void removeAndClearReferences(TypeGraphInferrerEngine inferrer) {
1015 // This method is a placeholder for the following comment:
1016 // We should maintain the information that the closure is a user
1017 // of its arguments because we do not check that the arguments
1018 // have a stable type for a closure call to be stable; our tracing
1019 // analysis want to know whether an (non-stable) argument is
1020 // passed to a closure.
1021 return super.removeAndClearReferences(inferrer);
1022 }
1023 }
1024
1025 /**
1026 * A [ConcreteTypeInformation] represents a type that needed
1027 * to be materialized during the creation of the graph. For example,
1028 * literals, [:this:] or [:super:] need a [ConcreteTypeInformation].
1029 *
1030 * [ConcreteTypeInformation] nodes have no assignment. Also, to save
1031 * on memory, we do not add users to [ConcreteTypeInformation] nodes,
1032 * because we know such node will never be refined to a different
1033 * type.
1034 */
1035 class ConcreteTypeInformation extends TypeInformation {
1036 ConcreteTypeInformation(TypeMask type) : super.untracked() {
1037 this.type = type;
1038 this.isStable = true;
1039 }
1040
1041 bool get isConcrete => true;
1042
1043 void addUser(TypeInformation user) {
1044 // Nothing to do, a concrete type does not get updated so never
1045 // needs to notify its users.
1046 }
1047
1048 void removeUser(TypeInformation user) {
1049 }
1050
1051 void addAssignment(TypeInformation assignment) {
1052 throw "Not supported";
1053 }
1054
1055 void removeAssignment(TypeInformation assignment) {
1056 throw "Not supported";
1057 }
1058
1059 TypeMask computeType(TypeGraphInferrerEngine inferrer) => type;
1060
1061 bool reset(TypeGraphInferrerEngine inferrer) {
1062 throw "Not supported";
1063 }
1064
1065 String toString() => 'Type $type';
1066
1067 accept(TypeInformationVisitor visitor) {
1068 return visitor.visitConcreteTypeInformation(this);
1069 }
1070
1071 bool hasStableType(TypeGraphInferrerEngine inferrer) => true;
1072 }
1073
1074 class StringLiteralTypeInformation extends ConcreteTypeInformation {
1075 final ast.DartString value;
1076
1077 StringLiteralTypeInformation(value, TypeMask mask)
1078 : super(new ValueTypeMask(mask, value.slowToString())),
1079 this.value = value;
1080
1081 String asString() => value.slowToString();
1082 String toString() => 'Type $type value ${value.slowToString()}';
1083
1084 accept(TypeInformationVisitor visitor) {
1085 return visitor.visitStringLiteralTypeInformation(this);
1086 }
1087 }
1088
1089 /**
1090 * A [NarrowTypeInformation] narrows a [TypeInformation] to a type,
1091 * represented in [typeAnnotation].
1092 *
1093 * A [NarrowTypeInformation] node has only one assignment: the
1094 * [TypeInformation] it narrows.
1095 *
1096 * [NarrowTypeInformation] nodes are created for:
1097 *
1098 * - Code after `is` and `as` checks, where we have more information
1099 * on the type of the right hand side of the expression.
1100 *
1101 * - Code after a dynamic call, where we have more information on the
1102 * type of the receiver: it can only be of a class that holds a
1103 * potential target of this dynamic call.
1104 *
1105 * - In checked mode, after a type annotation, we have more
1106 * information on the type of a local.
1107 */
1108 class NarrowTypeInformation extends TypeInformation {
1109 final TypeMask typeAnnotation;
1110
1111 NarrowTypeInformation(TypeInformation narrowedType, this.typeAnnotation)
1112 : super(narrowedType.context) {
1113 addAssignment(narrowedType);
1114 }
1115
1116 addAssignment(TypeInformation info) {
1117 super.addAssignment(info);
1118 assert(assignments.length == 1);
1119 }
1120
1121 TypeMask computeType(TypeGraphInferrerEngine inferrer) {
1122 TypeMask input = assignments.first.type;
1123 TypeMask intersection = input.intersection(typeAnnotation,
1124 inferrer.classWorld);
1125 if (_ANOMALY_WARN) {
1126 if (!input.containsMask(intersection, inferrer.classWorld) ||
1127 !typeAnnotation.containsMask(intersection, inferrer.classWorld)) {
1128 print("ANOMALY WARNING: narrowed $input to $intersection via "
1129 "$typeAnnotation");
1130 }
1131 }
1132 return intersection;
1133 }
1134
1135 String toString() {
1136 return 'Narrow to $typeAnnotation $type';
1137 }
1138
1139 accept(TypeInformationVisitor visitor) {
1140 return visitor.visitNarrowTypeInformation(this);
1141 }
1142 }
1143
1144 /**
1145 * An [InferredTypeInformation] is a [TypeInformation] that
1146 * defaults to the dynamic type until it is marked as beeing
1147 * inferred, at which point it computes its type based on
1148 * its assignments.
1149 */
1150 abstract class InferredTypeInformation extends TypeInformation {
1151 /** Whether the element type in that container has been inferred. */
1152 bool inferred = false;
1153
1154 InferredTypeInformation(MemberTypeInformation context,
1155 TypeInformation parentType)
1156 : super(context) {
1157 if (parentType != null) addAssignment(parentType);
1158 }
1159
1160 TypeMask computeType(TypeGraphInferrerEngine inferrer) {
1161 if (!inferred) return safeType(inferrer);
1162 return inferrer.types.computeTypeMask(assignments);
1163 }
1164
1165 bool hasStableType(TypeGraphInferrerEngine inferrer) {
1166 return inferred && super.hasStableType(inferrer);
1167 }
1168 }
1169
1170 /**
1171 * A [ListTypeInformation] is a [TypeInformation] created
1172 * for each `List` instantiations.
1173 */
1174 class ListTypeInformation extends TypeInformation
1175 with TracedTypeInformation {
1176 final ElementInContainerTypeInformation elementType;
1177
1178 /** The container type before it is inferred. */
1179 final ContainerTypeMask originalType;
1180
1181 /** The length at the allocation site. */
1182 final int originalLength;
1183
1184 /** The length after the container has been traced. */
1185 int inferredLength;
1186
1187 /**
1188 * Whether this list goes through a growable check.
1189 * We conservatively assume it does.
1190 */
1191 bool checksGrowable = true;
1192
1193 ListTypeInformation(MemberTypeInformation context,
1194 this.originalType,
1195 this.elementType,
1196 this.originalLength)
1197 : super(context) {
1198 type = originalType;
1199 inferredLength = originalType.length;
1200 elementType.addUser(this);
1201 }
1202
1203 String toString() => 'List type $type';
1204
1205 accept(TypeInformationVisitor visitor) {
1206 return visitor.visitListTypeInformation(this);
1207 }
1208
1209 bool hasStableType(TypeGraphInferrerEngine inferrer) {
1210 return elementType.isStable && super.hasStableType(inferrer);
1211 }
1212
1213 TypeMask computeType(TypeGraphInferrerEngine inferrer) {
1214 var mask = type;
1215 if (!mask.isContainer ||
1216 mask.elementType != elementType.type ||
1217 mask.length != inferredLength) {
1218 return new ContainerTypeMask(originalType.forwardTo,
1219 originalType.allocationNode,
1220 originalType.allocationElement,
1221 elementType.type,
1222 inferredLength);
1223 }
1224 return mask;
1225 }
1226
1227 TypeMask safeType(TypeGraphInferrerEngine inferrer) => originalType;
1228 }
1229
1230 /**
1231 * An [ElementInContainerTypeInformation] holds the common type of the
1232 * elements in a [ListTypeInformation].
1233 */
1234 class ElementInContainerTypeInformation extends InferredTypeInformation {
1235 ElementInContainerTypeInformation(MemberTypeInformation context,
1236 elementType)
1237 : super(context, elementType);
1238
1239 String toString() => 'Element in container $type';
1240
1241 accept(TypeInformationVisitor visitor) {
1242 return visitor.visitElementInContainerTypeInformation(this);
1243 }
1244 }
1245
1246 /**
1247 * A [MapTypeInformation] is a [TypeInformation] created
1248 * for maps.
1249 */
1250 class MapTypeInformation extends TypeInformation
1251 with TracedTypeInformation {
1252 // When in Dictionary mode, this map tracks the type of the values that
1253 // have been assigned to a specific [String] key.
1254 final Map<String, ValueInMapTypeInformation> typeInfoMap = {};
1255 // These fields track the overall type of the keys/values in the map.
1256 final KeyInMapTypeInformation keyType;
1257 final ValueInMapTypeInformation valueType;
1258 final MapTypeMask originalType;
1259
1260 // Set to false if a statically unknown key flows into this map.
1261 bool _allKeysAreStrings = true;
1262
1263 bool get inDictionaryMode => !bailedOut && _allKeysAreStrings;
1264
1265 MapTypeInformation(MemberTypeInformation context,
1266 this.originalType,
1267 this.keyType,
1268 this.valueType)
1269 : super(context) {
1270 keyType.addUser(this);
1271 valueType.addUser(this);
1272 type = originalType;
1273 }
1274
1275 TypeInformation addEntryAssignment(TypeInformation key,
1276 TypeInformation value,
1277 [bool nonNull = false]) {
1278 TypeInformation newInfo = null;
1279 if (_allKeysAreStrings && key is StringLiteralTypeInformation) {
1280 String keyString = key.asString();
1281 typeInfoMap.putIfAbsent(keyString, () {
1282 newInfo = new ValueInMapTypeInformation(context, null, nonNull);
1283 return newInfo;
1284 });
1285 typeInfoMap[keyString].addAssignment(value);
1286 } else {
1287 _allKeysAreStrings = false;
1288 typeInfoMap.clear();
1289 }
1290 keyType.addAssignment(key);
1291 valueType.addAssignment(value);
1292 if (newInfo != null) newInfo.addUser(this);
1293
1294 return newInfo;
1295 }
1296
1297 List<TypeInformation> addMapAssignment(MapTypeInformation other) {
1298 List<TypeInformation> newInfos = <TypeInformation>[];
1299 if (_allKeysAreStrings && other.inDictionaryMode) {
1300 other.typeInfoMap.forEach((keyString, value) {
1301 typeInfoMap.putIfAbsent(keyString, () {
1302 TypeInformation newInfo =
1303 new ValueInMapTypeInformation(context, null, false);
1304 newInfos.add(newInfo);
1305 return newInfo;
1306 });
1307 typeInfoMap[keyString].addAssignment(value);
1308 });
1309 } else {
1310 _allKeysAreStrings = false;
1311 typeInfoMap.clear();
1312 }
1313 keyType.addAssignment(other.keyType);
1314 valueType.addAssignment(other.valueType);
1315
1316 return newInfos;
1317 }
1318
1319 markAsInferred() {
1320 keyType.inferred = valueType.inferred = true;
1321 typeInfoMap.values.forEach((v) => v.inferred = true);
1322 }
1323
1324 addAssignment(TypeInformation other) {
1325 throw "not supported";
1326 }
1327
1328 accept(TypeInformationVisitor visitor) {
1329 return visitor.visitMapTypeInformation(this);
1330 }
1331
1332 TypeMask toTypeMask(TypeGraphInferrerEngine inferrer) {
1333 if (inDictionaryMode) {
1334 Map<String, TypeMask> mappings = new Map<String, TypeMask>();
1335 for (var key in typeInfoMap.keys) {
1336 mappings[key] = typeInfoMap[key].type;
1337 }
1338 return new DictionaryTypeMask(originalType.forwardTo,
1339 originalType.allocationNode,
1340 originalType.allocationElement,
1341 keyType.type,
1342 valueType.type,
1343 mappings);
1344 } else {
1345 return new MapTypeMask(originalType.forwardTo,
1346 originalType.allocationNode,
1347 originalType.allocationElement,
1348 keyType.type,
1349 valueType.type);
1350 }
1351 }
1352
1353 TypeMask computeType(TypeGraphInferrerEngine inferrer) {
1354 if (type.isDictionary != inDictionaryMode) {
1355 return toTypeMask(inferrer);
1356 } else if (type.isDictionary) {
1357 assert(inDictionaryMode);
1358 DictionaryTypeMask mask = type;
1359 for (var key in typeInfoMap.keys) {
1360 TypeInformation value = typeInfoMap[key];
1361 if (!mask.typeMap.containsKey(key) &&
1362 !value.type.containsAll(inferrer.classWorld) &&
1363 !value.type.isNullable) {
1364 return toTypeMask(inferrer);
1365 }
1366 if (mask.typeMap[key] != typeInfoMap[key].type) {
1367 return toTypeMask(inferrer);
1368 }
1369 }
1370 } else if (type.isMap) {
1371 MapTypeMask mask = type;
1372 if (mask.keyType != keyType.type ||
1373 mask.valueType != valueType.type) {
1374 return toTypeMask(inferrer);
1375 }
1376 } else {
1377 return toTypeMask(inferrer);
1378 }
1379
1380 return type;
1381 }
1382
1383 TypeMask safeType(TypeGraphInferrerEngine inferrer) => originalType;
1384
1385 bool hasStableType(TypeGraphInferrerEngine inferrer) {
1386 return keyType.isStable &&
1387 valueType.isStable &&
1388 super.hasStableType(inferrer);
1389 }
1390
1391 String toString() {
1392 return 'Map $type (K:$keyType, V:$valueType) contents $typeInfoMap';
1393 }
1394 }
1395
1396 /**
1397 * A [KeyInMapTypeInformation] holds the common type
1398 * for the keys in a [MapTypeInformation]
1399 */
1400 class KeyInMapTypeInformation extends InferredTypeInformation {
1401 KeyInMapTypeInformation(MemberTypeInformation context,
1402 TypeInformation keyType)
1403 : super(context, keyType);
1404
1405 accept(TypeInformationVisitor visitor) {
1406 return visitor.visitKeyInMapTypeInformation(this);
1407 }
1408
1409 String toString() => 'Key in Map $type';
1410 }
1411
1412 /**
1413 * A [ValueInMapTypeInformation] holds the common type
1414 * for the values in a [MapTypeInformation]
1415 */
1416 class ValueInMapTypeInformation extends InferredTypeInformation {
1417 // [nonNull] is set to true if this value is known to be part of the map.
1418 // Note that only values assigned to a specific key value in dictionary
1419 // mode can ever be marked as [nonNull].
1420 final bool nonNull;
1421
1422 ValueInMapTypeInformation(MemberTypeInformation context,
1423 TypeInformation valueType, [this.nonNull = false])
1424 : super(context, valueType);
1425
1426 accept(TypeInformationVisitor visitor) {
1427 return visitor.visitValueInMapTypeInformation(this);
1428 }
1429
1430 TypeMask computeType(TypeGraphInferrerEngine inferrer) {
1431 return nonNull ? super.computeType(inferrer)
1432 : super.computeType(inferrer).nullable();
1433 }
1434
1435 String toString() => 'Value in Map $type';
1436 }
1437
1438 /**
1439 * A [PhiElementTypeInformation] is an union of
1440 * [ElementTypeInformation], that is local to a method.
1441 */
1442 class PhiElementTypeInformation extends TypeInformation {
1443 final ast.Node branchNode;
1444 final bool isLoopPhi;
1445 final Local variable;
1446
1447 PhiElementTypeInformation(MemberTypeInformation context, this.branchNode,
1448 this.isLoopPhi, this.variable)
1449 : super(context);
1450
1451 TypeMask computeType(TypeGraphInferrerEngine inferrer) {
1452 return inferrer.types.computeTypeMask(assignments);
1453 }
1454
1455 String toString() => 'Phi $variable $type';
1456
1457 accept(TypeInformationVisitor visitor) {
1458 return visitor.visitPhiElementTypeInformation(this);
1459 }
1460 }
1461
1462 class ClosureTypeInformation extends TypeInformation
1463 with ApplyableTypeInformation {
1464 final ast.Node node;
1465 final Element element;
1466
1467 ClosureTypeInformation(MemberTypeInformation context, this.node,
1468 this.element)
1469 : super(context);
1470
1471 TypeMask computeType(TypeGraphInferrerEngine inferrer) => safeType(inferrer);
1472
1473 TypeMask safeType(TypeGraphInferrerEngine inferrer) {
1474 return inferrer.types.functionType.type;
1475 }
1476
1477 String toString() => 'Closure $element';
1478
1479 accept(TypeInformationVisitor visitor) {
1480 return visitor.visitClosureTypeInformation(this);
1481 }
1482
1483 bool hasStableType(TypeGraphInferrerEngine inferrer) {
1484 return false;
1485 }
1486 }
1487
1488 /**
1489 * Mixin for [TypeInformation] nodes that can bail out during tracing.
1490 */
1491 abstract class TracedTypeInformation implements TypeInformation {
1492 /// Set to false once analysis has succeeded.
1493 bool bailedOut = true;
1494 /// Set to true once analysis is completed.
1495 bool analyzed = false;
1496
1497 Set<TypeInformation> _flowsInto;
1498
1499 /**
1500 * The set of [TypeInformation] nodes where values from the traced node could
1501 * flow in.
1502 */
1503 Set<TypeInformation> get flowsInto {
1504 return (_flowsInto == null) ? const ImmutableEmptySet<TypeInformation>()
1505 : _flowsInto;
1506 }
1507
1508 /**
1509 * Adds [nodes] to the sets of values this [TracedTypeInformation] flows into.
1510 */
1511 void addFlowsIntoTargets(Iterable<TypeInformation> nodes) {
1512 if (_flowsInto == null) {
1513 _flowsInto = nodes.toSet();
1514 } else {
1515 _flowsInto.addAll(nodes);
1516 }
1517 }
1518 }
1519
1520 abstract class TypeInformationVisitor<T> {
1521 T visitNarrowTypeInformation(NarrowTypeInformation info);
1522 T visitPhiElementTypeInformation(PhiElementTypeInformation info);
1523 T visitElementInContainerTypeInformation(
1524 ElementInContainerTypeInformation info);
1525 T visitKeyInMapTypeInformation(KeyInMapTypeInformation info);
1526 T visitValueInMapTypeInformation(ValueInMapTypeInformation info);
1527 T visitListTypeInformation(ListTypeInformation info);
1528 T visitMapTypeInformation(MapTypeInformation info);
1529 T visitConcreteTypeInformation(ConcreteTypeInformation info);
1530 T visitStringLiteralTypeInformation(StringLiteralTypeInformation info);
1531 T visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info);
1532 T visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info);
1533 T visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info);
1534 T visitMemberTypeInformation(MemberTypeInformation info);
1535 T visitParameterTypeInformation(ParameterTypeInformation info);
1536 T visitClosureTypeInformation(ClosureTypeInformation info);
1537 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698