Index: pkg/kernel/lib/lookup_table.dart |
diff --git a/pkg/kernel/lib/lookup_table.dart b/pkg/kernel/lib/lookup_table.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..bef64d03b4fa7cdbf7fa40e8ba85b4c447254d0c |
--- /dev/null |
+++ b/pkg/kernel/lib/lookup_table.dart |
@@ -0,0 +1,203 @@ |
+// Copyright (c) 2016, 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 kernel.lookup_table; |
+ |
+import 'ast.dart'; |
+ |
+/// Provides name-based access to library, class, and member AST nodes. |
+/// |
+/// When constructed, the lookup table indexes a given set of libraries, and |
+/// will not be up-to-date with changes made after it was created. |
+class LookupTable { |
+ static const String getterPrefix = 'get:'; |
+ static const String setterPrefix = 'set:'; |
+ |
+ /// A name that can be used as a class name to access the top-level members |
+ /// of a library. |
+ static const String topLevel = '::'; |
+ |
+ final Map<String, _LibraryIndex> _libraries = <String, _LibraryIndex>{}; |
+ |
+ /// Indexes the libraries with the URIs given in [libraryUris]. |
+ LookupTable(Program program, Iterable<String> libraryUris) { |
+ for (var uri in libraryUris) { |
+ _libraries[uri] = new _LibraryIndex(); |
+ } |
+ for (var library in program.libraries) { |
+ var index = _libraries['${library.importUri}']; |
+ if (index != null) { |
+ index.build(library); |
+ } |
+ } |
+ } |
+ |
+ /// Indexes the libraries with the URIs given in [libraryUris]. |
+ LookupTable.byUri(Program program, Iterable<Uri> libraryUris) |
+ : this(program, libraryUris.map((uri) => '$uri')); |
+ |
+ /// Indexes the libraries with the URIs given in [libraryUris]. |
+ LookupTable.coreLibraries(Program program) { |
+ for (var library in program.libraries) { |
+ if (library.importUri.scheme == 'dart') { |
+ _libraries['${library.importUri}'] = new _LibraryIndex() |
+ ..build(library); |
+ } |
+ } |
+ } |
+ |
+ /// Indexes the entire program. |
+ /// |
+ /// Consider using another constructor to only index the libraries that |
+ /// are needed. |
+ LookupTable.all(Program program) { |
+ for (var library in program.libraries) { |
+ _libraries['${library.importUri}'] = new _LibraryIndex()..build(library); |
+ } |
+ } |
+ |
+ _LibraryIndex _getLibraryIndex(String uri) { |
+ _LibraryIndex libraryIndex = _libraries[uri]; |
+ if (libraryIndex == null) { |
+ throw "The library '$uri' has not been indexed"; |
+ } |
+ return libraryIndex; |
+ } |
+ |
+ Library getLibrary(String uri) => _getLibraryIndex(uri).library; |
+ |
+ /// Returns the class with the given name in the given library. |
+ /// |
+ /// An error is thrown if the class is not found. |
+ Class getClass(String library, String className) { |
+ return _getLibraryIndex(library).getClass(className); |
+ } |
+ |
+ /// Returns the member with the given name, in the given class, in the |
+ /// given library. |
+ /// |
+ /// If a getter or setter is wanted, the `get:` or `set:` prefix must be |
+ /// added in front of the member name. |
+ /// |
+ /// The special class name `::` can be used to access top-level members. |
+ /// |
+ /// If the member name is private it is considered private to [library]. |
+ /// It is not possible with this class to lookup members whose name is private |
+ /// to a library other than the one containing it. |
+ /// |
+ /// An error is thrown if the member is not found. |
+ Member getMember(String library, String className, String memberName) { |
+ return _getLibraryIndex(library).getMember(className, memberName); |
+ } |
+ |
+ /// Returns the top-level member with the given name, in the given library. |
+ /// |
+ /// If a getter or setter is wanted, the `get:` or `set:` prefix must be |
+ /// added in front of the member name. |
+ /// |
+ /// If the member name is private it is considered private to [library]. |
+ /// It is not possible with this class to lookup members whose name is private |
+ /// to a library other than the one containing it. |
+ /// |
+ /// An error is thrown if the member is not found. |
+ Member getTopLevelMember(String library, String memberName) { |
+ return getMember(library, topLevel, memberName); |
+ } |
+} |
+ |
+class _LibraryIndex { |
+ Library library; |
+ final Map<String, _ClassIndex> classes = <String, _ClassIndex>{}; |
+ |
+ void build(Library library) { |
+ this.library = library; |
+ classes[LookupTable.topLevel] = new _ClassIndex.topLevel(this); |
+ for (var class_ in library.classes) { |
+ classes[class_.name] = new _ClassIndex(this, class_); |
+ } |
+ } |
+ |
+ String get containerName { |
+ // It can be helpful to indicate if the library is external, since then |
+ // the class might be in the library, but just not seen from this build |
+ // unit. |
+ return library.isExternal |
+ ? "external library '${library.importUri}'" |
+ : "library '${library.importUri}'"; |
+ } |
+ |
+ _ClassIndex _getClassIndex(String name) { |
+ var indexer = classes[name]; |
+ if (indexer == null) { |
+ throw "Class '$name' not found in $containerName"; |
+ } |
+ return indexer; |
+ } |
+ |
+ Class getClass(String name) { |
+ return _getClassIndex(name).class_; |
+ } |
+ |
+ Member getMember(String className, String memberName) { |
+ return _getClassIndex(className).getMember(memberName); |
+ } |
+} |
+ |
+class _ClassIndex { |
+ final _LibraryIndex parent; |
+ final Class class_; // Null for top-level. |
+ final Map<String, Member> members = <String, Member>{}; |
+ |
+ Library get library => parent.library; |
+ |
+ _ClassIndex(this.parent, this.class_) { |
+ class_.procedures.forEach(addMember); |
+ class_.fields.forEach(addMember); |
+ class_.constructors.forEach(addMember); |
+ } |
+ |
+ _ClassIndex.topLevel(this.parent) : class_ = null { |
+ library.procedures.forEach(addMember); |
+ library.fields.forEach(addMember); |
+ } |
+ |
+ String getDisambiguatedName(Member member) { |
+ if (member is Procedure) { |
+ if (member.isGetter) return LookupTable.getterPrefix + member.name.name; |
+ if (member.isSetter) return LookupTable.setterPrefix + member.name.name; |
+ } |
+ return member.name.name; |
+ } |
+ |
+ void addMember(Member member) { |
+ if (member.name.isPrivate && member.name.library != library) { |
+ // Members whose name is private to other libraries cannot currently |
+ // be found with the LookupTable. |
+ return; |
+ } |
+ members[getDisambiguatedName(member)] = member; |
+ } |
+ |
+ String get containerName { |
+ if (class_ == null) { |
+ return "top-level of ${parent.containerName}"; |
+ } else { |
+ return "class '${class_.name}' in ${parent.containerName}"; |
+ } |
+ } |
+ |
+ Member getMember(String name) { |
+ var member = members[name]; |
+ if (member == null) { |
+ String message = "A member with disambiguated name '$name' was not found " |
+ "in $containerName"; |
+ var getter = LookupTable.getterPrefix + name; |
+ var setter = LookupTable.setterPrefix + name; |
+ if (members[getter] != null || members[setter] != null) { |
+ throw "$message. Did you mean '$getter' or '$setter'?"; |
+ } |
+ throw message; |
+ } |
+ return member; |
+ } |
+} |