| Index: sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart
|
| ===================================================================
|
| --- sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart (revision 24382)
|
| +++ sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart (working copy)
|
| @@ -52,17 +52,82 @@
|
| }
|
|
|
| /**
|
| - * Placeholder for type information of final fields of classes.
|
| + * A [TypeInformation] object contains information from the inferrer
|
| + * on a specific [Element].
|
| */
|
| -class ClassInfoForFinalFields {
|
| +abstract class TypeInformation {
|
| /**
|
| - * Maps a final field to a map from generative constructor to the
|
| - * inferred type of the field in that generative constructor.
|
| + * Assignments on the element and the types inferred at
|
| + * these assignments.
|
| */
|
| - final Map<Element, Map<Node, TypeMask>> typesOfFinalFields =
|
| - new Map<Element, Map<Node, TypeMask>>();
|
| + Map<Node, TypeMask> get assignments => null;
|
|
|
| /**
|
| + * Callers of an element.
|
| + */
|
| + Set<Element> get callers => null;
|
| +
|
| + /**
|
| + * Number of times the element has been processed.
|
| + */
|
| + int get analyzeCount => 0;
|
| + void set analyzeCount(value) {}
|
| +
|
| + TypeMask get type => null;
|
| + void set type(value) {}
|
| +
|
| + TypeMask get returnType => null;
|
| + void set returnType(value) {}
|
| +
|
| + void addCaller(Element element) {
|
| + callers.add(element);
|
| + }
|
| +
|
| + void addAssignment(Node node, TypeMask mask) {
|
| + assignments[node] = mask;
|
| + }
|
| +
|
| + void clear();
|
| +}
|
| +
|
| +class FunctionTypeInformation extends TypeInformation {
|
| + Set<Element> callers = new Set<Element>();
|
| + TypeMask returnType;
|
| + int analyzeCount = 0;
|
| + bool canBeClosurized = false;
|
| +
|
| + void clear() {
|
| + callers = null;
|
| + }
|
| +}
|
| +
|
| +class ParameterTypeInformation extends TypeInformation {
|
| + Map<Node, TypeMask> assignments = new Map<Node, TypeMask>();
|
| + TypeMask type;
|
| + TypeMask defaultType;
|
| +
|
| + void clear() {
|
| + assignments = null;
|
| + }
|
| +}
|
| +
|
| +class FieldTypeInformation extends TypeInformation {
|
| + TypeMask type;
|
| + Set<Element> callers = new Set<Element>();
|
| + Map<Node, TypeMask> assignments = new Map<Node, TypeMask>();
|
| + int analyzeCount = 0;
|
| +
|
| + void clear() {
|
| + assignments = null;
|
| + callers = null;
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * A class for knowing when can we compute a type for final fields.
|
| + */
|
| +class ClassTypeInformation {
|
| + /**
|
| * The number of generative constructors that need to be visited
|
| * before we can take any decision on the type of the fields.
|
| * Given that all generative constructors must be analyzed before
|
| @@ -71,22 +136,9 @@
|
| */
|
| int constructorsToVisitCount;
|
|
|
| - ClassInfoForFinalFields(this.constructorsToVisitCount);
|
| + ClassTypeInformation(this.constructorsToVisitCount);
|
|
|
| /**
|
| - * Records that the generative [constructor] has inferred [type]
|
| - * for the final [field].
|
| - */
|
| - void recordFinalFieldType(Node node,
|
| - Element constructor,
|
| - Element field,
|
| - TypeMask type) {
|
| - Map<Node, TypeMask> typesFor = typesOfFinalFields.putIfAbsent(
|
| - field, () => new Map<Node, TypeMask>());
|
| - typesFor[node] = type;
|
| - }
|
| -
|
| - /**
|
| * Records that [constructor] has been analyzed. If not at 0,
|
| * decrement [constructorsToVisitCount].
|
| */
|
| @@ -145,13 +197,9 @@
|
| if (compiler.disableTypeInference) return dynamicType;
|
| return internal.getTypeOfSelector(selector);
|
| }
|
| -
|
| Iterable<Element> getCallersOf(Element element) {
|
| if (compiler.disableTypeInference) throw "Don't use me";
|
| - Iterable<Element> result = internal.callersOf[element.implementation];
|
| - return result == null
|
| - ? internal.callersOf[element] = const <Element>[]
|
| - : result;
|
| + return internal.getCallersOf(element.implementation);
|
| }
|
|
|
| bool analyzeMain(Element element) {
|
| @@ -172,67 +220,25 @@
|
|
|
| class InternalSimpleTypesInferrer extends TypesInferrer {
|
| /**
|
| - * Maps a node to its type. Currently used for computing element
|
| - * types of lists.
|
| + * Maps a class to a [ClassTypeInformation] to help collect type
|
| + * information of final fields.
|
| */
|
| - final Map<Node, TypeMask> concreteTypes = new Map<Node, TypeMask>();
|
| + Map<ClassElement, ClassTypeInformation> classInfoForFinalFields =
|
| + new Map<ClassElement, ClassTypeInformation>();
|
|
|
| /**
|
| - * Maps an element to its callers.
|
| + * Maps an element to its corresponding [TypeInformation].
|
| */
|
| - final Map<Element, Iterable<Element>> callersOf =
|
| - new Map<Element, Iterable<Element>>();
|
| + final Map<Element, TypeInformation> typeInfo =
|
| + new Map<Element, TypeInformation>();
|
|
|
| /**
|
| - * Maps an element to its return type.
|
| + * Maps a node to its type. Currently used for computing element
|
| + * types of lists.
|
| */
|
| - final Map<Element, TypeMask> returnTypeOf =
|
| - new Map<Element, TypeMask>();
|
| + final Map<Node, TypeMask> concreteTypes = new Map<Node, TypeMask>();
|
|
|
| /**
|
| - * Maps an element to its type.
|
| - */
|
| - final Map<Element, TypeMask> typeOf = new Map<Element, TypeMask>();
|
| -
|
| - /**
|
| - * Maps an element to its assignments and the types inferred at
|
| - * these assignments.
|
| - */
|
| - final Map<Element, Map<Node, TypeMask>> typeOfFields =
|
| - new Map<Element, Map<Node, TypeMask>>();
|
| -
|
| - /**
|
| - * Maps an element to the type of its parameters at call sites.
|
| - */
|
| - final Map<Element, Map<Node, ArgumentsTypes>> typeOfArguments =
|
| - new Map<Element, Map<Node, ArgumentsTypes>>();
|
| -
|
| - /**
|
| - * Maps an optional parameter to its default type.
|
| - */
|
| - final Map<Element, TypeMask> defaultTypeOfParameter =
|
| - new Map<Element, TypeMask>();
|
| -
|
| - /**
|
| - * Set of methods that the inferrer found could be closurized. We
|
| - * don't compute parameter types for such methods.
|
| - */
|
| - final Set<Element> methodsThatCanBeClosurized = new Set<Element>();
|
| -
|
| - /**
|
| - * Maps an element to the number of times this type inferrer
|
| - * analyzed it.
|
| - */
|
| - final Map<Element, int> analyzeCount = new Map<Element, int>();
|
| -
|
| - /**
|
| - * Maps a class to a [ClassInfoForFinalFields] to help collect type
|
| - * information of final fields.
|
| - */
|
| - final Map<ClassElement, ClassInfoForFinalFields> classInfoForFinalFields =
|
| - new Map<ClassElement, ClassInfoForFinalFields>();
|
| -
|
| - /**
|
| * A map of constraints on a setter. When computing the type
|
| * of a field, these [Node] are initially discarded, and once the
|
| * type is computed, we make sure these constraints are satisfied
|
| @@ -247,12 +253,12 @@
|
| * returns that type.
|
| *
|
| */
|
| - final Map<Node, CallSite> setterConstraints = new Map<Node, CallSite>();
|
| + Map<Node, CallSite> setterConstraints = new Map<Node, CallSite>();
|
|
|
| /**
|
| * The work list of the inferrer.
|
| */
|
| - final WorkSet<Element> workSet = new WorkSet<Element>();
|
| + WorkSet<Element> workSet = new WorkSet<Element>();
|
|
|
| /**
|
| * Heuristic for avoiding too many re-analysis of an element.
|
| @@ -320,6 +326,11 @@
|
| */
|
| int numberOfElementsToAnalyze;
|
|
|
| + /**
|
| + * The number of analysis already done.
|
| + */
|
| + int analyzed = 0;
|
| +
|
| InternalSimpleTypesInferrer(this.compiler, this.optimismState);
|
|
|
| /**
|
| @@ -328,7 +339,6 @@
|
| */
|
| bool analyzeMain(Element element) {
|
| buildWorkQueue();
|
| - int analyzed = 0;
|
| compiler.progress.reset();
|
| int maxReanalysis = (numberOfElementsToAnalyze * 1.5).toInt();
|
| do {
|
| @@ -339,7 +349,7 @@
|
| element = workSet.remove();
|
| if (element.isErroneous()) continue;
|
|
|
| - bool wasAnalyzed = analyzeCount.containsKey(element);
|
| + bool wasAnalyzed = typeInformationOf(element).analyzeCount != 0;
|
| if (wasAnalyzed) {
|
| recompiles++;
|
| if (recompiles >= maxReanalysis) {
|
| @@ -366,15 +376,28 @@
|
| return true;
|
| }
|
|
|
| + TypeInformation typeInformationOf(Element element) {
|
| + return typeInfo.putIfAbsent(element, () {
|
| + if (element.isParameter() || element.isFieldParameter()) {
|
| + return new ParameterTypeInformation();
|
| + } else if (element.isField() || element.isVariable()) {
|
| + return new FieldTypeInformation();
|
| + } else {
|
| + assert(element is FunctionElement);
|
| + return new FunctionTypeInformation();
|
| + }
|
| + });
|
| + }
|
| +
|
| /**
|
| * Query method after the analysis to know the type of [element].
|
| */
|
| TypeMask getReturnTypeOfElement(Element element) {
|
| - return getNonNullType(returnTypeOf[element]);
|
| + return getNonNullType(typeInformationOf(element).returnType);
|
| }
|
|
|
| TypeMask getTypeOfElement(Element element) {
|
| - return getNonNullType(typeOf[element]);
|
| + return getNonNullType(typeInformationOf(element).type);
|
| }
|
|
|
| TypeMask getTypeOfSelector(Selector selector) {
|
| @@ -389,6 +412,10 @@
|
| return returnType != null ? returnType : dynamicType;
|
| }
|
|
|
| + Iterable<Element> getCallersOf(Element element) {
|
| + return typeInformationOf(element).callers;
|
| + }
|
| +
|
| /**
|
| * Query method after the analysis to know the type of [node],
|
| * defined in the context of [owner].
|
| @@ -399,29 +426,39 @@
|
|
|
| void checkAnalyzedAll() {
|
| if (hasAnalyzedAll) return;
|
| - if (analyzeCount.length != numberOfElementsToAnalyze) return;
|
| + if (analyzed < numberOfElementsToAnalyze) return;
|
| hasAnalyzedAll = true;
|
| +
|
| // If we have analyzed all the world, we know all assigments to
|
| - // fields and can therefore infer a type for them.
|
| - typeOfFields.keys.forEach(updateNonFinalFieldType);
|
| - // We also know all calls to methods.
|
| - typeOfArguments.keys.forEach(updateArgumentsType);
|
| + // fields and parameters, and can therefore infer a type for them.
|
| + typeInfo.forEach((element, TypeInformation info) {
|
| + if (element.isParameter() || element.isFieldParameter()) {
|
| + if (updateParameterType(element)) {
|
| + enqueueAgain(element.enclosingElement);
|
| + }
|
| + } else if (element.isField()
|
| + && !(element.modifiers.isFinal()
|
| + || element.modifiers.isConst())) {
|
| + updateNonFinalFieldType(element);
|
| + } else if (element.isVariable()) {
|
| + updateNonFinalFieldType(element);
|
| + }
|
| + });
|
| }
|
|
|
| /**
|
| * Enqueues [e] in the work queue if it is valuable.
|
| */
|
| void enqueueAgain(Element e) {
|
| - int count = analyzeCount[e];
|
| + assert(isNotClosure(e));
|
| + int count = typeInformationOf(e).analyzeCount;
|
| if (count != null && count > MAX_ANALYSIS_COUNT_PER_ELEMENT) return;
|
| workSet.add(e);
|
| }
|
|
|
| void enqueueCallersOf(Element element) {
|
| - Set<Element> methodCallers = callersOf[element];
|
| - if (methodCallers != null) {
|
| - methodCallers.forEach(enqueueAgain);
|
| - }
|
| + assert(isNotClosure(element));
|
| + typeInformationOf(element).callers.forEach(enqueueAgain);
|
| }
|
|
|
| /**
|
| @@ -454,7 +491,9 @@
|
| // Optimistically assume that they return bool. We may need to back
|
| // out of this.
|
| if (optimismState == OPTIMISTIC) {
|
| - returnTypeOf[element.implementation] = boolType;
|
| + FunctionTypeInformation info =
|
| + typeInformationOf(element.implementation);
|
| + info.returnType = boolType;
|
| }
|
| } else {
|
| // Put the other operators in buckets by length, later to be added in
|
| @@ -491,7 +530,7 @@
|
| }
|
| });
|
| classInfoForFinalFields[cls.implementation] =
|
| - new ClassInfoForFinalFields(constructorCount);
|
| + new ClassTypeInformation(constructorCount);
|
| });
|
| }
|
|
|
| @@ -505,13 +544,15 @@
|
|
|
| dump() {
|
| int interestingTypes = 0;
|
| - returnTypeOf.forEach((Element element, TypeMask type) {
|
| - if (type != nullType && !isDynamicType(type)) {
|
| + typeInfo.forEach((element, TypeInformation info) {
|
| + TypeMask type = info.type;
|
| + TypeMask returnType = info.returnType;
|
| + if (type != null && type != nullType && !isDynamicType(type)) {
|
| interestingTypes++;
|
| }
|
| - });
|
| - typeOf.forEach((Element element, TypeMask type) {
|
| - if (type != nullType && !isDynamicType(type)) {
|
| + if (returnType != null
|
| + && returnType != nullType
|
| + && !isDynamicType(returnType)) {
|
| interestingTypes++;
|
| }
|
| });
|
| @@ -526,11 +567,10 @@
|
| * Clear data structures that are not used after the analysis.
|
| */
|
| void clear() {
|
| - callersOf.clear();
|
| - analyzeCount.clear();
|
| - classInfoForFinalFields.clear();
|
| - typeOfFields.clear();
|
| - setterConstraints.clear();
|
| + classInfoForFinalFields = null;
|
| + setterConstraints = null;
|
| + workSet = null;
|
| + typeInfo.forEach((_, info) { info.clear(); });
|
| }
|
|
|
| bool analyze(Element element) {
|
| @@ -540,11 +580,7 @@
|
| SimpleTypeInferrerVisitor visitor =
|
| new SimpleTypeInferrerVisitor(element, compiler, this);
|
| TypeMask returnType = visitor.run();
|
| - if (analyzeCount.containsKey(element)) {
|
| - analyzeCount[element]++;
|
| - } else {
|
| - analyzeCount[element] = 1;
|
| - }
|
| + typeInformationOf(element).analyzeCount++;
|
| if (element.isGenerativeConstructor()) {
|
| // We always know the return type of a generative constructor.
|
| return false; // Nothing changed.
|
| @@ -576,11 +612,18 @@
|
| }
|
|
|
| bool recordType(Element analyzedElement, TypeMask type) {
|
| + if (isNativeElement(analyzedElement)) return false;
|
| assert(type != null);
|
| assert(analyzedElement.isField()
|
| || analyzedElement.isParameter()
|
| || analyzedElement.isFieldParameter());
|
| - return internalRecordType(analyzedElement, type, typeOf);
|
| + TypeMask newType = checkTypeAnnotation(analyzedElement, type);
|
| + TypeMask existing = typeInformationOf(analyzedElement).type;
|
| + typeInformationOf(analyzedElement).type = newType;
|
| + // If the type is useful, say it has changed.
|
| + return existing != newType
|
| + && !isDynamicType(newType)
|
| + && newType != nullType;
|
| }
|
|
|
| /**
|
| @@ -589,15 +632,23 @@
|
| * [analyzedElement].
|
| */
|
| bool recordReturnType(Element analyzedElement, TypeMask returnType) {
|
| + if (isNativeElement(analyzedElement)) return false;
|
| assert(analyzedElement.implementation == analyzedElement);
|
| + TypeMask existing = typeInformationOf(analyzedElement).returnType;
|
| if (optimismState == OPTIMISTIC
|
| && shouldOptimisticallyOptimizeToBool(analyzedElement)
|
| - && returnType != returnTypeOf[analyzedElement]) {
|
| + && returnType != existing) {
|
| // One of the functions turned out not to return what we expected.
|
| // This means we need to restart the analysis.
|
| optimismState = RETRY;
|
| }
|
| - return internalRecordType(analyzedElement, returnType, returnTypeOf);
|
| + TypeMask newType = checkTypeAnnotation(analyzedElement, returnType);
|
| + FunctionTypeInformation info = typeInformationOf(analyzedElement);
|
| + info.returnType = newType;
|
| + // If the return type is useful, say it has changed.
|
| + return existing != newType
|
| + && !isDynamicType(newType)
|
| + && newType != nullType;
|
| }
|
|
|
| bool isNativeElement(Element element) {
|
| @@ -607,43 +658,32 @@
|
| && element.isField();
|
| }
|
|
|
| - bool internalRecordType(Element analyzedElement,
|
| - TypeMask newType,
|
| - Map<Element, TypeMask> types) {
|
| + TypeMask checkTypeAnnotation(Element analyzedElement, TypeMask newType) {
|
| if (compiler.trustTypeAnnotations
|
| // Parameters are being checked by the method, and we can
|
| // therefore only trust their type after the checks.
|
| || (compiler.enableTypeAssertions && !analyzedElement.isParameter())) {
|
| var annotation = analyzedElement.computeType(compiler);
|
| - if (types == returnTypeOf) {
|
| + if (analyzedElement.isGetter()
|
| + || analyzedElement.isFunction()
|
| + || analyzedElement.isConstructor()
|
| + || analyzedElement.isSetter()) {
|
| assert(annotation is FunctionType);
|
| annotation = annotation.returnType;
|
| }
|
| newType = narrowType(newType, annotation, compiler);
|
| }
|
| -
|
| - // Fields and native methods of native classes are handled
|
| - // specially when querying for their type or return type.
|
| - if (isNativeElement(analyzedElement)) return false;
|
| - assert(newType != null);
|
| - TypeMask existing = types[analyzedElement];
|
| - types[analyzedElement] = newType;
|
| - // If the return type is useful, say it has changed.
|
| - return existing != newType
|
| - && !isDynamicType(newType)
|
| - && newType != nullType;
|
| + return newType;
|
| }
|
|
|
| TypeMask fetchReturnType(Element element) {
|
| - return returnTypeOf[element] is ElementTypeMask
|
| - ? dynamicType
|
| - : returnTypeOf[element];
|
| + TypeMask returnType = typeInformationOf(element).returnType;
|
| + return returnType is ElementTypeMask ? dynamicType : returnType;
|
| }
|
|
|
| TypeMask fetchType(Element element) {
|
| - return typeOf[element] is ElementTypeMask
|
| - ? dynamicType
|
| - : typeOf[element];
|
| + TypeMask type = typeInformationOf(element).type;
|
| + return type is ElementTypeMask ? dynamicType : type;
|
| }
|
|
|
| /**
|
| @@ -652,22 +692,25 @@
|
| */
|
| TypeMask returnTypeOfElement(Element element) {
|
| element = element.implementation;
|
| + TypeInformation info = typeInformationOf(element);
|
| if (element.isGenerativeConstructor()) {
|
| - return returnTypeOf.putIfAbsent(element, () {
|
| - return new TypeMask.nonNullExact(
|
| - rawTypeOf(element.getEnclosingClass()));
|
| - });
|
| + return info.returnType == null
|
| + ? info.returnType = new TypeMask.nonNullExact(
|
| + rawTypeOf(element.getEnclosingClass()))
|
| + : info.returnType;
|
| } else if (element.isNative()) {
|
| - return returnTypeOf.putIfAbsent(element, () {
|
| + if (info.returnType == null) {
|
| var elementType = element.computeType(compiler);
|
| if (elementType.kind != TypeKind.FUNCTION) {
|
| - return dynamicType;
|
| + info.returnType = dynamicType;
|
| + } else {
|
| + info.returnType = typeOfNativeBehavior(
|
| + native.NativeBehavior.ofMethod(element, compiler));
|
| }
|
| - return typeOfNativeBehavior(
|
| - native.NativeBehavior.ofMethod(element, compiler));
|
| - });
|
| + }
|
| + return info.returnType;
|
| }
|
| - TypeMask returnType = returnTypeOf[element];
|
| + TypeMask returnType = info.returnType;
|
| if (returnType == null) {
|
| if ((compiler.trustTypeAnnotations || compiler.enableTypeAssertions)
|
| && (element.isFunction()
|
| @@ -676,7 +719,7 @@
|
| FunctionType functionType = element.computeType(compiler);
|
| returnType = narrowType(dynamicType, functionType.returnType, compiler);
|
| } else {
|
| - returnType = returnTypeOf[element] =
|
| + returnType = info.returnType =
|
| new ElementTypeMask(fetchReturnType, element);
|
| }
|
| }
|
| @@ -736,15 +779,18 @@
|
| */
|
| TypeMask typeOfElement(Element element) {
|
| element = element.implementation;
|
| + TypeInformation info = typeInformationOf(element);
|
| + TypeMask type = info.type;
|
| if (isNativeElement(element) && element.isField()) {
|
| - var type = typeOf.putIfAbsent(element, () {
|
| + if (type == null) {
|
| InterfaceType rawType = element.computeType(compiler).asRaw();
|
| - return rawType.isDynamic ? dynamicType : new TypeMask.subtype(rawType);
|
| - });
|
| + info.type = type = rawType.isDynamic
|
| + ? dynamicType
|
| + : new TypeMask.subtype(rawType);
|
| + }
|
| assert(type != null);
|
| return type;
|
| }
|
| - TypeMask type = typeOf[element];
|
| if (type == null) {
|
| if ((compiler.trustTypeAnnotations
|
| && (element.isField()
|
| @@ -756,7 +802,7 @@
|
| && (element.isField() || element.isVariable()))) {
|
| type = narrowType(dynamicType, element.computeType(compiler), compiler);
|
| } else {
|
| - type = typeOf[element] = new ElementTypeMask(fetchType, element);
|
| + type = info.type = new ElementTypeMask(fetchType, element);
|
| }
|
| }
|
| return type;
|
| @@ -808,9 +854,8 @@
|
| ContainerTypeMask mask = selector.mask;
|
| TypeMask elementType = mask.elementType;
|
| return elementType == null ? dynamicType : elementType;
|
| - } else if (element.isGetter()) {
|
| - // Closure call.
|
| - assert(selector.isCall());
|
| + } else if (element.isGetter() || element.isField()) {
|
| + assert(selector.isCall() || selector.isSetter());
|
| return dynamicType;
|
| } else {
|
| return returnTypeOfElement(element);
|
| @@ -828,19 +873,76 @@
|
| assert(caller.isImplementation);
|
| assert(callee.isImplementation);
|
| assert(isNotClosure(caller));
|
| - Set<Element> callers = callersOf.putIfAbsent(
|
| - callee, () => new Set<Element>());
|
| + Set<Element> callers = typeInformationOf(callee).callers;
|
| callers.add(caller);
|
| }
|
|
|
| - bool addArguments(Node node, Element element, ArgumentsTypes arguments) {
|
| - Map<Node, ArgumentsTypes> types = typeOfArguments.putIfAbsent(
|
| - element, () => new Map<Node, ArgumentsTypes>());
|
| - ArgumentsTypes existing = types[node];
|
| - types[node] = arguments;
|
| - return existing != arguments;
|
| + bool addArguments(Node node,
|
| + FunctionElement element,
|
| + ArgumentsTypes arguments) {
|
| + FunctionTypeInformation info = typeInformationOf(element);
|
| + if (info.canBeClosurized) return false;
|
| + // A [noSuchMethod] method can be the target of any call, with
|
| + // any number of arguments. For simplicity, we just do not
|
| + // infer any parameter types for [noSuchMethod].
|
| + if (element.name == Compiler.NO_SUCH_METHOD) return false;
|
| +
|
| + FunctionSignature signature = element.computeSignature(compiler);
|
| + int parameterIndex = 0;
|
| + bool changed = false;
|
| + bool visitingOptionalParameter = false;
|
| + signature.forEachParameter((Element parameter) {
|
| + if (parameter == signature.firstOptionalParameter) {
|
| + visitingOptionalParameter = true;
|
| + }
|
| + TypeMask type;
|
| + ParameterTypeInformation info = typeInformationOf(parameter);
|
| + if (!visitingOptionalParameter) {
|
| + type = arguments.positional[parameterIndex];
|
| + } else {
|
| + type = signature.optionalParametersAreNamed
|
| + ? arguments.named[parameter.name]
|
| + : parameterIndex < arguments.positional.length
|
| + ? arguments.positional[parameterIndex]
|
| + : info.defaultType;
|
| + }
|
| + TypeMask oldType = info.assignments[node];
|
| + info.addAssignment(node, type);
|
| + changed = changed || (oldType != type);
|
| + parameterIndex++;
|
| + });
|
| + return changed;
|
| }
|
|
|
| + bool updateParameterType(Element parameter) {
|
| + FunctionTypeInformation functionInfo =
|
| + typeInformationOf(parameter.enclosingElement);
|
| + if (functionInfo.canBeClosurized) return false;
|
| + if (!isNotClosure(parameter.enclosingElement)) return false;
|
| +
|
| + ParameterTypeInformation info = typeInformationOf(parameter);
|
| + TypeMask elementType;
|
| + info.assignments.forEach((Node node, TypeMask mask) {
|
| + if (mask == null) {
|
| + // Now that we know we have analyzed the function holding
|
| + // [parameter], we have a default type for that [parameter].
|
| + mask = info.defaultType;
|
| + info.addAssignment(node, mask);
|
| + }
|
| + elementType = computeLubFor(elementType, mask, parameter);
|
| + });
|
| + if (elementType == null) {
|
| + elementType = dynamicType;
|
| + }
|
| + return recordType(parameter, elementType);
|
| + }
|
| +
|
| + void updateAllParametersOf(FunctionElement function) {
|
| + function.computeSignature(compiler).forEachParameter((Element parameter) {
|
| + updateParameterType(parameter);
|
| + });
|
| + }
|
| +
|
| void updateSideEffects(SideEffects sideEffects,
|
| Selector selector,
|
| Element callee) {
|
| @@ -907,9 +1009,8 @@
|
|
|
| assert(isNotClosure(caller));
|
| callee = callee.implementation;
|
| - if (!analyzeCount.containsKey(caller)) {
|
| - addCaller(caller, callee);
|
| - }
|
| + TypeInformation info = typeInformationOf(callee);
|
| + info.addCaller(caller);
|
|
|
| if (selector.isSetter() && callee.isField()) {
|
| recordNonFinalFieldElementType(
|
| @@ -921,7 +1022,8 @@
|
| } else if (selector.isGetter()) {
|
| assert(arguments == null);
|
| if (callee.isFunction()) {
|
| - methodsThatCanBeClosurized.add(callee);
|
| + FunctionTypeInformation functionInfo = info;
|
| + functionInfo.canBeClosurized = true;
|
| }
|
| return;
|
| } else if (callee.isField()) {
|
| @@ -947,18 +1049,22 @@
|
| Element callee) {
|
| if (callee.isField()) {
|
| if (selector.isSetter()) {
|
| - Map<Node, TypeMask> types = typeOfFields[callee];
|
| - if (types == null || !types.containsKey(node)) return;
|
| - types.remove(node);
|
| + Map<Node, TypeMask> assignments = typeInformationOf(callee).assignments;
|
| + if (assignments == null || !assignments.containsKey(node)) return;
|
| + assignments.remove(node);
|
| if (hasAnalyzedAll) updateNonFinalFieldType(callee);
|
| }
|
| } else if (callee.isGetter()) {
|
| return;
|
| } else {
|
| - Map<Node, ArgumentsTypes> types = typeOfArguments[callee];
|
| - if (types == null || !types.containsKey(node)) return;
|
| - types.remove(node);
|
| - if (hasAnalyzedAll) enqueueAgain(callee);
|
| + FunctionElement element = callee;
|
| + element.computeSignature(compiler).forEachParameter((Element parameter) {
|
| + Map<Node, TypeMask> assignments =
|
| + typeInformationOf(parameter).assignments;
|
| + if (assignments == null || !assignments.containsKey(node)) return;
|
| + assignments.remove(node);
|
| + if (hasAnalyzedAll) enqueueAgain(callee);
|
| + });
|
| }
|
| }
|
|
|
| @@ -976,62 +1082,6 @@
|
| return computeLUB(firstType, secondType, compiler);
|
| }
|
|
|
| - /**
|
| - * Computes the parameter types of [element], based on all call sites we
|
| - * have collected on that [element]. This method can only be called after
|
| - * we have analyzed all elements in the world.
|
| - */
|
| - void updateArgumentsType(FunctionElement element) {
|
| - assert(hasAnalyzedAll);
|
| - if (methodsThatCanBeClosurized.contains(element)) return;
|
| - // A [noSuchMethod] method can be the target of any call, with
|
| - // any number of arguments. For simplicity, we just do not
|
| - // infer any parameter types for [noSuchMethod].
|
| - if (element.name == Compiler.NO_SUCH_METHOD) return;
|
| - FunctionSignature signature = element.computeSignature(compiler);
|
| -
|
| - if (typeOfArguments[element] == null || typeOfArguments[element].isEmpty) {
|
| - signature.forEachParameter((Element parameter) {
|
| - typeOf.remove(parameter);
|
| - });
|
| - return;
|
| - }
|
| -
|
| - int parameterIndex = 0;
|
| - bool changed = false;
|
| - bool visitingOptionalParameter = false;
|
| - signature.forEachParameter((Element parameter) {
|
| - if (parameter == signature.firstOptionalParameter) {
|
| - visitingOptionalParameter = true;
|
| - }
|
| - TypeMask type;
|
| - typeOfArguments[element].forEach((_, ArgumentsTypes arguments) {
|
| - if (!visitingOptionalParameter) {
|
| - type = computeLubFor(
|
| - type, arguments.positional[parameterIndex], parameter);
|
| - } else {
|
| - TypeMask argumentType = signature.optionalParametersAreNamed
|
| - ? arguments.named[parameter.name]
|
| - : parameterIndex < arguments.positional.length
|
| - ? arguments.positional[parameterIndex]
|
| - : null;
|
| - if (argumentType == null) {
|
| - argumentType = defaultTypeOfParameter[parameter];
|
| - }
|
| - assert(argumentType != null);
|
| - type = computeLubFor(type, argumentType, parameter);
|
| - }
|
| - });
|
| - if (type == null) type = new TypeMask.nonNullEmpty();
|
| - if (recordType(parameter, type)) {
|
| - changed = true;
|
| - }
|
| - parameterIndex++;
|
| - });
|
| -
|
| - if (changed) enqueueAgain(element);
|
| - }
|
| -
|
| TypeMask handleIntrisifiedSelector(Selector selector,
|
| ArgumentsTypes arguments) {
|
| if (selector.mask != intType) return null;
|
| @@ -1116,10 +1166,9 @@
|
| Element element,
|
| TypeMask argumentType,
|
| CallSite constraint) {
|
| - Map<Node, TypeMask> map =
|
| - typeOfFields.putIfAbsent(element, () => new Map<Node, TypeMask>());
|
| - map[node] = argumentType;
|
| - bool changed = typeOf[element] != argumentType;
|
| + TypeInformation info = typeInformationOf(element);
|
| + info.addAssignment(node, argumentType);
|
| + bool changed = info.type != argumentType;
|
| if (constraint != null && constraint != setterConstraints[node]) {
|
| changed = true;
|
| setterConstraints[node] = constraint;
|
| @@ -1131,9 +1180,10 @@
|
| }
|
| }
|
|
|
| - TypeMask computeFieldTypeWithConstraints(Element element, Map types) {
|
| + TypeMask computeTypeWithConstraints(Element element,
|
| + Map<Node, TypeMask> types) {
|
| List<CallSite> constraints = <CallSite>[];
|
| - TypeMask fieldType;
|
| + TypeMask elementType;
|
| types.forEach((Node node, TypeMask mask) {
|
| CallSite constraint = setterConstraints[node];
|
| if (constraint != null) {
|
| @@ -1141,17 +1191,18 @@
|
| // use its type.
|
| constraints.add(constraint);
|
| } else {
|
| - fieldType = computeLubFor(fieldType, mask, element);
|
| + elementType = computeLubFor(elementType, mask, element);
|
| }
|
| });
|
|
|
| - if (!constraints.isEmpty && !isDynamicType(fieldType)) {
|
| + if (!constraints.isEmpty && !isDynamicType(elementType)) {
|
| // Now that we have found a type, we go over the collected
|
| // constraints, and make sure they apply to the found type. We
|
| // update [typeOf] to make sure [typeOfSelector] knows the field
|
| // type.
|
| - TypeMask existing = typeOf[element];
|
| - typeOf[element] = fieldType;
|
| + TypeInformation info = typeInformationOf(element);
|
| + TypeMask existing = info.type;
|
| + info.type = elementType;
|
|
|
| for (CallSite constraint in constraints) {
|
| Selector selector = constraint.selector;
|
| @@ -1159,8 +1210,8 @@
|
| if (selector.isOperator()) {
|
| // If the constraint is on an operator, we type the receiver
|
| // to be the field.
|
| - if (fieldType != null) {
|
| - selector = new TypedSelector(fieldType, selector);
|
| + if (elementType != null) {
|
| + selector = new TypedSelector(elementType, selector);
|
| }
|
| type = handleIntrisifiedSelector(selector, constraint.arguments);
|
| if (type == null) type = typeOfSelector(selector);
|
| @@ -1169,18 +1220,14 @@
|
| assert(selector.isGetter());
|
| type = typeOfSelector(selector);
|
| }
|
| - fieldType = computeLUB(fieldType, type, compiler);
|
| + elementType = computeLUB(elementType, type, compiler);
|
| }
|
| - if (existing == null) {
|
| - typeOf.remove(element);
|
| - } else {
|
| - typeOf[element] = existing;
|
| - }
|
| + info.type = existing;
|
| }
|
| - if (fieldType == null) {
|
| - fieldType = new TypeMask.nonNullEmpty();
|
| + if (elementType == null) {
|
| + elementType = new TypeMask.nonNullEmpty();
|
| }
|
| - return fieldType;
|
| + return elementType;
|
| }
|
|
|
| /**
|
| @@ -1192,13 +1239,11 @@
|
| if (isNativeElement(element)) return;
|
| assert(hasAnalyzedAll);
|
|
|
| - if (typeOfFields[element] == null || typeOfFields[element].isEmpty) {
|
| - typeOf.remove(element);
|
| - return;
|
| - }
|
| + TypeInformation info = typeInformationOf(element);
|
| + Map<Node, TypeMask> assignments = info.assignments;
|
| + if (assignments.isEmpty) return;
|
|
|
| - TypeMask fieldType = computeFieldTypeWithConstraints(
|
| - element, typeOfFields[element]);
|
| + TypeMask fieldType = computeTypeWithConstraints(element, assignments);
|
|
|
| // If the type of [element] has changed, re-analyze its users.
|
| if (recordType(element, fieldType)) {
|
| @@ -1222,9 +1267,8 @@
|
| // being tracked in the [classInfoForFinalFields] map.
|
| if (constructor == field) return;
|
| assert(field.modifiers.isFinal() || field.modifiers.isConst());
|
| - ClassElement cls = constructor.getEnclosingClass();
|
| - ClassInfoForFinalFields info = classInfoForFinalFields[cls.implementation];
|
| - info.recordFinalFieldType(node, constructor, field, type);
|
| + TypeInformation info = typeInformationOf(field);
|
| + info.addAssignment(node, type);
|
| }
|
|
|
| /**
|
| @@ -1234,23 +1278,26 @@
|
| */
|
| void doneAnalyzingGenerativeConstructor(Element constructor) {
|
| ClassElement cls = constructor.getEnclosingClass();
|
| - ClassInfoForFinalFields info = classInfoForFinalFields[cls.implementation];
|
| + ClassTypeInformation info = classInfoForFinalFields[cls.implementation];
|
| info.doneAnalyzingGenerativeConstructor(constructor);
|
| if (info.isDone) {
|
| - updateFinalFieldsType(info);
|
| + updateFinalFieldsType(info, constructor.getEnclosingClass());
|
| }
|
| }
|
|
|
| /**
|
| * Updates types of final fields listed in [info].
|
| */
|
| - void updateFinalFieldsType(ClassInfoForFinalFields info) {
|
| + void updateFinalFieldsType(ClassTypeInformation info, ClassElement cls) {
|
| assert(info.isDone);
|
| - info.typesOfFinalFields.forEach((Element field,
|
| - Map<Node, TypeMask> types) {
|
| + cls.forEachInstanceField((_, Element field) {
|
| if (isNativeElement(field)) return;
|
| - assert(field.modifiers.isFinal());
|
| - TypeMask fieldType = computeFieldTypeWithConstraints(field, types);
|
| + if (!field.modifiers.isFinal()) return;
|
| + // If the field is being set at its declaration site, it is not
|
| + // being tracked in the [classInfoForFinalFields] map.
|
| + if (field.parseNode(compiler).asSendSet() != null) return;
|
| + TypeInformation info = typeInformationOf(field);
|
| + TypeMask fieldType = computeTypeWithConstraints(field, info.assignments);
|
| if (recordType(field, fieldType)) {
|
| enqueueCallersOf(field);
|
| }
|
| @@ -1349,16 +1396,16 @@
|
|
|
| FunctionElement function = analyzedElement;
|
| if (inferrer.hasAnalyzedAll) {
|
| - inferrer.updateArgumentsType(function);
|
| + inferrer.updateAllParametersOf(function);
|
| }
|
| FunctionSignature signature = function.computeSignature(compiler);
|
| signature.forEachOptionalParameter((element) {
|
| Node node = element.parseNode(compiler);
|
| Send send = node.asSendSet();
|
| - inferrer.defaultTypeOfParameter[element] = (send == null)
|
| + ParameterTypeInformation info = inferrer.typeInformationOf(element);
|
| + info.defaultType = (send == null)
|
| ? inferrer.nullType
|
| : visit(send.arguments.head);
|
| - assert(inferrer.defaultTypeOfParameter[element] != null);
|
| });
|
|
|
| if (analyzedElement.isNative()) {
|
|
|