| // 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 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_!.memberDescriptors.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, | 
 |       {required bool forTearOff}) { | 
 |     if (forTearOff) { | 
 |       return LibraryIndex.tearoffPrefix + extensionMember.name.text; | 
 |     } | 
 |     switch (extensionMember.kind) { | 
 |       case ExtensionMemberKind.Getter: | 
 |         return LibraryIndex.getterPrefix + extensionMember.name.text; | 
 |       case ExtensionMemberKind.Setter: | 
 |         return LibraryIndex.setterPrefix + extensionMember.name.text; | 
 |       case ExtensionMemberKind.Field: | 
 |       case ExtensionMemberKind.Method: | 
 |       case ExtensionMemberKind.Operator: | 
 |         return extensionMember.name.text; | 
 |     } | 
 |   } | 
 |  | 
 |   void _addExtensionMember(ExtensionMemberDescriptor extensionMember) { | 
 |     void addReference(Reference? reference, {required bool forTearOff}) { | 
 |       final NamedNode? replacement = reference?.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, | 
 |           forTearOff: forTearOff); | 
 |       _members![name] = replacement; | 
 |     } | 
 |  | 
 |     addReference(extensionMember.memberReference, forTearOff: false); | 
 |     addReference(extensionMember.tearOffReference, forTearOff: true); | 
 |   } | 
 |  | 
 |   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: ${members.keys}"; | 
 |       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; | 
 |   } | 
 | } |