| Index: sdk/lib/_internal/compiler/implementation/inferrer/simple_types_inferrer.dart
 | 
| diff --git a/sdk/lib/_internal/compiler/implementation/inferrer/simple_types_inferrer.dart b/sdk/lib/_internal/compiler/implementation/inferrer/simple_types_inferrer.dart
 | 
| deleted file mode 100644
 | 
| index f06b1ccafc9f8913e0e4797441cb8ba668394dca..0000000000000000000000000000000000000000
 | 
| --- a/sdk/lib/_internal/compiler/implementation/inferrer/simple_types_inferrer.dart
 | 
| +++ /dev/null
 | 
| @@ -1,1344 +0,0 @@
 | 
| -// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
 | 
| -// for details. All rights reserved. Use of this source code is governed by a
 | 
| -// BSD-style license that can be found in the LICENSE file.
 | 
| -
 | 
| -library simple_types_inferrer;
 | 
| -
 | 
| -import '../closure.dart' show ClosureClassMap, ClosureScope;
 | 
| -import '../dart_types.dart'
 | 
| -    show DartType, InterfaceType, FunctionType, TypeKind;
 | 
| -import '../elements/elements.dart';
 | 
| -import '../js_backend/js_backend.dart' as js;
 | 
| -import '../native/native.dart' as native;
 | 
| -import '../tree/tree.dart' as ast;
 | 
| -import '../cps_ir/cps_ir_nodes.dart' as cps_ir show Node;
 | 
| -import '../util/util.dart' show Link, Spannable, Setlet;
 | 
| -import '../types/types.dart'
 | 
| -    show TypesInferrer, FlatTypeMask, TypeMask, ContainerTypeMask,
 | 
| -         ElementTypeMask, ValueTypeMask, TypeSystem, MinimalInferrerEngine;
 | 
| -import 'inferrer_visitor.dart';
 | 
| -
 | 
| -// 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;
 | 
| -
 | 
| -/**
 | 
| - * An implementation of [TypeSystem] for [TypeMask].
 | 
| - */
 | 
