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

Unified Diff: sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart

Issue 16773003: Refactor type inference visitor to move all the logic of locals handling in a specific visitor. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 6 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart
===================================================================
--- sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart (revision 23842)
+++ sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart (working copy)
@@ -2,1339 +2,52 @@
// 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.
-library simple_types_inferrer;
+part of simple_types_inferrer;
-import 'dart:collection' show Queue, LinkedHashSet;
-
-import '../closure.dart' show ClosureClassMap, ClosureScope;
-import '../dart_types.dart'
- show DartType, InterfaceType, FunctionType, TypeKind;
-import '../elements/elements.dart';
-import '../native_handler.dart' as native;
-import '../tree/tree.dart';
-import '../util/util.dart' show Link;
-import 'types.dart' show TypesInferrer, FlatTypeMask, TypeMask;
-
-// BUG(8802): There's a bug in the analyzer that makes the re-export
-// of Selector from dart2jslib.dart fail. For now, we work around that
-// by importing universe.dart explicitly and disabling the re-export.
-import '../dart2jslib.dart' hide Selector, TypedSelector;
-import '../universe/universe.dart' show Selector, SideEffects, TypedSelector;
-
-/**
- * A work queue that ensures there are no duplicates, and adds and
- * removes in FIFO.
- */
-class WorkSet<E extends Element> {
- final Queue<E> queue = new Queue<E>();
- final Set<E> elementsInQueue = new Set<E>();
-
- void add(E element) {
- element = element.implementation;
- if (elementsInQueue.contains(element)) return;
- queue.addLast(element);
- elementsInQueue.add(element);
- }
-
- E remove() {
- E element = queue.removeFirst();
- elementsInQueue.remove(element);
- return element;
- }
-
- bool get isEmpty => queue.isEmpty;
-
- int get length => queue.length;
-}
-
-/**
- * Placeholder for type information of final fields of classes.
- */
-class ClassInfoForFinalFields {
- /**
- * Maps a final field to a map from generative constructor to the
- * inferred type of the field in that generative constructor.
- */
- final Map<Element, Map<Node, TypeMask>> typesOfFinalFields =
- new Map<Element, Map<Node, TypeMask>>();
-
- /**
- * 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
- * re-analyzing one, we know that once [constructorsToVisitCount]
- * reaches to 0, all generative constructors have been analyzed.
- */
- int constructorsToVisitCount;
-
- ClassInfoForFinalFields(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].
- */
- void doneAnalyzingGenerativeConstructor(Element constructor) {
- if (constructorsToVisitCount != 0) constructorsToVisitCount--;
- }
-
- /**
- * Returns whether all generative constructors of the class have
- * been analyzed.
- */
- bool get isDone => constructorsToVisitCount == 0;
-}
-
-/**
- * A sentinel type mask class used by the inferrer for the give up
- * type, and the dynamic type.
- */
-class SentinelTypeMask extends FlatTypeMask {
- final String name;
-
- SentinelTypeMask(this.name) : super(null, 0, false);
-
- bool operator==(other) {
- return identical(this, other);
- }
-
- TypeMask nullable() {
- throw 'Unsupported operation';
- }
-
- TypeMask intersection(TypeMask other, Compiler compiler) {
- return other;
- }
-
- bool get isNullable => true;
-
- String toString() => '$name sentinel type mask';
-}
-
-final OPTIMISTIC = 0;
-final RETRY = 1;
-final PESSIMISTIC = 2;
-
-class SimpleTypesInferrer extends TypesInferrer {
- InternalSimpleTypesInferrer internal;
- Compiler compiler;
-
- SimpleTypesInferrer(Compiler compiler) :
- compiler = compiler,
- internal = new InternalSimpleTypesInferrer(compiler, OPTIMISTIC);
-
- TypeMask get dynamicType => internal.dynamicType;
- TypeMask get nullType => internal.nullType;
- TypeMask get intType => internal.intType;
- TypeMask get doubleType => internal.doubleType;
- TypeMask get numType => internal.numType;
- TypeMask get boolType => internal.boolType;
- TypeMask get functionType => internal.functionType;
- TypeMask get listType => internal.listType;
- TypeMask get constListType => internal.constListType;
- TypeMask get fixedListType => internal.fixedListType;
- TypeMask get growableListType => internal.growableListType;
- TypeMask get mapType => internal.mapType;
- TypeMask get constMapType => internal.constMapType;
- TypeMask get stringType => internal.stringType;
- TypeMask get typeType => internal.typeType;
-
- TypeMask getReturnTypeOfElement(Element element) {
- if (compiler.disableTypeInference) return dynamicType;
- return internal.getReturnTypeOfElement(element);
- }
- TypeMask getTypeOfElement(Element element) {
- if (compiler.disableTypeInference) return dynamicType;
- return internal.getTypeOfElement(element);
- }
- TypeMask getTypeOfNode(Element owner, Node node) {
- if (compiler.disableTypeInference) return dynamicType;
- return internal.getTypeOfNode(owner, node);
- }
- TypeMask getTypeOfSelector(Selector selector) {
- if (compiler.disableTypeInference) return dynamicType;
- return internal.getTypeOfSelector(selector);
- }
-
- bool analyzeMain(Element element) {
- if (compiler.disableTypeInference) return true;
- bool result = internal.analyzeMain(element);
- if (internal.optimismState == OPTIMISTIC) return result;
- assert(internal.optimismState == RETRY);
-
- // Discard the inferrer and start again with a pessimistic one.
- internal = new InternalSimpleTypesInferrer(compiler, PESSIMISTIC);
- return internal.analyzeMain(element);
- }
-}
-
-
-
-class InternalSimpleTypesInferrer extends TypesInferrer {
- /**
- * Maps an element to its callers.
- */
- final Map<Element, Set<Element>> callersOf =
- new Map<Element, Set<Element>>();
-
- /**
- * Maps an element to its return type.
- */
- final Map<Element, TypeMask> returnTypeOf =
- new Map<Element, 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
- * for that type. For example:
- *
- * [: field++ ], or [: field += 42 :], the constraint is on the
- * operator+, and we make sure that a typed selector with the found
- * type returns that type.
- *
- * [: field = other.field :], the constraint in on the [:field]
- * getter selector, and we make sure that the getter selector
- * returns that type.
- *
- */
- final Map<Node, Selector> setterConstraints = new Map<Node, Selector>();
-
- /**
- * The work list of the inferrer.
- */
- final WorkSet<Element> workSet = new WorkSet<Element>();
-
- /**
- * Heuristic for avoiding too many re-analysis of an element.
- */
- final int MAX_ANALYSIS_COUNT_PER_ELEMENT = 5;
-
- int optimismState;
-
- /**
- * Sentinel used by the inferrer to notify that it does not know
- * the type of a specific element.
- */
- TypeMask dynamicType;
- bool isDynamicType(TypeMask type) => identical(type, dynamicType);
-
- TypeMask nullType;
- TypeMask intType;
- TypeMask doubleType;
- TypeMask numType;
- TypeMask boolType;
- TypeMask functionType;
- TypeMask listType;
- TypeMask constListType;
- TypeMask fixedListType;
- TypeMask growableListType;
- TypeMask mapType;
- TypeMask constMapType;
- TypeMask stringType;
- TypeMask typeType;
-
- /**
- * These are methods that are expected to return only bool. We optimistically
- * assume that they do this. If we later find a contradiction, we have to
- * restart the simple types inferrer, because it normally goes from less
- * optimistic to more optimistic as it refines its type information. Without
- * this optimization, method names that are mutually recursive in the tail
- * position will be typed as dynamic.
- */
- // TODO(erikcorry): Autogenerate the alphanumeric names in this set.
- Set<SourceString> PREDICATES = new Set<SourceString>.from([
- const SourceString('=='),
- const SourceString('<='),
- const SourceString('>='),
- const SourceString('>'),
- const SourceString('<'),
- const SourceString('moveNext')]);
-
- bool shouldOptimisticallyOptimizeToBool(Element element) {
- return element == compiler.identicalFunction.implementation
- || (element.isFunction()
- && element.isInstanceMember()
- && PREDICATES.contains(element.name));
- }
-
- final Compiler compiler;
-
- // Times the computation of re-analysis of methods.
- final Stopwatch recomputeWatch = new Stopwatch();
- // Number of re-analysis.
- int recompiles = 0;
-
- /**
- * Set to [true] when the analysis has analyzed all elements in the
- * world.
- */
- bool hasAnalyzedAll = false;
-
- /**
- * The number of elements in the world.
- */
- int numberOfElementsToAnalyze;
-
- InternalSimpleTypesInferrer(this.compiler, this.optimismState);
-
- /**
- * Main entry point of the inferrer. Analyzes all elements that the resolver
- * found as reachable. Returns whether it succeeded.
- */
- bool analyzeMain(Element element) {
- initializeTypes();
- buildWorkQueue();
- int analyzed = 0;
- compiler.progress.reset();
- int maxReanalysis = (numberOfElementsToAnalyze * 1.5).toInt();
- do {
- if (compiler.progress.elapsedMilliseconds > 500) {
- compiler.log('Inferred $analyzed methods.');
- compiler.progress.reset();
- }
- element = workSet.remove();
- if (element.isErroneous()) continue;
-
- bool wasAnalyzed = analyzeCount.containsKey(element);
- if (wasAnalyzed) {
- recompiles++;
- if (recompiles >= maxReanalysis) {
- compiler.log('Ran out of budget for inferring.');
- break;
- }
- if (compiler.verbose) recomputeWatch.start();
- }
- bool changed =
- compiler.withCurrentElement(element, () => analyze(element));
- if (optimismState == RETRY) return true; // Abort.
- analyzed++;
- if (wasAnalyzed && compiler.verbose) {
- recomputeWatch.stop();
- }
- checkAnalyzedAll();
- if (changed) {
- // If something changed during the analysis of [element], put back
- // callers of it in the work list.
- enqueueCallersOf(element);
- }
- } while (!workSet.isEmpty);
- dump();
- clear();
- return true;
- }
-
- /**
- * Query method after the analysis to know the type of [element].
- */
- TypeMask getReturnTypeOfElement(Element element) {
- return getTypeIfValuable(returnTypeOf[element]);
- }
-
- TypeMask getTypeOfElement(Element element) {
- return getTypeIfValuable(typeOf[element]);
- }
-
- TypeMask getTypeOfSelector(Selector selector) {
- return getTypeIfValuable(typeOfSelector(selector));
- }
-
- bool isTypeValuable(TypeMask returnType) {
- return !isDynamicType(returnType);
- }
-
- TypeMask getTypeIfValuable(TypeMask returnType) {
- return isTypeValuable(returnType) ? returnType : null;
- }
-
- /**
- * Query method after the analysis to know the type of [node],
- * defined in the context of [owner].
- */
- TypeMask getTypeOfNode(Element owner, Node node) {
- var elements = compiler.enqueuer.resolution.resolvedElements[owner];
- // TODO(ngeoffray): Not sure why the resolver would put a null
- // mapping.
- if (elements == null) return null;
- Selector selector = elements.getSelector(node);
- // TODO(ngeoffray): Should the builder call this method with a
- // SendSet?
- if (selector == null || selector.isSetter() || selector.isIndexSet()) {
- return null;
- }
- return getTypeIfValuable(typeOfSelector(selector));
- }
-
- void checkAnalyzedAll() {
- if (hasAnalyzedAll) return;
- if (analyzeCount.length != 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);
- }
-
- /**
- * Enqueues [e] in the work queue if it is valuable.
- */
- void enqueueAgain(Element e) {
- int count = analyzeCount[e];
- 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);
- }
- }
-
- /**
- * Builds the initial work queue by adding all resolved elements in
- * the work queue, ordered by the number of selectors they use. This
- * order is benficial for the analysis of return types, but we may
- * have to refine it once we analyze parameter types too.
- */
- void buildWorkQueue() {
- int max = 0;
- Map<int, Set<Element>> methodSizes = new Map<int, Set<Element>>();
- compiler.enqueuer.resolution.resolvedElements.forEach(
- (Element element, TreeElementMapping mapping) {
- if (element.impliesType()) return;
- assert(invariant(element,
- element.isField() ||
- element.isFunction() ||
- element.isGenerativeConstructor() ||
- element.isGetter() ||
- element.isSetter(),
- message: 'Unexpected element kind: ${element.kind}'));
- // TODO(ngeoffray): Not sure why the resolver would put a null
- // mapping.
- if (mapping == null) return;
- if (element.isAbstract(compiler)) return;
- // Add the relational operators, ==, !=, <, etc., before any
- // others, as well as the identical function.
- if (shouldOptimisticallyOptimizeToBool(element)) {
- workSet.add(element);
- // Optimistically assume that they return bool. We may need to back
- // out of this.
- if (optimismState == OPTIMISTIC) {
- returnTypeOf[element.implementation] = boolType;
- }
- } else {
- // Put the other operators in buckets by length, later to be added in
- // length order.
- int length = mapping.selectors.length;
- max = length > max ? length : max;
- Set<Element> set = methodSizes.putIfAbsent(
- length, () => new LinkedHashSet<Element>());
- set.add(element);
- }
- });
-
- // This iteration assumes the [WorkSet] is FIFO.
- for (int i = 0; i <= max; i++) {
- Set<Element> set = methodSizes[i];
- if (set != null) {
- set.forEach((e) { workSet.add(e); });
- }
- }
- numberOfElementsToAnalyze = workSet.length;
-
- // Build the [classInfoForFinalFields] map by iterating over all
- // seen classes and counting the number of their generative
- // constructors.
- // We iterate over the seen classes and not the instantiated ones,
- // because we also need to analyze the final fields of super
- // classes that are not instantiated.
- compiler.enqueuer.resolution.seenClasses.forEach((ClassElement cls) {
- int constructorCount = 0;
- cls.forEachMember((_, member) {
- if (member.isGenerativeConstructor()
- && compiler.enqueuer.resolution.isProcessed(member)) {
- constructorCount++;
- }
- });
- classInfoForFinalFields[cls.implementation] =
- new ClassInfoForFinalFields(constructorCount);
- });
- }
-
- // TODO(ngeoffray): Get rid of this method. Unit tests don't always
- // ensure these classes are resolved.
- rawTypeOf(ClassElement cls) {
- cls.ensureResolved(compiler);
- assert(cls.rawType != null);
- return cls.rawType;
- }
-
- void initializeTypes() {
- nullType = new TypeMask.empty();
-
- Backend backend = compiler.backend;
- intType = new TypeMask.nonNullExact(
- rawTypeOf(backend.intImplementation));
- doubleType = new TypeMask.nonNullExact(
- rawTypeOf(backend.doubleImplementation));
- numType = new TypeMask.nonNullSubclass(
- rawTypeOf(backend.numImplementation));
- stringType = new TypeMask.nonNullExact(
- rawTypeOf(backend.stringImplementation));
- boolType = new TypeMask.nonNullExact(
- rawTypeOf(backend.boolImplementation));
-
- listType = new TypeMask.nonNullExact(
- rawTypeOf(backend.listImplementation));
- constListType = new TypeMask.nonNullExact(
- rawTypeOf(backend.constListImplementation));
- fixedListType = new TypeMask.nonNullExact(
- rawTypeOf(backend.fixedListImplementation));
- growableListType = new TypeMask.nonNullExact(
- rawTypeOf(backend.growableListImplementation));
-
- mapType = new TypeMask.nonNullSubtype(
- rawTypeOf(backend.mapImplementation));
- constMapType = new TypeMask.nonNullSubtype(
- rawTypeOf(backend.constMapImplementation));
- functionType = new TypeMask.nonNullSubtype(
- rawTypeOf(backend.functionImplementation));
- typeType = new TypeMask.nonNullExact(
- rawTypeOf(backend.typeImplementation));
-
- dynamicType = new TypeMask.subclass(rawTypeOf(compiler.objectClass));
- }
-
- dump() {
- int interestingTypes = 0;
- returnTypeOf.forEach((Element element, TypeMask type) {
- if (type != nullType && !isDynamicType(type)) {
- interestingTypes++;
- }
- });
- typeOf.forEach((Element element, TypeMask type) {
- if (type != nullType && !isDynamicType(type)) {
- interestingTypes++;
- }
- });
-
- compiler.log('Type inferrer re-analyzed methods $recompiles times '
- 'in ${recomputeWatch.elapsedMilliseconds} ms.');
- compiler.log('Type inferrer found $interestingTypes interesting '
- 'types.');
- }
-
- /**
- * Clear data structures that are not used after the analysis.
- */
- void clear() {
- callersOf.clear();
- analyzeCount.clear();
- classInfoForFinalFields.clear();
- typeOfFields.clear();
- setterConstraints.clear();
- }
-
- bool analyze(Element element) {
- if (element.isForwardingConstructor) {
- element = element.targetConstructor;
- }
- SimpleTypeInferrerVisitor visitor =
- new SimpleTypeInferrerVisitor(element, compiler, this);
- TypeMask returnType = visitor.run();
- if (analyzeCount.containsKey(element)) {
- analyzeCount[element]++;
- } else {
- analyzeCount[element] = 1;
- }
- if (element.isGenerativeConstructor()) {
- // We always know the return type of a generative constructor.
- return false; // Nothing changed.
- } else if (element.isField()) {
- Node node = element.parseNode(compiler);
- if (element.modifiers.isFinal() || element.modifiers.isConst()) {
- // If [element] is final and has an initializer, we record
- // the inferred type.
- if (node.asSendSet() != null) {
- return recordType(element, returnType);
- }
- return false;
- } else if (node.asSendSet() == null) {
- // Only update types of static fields if there is no
- // assignment. Instance fields are dealt with in the constructor.
- if (Elements.isStaticOrTopLevelField(element)) {
- recordNonFinalFieldElementType(node, element, returnType, null);
- }
- return false;
- } else {
- recordNonFinalFieldElementType(node, element, returnType, null);
- // [recordNonFinalFieldElementType] takes care of re-enqueuing
- // users of the field.
- return false;
- }
- } else {
- return recordReturnType(element, returnType);
- }
- }
-
- bool recordType(Element analyzedElement, TypeMask type) {
- assert(type != null);
- assert(analyzedElement.isField()
- || analyzedElement.isParameter()
- || analyzedElement.isFieldParameter());
- return internalRecordType(analyzedElement, type, typeOf);
- }
-
- /**
- * Records [returnType] as the return type of [analyzedElement].
- * Returns whether the new type is worth recompiling the callers of
- * [analyzedElement].
- */
- bool recordReturnType(Element analyzedElement, TypeMask returnType) {
- assert(analyzedElement.implementation == analyzedElement);
- if (optimismState == OPTIMISTIC
- && shouldOptimisticallyOptimizeToBool(analyzedElement)
- && returnType != returnTypeOf[analyzedElement]) {
- // 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);
- }
-
- bool isNativeElement(Element element) {
- if (element.isNative()) return true;
- return element.isMember()
- && element.getEnclosingClass().isNative()
- && element.isField();
- }
-
- bool internalRecordType(Element analyzedElement,
- TypeMask newType,
- Map<Element, TypeMask> types) {
- 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) {
- assert(annotation is FunctionType);
- annotation = annotation.returnType;
- }
- newType = narrowType(newType, annotation);
- }
-
- // 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;
- }
-
- /**
- * Returns the return type of [element]. Returns [:dynamic:] if
- * [element] has not been analyzed yet.
- */
- TypeMask returnTypeOfElement(Element element) {
- element = element.implementation;
- if (element.isGenerativeConstructor()) {
- return returnTypeOf.putIfAbsent(element, () {
- return new TypeMask.nonNullExact(
- rawTypeOf(element.getEnclosingClass()));
- });
- } else if (element.isNative()) {
- return returnTypeOf.putIfAbsent(element, () {
- var elementType = element.computeType(compiler);
- if (elementType.kind != TypeKind.FUNCTION) {
- return dynamicType;
- }
- return typeOfNativeBehavior(
- native.NativeBehavior.ofMethod(element, compiler));
- });
- }
- TypeMask returnType = returnTypeOf[element];
- if (returnType == null) {
- if ((compiler.trustTypeAnnotations || compiler.enableTypeAssertions)
- && (element.isFunction()
- || element.isGetter()
- || element.isFactoryConstructor())) {
- FunctionType functionType = element.computeType(compiler);
- returnType = narrowType(dynamicType, functionType.returnType);
- } else {
- returnType = dynamicType;
- }
- }
- return returnType;
- }
-
- TypeMask typeOfNativeBehavior(native.NativeBehavior nativeBehavior) {
- if (nativeBehavior == null) return dynamicType;
- List typesReturned = nativeBehavior.typesReturned;
- if (typesReturned.isEmpty) return dynamicType;
- TypeMask returnType;
- for (var type in typesReturned) {
- TypeMask mappedType;
- if (type == native.SpecialType.JsObject) {
- mappedType = new TypeMask.nonNullExact(rawTypeOf(compiler.objectClass));
- } else if (type == native.SpecialType.JsArray) {
- mappedType = listType;
- } else if (type.element == compiler.stringClass) {
- mappedType = stringType;
- } else if (type.element == compiler.intClass) {
- mappedType = intType;
- } else if (type.element == compiler.doubleClass) {
- mappedType = doubleType;
- } else if (type.element == compiler.numClass) {
- mappedType = numType;
- } else if (type.element == compiler.boolClass) {
- mappedType = boolType;
- } else if (type.element == compiler.nullClass) {
- mappedType = nullType;
- } else if (type.isVoid) {
- mappedType = nullType;
- } else if (type.isDynamic) {
- return dynamicType;
- } else if (compiler.world.hasAnySubclass(type.element)) {
- mappedType = new TypeMask.nonNullSubclass(rawTypeOf(type.element));
- } else if (compiler.world.hasAnySubtype(type.element)) {
- mappedType = new TypeMask.nonNullSubtype(rawTypeOf(type.element));
- } else {
- mappedType = new TypeMask.nonNullExact(rawTypeOf(type.element));
- }
- returnType = computeLUB(returnType, mappedType);
- if (!isTypeValuable(returnType)) {
- returnType = dynamicType;
- break;
- }
- }
- return returnType;
- }
-
-
- /**
- * Returns the type of [element]. Returns [:dynamic:] if
- * [element] has not been analyzed yet.
- */
- TypeMask typeOfElement(Element element) {
- element = element.implementation;
- if (isNativeElement(element) && element.isField()) {
- var type = typeOf.putIfAbsent(element, () {
- InterfaceType rawType = element.computeType(compiler).asRaw();
- return rawType.isDynamic ? dynamicType : new TypeMask.subtype(rawType);
- });
- assert(type != null);
- return type;
- }
- TypeMask type = typeOf[element];
- if (type == null) {
- if ((compiler.trustTypeAnnotations
- && (element.isField()
- || element.isParameter()
- || element.isVariable()))
- // Parameters are being checked by the method, and we can
- // therefore only trust their type after the checks.
- || (compiler.enableTypeAssertions
- && (element.isField() || element.isVariable()))) {
- type = narrowType(dynamicType, element.computeType(compiler));
- } else {
- type = dynamicType;
- }
- }
+TypeMask narrowType(TypeMask type,
+ DartType annotation,
+ Compiler compiler,
+ {bool isNullable: true}) {
+ if (annotation.isDynamic) return type;
+ if (annotation.isMalformed) return type;
+ if (annotation.isVoid) return compiler.typesTask.typesInferrer.nullType;
+ if (annotation.element == compiler.objectClass) return type;
+ TypeMask otherType;
+ if (annotation.kind == TypeKind.TYPEDEF
+ || annotation.kind == TypeKind.FUNCTION) {
+ otherType = compiler.typesTask.typesInferrer.functionType;
+ } else if (annotation.kind == TypeKind.TYPE_VARIABLE) {
return type;
+ } else {
+ assert(annotation.kind == TypeKind.INTERFACE);
+ otherType = new TypeMask.nonNullSubtype(annotation);
}
-
- /**
- * Returns the union of the types of all elements that match
- * the called [selector].
- */
- TypeMask typeOfSelector(Selector selector) {
- // Bailout for closure calls. We're not tracking types of
- // closures.
- if (selector.isClosureCall()) return dynamicType;
-
- TypeMask result;
- iterateOverElements(selector, (Element element) {
- assert(element.isImplementation);
- TypeMask type = typeOfElementWithSelector(element, selector);
- result = computeLUB(result, type);
- return isTypeValuable(result);
- });
- if (result == null) {
- result = new TypeMask.nonNullEmpty();
- }
- return result;
- }
-
- TypeMask typeOfElementWithSelector(Element element, Selector selector) {
- if (element.name == Compiler.NO_SUCH_METHOD
- && selector.name != element.name) {
- // An invocation can resolve to a [noSuchMethod], in which case
- // we get the return type of [noSuchMethod].
- return returnTypeOfElement(element);
- } else if (selector.isGetter()) {
- if (element.isFunction()) {
- // [functionType] is null if the inferrer did not run.
- return functionType == null ? dynamicType : functionType;
- } else if (element.isField()) {
- return typeOfElement(element);
- } else if (Elements.isUnresolved(element)) {
- return dynamicType;
- } else {
- assert(element.isGetter());
- return returnTypeOfElement(element);
- }
- } else {
- return returnTypeOfElement(element);
- }
- }
-
- bool isNotClosure(Element element) {
- // If the outermost enclosing element of [element] is [element]
- // itself, we know it cannot be a closure.
- Element outermost = element.getOutermostEnclosingMemberOrTopLevel();
- return outermost.declaration == element.declaration;
- }
-
- void addCaller(Element caller, Element callee) {
- assert(caller.isImplementation);
- assert(callee.isImplementation);
- assert(isNotClosure(caller));
- Set<Element> callers = callersOf.putIfAbsent(
- callee, () => new Set<Element>());
- 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;
- }
-
- void updateSideEffects(SideEffects sideEffects,
- Selector selector,
- Element callee) {
- if (callee.isField()) {
- if (callee.isInstanceMember()) {
- if (selector.isSetter()) {
- sideEffects.setChangesInstanceProperty();
- } else if (selector.isGetter()) {
- sideEffects.setDependsOnInstancePropertyStore();
- } else {
- sideEffects.setAllSideEffects();
- sideEffects.setDependsOnSomething();
- }
- } else {
- if (selector.isSetter()) {
- sideEffects.setChangesStaticProperty();
- } else if (selector.isGetter()) {
- sideEffects.setDependsOnStaticPropertyStore();
- } else {
- sideEffects.setAllSideEffects();
- sideEffects.setDependsOnSomething();
- }
- }
- } else if (callee.isGetter() && !selector.isGetter()) {
- sideEffects.setAllSideEffects();
- sideEffects.setDependsOnSomething();
- } else {
- sideEffects.add(compiler.world.getSideEffectsOfElement(callee));
- }
- }
-
- /**
- * Registers that [caller] calls [callee] with the given
- * [arguments]. [constraint] is a setter constraint (see
- * [setterConstraints] documentation).
- */
- void registerCalledElement(Node node,
- Selector selector,
- Element caller,
- Element callee,
- ArgumentsTypes arguments,
- Selector constraint,
- SideEffects sideEffects,
- bool inLoop) {
- updateSideEffects(sideEffects, selector, callee);
-
- // Bailout for closure calls. We're not tracking types of
- // arguments for closures.
- if (callee.isInstanceMember() && selector.isClosureCall()) {
- return;
- }
- if (inLoop) {
- // For instance methods, we only register a selector called in a
- // loop if it is a typed selector, to avoid marking too many
- // methods as being called from within a loop. This cuts down
- // on the code bloat.
- // TODO(ngeoffray): We should move the filtering on the selector
- // in the backend. It is not the inferrer role to do this kind
- // of optimization.
- if (Elements.isStaticOrTopLevel(callee) || selector.mask != null) {
- compiler.world.addFunctionCalledInLoop(callee);
- }
- }
-
- assert(isNotClosure(caller));
- callee = callee.implementation;
- if (!analyzeCount.containsKey(caller)) {
- addCaller(caller, callee);
- }
-
- if (selector.isSetter() && callee.isField()) {
- recordNonFinalFieldElementType(
- node, callee, arguments.positional[0], constraint);
- return;
- } else if (selector.isGetter()) {
- assert(arguments == null);
- if (callee.isFunction()) {
- methodsThatCanBeClosurized.add(callee);
- }
- return;
- } else if (callee.isField()) {
- // We're not tracking closure calls.
- return;
- } else if (callee.isGetter()) {
- // Getters don't have arguments.
- return;
- }
- FunctionElement function = callee;
- if (function.computeSignature(compiler).parameterCount == 0) return;
-
- assert(arguments != null);
- bool isUseful = addArguments(node, callee, arguments);
- if (hasAnalyzedAll && isUseful) {
- enqueueAgain(callee);
- }
- }
-
- void unregisterCalledElement(Node node,
- Selector selector,
- Element caller,
- Element callee) {
- if (callee.isField()) {
- if (selector.isSetter()) {
- Map<Node, TypeMask> types = typeOfFields[callee];
- if (types == null || !types.containsKey(node)) return;
- types.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);
- }
- }
-
- /**
- * 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 = computeLUB(type, arguments.positional[parameterIndex]);
- } 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 = computeLUB(type, argumentType);
- }
- });
- if (recordType(parameter, type)) {
- changed = true;
- }
- parameterIndex++;
- });
-
- if (changed) enqueueAgain(element);
- }
-
- TypeMask handleIntrisifiedSelector(Selector selector,
- ArgumentsTypes arguments) {
- if (selector.mask != intType) return null;
- if (!selector.isCall() && !selector.isOperator()) return null;
- if (!arguments.named.isEmpty) return null;
- if (arguments.positional.length > 1) return null;
-
- SourceString name = selector.name;
- if (name == const SourceString('*')
- || name == const SourceString('+')
- || name == const SourceString('%')
- || name == const SourceString('remainder')) {
- return arguments.hasOnePositionalArgumentWithType(intType)
- ? intType
- : null;
- } else if (name == const SourceString('-')) {
- if (arguments.hasNoArguments()) return intType;
- if (arguments.hasOnePositionalArgumentWithType(intType)) return intType;
- return null;
- } else if (name == const SourceString('abs')) {
- return arguments.hasNoArguments() ? intType : null;
- }
- return null;
- }
-
- /**
- * Registers that [caller] calls an element matching [selector]
- * with the given [arguments].
- */
- TypeMask registerCalledSelector(Node node,
- Selector selector,
- TypeMask receiverType,
- Element caller,
- ArgumentsTypes arguments,
- Selector constraint,
- SideEffects sideEffects,
- bool inLoop) {
- TypeMask result;
- Iterable<Element> untypedTargets =
- compiler.world.allFunctions.filter(selector.asUntyped);
- Iterable<Element> typedTargets =
- compiler.world.allFunctions.filter(selector);
- for (Element element in untypedTargets) {
- element = element.implementation;
- if (!typedTargets.contains(element.declaration)) {
- unregisterCalledElement(node, selector, caller, element);
- } else {
- registerCalledElement(
- node, selector, caller, element, arguments,
- constraint, sideEffects, inLoop);
-
- if (!selector.isSetter()) {
- TypeMask type = handleIntrisifiedSelector(selector, arguments);
- if (type == null) type = typeOfElementWithSelector(element, selector);
- result = computeLUB(result, type);
- }
- }
- }
-
- if (result == null) {
- result = dynamicType;
- }
- return result;
- }
-
- /**
- * Applies [f] to all elements in the universe that match
- * [selector]. If [f] returns false, aborts the iteration.
- */
- void iterateOverElements(Selector selector, bool f(Element element)) {
- Iterable<Element> elements = compiler.world.allFunctions.filter(selector);
- for (Element e in elements) {
- if (!f(e.implementation)) return;
- }
- }
-
- /**
- * Records an assignment to [element] with the given
- * [argumentType].
- */
- void recordNonFinalFieldElementType(Node node,
- Element element,
- TypeMask argumentType,
- Selector constraint) {
- Map<Node, TypeMask> map =
- typeOfFields.putIfAbsent(element, () => new Map<Node, TypeMask>());
- map[node] = argumentType;
- bool changed = typeOf[element] != argumentType;
- if (constraint != null && constraint != setterConstraints[node]) {
- changed = true;
- setterConstraints[node] = constraint;
- }
- // If we have analyzed all elements, we can update the type of the
- // field right away.
- if (hasAnalyzedAll && changed) {
- updateNonFinalFieldType(element);
- }
- }
-
- TypeMask computeFieldTypeWithConstraints(Element element, Map types) {
- Set<Selector> constraints = new Set<Selector>();
- TypeMask fieldType;
- types.forEach((Node node, TypeMask mask) {
- Selector constraint = setterConstraints[node];
- if (constraint != null) {
- // If this update has a constraint, we collect it and don't
- // use its type.
- constraints.add(constraint);
- } else {
- fieldType = computeLUB(fieldType, mask);
- }
- });
-
- if (!constraints.isEmpty && !isDynamicType(fieldType)) {
- // 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;
-
- for (Selector constraint in constraints) {
- if (constraint.isOperator()) {
- // If the constraint is on an operator, we type the receiver
- // to be the field.
- if (fieldType != null) {
- constraint = new TypedSelector(fieldType, constraint);
- }
- } else {
- // Otherwise the constraint is on the form [: field = other.field :].
- assert(constraint.isGetter());
- }
- fieldType = computeLUB(fieldType, typeOfSelector(constraint));
- }
- if (existing == null) {
- typeOf.remove(element);
- } else {
- typeOf[element] = existing;
- }
- }
- return fieldType;
- }
-
- /**
- * Computes the type of [element], based on all assignments we have
- * collected on that [element]. This method can only be called after
- * we have analyzed all elements in the world.
- */
- void updateNonFinalFieldType(Element element) {
- if (isNativeElement(element)) return;
- assert(hasAnalyzedAll);
-
- if (typeOfFields[element] == null || typeOfFields[element].isEmpty) {
- typeOf.remove(element);
- return;
- }
-
- TypeMask fieldType = computeFieldTypeWithConstraints(
- element, typeOfFields[element]);
-
- // If the type of [element] has changed, re-analyze its users.
- if (recordType(element, fieldType)) {
- enqueueCallersOf(element);
- }
- }
-
- /**
- * Records in [classInfoForFinalFields] that [constructor] has
- * inferred [type] for the final [field].
- */
- void recordFinalFieldType(Node node,
- Element constructor,
- Element field,
- TypeMask type,
- Selector constraint) {
- if (constraint != null) {
- setterConstraints[node] = constraint;
- }
- // If the field is being set at its declaration site, it is not
- // 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);
- }
-
- /**
- * Records that we are done analyzing [constructor]. If all
- * generative constructors of its enclosing class have already been
- * analyzed, this method updates the types of final fields.
- */
- void doneAnalyzingGenerativeConstructor(Element constructor) {
- ClassElement cls = constructor.getEnclosingClass();
- ClassInfoForFinalFields info = classInfoForFinalFields[cls.implementation];
- info.doneAnalyzingGenerativeConstructor(constructor);
- if (info.isDone) {
- updateFinalFieldsType(info);
- }
- }
-
- /**
- * Updates types of final fields listed in [info].
- */
- void updateFinalFieldsType(ClassInfoForFinalFields info) {
- assert(info.isDone);
- info.typesOfFinalFields.forEach((Element field,
- Map<Node, TypeMask> types) {
- if (isNativeElement(field)) return;
- assert(field.modifiers.isFinal());
- TypeMask fieldType = computeFieldTypeWithConstraints(field, types);
- if (recordType(field, fieldType)) {
- enqueueCallersOf(field);
- }
- });
- }
-
- /**
- * Returns the least upper bound between [firstType] and
- * [secondType].
- */
- TypeMask computeLUB(TypeMask firstType, TypeMask secondType) {
- if (firstType == null) {
- return secondType;
- } else if (isDynamicType(secondType)) {
- return secondType;
- } else if (isDynamicType(firstType)) {
- return firstType;
- } else {
- TypeMask union = firstType.union(secondType, compiler);
- // TODO(kasperl): If the union isn't nullable it seems wasteful
- // to use dynamic. Fix that.
- return union.containsAll(compiler) ? dynamicType : union;
- }
- }
-
- TypeMask narrowType(TypeMask type,
- DartType annotation,
- {bool isNullable: true}) {
- if (annotation.isDynamic) return type;
- if (annotation.isMalformed) return type;
- if (annotation.isVoid) return nullType;
- if (annotation.element == compiler.objectClass) return type;
- TypeMask otherType;
- if (annotation.kind == TypeKind.TYPEDEF
- || annotation.kind == TypeKind.FUNCTION) {
- otherType = functionType;
- } else if (annotation.kind == TypeKind.TYPE_VARIABLE) {
- return type;
- } else {
- assert(annotation.kind == TypeKind.INTERFACE);
- otherType = new TypeMask.nonNullSubtype(annotation);
- }
- if (isNullable) otherType = otherType.nullable();
- if (type == null) return otherType;
- return type.intersection(otherType, compiler);
- }
+ if (isNullable) otherType = otherType.nullable();
+ if (type == null) return otherType;
+ return type.intersection(otherType, compiler);
}
/**
- * Placeholder for inferred arguments types on sends.
+ * Returns the least upper bound between [firstType] and
+ * [secondType].
*/
-class ArgumentsTypes {
- final List<TypeMask> positional;
- final Map<SourceString, TypeMask> named;
- ArgumentsTypes(this.positional, named)
- : this.named = (named == null) ? new Map<SourceString, TypeMask>() : named;
-
- int get length => positional.length + named.length;
-
- String toString() => "{ positional = $positional, named = $named }";
-
- bool operator==(other) {
- if (positional.length != other.positional.length) return false;
- if (named.length != other.named.length) return false;
- for (int i = 0; i < positional.length; i++) {
- if (positional[i] != other.positional[i]) return false;
- }
- named.forEach((name, type) {
- if (other.named[name] != type) return false;
- });
- return true;
+ TypeMask computeLUB(TypeMask firstType,
+ TypeMask secondType,
+ Compiler compiler) {
+ TypesInferrer inferrer = compiler.typesTask.typesInferrer;
+ TypeMask dynamicType = inferrer.dynamicType;
+ if (firstType == null) {
+ return secondType;
+ } else if (secondType == dynamicType) {
+ return secondType;
+ } else if (firstType == dynamicType) {
+ return firstType;
+ } else {
+ TypeMask union = firstType.union(secondType, compiler);
+ // TODO(kasperl): If the union isn't nullable it seems wasteful
+ // to use dynamic. Fix that.
+ return union.containsAll(compiler) ? dynamicType : union;
}
-
- bool hasNoArguments() => positional.isEmpty && named.isEmpty;
-
- bool hasOnePositionalArgumentWithType(TypeMask type) {
- return named.isEmpty && positional.length == 1 && positional[0] == type;
- }
}
/**
@@ -1372,16 +85,16 @@
TypeMask use(Element local) {
if (capturedAndBoxed.containsKey(local)) {
- return inferrer.typeOfElement(capturedAndBoxed[local]);
+ return inferrer.getTypeOfElement(capturedAndBoxed[local]);
}
return locals[local];
}
void update(Element local, TypeMask type) {
assert(type != null);
- if (inferrer.compiler.trustTypeAnnotations
- || inferrer.compiler.enableTypeAssertions) {
- type = inferrer.narrowType(type, local.computeType(inferrer.compiler));
+ Compiler compiler = inferrer.compiler;
+ if (compiler.trustTypeAnnotations || compiler.enableTypeAssertions) {
+ type = narrowType(type, local.computeType(compiler), compiler);
}
if (capturedAndBoxed.containsKey(local) || inTryBlock) {
// If a local is captured and boxed, or is set in a try block,
@@ -1390,7 +103,7 @@
// We don't know if an assignment in a try block
// will be executed, so all assigments in that block are
// potential types after we have left it.
- type = inferrer.computeLUB(locals[local], type);
+ type = computeLUB(locals[local], type, compiler);
}
locals[local] = type;
}
@@ -1426,7 +139,7 @@
} else if (!isCaptured && other.aborts && discardIfAborts) {
// Don't do anything.
} else {
- TypeMask type = inferrer.computeLUB(oldType, otherType);
+ TypeMask type = computeLUB(oldType, otherType, inferrer.compiler);
if (type != oldType) changed = true;
locals[local] = type;
}
@@ -1462,7 +175,7 @@
toRemove.add(element);
} else {
fieldsInitializedInConstructor[element] =
- inferrer.computeLUB(type, otherType);
+ computeLUB(type, otherType, inferrer.compiler);
}
});
// Remove fields that were not initialized in [other].
@@ -1482,9 +195,8 @@
}
}
-class SimpleTypeInferrerVisitor extends ResolvedVisitor<TypeMask> {
+abstract class InferrerVisitor extends ResolvedVisitor<TypeMask> {
final Element analyzedElement;
- final Element outermostElement;
final InternalSimpleTypesInferrer inferrer;
final Compiler compiler;
final Map<TargetElement, List<LocalsHandler>> breaksFor =
@@ -1492,196 +204,40 @@
final Map<TargetElement, List<LocalsHandler>> continuesFor =
new Map<TargetElement, List<LocalsHandler>>();
LocalsHandler locals;
- TypeMask returnType;
- bool visitingInitializers = false;
- bool isConstructorRedirect = false;
bool accumulateIsChecks = false;
bool conditionIsSimple = false;
-
List<Send> isChecks;
int loopLevel = 0;
- SideEffects sideEffects = new SideEffects.empty();
bool get inLoop => loopLevel > 0;
bool get isThisExposed => locals.isThisExposed;
void set isThisExposed(value) { locals.isThisExposed = value; }
- SimpleTypeInferrerVisitor.internal(TreeElements mapping,
- this.analyzedElement,
- this.outermostElement,
- this.inferrer,
- this.compiler,
- this.locals)
- : super(mapping);
+ InferrerVisitor(Element analyzedElement,
+ this.inferrer,
+ Compiler compiler,
+ this.locals)
+ : this.compiler = compiler,
+ this.analyzedElement = analyzedElement,
+ super(compiler.enqueuer.resolution.getCachedElements(analyzedElement));
- factory SimpleTypeInferrerVisitor(Element element,
- Compiler compiler,
- InternalSimpleTypesInferrer inferrer,
- [LocalsHandler handler]) {
- Element outermostElement =
- element.getOutermostEnclosingMemberOrTopLevel().implementation;
- TreeElements elements = compiler.enqueuer.resolution.resolvedElements[
- outermostElement.declaration];
- assert(elements != null);
- assert(outermostElement != null);
- handler = handler != null ? handler : new LocalsHandler(inferrer);
- return new SimpleTypeInferrerVisitor.internal(
- elements, element, outermostElement, inferrer, compiler, handler);
- }
+ TypeMask visitSendSet(SendSet node);
- TypeMask run() {
- var node = analyzedElement.parseNode(compiler);
- if (analyzedElement.isField() && node.asSendSet() == null) {
- // Eagerly bailout, because computing the closure data only
- // works for functions and field assignments.
- return inferrer.nullType;
- }
- // Update the locals that are boxed in [locals]. These locals will
- // be handled specially, in that we are computing their LUB at
- // each update, and reading them yields the type that was found in a
- // previous analysis of [outermostElement].
- ClosureClassMap closureData =
- compiler.closureToClassMapper.computeClosureToClassMapping(
- analyzedElement, node, elements);
- ClosureScope scopeData = closureData.capturingScopes[node];
- if (scopeData != null) {
- scopeData.capturedVariableMapping.forEach((variable, field) {
- locals.setCapturedAndBoxed(variable, field);
- });
- }
- if (analyzedElement.isField()) {
- return visit(node.asSendSet().arguments.head);
- }
+ TypeMask visitSuperSend(Send node);
- FunctionElement function = analyzedElement;
- if (inferrer.hasAnalyzedAll) {
- inferrer.updateArgumentsType(function);
- }
- FunctionSignature signature = function.computeSignature(compiler);
- signature.forEachOptionalParameter((element) {
- Node node = element.parseNode(compiler);
- Send send = node.asSendSet();
- inferrer.defaultTypeOfParameter[element] = (send == null)
- ? inferrer.nullType
- : visit(send.arguments.head);
- assert(inferrer.defaultTypeOfParameter[element] != null);
- });
+ TypeMask visitStaticSend(Send node);
- if (analyzedElement.isNative()) {
- // Native methods do not have a body, and we currently just say
- // they return dynamic.
- return inferrer.dynamicType;
- }
+ TypeMask visitGetterSend(Send node);
- if (analyzedElement.isGenerativeConstructor()) {
- isThisExposed = false;
- signature.forEachParameter((element) {
- TypeMask parameterType = inferrer.typeOfElement(element);
- if (element.kind == ElementKind.FIELD_PARAMETER) {
- if (element.fieldElement.modifiers.isFinal()) {
- inferrer.recordFinalFieldType(
- node,
- analyzedElement,
- element.fieldElement,
- parameterType,
- null);
- } else {
- locals.updateField(element.fieldElement, parameterType);
- inferrer.recordNonFinalFieldElementType(
- element.parseNode(compiler),
- element.fieldElement,
- parameterType,
- null);
- }
- } else {
- locals.update(element, parameterType);
- }
- });
- visitingInitializers = true;
- visit(node.initializers);
- visitingInitializers = false;
- visit(node.body);
- ClassElement cls = analyzedElement.getEnclosingClass();
- if (!isConstructorRedirect) {
- // Iterate over all instance fields, and give a null type to
- // fields that we haven't initialized for sure.
- cls.forEachInstanceField((_, field) {
- if (field.modifiers.isFinal()) return;
- TypeMask type = locals.fieldsInitializedInConstructor[field];
- if (type == null && field.parseNode(compiler).asSendSet() == null) {
- inferrer.recordNonFinalFieldElementType(
- node, field, inferrer.nullType, null);
- }
- });
- }
- inferrer.doneAnalyzingGenerativeConstructor(analyzedElement);
- returnType = new TypeMask.nonNullExact(inferrer.rawTypeOf(cls));
- } else {
- signature.forEachParameter((element) {
- locals.update(element, inferrer.typeOfElement(element));
- });
- visit(node.body);
- if (returnType == null) {
- // No return in the body.
- returnType = inferrer.nullType;
- } else if (!locals.seenReturn && !inferrer.isDynamicType(returnType)) {
- // We haven't seen returns on all branches. So the method may
- // also return null.
- returnType = returnType.nullable();
- }
+ TypeMask visitClosureSend(Send node);
- if (analyzedElement.name == const SourceString('==')) {
- // TODO(ngeoffray): Should this be done at the call site?
- // When the argument passed in is null, we know we return a
- // bool.
- signature.forEachParameter((Element parameter) {
- if (inferrer.typeOfElement(parameter).isNullable){
- returnType = inferrer.computeLUB(returnType, inferrer.boolType);
- }
- });
- }
- }
+ TypeMask visitDynamicSend(Send node);
- if (analyzedElement == outermostElement) {
- bool changed = false;
- locals.capturedAndBoxed.forEach((Element local, Element field) {
- if (inferrer.recordType(field, locals.locals[local])) {
- changed = true;
- }
- });
- // TODO(ngeoffray): Re-analyze method if [changed]?
- }
- compiler.world.registerSideEffects(analyzedElement, sideEffects);
- assert(breaksFor.isEmpty);
- assert(continuesFor.isEmpty);
- return returnType;
- }
+ TypeMask visitForIn(ForIn node);
- TypeMask _thisType;
- TypeMask get thisType {
- if (_thisType != null) return _thisType;
- ClassElement cls = outermostElement.getEnclosingClass();
- if (compiler.world.isUsedAsMixin(cls)) {
- return _thisType = new TypeMask.nonNullSubtype(inferrer.rawTypeOf(cls));
- } else if (compiler.world.hasAnySubclass(cls)) {
- return _thisType = new TypeMask.nonNullSubclass(inferrer.rawTypeOf(cls));
- } else {
- return _thisType = new TypeMask.nonNullExact(inferrer.rawTypeOf(cls));
- }
- }
+ TypeMask visitReturn(Return node);
- TypeMask _superType;
- TypeMask get superType {
- if (_superType != null) return _superType;
- return _superType = new TypeMask.nonNullExact(
- inferrer.rawTypeOf(outermostElement.getEnclosingClass().superclass));
- }
-
- void recordReturnType(TypeMask type) {
- returnType = inferrer.computeLUB(returnType, type);
- }
-
TypeMask visitNode(Node node) {
node.visitChildren(this);
return inferrer.dynamicType;
@@ -1696,32 +252,6 @@
}
TypeMask visitFunctionExpression(FunctionExpression node) {
- Element element = elements[node];
- // We don't put the closure in the work queue of the
- // inferrer, because it will share information with its enclosing
- // method, like for example the types of local variables.
- LocalsHandler closureLocals = new LocalsHandler.from(locals);
- SimpleTypeInferrerVisitor visitor = new SimpleTypeInferrerVisitor(
- element, compiler, inferrer, closureLocals);
- visitor.run();
- inferrer.recordReturnType(element, visitor.returnType);
- locals.merge(visitor.locals);
-
- // Record the types of captured non-boxed variables. Types of
- // these variables may already be there, because of an analysis of
- // a previous closure. Note that analyzing the same closure multiple
- // times closure will refine the type of those variables, therefore
- // [:inferrer.typeOf[variable]:] is not necessarilly null, nor the
- // same as [newType].
- ClosureClassMap nestedClosureData =
- compiler.closureToClassMapper.getMappingForNestedFunction(node);
- nestedClosureData.forEachNonBoxedCapturedVariable((variable, field) {
- // The type may be null for instance contexts (this and type
- // parameters), as well as captured argument checks.
- if (locals.locals[variable] == null) return;
- inferrer.recordType(field, locals.locals[variable]);
- });
-
return inferrer.functionType;
}
@@ -1780,219 +310,29 @@
bool isThisOrSuper(Node node) => node.isThis() || node.isSuper();
- void checkIfExposesThis(Selector selector) {
- if (isThisExposed) return;
- inferrer.iterateOverElements(selector, (element) {
- if (element.isField()) {
- if (!selector.isSetter()
- && element.getEnclosingClass() ==
- outermostElement.getEnclosingClass()
- && !element.modifiers.isFinal()
- && locals.fieldsInitializedInConstructor[element] == null
- && element.parseNode(compiler).asSendSet() == null) {
- // If the field is being used before this constructor
- // actually had a chance to initialize it, say it can be
- // null.
- inferrer.recordNonFinalFieldElementType(
- analyzedElement.parseNode(compiler), element,
- inferrer.nullType, null);
- }
- // Accessing a field does not expose [:this:].
- return true;
- }
- // TODO(ngeoffray): We could do better here if we knew what we
- // are calling does not expose this.
- isThisExposed = true;
- return false;
- });
+ Element get outermostElement {
+ return
+ analyzedElement.getOutermostEnclosingMemberOrTopLevel().implementation;
}
- TypeMask visitSendSet(SendSet node) {
- Element element = elements[node];
- if (!Elements.isUnresolved(element) && element.impliesType()) {
- node.visitChildren(this);
- return inferrer.dynamicType;
- }
-
- Selector getterSelector =
- elements.getGetterSelectorInComplexSendSet(node);
- Selector operatorSelector =
- elements.getOperatorSelectorInComplexSendSet(node);
- Selector setterSelector = elements.getSelector(node);
-
- String op = node.assignmentOperator.source.stringValue;
- bool isIncrementOrDecrement = op == '++' || op == '--';
-
- TypeMask receiverType;
- bool isCallOnThis = false;
- if (node.receiver == null
- && element != null
- && element.isInstanceMember()) {
- receiverType = thisType;
- isCallOnThis = true;
+ TypeMask _thisType;
+ TypeMask get thisType {
+ if (_thisType != null) return _thisType;
+ ClassElement cls = outermostElement.getEnclosingClass();
+ if (compiler.world.isUsedAsMixin(cls)) {
+ return _thisType = new TypeMask.nonNullSubtype(inferrer.rawTypeOf(cls));
+ } else if (compiler.world.hasAnySubclass(cls)) {
+ return _thisType = new TypeMask.nonNullSubclass(inferrer.rawTypeOf(cls));
} else {
- receiverType = visit(node.receiver);
- isCallOnThis = node.receiver != null && isThisOrSuper(node.receiver);
+ return _thisType = new TypeMask.nonNullExact(inferrer.rawTypeOf(cls));
}
-
- TypeMask rhsType;
- TypeMask indexType;
-
- if (isIncrementOrDecrement) {
- rhsType = inferrer.intType;
- if (node.isIndex) indexType = visit(node.arguments.head);
- } else if (node.isIndex) {
- indexType = visit(node.arguments.head);
- rhsType = visit(node.arguments.tail.head);
- } else {
- rhsType = visit(node.arguments.head);
- }
-
- if (!visitingInitializers && !isThisExposed) {
- for (Node node in node.arguments) {
- if (isThisOrSuper(node)) {
- isThisExposed = true;
- break;
- }
- }
- if (!isThisExposed && isCallOnThis) {
- checkIfExposesThis(new TypedSelector(receiverType, setterSelector));
- }
- }
-
- if (node.isIndex) {
- if (op == '=') {
- // [: foo[0] = 42 :]
- handleDynamicSend(
- node,
- setterSelector,
- receiverType,
- new ArgumentsTypes([indexType, rhsType], null));
- return rhsType;
- } else {
- // [: foo[0] += 42 :] or [: foo[0]++ :].
- TypeMask getterType = handleDynamicSend(
- node,
- getterSelector,
- receiverType,
- new ArgumentsTypes([indexType], null));
- TypeMask returnType = handleDynamicSend(
- node,
- operatorSelector,
- getterType,
- new ArgumentsTypes([rhsType], null));
- handleDynamicSend(
- node,
- setterSelector,
- receiverType,
- new ArgumentsTypes([indexType, returnType], null));
-
- if (node.isPostfix) {
- return getterType;
- } else {
- return returnType;
- }
- }
- } else if (op == '=') {
- return handlePlainAssignment(
- node, element, setterSelector, receiverType, rhsType,
- node.arguments.head);
- } else {
- // [: foo++ :] or [: foo += 1 :].
- Selector constraint;
- if (!Elements.isLocal(element)) {
- // Record a constraint of the form [: field++ :], or [: field += 42 :].
- constraint = operatorSelector;
- }
- TypeMask getterType;
- TypeMask newType;
- ArgumentsTypes operatorArguments = new ArgumentsTypes([rhsType], null);
- if (Elements.isStaticOrTopLevelField(element)) {
- Element getterElement = elements[node.selector];
- getterType =
- inferrer.typeOfElementWithSelector(getterElement, getterSelector);
- handleStaticSend(node, getterSelector, getterElement, null);
- newType = handleDynamicSend(
- node, operatorSelector, getterType, operatorArguments);
- handleStaticSend(
- node, setterSelector, element,
- new ArgumentsTypes([newType], null));
- } else if (Elements.isUnresolved(element)
- || element.isSetter()
- || element.isField()) {
- getterType = handleDynamicSend(
- node, getterSelector, receiverType, null);
- newType = handleDynamicSend(
- node, operatorSelector, getterType, operatorArguments);
- handleDynamicSend(node, setterSelector, receiverType,
- new ArgumentsTypes([newType], null),
- constraint);
- } else if (Elements.isLocal(element)) {
- getterType = locals.use(element);
- newType = handleDynamicSend(
- node, operatorSelector, getterType, operatorArguments);
- locals.update(element, newType);
- } else {
- // Bogus SendSet, for example [: myMethod += 42 :].
- getterType = inferrer.dynamicType;
- newType = handleDynamicSend(
- node, operatorSelector, getterType, operatorArguments);
- }
-
- if (node.isPostfix) {
- return getterType;
- } else {
- return newType;
- }
- }
}
- TypeMask handlePlainAssignment(Node node,
- Element element,
- Selector setterSelector,
- TypeMask receiverType,
- TypeMask rhsType,
- Node rhs) {
- Selector constraint;
- if (node.asSend() != null && !Elements.isLocal(element)) {
- // Recognize a constraint of the form [: field = other.field :].
- // Note that we check if the right hand side is a local to
- // recognize the situation [: var a = 42; this.a = a; :]. Our
- // constraint mechanism only works with members or top level
- // elements.
- Send send = rhs.asSend();
- if (send != null
- && send.isPropertyAccess
- && !Elements.isLocal(elements[rhs])
- && send.selector.asIdentifier().source
- == node.asSend().selector.asIdentifier().source) {
- constraint = elements.getSelector(rhs);
- }
- }
- ArgumentsTypes arguments = new ArgumentsTypes([rhsType], null);
- if (Elements.isStaticOrTopLevelField(element)) {
- handleStaticSend(node, setterSelector, element, arguments);
- } else if (Elements.isUnresolved(element) || element.isSetter()) {
- handleDynamicSend(
- node, setterSelector, receiverType, arguments, constraint);
- } else if (element.isField()) {
- if (element.modifiers.isFinal()) {
- inferrer.recordFinalFieldType(
- node, outermostElement, element, rhsType, constraint);
- } else {
- locals.updateField(element, rhsType);
- if (visitingInitializers) {
- inferrer.recordNonFinalFieldElementType(
- node, element, rhsType, constraint);
- } else {
- handleDynamicSend(
- node, setterSelector, receiverType, arguments, constraint);
- }
- }
- } else if (Elements.isLocal(element)) {
- locals.update(element, rhsType);
- }
- return rhsType;
+ TypeMask _superType;
+ TypeMask get superType {
+ if (_superType != null) return _superType;
+ return _superType = new TypeMask.nonNullExact(
+ inferrer.rawTypeOf(outermostElement.getEnclosingClass().superclass));
}
TypeMask visitIdentifier(Identifier node) {
@@ -2004,94 +344,6 @@
return inferrer.dynamicType;
}
- TypeMask visitSuperSend(Send node) {
- Element element = elements[node];
- if (Elements.isUnresolved(element)) {
- return inferrer.dynamicType;
- }
- Selector selector = elements.getSelector(node);
- // TODO(ngeoffray): We could do better here if we knew what we
- // are calling does not expose this.
- isThisExposed = true;
- if (node.isPropertyAccess) {
- handleStaticSend(node, selector, element, null);
- return inferrer.typeOfElementWithSelector(element, selector);
- } else if (element.isFunction()) {
- if (!selector.applies(element, compiler)) return inferrer.dynamicType;
- ArgumentsTypes arguments = analyzeArguments(node.arguments);
- handleStaticSend(node, selector, element, arguments);
- return inferrer.returnTypeOfElement(element);
- } else {
- analyzeArguments(node.arguments);
- // Closure call on a getter. We don't have function types yet,
- // so we just return [:dynamic:].
- return inferrer.dynamicType;
- }
- }
-
- TypeMask visitStaticSend(Send node) {
- if (visitingInitializers && Initializers.isConstructorRedirect(node)) {
- isConstructorRedirect = true;
- }
- Element element = elements[node];
- if (element.isForeign(compiler)) {
- return handleForeignSend(node);
- }
- Selector selector = elements.getSelector(node);
- ArgumentsTypes arguments = analyzeArguments(node.arguments);
- if (!selector.applies(element, compiler)) return inferrer.dynamicType;
-
- handleStaticSend(node, selector, element, arguments);
- if (Elements.isGrowableListConstructorCall(element, node, compiler)) {
- return inferrer.growableListType;
- } else if (Elements.isFixedListConstructorCall(element, node, compiler)) {
- return inferrer.fixedListType;
- } else if (element.isFunction() || element.isConstructor()) {
- return inferrer.returnTypeOfElement(element);
- } else {
- assert(element.isField() || element.isGetter());
- // Closure call.
- return inferrer.dynamicType;
- }
- }
-
- TypeMask handleForeignSend(Send node) {
- node.visitChildren(this);
- Selector selector = elements.getSelector(node);
- SourceString name = selector.name;
- if (name == const SourceString('JS')) {
- native.NativeBehavior nativeBehavior =
- compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node);
- sideEffects.add(nativeBehavior.sideEffects);
- return inferrer.typeOfNativeBehavior(nativeBehavior);
- } else if (name == const SourceString('JS_OPERATOR_IS_PREFIX')
- || name == const SourceString('JS_OPERATOR_AS_PREFIX')
- || name == const SourceString('JS_OBJECT_CLASS_NAME')) {
- return inferrer.stringType;
- } else {
- sideEffects.setAllSideEffects();
- return inferrer.dynamicType;
- }
- }
-
- ArgumentsTypes analyzeArguments(Link<Node> arguments) {
- List<TypeMask> positional = [];
- Map<SourceString, TypeMask> named = new Map<SourceString, TypeMask>();
- for (var argument in arguments) {
- NamedArgument namedArgument = argument.asNamedArgument();
- if (namedArgument != null) {
- argument = namedArgument.expression;
- named[namedArgument.name.source] = argument.accept(this);
- } else {
- positional.add(argument.accept(this));
- }
- // TODO(ngeoffray): We could do better here if we knew what we
- // are calling does not expose this.
- isThisExposed = isThisExposed || argument.isThis();
- }
- return new ArgumentsTypes(positional, named);
- }
-
void potentiallyAddIsCheck(Send node) {
if (!accumulateIsChecks) return;
if (!Elements.isLocal(elements[node.receiver])) return;
@@ -2109,7 +361,8 @@
DartType type = elements.getType(node.typeAnnotationFromIsCheck);
Element element = elements[node.receiver];
TypeMask existing = locals.use(element);
- TypeMask newType = inferrer.narrowType(existing, type, isNullable: false);
+ TypeMask newType = narrowType(
+ existing, type, inferrer.compiler, isNullable: false);
locals.update(element, newType);
}
}
@@ -2155,7 +408,7 @@
} else if (const SourceString("as") == op.source) {
TypeMask receiverType = visit(node.receiver);
DartType type = elements.getType(node.arguments.head);
- return inferrer.narrowType(receiverType, type);
+ return narrowType(receiverType, type, inferrer.compiler);
} else if (node.isParameterCheck) {
node.visitChildren(this);
return inferrer.boolType;
@@ -2178,160 +431,6 @@
// to avoid confusing the [ResolvedVisitor].
visitTypeAnnotation(TypeAnnotation node) {}
- TypeMask visitGetterSend(Send node) {
- Element element = elements[node];
- Selector selector = elements.getSelector(node);
- if (Elements.isStaticOrTopLevelField(element)) {
- handleStaticSend(node, selector, element, null);
- return inferrer.typeOfElementWithSelector(element, selector);
- } else if (Elements.isInstanceSend(node, elements)) {
- return visitDynamicSend(node);
- } else if (Elements.isStaticOrTopLevelFunction(element)) {
- handleStaticSend(node, selector, element, null);
- return inferrer.functionType;
- } else if (Elements.isErroneousElement(element)) {
- return inferrer.dynamicType;
- } else if (Elements.isLocal(element)) {
- assert(locals.use(element) != null);
- return locals.use(element);
- } else {
- node.visitChildren(this);
- return inferrer.dynamicType;
- }
- }
-
- TypeMask visitClosureSend(Send node) {
- node.visitChildren(this);
- Element element = elements[node];
- Selector selector = elements.getSelector(node);
- if (element != null && element.isFunction()) {
- assert(Elements.isLocal(element));
- // This only works for function statements. We need a
- // more sophisticated type system with function types to support
- // more.
- inferrer.updateSideEffects(sideEffects, selector, element);
- return inferrer.returnTypeOfElement(element);
- }
- sideEffects.setDependsOnSomething();
- sideEffects.setAllSideEffects();
- return inferrer.dynamicType;
- }
-
- void handleStaticSend(Node node,
- Selector selector,
- Element element,
- ArgumentsTypes arguments) {
- if (Elements.isUnresolved(element)) return;
- inferrer.registerCalledElement(
- node, selector, outermostElement, element, arguments, null,
- sideEffects, inLoop);
- }
-
- void updateSelectorInTree(Node node, Selector selector) {
- if (node.asSendSet() != null) {
- if (selector.isSetter() || selector.isIndexSet()) {
- elements.setSelector(node, selector);
- } else if (selector.isGetter() || selector.isIndex()) {
- elements.setGetterSelectorInComplexSendSet(node, selector);
- } else {
- assert(selector.isOperator());
- elements.setOperatorSelectorInComplexSendSet(node, selector);
- }
- } else if (node.asSend() != null) {
- elements.setSelector(node, selector);
- } else {
- assert(node.asForIn() != null);
- if (selector.asUntyped == compiler.iteratorSelector) {
- elements.setIteratorSelector(node, selector);
- } else if (selector.asUntyped == compiler.currentSelector) {
- elements.setCurrentSelector(node, selector);
- } else {
- assert(selector.asUntyped == compiler.moveNextSelector);
- elements.setMoveNextSelector(node, selector);
- }
- }
- }
-
- TypeMask handleDynamicSend(Node node,
- Selector selector,
- TypeMask receiver,
- ArgumentsTypes arguments,
- [Selector constraint]) {
- if (selector.mask != receiver) {
- selector = inferrer.isDynamicType(receiver)
- ? selector.asUntyped
- : new TypedSelector(receiver, selector);
- updateSelectorInTree(node, selector);
- }
- return inferrer.registerCalledSelector(
- node, selector, receiver, outermostElement, arguments,
- constraint, sideEffects, inLoop);
- }
-
- TypeMask visitDynamicSend(Send node) {
- Element element = elements[node];
- TypeMask receiverType;
- bool isCallOnThis = false;
- if (node.receiver == null) {
- isCallOnThis = true;
- receiverType = thisType;
- } else {
- Node receiver = node.receiver;
- isCallOnThis = isThisOrSuper(receiver);
- receiverType = visit(receiver);
- }
-
- Selector selector = elements.getSelector(node);
- if (!isThisExposed && isCallOnThis) {
- checkIfExposesThis(new TypedSelector(receiverType, selector));
- }
-
- ArgumentsTypes arguments = node.isPropertyAccess
- ? null
- : analyzeArguments(node.arguments);
- return handleDynamicSend(node, selector, receiverType, arguments);
- }
-
- TypeMask visitReturn(Return node) {
- if (node.isRedirectingFactoryBody) {
- Element element = elements[node.expression];
- if (Elements.isErroneousElement(element)) {
- recordReturnType(inferrer.dynamicType);
- } else {
- element = element.implementation;
- // We don't create a selector for redirecting factories, and
- // the send is just a property access. Therefore we must
- // manually create the [ArgumentsTypes] of the call, and
- // manually register [analyzedElement] as a caller of [element].
- FunctionElement function = analyzedElement;
- FunctionSignature signature = function.computeSignature(compiler);
- List<TypeMask> unnamed = <TypeMask>[];
- Map<SourceString, TypeMask> named = new Map<SourceString, TypeMask>();
- signature.forEachRequiredParameter((Element element) {
- unnamed.add(locals.use(element));
- });
- signature.forEachOptionalParameter((Element element) {
- if (signature.optionalParametersAreNamed) {
- named[element.name] = locals.use(element);
- } else {
- unnamed.add(locals.use(element));
- }
- });
- ArgumentsTypes arguments = new ArgumentsTypes(unnamed, named);
- inferrer.addCaller(analyzedElement, element);
- inferrer.addArguments(node.expression, element, arguments);
- recordReturnType(inferrer.returnTypeOfElement(element));
- }
- } else {
- Node expression = node.expression;
- recordReturnType(expression == null
- ? inferrer.nullType
- : expression.accept(this));
- }
- locals.seenReturn = true;
- return inferrer.dynamicType;
- }
-
TypeMask visitConditional(Conditional node) {
List<Send> tests = <Send>[];
bool simpleCondition = handleCondition(node.condition, tests);
@@ -2343,7 +442,7 @@
if (simpleCondition) updateIsChecks(tests, usePositive: false);
TypeMask secondType = visit(node.elseExpression);
locals.merge(thenLocals);
- TypeMask type = inferrer.computeLUB(firstType, secondType);
+ TypeMask type = computeLUB(firstType, secondType, compiler);
return type;
}
@@ -2467,49 +566,6 @@
});
}
- TypeMask visitForIn(ForIn node) {
- bool changed = false;
- TypeMask expressionType = visit(node.expression);
- Selector iteratorSelector = elements.getIteratorSelector(node);
- Selector currentSelector = elements.getCurrentSelector(node);
- Selector moveNextSelector = elements.getMoveNextSelector(node);
-
- TypeMask iteratorType =
- handleDynamicSend(node, iteratorSelector, expressionType, null);
- handleDynamicSend(node, moveNextSelector,
- iteratorType, new ArgumentsTypes([], null));
- TypeMask currentType =
- handleDynamicSend(node, currentSelector, iteratorType, null);
-
- // We nullify the type in case there is no element in the
- // iterable.
- currentType = currentType.nullable();
-
- if (node.expression.isThis()) {
- // Any reasonable implementation of an iterator would expose
- // this, so we play it safe and assume it will.
- isThisExposed = true;
- }
-
- Node identifier = node.declaredIdentifier;
- Element element = elements[identifier];
- Selector selector = elements.getSelector(identifier);
-
- TypeMask receiverType;
- if (element != null && element.isInstanceMember()) {
- receiverType = thisType;
- } else {
- receiverType = inferrer.dynamicType;
- }
-
- handlePlainAssignment(identifier, element, selector,
- receiverType, currentType,
- node.expression);
- return handleLoop(node, () {
- visit(node.body);
- });
- }
-
TypeMask visitTryStatement(TryStatement node) {
LocalsHandler saved = locals;
locals = new LocalsHandler.from(locals, inTryBlock: true);
« no previous file with comments | « no previous file | sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698