| Index: pkg/kernel/lib/ast.dart
 | 
| diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
 | 
| index 2f3cb111d74337e809f435bacbdfa2000ae8f9db..4bcc7f8e358581854631c9e2d8044e70962a41e9 100644
 | 
| --- a/pkg/kernel/lib/ast.dart
 | 
| +++ b/pkg/kernel/lib/ast.dart
 | 
| @@ -56,6 +56,9 @@ export 'visitor.dart';
 | 
|  import 'type_propagation/type_propagation.dart';
 | 
|  export 'type_propagation/type_propagation.dart';
 | 
|  
 | 
| +import 'canonical_name.dart' show CanonicalName;
 | 
| +export 'canonical_name.dart' show CanonicalName;
 | 
| +
 | 
|  import 'transformations/flags.dart';
 | 
|  import 'text/ast_to_text.dart';
 | 
|  import 'type_algebra.dart';
 | 
| @@ -152,11 +155,86 @@ abstract class TreeNode extends Node {
 | 
|    }
 | 
|  }
 | 
|  
 | 
| +/// An AST node that can be referenced by other nodes.
 | 
| +///
 | 
| +/// There is a single [reference] belonging to this node, providing a level of
 | 
| +/// indirection that is needed during serialization.
 | 
| +abstract class NamedNode extends TreeNode {
 | 
| +  final Reference reference;
 | 
| +
 | 
| +  NamedNode(Reference reference)
 | 
| +      : this.reference = reference ?? new Reference() {
 | 
| +    this.reference.node = this;
 | 
| +  }
 | 
| +
 | 
| +  CanonicalName get canonicalName => reference?.canonicalName;
 | 
| +}
 | 
| +
 | 
| +/// Indirection between a reference and its definition.
 | 
| +///
 | 
| +/// There is only one reference object per [NamedNode].
 | 
| +class Reference {
 | 
| +  CanonicalName canonicalName;
 | 
| +  NamedNode node;
 | 
| +
 | 
| +  String toString() {
 | 
| +    if (canonicalName != null) {
 | 
| +      return 'Reference to $canonicalName';
 | 
| +    }
 | 
| +    if (node != null) {
 | 
| +      return 'Reference to $node';
 | 
| +    }
 | 
| +    return 'Unbound reference';
 | 
| +  }
 | 
| +
 | 
| +  Library get asLibrary {
 | 
| +    if (node == null) {
 | 
| +      throw '$this is not bound to an AST node. A library was expected';
 | 
| +    }
 | 
| +    return node as Library;
 | 
| +  }
 | 
| +
 | 
| +  Class get asClass {
 | 
| +    if (node == null) {
 | 
| +      throw '$this is not bound to an AST node. A class was expected';
 | 
| +    }
 | 
| +    return node as Class;
 | 
| +  }
 | 
| +
 | 
| +  Member get asMember {
 | 
| +    if (node == null) {
 | 
| +      throw '$this is not bound to an AST node. A member was expected';
 | 
| +    }
 | 
| +    return node as Member;
 | 
| +  }
 | 
| +
 | 
| +  Field get asField {
 | 
| +    if (node == null) {
 | 
| +      throw '$this is not bound to an AST node. A field was expected';
 | 
| +    }
 | 
| +    return node as Field;
 | 
| +  }
 | 
| +
 | 
| +  Constructor get asConstructor {
 | 
| +    if (node == null) {
 | 
| +      throw '$this is not bound to an AST node. A constructor was expected';
 | 
| +    }
 | 
| +    return node as Constructor;
 | 
| +  }
 | 
| +
 | 
| +  Procedure get asProcedure {
 | 
| +    if (node == null) {
 | 
| +      throw '$this is not bound to an AST node. A procedure was expected';
 | 
| +    }
 | 
| +    return node as Procedure;
 | 
| +  }
 | 
| +}
 | 
| +
 | 
|  // ------------------------------------------------------------------------
 | 
|  //                      LIBRARIES and CLASSES
 | 
|  // ------------------------------------------------------------------------
 | 
|  
 | 
| -class Library extends TreeNode implements Comparable<Library> {
 | 
| +class Library extends NamedNode implements Comparable<Library> {
 | 
|    /// An import path to this library.
 | 
|    ///
 | 
|    /// The [Uri] should have the `dart`, `package`, `app`, or `file` scheme.
 | 
| @@ -191,11 +269,13 @@ class Library extends TreeNode implements Comparable<Library> {
 | 
|        List<Class> classes,
 | 
|        List<Procedure> procedures,
 | 
|        List<Field> fields,
 | 
| -      this.fileUri})
 | 
| +      this.fileUri,
 | 
| +      Reference reference})
 | 
|        : this.deferredImports = imports ?? <DeferredImport>[],
 | 
|          this.classes = classes ?? <Class>[],
 | 
|          this.procedures = procedures ?? <Procedure>[],
 | 
