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.lookup_table; |
| 5 |
| 6 import 'ast.dart'; |
| 7 |
| 8 /// Provides name-based access to library, class, and member AST nodes. |
| 9 /// |
| 10 /// When constructed, the lookup table indexes a given set of libraries, and |
| 11 /// will not be up-to-date with changes made after it was created. |
| 12 class LookupTable { |
| 13 static const String getterPrefix = 'get:'; |
| 14 static const String setterPrefix = 'set:'; |
| 15 |
| 16 /// A name that can be used as a class name to access the top-level members |
| 17 /// of a library. |
| 18 static const String topLevel = '::'; |
| 19 |
| 20 final Map<String, _LibraryIndex> _libraries = <String, _LibraryIndex>{}; |
| 21 |
| 22 /// Indexes the libraries with the URIs given in [libraryUris]. |
| 23 LookupTable(Program program, Iterable<String> libraryUris) { |
| 24 for (var uri in libraryUris) { |
| 25 _libraries[uri] = new _LibraryIndex(); |
| 26 } |
| 27 for (var library in program.libraries) { |
| 28 var index = _libraries['${library.importUri}']; |
| 29 if (index != null) { |
| 30 index.build(library); |
| 31 } |
| 32 } |
| 33 } |
| 34 |
| 35 /// Indexes the libraries with the URIs given in [libraryUris]. |
| 36 LookupTable.byUri(Program program, Iterable<Uri> libraryUris) |
| 37 : this(program, libraryUris.map((uri) => '$uri')); |
| 38 |
| 39 /// Indexes the libraries with the URIs given in [libraryUris]. |
| 40 LookupTable.coreLibraries(Program program) { |
| 41 for (var library in program.libraries) { |
| 42 if (library.importUri.scheme == 'dart') { |
| 43 _libraries['${library.importUri}'] = new _LibraryIndex() |
| 44 ..build(library); |
| 45 } |
| 46 } |
| 47 } |
| 48 |
| 49 /// Indexes the entire program. |
| 50 /// |
| 51 /// Consider using another constructor to only index the libraries that |
| 52 /// are needed. |
| 53 LookupTable.all(Program program) { |
| 54 for (var library in program.libraries) { |
| 55 _libraries['${library.importUri}'] = new _LibraryIndex()..build(library); |
| 56 } |
| 57 } |
| 58 |
| 59 _LibraryIndex _getLibraryIndex(String uri) { |
| 60 _LibraryIndex libraryIndex = _libraries[uri]; |
| 61 if (libraryIndex == null) { |
| 62 throw "The library '$uri' has not been indexed"; |
| 63 } |
| 64 return libraryIndex; |
| 65 } |
| 66 |
| 67 Library getLibrary(String uri) => _getLibraryIndex(uri).library; |
| 68 |
| 69 /// Returns the class with the given name in the given library. |
| 70 /// |
| 71 /// An error is thrown if the class is not found. |
| 72 Class getClass(String library, String className) { |
| 73 return _getLibraryIndex(library).getClass(className); |
| 74 } |
| 75 |
| 76 /// Returns the member with the given name, in the given class, in the |
| 77 /// given library. |
| 78 /// |
| 79 /// If a getter or setter is wanted, the `get:` or `set:` prefix must be |
| 80 /// added in front of the member name. |
| 81 /// |
| 82 /// The special class name `::` can be used to access top-level members. |
| 83 /// |
| 84 /// If the member name is private it is considered private to [library]. |
| 85 /// It is not possible with this class to lookup members whose name is private |
| 86 /// to a library other than the one containing it. |
| 87 /// |
| 88 /// An error is thrown if the member is not found. |
| 89 Member getMember(String library, String className, String memberName) { |
| 90 return _getLibraryIndex(library).getMember(className, memberName); |
| 91 } |
| 92 |
| 93 /// Returns the top-level member with the given name, in the given library. |
| 94 /// |
| 95 /// If a getter or setter is wanted, the `get:` or `set:` prefix must be |
| 96 /// added in front of the member name. |
| 97 /// |
| 98 /// If the member name is private it is considered private to [library]. |
| 99 /// It is not possible with this class to lookup members whose name is private |
| 100 /// to a library other than the one containing it. |
| 101 /// |
| 102 /// An error is thrown if the member is not found. |
| 103 Member getTopLevelMember(String library, String memberName) { |
| 104 return getMember(library, topLevel, memberName); |
| 105 } |
| 106 } |
| 107 |
| 108 class _LibraryIndex { |
| 109 Library library; |
| 110 final Map<String, _ClassIndex> classes = <String, _ClassIndex>{}; |
| 111 |
| 112 void build(Library library) { |
| 113 this.library = library; |
| 114 classes[LookupTable.topLevel] = new _ClassIndex.topLevel(this); |
| 115 for (var class_ in library.classes) { |
| 116 classes[class_.name] = new _ClassIndex(this, class_); |
| 117 } |
| 118 } |
| 119 |
| 120 String get containerName { |
| 121 // It can be helpful to indicate if the library is external, since then |
| 122 // the class might be in the library, but just not seen from this build |
| 123 // unit. |
| 124 return library.isExternal |
| 125 ? "external library '${library.importUri}'" |
| 126 : "library '${library.importUri}'"; |
| 127 } |
| 128 |
| 129 _ClassIndex _getClassIndex(String name) { |
| 130 var indexer = classes[name]; |
| 131 if (indexer == null) { |
| 132 throw "Class '$name' not found in $containerName"; |
| 133 } |
| 134 return indexer; |
| 135 } |
| 136 |
| 137 Class getClass(String name) { |
| 138 return _getClassIndex(name).class_; |
| 139 } |
| 140 |
| 141 Member getMember(String className, String memberName) { |
| 142 return _getClassIndex(className).getMember(memberName); |
| 143 } |
| 144 } |
| 145 |
| 146 class _ClassIndex { |
| 147 final _LibraryIndex parent; |
| 148 final Class class_; // Null for top-level. |
| 149 final Map<String, Member> members = <String, Member>{}; |
| 150 |
| 151 Library get library => parent.library; |
| 152 |
| 153 _ClassIndex(this.parent, this.class_) { |
| 154 class_.procedures.forEach(addMember); |
| 155 class_.fields.forEach(addMember); |
| 156 class_.constructors.forEach(addMember); |
| 157 } |
| 158 |
| 159 _ClassIndex.topLevel(this.parent) : class_ = null { |
| 160 library.procedures.forEach(addMember); |
| 161 library.fields.forEach(addMember); |
| 162 } |
| 163 |
| 164 String getDisambiguatedName(Member member) { |
| 165 if (member is Procedure) { |
| 166 if (member.isGetter) return LookupTable.getterPrefix + member.name.name; |
| 167 if (member.isSetter) return LookupTable.setterPrefix + member.name.name; |
| 168 } |
| 169 return member.name.name; |
| 170 } |
| 171 |
| 172 void addMember(Member member) { |
| 173 if (member.name.isPrivate && member.name.library != library) { |
| 174 // Members whose name is private to other libraries cannot currently |
| 175 // be found with the LookupTable. |
| 176 return; |
| 177 } |
| 178 members[getDisambiguatedName(member)] = member; |
| 179 } |
| 180 |
| 181 String get containerName { |
| 182 if (class_ == null) { |
| 183 return "top-level of ${parent.containerName}"; |
| 184 } else { |
| 185 return "class '${class_.name}' in ${parent.containerName}"; |
| 186 } |
| 187 } |
| 188 |
| 189 Member getMember(String name) { |
| 190 var member = members[name]; |
| 191 if (member == null) { |
| 192 String message = "A member with disambiguated name '$name' was not found " |
| 193 "in $containerName"; |
| 194 var getter = LookupTable.getterPrefix + name; |
| 195 var setter = LookupTable.setterPrefix + name; |
| 196 if (members[getter] != null || members[setter] != null) { |
| 197 throw "$message. Did you mean '$getter' or '$setter'?"; |
| 198 } |
| 199 throw message; |
| 200 } |
| 201 return member; |
| 202 } |
| 203 } |
OLD | NEW |