| // 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.library_index; |
| |
| import 'ast.dart'; |
| |
| /// Provides name-based access to library, class, and member AST nodes. |
| /// |
| /// When constructed, a given set of libraries are indexed immediately, and |
| /// will not be up-to-date with changes made after it was created. |
| class LibraryIndex { |
| static const String getterPrefix = 'get:'; |
| static const String setterPrefix = 'set:'; |
| static const String tearoffPrefix = 'get#'; |
| |
| /// A special class name that can be used to access the top-level members |
| /// of a library. |
| static const String topLevel = '::'; |
| |
| final Map<String, _ClassTable> _libraries = <String, _ClassTable>{}; |
| |
| /// Indexes the libraries with the URIs given in [libraryUris]. |
| LibraryIndex(Component component, Iterable<String> libraryUris) |
| : this.fromLibraries(component.libraries, libraryUris); |
| |
| /// Indexes the libraries with the URIs given in [libraryUris]. |
| LibraryIndex.fromLibraries( |
| Iterable<Library> libraries, Iterable<String> libraryUris) { |
| Set<String> libraryUriSet = libraryUris.toSet(); |
| for (Library library in libraries) { |
| String uri = '${library.importUri}'; |
| if (libraryUriSet.contains(uri)) { |
| _libraries[uri] = new _ClassTable(library); |
| } |
| } |
| } |
| |
| /// Indexes the libraries with the URIs given in [libraryUris]. |
| LibraryIndex.byUri(Component component, Iterable<Uri> libraryUris) |
| : this(component, libraryUris.map((uri) => '$uri')); |
| |
| /// Indexes `dart:` libraries. |
| LibraryIndex.coreLibraries(Component component) { |
| for (Library library in component.libraries) { |
| if (library.importUri.isScheme('dart')) { |
| _libraries['${library.importUri}'] = new _ClassTable(library); |
| } |
| } |
| } |
| |
| /// Indexes the entire component. |
| /// |
| /// Consider using another constructor to only index the libraries that |
| /// are needed. |
| LibraryIndex.all(Component component) { |
| for (Library library in component.libraries) { |
| _libraries['${library.importUri}'] = new _ClassTable(library); |
| } |
| } |
| |
| _ClassTable _getLibraryIndex(String uri) { |
| _ClassTable? libraryIndex = _libraries[uri]; |
| if (libraryIndex == null) { |
| throw "The library '$uri' has not been indexed"; |
| } |
| return libraryIndex; |
| } |
| |
| /// Returns the library with the given URI. |
| /// |
| /// Throws an error if it does not exist. |
| Library getLibrary(String uri) => _getLibraryIndex(uri).library; |
| |
| /// Like [getLibrary] but returns `null` if not found. |
| Library? tryGetLibrary(String uri) => _libraries[uri]?.library; |
| |
| /// True if the library with the given URI exists and was indexed. |
| bool containsLibrary(String uri) => _libraries.containsKey(uri); |
| |
| /// 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); |
| } |
| |
| /// Like [getClass] but returns `null` if not found. |
| Class? tryGetClass(String library, String className) { |
| return _libraries[library]?.tryGetClass(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); |
| } |
| |
| /// Like [getMember] but returns `null` if not found. |
| Member? tryGetMember(String library, String className, String memberName) { |
| return _libraries[library]?.tryGetMember(className, memberName); |
| } |
| |
| Constructor getConstructor( |
| String library, String className, String memberName) { |
| return _getLibraryIndex(library).getConstructor(className, memberName); |
| } |
| |
| Procedure getProcedure(String library, String className, String memberName) { |
| return _getLibraryIndex(library).getProcedure(className, memberName); |
| } |
| |
| Field getField(String library, String className, String memberName) { |
| return _getLibraryIndex(library).getField(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); |
| } |
| |
| /// Like [getTopLevelMember] but returns `null` if not found. |
| Member? tryGetTopLevelMember( |
| String library, String className, String memberName) { |
| return tryGetMember(library, topLevel, memberName); |
| } |
| |
| Procedure getTopLevelProcedure(String library, String memberName) { |
| return getProcedure(library, topLevel, memberName); |
| } |
| |
| Field getTopLevelField(String library, String memberName) { |
| return getField(library, topLevel, memberName); |
| } |
| } |
| |
| class _ClassTable { |
| final Library library; |
| |
| Map<String, _MemberTable>? _classes; |
| |
| _ClassTable(this.library); |
| |
| Map<String, _MemberTable> get classes { |
| if (_classes == null) { |
| _classes = <String, _MemberTable>{}; |
| _classes![LibraryIndex.topLevel] = new _MemberTable.topLevel(this); |
| for (Class class_ in library.classes) { |
| _classes![class_.name] = new _MemberTable.fromClass(this, class_); |
| } |
| for (Extension extension_ in library.extensions) { |
| _classes![extension_.name] = |
| new _MemberTable.fromExtension(this, extension_); |
| } |
| for (Reference reference in library.additionalExports) { |
| NamedNode? node = reference.node; |
| if (node is Class) { |
| _classes![node.name] = new _MemberTable.fromClass(this, node); |
| } else if (node is Extension) { |
| _classes![node.name] = new _MemberTable.fromExtension(this, node); |
| } |
| } |
| } |
| return _classes!; |
| } |
| |
| String get containerName { |
| return "library '${library.importUri}'"; |
| } |
| |
| _MemberTable _getClassIndex(String name) { |
| _MemberTable? indexer = classes[name]; |
| if (indexer == null) { |
| throw "Class '$name' not found in $containerName"; |
| } |
| return indexer; |
| } |
| |
| Class getClass(String name) { |
| return _getClassIndex(name).class_!; |
| } |
| |
| Class? tryGetClass(String name) { |
| return classes[name]?.class_; |
| } |
| |
| Member getMember(String className, String memberName) { |
| return _getClassIndex(className).getMember(memberName); |
| } |
| |
| Member? tryGetMember(String className, String memberName) { |
| return classes[className]?.tryGetMember(memberName); |
| } |
| |
| Constructor getConstructor(String className, String memberName) { |
| return _getClassIndex(className).getConstructor(memberName); |
| } |
| |
| Procedure getProcedure(String className, String memberName) { |
| return _getClassIndex(className).getProcedure(memberName); |
| } |
| |
| Field getField(String className, String memberName) { |
| return _getClassIndex(className).getField(memberName); |
| } |
| } |
| |
| class _MemberTable { |
| final _ClassTable parent; |
| final Class? class_; // Null for top-level or extension. |
| final Extension? extension_; // Null for top-level or class. |
| Map<String, Member>? _members; |
| |
| Library get library => parent.library; |
| |
| _MemberTable.fromClass(this.parent, this.class_) : extension_ = null; |
| _MemberTable.fromExtension(this.parent, this.extension_) : class_ = null; |
| _MemberTable.topLevel(this.parent) |
| : class_ = null, |
| extension_ = null; |
| |
| Map<String, Member> get members { |
| if (_members == null) { |
| _members = <String, Member>{}; |
| if (class_ != null) { |
| class_!.procedures.forEach(_addMember); |
| class_!.fields.forEach(_addMember); |
| class_!.constructors.forEach(_addMember); |
| } else if (extension_ != null) { |
| extension_!.members.forEach(_addExtensionMember); |
| } else { |
| library.procedures.forEach(_addMember); |
| library.fields.forEach(_addMember); |
| } |
| } |
| return _members!; |
| } |
| |
| String getDisambiguatedName(Member member) { |
| if (member is Procedure) { |
| if (member.isGetter) return LibraryIndex.getterPrefix + member.name.text; |
| if (member.isSetter) return LibraryIndex.setterPrefix + member.name.text; |
| } |
| return member.name.text; |
| } |
| |
| 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 LibraryIndex class. |
| return; |
| } |
| _members![getDisambiguatedName(member)] = member; |
| } |
| |
| String getDisambiguatedExtensionName( |
| ExtensionMemberDescriptor extensionMember) { |
| if (extensionMember.kind == ExtensionMemberKind.TearOff) { |
| return LibraryIndex.tearoffPrefix + extensionMember.name.text; |
| } |
| if (extensionMember.kind == ExtensionMemberKind.Getter) { |
| return LibraryIndex.getterPrefix + extensionMember.name.text; |
| } |
| if (extensionMember.kind == ExtensionMemberKind.Setter) { |
| return LibraryIndex.setterPrefix + extensionMember.name.text; |
| } |
| return extensionMember.name.text; |
| } |
| |
| void _addExtensionMember(ExtensionMemberDescriptor extensionMember) { |
| final NamedNode? replacement = extensionMember.member.node; |
| if (replacement is! Member) return; |
| Member member = replacement; |
| if (member.name.isPrivate && member.name.library != library) { |
| // Members whose name is private to other libraries cannot currently |
| // be found with the LibraryIndex class. |
| return; |
| } |
| |
| final String name = getDisambiguatedExtensionName(extensionMember); |
| _members![name] = replacement; |
| } |
| |
| String get containerName { |
| if (class_ != null) { |
| return "class '${class_!.name}' in ${parent.containerName}"; |
| } else if (extension_ != null) { |
| return "extension '${extension_!.name}' in ${parent.containerName}"; |
| } else { |
| return "top-level of ${parent.containerName}"; |
| } |
| } |
| |
| Member getMember(String name) { |
| Member? member = members[name]; |
| if (member == null) { |
| String message = "A member with disambiguated name '$name' was not found " |
| "in $containerName"; |
| String getter = LibraryIndex.getterPrefix + name; |
| String setter = LibraryIndex.setterPrefix + name; |
| if (members[getter] != null || members[setter] != null) { |
| throw "$message. Did you mean '$getter' or '$setter'?"; |
| } |
| throw message; |
| } |
| return member; |
| } |
| |
| Member? tryGetMember(String name) => members[name]; |
| |
| Constructor getConstructor(String name) { |
| Member member = getMember(name); |
| if (member is! Constructor) { |
| throw "Member '$name' in $containerName is not a Constructor: " |
| "${member} (${member.runtimeType})."; |
| } |
| return member; |
| } |
| |
| Procedure getProcedure(String name) { |
| Member member = getMember(name); |
| if (member is! Procedure) { |
| throw "Member '$name' in $containerName is not a Procedure: " |
| "${member} (${member.runtimeType})."; |
| } |
| return member; |
| } |
| |
| Field getField(String name) { |
| Member member = getMember(name); |
| if (member is! Field) { |
| throw "Member '$name' in $containerName is not a Field: " |
| "${member} (${member.runtimeType})."; |
| } |
| return member; |
| } |
| } |