| -        this.fields = fields ?? <Field>[] {
 | 
| +        this.fields = fields ?? <Field>[],
 | 
| +        super(reference) {
 | 
|      setParents(this.classes, this);
 | 
|      setParents(this.procedures, this);
 | 
|      setParents(this.fields, this);
 | 
| @@ -224,6 +304,20 @@ class Library extends TreeNode implements Comparable<Library> {
 | 
|      classes.add(class_);
 | 
|    }
 | 
|  
 | 
| +  void computeCanonicalNames() {
 | 
| +    assert(canonicalName != null);
 | 
| +    for (var field in fields) {
 | 
| +      canonicalName.getChildFromMember(field).bindTo(field.reference);
 | 
| +    }
 | 
| +    for (var member in procedures) {
 | 
| +      canonicalName.getChildFromMember(member).bindTo(member.reference);
 | 
| +    }
 | 
| +    for (var class_ in classes) {
 | 
| +      canonicalName.getChild(class_.name).bindTo(class_.reference);
 | 
| +      class_.computeCanonicalNames();
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
|    accept(TreeVisitor v) => v.visitLibrary(this);
 | 
|  
 | 
|    visitChildren(Visitor v) {
 | 
| @@ -254,12 +348,16 @@ class Library extends TreeNode implements Comparable<Library> {
 | 
|  
 | 
|  /// An import of form: `import <url> deferred as <name>;`.
 | 
|  class DeferredImport extends TreeNode {
 | 
| -  Library importedLibrary;
 | 
| +  Reference importedLibraryReference;
 | 
|    String name;
 | 
|  
 | 
| -  DeferredImport(this.importedLibrary, this.name);
 | 
| +  DeferredImport(Library importedLibrary, String name)
 | 
| +      : this.byReference(importedLibrary.reference, name);
 | 
| +
 | 
| +  DeferredImport.byReference(this.importedLibraryReference, this.name);
 | 
|  
 | 
|    Library get enclosingLibrary => parent;
 | 
| +  Library get importedLibrary => importedLibraryReference.asLibrary;
 | 
|  
 | 
|    accept(TreeVisitor v) => v.visitDeferredImport(this);
 | 
|  
 | 
| @@ -321,7 +419,7 @@ enum ClassLevel {
 | 
|  /// use those from its mixed-in type.  However, the IR does not enforce this
 | 
|  /// rule directly, as doing so can obstruct transformations.  It is possible to
 | 
|  /// transform a mixin application to become a regular class, and vice versa.
 | 
| -class Class extends TreeNode {
 | 
| +class Class extends NamedNode {
 | 
|    /// The degree to which the contents of the class have been loaded.
 | 
|    ClassLevel level = ClassLevel.Body;
 | 
|  
 | 
| @@ -378,18 +476,33 @@ class Class extends TreeNode {
 | 
|        List<Constructor> constructors,
 | 
|        List<Procedure> procedures,
 | 
|        List<Field> fields,
 | 
| -      this.fileUri})
 | 
| +      this.fileUri,
 | 
| +      Reference reference})
 | 
|        : this.typeParameters = typeParameters ?? <TypeParameter>[],
 | 
|          this.implementedTypes = implementedTypes ?? <Supertype>[],
 | 
|          this.fields = fields ?? <Field>[],
 | 
|          this.constructors = constructors ?? <Constructor>[],
 | 
| -        this.procedures = procedures ?? <Procedure>[] {
 | 
| +        this.procedures = procedures ?? <Procedure>[],
 | 
| +        super(reference) {
 | 
|      setParents(this.typeParameters, this);
 | 
|      setParents(this.constructors, this);
 | 
|      setParents(this.procedures, this);
 | 
|      setParents(this.fields, this);
 | 
|    }
 | 
|  
 | 
| +  void computeCanonicalNames() {
 | 
| +    assert(canonicalName != null);
 | 
| +    for (var member in fields) {
 | 
| +      canonicalName.getChildFromMember(member).bindTo(member.reference);
 | 
| +    }
 | 
| +    for (var member in procedures) {
 | 
| +      canonicalName.getChildFromMember(member).bindTo(member.reference);
 | 
| +    }
 | 
| +    for (var member in constructors) {
 | 
| +      canonicalName.getChildFromMember(member).bindTo(member.reference);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
|    /// The immediate super class, or `null` if this is the root class.
 | 
|    Class get superclass => supertype?.classNode;
 | 
|  
 | 
| @@ -522,14 +635,7 @@ class Class extends TreeNode {
 | 
|  //                            MEMBERS
 | 
|  // ------------------------------------------------------------------------
 | 
|  
 | 
| -/// A indirect reference to a member, which can be updated to point at another
 | 
| -/// member at a later time.
 | 
| -class _MemberAccessor {
 | 
| -  Member target;
 | 
| -  _MemberAccessor(this.target);
 | 
| -}
 | 
| -
 | 
| -abstract class Member extends TreeNode {
 | 
| +abstract class Member extends NamedNode {
 | 
|    /// End offset in the source file it comes from. Valid values are from 0 and
 | 
|    /// up, or -1 ([TreeNode.noOffset]) if the file end offset is not available
 | 
|    /// (this is the default if none is specifically set).
 | 
| @@ -559,7 +665,7 @@ abstract class Member extends TreeNode {
 | 
|    // TODO(asgerf): It might be worthwhile to put this on classes as well.
 | 
|    int transformerFlags = 0;
 | 
|  
 | 
| -  Member(this.name);
 | 
| +  Member(this.name, Reference reference) : super(reference);
 | 
|  
 | 
|    Class get enclosingClass => parent is Class ? parent : null;
 | 
|    Library get enclosingLibrary => parent is Class ? parent.parent : parent;
 | 
| @@ -617,9 +723,6 @@ abstract class Member extends TreeNode {
 | 
|    bool get containsSuperCalls {
 | 
|      return transformerFlags & TransformerFlag.superCalls != 0;
 | 
|    }
 | 
| -
 | 
| -  _MemberAccessor get _getterInterface;
 | 
| -  _MemberAccessor get _setterInterface;
 | 
|  }
 | 
|  
 | 
|  /// A field declaration.
 | 
| @@ -627,8 +730,6 @@ abstract class Member extends TreeNode {
 | 
|  /// The implied getter and setter for the field are not represented explicitly,
 | 
|  /// but can be made explicit if needed.
 | 
|  class Field extends Member {
 | 
| -  _MemberAccessor _getterInterface, _setterInterface;
 | 
| -
 | 
|    DartType type; // Not null. Defaults to DynamicType.
 | 
|    InferredValue inferredValue; // May be null.
 | 
|    int flags = 0;
 | 
| @@ -647,10 +748,9 @@ class Field extends Member {
 | 
|        bool hasImplicitGetter,
 | 
|        bool hasImplicitSetter,
 | 
|        int transformerFlags: 0,
 | 
| -      this.fileUri})
 | 
| -      : super(name) {
 | 
| -    _getterInterface = new _MemberAccessor(this);
 | 
| -    _setterInterface = new _MemberAccessor(this);
 | 
| +      this.fileUri,
 | 
| +      Reference reference})
 | 
| +      : super(name, reference) {
 | 
|      assert(type != null);
 | 
|      initializer?.parent = this;
 | 
|      this.isFinal = isFinal;
 | 
| @@ -750,46 +850,6 @@ class Field extends Member {
 | 
|    DartType get getterType => type;
 | 
|    DartType get setterType => isMutable ? type : const BottomType();
 | 
|  
 | 
| -  /// Makes all [PropertyGet]s that have this field as its interface target
 | 
| -  /// use [getter] as its interface target instead.
 | 
| -  ///
 | 
| -  /// That can be used to introduce an explicit getter for a field instead of
 | 
| -  /// its implicit getter.
 | 
| -  ///
 | 
| -  /// This method only updates the stored interface target -- the caller must
 | 
| -  /// ensure that [getter] actually becomes the target for dispatches that
 | 
| -  /// would previously hit the implicit field getter.
 | 
| -  ///
 | 
| -  /// [DirectPropertyGet]s are not affected, and will continue to access the
 | 
| -  /// field directly. [PropertyGet] nodes created after the call will not be
 | 
| -  /// affected until the method is called again.
 | 
| -  ///
 | 
| -  /// Existing [ClassHierarchy] instances are not affected by this call.
 | 
| -  void replaceGetterInterfaceWith(Procedure getter) {
 | 
| -    _getterInterface.target = getter;
 | 
| -    _getterInterface = new _MemberAccessor(this);
 | 
| -  }
 | 
| -
 | 
| -  /// Makes all [PropertySet]s that have this field as its interface target
 | 
| -  /// use [setter] as its interface target instead.
 | 
| -  ///
 | 
| -  /// That can be used to introduce an explicit setter for a field instead of
 | 
| -  /// its implicit setter.
 | 
| -  ///
 | 
| -  /// This method only updates the stored interface target -- the caller must
 | 
| -  /// ensure that [setter] actually becomes the target for dispatches that
 | 
| -  /// would previously hit the implicit field setter.
 | 
| -  ///
 | 
| -  /// [DirectPropertySet] and [FieldInitializer]s are not affected, and will
 | 
| -  /// continue to access the field directly.  [PropertySet] nodes created after
 | 
| -  /// the call will not be affected until the method is called again.
 | 
| -  ///
 | 
| -  /// Existing [ClassHierarchy] instances are not affected by this call.
 | 
| -  void replaceSetterInterfaceWith(Procedure setter) {
 | 
| -    _setterInterface.target = setter;
 | 
| -    _setterInterface = new _MemberAccessor(this);
 | 
| -  }
 | 
| -
 | 
|    Location _getLocationInEnclosingFile(int offset) {
 | 
|      return enclosingProgram.getLocation(fileUri, offset);
 | 
|    }
 | 
| @@ -813,9 +873,10 @@ class Constructor extends Member {
 | 
|        bool isConst: false,
 | 
|        bool isExternal: false,
 | 
|        List<Initializer> initializers,
 | 
| -      int transformerFlags: 0})
 | 
| +      int transformerFlags: 0,
 | 
| +      Reference reference})
 | 
|        : this.initializers = initializers ?? <Initializer>[],
 | 
| -        super(name) {
 | 
| +        super(name, reference) {
 | 
|      function?.parent = this;
 | 
|      setParents(this.initializers, this);
 | 
|      this.isConst = isConst;
 | 
| @@ -864,14 +925,6 @@ class Constructor extends Member {
 | 
|  
 | 
|    DartType get getterType => const BottomType();
 | 
|    DartType get setterType => const BottomType();
 | 
| -
 | 
| -  _MemberAccessor get _getterInterface {
 | 
| -    throw 'Constructors cannot be used as getters';
 | 
| -  }
 | 
| -
 | 
| -  _MemberAccessor get _setterInterface {
 | 
| -    throw 'Constructors cannot be used as setters';
 | 
| -  }
 | 
|  }
 | 
|  
 | 
|  /// A method, getter, setter, index-getter, index-setter, operator overloader,
 | 
| @@ -890,7 +943,6 @@ class Constructor extends Member {
 | 
|  /// For operators, this is the token for the operator, e.g. `+` or `==`,
 | 
|  /// except for the unary minus operator, whose name is `unary-`.
 | 
|  class Procedure extends Member {
 | 
| -  _MemberAccessor _reference;
 | 
|    ProcedureKind kind;
 | 
|    int flags = 0;
 | 
|    FunctionNode function; // Body is null if and only if abstract or external.
 | 
| @@ -904,9 +956,9 @@ class Procedure extends Member {
 | 
|        bool isExternal: false,
 | 
|        bool isConst: false,
 | 
|        int transformerFlags: 0,
 | 
| -      this.fileUri})
 | 
| -      : super(name) {
 | 
| -    _reference = new _MemberAccessor(this);
 | 
| +      this.fileUri,
 | 
| +      Reference reference})
 | 
| +      : super(name, reference) {
 | 
|      function?.parent = this;
 | 
|      this.isAbstract = isAbstract;
 | 
|      this.isStatic = isStatic;
 | 
| @@ -979,9 +1031,6 @@ class Procedure extends Member {
 | 
|          : const BottomType();
 | 
|    }
 | 
|  
 | 
| -  _MemberAccessor get _getterInterface => _reference;
 | 
| -  _MemberAccessor get _setterInterface => _reference;
 | 
| -
 | 
|    Location _getLocationInEnclosingFile(int offset) {
 | 
|      return enclosingProgram.getLocation(fileUri, offset);
 | 
|    }
 | 
| @@ -1027,13 +1076,22 @@ class InvalidInitializer extends Initializer {
 | 
|  //  exactly once, and that no fields are assigned twice in the initializer list.
 | 
|  class FieldInitializer extends Initializer {
 | 
|    /// Reference to the field being initialized.  Not null.
 | 
| -  Field field;
 | 
| +  Reference fieldReference;
 | 
|    Expression value;
 | 
|  
 | 
| -  FieldInitializer(this.field, this.value) {
 | 
| +  FieldInitializer(Field field, Expression value)
 | 
| +      : this.byReference(field?.reference, value);
 | 
| +
 | 
| +  FieldInitializer.byReference(this.fieldReference, this.value) {
 | 
|      value?.parent = this;
 | 
|    }
 | 
|  
 | 
| +  Field get field => fieldReference?.node;
 | 
| +
 | 
| +  void set field(Field field) {
 | 
| +    fieldReference = field?.reference;
 | 
| +  }
 | 
| +
 | 
|    accept(InitializerVisitor v) => v.visitFieldInitializer(this);
 | 
|  
 | 
|    visitChildren(Visitor v) {
 | 
| @@ -1060,13 +1118,22 @@ class FieldInitializer extends Initializer {
 | 
|  // from the extends clause.
 | 
|  class SuperInitializer extends Initializer {
 | 
|    /// Reference to the constructor being invoked in the super class. Not null.
 | 
| -  Constructor target;
 | 
| +  Reference targetReference;
 | 
|    Arguments arguments;
 | 
|  
 | 
| -  SuperInitializer(this.target, this.arguments) {
 | 
| +  SuperInitializer(Constructor target, Arguments arguments)
 | 
| +      : this.byReference(getMemberReference(target), arguments);
 | 
| +
 | 
| +  SuperInitializer.byReference(this.targetReference, this.arguments) {
 | 
|      arguments?.parent = this;
 | 
|    }
 | 
|  
 | 
| +  Constructor get target => targetReference?.asConstructor;
 | 
| +
 | 
| +  void set target(Constructor target) {
 | 
| +    targetReference = getMemberReference(target);
 | 
| +  }
 | 
| +
 | 
|    accept(InitializerVisitor v) => v.visitSuperInitializer(this);
 | 
|  
 | 
|    visitChildren(Visitor v) {
 | 
| @@ -1089,13 +1156,22 @@ class SuperInitializer extends Initializer {
 | 
|  // constructor has a body or if there is a cycle in the initializer calls.
 | 
|  class RedirectingInitializer extends Initializer {
 | 
|    /// Reference to the constructor being invoked in the same class. Not null.
 | 
| -  Constructor target;
 | 
| +  Reference targetReference;
 | 
|    Arguments arguments;
 | 
|  
 | 
| -  RedirectingInitializer(this.target, this.arguments) {
 | 
| +  RedirectingInitializer(Constructor target, Arguments arguments)
 | 
| +      : this.byReference(getMemberReference(target), arguments);
 | 
| +
 | 
| +  RedirectingInitializer.byReference(this.targetReference, this.arguments) {
 | 
|      arguments?.parent = this;
 | 
|    }
 | 
|  
 | 
| +  Constructor get target => targetReference?.asConstructor;
 | 
| +
 | 
| +  void set target(Constructor target) {
 | 
| +    targetReference = getMemberReference(target);
 | 
| +  }
 | 
| +
 | 
|    accept(InitializerVisitor v) => v.visitRedirectingInitializer(this);
 | 
|  
 | 
|    visitChildren(Visitor v) {
 | 
| @@ -1388,17 +1464,20 @@ class PropertyGet extends Expression {
 | 
|    Expression receiver;
 | 
|    Name name;
 | 
|  
 | 
| -  _MemberAccessor _interfaceTargetReference;
 | 
| +  Reference interfaceTargetReference;
 | 
| +
 | 
| +  PropertyGet(Expression receiver, Name name, [Member interfaceTarget])
 | 
| +      : this.byReference(receiver, name, getMemberReference(interfaceTarget));
 | 
|  
 | 
| -  PropertyGet(this.receiver, this.name, [Member interfaceTarget]) {
 | 
| +  PropertyGet.byReference(
 | 
| +      this.receiver, this.name, this.interfaceTargetReference) {
 | 
|      receiver?.parent = this;
 | 
| -    this.interfaceTarget = interfaceTarget;
 | 
|    }
 | 
|  
 | 
| -  Member get interfaceTarget => _interfaceTargetReference?.target;
 | 
| +  Member get interfaceTarget => interfaceTargetReference?.asMember;
 | 
|  
 | 
| -  void set interfaceTarget(Member newTarget) {
 | 
| -    _interfaceTargetReference = newTarget?._getterInterface;
 | 
| +  void set interfaceTarget(Member member) {
 | 
| +    interfaceTargetReference = getMemberReference(member);
 | 
|    }
 | 
|  
 | 
|    DartType getStaticType(TypeEnvironment types) {
 | 
| @@ -1445,18 +1524,23 @@ class PropertySet extends Expression {
 | 
|    Name name;
 | 
|    Expression value;
 | 
|  
 | 
| -  _MemberAccessor _interfaceTargetReference;
 | 
| +  Reference interfaceTargetReference;
 | 
|  
 | 
| -  PropertySet(this.receiver, this.name, this.value, [Member interfaceTarget]) {
 | 
| +  PropertySet(Expression receiver, Name name, Expression value,
 | 
| +      [Member interfaceTarget])
 | 
| +      : this.byReference(
 | 
| +            receiver, name, value, getMemberReference(interfaceTarget));
 | 
| +
 | 
| +  PropertySet.byReference(
 | 
| +      this.receiver, this.name, this.value, this.interfaceTargetReference) {
 | 
|      receiver?.parent = this;
 | 
|      value?.parent = this;
 | 
| -    this.interfaceTarget = interfaceTarget;
 | 
|    }
 | 
|  
 | 
| -  Member get interfaceTarget => _interfaceTargetReference?.target;
 | 
| +  Member get interfaceTarget => interfaceTargetReference?.asMember;
 | 
|  
 | 
| -  void set interfaceTarget(Member newTarget) {
 | 
| -    _interfaceTargetReference = newTarget?._setterInterface;
 | 
| +  void set interfaceTarget(Member member) {
 | 
| +    interfaceTargetReference = getMemberReference(member);
 | 
|    }
 | 
|  
 | 
|    DartType getStaticType(TypeEnvironment types) => value.getStaticType(types);
 | 
| @@ -1484,12 +1568,21 @@ class PropertySet extends Expression {
 | 
|  /// Directly read a field, call a getter, or tear off a method.
 | 
|  class DirectPropertyGet extends Expression {
 | 
|    Expression receiver;
 | 
| -  Member target;
 | 
| +  Reference targetReference;
 | 
| +
 | 
| +  DirectPropertyGet(Expression receiver, Member target)
 | 
| +      : this.byReference(receiver, getMemberReference(target));
 | 
|  
 | 
| -  DirectPropertyGet(this.receiver, this.target) {
 | 
| +  DirectPropertyGet.byReference(this.receiver, this.targetReference) {
 | 
|      receiver?.parent = this;
 | 
|    }
 | 
|  
 | 
| +  Member get target => targetReference?.asMember;
 | 
| +
 | 
| +  void set target(Member target) {
 | 
| +    targetReference = getMemberReference(target);
 | 
| +  }
 | 
| +
 | 
|    visitChildren(Visitor v) {
 | 
|      receiver?.accept(v);
 | 
|      target?.acceptReference(v);
 | 
| @@ -1518,14 +1611,24 @@ class DirectPropertyGet extends Expression {
 | 
|  /// Evaluates to the value of [value].
 | 
|  class DirectPropertySet extends Expression {
 | 
|    Expression receiver;
 | 
| -  Member target;
 | 
| +  Reference targetReference;
 | 
|    Expression value;
 | 
|  
 | 
| -  DirectPropertySet(this.receiver, this.target, this.value) {
 | 
| +  DirectPropertySet(Expression receiver, Member target, Expression value)
 | 
| +      : this.byReference(receiver, getMemberReference(target), value);
 | 
| +
 | 
| +  DirectPropertySet.byReference(
 | 
| +      this.receiver, this.targetReference, this.value) {
 | 
|      receiver?.parent = this;
 | 
|      value?.parent = this;
 | 
|    }
 | 
|  
 | 
| +  Member get target => targetReference?.asMember;
 | 
| +
 | 
| +  void set target(Member target) {
 | 
| +    targetReference = getMemberReference(target);
 | 
| +  }
 | 
| +
 | 
|    visitChildren(Visitor v) {
 | 
|      receiver?.accept(v);
 | 
|      target?.acceptReference(v);
 | 
| @@ -1551,14 +1654,25 @@ class DirectPropertySet extends Expression {
 | 
|  /// Directly call an instance method, bypassing ordinary dispatch.
 | 
|  class DirectMethodInvocation extends InvocationExpression {
 | 
|    Expression receiver;
 | 
| -  Procedure target;
 | 
| +  Reference targetReference;
 | 
|    Arguments arguments;
 | 
|  
 | 
| -  DirectMethodInvocation(this.receiver, this.target, this.arguments) {
 | 
| +  DirectMethodInvocation(
 | 
| +      Expression receiver, Procedure target, Arguments arguments)
 | 
| +      : this.byReference(receiver, getMemberReference(target), arguments);
 | 
| +
 | 
| +  DirectMethodInvocation.byReference(
 | 
| +      this.receiver, this.targetReference, this.arguments) {
 | 
|      receiver?.parent = this;
 | 
|      arguments?.parent = this;
 | 
|    }
 | 
|  
 | 
| +  Procedure get target => targetReference?.asProcedure;
 | 
| +
 | 
| +  void set target(Procedure target) {
 | 
| +    targetReference = getMemberReference(target);
 | 
| +  }
 | 
| +
 | 
|    Name get name => target?.name;
 | 
|  
 | 
|    visitChildren(Visitor v) {
 | 
| @@ -1601,16 +1715,17 @@ class DirectMethodInvocation extends InvocationExpression {
 | 
|  /// This may invoke a getter, read a field, or tear off a method.
 | 
|  class SuperPropertyGet extends Expression {
 | 
|    Name name;
 | 
| -  _MemberAccessor _interfaceTargetReference;
 | 
| +  Reference interfaceTargetReference;
 | 
|  
 | 
| -  SuperPropertyGet(this.name, [Member interfaceTarget]) {
 | 
| -    _interfaceTargetReference = interfaceTarget?._getterInterface;
 | 
| -  }
 | 
| +  SuperPropertyGet(Name name, [Member interfaceTarget])
 | 
| +      : this.byReference(name, getMemberReference(interfaceTarget));
 | 
|  
 | 
| -  Member get interfaceTarget => _interfaceTargetReference?.target;
 | 
| +  SuperPropertyGet.byReference(this.name, this.interfaceTargetReference);
 | 
|  
 | 
| -  void set interfaceTarget(Member newTarget) {
 | 
| -    _interfaceTargetReference = newTarget?._getterInterface;
 | 
| +  Member get interfaceTarget => interfaceTargetReference?.asMember;
 | 
| +
 | 
| +  void set interfaceTarget(Member member) {
 | 
| +    interfaceTargetReference = getMemberReference(member);
 | 
|    }
 | 
|  
 | 
|    DartType getStaticType(TypeEnvironment types) {
 | 
| @@ -1642,17 +1757,20 @@ class SuperPropertyGet extends Expression {
 | 
|  class SuperPropertySet extends Expression {
 | 
|    Name name;
 | 
|    Expression value;
 | 
| -  _MemberAccessor _interfaceTargetReference;
 | 
| +  Reference interfaceTargetReference;
 | 
| +
 | 
| +  SuperPropertySet(Name name, Expression value, Member interfaceTarget)
 | 
| +      : this.byReference(name, value, getMemberReference(interfaceTarget));
 | 
|  
 | 
| -  SuperPropertySet(this.name, this.value, [Member interfaceTarget]) {
 | 
| +  SuperPropertySet.byReference(
 | 
| +      this.name, this.value, this.interfaceTargetReference) {
 | 
|      value?.parent = this;
 | 
| -    _interfaceTargetReference = interfaceTarget?._setterInterface;
 | 
|    }
 | 
|  
 | 
| -  Member get interfaceTarget => _interfaceTargetReference?.target;
 | 
| +  Member get interfaceTarget => interfaceTargetReference?.asMember;
 | 
|  
 | 
| -  void set interfaceTarget(Member newTarget) {
 | 
| -    _interfaceTargetReference = newTarget?._setterInterface;
 | 
| +  void set interfaceTarget(Member member) {
 | 
| +    interfaceTargetReference = getMemberReference(member);
 | 
|    }
 | 
|  
 | 
|    DartType getStaticType(TypeEnvironment types) => value.getStaticType(types);
 | 
| @@ -1675,9 +1793,17 @@ class SuperPropertySet extends Expression {
 | 
|  /// Read a static field, call a static getter, or tear off a static method.
 | 
|  class StaticGet extends Expression {
 | 
|    /// A static field, getter, or method (for tear-off).
 | 
| -  Member target;
 | 
| +  Reference targetReference;
 | 
|  
 | 
| -  StaticGet(this.target);
 | 
| +  StaticGet(Member target) : this.byReference(getMemberReference(target));
 | 
| +
 | 
| +  StaticGet.byReference(this.targetReference);
 | 
| +
 | 
| +  Member get target => targetReference?.asMember;
 | 
| +
 | 
| +  void set target(Member target) {
 | 
| +    targetReference = getMemberReference(target);
 | 
| +  }
 | 
|  
 | 
|    DartType getStaticType(TypeEnvironment types) => target.getterType;
 | 
|  
 | 
| @@ -1695,13 +1821,22 @@ class StaticGet extends Expression {
 | 
|  /// Evaluates to the value of [value].
 | 
|  class StaticSet extends Expression {
 | 
|    /// A mutable static field or a static setter.
 | 
| -  Member target;
 | 
| +  Reference targetReference;
 | 
|    Expression value;
 | 
|  
 | 
| -  StaticSet(this.target, this.value) {
 | 
| +  StaticSet(Member target, Expression value)
 | 
| +      : this.byReference(getMemberReference(target), value);
 | 
| +
 | 
| +  StaticSet.byReference(this.targetReference, this.value) {
 | 
|      value?.parent = this;
 | 
|    }
 | 
|  
 | 
| +  Member get target => targetReference?.asMember;
 | 
| +
 | 
| +  void set target(Member target) {
 | 
| +    targetReference = getMemberReference(target);
 | 
| +  }
 | 
| +
 | 
|    DartType getStaticType(TypeEnvironment types) => value.getStaticType(types);
 | 
|  
 | 
|    accept(ExpressionVisitor v) => v.visitStaticSet(this);
 | 
| @@ -1795,14 +1930,25 @@ class MethodInvocation extends InvocationExpression {
 | 
|    Name name;
 | 
|    Arguments arguments;
 | 
|  
 | 
| -  Procedure interfaceTarget;
 | 
| +  Reference interfaceTargetReference;
 | 
|  
 | 
| -  MethodInvocation(this.receiver, this.name, this.arguments,
 | 
| -      [this.interfaceTarget]) {
 | 
| +  MethodInvocation(Expression receiver, Name name, Arguments arguments,
 | 
| +      [Procedure interfaceTarget])
 | 
| +      : this.byReference(
 | 
| +            receiver, name, arguments, getMemberReference(interfaceTarget));
 | 
| +
 | 
| +  MethodInvocation.byReference(
 | 
| +      this.receiver, this.name, this.arguments, this.interfaceTargetReference) {
 | 
|      receiver?.parent = this;
 | 
|      arguments?.parent = this;
 | 
|    }
 | 
|  
 | 
| +  Procedure get interfaceTarget => interfaceTargetReference?.asProcedure;
 | 
| +
 | 
| +  void set interfaceTarget(Member target) {
 | 
| +    interfaceTargetReference = getMemberReference(target);
 | 
| +  }
 | 
| +
 | 
|    DartType getStaticType(TypeEnvironment types) {
 | 
|      if (interfaceTarget != null) {
 | 
|        if (types.isOverloadedArithmeticOperator(interfaceTarget)) {
 | 
| @@ -1864,12 +2010,23 @@ class SuperMethodInvocation extends InvocationExpression {
 | 
|    Name name;
 | 
|    Arguments arguments;
 | 
|  
 | 
| -  Procedure interfaceTarget;
 | 
| +  Reference interfaceTargetReference;
 | 
| +
 | 
| +  SuperMethodInvocation(Name name, Arguments arguments,
 | 
| +      [Procedure interfaceTarget])
 | 
| +      : this.byReference(name, arguments, getMemberReference(interfaceTarget));
 | 
|  
 | 
| -  SuperMethodInvocation(this.name, this.arguments, this.interfaceTarget) {
 | 
| +  SuperMethodInvocation.byReference(
 | 
| +      this.name, this.arguments, this.interfaceTargetReference) {
 | 
|      arguments?.parent = this;
 | 
|    }
 | 
|  
 | 
| +  Procedure get interfaceTarget => interfaceTargetReference?.asProcedure;
 | 
| +
 | 
| +  void set interfaceTarget(Procedure target) {
 | 
| +    interfaceTargetReference = getMemberReference(target);
 | 
| +  }
 | 
| +
 | 
|    DartType getStaticType(TypeEnvironment types) {
 | 
|      if (interfaceTarget == null) return const DynamicType();
 | 
|      Class superclass = interfaceTarget.enclosingClass;
 | 
| @@ -1903,7 +2060,7 @@ class SuperMethodInvocation extends InvocationExpression {
 | 
|  ///
 | 
|  /// The provided arguments might not match the parameters of the target.
 | 
|  class StaticInvocation extends InvocationExpression {
 | 
| -  Procedure target;
 | 
| +  Reference targetReference;
 | 
|    Arguments arguments;
 | 
|  
 | 
|    /// True if this is a constant call to an external constant factory.
 | 
| @@ -1911,10 +2068,21 @@ class StaticInvocation extends InvocationExpression {
 | 
|  
 | 
|    Name get name => target?.name;
 | 
|  
 | 
| -  StaticInvocation(this.target, this.arguments, {this.isConst: false}) {
 | 
| +  StaticInvocation(Procedure target, Arguments arguments, {bool isConst: false})
 | 
| +      : this.byReference(getMemberReference(target), arguments,
 | 
| +            isConst: isConst);
 | 
| +
 | 
| +  StaticInvocation.byReference(this.targetReference, this.arguments,
 | 
| +      {this.isConst: false}) {
 | 
|      arguments?.parent = this;
 | 
|    }
 | 
|  
 | 
| +  Procedure get target => targetReference?.asProcedure;
 | 
| +
 | 
| +  void set target(Procedure target) {
 | 
| +    targetReference = getMemberReference(target);
 | 
| +  }
 | 
| +
 | 
|    DartType getStaticType(TypeEnvironment types) {
 | 
|      return Substitution
 | 
|          .fromPairs(target.function.typeParameters, arguments.types)
 | 
| @@ -1944,16 +2112,28 @@ class StaticInvocation extends InvocationExpression {
 | 
|  // `classTypeArguments`? They are quite different from type arguments to
 | 
|  // generic functions.
 | 
|  class ConstructorInvocation extends InvocationExpression {
 | 
| -  Constructor target;
 | 
| +  Reference targetReference;
 | 
|    Arguments arguments;
 | 
|    bool isConst;
 | 
|  
 | 
|    Name get name => target?.name;
 | 
|  
 | 
| -  ConstructorInvocation(this.target, this.arguments, {this.isConst: false}) {
 | 
| +  ConstructorInvocation(Constructor target, Arguments arguments,
 | 
| +      {bool isConst: false})
 | 
| +      : this.byReference(getMemberReference(target), arguments,
 | 
| +            isConst: isConst);
 | 
| +
 | 
| +  ConstructorInvocation.byReference(this.targetReference, this.arguments,
 | 
| +      {this.isConst: false}) {
 | 
|      arguments?.parent = this;
 | 
|    }
 | 
|  
 | 
| +  Constructor get target => targetReference?.asConstructor;
 | 
| +
 | 
| +  void set target(Constructor target) {
 | 
| +    targetReference = getMemberReference(target);
 | 
| +  }
 | 
| +
 | 
|    DartType getStaticType(TypeEnvironment types) {
 | 
|      return arguments.types.isEmpty
 | 
|          ? target.enclosingClass.rawType
 | 
| @@ -3200,17 +3380,21 @@ class FunctionDeclaration extends Statement {
 | 
|  abstract class Name implements Node {
 | 
|    final int hashCode;
 | 
|    final String name;
 | 
| +  Reference get libraryName;
 | 
|    Library get library;
 | 
|    bool get isPrivate;
 | 
|  
 | 
|    Name._internal(this.hashCode, this.name);
 | 
|  
 | 
| -  factory Name(String name, [Library library]) {
 | 
| +  factory Name(String name, [Library library]) =>
 | 
| +      new Name.byReference(name, library?.reference);
 | 
| +
 | 
| +  factory Name.byReference(String name, Reference libraryName) {
 | 
|      /// Use separate subclasses for the public and private case to save memory
 | 
|      /// for public names.
 | 
|      if (name.startsWith('_')) {
 | 
| -      assert(library != null);
 | 
| -      return new _PrivateName(name, library);
 | 
| +      assert(libraryName != null);
 | 
| +      return new _PrivateName(name, libraryName);
 | 
|      } else {
 | 
|        return new _PublicName(name);
 | 
|      }
 | 
| @@ -3228,21 +3412,24 @@ abstract class Name implements Node {
 | 
|  }
 | 
|  
 | 
|  class _PrivateName extends Name {
 | 
| -  final Library library;
 | 
| +  final Reference libraryName;
 | 
|    bool get isPrivate => true;
 | 
|  
 | 
| -  _PrivateName(String name, Library library)
 | 
| -      : this.library = library,
 | 
| -        super._internal(_computeHashCode(name, library), name);
 | 
| +  _PrivateName(String name, Reference libraryName)
 | 
| +      : this.libraryName = libraryName,
 | 
| +        super._internal(_computeHashCode(name, libraryName), name);
 | 
|  
 | 
|    String toString() => library != null ? '$library::$name' : name;
 | 
|  
 | 
| -  static int _computeHashCode(String name, Library library) {
 | 
| -    return 131 * name.hashCode + 17 * library.hashCode;
 | 
| +  Library get library => libraryName.asLibrary;
 | 
| +
 | 
| +  static int _computeHashCode(String name, Reference libraryName) {
 | 
| +    return 131 * name.hashCode + 17 * libraryName.hashCode;
 | 
|    }
 | 
|  }
 | 
|  
 | 
|  class _PublicName extends Name {
 | 
| +  Reference get libraryName => null;
 | 
|    Library get library => null;
 | 
|    bool get isPrivate => false;
 | 
|  
 | 
| @@ -3322,14 +3509,18 @@ class BottomType extends DartType {
 | 
|  }
 | 
|  
 | 
|  class InterfaceType extends DartType {
 | 
| -  final Class classNode;
 | 
| +  final Reference className;
 | 
|    final List<DartType> typeArguments;
 | 
|  
 | 
|    /// The [typeArguments] list must not be modified after this call. If the
 | 
|    /// list is omitted, 'dynamic' type arguments are filled in.
 | 
|    InterfaceType(Class classNode, [List<DartType> typeArguments])
 | 
| -      : this.classNode = classNode,
 | 
| -        this.typeArguments = typeArguments ?? _defaultTypeArguments(classNode);
 | 
| +      : this.byReference(getClassReference(classNode),
 | 
| +            typeArguments ?? _defaultTypeArguments(classNode));
 | 
| +
 | 
| +  InterfaceType.byReference(this.className, this.typeArguments);
 | 
| +
 | 
| +  Class get classNode => className.asClass;
 | 
|  
 | 
|    static List<DartType> _defaultTypeArguments(Class classNode) {
 | 
|      if (classNode.typeParameters.length == 0) {
 | 
| @@ -3351,7 +3542,7 @@ class InterfaceType extends DartType {
 | 
|    bool operator ==(Object other) {
 | 
|      if (identical(this, other)) return true;
 | 
|      if (other is InterfaceType) {
 | 
| -      if (classNode != other.classNode) return false;
 | 
| +      if (className != other.className) return false;
 | 
|        if (typeArguments.length != other.typeArguments.length) return false;
 | 
|        for (int i = 0; i < typeArguments.length; ++i) {
 | 
|          if (typeArguments[i] != other.typeArguments[i]) return false;
 | 
| @@ -3363,7 +3554,7 @@ class InterfaceType extends DartType {
 | 
|    }
 | 
|  
 | 
|    int get hashCode {
 | 
| -    int hash = 0x3fffffff & classNode.hashCode;
 | 
| +    int hash = 0x3fffffff & className.hashCode;
 | 
|      for (int i = 0; i < typeArguments.length; ++i) {
 | 
|        hash = 0x3fffffff & (hash * 31 + (hash ^ typeArguments[i].hashCode));
 | 
|      }
 | 
| @@ -3551,10 +3742,15 @@ class TypeParameter extends TreeNode {
 | 
|  }
 | 
|  
 | 
|  class Supertype extends Node {
 | 
| -  final Class classNode;
 | 
| +  final Reference className;
 | 
|    final List<DartType> typeArguments;
 | 
|  
 | 
| -  Supertype(this.classNode, this.typeArguments);
 | 
| +  Supertype(Class classNode, List<DartType> typeArguments)
 | 
| +      : this.byReference(getClassReference(classNode), typeArguments);
 | 
| +
 | 
| +  Supertype.byReference(this.className, this.typeArguments);
 | 
| +
 | 
| +  Class get classNode => className.asClass;
 | 
|  
 | 
|    accept(Visitor v) => v.visitSupertype(this);
 | 
|  
 | 
| @@ -3570,7 +3766,7 @@ class Supertype extends Node {
 | 
|    bool operator ==(Object other) {
 | 
|      if (identical(this, other)) return true;
 | 
|      if (other is Supertype) {
 | 
| -      if (classNode != other.classNode) return false;
 | 
| +      if (className != other.className) return false;
 | 
|        if (typeArguments.length != other.typeArguments.length) return false;
 | 
|        for (int i = 0; i < typeArguments.length; ++i) {
 | 
|          if (typeArguments[i] != other.typeArguments[i]) return false;
 | 
| @@ -3582,7 +3778,7 @@ class Supertype extends Node {
 | 
|    }
 | 
|  
 | 
|    int get hashCode {
 | 
| -    int hash = 0x3fffffff & classNode.hashCode;
 | 
| +    int hash = 0x3fffffff & className.hashCode;
 | 
|      for (int i = 0; i < typeArguments.length; ++i) {
 | 
|        hash = 0x3fffffff & (hash * 31 + (hash ^ typeArguments[i].hashCode));
 | 
|      }
 | 
| @@ -3596,6 +3792,8 @@ class Supertype extends Node {
 | 
|  
 | 
|  /// A way to bundle up all the libraries in a program.
 | 
|  class Program extends TreeNode {
 | 
| +  final CanonicalName root = new CanonicalName.root();
 | 
| +
 | 
|    final List<Library> libraries;
 | 
|  
 | 
|    /// Map from a source file uri to a line-starts table and source code.
 | 
| @@ -3604,12 +3802,29 @@ class Program extends TreeNode {
 | 
|    final Map<String, Source> uriToSource;
 | 
|  
 | 
|    /// Reference to the main method in one of the libraries.
 | 
| -  Procedure mainMethod;
 | 
| +  Reference mainMethodName;
 | 
|  
 | 
|    Program([List<Library> libraries, Map<String, Source> uriToSource])
 | 
|        : libraries = libraries ?? <Library>[],
 | 
|          uriToSource = uriToSource ?? <String, Source>{} {
 | 
| -    setParents(libraries, this);
 | 
| +    setParents(this.libraries, this);
 | 
| +  }
 | 
| +
 | 
| +  void computeCanonicalNames() {
 | 
| +    for (var library in libraries) {
 | 
| +      root.getChildFromUri(library.importUri).bindTo(library.reference);
 | 
| +      library.computeCanonicalNames();
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  void unbindCanonicalNames() {
 | 
| +    root.unbindAll();
 | 
| +  }
 | 
| +
 | 
| +  Procedure get mainMethod => mainMethodName?.asProcedure;
 | 
| +
 | 
| +  void set mainMethod(Procedure main) {
 | 
| +    mainMethodName = getMemberReference(main);
 | 
|    }
 | 
|  
 | 
|    accept(TreeVisitor v) => v.visitProgram(this);
 | 
| @@ -3752,3 +3967,53 @@ class Source {
 | 
|  
 | 
|    Source(this.lineStarts, this.source);
 | 
|  }
 | 
| +
 | 
| +/// Returns the [Reference] object for the given member.
 | 
| +///
 | 
| +/// Returns `null` if the member is `null`.
 | 
| +Reference getMemberReference(Member member) {
 | 
| +  return member?.reference;
 | 
| +}
 | 
| +
 | 
| +/// Returns the [Reference] object for the given class.
 | 
| +///
 | 
| +/// Returns `null` if the class is `null`.
 | 
| +Reference getClassReference(Class class_) {
 | 
| +  return class_?.reference;
 | 
| +}
 | 
| +
 | 
| +/// Returns the canonical name of [member], or throws an exception if the
 | 
| +/// member has not been assigned a canonical name yet.
 | 
| +///
 | 
| +/// Returns `null` if the member is `null`.
 | 
| +CanonicalName getCanonicalNameOfMember(Member member) {
 | 
| +  if (member == null) return null;
 | 
| +  if (member.canonicalName == null) {
 | 
| +    throw '$member has no canonical name';
 | 
| +  }
 | 
| +  return member.canonicalName;
 | 
| +}
 | 
| +
 | 
| +/// Returns the canonical name of [class_], or throws an exception if the
 | 
| +/// class has not been assigned a canonical name yet.
 | 
| +///
 | 
| +/// Returns `null` if the class is `null`.
 | 
| +CanonicalName getCanonicalNameOfClass(Class class_) {
 | 
| +  if (class_ == null) return null;
 | 
| +  if (class_.canonicalName == null) {
 | 
| +    throw '$class_ has no canonical name';
 | 
| +  }
 | 
| +  return class_.canonicalName;
 | 
| +}
 | 
| +
 | 
| +/// Returns the canonical name of [library], or throws an exception if the
 | 
| +/// library has not been assigned a canonical name yet.
 | 
| +///
 | 
| +/// Returns `null` if the library is `null`.
 | 
| +CanonicalName getCanonicalNameOfLibrary(Library library) {
 | 
| +  if (library == null) return null;
 | 
| +  if (library.canonicalName == null) {
 | 
| +    throw '$library has no canonical name';
 | 
| +  }
 | 
| +  return library.canonicalName;
 | 
| +}
 | 
| 
 |