//===--- ModuleFile.h - Info about a loaded serialized module ---*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #ifndef SWIFT_SERIALIZATION_MODULEFILE_H #define SWIFT_SERIALIZATION_MODULEFILE_H #include "swift/AST/Identifier.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/LinkLibrary.h" #include "swift/AST/Module.h" #include "swift/AST/RawComment.h" #include "swift/AST/TypeLoc.h" #include "swift/Serialization/ModuleFormat.h" #include "swift/Serialization/Validation.h" #include "swift/Basic/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Bitcode/BitstreamReader.h" #include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" namespace llvm { class BitstreamCursor; class BitstreamReader; class MemoryBuffer; template class OnDiskIterableChainedHashTable; } namespace swift { class Decl; class FileUnit; class ModuleDecl; class Pattern; class ProtocolConformance; /// A serialized module, along with the tools to access it. class ModuleFile : public LazyMemberLoader, public LazyConformanceLoader { friend class SerializedASTFile; friend class SILDeserializer; using Status = serialization::Status; /// A reference back to the AST representation of the file. FileUnit *FileContext = nullptr; /// The module shadowed by this module, if any. ModuleDecl *ShadowedModule = nullptr; /// The module file data. std::unique_ptr ModuleInputBuffer; std::unique_ptr ModuleDocInputBuffer; /// The cursor used to lazily load things from the file. llvm::BitstreamCursor DeclTypeCursor; llvm::BitstreamCursor SILCursor; llvm::BitstreamCursor SILIndexCursor; /// The name of the module. StringRef Name; friend StringRef getNameOfModule(const ModuleFile *); /// The target the module was built for. StringRef TargetTriple; /// The Swift compatibility version in use when this module was built. version::Version CompatibilityVersion; /// The data blob containing all of the module's identifiers. StringRef IdentifierData; /// A callback to be invoked every time a type was deserialized. std::function DeserializedTypeCallback; /// The number of entities that are currently being deserialized. unsigned NumCurrentDeserializingEntities = 0; /// RAII class to be used when deserializing an entity. class DeserializingEntityRAII { ModuleFile &MF; public: DeserializingEntityRAII(ModuleFile &mf) : MF(mf.getModuleFileForDelayedActions()) { ++MF.NumCurrentDeserializingEntities; } ~DeserializingEntityRAII() { assert(MF.NumCurrentDeserializingEntities > 0 && "Imbalanced currently-deserializing count?"); if (MF.NumCurrentDeserializingEntities == 1) { MF.finishPendingActions(); } --MF.NumCurrentDeserializingEntities; } }; friend class DeserializingEntityRAII; /// Picks a specific ModuleFile instance to serve as the "delayer" for the /// entire module. /// /// This is usually \c this, but when there are partial swiftmodules all /// loaded for the same module it may be different. ModuleFile &getModuleFileForDelayedActions(); /// Finish any pending actions that were waiting for the topmost entity to /// be deserialized. void finishPendingActions(); public: /// Represents another module that has been imported as a dependency. class Dependency { public: ModuleDecl::ImportedModule Import = {}; const StringRef RawPath; private: unsigned IsExported : 1; const unsigned IsHeader : 1; const unsigned IsScoped : 1; Dependency(StringRef path, bool isHeader, bool exported, bool isScoped) : RawPath(path), IsExported(exported), IsHeader(isHeader), IsScoped(isScoped) {} public: Dependency(StringRef path, bool exported, bool isScoped) : Dependency(path, false, exported, isScoped) {} static Dependency forHeader(StringRef headerPath, bool exported) { return Dependency(headerPath, true, exported, false); } bool isLoaded() const { return Import.second != nullptr; } bool isExported() const { return IsExported; } bool isHeader() const { return IsHeader; } bool isScoped() const { return IsScoped; } void forceExported() { IsExported = true; } std::string getPrettyPrintedPath() const; }; private: /// All modules this module depends on. SmallVector Dependencies; struct SearchPath { StringRef Path; bool IsFramework; bool IsSystem; }; /// Search paths this module may provide. /// /// This is not intended for use by frameworks, but may show up in debug /// modules. std::vector SearchPaths; /// Info for the (lone) imported header for this module. struct { off_t fileSize; time_t fileModTime; StringRef contents; } importedHeaderInfo = {}; /// All of this module's link-time dependencies. SmallVector LinkLibraries; public: template class Serialized { private: using RawBitOffset = uint64_t; using ImplTy = PointerUnion; ImplTy Value; public: /*implicit*/ Serialized(serialization::BitOffset offset) : Value(offset) {} bool isComplete() const { return Value.template is(); } T get() const { return Value.template get(); } /*implicit*/ operator T() const { return get(); } /*implicit*/ operator serialization::BitOffset() const { return Value.template get(); } /*implicit*/ operator RawBitOffset() const { return Value.template get(); } template Serialized &operator=(Derived deserialized) { assert(!isComplete() || ImplTy(deserialized) == Value); Value = deserialized; return *this; } void unsafeOverwrite(T t) { Value = t; } }; /// A class for holding a value that can be partially deserialized. /// /// This class assumes that "T()" is not a valid deserialized value. template class PartiallySerialized { private: using RawBitOffset = decltype(DeclTypeCursor.GetCurrentBitNo()); /// The deserialized value. T Value; /// The offset. serialization::BitOffset Offset; unsigned IsFullyDeserialized : 1; public: /*implicit*/ PartiallySerialized(serialization::BitOffset offset) : Value(), Offset(offset), IsFullyDeserialized(0) {} /*implicit*/ PartiallySerialized(RawBitOffset offset) : Value(), Offset(static_cast(offset)), IsFullyDeserialized(0) { assert(Offset == offset && "offset is too large"); } bool isDeserialized() const { return Value != T(); } bool isFullyDeserialized() const { return isDeserialized() && IsFullyDeserialized; } serialization::BitOffset getOffset() const { assert(!isFullyDeserialized()); return Offset; } T get() const { assert(isDeserialized()); return Value; } void reset() { IsFullyDeserialized = 0; Value = T(); } void set(T value, bool isFullyDeserialized) { assert(!isDeserialized() || Value == value); Value = value; IsFullyDeserialized = isFullyDeserialized; } }; private: /// Decls referenced by this module. std::vector> Decls; /// DeclContexts referenced by this module. std::vector> DeclContexts; /// Local DeclContexts referenced by this module. std::vector> LocalDeclContexts; /// Normal protocol conformances referenced by this module. std::vector> NormalConformances; /// SILLayouts referenced by this module. std::vector> SILLayouts; /// Types referenced by this module. std::vector> Types; /// Generic environments referenced by this module. std::vector> GenericEnvironments; /// Represents an identifier that may or may not have been deserialized yet. /// /// If \c Offset is non-zero, the identifier has not been loaded yet. class SerializedIdentifier { public: Identifier Ident; serialization::BitOffset Offset; template /*implicit*/ SerializedIdentifier(IntTy rawOffset) : Offset(static_cast(rawOffset)) { assert(Offset == rawOffset && "not enough bits"); } }; /// Identifiers referenced by this module. std::vector Identifiers; class DeclTableInfo; using SerializedDeclTable = llvm::OnDiskIterableChainedHashTable; class ExtensionTableInfo; using SerializedExtensionTable = llvm::OnDiskIterableChainedHashTable; class LocalDeclTableInfo; using SerializedLocalDeclTable = llvm::OnDiskIterableChainedHashTable; class NestedTypeDeclsTableInfo; using SerializedNestedTypeDeclsTable = llvm::OnDiskIterableChainedHashTable; std::unique_ptr TopLevelDecls; std::unique_ptr OperatorDecls; std::unique_ptr PrecedenceGroupDecls; std::unique_ptr ClassMembersByName; std::unique_ptr OperatorMethodDecls; std::unique_ptr ExtensionDecls; std::unique_ptr LocalTypeDecls; std::unique_ptr NestedTypeDecls; class ObjCMethodTableInfo; using SerializedObjCMethodTable = llvm::OnDiskIterableChainedHashTable; std::unique_ptr ObjCMethods; llvm::DenseMap PrivateDiscriminatorsByValue; TinyPtrVector ImportDecls; using DeclIDVector = SmallVector; DeclIDVector EagerDeserializationDecls; class DeclCommentTableInfo; using SerializedDeclCommentTable = llvm::OnDiskIterableChainedHashTable; using GroupNameTable = llvm::DenseMap; std::unique_ptr GroupNamesMap; std::unique_ptr DeclCommentTable; struct ModuleBits { /// The decl ID of the main class in this module file, if it has one. unsigned EntryPointDeclID : 31; /// Whether or not this module file comes from a context that had a main /// entry point. unsigned HasEntryPoint : 1; /// Whether this module file comes from a framework. unsigned IsFramework : 1; /// THIS SETTING IS OBSOLETE BUT IS STILL USED BY OLDER MODULES. /// /// Whether this module has a shadowed module that's part of its public /// interface. unsigned HasUnderlyingModule : 1; /// Whether or not ImportDecls is valid. unsigned ComputedImportDecls : 1; /// Whether this module file can be used, and what's wrong if not. unsigned Status : 4; // Explicitly pad out to the next word boundary. unsigned : 0; } Bits = {}; static_assert(sizeof(ModuleBits) <= 8, "The bit set should be small"); void setStatus(Status status) { Bits.Status = static_cast(status); assert(status == getStatus() && "not enough bits for status"); } void setEntryPointClassID(serialization::DeclID DID) { Bits.HasEntryPoint = true; Bits.EntryPointDeclID = DID; assert(Bits.EntryPointDeclID == DID && "not enough bits for DeclID"); } /// Creates a new AST node to represent a deserialized decl. template T *createDecl(Args &&... args); /// Constructs a new module and validates it. ModuleFile(std::unique_ptr moduleInputBuffer, std::unique_ptr moduleDocInputBuffer, bool isFramework, serialization::ValidationInfo &info, serialization::ExtendedValidationInfo *extInfo); public: /// Change the status of the current module. Default argument marks the module /// as being malformed. Status error(Status issue = Status::Malformed) { assert(issue != Status::Valid); if (FileContext && issue == Status::Malformed) { // This would normally be an assertion but it's more useful to print the // PrettyStackTrace here even in no-asserts builds. Malformed modules are // generally unrecoverable. fatal(llvm::make_error( "(see \"While...\" info below)", llvm::inconvertibleErrorCode())); } setStatus(issue); return getStatus(); } /// Emits one last diagnostic, logs the error, and then aborts for the stack /// trace. LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error error); ASTContext &getContext() const { assert(FileContext && "no associated context yet"); return FileContext->getParentModule()->getASTContext(); } ModuleDecl *getAssociatedModule() const { assert(FileContext && "no associated context yet"); return FileContext->getParentModule(); } FileUnit *getFile() const { assert(FileContext && "no associated context yet"); return FileContext; } private: /// Read an on-disk decl hash table stored in index_block::DeclListLayout /// format. std::unique_ptr readDeclTable(ArrayRef fields, StringRef blobData); /// Read an on-disk local decl hash table stored in /// index_block::DeclListLayout format. std::unique_ptr readLocalDeclTable(ArrayRef fields, StringRef blobData); /// Read an on-disk Objective-C method table stored in /// index_block::ObjCMethodTableLayout format. std::unique_ptr readObjCMethodTable(ArrayRef fields, StringRef blobData); /// Read an on-disk local decl hash table stored in /// index_block::ExtensionTableLayout format. std::unique_ptr readExtensionTable(ArrayRef fields, StringRef blobData); /// Read an on-disk local decl hash table stored in /// index_block::NestedTypeDeclsLayout format. std::unique_ptr readNestedTypeDeclsTable(ArrayRef fields, StringRef blobData); /// Reads the index block, which contains global tables. /// /// Returns false if there was an error. bool readIndexBlock(llvm::BitstreamCursor &cursor); /// Read an on-disk decl hash table stored in /// \c comment_block::DeclCommentListLayout format. std::unique_ptr readDeclCommentTable(ArrayRef fields, StringRef blobData); std::unique_ptr readGroupTable(ArrayRef fields, StringRef blobData); /// Reads the comment block, which contains USR to comment mappings. /// /// Returns false if there was an error. bool readCommentBlock(llvm::BitstreamCursor &cursor); /// Recursively reads a pattern from \c DeclTypeCursor. llvm::Expected readPattern(DeclContext *owningDC); ParameterList *readParameterList(); /// Reads a generic param list from \c DeclTypeCursor. /// /// If the record at the cursor is not a generic param list, returns null /// without moving the cursor. GenericParamList *maybeReadGenericParams(DeclContext *DC, GenericParamList *outerParams = nullptr); /// Reads a set of requirements from \c DeclTypeCursor. void readGenericRequirements(SmallVectorImpl &requirements, llvm::BitstreamCursor &Cursor); /// Set up a (potentially lazy) generic environment for the given type, /// function or extension. void configureGenericEnvironment( GenericContext *genericDecl, serialization::GenericEnvironmentID envID); /// Populates the protocol's default witness table. /// /// Returns true if there is an error. /// /// Note: this destroys the cursor's position in the stream. Furthermore, /// because it reads from the cursor, it is not possible to reset the cursor /// after reading. Nothing should ever follow a DEFAULT_WITNESS_TABLE record. bool readDefaultWitnessTable(ProtocolDecl *proto); /// Resolves a cross-reference, starting from the given module. /// /// Note: this destroys the cursor's position in the stream. Furthermore, /// because it reads from the cursor, it is not possible to reset the cursor /// after reading. Nothing should ever follow an XREF record except /// XREF_PATH_PIECE records. llvm::Expected resolveCrossReference(ModuleDecl *M, uint32_t pathLen); /// Populates TopLevelIDs for name lookup. void buildTopLevelDeclMap(); /// Sets the accessors for \p storage based on \p rawStorageKind. void configureStorage(AbstractStorageDecl *storage, unsigned rawStorageKind, serialization::DeclID getter, serialization::DeclID setter, serialization::DeclID materializeForSet, serialization::DeclID addressor, serialization::DeclID mutableAddressor, serialization::DeclID willSet, serialization::DeclID didSet); public: /// Loads a module from the given memory buffer. /// /// \param moduleInputBuffer A memory buffer containing the serialized module /// data. The created ModuleFile takes ownership of the buffer, even if /// there's an error in loading. /// \param moduleDocInputBuffer An optional memory buffer containing /// documentation data for the module. The created ModuleFile takes ownership /// of the buffer, even if there's an error in loading. /// \param isFramework If true, this is treated as a framework module for /// linking purposes. /// \param[out] theModule The loaded module. /// \param[out] extInfo Optionally, extra info serialized about the module. /// \returns Whether the module was successfully loaded, or what went wrong /// if it was not. static serialization::ValidationInfo load(std::unique_ptr moduleInputBuffer, std::unique_ptr moduleDocInputBuffer, bool isFramework, std::unique_ptr &theModule, serialization::ExtendedValidationInfo *extInfo = nullptr) { serialization::ValidationInfo info; theModule.reset(new ModuleFile(std::move(moduleInputBuffer), std::move(moduleDocInputBuffer), isFramework, info, extInfo)); return info; } // Out of line to avoid instantiation OnDiskChainedHashTable here. ~ModuleFile(); /// Associates this module file with an AST module. /// /// Returns any error that occurred during association, including validation /// that the module file is compatible with the module it's being loaded as. Status associateWithFileContext(FileUnit *file, SourceLoc diagLoc); /// Checks whether this module can be used. Status getStatus() const { return static_cast(Bits.Status); } /// Returns the list of modules this module depends on. ArrayRef getDependencies() const { return Dependencies; } /// The module shadowed by this module, if any. ModuleDecl *getShadowedModule() const { return ShadowedModule; } /// Searches the module's top-level decls for the given identifier. void lookupValue(DeclName name, SmallVectorImpl &results); /// Searches the module's local type decls for the given mangled name. TypeDecl *lookupLocalType(StringRef MangledName); /// Searches the module's nested type decls table for the given member of /// the given type. TypeDecl *lookupNestedType(Identifier name, const NominalTypeDecl *parent); /// Searches the module's operators for one with the given name and fixity. /// /// If none is found, returns null. OperatorDecl *lookupOperator(Identifier name, DeclKind fixity); /// Searches the module's precedence groups for one with the given /// name and fixity. /// /// If none is found, returns null. PrecedenceGroupDecl *lookupPrecedenceGroup(Identifier name); /// Adds any imported modules to the given vector. void getImportedModules(SmallVectorImpl &results, ModuleDecl::ImportFilter filter); void getImportDecls(SmallVectorImpl &Results); /// Reports all visible top-level members in this module. void lookupVisibleDecls(ModuleDecl::AccessPathTy accessPath, VisibleDeclConsumer &consumer, NLKind lookupKind); /// Loads extensions for the given decl. /// /// Note that this may cause other decls to load as well. void loadExtensions(NominalTypeDecl *nominal); /// \brief Load the methods within the given class that produce /// Objective-C class or instance methods with the given selector. /// /// \param classDecl The class in which we are searching for @objc methods. /// The search only considers this class and its extensions; not any /// superclasses. /// /// \param selector The selector to search for. /// /// \param isInstanceMethod Whether we are looking for an instance method /// (vs. a class method). /// /// \param methods The list of @objc methods in this class that have this /// selector and are instance/class methods as requested. void loadObjCMethods(ClassDecl *classDecl, ObjCSelector selector, bool isInstanceMethod, llvm::TinyPtrVector &methods); /// Reports all class members in the module to the given consumer. /// /// This is intended for use with id-style lookup and code completion. void lookupClassMembers(ModuleDecl::AccessPathTy accessPath, VisibleDeclConsumer &consumer); /// Adds class members in the module with the given name to the given vector. /// /// This is intended for use with id-style lookup. void lookupClassMember(ModuleDecl::AccessPathTy accessPath, DeclName name, SmallVectorImpl &results); /// Find all Objective-C methods with the given selector. void lookupObjCMethods( ObjCSelector selector, SmallVectorImpl &results); /// Reports all link-time dependencies. void collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const; /// Adds all top-level decls to the given vector. void getTopLevelDecls(SmallVectorImpl &Results); /// Adds all local type decls to the given vector. void getLocalTypeDecls(SmallVectorImpl &Results); /// Adds all top-level decls to the given vector. /// /// This includes all decls that should be displayed to clients of the module. /// This can differ from \c getTopLevelDecls, e.g. it returns decls from a /// shadowed clang module. void getDisplayDecls(SmallVectorImpl &results); StringRef getModuleFilename() const { // FIXME: This seems fragile, maybe store the filename separately ? return ModuleInputBuffer->getBufferIdentifier(); } /// AST-verify imported decls. /// /// Has no effect in NDEBUG builds. void verify() const; virtual void loadAllMembers(Decl *D, uint64_t contextData) override; virtual void loadAllConformances(const Decl *D, uint64_t contextData, SmallVectorImpl &Conforms) override; virtual TypeLoc loadAssociatedTypeDefault(const AssociatedTypeDecl *ATD, uint64_t contextData) override; virtual void finishNormalConformance(NormalProtocolConformance *conformance, uint64_t contextData) override; GenericEnvironment *loadGenericEnvironment(const DeclContext *decl, uint64_t contextData) override; Optional getGroupNameById(unsigned Id) const; Optional getSourceFileNameById(unsigned Id) const; Optional getGroupNameForDecl(const Decl *D) const; Optional getSourceFileNameForDecl(const Decl *D) const; Optional getSourceOrderForDecl(const Decl *D) const; void collectAllGroups(std::vector &Names) const; Optional getCommentForDecl(const Decl *D) const; Optional getCommentForDeclByUSR(StringRef USR) const; Optional getGroupNameByUSR(StringRef USR) const; Identifier getDiscriminatorForPrivateValue(const ValueDecl *D); // MARK: Deserialization interface llvm::BitstreamCursor getSILCursor() const { return SILCursor; } llvm::BitstreamCursor getSILIndexCursor() const { return SILIndexCursor; } /// Returns the type with the given ID, deserializing it if needed. /// /// \sa getTypeChecked Type getType(serialization::TypeID TID); /// Returns the type with the given ID, deserializing it if needed. llvm::Expected getTypeChecked(serialization::TypeID TID); /// Returns the base name with the given ID, deserializing it if needed. DeclBaseName getDeclBaseName(serialization::IdentifierID IID); /// Convenience method to retrieve the identifier backing the name with /// given ID. Asserts that the name with this ID is not special. Identifier getIdentifier(serialization::IdentifierID IID); /// Returns the decl with the given ID, deserializing it if needed. /// /// \param DID The ID for the decl within this module. /// \param ForcedContext Optional override for the decl context of certain /// kinds of decls, used to avoid re-entrant /// deserialization. /// /// \sa getDeclChecked Decl *getDecl(serialization::DeclID DID, Optional ForcedContext = None); /// Returns the decl with the given ID, deserializing it if needed. /// /// \param DID The ID for the decl within this module. /// \param ForcedContext Optional override for the decl context of certain /// kinds of decls, used to avoid re-entrant /// deserialization. llvm::Expected getDeclChecked(serialization::DeclID DID, Optional ForcedContext = None); /// Returns the decl context with the given ID, deserializing it if needed. DeclContext *getDeclContext(serialization::DeclContextID DID); /// Returns the local decl context with the given ID, deserializing it if needed. DeclContext *getLocalDeclContext(serialization::DeclContextID DID); /// Returns the appropriate module for the given ID. ModuleDecl *getModule(serialization::ModuleID MID); /// Returns the appropriate module for the given name. /// /// If the name matches the name of the current module, a shadowed module /// is loaded instead. ModuleDecl *getModule(ArrayRef name); /// Returns the generic signature or environment for the given ID, /// deserializing it if needed. /// /// \param wantEnvironment If true, always return the full generic /// environment. Otherwise, only return the generic environment if it's /// already been constructed, and the signature in other cases. llvm::PointerUnion getGenericSignatureOrEnvironment(serialization::GenericEnvironmentID ID, bool wantEnvironment = false); /// Returns the generic environment for the given ID, deserializing it if /// needed. GenericEnvironment *getGenericEnvironment( serialization::GenericEnvironmentID ID); /// Reads a substitution record from \c DeclTypeCursor. /// /// If the record at the cursor is not a substitution, returns None. Optional maybeReadSubstitution(llvm::BitstreamCursor &Cursor, GenericEnvironment *genericEnv = nullptr); /// Recursively reads a protocol conformance from the given cursor. ProtocolConformanceRef readConformance(llvm::BitstreamCursor &Cursor, GenericEnvironment *genericEnv = nullptr); /// Read a SILLayout from the given cursor. SILLayout *readSILLayout(llvm::BitstreamCursor &Cursor); /// Read the given normal conformance from the current module file. NormalProtocolConformance * readNormalConformance(serialization::NormalConformanceID id); /// Reads a foreign error conformance from \c DeclTypeCursor, if present. Optional maybeReadForeignErrorConvention(); }; } // end namespace swift #endif