//===--- ModuleFileSharedCore.h - Core of a serialized module ---*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2020 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_MODULEFILECORE_H #define SWIFT_SERIALIZATION_MODULEFILECORE_H #include "ModuleFormat.h" #include "swift/AST/LinkLibrary.h" #include "swift/AST/Module.h" #include "swift/Serialization/Validation.h" #include "llvm/Bitstream/BitstreamReader.h" namespace llvm { template class OnDiskIterableChainedHashTable; } namespace swift { /// Serialized core data of a module. The difference with `ModuleFile` is that /// `ModuleFileSharedCore` provides immutable data and is independent of a /// particular ASTContext. It is designed to be able to be shared across /// multiple `ModuleFile`s of different `ASTContext`s in a thread-safe manner. /// /// It is **important** to preserve the following properties for /// `ModuleFileSharedCore`: /// * a `ModuleFile` should access its assigned `ModuleFileSharedCore` as /// immutable and thread-safe /// * `ModuleFileSharedCore` should be Independent of an `ASTContext` object. class ModuleFileSharedCore { friend class ModuleFile; using DeclID = serialization::DeclID; using Status = serialization::Status; /// The module file data. std::unique_ptr ModuleInputBuffer; std::unique_ptr ModuleDocInputBuffer; std::unique_ptr ModuleSourceInfoInputBuffer; /// The cursor used to lazily load things from the file. llvm::BitstreamCursor DeclTypeCursor; llvm::BitstreamCursor SILCursor; llvm::BitstreamCursor SILIndexCursor; llvm::BitstreamCursor DeclMemberTablesCursor; /// The name of the module. StringRef Name; /// The target the module was built for. StringRef TargetTriple; /// The canonical name of the SDK the module was built with. StringRef SDKName; /// The name of the module interface this module was compiled from. /// /// Empty if this module didn't come from an interface file. StringRef ModuleInterfacePath; /// The Swift compatibility version in use when this module was built. version::Version CompatibilityVersion; /// User-defined module version number. llvm::VersionTuple UserModuleVersion; /// The data blob containing all of the module's identifiers. StringRef IdentifierData; /// Full blob from the misc. version field of the metadata block. This should /// include the version string of the compiler that built the module. StringRef MiscVersion; /// The module ABI name. StringRef ModuleABIName; /// The name of the package this module belongs to. StringRef ModulePackageName; /// Module name to use when referenced in clients module interfaces. StringRef ModuleExportAsName; /// \c true if this module has incremental dependency information. bool HasIncrementalInfo = false; /// \c true if this module was compiled with -enable-ossa-modules. bool RequiresOSSAModules; /// An array of module names that are allowed to import this one. ArrayRef AllowableClientNames; public: /// Represents another module that has been imported as a dependency. class Dependency { public: const StringRef RawPath; const StringRef RawSPIs; private: using ImportFilterKind = ModuleDecl::ImportFilterKind; const unsigned RawImportControl : 2; const unsigned IsHeader : 1; const unsigned IsScoped : 1; static unsigned rawControlFromKind(ImportFilterKind importKind) { return llvm::countTrailingZeros(static_cast(importKind)); } ImportFilterKind getImportControl() const { return static_cast(1 << RawImportControl); } Dependency(StringRef path, StringRef spiGroups, bool isHeader, ImportFilterKind importControl, bool isScoped) : RawPath(path), RawSPIs(spiGroups), RawImportControl(rawControlFromKind(importControl)), IsHeader(isHeader), IsScoped(isScoped) { assert(llvm::countPopulation(static_cast(importControl)) == 1 && "must be a particular filter option, not a bitset"); assert(getImportControl() == importControl && "not enough bits"); } public: Dependency(StringRef path, StringRef spiGroups, ImportFilterKind importControl, bool isScoped) : Dependency(path, spiGroups, false, importControl, isScoped) {} static Dependency forHeader(StringRef headerPath, bool exported) { auto importControl = exported ? ImportFilterKind::Exported : ImportFilterKind::Default; return Dependency(headerPath, StringRef(), true, importControl, false); } bool isExported() const { return getImportControl() == ImportFilterKind::Exported; } bool isImplementationOnly() const { return getImportControl() == ImportFilterKind::ImplementationOnly; } bool isHeader() const { return IsHeader; } bool isScoped() const { return IsScoped; } std::string getPrettyPrintedPath() const; }; private: /// All modules this module depends on. SmallVector Dependencies; /// 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: using RawBitOffset = uint64_t; private: /// An allocator for buffers owned by the file. llvm::BumpPtrAllocator Allocator; /// Allocates a buffer using #Allocator and initializes it with the contents /// of the container \p rawData, then stores it in \p buffer. /// /// \p buffer is passed as an argument rather than returned so that the /// element type can be inferred. template void allocateBuffer(MutableArrayRef &buffer, const RawData &rawData); /// Allocates a buffer using #Allocator and initializes it with the contents /// of the container \p rawData, then stores it in \p buffer. /// /// \p buffer is passed as an argument rather than returned so that the /// element type can be inferred. template void allocateBuffer(ArrayRef &buffer, const RawData &rawData) { assert(buffer.empty()); MutableArrayRef result; allocateBuffer(result, rawData); buffer = result; } /// Decls referenced by this module. ArrayRef Decls; /// Local DeclContexts referenced by this module. ArrayRef LocalDeclContexts; /// Protocol conformances referenced by this module. ArrayRef Conformances; /// SILLayouts referenced by this module. ArrayRef SILLayouts; /// Types referenced by this module. ArrayRef Types; /// Clang types referenced by this module. ArrayRef ClangTypes; /// Generic signatures referenced by this module. ArrayRef GenericSignatures; /// Generic environments referenced by this module. ArrayRef GenericEnvironments; /// Substitution maps referenced by this module. ArrayRef SubstitutionMaps; /// Identifiers referenced by this module. ArrayRef Identifiers; class DeclTableInfo; using SerializedDeclTable = llvm::OnDiskIterableChainedHashTable; class ExtensionTableInfo; using SerializedExtensionTable = llvm::OnDiskIterableChainedHashTable; class LocalDeclTableInfo; using SerializedLocalDeclTable = llvm::OnDiskIterableChainedHashTable; using OpaqueReturnTypeDeclTableInfo = LocalDeclTableInfo; using SerializedOpaqueReturnTypeDeclTable = llvm::OnDiskIterableChainedHashTable; class NestedTypeDeclsTableInfo; using SerializedNestedTypeDeclsTable = llvm::OnDiskIterableChainedHashTable; class DeclMemberNamesTableInfo; using SerializedDeclMemberNamesTable = llvm::OnDiskIterableChainedHashTable; class DeclMembersTableInfo; using SerializedDeclMembersTable = llvm::OnDiskIterableChainedHashTable; class DeclFingerprintsTableInfo; using SerializedDeclFingerprintsTable = llvm::OnDiskIterableChainedHashTable; std::unique_ptr TopLevelDecls; std::unique_ptr OperatorDecls; std::unique_ptr PrecedenceGroupDecls; std::unique_ptr ClassMembersForDynamicLookup; std::unique_ptr OperatorMethodDecls; std::unique_ptr ExtensionDecls; std::unique_ptr LocalTypeDecls; std::unique_ptr OpaqueReturnTypeDecls; std::unique_ptr NestedTypeDecls; std::unique_ptr DeclMemberNames; std::unique_ptr DeclFingerprints; class ObjCMethodTableInfo; using SerializedObjCMethodTable = llvm::OnDiskIterableChainedHashTable; std::unique_ptr ObjCMethods; ArrayRef OrderedTopLevelDecls; ArrayRef ExportedPrespecializationDecls; class DeclCommentTableInfo; using SerializedDeclCommentTable = llvm::OnDiskIterableChainedHashTable; struct DeserializedCommentInfo; using GroupNameTable = const llvm::DenseMap; std::unique_ptr GroupNamesMap; std::unique_ptr DeclCommentTable; class DeclUSRTableInfo; using SerializedDeclUSRTable = llvm::OnDiskIterableChainedHashTable; std::unique_ptr DeclUSRsTable; class DerivativeFunctionConfigTableInfo; using SerializedDerivativeFunctionConfigTable = llvm::OnDiskIterableChainedHashTable; std::unique_ptr DerivativeFunctionConfigurations; /// A blob of 0 terminated string segments referenced in \c SourceLocsTextData StringRef SourceLocsTextData; /// A blob of source file list. StringRef SourceFileListData; /// An array of fixed size source location data for each USR appearing in /// \c DeclUSRsTable. StringRef BasicDeclLocsData; /// An array of fixed-size location data for each `SingleRawComment` piece /// of declaration's documentation `RawComment`s. StringRef DocRangesData; 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; /// Whether an error has been detected setting up this module file. unsigned HasError : 1; /// Whether this module is `-enable-private-imports`. unsigned ArePrivateImportsEnabled : 1; /// Whether this module file is actually a .sib file. unsigned IsSIB : 1; /// Whether this module is compiled as static library. unsigned IsStaticLibrary : 1; /// Whether this module was built with -experimental-hermetic-seal-at-link. unsigned HasHermeticSealAtLink : 1; /// Whether this module file is compiled with '-enable-testing'. unsigned IsTestable : 1; /// Discriminator for resilience strategy. unsigned ResilienceStrategy : 2; /// Whether the module was rebuilt from a module interface instead of being /// build from the full source. unsigned IsBuiltFromInterface: 1; /// Whether this module is compiled with implicit dynamic. unsigned IsImplicitDynamicEnabled: 1; /// Whether this module is compiled while allowing errors. unsigned IsAllowModuleWithCompilerErrorsEnabled: 1; /// \c true if this module was built with complete checking for concurrency. unsigned IsConcurrencyChecked: 1; // Explicitly pad out to the next word boundary. unsigned : 4; } Bits = {}; static_assert(sizeof(ModuleBits) <= 8, "The bit set should be small"); bool hasError() const { return Bits.HasError; } void setEntryPointClassID(serialization::DeclID DID) { Bits.HasEntryPoint = true; Bits.EntryPointDeclID = DID; assert(Bits.EntryPointDeclID == DID && "not enough bits for DeclID"); } /// Constructs a new module and validates it. ModuleFileSharedCore( std::unique_ptr moduleInputBuffer, std::unique_ptr moduleDocInputBuffer, std::unique_ptr moduleSourceInfoInputBuffer, bool isFramework, bool requiresOSSAModules, StringRef requiredSDK, serialization::ValidationInfo &info, PathObfuscator &pathRecoverer); /// Change the status of the current module. Status error(Status issue) { assert(issue != Status::Valid); Bits.HasError = true; return issue; } /// Emits one last diagnostic, logs the error, and then aborts for the stack /// trace. [[noreturn]] void fatal(llvm::Error error) const; void fatalIfNotSuccess(llvm::Error error) const { if (error) fatal(std::move(error)); } template T fatalIfUnexpected(llvm::Expected expected) const { if (expected) return std::move(expected.get()); fatal(expected.takeError()); } /// Read an on-disk decl hash table stored in index_block::DeclListLayout /// format. std::unique_ptr readDeclTable(ArrayRef fields, StringRef blobData) const; /// Read an on-disk local decl hash table stored in /// index_block::DeclListLayout format. std::unique_ptr readLocalDeclTable(ArrayRef fields, StringRef blobData) const; /// Read an on-disk Objective-C method table stored in /// index_block::ObjCMethodTableLayout format. std::unique_ptr readObjCMethodTable(ArrayRef fields, StringRef blobData) const; /// Read an on-disk local decl hash table stored in /// index_block::ExtensionTableLayout format. std::unique_ptr readExtensionTable(ArrayRef fields, StringRef blobData) const; /// Read an on-disk local decl hash table stored in /// index_block::NestedTypeDeclsLayout format. std::unique_ptr readNestedTypeDeclsTable(ArrayRef fields, StringRef blobData) const; /// Read an on-disk local decl-name hash table stored in /// index_block::DeclMemberNamesLayout format. std::unique_ptr readDeclMemberNamesTable(ArrayRef fields, StringRef blobData) const; /// Read an on-disk local decl-members hash table stored in /// index_block::DeclMembersLayout format. std::unique_ptr readDeclMembersTable(ArrayRef fields, StringRef blobData) const; /// Read an on-disk local declid-string hash table stored in /// index_block::DeclFingerprintsLayout format. std::unique_ptr readDeclFingerprintsTable(ArrayRef fields, StringRef blobData) const; /// Read an on-disk derivative function configuration table stored in /// index_block::DerivativeFunctionConfigTableLayout format. std::unique_ptr readDerivativeFunctionConfigTable(ArrayRef fields, StringRef blobData) const; /// 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) const; std::unique_ptr readGroupTable(ArrayRef fields, StringRef blobData) const; /// Reads the comment block, which contains USR to comment mappings. /// /// Returns false if there was an error. bool readCommentBlock(llvm::BitstreamCursor &cursor); /// Loads data from #ModuleDocInputBuffer. /// /// Returns false if there was an error. bool readModuleDocIfPresent(PathObfuscator &pathRecoverer); /// Reads the source loc block, which contains USR to decl location mapping. /// /// Returns false if there was an error. bool readDeclLocsBlock(llvm::BitstreamCursor &cursor); /// Loads data from #ModuleSourceInfoInputBuffer. /// /// Returns false if there was an error. bool readModuleSourceInfoIfPresent(PathObfuscator &pathRecoverer); /// Read an on-disk decl hash table stored in /// \c sourceinfo_block::DeclUSRSLayout format. std::unique_ptr readDeclUSRsTable(ArrayRef fields, StringRef blobData) const; /// Returns the appropriate module name for the given ID. StringRef getModuleNameFromID(serialization::ModuleID MID) const; /// Convenience method to retrieve the text of the name with the given ID. /// Asserts that the name with this ID is not special. StringRef getIdentifierText(serialization::IdentifierID IID) const; 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 requiresOSSAModules If true, this requires dependent modules to be /// compiled with -enable-ossa-modules. /// \param[out] theModule The loaded module. /// \returns Whether the module was successfully loaded, or what went wrong /// if it was not. static serialization::ValidationInfo load(StringRef moduleInterfacePath, std::unique_ptr moduleInputBuffer, std::unique_ptr moduleDocInputBuffer, std::unique_ptr moduleSourceInfoInputBuffer, bool isFramework, bool requiresOSSAModules, StringRef requiredSDK, PathObfuscator &pathRecoverer, std::shared_ptr &theModule) { serialization::ValidationInfo info; auto *core = new ModuleFileSharedCore( std::move(moduleInputBuffer), std::move(moduleDocInputBuffer), std::move(moduleSourceInfoInputBuffer), isFramework, requiresOSSAModules, requiredSDK, info, pathRecoverer); if (!moduleInterfacePath.empty()) { ArrayRef path; core->allocateBuffer(path, moduleInterfacePath); core->ModuleInterfacePath = StringRef(path.data(), path.size()); } theModule.reset(core); return info; } /// Outputs information useful for diagnostics to \p out void outputDiagnosticInfo(llvm::raw_ostream &os) const; // Out of line to avoid instantiation OnDiskChainedHashTable here. ~ModuleFileSharedCore(); /// The name of the module. StringRef getName() const { return Name; } /// Returns the list of modules this module depends on. ArrayRef getDependencies() const { return Dependencies; } /// Returns \c true if this module file contains a section with incremental /// information. bool hasIncrementalInfo() const { return HasIncrementalInfo; } /// Returns \c true if a corresponding .swiftsourceinfo has been found. bool hasSourceInfoFile() const { return !!ModuleSourceInfoInputBuffer; } /// Returns \c true if a corresponding .swiftsourceinfo has been found *and /// read*. bool hasSourceInfo() const; bool isConcurrencyChecked() const { return Bits.IsConcurrencyChecked; } }; template void ModuleFileSharedCore::allocateBuffer(MutableArrayRef &buffer, const RawData &rawData) { assert(buffer.empty() && "reallocating deserialized buffer"); if (rawData.empty()) return; void *rawBuffer = Allocator.Allocate(sizeof(T) * rawData.size(), alignof(T)); buffer = llvm::makeMutableArrayRef(static_cast(rawBuffer), rawData.size()); std::uninitialized_copy(rawData.begin(), rawData.end(), buffer.begin()); } } // end namespace swift #endif