blob: 920c5b08e6724c1b1dacda21d9a2b9f2d2c7675f [file] [log] [blame]
//===--- 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 <typename Info> 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<llvm::MemoryBuffer> ModuleInputBuffer;
std::unique_ptr<llvm::MemoryBuffer> ModuleDocInputBuffer;
std::unique_ptr<llvm::MemoryBuffer> 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 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;
/// 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;
/// \c true if this module has incremental dependency information.
bool HasIncrementalInfo = false;
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<unsigned>(importKind));
}
ImportFilterKind getImportControl() const {
return static_cast<ImportFilterKind>(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<unsigned>(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<Dependency, 8> 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<SearchPath> 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<LinkLibrary, 8> 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 <typename T, typename RawData>
void allocateBuffer(MutableArrayRef<T> &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 <typename T, typename RawData>
void allocateBuffer(ArrayRef<T> &buffer, const RawData &rawData) {
assert(buffer.empty());
MutableArrayRef<T> result;
allocateBuffer(result, rawData);
buffer = result;
}
/// Decls referenced by this module.
ArrayRef<RawBitOffset> Decls;
/// Local DeclContexts referenced by this module.
ArrayRef<RawBitOffset> LocalDeclContexts;
/// Normal protocol conformances referenced by this module.
ArrayRef<RawBitOffset> NormalConformances;
/// SILLayouts referenced by this module.
ArrayRef<RawBitOffset> SILLayouts;
/// Types referenced by this module.
ArrayRef<RawBitOffset> Types;
/// Clang types referenced by this module.
ArrayRef<RawBitOffset> ClangTypes;
/// Generic signatures referenced by this module.
ArrayRef<RawBitOffset> GenericSignatures;
/// Substitution maps referenced by this module.
ArrayRef<RawBitOffset> SubstitutionMaps;
/// Identifiers referenced by this module.
ArrayRef<RawBitOffset> Identifiers;
class DeclTableInfo;
using SerializedDeclTable =
llvm::OnDiskIterableChainedHashTable<DeclTableInfo>;
class ExtensionTableInfo;
using SerializedExtensionTable =
llvm::OnDiskIterableChainedHashTable<ExtensionTableInfo>;
class LocalDeclTableInfo;
using SerializedLocalDeclTable =
llvm::OnDiskIterableChainedHashTable<LocalDeclTableInfo>;
using OpaqueReturnTypeDeclTableInfo = LocalDeclTableInfo;
using SerializedOpaqueReturnTypeDeclTable =
llvm::OnDiskIterableChainedHashTable<OpaqueReturnTypeDeclTableInfo>;
class NestedTypeDeclsTableInfo;
using SerializedNestedTypeDeclsTable =
llvm::OnDiskIterableChainedHashTable<NestedTypeDeclsTableInfo>;
class DeclMemberNamesTableInfo;
using SerializedDeclMemberNamesTable =
llvm::OnDiskIterableChainedHashTable<DeclMemberNamesTableInfo>;
class DeclMembersTableInfo;
using SerializedDeclMembersTable =
llvm::OnDiskIterableChainedHashTable<DeclMembersTableInfo>;
class DeclFingerprintsTableInfo;
using SerializedDeclFingerprintsTable =
llvm::OnDiskIterableChainedHashTable<DeclFingerprintsTableInfo>;
std::unique_ptr<SerializedDeclTable> TopLevelDecls;
std::unique_ptr<SerializedDeclTable> OperatorDecls;
std::unique_ptr<SerializedDeclTable> PrecedenceGroupDecls;
std::unique_ptr<SerializedDeclTable> ClassMembersForDynamicLookup;
std::unique_ptr<SerializedDeclTable> OperatorMethodDecls;
std::unique_ptr<SerializedExtensionTable> ExtensionDecls;
std::unique_ptr<SerializedLocalDeclTable> LocalTypeDecls;
std::unique_ptr<SerializedOpaqueReturnTypeDeclTable> OpaqueReturnTypeDecls;
std::unique_ptr<SerializedNestedTypeDeclsTable> NestedTypeDecls;
std::unique_ptr<SerializedDeclMemberNamesTable> DeclMemberNames;
std::unique_ptr<SerializedDeclFingerprintsTable> DeclFingerprints;
class ObjCMethodTableInfo;
using SerializedObjCMethodTable =
llvm::OnDiskIterableChainedHashTable<ObjCMethodTableInfo>;
std::unique_ptr<SerializedObjCMethodTable> ObjCMethods;
ArrayRef<serialization::DeclID> OrderedTopLevelDecls;
ArrayRef<serialization::DeclID> ExportedPrespecializationDecls;
class DeclCommentTableInfo;
using SerializedDeclCommentTable =
llvm::OnDiskIterableChainedHashTable<DeclCommentTableInfo>;
struct DeserializedCommentInfo;
using GroupNameTable = const llvm::DenseMap<unsigned, StringRef>;
std::unique_ptr<GroupNameTable> GroupNamesMap;
std::unique_ptr<SerializedDeclCommentTable> DeclCommentTable;
class DeclUSRTableInfo;
using SerializedDeclUSRTable =
llvm::OnDiskIterableChainedHashTable<DeclUSRTableInfo>;
std::unique_ptr<SerializedDeclUSRTable> DeclUSRsTable;
class DerivativeFunctionConfigTableInfo;
using SerializedDerivativeFunctionConfigTable =
llvm::OnDiskIterableChainedHashTable<DerivativeFunctionConfigTableInfo>;
std::unique_ptr<SerializedDerivativeFunctionConfigTable>
DerivativeFunctionConfigurations;
/// A blob of 0 terminated string segments referenced in \c SourceLocsTextData
StringRef SourceLocsTextData;
/// 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 file is compiled with '-enable-testing'.
unsigned IsTestable : 1;
/// Discriminator for resilience strategy.
unsigned ResilienceStrategy : 2;
/// Whether this module is compiled with implicit dynamic.
unsigned IsImplicitDynamicEnabled: 1;
/// Whether this module is compiled while allowing errors.
unsigned IsAllowModuleWithCompilerErrorsEnabled: 1;
// Explicitly pad out to the next word boundary.
unsigned : 3;
} 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<llvm::MemoryBuffer> moduleInputBuffer,
std::unique_ptr<llvm::MemoryBuffer> moduleDocInputBuffer,
std::unique_ptr<llvm::MemoryBuffer> moduleSourceInfoInputBuffer,
bool isFramework, serialization::ValidationInfo &info);
/// 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.
LLVM_ATTRIBUTE_NORETURN static void fatal(llvm::Error error);
void fatalIfNotSuccess(llvm::Error error) {
if (error)
fatal(std::move(error));
}
template <typename T> T fatalIfUnexpected(llvm::Expected<T> expected) {
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<SerializedDeclTable>
readDeclTable(ArrayRef<uint64_t> fields, StringRef blobData) const;
/// Read an on-disk local decl hash table stored in
/// index_block::DeclListLayout format.
std::unique_ptr<SerializedLocalDeclTable>
readLocalDeclTable(ArrayRef<uint64_t> fields, StringRef blobData) const;
/// Read an on-disk Objective-C method table stored in
/// index_block::ObjCMethodTableLayout format.
std::unique_ptr<ModuleFileSharedCore::SerializedObjCMethodTable>
readObjCMethodTable(ArrayRef<uint64_t> fields, StringRef blobData) const;
/// Read an on-disk local decl hash table stored in
/// index_block::ExtensionTableLayout format.
std::unique_ptr<SerializedExtensionTable>
readExtensionTable(ArrayRef<uint64_t> fields, StringRef blobData) const;
/// Read an on-disk local decl hash table stored in
/// index_block::NestedTypeDeclsLayout format.
std::unique_ptr<SerializedNestedTypeDeclsTable>
readNestedTypeDeclsTable(ArrayRef<uint64_t> fields, StringRef blobData) const;
/// Read an on-disk local decl-name hash table stored in
/// index_block::DeclMemberNamesLayout format.
std::unique_ptr<SerializedDeclMemberNamesTable>
readDeclMemberNamesTable(ArrayRef<uint64_t> fields, StringRef blobData) const;
/// Read an on-disk local decl-members hash table stored in
/// index_block::DeclMembersLayout format.
std::unique_ptr<SerializedDeclMembersTable>
readDeclMembersTable(ArrayRef<uint64_t> fields, StringRef blobData) const;
/// Read an on-disk local declid-string hash table stored in
/// index_block::DeclFingerprintsLayout format.
std::unique_ptr<SerializedDeclFingerprintsTable>
readDeclFingerprintsTable(ArrayRef<uint64_t> fields,
StringRef blobData) const;
/// Read an on-disk derivative function configuration table stored in
/// index_block::DerivativeFunctionConfigTableLayout format.
std::unique_ptr<ModuleFileSharedCore::SerializedDerivativeFunctionConfigTable>
readDerivativeFunctionConfigTable(ArrayRef<uint64_t> 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<SerializedDeclCommentTable>
readDeclCommentTable(ArrayRef<uint64_t> fields, StringRef blobData) const;
std::unique_ptr<GroupNameTable>
readGroupTable(ArrayRef<uint64_t> 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();
/// 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();
/// Read an on-disk decl hash table stored in
/// \c sourceinfo_block::DeclUSRSLayout format.
std::unique_ptr<SerializedDeclUSRTable>
readDeclUSRsTable(ArrayRef<uint64_t> 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[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<llvm::MemoryBuffer> moduleInputBuffer,
std::unique_ptr<llvm::MemoryBuffer> moduleDocInputBuffer,
std::unique_ptr<llvm::MemoryBuffer> moduleSourceInfoInputBuffer,
bool isFramework,
std::shared_ptr<const ModuleFileSharedCore> &theModule) {
serialization::ValidationInfo info;
auto *core = new ModuleFileSharedCore(
std::move(moduleInputBuffer), std::move(moduleDocInputBuffer),
std::move(moduleSourceInfoInputBuffer), isFramework, info);
if (!moduleInterfacePath.empty()) {
ArrayRef<char> path;
core->allocateBuffer(path, moduleInterfacePath);
core->ModuleInterfacePath = StringRef(path.data(), path.size());
}
theModule.reset(core);
return info;
}
// 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<Dependency> getDependencies() const {
return Dependencies;
}
/// Returns \c true if this module file contains a section with incremental
/// information.
bool hasIncrementalInfo() const { return HasIncrementalInfo; }
};
template <typename T, typename RawData>
void ModuleFileSharedCore::allocateBuffer(MutableArrayRef<T> &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<T *>(rawBuffer),
rawData.size());
std::uninitialized_copy(rawData.begin(), rawData.end(), buffer.begin());
}
} // end namespace swift
#endif