OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 library kernel.canonical_name; |
| 5 |
| 6 import 'ast.dart'; |
| 7 |
| 8 /// A string sequence that identifies a library, class, or member. |
| 9 /// |
| 10 /// Canonical names are organized in a prefix tree. Each node knows its |
| 11 /// parent, children, and the AST node it is currently bound to. |
| 12 /// |
| 13 /// The following schema specifies how the canonical name of a given object |
| 14 /// is defined: |
| 15 /// |
| 16 /// Library: |
| 17 /// URI of library |
| 18 /// |
| 19 /// Class: |
| 20 /// URI of enclosing library |
| 21 /// Name of class |
| 22 /// |
| 23 /// Constructor: |
| 24 /// Canonical name of enclosing class or library |
| 25 /// "@constructors" |
| 26 /// Qualified name |
| 27 /// |
| 28 /// Field: |
| 29 /// Canonical name of enclosing class or library |
| 30 /// "@fields" |
| 31 /// Qualified name |
| 32 /// |
| 33 /// Procedure that is not an accessor: |
| 34 /// Canonical name of enclosing class or library |
| 35 /// "@methods" |
| 36 /// Qualified name |
| 37 /// |
| 38 /// Procedure that is a getter: |
| 39 /// Canonical name of enclosing class or library |
| 40 /// "@getters" |
| 41 /// Qualified name |
| 42 /// |
| 43 /// Procedure that is a setter: |
| 44 /// Canonical name of enclosing class or library |
| 45 /// "@setters" |
| 46 /// Qualified name |
| 47 /// |
| 48 /// Qualified name: |
| 49 /// if private: URI of library |
| 50 /// Name text |
| 51 /// |
| 52 /// The "qualified name" allows a member to have a name that is private to |
| 53 /// a library other than the one containing that member. |
| 54 class CanonicalName { |
| 55 final CanonicalName parent; |
| 56 final String name; |
| 57 |
| 58 Map<String, CanonicalName> _children; |
| 59 |
| 60 /// The library, class, or member bound to this name. |
| 61 LinkedNode definition; |
| 62 |
| 63 /// Temporary index used during serialization. |
| 64 int index = -1; |
| 65 |
| 66 CanonicalName._(this.parent, this.name) { |
| 67 assert(name != null); |
| 68 } |
| 69 |
| 70 CanonicalName.root() |
| 71 : parent = null, |
| 72 name = ''; |
| 73 |
| 74 /// Used as temporary measure to get the dart2js-kernel adaptor working. |
| 75 /// |
| 76 /// This canonical name cannot be used in serialization but can hold in-memory |
| 77 /// references. |
| 78 CanonicalName.dummy() |
| 79 : parent = null, |
| 80 name = 'dummy'; |
| 81 |
| 82 bool get isRoot => parent == null; |
| 83 |
| 84 /// True if this is a fake canonical name object used by the dart2js/kernel |
| 85 /// adaptor. |
| 86 bool get isDummy => parent == null && name == 'dummy'; |
| 87 |
| 88 Iterable<CanonicalName> get children => |
| 89 _children?.values ?? const <CanonicalName>[]; |
| 90 |
| 91 CanonicalName getChild(String name) { |
| 92 var map = _children ??= <String, CanonicalName>{}; |
| 93 return map[name] ??= new CanonicalName._(this, name); |
| 94 } |
| 95 |
| 96 CanonicalName getChildFromUri(Uri uri) { |
| 97 // Note that the Uri class caches its string representation, and all library |
| 98 // URIs will be stringified for serialization anyway, so there is no |
| 99 // significant cost for converting the Uri to a string here. |
| 100 return getChild('$uri'); |
| 101 } |
| 102 |
| 103 CanonicalName getChildFromQualifiedName(Name name) { |
| 104 return name.isPrivate |
| 105 ? getChild(name.libraryName.name).getChild(name.name) |
| 106 : getChild(name.name); |
| 107 } |
| 108 |
| 109 CanonicalName getChildFromMember(Member member) { |
| 110 return getChild(getMemberQualifier(member)) |
| 111 .getChildFromQualifiedName(member.name); |
| 112 } |
| 113 |
| 114 void bindTo(LinkedNode target) { |
| 115 if (definition == target) return; |
| 116 if (definition != null) { |
| 117 throw '$this is already bound'; |
| 118 } |
| 119 if (target.canonicalName != null) { |
| 120 throw 'Cannot bind $this to $target, target is already bound'; |
| 121 } |
| 122 target.canonicalName = this; |
| 123 this.definition = target; |
| 124 } |
| 125 |
| 126 void unbind() { |
| 127 if (definition == null) return; |
| 128 assert(definition.canonicalName == this); |
| 129 definition.canonicalName = null; |
| 130 definition = null; |
| 131 } |
| 132 |
| 133 String toString() => parent == null ? 'root' : '$parent::$name'; |
| 134 |
| 135 Library get asLibrary { |
| 136 if (definition == null) throw '$this is not bound'; |
| 137 return definition as Library; |
| 138 } |
| 139 |
| 140 Class get asClass { |
| 141 if (definition == null) throw '$this is not bound'; |
| 142 return definition as Class; |
| 143 } |
| 144 |
| 145 Member get asMember { |
| 146 if (definition == null) throw '$this is not bound'; |
| 147 return definition as Member; |
| 148 } |
| 149 |
| 150 Constructor get asConstructor { |
| 151 if (definition == null) throw '$this is not bound'; |
| 152 return definition as Constructor; |
| 153 } |
| 154 |
| 155 Procedure get asProcedure { |
| 156 if (definition == null) throw '$this is not bound'; |
| 157 return definition as Procedure; |
| 158 } |
| 159 |
| 160 Field get asField { |
| 161 if (definition == null) throw '$this is not bound'; |
| 162 return definition as Field; |
| 163 } |
| 164 |
| 165 static String getMemberQualifier(Member member) { |
| 166 if (member is Procedure) { |
| 167 if (member.isGetter) return '@getters'; |
| 168 if (member.isSetter) return '@setters'; |
| 169 return '@methods'; |
| 170 } |
| 171 if (member is Field) { |
| 172 return '@fields'; |
| 173 } |
| 174 if (member is Constructor) { |
| 175 return '@constructors'; |
| 176 } |
| 177 throw 'Unexpected member: $member'; |
| 178 } |
| 179 } |
OLD | NEW |