| -class TypeMaskSystem implements TypeSystem<TypeMask> {
 | 
| -  final Compiler compiler;
 | 
| -  final ClassWorld classWorld;
 | 
| -  TypeMaskSystem(Compiler compiler)
 | 
| -      : this.compiler = compiler,
 | 
| -        this.classWorld = compiler.world;
 | 
| -
 | 
| -  TypeMask narrowType(TypeMask type,
 | 
| -                      DartType annotation,
 | 
| -                      {bool isNullable: true}) {
 | 
| -    if (annotation.treatAsDynamic) return type;
 | 
| -    if (annotation.element == compiler.objectClass) return type;
 | 
| -    TypeMask otherType;
 | 
| -    if (annotation.isTypedef || annotation.isFunctionType) {
 | 
| -      otherType = functionType;
 | 
| -    } else if (annotation.isTypeVariable) {
 | 
| -      // TODO(ngeoffray): Narrow to bound.
 | 
| -      return type;
 | 
| -    } else if (annotation.isVoid) {
 | 
| -      otherType = nullType;
 | 
| -    } else {
 | 
| -      assert(annotation.isInterfaceType);
 | 
| -      otherType = new TypeMask.nonNullSubtype(annotation.element, classWorld);
 | 
| -    }
 | 
| -    if (isNullable) otherType = otherType.nullable();
 | 
| -    if (type == null) return otherType;
 | 
| -    return type.intersection(otherType, classWorld);
 | 
| -  }
 | 
| -
 | 
| -  TypeMask computeLUB(TypeMask firstType, TypeMask secondType) {
 | 
| -    if (firstType == null) {
 | 
| -      return secondType;
 | 
| -    } else if (secondType == dynamicType || firstType == dynamicType) {
 | 
| -      return dynamicType;
 | 
| -    } else if (firstType == secondType) {
 | 
| -      return firstType;
 | 
| -    } else {
 | 
| -      TypeMask union = firstType.union(secondType, classWorld);
 | 
| -      // TODO(kasperl): If the union isn't nullable it seems wasteful
 | 
| -      // to use dynamic. Fix that.
 | 
| -      return union.containsAll(classWorld) ? dynamicType : union;
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  TypeMask allocateDiamondPhi(TypeMask firstType, TypeMask secondType) {
 | 
| -    return computeLUB(firstType, secondType);
 | 
| -  }
 | 
| -
 | 
| -  TypeMask get dynamicType => compiler.typesTask.dynamicType;
 | 
| -  TypeMask get nullType => compiler.typesTask.nullType;
 | 
| -  TypeMask get intType => compiler.typesTask.intType;
 | 
| -  TypeMask get uint32Type => compiler.typesTask.uint32Type;
 | 
| -  TypeMask get uint31Type => compiler.typesTask.uint31Type;
 | 
| -  TypeMask get positiveIntType => compiler.typesTask.positiveIntType;
 | 
| -  TypeMask get doubleType => compiler.typesTask.doubleType;
 | 
| -  TypeMask get numType => compiler.typesTask.numType;
 | 
| -  TypeMask get boolType => compiler.typesTask.boolType;
 | 
| -  TypeMask get functionType => compiler.typesTask.functionType;
 | 
| -  TypeMask get listType => compiler.typesTask.listType;
 | 
| -  TypeMask get constListType => compiler.typesTask.constListType;
 | 
| -  TypeMask get fixedListType => compiler.typesTask.fixedListType;
 | 
| -  TypeMask get growableListType => compiler.typesTask.growableListType;
 | 
| -  TypeMask get mapType => compiler.typesTask.mapType;
 | 
| -  TypeMask get constMapType => compiler.typesTask.constMapType;
 | 
| -  TypeMask get stringType => compiler.typesTask.stringType;
 | 
| -  TypeMask get typeType => compiler.typesTask.typeType;
 | 
| -  bool isNull(TypeMask mask) => mask.isEmpty && mask.isNullable;
 | 
| -
 | 
| -  TypeMask stringLiteralType(ast.DartString value) => stringType;
 | 
| -
 | 
| -  TypeMask nonNullSubtype(ClassElement type)
 | 
| -      => new TypeMask.nonNullSubtype(type.declaration, classWorld);
 | 
| -  TypeMask nonNullSubclass(ClassElement type)
 | 
| -      => new TypeMask.nonNullSubclass(type.declaration, classWorld);
 | 
| -  TypeMask nonNullExact(ClassElement type)
 | 
| -      => new TypeMask.nonNullExact(type.declaration, classWorld);
 | 
| -  TypeMask nonNullEmpty() => new TypeMask.nonNullEmpty();
 | 
| -
 | 
| -  TypeMask allocateList(TypeMask type,
 | 
| -                        ast.Node node,
 | 
| -                        Element enclosing,
 | 
| -                        [TypeMask elementType, int length]) {
 | 
| -    return new ContainerTypeMask(type, node, enclosing, elementType, length);
 | 
| -  }
 | 
| -
 | 
| -  TypeMask allocateMap(TypeMask type, ast.Node node, Element element,
 | 
| -                       [List<TypeMask> keys, List<TypeMask> values]) {
 | 
| -    return type;
 | 
| -  }
 | 
| -
 | 
| -  TypeMask allocateClosure(ast.Node node, Element element) {
 | 
| -    return functionType;
 | 
| -  }
 | 
| -
 | 
| -  Selector newTypedSelector(TypeMask receiver, Selector selector) {
 | 
| -    return new TypedSelector(receiver, selector, compiler.world);
 | 
| -  }
 | 
| -
 | 
| -  TypeMask addPhiInput(Local variable,
 | 
| -                       TypeMask phiType,
 | 
| -                       TypeMask newType) {
 | 
| -    return computeLUB(phiType, newType);
 | 
| -  }
 | 
| -
 | 
| -  TypeMask allocatePhi(ast.Node node,
 | 
| -                       Local variable,
 | 
| -                       TypeMask inputType) {
 | 
| -    return inputType;
 | 
| -  }
 | 
| -
 | 
| -  TypeMask allocateLoopPhi(ast.Node node,
 | 
| -                           Local variable,
 | 
| -                           TypeMask inputType) {
 | 
| -    return inputType;
 | 
| -  }
 | 
| -
 | 
| -  TypeMask simplifyPhi(ast.Node node,
 | 
| -                       Local variable,
 | 
| -                       TypeMask phiType) {
 | 
| -    return phiType;
 | 
| -  }
 | 
| -
 | 
| -  bool selectorNeedsUpdate(TypeMask type, Selector selector) {
 | 
| -    return type != selector.mask;
 | 
| -  }
 | 
| -
 | 
| -  TypeMask refineReceiver(Selector selector, TypeMask receiverType) {
 | 
| -    TypeMask newType = compiler.world.allFunctions.receiverType(selector);
 | 
| -    return receiverType.intersection(newType, classWorld);
 | 
| -  }
 | 
| -
 | 
| -  TypeMask getConcreteTypeFor(TypeMask mask) => mask;
 | 
| -}
 | 
| -
 | 
| -/**
 | 
| - * Common super class used by [SimpleTypeInferrerVisitor] to propagate
 | 
| - * type information about visited nodes, as well as to request type
 | 
| - * information of elements.
 | 
| - */
 | 
| -abstract class InferrerEngine<T, V extends TypeSystem>
 | 
| -    implements MinimalInferrerEngine<T> {
 | 
| -  final Compiler compiler;
 | 
| -  final ClassWorld classWorld;
 | 
| -  final V types;
 | 
| -  final Map<ast.Node, T> concreteTypes = new Map<ast.Node, T>();
 | 
| -  final Set<Element> generativeConstructorsExposingThis = new Set<Element>();
 | 
| -
 | 
| -  InferrerEngine(Compiler compiler, this.types)
 | 
| -      : this.compiler = compiler,
 | 
| -        this.classWorld = compiler.world;
 | 
| -
 | 
| -  /**
 | 
| -   * Records the default type of parameter [parameter].
 | 
| -   */
 | 
| -  void setDefaultTypeOfParameter(ParameterElement parameter, T type);
 | 
| -
 | 
| -  /**
 | 
| -   * This helper breaks abstractions but is currently required to work around
 | 
| -   * the wrong modelling of default values of optional parameters of
 | 
| -   * synthetic constructors.
 | 
| -   *
 | 
| -   * TODO(johnniwinther): Remove once default values of synthetic parameters
 | 
| -   * are fixed.
 | 
| -   */
 | 
| -  bool hasAlreadyComputedTypeOfParameterDefault(ParameterElement paramemter);
 | 
| -
 | 
| -  /**
 | 
| -   * Returns the type of [element].
 | 
| -   */
 | 
| -  T typeOfElement(Element element);
 | 
| -
 | 
| -  /**
 | 
| -   * Returns the return type of [element].
 | 
| -   */
 | 
| -  T returnTypeOfElement(Element element);
 | 
| -
 | 
| -  /**
 | 
| -   * Records that [node] sets final field [element] to be of type [type].
 | 
| -   *
 | 
| -   * [nodeHolder] is the element holder of [node].
 | 
| -   */
 | 
| -  void recordTypeOfFinalField(ast.Node node,
 | 
| -                              Element nodeHolder,
 | 
| -                              Element field,
 | 
| -                              T type);
 | 
| -
 | 
| -  /**
 | 
| -   * Records that [node] sets non-final field [element] to be of type
 | 
| -   * [type].
 | 
| -   */
 | 
| -  void recordTypeOfNonFinalField(Spannable node, Element field, T type);
 | 
| -
 | 
| -  /**
 | 
| -   * Records that [element] is of type [type].
 | 
| -   */
 | 
| -  void recordType(Element element, T type);
 | 
| -
 | 
| -  /**
 | 
| -   * Records that the return type [element] is of type [type].
 | 
| -   */
 | 
| -  void recordReturnType(Element element, T type);
 | 
| -
 | 
| -  /**
 | 
| -   * Registers that [caller] calls [callee] at location [node], with
 | 
| -   * [selector], and [arguments]. Note that [selector] is null for
 | 
| -   * forwarding constructors.
 | 
| -   *
 | 
| -   * [sideEffects] will be updated to incorporate [callee]'s side
 | 
| -   * effects.
 | 
| -   *
 | 
| -   * [inLoop] tells whether the call happens in a loop.
 | 
| -   */
 | 
| -  T registerCalledElement(Spannable node,
 | 
| -                          Selector selector,
 | 
| -                          Element caller,
 | 
| -                          Element callee,
 | 
| -                          ArgumentsTypes<T> arguments,
 | 
| -                          SideEffects sideEffects,
 | 
| -                          bool inLoop);
 | 
| -
 | 
| -  /**
 | 
| -   * Registers that [caller] calls [selector] with [receiverType] as
 | 
| -   * receiver, and [arguments].
 | 
| -   *
 | 
| -   * [sideEffects] will be updated to incorporate the potential
 | 
| -   * callees' side effects.
 | 
| -   *
 | 
| -   * [inLoop] tells whether the call happens in a loop.
 | 
| -   */
 | 
| -  T registerCalledSelector(ast.Node node,
 | 
| -                           Selector selector,
 | 
| -                           T receiverType,
 | 
| -                           Element caller,
 | 
| -                           ArgumentsTypes<T> arguments,
 | 
| -                           SideEffects sideEffects,
 | 
| -                           bool inLoop);
 | 
| -
 | 
| -  /**
 | 
| -   * Registers that [caller] calls [closure] with [arguments].
 | 
| -   *
 | 
| -   * [sideEffects] will be updated to incorporate the potential
 | 
| -   * callees' side effects.
 | 
| -   *
 | 
| -   * [inLoop] tells whether the call happens in a loop.
 | 
| -   */
 | 
| -  T registerCalledClosure(ast.Node node,
 | 
| -                          Selector selector,
 | 
| -                          T closure,
 | 
| -                          Element caller,
 | 
| -                          ArgumentsTypes<T> arguments,
 | 
| -                          SideEffects sideEffects,
 | 
| -                          bool inLoop);
 | 
| -
 | 
| -  /**
 | 
| -   * Notifies to the inferrer that [analyzedElement] can have return
 | 
| -   * type [newType]. [currentType] is the type the [InferrerVisitor]
 | 
| -   * currently found.
 | 
| -   *
 | 
| -   * Returns the new type for [analyzedElement].
 | 
| -   */
 | 
| -  T addReturnTypeFor(Element analyzedElement, T currentType, T newType);
 | 
| -
 | 
| -  /**
 | 
| -   * Applies [f] to all elements in the universe that match
 | 
| -   * [selector]. If [f] returns false, aborts the iteration.
 | 
| -   */
 | 
| -  void forEachElementMatching(Selector selector, bool f(Element element)) {
 | 
| -    Iterable<Element> elements = compiler.world.allFunctions.filter(selector);
 | 
| -    for (Element e in elements) {
 | 
| -      if (!f(e.implementation)) return;
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  /**
 | 
| -   * Update [sideEffects] with the side effects of [callee] being
 | 
| -   * called with [selector].
 | 
| -   */
 | 
| -  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));
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  /**
 | 
| -   * Returns the type for [nativeBehavior]. See documentation on
 | 
| -   * [native.NativeBehavior].
 | 
| -   */
 | 
| -  T typeOfNativeBehavior(native.NativeBehavior nativeBehavior) {
 | 
| -    if (nativeBehavior == null) return types.dynamicType;
 | 
| -    List typesReturned = nativeBehavior.typesReturned;
 | 
| -    if (typesReturned.isEmpty) return types.dynamicType;
 | 
| -    T returnType;
 | 
| -    for (var type in typesReturned) {
 | 
| -      T mappedType;
 | 
| -      if (type == native.SpecialType.JsObject) {
 | 
| -        mappedType = types.nonNullExact(compiler.objectClass);
 | 
| -      } else if (type.element == compiler.stringClass) {
 | 
| -        mappedType = types.stringType;
 | 
| -      } else if (type.element == compiler.intClass) {
 | 
| -        mappedType = types.intType;
 | 
| -      } else if (type.element == compiler.doubleClass) {
 | 
| -        mappedType = types.doubleType;
 | 
| -      } else if (type.element == compiler.numClass) {
 | 
| -        mappedType = types.numType;
 | 
| -      } else if (type.element == compiler.boolClass) {
 | 
| -        mappedType = types.boolType;
 | 
| -      } else if (type.element == compiler.nullClass) {
 | 
| -        mappedType = types.nullType;
 | 
| -      } else if (type.isVoid) {
 | 
| -        mappedType = types.nullType;
 | 
| -      } else if (type.isDynamic) {
 | 
| -        return types.dynamicType;
 | 
| -      } else {
 | 
| -        mappedType = types.nonNullSubtype(type.element);
 | 
| -      }
 | 
| -      returnType = types.computeLUB(returnType, mappedType);
 | 
| -      if (returnType == types.dynamicType) {
 | 
| -        break;
 | 
| -      }
 | 
| -    }
 | 
| -    return returnType;
 | 
| -  }
 | 
| -
 | 
| -  void updateSelectorInTree(
 | 
| -      AstElement owner, Spannable node, Selector selector) {
 | 
| -    if (node is cps_ir.Node) {
 | 
| -      // TODO(lry): update selector for IrInvokeDynamic.
 | 
| -      throw "updateSelector for IR node $node";
 | 
| -    }
 | 
| -    ast.Node astNode = node;
 | 
| -    TreeElements elements = owner.resolvedAst.elements;
 | 
| -    if (astNode.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 (astNode.asSend() != null) {
 | 
| -      elements.setSelector(node, selector);
 | 
| -    } else {
 | 
| -      assert(astNode.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);
 | 
| -      }
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  bool isNativeElement(Element element) {
 | 
| -    if (element.isNative) return true;
 | 
| -    return element.isClassMember
 | 
| -        && element.enclosingClass.isNative
 | 
| -        && element.isField;
 | 
| -  }
 | 
| -
 | 
| -  void analyze(Element element, ArgumentsTypes arguments);
 | 
| -
 | 
| -  bool checkIfExposesThis(Element element) {
 | 
| -    element = element.implementation;
 | 
| -    return generativeConstructorsExposingThis.contains(element);
 | 
| -  }
 | 
| -
 | 
| -  void recordExposesThis(Element element, bool exposesThis) {
 | 
| -    element = element.implementation;
 | 
| -    if (exposesThis) {
 | 
| -      generativeConstructorsExposingThis.add(element);
 | 
| -    }
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -class SimpleTypeInferrerVisitor<T>
 | 
| -    extends InferrerVisitor<T, InferrerEngine<T, TypeSystem<T>>> {
 | 
| -  T returnType;
 | 
| -  bool visitingInitializers = false;
 | 
| -  bool isConstructorRedirect = false;
 | 
| -  bool seenSuperConstructorCall = false;
 | 
| -  SideEffects sideEffects = new SideEffects.empty();
 | 
| -  final Element outermostElement;
 | 
| -  final InferrerEngine<T, TypeSystem<T>> inferrer;
 | 
| -  final Setlet<Element> capturedVariables = new Setlet<Element>();
 | 
| -
 | 
| -  SimpleTypeInferrerVisitor.internal(analyzedElement,
 | 
| -                                     this.outermostElement,
 | 
| -                                     inferrer,
 | 
| -                                     compiler,
 | 
| -                                     locals)
 | 
| -    : super(analyzedElement, inferrer, inferrer.types, compiler, locals),
 | 
| -      this.inferrer = inferrer {
 | 
| -    assert(outermostElement != null);
 | 
| -  }
 | 
| -
 | 
| -  SimpleTypeInferrerVisitor(Element element,
 | 
| -                            Compiler compiler,
 | 
| -                            InferrerEngine<T, TypeSystem<T>> inferrer,
 | 
| -                            [LocalsHandler<T> handler])
 | 
| -    : this.internal(element,
 | 
| -        element.outermostEnclosingMemberOrTopLevel.implementation,
 | 
| -        inferrer, compiler, handler);
 | 
| -
 | 
| -  void analyzeSuperConstructorCall(Element target, ArgumentsTypes arguments) {
 | 
| -    inferrer.analyze(target, arguments);
 | 
| -    isThisExposed = isThisExposed || inferrer.checkIfExposesThis(target);
 | 
| -  }
 | 
| -
 | 
| -  T run() {
 | 
| -    var node = analyzedElement.node;
 | 
| -    ast.Expression initializer;
 | 
| -    if (analyzedElement.isField) {
 | 
| -      VariableElement fieldElement = analyzedElement;
 | 
| -      initializer = fieldElement.initializer;
 | 
| -      if (initializer == null) {
 | 
| -        // Eagerly bailout, because computing the closure data only
 | 
| -        // works for functions and field assignments.
 | 
| -        return types.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);
 | 
| -    closureData.forEachCapturedVariable((variable, field) {
 | 
| -      locals.setCaptured(variable, field);
 | 
| -    });
 | 
| -    closureData.forEachBoxedVariable((variable, field) {
 | 
| -      locals.setCapturedAndBoxed(variable, field);
 | 
| -    });
 | 
| -    if (analyzedElement.isField) {
 | 
| -      return visit(initializer);
 | 
| -    }
 | 
| -
 | 
| -    FunctionElement function = analyzedElement;
 | 
| -    FunctionSignature signature = function.functionSignature;
 | 
| -    signature.forEachOptionalParameter((ParameterElement element) {
 | 
| -      ast.Expression defaultValue = element.initializer;
 | 
| -      // If this is a default value from a different context (because
 | 
| -      // the current function is synthetic, e.g., a constructor from
 | 
| -      // a mixin application), we have to start a new inferrer visitor
 | 
| -      // with the correct context.
 | 
| -      // TODO(johnniwinther): Remove once function signatures are fixed.
 | 
| -      SimpleTypeInferrerVisitor visitor = this;
 | 
| -      if (inferrer.hasAlreadyComputedTypeOfParameterDefault(element)) return;
 | 
| -      if (element.functionDeclaration != analyzedElement) {
 | 
| -        visitor = new SimpleTypeInferrerVisitor(
 | 
| -            element.functionDeclaration, compiler, inferrer);
 | 
| -      }
 | 
| -      T type =
 | 
| -          (defaultValue == null) ? types.nullType : visitor.visit(defaultValue);
 | 
| -      inferrer.setDefaultTypeOfParameter(element, type);
 | 
| -    });
 | 
| -
 | 
| -    if (analyzedElement.isNative) {
 | 
| -      // Native methods do not have a body, and we currently just say
 | 
| -      // they return dynamic.
 | 
| -      return types.dynamicType;
 | 
| -    }
 | 
| -
 | 
| -    if (analyzedElement.isGenerativeConstructor) {
 | 
| -      isThisExposed = false;
 | 
| -      signature.forEachParameter((ParameterElement element) {
 | 
| -        T parameterType = inferrer.typeOfElement(element);
 | 
| -        if (element.isInitializingFormal) {
 | 
| -          InitializingFormalElement initializingFormal = element;
 | 
| -          if (initializingFormal.fieldElement.isFinal) {
 | 
| -            inferrer.recordTypeOfFinalField(
 | 
| -                node,
 | 
| -                analyzedElement,
 | 
| -                initializingFormal.fieldElement,
 | 
| -                parameterType);
 | 
| -          } else {
 | 
| -            locals.updateField(initializingFormal.fieldElement, parameterType);
 | 
| -            inferrer.recordTypeOfNonFinalField(
 | 
| -                initializingFormal.node,
 | 
| -                initializingFormal.fieldElement,
 | 
| -                parameterType);
 | 
| -          }
 | 
| -        }
 | 
| -        locals.update(element, parameterType, node);
 | 
| -      });
 | 
| -      ClassElement cls = analyzedElement.enclosingClass;
 | 
| -      if (analyzedElement.isSynthesized) {
 | 
| -        node = analyzedElement;
 | 
| -        ConstructorElement constructor = analyzedElement;
 | 
| -        synthesizeForwardingCall(node, constructor.definingConstructor);
 | 
| -      } else {
 | 
| -        visitingInitializers = true;
 | 
| -        visit(node.initializers);
 | 
| -        visitingInitializers = false;
 | 
| -        // For a generative constructor like: `Foo();`, we synthesize
 | 
| -        // a call to the default super constructor (the one that takes
 | 
| -        // no argument). Resolution ensures that such a constructor
 | 
| -        // exists.
 | 
| -        if (!isConstructorRedirect
 | 
| -            && !seenSuperConstructorCall
 | 
| -            && !cls.isObject) {
 | 
| -          Selector selector =
 | 
| -              new Selector.callDefaultConstructor(analyzedElement.library);
 | 
| -          FunctionElement target = cls.superclass.lookupConstructor(selector);
 | 
| -          analyzeSuperConstructorCall(target, new ArgumentsTypes([], {}));
 | 
| -          synthesizeForwardingCall(analyzedElement, target);
 | 
| -        }
 | 
| -        visit(node.body);
 | 
| -        inferrer.recordExposesThis(analyzedElement, isThisExposed);
 | 
| -      }
 | 
| -      if (!isConstructorRedirect) {
 | 
| -        // Iterate over all instance fields, and give a null type to
 | 
| -        // fields that we haven't initialized for sure.
 | 
| -        cls.forEachInstanceField((_, FieldElement field) {
 | 
| -          if (field.isFinal) return;
 | 
| -          T type = locals.fieldScope.readField(field);
 | 
| -          if (type == null && field.initializer == null) {
 | 
| -            inferrer.recordTypeOfNonFinalField(node, field, types.nullType);
 | 
| -          }
 | 
| -        });
 | 
| -      }
 | 
| -      returnType = types.nonNullExact(cls);
 | 
| -    } else {
 | 
| -      signature.forEachParameter((element) {
 | 
| -        locals.update(element, inferrer.typeOfElement(element), node);
 | 
| -      });
 | 
| -      visit(node.body);
 | 
| -      if (returnType == null) {
 | 
| -        // No return in the body.
 | 
| -        returnType = locals.seenReturnOrThrow
 | 
| -            ? types.nonNullEmpty()  // Body always throws.
 | 
| -            : types.nullType;
 | 
| -      } else if (!locals.seenReturnOrThrow) {
 | 
| -        // We haven't seen returns on all branches. So the method may
 | 
| -        // also return null.
 | 
| -        returnType = inferrer.addReturnTypeFor(
 | 
| -            analyzedElement, returnType, types.nullType);
 | 
| -      }
 | 
| -    }
 | 
| -
 | 
| -    compiler.world.registerSideEffects(analyzedElement, sideEffects);
 | 
| -    assert(breaksFor.isEmpty);
 | 
| -    assert(continuesFor.isEmpty);
 | 
| -    return returnType;
 | 
| -  }
 | 
| -
 | 
| -  T visitFunctionExpression(ast.FunctionExpression node) {
 | 
| -    // We loose track of [this] in closures (see issue 20840). To be on
 | 
| -    // the safe side, we mark [this] as exposed here. We could do better by
 | 
| -    // analyzing the closure.
 | 
| -    // TODO(herhut): Analyze whether closure exposes this.
 | 
| -    isThisExposed = true;
 | 
| -    LocalFunctionElement element = elements.getFunctionDefinition(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<T>.from(
 | 
| -        locals, node, useOtherTryBlock: false);
 | 
| -    SimpleTypeInferrerVisitor visitor = new SimpleTypeInferrerVisitor<T>(
 | 
| -        element, compiler, inferrer, closureLocals);
 | 
| -    visitor.run();
 | 
| -    inferrer.recordReturnType(element, visitor.returnType);
 | 
| -
 | 
| -    // Record the types of captured non-boxed variables. Types of
 | 
| -    // these variables may already be there, because of an analysis of
 | 
| -    // a previous closure.
 | 
| -    ClosureClassMap nestedClosureData =
 | 
| -        compiler.closureToClassMapper.getMappingForNestedFunction(node);
 | 
| -    nestedClosureData.forEachCapturedVariable((variable, field) {
 | 
| -      if (!nestedClosureData.isVariableBoxed(variable)) {
 | 
| -        if (variable == nestedClosureData.thisLocal) {
 | 
| -          inferrer.recordType(field, thisType);
 | 
| -        }
 | 
| -        // The type is null for type parameters.
 | 
| -        if (locals.locals[variable] == null) return;
 | 
| -        inferrer.recordType(field, locals.locals[variable]);
 | 
| -      }
 | 
| -      capturedVariables.add(variable);
 | 
| -    });
 | 
| -
 | 
| -    return inferrer.concreteTypes.putIfAbsent(node, () {
 | 
| -      return types.allocateClosure(node, element);
 | 
| -    });
 | 
| -  }
 | 
| -
 | 
| -  T visitFunctionDeclaration(ast.FunctionDeclaration node) {
 | 
| -    LocalFunctionElement element = elements.getFunctionDefinition(node.function);
 | 
| -    T type = inferrer.concreteTypes.putIfAbsent(node.function, () {
 | 
| -      return types.allocateClosure(node.function, element);
 | 
| -    });
 | 
| -    locals.update(element, type, node);
 | 
| -    visit(node.function);
 | 
| -    return type;
 | 
| -  }
 | 
| -
 | 
| -  T visitStringInterpolation(ast.StringInterpolation node) {
 | 
| -    // Interpolation could have any effects since it could call any toString()
 | 
| -    // method.
 | 
| -    // TODO(sra): This could be modelled by a call to toString() but with a
 | 
| -    // guaranteed String return type.  Interpolation of known types would get
 | 
| -    // specialized effects.  This would not currently be effective since the JS
 | 
| -    // code in the toString methods for intercepted primitive types is assumed
 | 
| -    // to have all effects.  Effect annotations on JS code would be needed to
 | 
| -    // get the benefit.
 | 
| -    sideEffects.setAllSideEffects();
 | 
| -    return super.visitStringInterpolation(node);
 | 
| -  }
 | 
| -
 | 
| -  T visitLiteralList(ast.LiteralList node) {
 | 
| -    // We only set the type once. We don't need to re-visit the children
 | 
| -    // when re-analyzing the node.
 | 
| -    return inferrer.concreteTypes.putIfAbsent(node, () {
 | 
| -      T elementType;
 | 
| -      int length = 0;
 | 
| -      for (ast.Node element in node.elements.nodes) {
 | 
| -        T type = visit(element);
 | 
| -        elementType = elementType == null
 | 
| -            ? types.allocatePhi(null, null, type)
 | 
| -            : types.addPhiInput(null, elementType, type);
 | 
| -        length++;
 | 
| -      }
 | 
| -      elementType = elementType == null
 | 
| -          ? types.nonNullEmpty()
 | 
| -          : types.simplifyPhi(null, null, elementType);
 | 
| -      T containerType = node.isConst
 | 
| -          ? types.constListType
 | 
| -          : types.growableListType;
 | 
| -      return types.allocateList(
 | 
| -          containerType,
 | 
| -          node,
 | 
| -          outermostElement,
 | 
| -          elementType,
 | 
| -          length);
 | 
| -    });
 | 
| -  }
 | 
| -
 | 
| -  T visitLiteralMap(ast.LiteralMap node) {
 | 
| -    return inferrer.concreteTypes.putIfAbsent(node, () {
 | 
| -      ast.NodeList entries = node.entries;
 | 
| -      List<T> keyTypes = [];
 | 
| -      List<T> valueTypes = [];
 | 
| -
 | 
| -      for (ast.LiteralMapEntry entry in entries) {
 | 
| -        keyTypes.add(visit(entry.key));
 | 
| -        valueTypes.add(visit(entry.value));
 | 
| -      }
 | 
| -
 | 
| -      T type = node.isConst ? types.constMapType : types.mapType;
 | 
| -      return types.allocateMap(type,
 | 
| -                               node,
 | 
| -                               outermostElement,
 | 
| -                               keyTypes,
 | 
| -                               valueTypes);
 | 
| -    });
 | 
| -  }
 | 
| -
 | 
| -  bool isThisOrSuper(ast.Node node) => node.isThis() || node.isSuper();
 | 
| -
 | 
| -  bool isInClassOrSubclass(Element element) {
 | 
| -    ClassElement cls = outermostElement.enclosingClass.declaration;
 | 
| -    ClassElement enclosing = element.enclosingClass.declaration;
 | 
| -    return compiler.world.isSubclassOf(enclosing, cls);
 | 
| -  }
 | 
| -
 | 
| -  void checkIfExposesThis(Selector selector) {
 | 
| -    if (isThisExposed) return;
 | 
| -    inferrer.forEachElementMatching(selector, (element) {
 | 
| -      if (element.isField) {
 | 
| -        if (!selector.isSetter
 | 
| -            && isInClassOrSubclass(element)
 | 
| -            && !element.modifiers.isFinal
 | 
| -            && locals.fieldScope.readField(element) == null
 | 
| -            && element.initializer == null) {
 | 
| -          // If the field is being used before this constructor
 | 
| -          // actually had a chance to initialize it, say it can be
 | 
| -          // null.
 | 
| -          inferrer.recordTypeOfNonFinalField(
 | 
| -              analyzedElement.node, element,
 | 
| -              types.nullType);
 | 
| -        }
 | 
| -        // 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;
 | 
| -    });
 | 
| -  }
 | 
| -
 | 
| -  bool get inInstanceContext {
 | 
| -    return (outermostElement.isInstanceMember && !outermostElement.isField)
 | 
| -        || outermostElement.isGenerativeConstructor;
 | 
| -  }
 | 
| -
 | 
| -  bool treatAsInstanceMember(Element element) {
 | 
| -    return (Elements.isUnresolved(element) && inInstanceContext)
 | 
| -        || (element != null && element.isInstanceMember);
 | 
| -  }
 | 
| -
 | 
| -  T visitSendSet(ast.SendSet node) {
 | 
| -    Element element = elements[node];
 | 
| -    if (!Elements.isUnresolved(element) && element.impliesType) {
 | 
| -      node.visitChildren(this);
 | 
| -      return types.dynamicType;
 | 
| -    }
 | 
| -
 | 
| -    Selector getterSelector =
 | 
| -        elements.getGetterSelectorInComplexSendSet(node);
 | 
| -    Selector operatorSelector =
 | 
| -        elements.getOperatorSelectorInComplexSendSet(node);
 | 
| -    Selector setterSelector = elements.getSelector(node);
 | 
| -
 | 
| -    String op = node.assignmentOperator.source;
 | 
| -    bool isIncrementOrDecrement = op == '++' || op == '--';
 | 
| -
 | 
| -    T receiverType;
 | 
| -    bool isCallOnThis = false;
 | 
| -    if (node.receiver == null) {
 | 
| -      if (treatAsInstanceMember(element)) {
 | 
| -        receiverType = thisType;
 | 
| -        isCallOnThis = true;
 | 
| -      }
 | 
| -    } else {
 | 
| -      receiverType = visit(node.receiver);
 | 
| -      isCallOnThis = isThisOrSuper(node.receiver);
 | 
| -    }
 | 
| -
 | 
| -    T rhsType;
 | 
| -    T indexType;
 | 
| -
 | 
| -    if (isIncrementOrDecrement) {
 | 
| -      rhsType = types.uint31Type;
 | 
| -      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 (ast.Node node in node.arguments) {
 | 
| -        if (isThisOrSuper(node)) {
 | 
| -          isThisExposed = true;
 | 
| -          break;
 | 
| -        }
 | 
| -      }
 | 
| -      if (!isThisExposed && isCallOnThis) {
 | 
| -        checkIfExposesThis(
 | 
| -            types.newTypedSelector(receiverType, setterSelector));
 | 
| -        if (getterSelector != null) {
 | 
| -          checkIfExposesThis(
 | 
| -              types.newTypedSelector(receiverType, getterSelector));
 | 
| -        }
 | 
| -      }
 | 
| -    }
 | 
| -
 | 
| -    if (node.isIndex) {
 | 
| -      if (op == '=') {
 | 
| -        // [: foo[0] = 42 :]
 | 
| -        handleDynamicSend(
 | 
| -            node,
 | 
| -            setterSelector,
 | 
| -            receiverType,
 | 
| -            new ArgumentsTypes<T>([indexType, rhsType], null));
 | 
| -        return rhsType;
 | 
| -      } else {
 | 
| -        // [: foo[0] += 42 :] or [: foo[0]++ :].
 | 
| -        T getterType = handleDynamicSend(
 | 
| -            node,
 | 
| -            getterSelector,
 | 
| -            receiverType,
 | 
| -            new ArgumentsTypes<T>([indexType], null));
 | 
| -        T returnType = handleDynamicSend(
 | 
| -            node,
 | 
| -            operatorSelector,
 | 
| -            getterType,
 | 
| -            new ArgumentsTypes<T>([rhsType], null));
 | 
| -        handleDynamicSend(
 | 
| -            node,
 | 
| -            setterSelector,
 | 
| -            receiverType,
 | 
| -            new ArgumentsTypes<T>([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 :].
 | 
| -      ArgumentsTypes operatorArguments = new ArgumentsTypes<T>([rhsType], null);
 | 
| -      T getterType;
 | 
| -      T newType;
 | 
| -      if (Elements.isErroneousElement(element)) {
 | 
| -        getterType = types.dynamicType;
 | 
| -        newType = types.dynamicType;
 | 
| -      } else if (Elements.isStaticOrTopLevelField(element)) {
 | 
| -        Element getterElement = elements[node.selector];
 | 
| -        getterType =
 | 
| -            handleStaticSend(node, getterSelector, getterElement, null);
 | 
| -        newType = handleDynamicSend(
 | 
| -            node, operatorSelector, getterType, operatorArguments);
 | 
| -        handleStaticSend(
 | 
| -            node, setterSelector, element,
 | 
| -            new ArgumentsTypes<T>([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<T>([newType], null));
 | 
| -      } else if (element.isLocal) {
 | 
| -        LocalElement local = element;
 | 
| -        getterType = locals.use(local);
 | 
| -        newType = handleDynamicSend(
 | 
| -            node, operatorSelector, getterType, operatorArguments);
 | 
| -        locals.update(element, newType, node);
 | 
| -      } else {
 | 
| -        // Bogus SendSet, for example [: myMethod += 42 :].
 | 
| -        getterType = types.dynamicType;
 | 
| -        newType = handleDynamicSend(
 | 
| -            node, operatorSelector, getterType, operatorArguments);
 | 
| -      }
 | 
| -
 | 
| -      if (node.isPostfix) {
 | 
| -        return getterType;
 | 
| -      } else {
 | 
| -        return newType;
 | 
| -      }
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  T handlePlainAssignment(ast.Node node,
 | 
| -                          Element element,
 | 
| -                          Selector setterSelector,
 | 
| -                          T receiverType,
 | 
| -                          T rhsType,
 | 
| -                          ast.Node rhs) {
 | 
| -    ArgumentsTypes arguments = new ArgumentsTypes<T>([rhsType], null);
 | 
| -    if (Elements.isErroneousElement(element)) {
 | 
| -      // Code will always throw.
 | 
| -    } else if (Elements.isStaticOrTopLevelField(element)) {
 | 
| -      handleStaticSend(node, setterSelector, element, arguments);
 | 
| -    } else if (Elements.isUnresolved(element) || element.isSetter) {
 | 
| -      if (analyzedElement.isGenerativeConstructor
 | 
| -          && (node.asSendSet() != null)
 | 
| -          && (node.asSendSet().receiver != null)
 | 
| -          && node.asSendSet().receiver.isThis()) {
 | 
| -        Iterable<Element> targets = compiler.world.allFunctions.filter(
 | 
| -            types.newTypedSelector(thisType, setterSelector));
 | 
| -        // We just recognized a field initialization of the form:
 | 
| -        // `this.foo = 42`. If there is only one target, we can update
 | 
| -        // its type.
 | 
| -        if (targets.length == 1) {
 | 
| -          Element single = targets.first;
 | 
| -          if (single.isField) {
 | 
| -            locals.updateField(single, rhsType);
 | 
| -          }
 | 
| -        }
 | 
| -      }
 | 
| -      handleDynamicSend(
 | 
| -          node, setterSelector, receiverType, arguments);
 | 
| -    } else if (element.isField) {
 | 
| -      if (element.isFinal) {
 | 
| -        inferrer.recordTypeOfFinalField(
 | 
| -            node, outermostElement, element, rhsType);
 | 
| -      } else {
 | 
| -        if (analyzedElement.isGenerativeConstructor) {
 | 
| -          locals.updateField(element, rhsType);
 | 
| -        }
 | 
| -        if (visitingInitializers) {
 | 
| -          inferrer.recordTypeOfNonFinalField(node, element, rhsType);
 | 
| -        } else {
 | 
| -          handleDynamicSend(
 | 
| -              node, setterSelector, receiverType, arguments);
 | 
| -        }
 | 
| -      }
 | 
| -    } else if (element.isLocal) {
 | 
| -      locals.update(element, rhsType, node);
 | 
| -    }
 | 
| -    return rhsType;
 | 
| -  }
 | 
| -
 | 
| -  T visitSuperSend(ast.Send node) {
 | 
| -    Element element = elements[node];
 | 
| -    ArgumentsTypes arguments = node.isPropertyAccess
 | 
| -        ? null
 | 
| -        : analyzeArguments(node.arguments);
 | 
| -    if (visitingInitializers) {
 | 
| -      seenSuperConstructorCall = true;
 | 
| -      analyzeSuperConstructorCall(element, arguments);
 | 
| -    }
 | 
| -    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 (Elements.isUnresolved(element)
 | 
| -        || !selector.applies(element, compiler.world)) {
 | 
| -      // Ensure we create a node, to make explicit the call to the
 | 
| -      // `noSuchMethod` handler.
 | 
| -      return handleDynamicSend(node, selector, superType, arguments);
 | 
| -    } else if (node.isPropertyAccess
 | 
| -              || element.isFunction
 | 
| -              || element.isGenerativeConstructor) {
 | 
| -      return handleStaticSend(node, selector, element, arguments);
 | 
| -    } else {
 | 
| -      return inferrer.registerCalledClosure(
 | 
| -          node, selector, inferrer.typeOfElement(element),
 | 
| -          outermostElement, arguments, sideEffects, inLoop);
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  // Try to find the length given to a fixed array constructor call.
 | 
| -  int findLength(ast.Send node) {
 | 
| -    ast.Node firstArgument = node.arguments.head;
 | 
| -    Element element = elements[firstArgument];
 | 
| -    ast.LiteralInt length = firstArgument.asLiteralInt();
 | 
| -    if (length != null) {
 | 
| -      return length.value;
 | 
| -    } else if (element != null
 | 
| -               && element.isField
 | 
| -               && Elements.isStaticOrTopLevelField(element)
 | 
| -               && compiler.world.fieldNeverChanges(element)) {
 | 
| -      var constant =
 | 
| -          compiler.backend.constants.getConstantForVariable(element);
 | 
| -      if (constant != null && constant.value.isInt) {
 | 
| -        return constant.value.primitiveValue;
 | 
| -      }
 | 
| -    }
 | 
| -    return null;
 | 
| -  }
 | 
| -
 | 
| -  T visitStaticSend(ast.Send node) {
 | 
| -    Element element = elements[node];
 | 
| -    if (elements.isAssert(node)) {
 | 
| -      js.JavaScriptBackend backend = compiler.backend;
 | 
| -      element = backend.assertMethod;
 | 
| -    }
 | 
| -    ArgumentsTypes arguments = analyzeArguments(node.arguments);
 | 
| -    if (visitingInitializers) {
 | 
| -      if (ast.Initializers.isConstructorRedirect(node)) {
 | 
| -        isConstructorRedirect = true;
 | 
| -      } else if (ast.Initializers.isSuperConstructorCall(node)) {
 | 
| -        seenSuperConstructorCall = true;
 | 
| -        analyzeSuperConstructorCall(element, arguments);
 | 
| -      }
 | 
| -    }
 | 
| -    // If we are looking at a new expression on a forwarding factory,
 | 
| -    // we have to forward the call to the effective target of the
 | 
| -    // factory.
 | 
| -    if (element.isFactoryConstructor) {
 | 
| -      // TODO(herhut): Remove the while loop once effectiveTarget forwards to
 | 
| -      //               patches.
 | 
| -      while (element.isFactoryConstructor) {
 | 
| -        ConstructorElement constructor = element;
 | 
| -        if (!constructor.isRedirectingFactory) break;
 | 
| -        element = constructor.effectiveTarget.implementation;
 | 
| -      }
 | 
| -    }
 | 
| -    if (element.isForeign(compiler.backend)) {
 | 
| -      return handleForeignSend(node);
 | 
| -    }
 | 
| -    Selector selector = elements.getSelector(node);
 | 
| -    // In erroneous code the number of arguments in the selector might not
 | 
| -    // match the function element.
 | 
| -    // TODO(polux): return nonNullEmpty and check it doesn't break anything
 | 
| -    if (!selector.applies(element, compiler.world)) return types.dynamicType;
 | 
| -
 | 
| -    T returnType = handleStaticSend(node, selector, element, arguments);
 | 
| -    if (Elements.isGrowableListConstructorCall(element, node, compiler)) {
 | 
| -      return inferrer.concreteTypes.putIfAbsent(
 | 
| -          node, () => types.allocateList(
 | 
| -              types.growableListType, node, outermostElement,
 | 
| -              types.nonNullEmpty(), 0));
 | 
| -    } else if (Elements.isFixedListConstructorCall(element, node, compiler)
 | 
| -        || Elements.isFilledListConstructorCall(element, node, compiler)) {
 | 
| -
 | 
| -      int length = findLength(node);
 | 
| -      T elementType =
 | 
| -          Elements.isFixedListConstructorCall(element, node, compiler)
 | 
| -              ? types.nullType
 | 
| -              : arguments.positional[1];
 | 
| -
 | 
| -      return inferrer.concreteTypes.putIfAbsent(
 | 
| -          node, () => types.allocateList(
 | 
| -              types.fixedListType, node, outermostElement,
 | 
| -              elementType, length));
 | 
| -    } else if (Elements.isConstructorOfTypedArraySubclass(element, compiler)) {
 | 
| -      int length = findLength(node);
 | 
| -      ConstructorElement constructor = element.implementation;
 | 
| -      constructor = constructor.effectiveTarget;
 | 
| -      T elementType = inferrer.returnTypeOfElement(
 | 
| -          constructor.enclosingClass.lookupMember('[]'));
 | 
| -      return inferrer.concreteTypes.putIfAbsent(
 | 
| -        node, () => types.allocateList(
 | 
| -          types.nonNullExact(constructor.enclosingClass), node,
 | 
| -          outermostElement, elementType, length));
 | 
| -    } else if (element.isFunction || element.isConstructor) {
 | 
| -      return returnType;
 | 
| -    } else {
 | 
| -      assert(element.isField || element.isGetter);
 | 
| -      return inferrer.registerCalledClosure(
 | 
| -          node, selector, inferrer.typeOfElement(element),
 | 
| -          outermostElement, arguments, sideEffects, inLoop);
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  T handleForeignSend(ast.Send node) {
 | 
| -    ArgumentsTypes arguments = analyzeArguments(node.arguments);
 | 
| -    Selector selector = elements.getSelector(node);
 | 
| -    String name = selector.name;
 | 
| -    handleStaticSend(node, selector, elements[node], arguments);
 | 
| -    if (name == 'JS' || name == 'JS_EMBEDDED_GLOBAL') {
 | 
| -      native.NativeBehavior nativeBehavior =
 | 
| -          compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node);
 | 
| -      sideEffects.add(nativeBehavior.sideEffects);
 | 
| -      return inferrer.typeOfNativeBehavior(nativeBehavior);
 | 
| -    } else if (name == 'JS_GET_NAME'
 | 
| -               || name == 'JS_NULL_CLASS_NAME'
 | 
| -               || name == 'JS_OBJECT_CLASS_NAME'
 | 
| -               || name == 'JS_OPERATOR_IS_PREFIX'
 | 
| -               || name == 'JS_OPERATOR_AS_PREFIX'
 | 
| -               || name == 'JS_STRING_CONCAT') {
 | 
| -      return types.stringType;
 | 
| -    } else {
 | 
| -      sideEffects.setAllSideEffects();
 | 
| -      return types.dynamicType;
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  ArgumentsTypes analyzeArguments(Link<ast.Node> arguments) {
 | 
| -    List<T> positional = [];
 | 
| -    Map<String, T> named;
 | 
| -    for (var argument in arguments) {
 | 
| -      ast.NamedArgument namedArgument = argument.asNamedArgument();
 | 
| -      if (namedArgument != null) {
 | 
| -        argument = namedArgument.expression;
 | 
| -        if (named == null) named = new Map<String, T>();
 | 
| -        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<T>(positional, named);
 | 
| -  }
 | 
| -
 | 
| -  T visitGetterSend(ast.Send node) {
 | 
| -    Element element = elements[node];
 | 
| -    Selector selector = elements.getSelector(node);
 | 
| -    if (Elements.isStaticOrTopLevelField(element)) {
 | 
| -      return handleStaticSend(node, selector, element, null);
 | 
| -    } else if (Elements.isInstanceSend(node, elements)) {
 | 
| -      return visitDynamicSend(node);
 | 
| -    } else if (Elements.isStaticOrTopLevelFunction(element)) {
 | 
| -      return handleStaticSend(node, selector, element, null);
 | 
| -    } else if (Elements.isErroneousElement(element)) {
 | 
| -      return types.dynamicType;
 | 
| -    } else if (element.isLocal) {
 | 
| -      LocalElement local = element;
 | 
| -      assert(locals.use(local) != null);
 | 
| -      return locals.use(local);
 | 
| -    } else {
 | 
| -      assert(element is PrefixElement);
 | 
| -      return null;
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  T visitClosureSend(ast.Send node) {
 | 
| -    assert(node.receiver == null);
 | 
| -    T closure = node.selector.accept(this);
 | 
| -    ArgumentsTypes arguments = analyzeArguments(node.arguments);
 | 
| -    Element element = elements[node];
 | 
| -    Selector selector = elements.getSelector(node);
 | 
| -    if (element != null && element.isFunction) {
 | 
| -      assert(element.isLocal);
 | 
| -      // This only works for function statements. We need a
 | 
| -      // more sophisticated type system with function types to support
 | 
| -      // more.
 | 
| -      return inferrer.registerCalledElement(
 | 
| -          node, selector, outermostElement, element, arguments,
 | 
| -          sideEffects, inLoop);
 | 
| -    } else {
 | 
| -      return inferrer.registerCalledClosure(
 | 
| -          node, selector, closure, outermostElement, arguments,
 | 
| -          sideEffects, inLoop);
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  T handleStaticSend(ast.Node node,
 | 
| -                     Selector selector,
 | 
| -                     Element element,
 | 
| -                     ArgumentsTypes arguments) {
 | 
| -    assert(!element.isFactoryConstructor ||
 | 
| -           !(element as ConstructorElement).isRedirectingFactory);
 | 
| -    // Erroneous elements may be unresolved, for example missing getters.
 | 
| -    if (Elements.isUnresolved(element)) return types.dynamicType;
 | 
| -    // TODO(herhut): should we follow redirecting constructors here? We would
 | 
| -    // need to pay attention of the constructor is pointing to an erroneous
 | 
| -    // element.
 | 
| -    return inferrer.registerCalledElement(
 | 
| -        node, selector, outermostElement, element, arguments,
 | 
| -        sideEffects, inLoop);
 | 
| -  }
 | 
| -
 | 
| -  T handleDynamicSend(ast.Node node,
 | 
| -                      Selector selector,
 | 
| -                      T receiverType,
 | 
| -                      ArgumentsTypes arguments) {
 | 
| -    assert(receiverType != null);
 | 
| -    if (types.selectorNeedsUpdate(receiverType, selector)) {
 | 
| -      selector = (receiverType == types.dynamicType)
 | 
| -          ? selector.asUntyped
 | 
| -          : types.newTypedSelector(receiverType, selector);
 | 
| -      inferrer.updateSelectorInTree(analyzedElement, node, selector);
 | 
| -    }
 | 
| -
 | 
| -    // If the receiver of the call is a local, we may know more about
 | 
| -    // its type by refining it with the potential targets of the
 | 
| -    // calls.
 | 
| -    if (node.asSend() != null) {
 | 
| -      ast.Node receiver = node.asSend().receiver;
 | 
| -      if (receiver != null) {
 | 
| -        Element element = elements[receiver];
 | 
| -        if (Elements.isLocal(element) && !capturedVariables.contains(element)) {
 | 
| -          T refinedType = types.refineReceiver(selector, receiverType);
 | 
| -          locals.update(element, refinedType, node);
 | 
| -        }
 | 
| -      }
 | 
| -    }
 | 
| -
 | 
| -    return inferrer.registerCalledSelector(
 | 
| -        node, selector, receiverType, outermostElement, arguments,
 | 
| -        sideEffects, inLoop);
 | 
| -  }
 | 
| -
 | 
| -  T visitDynamicSend(ast.Send node) {
 | 
| -    Element element = elements[node];
 | 
| -    T receiverType;
 | 
| -    bool isCallOnThis = false;
 | 
| -    if (node.receiver == null) {
 | 
| -      if (treatAsInstanceMember(element)) {
 | 
| -        isCallOnThis = true;
 | 
| -        receiverType = thisType;
 | 
| -      }
 | 
| -    } else {
 | 
| -      ast.Node receiver = node.receiver;
 | 
| -      isCallOnThis = isThisOrSuper(receiver);
 | 
| -      receiverType = visit(receiver);
 | 
| -    }
 | 
| -
 | 
| -    Selector selector = elements.getSelector(node);
 | 
| -    if (!isThisExposed && isCallOnThis) {
 | 
| -      checkIfExposesThis(types.newTypedSelector(receiverType, selector));
 | 
| -    }
 | 
| -
 | 
| -    ArgumentsTypes arguments = node.isPropertyAccess
 | 
| -        ? null
 | 
| -        : analyzeArguments(node.arguments);
 | 
| -    if (selector.name == '=='
 | 
| -        || selector.name == '!=') {
 | 
| -      if (types.isNull(receiverType)) {
 | 
| -        potentiallyAddNullCheck(node, node.arguments.head);
 | 
| -        return types.boolType;
 | 
| -      } else if (types.isNull(arguments.positional[0])) {
 | 
| -        potentiallyAddNullCheck(node, node.receiver);
 | 
| -        return types.boolType;
 | 
| -      }
 | 
| -    }
 | 
| -    return handleDynamicSend(node, selector, receiverType, arguments);
 | 
| -  }
 | 
| -
 | 
| -  void recordReturnType(T type) {
 | 
| -    returnType = inferrer.addReturnTypeFor(analyzedElement, returnType, type);
 | 
| -  }
 | 
| -
 | 
| -  T synthesizeForwardingCall(Spannable node, FunctionElement element) {
 | 
| -    element = element.implementation;
 | 
| -    FunctionElement function = analyzedElement;
 | 
| -    FunctionSignature signature = function.functionSignature;
 | 
| -    FunctionSignature calleeSignature = element.functionSignature;
 | 
| -    if (!calleeSignature.isCompatibleWith(signature)) {
 | 
| -      return types.nonNullEmpty();
 | 
| -    }
 | 
| -
 | 
| -    List<T> unnamed = <T>[];
 | 
| -    signature.forEachRequiredParameter((ParameterElement element) {
 | 
| -      assert(locals.use(element) != null);
 | 
| -      unnamed.add(locals.use(element));
 | 
| -    });
 | 
| -
 | 
| -    Map<String, T> named;
 | 
| -    if (signature.optionalParametersAreNamed) {
 | 
| -      named = new Map<String, T>();
 | 
| -      signature.forEachOptionalParameter((ParameterElement element) {
 | 
| -        named[element.name] = locals.use(element);
 | 
| -      });
 | 
| -    } else {
 | 
| -      signature.forEachOptionalParameter((ParameterElement element) {
 | 
| -        unnamed.add(locals.use(element));
 | 
| -      });
 | 
| -    }
 | 
| -
 | 
| -    ArgumentsTypes arguments = new ArgumentsTypes<T>(unnamed, named);
 | 
| -    return inferrer.registerCalledElement(node,
 | 
| -                                          null,
 | 
| -                                          outermostElement,
 | 
| -                                          element,
 | 
| -                                          arguments,
 | 
| -                                          sideEffects,
 | 
| -                                          inLoop);
 | 
| -  }
 | 
| -  T visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) {
 | 
| -    Element element = elements.getRedirectingTargetConstructor(node);
 | 
| -    if (Elements.isErroneousElement(element)) {
 | 
| -      recordReturnType(types.dynamicType);
 | 
| -    } else {
 | 
| -      // 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].
 | 
| -      T mask = synthesizeForwardingCall(node.constructorReference, element);
 | 
| -      recordReturnType(mask);
 | 
| -    }
 | 
| -    locals.seenReturnOrThrow = true;
 | 
| -    return null;
 | 
| -  }
 | 
| -
 | 
| -  T visitReturn(ast.Return node) {
 | 
| -    ast.Node expression = node.expression;
 | 
| -    recordReturnType(expression == null
 | 
| -        ? types.nullType
 | 
| -        : expression.accept(this));
 | 
| -    locals.seenReturnOrThrow = true;
 | 
| -    return null;
 | 
| -  }
 | 
| -
 | 
| -  T visitForIn(ast.ForIn node) {
 | 
| -    T expressionType = visit(node.expression);
 | 
| -    Selector iteratorSelector = elements.getIteratorSelector(node);
 | 
| -    Selector currentSelector = elements.getCurrentSelector(node);
 | 
| -    Selector moveNextSelector = elements.getMoveNextSelector(node);
 | 
| -
 | 
| -    T iteratorType =
 | 
| -        handleDynamicSend(node, iteratorSelector, expressionType, null);
 | 
| -    handleDynamicSend(node, moveNextSelector,
 | 
| -                      iteratorType, new ArgumentsTypes<T>([], null));
 | 
| -    T currentType =
 | 
| -        handleDynamicSend(node, currentSelector, iteratorType, null);
 | 
| -
 | 
| -    if (node.expression.isThis()) {
 | 
| -      // Any reasonable implementation of an iterator would expose
 | 
| -      // this, so we play it safe and assume it will.
 | 
| -      isThisExposed = true;
 | 
| -    }
 | 
| -
 | 
| -    ast.Node identifier = node.declaredIdentifier;
 | 
| -    Element element = elements.getForInVariable(node);
 | 
| -    Selector selector = elements.getSelector(identifier);
 | 
| -
 | 
| -    T receiverType;
 | 
| -    if (element != null && element.isInstanceMember) {
 | 
| -      receiverType = thisType;
 | 
| -    } else {
 | 
| -      receiverType = types.dynamicType;
 | 
| -    }
 | 
| -
 | 
| -    handlePlainAssignment(identifier, element, selector,
 | 
| -                          receiverType, currentType,
 | 
| -                          node.expression);
 | 
| -    return handleLoop(node, () {
 | 
| -      visit(node.body);
 | 
| -    });
 | 
| -  }
 | 
| -}
 | 
| 
 |