Index: pkg/compiler/lib/src/inferrer/type_graph_nodes.dart |
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart |
deleted file mode 100644 |
index 8941c7bc28b0ddfdf7233193c09f88c60e833317..0000000000000000000000000000000000000000 |
--- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart |
+++ /dev/null |
@@ -1,1537 +0,0 @@ |
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-part of type_graph_inferrer; |
- |
-/** |
- * Common class for all nodes in the graph. The current nodes are: |
- * |
- * - Concrete types |
- * - Elements |
- * - Call sites |
- * - Narrowing instructions |
- * - Phi instructions |
- * - Containers (for lists) |
- * - Type of the element in a container |
- * |
- * A node has a set of assignments and users. Assignments are used to |
- * compute the type of the node ([TypeInformation.computeType]). Users are |
- * added to the inferrer's work queue when the type of the node |
- * changes. |
- */ |
-abstract class TypeInformation { |
- Set<TypeInformation> users; |
- var /* List|ParameterAssignments */ _assignments; |
- |
- /// The type the inferrer has found for this [TypeInformation]. |
- /// Initially empty. |
- TypeMask type = const TypeMask.nonNullEmpty(); |
- |
- /// The graph node of the member this [TypeInformation] node belongs to. |
- final MemberTypeInformation context; |
- |
- /// The element this [TypeInformation] node belongs to. |
- MemberElement get contextMember => context == null ? null : context.element; |
- |
- Iterable<TypeInformation> get assignments => _assignments; |
- |
- /// We abandon inference in certain cases (complex cyclic flow, native |
- /// behaviours, etc.). In some case, we might resume inference in the |
- /// closure tracer, which is handled by checking whether [assignments] has |
- /// been set to [STOP_TRACKING_ASSIGNMENTS_MARKER]. |
- bool abandonInferencing = false; |
- bool get mightResume => |
- !identical(assignments, STOP_TRACKING_ASSIGNMENTS_MARKER); |
- |
- /// Number of times this [TypeInformation] has changed type. |
- int refineCount = 0; |
- |
- /// Whether this [TypeInformation] is currently in the inferrer's |
- /// work queue. |
- bool inQueue = false; |
- |
- /// Used to disable enqueueing of type informations where we know that their |
- /// type will not change for other reasons than being stable. For example, |
- /// if inference is disabled for a type and it is hardwired to dynamic, this |
- /// is set to true to spare recomputing dynamic again and again. Changing this |
- /// to false should never change inference outcome, just make is slower. |
- bool doNotEnqueue = false; |
- |
- /// Whether this [TypeInformation] has a stable [type] that will not |
- /// change. |
- bool isStable = false; |
- |
- // TypeInformations are unique. |
- static int staticHashCode = 0; |
- final int hashCode = staticHashCode++; |
- |
- bool get isConcrete => false; |
- |
- TypeInformation(this.context) : _assignments = <TypeInformation>[], |
- users = new Setlet<TypeInformation>(); |
- |
- TypeInformation.noAssignments(this.context) |
- : _assignments = const <TypeInformation>[], |
- users = new Setlet<TypeInformation>(); |
- |
- TypeInformation.untracked() |
- : _assignments = const <TypeInformation>[], |
- users = const ImmutableEmptySet(), |
- context = null; |
- |
- TypeInformation.withAssignments(this.context, this._assignments) |
- : users = new Setlet<TypeInformation>(); |
- |
- void addUser(TypeInformation user) { |
- assert(!user.isConcrete); |
- users.add(user); |
- } |
- |
- void removeUser(TypeInformation user) { |
- assert(!user.isConcrete); |
- users.remove(user); |
- } |
- |
- // The below is not a compile time constant to make it differentiable |
- // from other empty lists of [TypeInformation]. |
- static final STOP_TRACKING_ASSIGNMENTS_MARKER = new List<TypeInformation>(0); |
- |
- bool areAssignmentsTracked() { |
- return assignments != STOP_TRACKING_ASSIGNMENTS_MARKER; |
- } |
- |
- void addAssignment(TypeInformation assignment) { |
- // Cheap one-level cycle detection. |
- if (assignment == this) return; |
- if (areAssignmentsTracked()) { |
- _assignments.add(assignment); |
- } |
- // Even if we abandon inferencing on this [TypeInformation] we |
- // need to collect the users, so that phases that track where |
- // elements flow in still work. |
- assignment.addUser(this); |
- } |
- |
- void removeAssignment(TypeInformation assignment) { |
- if (!abandonInferencing || mightResume) { |
- _assignments.remove(assignment); |
- } |
- // We can have multiple assignments of the same [TypeInformation]. |
- if (!assignments.contains(assignment)) { |
- assignment.removeUser(this); |
- } |
- } |
- |
- TypeMask refine(TypeGraphInferrerEngine inferrer) { |
- return abandonInferencing ? safeType(inferrer) : computeType(inferrer); |
- } |
- |
- /** |
- * Computes a new type for this [TypeInformation] node depending on its |
- * potentially updated inputs. |
- */ |
- TypeMask computeType(TypeGraphInferrerEngine inferrer); |
- |
- /** |
- * Returns an approximation for this [TypeInformation] node that is always |
- * safe to use. Used when abandoning inference on a node. |
- */ |
- TypeMask safeType(TypeGraphInferrerEngine inferrer) { |
- return inferrer.types.dynamicType.type; |
- } |
- |
- void giveUp(TypeGraphInferrerEngine inferrer, {bool clearAssignments: true}) { |
- abandonInferencing = true; |
- // Do not remove [this] as a user of nodes in [assignments], |
- // because our tracing analysis could be interested in tracing |
- // this node. |
- if (clearAssignments) _assignments = STOP_TRACKING_ASSIGNMENTS_MARKER; |
- // Do not remove users because our tracing analysis could be |
- // interested in tracing the users of this node. |
- } |
- |
- void clear() { |
- _assignments = STOP_TRACKING_ASSIGNMENTS_MARKER; |
- users = const ImmutableEmptySet(); |
- } |
- |
- /// Reset the analysis of this node by making its type empty. |
- |
- bool reset(TypeGraphInferrerEngine inferrer) { |
- if (abandonInferencing) return false; |
- type = const TypeMask.nonNullEmpty(); |
- refineCount = 0; |
- return true; |
- } |
- |
- accept(TypeInformationVisitor visitor); |
- |
- /// The [Element] where this [TypeInformation] was created. May be |
- /// for some [TypeInformation] nodes, where we do not need to store |
- /// the information. |
- Element get owner => null; |
- |
- /// Returns whether the type cannot change after it has been |
- /// inferred. |
- bool hasStableType(TypeGraphInferrerEngine inferrer) { |
- return !mightResume && assignments.every((e) => e.isStable); |
- } |
- |
- void removeAndClearReferences(TypeGraphInferrerEngine inferrer) { |
- assignments.forEach((info) { info.removeUser(this); }); |
- } |
- |
- void stabilize(TypeGraphInferrerEngine inferrer) { |
- removeAndClearReferences(inferrer); |
- // Do not remove users because the tracing analysis could be interested |
- // in tracing the users of this node. |
- _assignments = STOP_TRACKING_ASSIGNMENTS_MARKER; |
- abandonInferencing = true; |
- isStable = true; |
- } |
- |
- void maybeResume() { |
- if (!mightResume) return; |
- abandonInferencing = false; |
- doNotEnqueue = false; |
- } |
-} |
- |
-abstract class ApplyableTypeInformation implements TypeInformation { |
- bool mightBePassedToFunctionApply = false; |
-} |
- |
-/** |
- * Marker node used only during tree construction but not during actual type |
- * refinement. |
- * |
- * Currently, this is used to give a type to an optional parameter even before |
- * the corresponding default expression has been analyzed. See |
- * [getDefaultTypeOfParameter] and [setDefaultTypeOfParameter] for details. |
- */ |
-class PlaceholderTypeInformation extends TypeInformation { |
- PlaceholderTypeInformation(MemberTypeInformation context) : super(context); |
- |
- void accept(TypeInformationVisitor visitor) { |
- throw new UnsupportedError("Cannot visit placeholder"); |
- } |
- |
- TypeMask computeType(TypeGraphInferrerEngine inferrer) { |
- throw new UnsupportedError("Cannot refine placeholder"); |
- } |
- |
- toString() => "Placeholder [$hashCode]"; |
-} |
- |
-/** |
- * Parameters of instance functions behave differently than other |
- * elements because the inferrer may remove assignments. This happens |
- * when the receiver of a dynamic call site can be refined |
- * to a type where we know more about which instance method is being |
- * called. |
- */ |
-class ParameterAssignments extends IterableBase<TypeInformation> { |
- final Map<TypeInformation, int> assignments = |
- new Map<TypeInformation, int>(); |
- |
- void remove(TypeInformation info) { |
- int existing = assignments[info]; |
- if (existing == null) return; |
- if (existing == 1) { |
- assignments.remove(info); |
- } else { |
- assignments[info] = existing - 1; |
- } |
- } |
- |
- void add(TypeInformation info) { |
- int existing = assignments[info]; |
- if (existing == null) { |
- assignments[info] = 1; |
- } else { |
- assignments[info] = existing + 1; |
- } |
- } |
- |
- void replace(TypeInformation old, TypeInformation replacement) { |
- int existing = assignments[old]; |
- if (existing != null) { |
- int other = assignments[replacement]; |
- if (other != null) existing += other; |
- assignments[replacement] = existing; |
- assignments.remove(old); |
- } |
- } |
- |
- Iterator<TypeInformation> get iterator => assignments.keys.iterator; |
- Iterable<TypeInformation> where(Function f) => assignments.keys.where(f); |
- |
- bool contains(TypeInformation info) => assignments.containsKey(info); |
- |
- String toString() => assignments.keys.toList().toString(); |
-} |
- |
-/** |
- * A node representing a resolved element of the program. The kind of |
- * elements that need an [ElementTypeInformation] are: |
- * |
- * - Functions (including getters and setters) |
- * - Constructors (factory or generative) |
- * - Fields |
- * - Parameters |
- * - Local variables mutated in closures |
- * |
- * The [ElementTypeInformation] of a function and a constructor is its |
- * return type. |
- * |
- * Note that a few elements of these kinds must be treated specially, |
- * and they are dealt in [ElementTypeInformation.handleSpecialCases]: |
- * |
- * - Parameters of closures, [noSuchMethod] and [call] instance |
- * methods: we currently do not infer types for those. |
- * |
- * - Fields and parameters being assigned by synthesized calls done by |
- * the backend: we do not know what types the backend will use. |
- * |
- * - Native functions and fields: because native methods contain no Dart |
- * code, and native fields do not have Dart assignments, we just |
- * trust their type annotation. |
- * |
- */ |
-abstract class ElementTypeInformation extends TypeInformation { |
- final Element element; |
- |
- /// Marker to disable inference for closures in [handleSpecialCases]. |
- bool disableInferenceForClosures = true; |
- |
- factory ElementTypeInformation(Element element, TypeInformationSystem types) { |
- if (element.isParameter || element.isInitializingFormal) { |
- ParameterElement parameter = element; |
- if (parameter.functionDeclaration.isInstanceMember) { |
- return new ParameterTypeInformation._instanceMember(element, types); |
- } |
- return new ParameterTypeInformation._internal(element, types); |
- } |
- return new MemberTypeInformation._internal(element); |
- } |
- |
- ElementTypeInformation._internal(MemberTypeInformation context, this.element) |
- : super(context); |
- ElementTypeInformation._withAssignments(MemberTypeInformation context, |
- this.element, assignments) |
- : super.withAssignments(context, assignments); |
-} |
- |
-/** |
- * A node representing members in the broadest sense: |
- * |
- * - Functions |
- * - Constructors |
- * - Fields (also synthetic ones due to closures) |
- * - Local functions (closures) |
- * |
- * These should never be created directly but instead are constructed by |
- * the [ElementTypeInformation] factory. |
- */ |
-class MemberTypeInformation extends ElementTypeInformation |
- with ApplyableTypeInformation { |
- TypedElement get element => super.element; |
- |
- /** |
- * If [element] is a function, [closurizedCount] is the number of |
- * times it is closurized. The value gets updated while infering. |
- */ |
- int closurizedCount = 0; |
- |
- /** |
- * This map contains the callers of [element]. It stores all unique call sites |
- * to enable counting the global number of call sites of [element]. |
- * |
- * A call site is either an AST [ast.Node], a [cps_ir.Node] or in the case of |
- * synthesized calls, an [Element] (see uses of [synthesizeForwardingCall] |
- * in [SimpleTypeInferrerVisitor]). |
- */ |
- final Map<Element, Setlet<Spannable>> _callers = new Map<Element, Setlet>(); |
- |
- MemberTypeInformation._internal(Element element) |
- : super._internal(null, element); |
- |
- void addCall(Element caller, Spannable node) { |
- assert(node is ast.Node || node is cps_ir.Node || node is Element); |
- _callers.putIfAbsent(caller, () => new Setlet()).add(node); |
- } |
- |
- void removeCall(Element caller, node) { |
- Setlet calls = _callers[caller]; |
- if (calls == null) return; |
- calls.remove(node); |
- if (calls.isEmpty) { |
- _callers.remove(caller); |
- } |
- } |
- |
- Iterable<Element> get callers => _callers.keys; |
- |
- bool isCalledOnce() { |
- int count = 0; |
- for (var set in _callers.values) { |
- count += set.length; |
- if (count > 1) return false; |
- } |
- return count == 1; |
- } |
- |
- bool get isClosurized => closurizedCount > 0; |
- |
- // Closurized methods never become stable to ensure that the information in |
- // [users] is accurate. The inference stops tracking users for stable types. |
- // Note that we only override the getter, the setter will still modify the |
- // state of the [isStable] field inhertied from [TypeInformation]. |
- bool get isStable => super.isStable && !isClosurized; |
- |
- TypeMask handleSpecialCases(TypeGraphInferrerEngine inferrer) { |
- if (element.isField && |
- !inferrer.compiler.backend.canBeUsedForGlobalOptimizations(element)) { |
- // Do not infer types for fields being assigned by synthesized calls. |
- giveUp(inferrer); |
- return safeType(inferrer); |
- } |
- if (inferrer.isNativeElement(element)) { |
- // Use the type annotation as the type for native elements. We |
- // also give up on inferring to make sure this element never |
- // goes in the work queue. |
- giveUp(inferrer); |
- if (element.isField) { |
- return inferrer.typeOfNativeBehavior( |
- native.NativeBehavior.ofFieldLoad(element, inferrer.compiler)).type; |
- } else { |
- assert(element.isFunction || |
- element.isGetter || |
- element.isSetter); |
- TypedElement typedElement = element; |
- var elementType = typedElement.type; |
- if (elementType.kind != TypeKind.FUNCTION) { |
- return safeType(inferrer); |
- } else { |
- return inferrer.typeOfNativeBehavior( |
- native.NativeBehavior.ofMethod(element, inferrer.compiler)).type; |
- } |
- } |
- } |
- |
- Compiler compiler = inferrer.compiler; |
- if (element.declaration == compiler.intEnvironment) { |
- giveUp(inferrer); |
- return compiler.typesTask.intType.nullable(); |
- } else if (element.declaration == compiler.boolEnvironment) { |
- giveUp(inferrer); |
- return compiler.typesTask.boolType.nullable(); |
- } else if (element.declaration == compiler.stringEnvironment) { |
- giveUp(inferrer); |
- return compiler.typesTask.stringType.nullable(); |
- } |
- return null; |
- } |
- |
- TypeMask potentiallyNarrowType(TypeMask mask, |
- TypeGraphInferrerEngine inferrer) { |
- Compiler compiler = inferrer.compiler; |
- if (!compiler.trustTypeAnnotations && !compiler.enableTypeAssertions) { |
- return mask; |
- } |
- if (element.isGenerativeConstructor || |
- element.isSetter) { |
- return mask; |
- } |
- if (element.isField) { |
- return new TypeMaskSystem(compiler).narrowType(mask, element.type); |
- } |
- assert(element.isFunction || |
- element.isGetter || |
- element.isFactoryConstructor); |
- |
- FunctionType type = element.type; |
- return new TypeMaskSystem(compiler).narrowType(mask, type.returnType); |
- } |
- |
- TypeMask computeType(TypeGraphInferrerEngine inferrer) { |
- TypeMask special = handleSpecialCases(inferrer); |
- if (special != null) return potentiallyNarrowType(special, inferrer); |
- return potentiallyNarrowType( |
- inferrer.types.computeTypeMask(assignments), inferrer); |
- } |
- |
- TypeMask safeType(TypeGraphInferrerEngine inferrer) { |
- return potentiallyNarrowType(super.safeType(inferrer), inferrer); |
- } |
- |
- String toString() => 'MemberElement $element $type'; |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitMemberTypeInformation(this); |
- } |
- |
- Element get owner => element.outermostEnclosingMemberOrTopLevel; |
- |
- bool hasStableType(TypeGraphInferrerEngine inferrer) { |
- // The number of assignments of non-final fields is |
- // not stable. Therefore such a field cannot be stable. |
- if (element.isField && !(element.isConst || element.isFinal)) { |
- return false; |
- } |
- |
- if (element.isFunction) return false; |
- |
- return super.hasStableType(inferrer); |
- } |
-} |
- |
-/** |
- * A node representing parameters: |
- * |
- * - Parameters |
- * - Initializing formals |
- * |
- * These should never be created directly but instead are constructed by |
- * the [ElementTypeInformation] factory. |
- */ |
-class ParameterTypeInformation extends ElementTypeInformation { |
- ParameterElement get element => super.element; |
- |
- ParameterTypeInformation._internal(ParameterElement element, |
- TypeInformationSystem types) |
- : super._internal(types.getInferredTypeOf(element.functionDeclaration), |
- element) { |
- assert(!element.functionDeclaration.isInstanceMember); |
- } |
- |
- ParameterTypeInformation._instanceMember(ParameterElement element, |
- TypeInformationSystem types) |
- : super._withAssignments( |
- types.getInferredTypeOf(element.functionDeclaration), |
- element, |
- new ParameterAssignments()) { |
- assert(element.functionDeclaration.isInstanceMember); |
- } |
- |
- bool isTearOffClosureParameter = false; |
- |
- void tagAsTearOffClosureParameter(TypeGraphInferrerEngine inferrer) { |
- assert(element.isParameter); |
- isTearOffClosureParameter = true; |
- } |
- |
- // TODO(herhut): Cleanup into one conditional. |
- TypeMask handleSpecialCases(TypeGraphInferrerEngine inferrer) { |
- if (!inferrer.compiler.backend.canBeUsedForGlobalOptimizations(element)) { |
- // Do not infer types for fields and parameters being assigned |
- // by synthesized calls. |
- giveUp(inferrer); |
- return safeType(inferrer); |
- } |
- |
- // The below do not apply to parameters of constructors, so skip |
- // initializing formals. |
- if (element.isInitializingFormal) return null; |
- |
- FunctionElement function = element.functionDeclaration; |
- if ((isTearOffClosureParameter || function.isLocal) && |
- disableInferenceForClosures) { |
- // Do not infer types for parameters of closures. We do not |
- // clear the assignments in case the closure is successfully |
- // traced. |
- giveUp(inferrer, clearAssignments: false); |
- return safeType(inferrer); |
- } |
- if (function.isInstanceMember && |
- (function.name == Compiler.NO_SUCH_METHOD || |
- (function.name == Compiler.CALL_OPERATOR_NAME && |
- disableInferenceForClosures))) { |
- // Do not infer types for parameters of [noSuchMethod] and |
- // [call] instance methods. |
- giveUp(inferrer); |
- return safeType(inferrer); |
- } |
- if (function == inferrer.mainElement) { |
- // The implicit call to main is not seen by the inferrer, |
- // therefore we explicitly set the type of its parameters as |
- // dynamic. |
- // TODO(14566): synthesize a call instead to get the exact |
- // types. |
- giveUp(inferrer); |
- return safeType(inferrer); |
- } |
- |
- return null; |
- } |
- |
- TypeMask potentiallyNarrowType(TypeMask mask, |
- TypeGraphInferrerEngine inferrer) { |
- Compiler compiler = inferrer.compiler; |
- if (!compiler.trustTypeAnnotations) { |
- return mask; |
- } |
- // When type assertions are enabled (aka checked mode), we have to always |
- // ignore type annotations to ensure that the checks are actually inserted |
- // into the function body and retained until runtime. |
- assert(!compiler.enableTypeAssertions); |
- return new TypeMaskSystem(compiler).narrowType(mask, element.type); |
- } |
- |
- TypeMask computeType(TypeGraphInferrerEngine inferrer) { |
- TypeMask special = handleSpecialCases(inferrer); |
- if (special != null) return special; |
- return potentiallyNarrowType(inferrer.types.computeTypeMask(assignments), |
- inferrer); |
- } |
- |
- TypeMask safeType(TypeGraphInferrerEngine inferrer) { |
- return potentiallyNarrowType(super.safeType(inferrer), inferrer); |
- } |
- |
- bool hasStableType(TypeGraphInferrerEngine inferrer) { |
- // The number of assignments of parameters of instance methods is |
- // not stable. Therefore such a parameter cannot be stable. |
- if (element.functionDeclaration.isInstanceMember) { |
- return false; |
- } |
- return super.hasStableType(inferrer); |
- } |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitParameterTypeInformation(this); |
- } |
- |
- String toString() => 'ParameterElement $element $type'; |
-} |
- |
-/** |
- * A [CallSiteTypeInformation] is a call found in the AST, or a |
- * synthesized call for implicit calls in Dart (such as forwarding |
- * factories). The [call] field is a [ast.Node] for the former, and an |
- * [Element] for the latter. |
- * |
- * In the inferrer graph, [CallSiteTypeInformation] nodes do not have |
- * any assignment. They rely on the [caller] field for static calls, |
- * and [selector] and [receiver] fields for dynamic calls. |
- */ |
-abstract class CallSiteTypeInformation extends TypeInformation |
- with ApplyableTypeInformation { |
- final Spannable call; |
- final Element caller; |
- final Selector selector; |
- final ArgumentsTypes arguments; |
- final bool inLoop; |
- |
- CallSiteTypeInformation( |
- MemberTypeInformation context, |
- this.call, |
- this.caller, |
- this.selector, |
- this.arguments, |
- this.inLoop) : super.noAssignments(context); |
- |
- String toString() => 'Call site $call $type'; |
- |
- /// Add [this] to the graph being computed by [engine]. |
- void addToGraph(TypeGraphInferrerEngine engine); |
- |
- /// Return an iterable over the targets of this call. |
- Iterable<Element> get callees; |
- |
- Element get owner => caller; |
-} |
- |
-class StaticCallSiteTypeInformation extends CallSiteTypeInformation { |
- final Element calledElement; |
- |
- StaticCallSiteTypeInformation( |
- MemberTypeInformation context, |
- Spannable call, |
- Element enclosing, |
- this.calledElement, |
- Selector selector, |
- ArgumentsTypes arguments, |
- bool inLoop) |
- : super(context, call, enclosing, selector, arguments, inLoop); |
- |
- void addToGraph(TypeGraphInferrerEngine inferrer) { |
- MemberTypeInformation callee = |
- inferrer.types.getInferredTypeOf(calledElement); |
- callee.addCall(caller, call); |
- callee.addUser(this); |
- if (arguments != null) { |
- arguments.forEach((info) => info.addUser(this)); |
- } |
- inferrer.updateParameterAssignments( |
- this, calledElement, arguments, selector, remove: false, |
- addToQueue: false); |
- } |
- |
- bool get isSynthesized { |
- // Some calls do not have a corresponding node, for example |
- // fowarding factory constructors, or synthesized super |
- // constructor calls. We synthesize these calls but do |
- // not create a selector for them. |
- return selector == null; |
- } |
- |
- TypeMask computeType(TypeGraphInferrerEngine inferrer) { |
- if (isSynthesized) { |
- assert(arguments != null); |
- return inferrer.types.getInferredTypeOf(calledElement).type; |
- } else { |
- return inferrer.typeOfElementWithSelector(calledElement, selector).type; |
- } |
- } |
- |
- Iterable<Element> get callees => [calledElement.implementation]; |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitStaticCallSiteTypeInformation(this); |
- } |
- |
- bool hasStableType(TypeGraphInferrerEngine inferrer) { |
- return inferrer.types.getInferredTypeOf(calledElement).isStable && |
- (arguments == null || arguments.every((info) => info.isStable)) && |
- super.hasStableType(inferrer); |
- } |
- |
- void removeAndClearReferences(TypeGraphInferrerEngine inferrer) { |
- ElementTypeInformation callee = |
- inferrer.types.getInferredTypeOf(calledElement); |
- callee.removeUser(this); |
- if (arguments != null) { |
- arguments.forEach((info) => info.removeUser(this)); |
- } |
- super.removeAndClearReferences(inferrer); |
- } |
-} |
- |
-class DynamicCallSiteTypeInformation extends CallSiteTypeInformation { |
- final TypeInformation receiver; |
- /// Cached targets of this call. |
- Iterable<Element> targets; |
- |
- DynamicCallSiteTypeInformation( |
- MemberTypeInformation context, |
- Spannable call, |
- Element enclosing, |
- Selector selector, |
- this.receiver, |
- ArgumentsTypes arguments, |
- bool inLoop) |
- : super(context, call, enclosing, selector, arguments, inLoop); |
- |
- void addToGraph(TypeGraphInferrerEngine inferrer) { |
- assert(receiver != null); |
- Selector typedSelector = computeTypedSelector(inferrer); |
- targets = inferrer.compiler.world.allFunctions.filter(typedSelector); |
- receiver.addUser(this); |
- if (arguments != null) { |
- arguments.forEach((info) => info.addUser(this)); |
- } |
- for (Element element in targets) { |
- MemberTypeInformation callee = inferrer.types.getInferredTypeOf(element); |
- callee.addCall(caller, call); |
- callee.addUser(this); |
- inferrer.updateParameterAssignments( |
- this, element, arguments, typedSelector, remove: false, |
- addToQueue: false); |
- } |
- } |
- |
- Iterable<Element> get callees => targets.map((e) => e.implementation); |
- |
- Selector computeTypedSelector(TypeGraphInferrerEngine inferrer) { |
- TypeMask receiverType = receiver.type; |
- |
- if (selector.mask != receiverType) { |
- return receiverType == inferrer.compiler.typesTask.dynamicType |
- ? selector.asUntyped |
- : new TypedSelector(receiverType, selector, inferrer.classWorld); |
- } else { |
- return selector; |
- } |
- } |
- |
- bool get targetsIncludeNoSuchMethod { |
- return targets.any((Element e) { |
- return e is FunctionElement && |
- e.isInstanceMember && |
- e.name == Compiler.NO_SUCH_METHOD; |
- }); |
- } |
- |
- /** |
- * We optimize certain operations on the [int] class because we know |
- * more about their return type than the actual Dart code. For |
- * example, we know int + int returns an int. The Dart code for |
- * [int.operator+] only says it returns a [num]. |
- */ |
- TypeInformation handleIntrisifiedSelector(Selector selector, |
- TypeGraphInferrerEngine inferrer) { |
- ClassWorld classWorld = inferrer.classWorld; |
- if (!classWorld.backend.intImplementation.isResolved) return null; |
- TypeMask emptyType = const TypeMask.nonNullEmpty(); |
- if (selector.mask == null) return null; |
- if (!selector.mask.containsOnlyInt(classWorld)) { |
- return null; |
- } |
- if (!selector.isCall && !selector.isOperator) return null; |
- if (!arguments.named.isEmpty) return null; |
- if (arguments.positional.length > 1) return null; |
- |
- ClassElement uint31Implementation = classWorld.backend.uint31Implementation; |
- bool isInt(info) => info.type.containsOnlyInt(classWorld); |
- bool isEmpty(info) => info.type == emptyType; |
- bool isUInt31(info) { |
- return info.type.satisfies(uint31Implementation, classWorld); |
- } |
- bool isPositiveInt(info) { |
- return info.type.satisfies( |
- classWorld.backend.positiveIntImplementation, classWorld); |
- } |
- |
- String name = selector.name; |
- // We are optimizing for the cases that are not expressed in the |
- // Dart code, for example: |
- // int + int -> int |
- // uint31 | uint31 -> uint31 |
- if (name == '*' || name == '+' || name == '%' || name == 'remainder' || |
- name == '~/') { |
- if (isPositiveInt(receiver) && |
- arguments.hasOnePositionalArgumentThatMatches(isPositiveInt)) { |
- return inferrer.types.positiveIntType; |
- } else if (arguments.hasOnePositionalArgumentThatMatches(isInt)) { |
- return inferrer.types.intType; |
- } else if (arguments.hasOnePositionalArgumentThatMatches(isEmpty)) { |
- return inferrer.types.nonNullEmptyType; |
- } else { |
- return null; |
- } |
- } else if (name == '|' || name == '^') { |
- if (isUInt31(receiver) && |
- arguments.hasOnePositionalArgumentThatMatches(isUInt31)) { |
- return inferrer.types.uint31Type; |
- } |
- } else if (name == '>>') { |
- if (isUInt31(receiver)) { |
- return inferrer.types.uint31Type; |
- } |
- } else if (name == '&') { |
- if (isUInt31(receiver) || |
- arguments.hasOnePositionalArgumentThatMatches(isUInt31)) { |
- return inferrer.types.uint31Type; |
- } |
- } else if (name == 'unary-') { |
- // The receiver being an int, the return value will also be an |
- // int. |
- return inferrer.types.intType; |
- } else if (name == '-') { |
- if (arguments.hasOnePositionalArgumentThatMatches(isInt)) { |
- return inferrer.types.intType; |
- } else if (arguments.hasOnePositionalArgumentThatMatches(isEmpty)) { |
- return inferrer.types.nonNullEmptyType; |
- } |
- return null; |
- } else if (name == 'abs') { |
- return arguments.hasNoArguments() ? inferrer.types.positiveIntType : null; |
- } |
- return null; |
- } |
- |
- TypeMask computeType(TypeGraphInferrerEngine inferrer) { |
- Iterable<Element> oldTargets = targets; |
- Selector typedSelector = computeTypedSelector(inferrer); |
- inferrer.updateSelectorInTree(caller, call, typedSelector); |
- |
- Compiler compiler = inferrer.compiler; |
- Selector selectorToUse = typedSelector.extendIfReachesAll(compiler); |
- bool canReachAll = compiler.enabledInvokeOn && |
- (selectorToUse != typedSelector); |
- |
- // If this call could potentially reach all methods that satisfy |
- // the untyped selector (through noSuchMethod's `Invocation` |
- // and a call to `delegate`), we iterate over all these methods to |
- // update their parameter types. |
- targets = compiler.world.allFunctions.filter(selectorToUse); |
- Iterable<Element> typedTargets = canReachAll |
- ? compiler.world.allFunctions.filter(typedSelector) |
- : targets; |
- |
- // Walk over the found targets, and compute the joined union type mask |
- // for all these targets. |
- TypeMask newType = inferrer.types.joinTypeMasks(targets.map((element) { |
- if (!oldTargets.contains(element)) { |
- MemberTypeInformation callee = |
- inferrer.types.getInferredTypeOf(element); |
- callee.addCall(caller, call); |
- callee.addUser(this); |
- inferrer.updateParameterAssignments( |
- this, element, arguments, typedSelector, remove: false, |
- addToQueue: true); |
- } |
- |
- // If [canReachAll] is true, then we are iterating over all |
- // targets that satisfy the untyped selector. We skip the return |
- // type of the targets that can only be reached through |
- // `Invocation.delegate`. Note that the `noSuchMethod` targets |
- // are included in [typedTargets]. |
- if (canReachAll && !typedTargets.contains(element)) { |
- return const TypeMask.nonNullEmpty(); |
- } |
- |
- if (inferrer.returnsListElementType(typedSelector)) { |
- ContainerTypeMask mask = receiver.type; |
- return mask.elementType; |
- } else if (inferrer.returnsMapValueType(typedSelector)) { |
- if (typedSelector.mask.isDictionary && |
- arguments.positional[0].type.isValue) { |
- DictionaryTypeMask mask = typedSelector.mask; |
- ValueTypeMask arg = arguments.positional[0].type; |
- String key = arg.value; |
- if (mask.typeMap.containsKey(key)) { |
- if (_VERBOSE) { |
- print("Dictionary lookup for $key yields ${mask.typeMap[key]}."); |
- } |
- return mask.typeMap[key]; |
- } else { |
- // The typeMap is precise, so if we do not find the key, the lookup |
- // will be [null] at runtime. |
- if (_VERBOSE) { |
- print("Dictionary lookup for $key yields [null]."); |
- } |
- return inferrer.types.nullType.type; |
- } |
- } |
- MapTypeMask mask = typedSelector.mask; |
- if (_VERBOSE) { |
- print("Map lookup for $typedSelector yields ${mask.valueType}."); |
- } |
- return mask.valueType; |
- } else { |
- TypeInformation info = |
- handleIntrisifiedSelector(typedSelector, inferrer); |
- if (info != null) return info.type; |
- return inferrer.typeOfElementWithSelector(element, typedSelector).type; |
- } |
- })); |
- |
- // Walk over the old targets, and remove calls that cannot happen |
- // anymore. |
- oldTargets.forEach((element) { |
- if (!targets.contains(element)) { |
- MemberTypeInformation callee = |
- inferrer.types.getInferredTypeOf(element); |
- callee.removeCall(caller, call); |
- callee.removeUser(this); |
- inferrer.updateParameterAssignments( |
- this, element, arguments, typedSelector, remove: true, |
- addToQueue: true); |
- } |
- }); |
- |
- return newType; |
- } |
- |
- void giveUp(TypeGraphInferrerEngine inferrer, {bool clearAssignments: true}) { |
- if (!abandonInferencing) { |
- inferrer.updateSelectorInTree(caller, call, selector); |
- Iterable<Element> oldTargets = targets; |
- targets = inferrer.compiler.world.allFunctions.filter(selector); |
- for (Element element in targets) { |
- if (!oldTargets.contains(element)) { |
- MemberTypeInformation callee = |
- inferrer.types.getInferredTypeOf(element); |
- callee.addCall(caller, call); |
- inferrer.updateParameterAssignments( |
- this, element, arguments, selector, remove: false, |
- addToQueue: true); |
- } |
- } |
- } |
- super.giveUp(inferrer, clearAssignments: clearAssignments); |
- } |
- |
- void removeAndClearReferences(TypeGraphInferrerEngine inferrer) { |
- for (Element element in targets) { |
- ElementTypeInformation callee = inferrer.types.getInferredTypeOf(element); |
- callee.removeUser(this); |
- } |
- if (arguments != null) { |
- arguments.forEach((info) => info.removeUser(this)); |
- } |
- super.removeAndClearReferences(inferrer); |
- } |
- |
- String toString() => 'Call site $call on ${receiver.type} $type'; |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitDynamicCallSiteTypeInformation(this); |
- } |
- |
- bool hasStableType(TypeGraphInferrerEngine inferrer) { |
- return receiver.isStable && |
- targets.every( |
- (element) => inferrer.types.getInferredTypeOf(element).isStable) && |
- (arguments == null || arguments.every((info) => info.isStable)) && |
- super.hasStableType(inferrer); |
- } |
-} |
- |
-class ClosureCallSiteTypeInformation extends CallSiteTypeInformation { |
- final TypeInformation closure; |
- |
- ClosureCallSiteTypeInformation( |
- MemberTypeInformation context, |
- Spannable call, |
- Element enclosing, |
- Selector selector, |
- this.closure, |
- ArgumentsTypes arguments, |
- bool inLoop) |
- : super(context, call, enclosing, selector, arguments, inLoop); |
- |
- void addToGraph(TypeGraphInferrerEngine inferrer) { |
- arguments.forEach((info) => info.addUser(this)); |
- closure.addUser(this); |
- } |
- |
- TypeMask computeType(TypeGraphInferrerEngine inferrer) => safeType(inferrer); |
- |
- Iterable<Element> get callees { |
- throw new UnsupportedError("Cannot compute callees of a closure call."); |
- } |
- |
- String toString() => 'Closure call $call on $closure'; |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitClosureCallSiteTypeInformation(this); |
- } |
- |
- void removeAndClearReferences(TypeGraphInferrerEngine inferrer) { |
- // This method is a placeholder for the following comment: |
- // We should maintain the information that the closure is a user |
- // of its arguments because we do not check that the arguments |
- // have a stable type for a closure call to be stable; our tracing |
- // analysis want to know whether an (non-stable) argument is |
- // passed to a closure. |
- return super.removeAndClearReferences(inferrer); |
- } |
-} |
- |
-/** |
- * A [ConcreteTypeInformation] represents a type that needed |
- * to be materialized during the creation of the graph. For example, |
- * literals, [:this:] or [:super:] need a [ConcreteTypeInformation]. |
- * |
- * [ConcreteTypeInformation] nodes have no assignment. Also, to save |
- * on memory, we do not add users to [ConcreteTypeInformation] nodes, |
- * because we know such node will never be refined to a different |
- * type. |
- */ |
-class ConcreteTypeInformation extends TypeInformation { |
- ConcreteTypeInformation(TypeMask type) : super.untracked() { |
- this.type = type; |
- this.isStable = true; |
- } |
- |
- bool get isConcrete => true; |
- |
- void addUser(TypeInformation user) { |
- // Nothing to do, a concrete type does not get updated so never |
- // needs to notify its users. |
- } |
- |
- void removeUser(TypeInformation user) { |
- } |
- |
- void addAssignment(TypeInformation assignment) { |
- throw "Not supported"; |
- } |
- |
- void removeAssignment(TypeInformation assignment) { |
- throw "Not supported"; |
- } |
- |
- TypeMask computeType(TypeGraphInferrerEngine inferrer) => type; |
- |
- bool reset(TypeGraphInferrerEngine inferrer) { |
- throw "Not supported"; |
- } |
- |
- String toString() => 'Type $type'; |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitConcreteTypeInformation(this); |
- } |
- |
- bool hasStableType(TypeGraphInferrerEngine inferrer) => true; |
-} |
- |
-class StringLiteralTypeInformation extends ConcreteTypeInformation { |
- final ast.DartString value; |
- |
- StringLiteralTypeInformation(value, TypeMask mask) |
- : super(new ValueTypeMask(mask, value.slowToString())), |
- this.value = value; |
- |
- String asString() => value.slowToString(); |
- String toString() => 'Type $type value ${value.slowToString()}'; |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitStringLiteralTypeInformation(this); |
- } |
-} |
- |
-/** |
- * A [NarrowTypeInformation] narrows a [TypeInformation] to a type, |
- * represented in [typeAnnotation]. |
- * |
- * A [NarrowTypeInformation] node has only one assignment: the |
- * [TypeInformation] it narrows. |
- * |
- * [NarrowTypeInformation] nodes are created for: |
- * |
- * - Code after `is` and `as` checks, where we have more information |
- * on the type of the right hand side of the expression. |
- * |
- * - Code after a dynamic call, where we have more information on the |
- * type of the receiver: it can only be of a class that holds a |
- * potential target of this dynamic call. |
- * |
- * - In checked mode, after a type annotation, we have more |
- * information on the type of a local. |
- */ |
-class NarrowTypeInformation extends TypeInformation { |
- final TypeMask typeAnnotation; |
- |
- NarrowTypeInformation(TypeInformation narrowedType, this.typeAnnotation) |
- : super(narrowedType.context) { |
- addAssignment(narrowedType); |
- } |
- |
- addAssignment(TypeInformation info) { |
- super.addAssignment(info); |
- assert(assignments.length == 1); |
- } |
- |
- TypeMask computeType(TypeGraphInferrerEngine inferrer) { |
- TypeMask input = assignments.first.type; |
- TypeMask intersection = input.intersection(typeAnnotation, |
- inferrer.classWorld); |
- if (_ANOMALY_WARN) { |
- if (!input.containsMask(intersection, inferrer.classWorld) || |
- !typeAnnotation.containsMask(intersection, inferrer.classWorld)) { |
- print("ANOMALY WARNING: narrowed $input to $intersection via " |
- "$typeAnnotation"); |
- } |
- } |
- return intersection; |
- } |
- |
- String toString() { |
- return 'Narrow to $typeAnnotation $type'; |
- } |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitNarrowTypeInformation(this); |
- } |
-} |
- |
-/** |
- * An [InferredTypeInformation] is a [TypeInformation] that |
- * defaults to the dynamic type until it is marked as beeing |
- * inferred, at which point it computes its type based on |
- * its assignments. |
- */ |
-abstract class InferredTypeInformation extends TypeInformation { |
- /** Whether the element type in that container has been inferred. */ |
- bool inferred = false; |
- |
- InferredTypeInformation(MemberTypeInformation context, |
- TypeInformation parentType) |
- : super(context) { |
- if (parentType != null) addAssignment(parentType); |
- } |
- |
- TypeMask computeType(TypeGraphInferrerEngine inferrer) { |
- if (!inferred) return safeType(inferrer); |
- return inferrer.types.computeTypeMask(assignments); |
- } |
- |
- bool hasStableType(TypeGraphInferrerEngine inferrer) { |
- return inferred && super.hasStableType(inferrer); |
- } |
-} |
- |
-/** |
- * A [ListTypeInformation] is a [TypeInformation] created |
- * for each `List` instantiations. |
- */ |
-class ListTypeInformation extends TypeInformation |
- with TracedTypeInformation { |
- final ElementInContainerTypeInformation elementType; |
- |
- /** The container type before it is inferred. */ |
- final ContainerTypeMask originalType; |
- |
- /** The length at the allocation site. */ |
- final int originalLength; |
- |
- /** The length after the container has been traced. */ |
- int inferredLength; |
- |
- /** |
- * Whether this list goes through a growable check. |
- * We conservatively assume it does. |
- */ |
- bool checksGrowable = true; |
- |
- ListTypeInformation(MemberTypeInformation context, |
- this.originalType, |
- this.elementType, |
- this.originalLength) |
- : super(context) { |
- type = originalType; |
- inferredLength = originalType.length; |
- elementType.addUser(this); |
- } |
- |
- String toString() => 'List type $type'; |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitListTypeInformation(this); |
- } |
- |
- bool hasStableType(TypeGraphInferrerEngine inferrer) { |
- return elementType.isStable && super.hasStableType(inferrer); |
- } |
- |
- TypeMask computeType(TypeGraphInferrerEngine inferrer) { |
- var mask = type; |
- if (!mask.isContainer || |
- mask.elementType != elementType.type || |
- mask.length != inferredLength) { |
- return new ContainerTypeMask(originalType.forwardTo, |
- originalType.allocationNode, |
- originalType.allocationElement, |
- elementType.type, |
- inferredLength); |
- } |
- return mask; |
- } |
- |
- TypeMask safeType(TypeGraphInferrerEngine inferrer) => originalType; |
-} |
- |
-/** |
- * An [ElementInContainerTypeInformation] holds the common type of the |
- * elements in a [ListTypeInformation]. |
- */ |
-class ElementInContainerTypeInformation extends InferredTypeInformation { |
- ElementInContainerTypeInformation(MemberTypeInformation context, |
- elementType) |
- : super(context, elementType); |
- |
- String toString() => 'Element in container $type'; |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitElementInContainerTypeInformation(this); |
- } |
-} |
- |
-/** |
- * A [MapTypeInformation] is a [TypeInformation] created |
- * for maps. |
- */ |
-class MapTypeInformation extends TypeInformation |
- with TracedTypeInformation { |
- // When in Dictionary mode, this map tracks the type of the values that |
- // have been assigned to a specific [String] key. |
- final Map<String, ValueInMapTypeInformation> typeInfoMap = {}; |
- // These fields track the overall type of the keys/values in the map. |
- final KeyInMapTypeInformation keyType; |
- final ValueInMapTypeInformation valueType; |
- final MapTypeMask originalType; |
- |
- // Set to false if a statically unknown key flows into this map. |
- bool _allKeysAreStrings = true; |
- |
- bool get inDictionaryMode => !bailedOut && _allKeysAreStrings; |
- |
- MapTypeInformation(MemberTypeInformation context, |
- this.originalType, |
- this.keyType, |
- this.valueType) |
- : super(context) { |
- keyType.addUser(this); |
- valueType.addUser(this); |
- type = originalType; |
- } |
- |
- TypeInformation addEntryAssignment(TypeInformation key, |
- TypeInformation value, |
- [bool nonNull = false]) { |
- TypeInformation newInfo = null; |
- if (_allKeysAreStrings && key is StringLiteralTypeInformation) { |
- String keyString = key.asString(); |
- typeInfoMap.putIfAbsent(keyString, () { |
- newInfo = new ValueInMapTypeInformation(context, null, nonNull); |
- return newInfo; |
- }); |
- typeInfoMap[keyString].addAssignment(value); |
- } else { |
- _allKeysAreStrings = false; |
- typeInfoMap.clear(); |
- } |
- keyType.addAssignment(key); |
- valueType.addAssignment(value); |
- if (newInfo != null) newInfo.addUser(this); |
- |
- return newInfo; |
- } |
- |
- List<TypeInformation> addMapAssignment(MapTypeInformation other) { |
- List<TypeInformation> newInfos = <TypeInformation>[]; |
- if (_allKeysAreStrings && other.inDictionaryMode) { |
- other.typeInfoMap.forEach((keyString, value) { |
- typeInfoMap.putIfAbsent(keyString, () { |
- TypeInformation newInfo = |
- new ValueInMapTypeInformation(context, null, false); |
- newInfos.add(newInfo); |
- return newInfo; |
- }); |
- typeInfoMap[keyString].addAssignment(value); |
- }); |
- } else { |
- _allKeysAreStrings = false; |
- typeInfoMap.clear(); |
- } |
- keyType.addAssignment(other.keyType); |
- valueType.addAssignment(other.valueType); |
- |
- return newInfos; |
- } |
- |
- markAsInferred() { |
- keyType.inferred = valueType.inferred = true; |
- typeInfoMap.values.forEach((v) => v.inferred = true); |
- } |
- |
- addAssignment(TypeInformation other) { |
- throw "not supported"; |
- } |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitMapTypeInformation(this); |
- } |
- |
- TypeMask toTypeMask(TypeGraphInferrerEngine inferrer) { |
- if (inDictionaryMode) { |
- Map<String, TypeMask> mappings = new Map<String, TypeMask>(); |
- for (var key in typeInfoMap.keys) { |
- mappings[key] = typeInfoMap[key].type; |
- } |
- return new DictionaryTypeMask(originalType.forwardTo, |
- originalType.allocationNode, |
- originalType.allocationElement, |
- keyType.type, |
- valueType.type, |
- mappings); |
- } else { |
- return new MapTypeMask(originalType.forwardTo, |
- originalType.allocationNode, |
- originalType.allocationElement, |
- keyType.type, |
- valueType.type); |
- } |
- } |
- |
- TypeMask computeType(TypeGraphInferrerEngine inferrer) { |
- if (type.isDictionary != inDictionaryMode) { |
- return toTypeMask(inferrer); |
- } else if (type.isDictionary) { |
- assert(inDictionaryMode); |
- DictionaryTypeMask mask = type; |
- for (var key in typeInfoMap.keys) { |
- TypeInformation value = typeInfoMap[key]; |
- if (!mask.typeMap.containsKey(key) && |
- !value.type.containsAll(inferrer.classWorld) && |
- !value.type.isNullable) { |
- return toTypeMask(inferrer); |
- } |
- if (mask.typeMap[key] != typeInfoMap[key].type) { |
- return toTypeMask(inferrer); |
- } |
- } |
- } else if (type.isMap) { |
- MapTypeMask mask = type; |
- if (mask.keyType != keyType.type || |
- mask.valueType != valueType.type) { |
- return toTypeMask(inferrer); |
- } |
- } else { |
- return toTypeMask(inferrer); |
- } |
- |
- return type; |
- } |
- |
- TypeMask safeType(TypeGraphInferrerEngine inferrer) => originalType; |
- |
- bool hasStableType(TypeGraphInferrerEngine inferrer) { |
- return keyType.isStable && |
- valueType.isStable && |
- super.hasStableType(inferrer); |
- } |
- |
- String toString() { |
- return 'Map $type (K:$keyType, V:$valueType) contents $typeInfoMap'; |
- } |
-} |
- |
-/** |
- * A [KeyInMapTypeInformation] holds the common type |
- * for the keys in a [MapTypeInformation] |
- */ |
-class KeyInMapTypeInformation extends InferredTypeInformation { |
- KeyInMapTypeInformation(MemberTypeInformation context, |
- TypeInformation keyType) |
- : super(context, keyType); |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitKeyInMapTypeInformation(this); |
- } |
- |
- String toString() => 'Key in Map $type'; |
-} |
- |
-/** |
- * A [ValueInMapTypeInformation] holds the common type |
- * for the values in a [MapTypeInformation] |
- */ |
-class ValueInMapTypeInformation extends InferredTypeInformation { |
- // [nonNull] is set to true if this value is known to be part of the map. |
- // Note that only values assigned to a specific key value in dictionary |
- // mode can ever be marked as [nonNull]. |
- final bool nonNull; |
- |
- ValueInMapTypeInformation(MemberTypeInformation context, |
- TypeInformation valueType, [this.nonNull = false]) |
- : super(context, valueType); |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitValueInMapTypeInformation(this); |
- } |
- |
- TypeMask computeType(TypeGraphInferrerEngine inferrer) { |
- return nonNull ? super.computeType(inferrer) |
- : super.computeType(inferrer).nullable(); |
- } |
- |
- String toString() => 'Value in Map $type'; |
-} |
- |
-/** |
- * A [PhiElementTypeInformation] is an union of |
- * [ElementTypeInformation], that is local to a method. |
- */ |
-class PhiElementTypeInformation extends TypeInformation { |
- final ast.Node branchNode; |
- final bool isLoopPhi; |
- final Local variable; |
- |
- PhiElementTypeInformation(MemberTypeInformation context, this.branchNode, |
- this.isLoopPhi, this.variable) |
- : super(context); |
- |
- TypeMask computeType(TypeGraphInferrerEngine inferrer) { |
- return inferrer.types.computeTypeMask(assignments); |
- } |
- |
- String toString() => 'Phi $variable $type'; |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitPhiElementTypeInformation(this); |
- } |
-} |
- |
-class ClosureTypeInformation extends TypeInformation |
- with ApplyableTypeInformation { |
- final ast.Node node; |
- final Element element; |
- |
- ClosureTypeInformation(MemberTypeInformation context, this.node, |
- this.element) |
- : super(context); |
- |
- TypeMask computeType(TypeGraphInferrerEngine inferrer) => safeType(inferrer); |
- |
- TypeMask safeType(TypeGraphInferrerEngine inferrer) { |
- return inferrer.types.functionType.type; |
- } |
- |
- String toString() => 'Closure $element'; |
- |
- accept(TypeInformationVisitor visitor) { |
- return visitor.visitClosureTypeInformation(this); |
- } |
- |
- bool hasStableType(TypeGraphInferrerEngine inferrer) { |
- return false; |
- } |
-} |
- |
-/** |
- * Mixin for [TypeInformation] nodes that can bail out during tracing. |
- */ |
-abstract class TracedTypeInformation implements TypeInformation { |
- /// Set to false once analysis has succeeded. |
- bool bailedOut = true; |
- /// Set to true once analysis is completed. |
- bool analyzed = false; |
- |
- Set<TypeInformation> _flowsInto; |
- |
- /** |
- * The set of [TypeInformation] nodes where values from the traced node could |
- * flow in. |
- */ |
- Set<TypeInformation> get flowsInto { |
- return (_flowsInto == null) ? const ImmutableEmptySet<TypeInformation>() |
- : _flowsInto; |
- } |
- |
- /** |
- * Adds [nodes] to the sets of values this [TracedTypeInformation] flows into. |
- */ |
- void addFlowsIntoTargets(Iterable<TypeInformation> nodes) { |
- if (_flowsInto == null) { |
- _flowsInto = nodes.toSet(); |
- } else { |
- _flowsInto.addAll(nodes); |
- } |
- } |
-} |
- |
-abstract class TypeInformationVisitor<T> { |
- T visitNarrowTypeInformation(NarrowTypeInformation info); |
- T visitPhiElementTypeInformation(PhiElementTypeInformation info); |
- T visitElementInContainerTypeInformation( |
- ElementInContainerTypeInformation info); |
- T visitKeyInMapTypeInformation(KeyInMapTypeInformation info); |
- T visitValueInMapTypeInformation(ValueInMapTypeInformation info); |
- T visitListTypeInformation(ListTypeInformation info); |
- T visitMapTypeInformation(MapTypeInformation info); |
- T visitConcreteTypeInformation(ConcreteTypeInformation info); |
- T visitStringLiteralTypeInformation(StringLiteralTypeInformation info); |
- T visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info); |
- T visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info); |
- T visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info); |
- T visitMemberTypeInformation(MemberTypeInformation info); |
- T visitParameterTypeInformation(ParameterTypeInformation info); |
- T visitClosureTypeInformation(ClosureTypeInformation info); |
-} |