blob: 2edf8f8c90cc6c750563d5787b285dfa099a70e9 [file] [log] [blame]
//===--- ModuleFileSharedCore.cpp - Core of a serialized module -----------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "ModuleFileSharedCore.h"
#include "ModuleFileCoreTableInfo.h"
#include "BCReadingExtras.h"
#include "DeserializationErrors.h"
#include "swift/Strings.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/OnDiskHashTable.h"
using namespace swift;
using namespace swift::serialization;
using namespace llvm::support;
using llvm::Expected;
StringRef swift::getNameOfModule(const ModuleFileSharedCore *MF) {
return MF->getName();
}
static bool checkModuleSignature(llvm::BitstreamCursor &cursor,
ArrayRef<unsigned char> signature) {
for (unsigned char byte : signature) {
if (cursor.AtEndOfStream())
return false;
if (Expected<llvm::SimpleBitstreamCursor::word_t> maybeRead =
cursor.Read(8)) {
if (maybeRead.get() != byte)
return false;
} else {
consumeError(maybeRead.takeError());
return false;
}
}
return true;
}
static bool enterTopLevelModuleBlock(llvm::BitstreamCursor &cursor,
unsigned ID,
bool shouldReadBlockInfo = true) {
Expected<llvm::BitstreamEntry> maybeNext = cursor.advance();
if (!maybeNext) {
// FIXME this drops the error on the floor.
consumeError(maybeNext.takeError());
return false;
}
llvm::BitstreamEntry next = maybeNext.get();
if (next.Kind != llvm::BitstreamEntry::SubBlock)
return false;
if (next.ID == llvm::bitc::BLOCKINFO_BLOCK_ID) {
if (shouldReadBlockInfo) {
if (!cursor.ReadBlockInfoBlock())
return false;
} else {
if (cursor.SkipBlock())
return false;
}
return enterTopLevelModuleBlock(cursor, ID, false);
}
if (next.ID != ID)
return false;
if (llvm::Error Err = cursor.EnterSubBlock(ID)) {
// FIXME this drops the error on the floor.
consumeError(std::move(Err));
return false;
}
return true;
}
/// Populate \p extendedInfo with the data from the options block.
///
/// Returns true on success.
static bool readOptionsBlock(llvm::BitstreamCursor &cursor,
SmallVectorImpl<uint64_t> &scratch,
ExtendedValidationInfo &extendedInfo) {
while (!cursor.AtEndOfStream()) {
Expected<llvm::BitstreamEntry> maybeEntry = cursor.advance();
if (!maybeEntry) {
// FIXME this drops the error on the floor.
consumeError(maybeEntry.takeError());
return false;
}
llvm::BitstreamEntry entry = maybeEntry.get();
if (entry.Kind == llvm::BitstreamEntry::EndBlock)
break;
if (entry.Kind == llvm::BitstreamEntry::Error)
return false;
if (entry.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown metadata sub-block, possibly for use by a future version of
// the module format.
if (cursor.SkipBlock())
return false;
continue;
}
scratch.clear();
StringRef blobData;
Expected<unsigned> maybeKind =
cursor.readRecord(entry.ID, scratch, &blobData);
if (!maybeKind) {
// FIXME this drops the error on the floor.
consumeError(maybeKind.takeError());
return false;
}
unsigned kind = maybeKind.get();
switch (kind) {
case options_block::SDK_PATH:
extendedInfo.setSDKPath(blobData);
break;
case options_block::XCC:
extendedInfo.addExtraClangImporterOption(blobData);
break;
case options_block::IS_SIB:
bool IsSIB;
options_block::IsSIBLayout::readRecord(scratch, IsSIB);
extendedInfo.setIsSIB(IsSIB);
break;
case options_block::IS_TESTABLE:
extendedInfo.setIsTestable(true);
break;
case options_block::ARE_PRIVATE_IMPORTS_ENABLED:
extendedInfo.setPrivateImportsEnabled(true);
break;
case options_block::IS_IMPLICIT_DYNAMIC_ENABLED:
extendedInfo.setImplicitDynamicEnabled(true);
break;
case options_block::RESILIENCE_STRATEGY:
unsigned Strategy;
options_block::ResilienceStrategyLayout::readRecord(scratch, Strategy);
extendedInfo.setResilienceStrategy(ResilienceStrategy(Strategy));
break;
case options_block::IS_ALLOW_MODULE_WITH_COMPILER_ERRORS_ENABLED:
extendedInfo.setAllowModuleWithCompilerErrorsEnabled(true);
break;
default:
// Unknown options record, possibly for use by a future version of the
// module format.
break;
}
}
return true;
}
static ValidationInfo
validateControlBlock(llvm::BitstreamCursor &cursor,
SmallVectorImpl<uint64_t> &scratch,
std::pair<uint16_t, uint16_t> expectedVersion,
ExtendedValidationInfo *extendedInfo) {
// The control block is malformed until we've at least read a major version
// number.
ValidationInfo result;
bool versionSeen = false;
while (!cursor.AtEndOfStream()) {
Expected<llvm::BitstreamEntry> maybeEntry = cursor.advance();
if (!maybeEntry) {
// FIXME this drops the error on the floor.
consumeError(maybeEntry.takeError());
result.status = Status::Malformed;
return result;
}
llvm::BitstreamEntry entry = maybeEntry.get();
if (entry.Kind == llvm::BitstreamEntry::EndBlock)
break;
if (entry.Kind == llvm::BitstreamEntry::Error) {
result.status = Status::Malformed;
return result;
}
if (entry.Kind == llvm::BitstreamEntry::SubBlock) {
if (entry.ID == OPTIONS_BLOCK_ID && extendedInfo) {
if (llvm::Error Err = cursor.EnterSubBlock(OPTIONS_BLOCK_ID)) {
// FIXME this drops the error on the floor.
consumeError(std::move(Err));
result.status = Status::Malformed;
return result;
}
if (!readOptionsBlock(cursor, scratch, *extendedInfo)) {
result.status = Status::Malformed;
return result;
}
} else {
// Unknown metadata sub-block, possibly for use by a future version of
// the module format.
if (cursor.SkipBlock()) {
result.status = Status::Malformed;
return result;
}
}
continue;
}
scratch.clear();
StringRef blobData;
Expected<unsigned> maybeKind =
cursor.readRecord(entry.ID, scratch, &blobData);
if (!maybeKind) {
// FIXME this drops the error on the floor.
consumeError(maybeKind.takeError());
result.status = Status::Malformed;
return result;
}
unsigned kind = maybeKind.get();
switch (kind) {
case control_block::METADATA: {
if (versionSeen) {
result.status = Status::Malformed;
break;
}
uint16_t versionMajor = scratch[0];
if (versionMajor > expectedVersion.first)
result.status = Status::FormatTooNew;
else if (versionMajor < expectedVersion.first)
result.status = Status::FormatTooOld;
else
result.status = Status::Valid;
// Major version 0 does not have stable minor versions.
if (versionMajor == 0) {
uint16_t versionMinor = scratch[1];
if (versionMinor != expectedVersion.second) {
if (versionMinor < expectedVersion.second)
result.status = Status::FormatTooOld;
else
result.status = Status::FormatTooNew;
}
}
// These fields were added later; be resilient against their absence.
switch (scratch.size()) {
default:
// Add new cases here, in descending order.
case 4:
if (scratch[3] != 0) {
result.compatibilityVersion =
version::Version(blobData.substr(scratch[2]+1, scratch[3]),
SourceLoc(), nullptr);
}
LLVM_FALLTHROUGH;
case 3:
result.shortVersion = blobData.slice(0, scratch[2]);
LLVM_FALLTHROUGH;
case 2:
case 1:
case 0:
break;
}
result.miscVersion = blobData;
versionSeen = true;
break;
}
case control_block::MODULE_NAME:
result.name = blobData;
break;
case control_block::TARGET:
result.targetTriple = blobData;
break;
default:
// Unknown metadata record, possibly for use by a future version of the
// module format.
break;
}
}
return result;
}
static bool validateInputBlock(
llvm::BitstreamCursor &cursor, SmallVectorImpl<uint64_t> &scratch,
SmallVectorImpl<SerializationOptions::FileDependency> &dependencies) {
SmallVector<StringRef, 4> dependencyDirectories;
SmallString<256> dependencyFullPathBuffer;
while (!cursor.AtEndOfStream()) {
Expected<llvm::BitstreamEntry> maybeEntry = cursor.advance();
if (!maybeEntry) {
// FIXME this drops the error on the floor.
consumeError(maybeEntry.takeError());
return true;
}
llvm::BitstreamEntry entry = maybeEntry.get();
if (entry.Kind == llvm::BitstreamEntry::EndBlock)
break;
if (entry.Kind == llvm::BitstreamEntry::Error)
return true;
scratch.clear();
StringRef blobData;
Expected<unsigned> maybeKind =
cursor.readRecord(entry.ID, scratch, &blobData);
if (!maybeKind) {
// FIXME this drops the error on the floor.
consumeError(maybeKind.takeError());
return true;
}
unsigned kind = maybeKind.get();
switch (kind) {
case input_block::FILE_DEPENDENCY: {
bool isHashBased = scratch[2] != 0;
bool isSDKRelative = scratch[3] != 0;
StringRef path = blobData;
size_t directoryIndex = scratch[4];
if (directoryIndex != 0) {
if (directoryIndex > dependencyDirectories.size())
return true;
dependencyFullPathBuffer = dependencyDirectories[directoryIndex-1];
llvm::sys::path::append(dependencyFullPathBuffer, blobData);
path = dependencyFullPathBuffer;
}
if (isHashBased) {
dependencies.push_back(
SerializationOptions::FileDependency::hashBased(
path, isSDKRelative, scratch[0], scratch[1]));
} else {
dependencies.push_back(
SerializationOptions::FileDependency::modTimeBased(
path, isSDKRelative, scratch[0], scratch[1]));
}
break;
}
case input_block::DEPENDENCY_DIRECTORY:
dependencyDirectories.push_back(blobData);
break;
default:
// Unknown metadata record, possibly for use by a future version of the
// module format.
break;
}
}
return false;
}
bool serialization::isSerializedAST(StringRef data) {
StringRef signatureStr(reinterpret_cast<const char *>(SWIFTMODULE_SIGNATURE),
llvm::array_lengthof(SWIFTMODULE_SIGNATURE));
return data.startswith(signatureStr);
}
ValidationInfo serialization::validateSerializedAST(
StringRef data,
ExtendedValidationInfo *extendedInfo,
SmallVectorImpl<SerializationOptions::FileDependency> *dependencies) {
ValidationInfo result;
// Check 32-bit alignment.
if (data.size() % SWIFTMODULE_ALIGNMENT != 0 ||
reinterpret_cast<uintptr_t>(data.data()) % SWIFTMODULE_ALIGNMENT != 0)
return result;
llvm::BitstreamCursor cursor(data);
SmallVector<uint64_t, 32> scratch;
if (!checkModuleSignature(cursor, SWIFTMODULE_SIGNATURE) ||
!enterTopLevelModuleBlock(cursor, MODULE_BLOCK_ID, false))
return result;
llvm::BitstreamEntry topLevelEntry;
while (!cursor.AtEndOfStream()) {
Expected<llvm::BitstreamEntry> maybeEntry =
cursor.advance(AF_DontPopBlockAtEnd);
if (!maybeEntry) {
// FIXME this drops the error on the floor.
consumeError(maybeEntry.takeError());
result.status = Status::Malformed;
return result;
}
topLevelEntry = maybeEntry.get();
if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock)
break;
if (topLevelEntry.ID == CONTROL_BLOCK_ID) {
if (llvm::Error Err = cursor.EnterSubBlock(CONTROL_BLOCK_ID)) {
// FIXME this drops the error on the floor.
consumeError(std::move(Err));
result.status = Status::Malformed;
return result;
}
result = validateControlBlock(cursor, scratch,
{SWIFTMODULE_VERSION_MAJOR,
SWIFTMODULE_VERSION_MINOR},
extendedInfo);
if (result.status == Status::Malformed)
return result;
} else if (dependencies &&
result.status == Status::Valid &&
topLevelEntry.ID == INPUT_BLOCK_ID) {
if (llvm::Error Err = cursor.EnterSubBlock(INPUT_BLOCK_ID)) {
// FIXME this drops the error on the floor.
consumeError(std::move(Err));
result.status = Status::Malformed;
return result;
}
if (validateInputBlock(cursor, scratch, *dependencies)) {
result.status = Status::Malformed;
return result;
}
} else {
if (cursor.SkipBlock()) {
result.status = Status::Malformed;
return result;
}
}
}
if (topLevelEntry.Kind == llvm::BitstreamEntry::EndBlock) {
cursor.ReadBlockEnd();
assert(cursor.GetCurrentBitNo() % CHAR_BIT == 0);
result.bytes = cursor.GetCurrentBitNo() / CHAR_BIT;
} else {
result.status = Status::Malformed;
}
return result;
}
std::string ModuleFileSharedCore::Dependency::getPrettyPrintedPath() const {
StringRef pathWithoutScope = RawPath;
if (isScoped()) {
size_t splitPoint = pathWithoutScope.find_last_of('\0');
pathWithoutScope = pathWithoutScope.slice(0, splitPoint);
}
std::string output = pathWithoutScope.str();
std::replace(output.begin(), output.end(), '\0', '.');
return output;
}
void ModuleFileSharedCore::fatal(llvm::Error error) {
logAllUnhandledErrors(std::move(error), llvm::errs(),
"\n*** DESERIALIZATION FAILURE (please include this "
"section in any bug report) ***\n");
abort();
}
ModuleFileSharedCore::~ModuleFileSharedCore() { }
std::unique_ptr<ModuleFileSharedCore::SerializedDeclTable>
ModuleFileSharedCore::readDeclTable(ArrayRef<uint64_t> fields,
StringRef blobData) const {
uint32_t tableOffset;
index_block::DeclListLayout::readRecord(fields, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(blobData.data());
using OwnedTable = std::unique_ptr<SerializedDeclTable>;
return OwnedTable(SerializedDeclTable::Create(base + tableOffset,
base + sizeof(uint32_t), base));
}
std::unique_ptr<ModuleFileSharedCore::SerializedExtensionTable>
ModuleFileSharedCore::readExtensionTable(ArrayRef<uint64_t> fields,
StringRef blobData) const {
uint32_t tableOffset;
index_block::DeclListLayout::readRecord(fields, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(blobData.data());
using OwnedTable = std::unique_ptr<SerializedExtensionTable>;
return OwnedTable(SerializedExtensionTable::Create(base + tableOffset,
base + sizeof(uint32_t), base, ExtensionTableInfo(*this)));
}
std::unique_ptr<ModuleFileSharedCore::SerializedLocalDeclTable>
ModuleFileSharedCore::readLocalDeclTable(ArrayRef<uint64_t> fields,
StringRef blobData) const {
uint32_t tableOffset;
index_block::DeclListLayout::readRecord(fields, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(blobData.data());
using OwnedTable = std::unique_ptr<SerializedLocalDeclTable>;
return OwnedTable(SerializedLocalDeclTable::Create(base + tableOffset,
base + sizeof(uint32_t), base));
}
std::unique_ptr<ModuleFileSharedCore::SerializedNestedTypeDeclsTable>
ModuleFileSharedCore::readNestedTypeDeclsTable(ArrayRef<uint64_t> fields,
StringRef blobData) const {
uint32_t tableOffset;
index_block::NestedTypeDeclsLayout::readRecord(fields, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(blobData.data());
using OwnedTable = std::unique_ptr<SerializedNestedTypeDeclsTable>;
return OwnedTable(SerializedNestedTypeDeclsTable::Create(base + tableOffset,
base + sizeof(uint32_t), base));
}
std::unique_ptr<ModuleFileSharedCore::SerializedDeclMemberNamesTable>
ModuleFileSharedCore::readDeclMemberNamesTable(ArrayRef<uint64_t> fields,
StringRef blobData) const {
uint32_t tableOffset;
index_block::DeclMemberNamesLayout::readRecord(fields, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(blobData.data());
using OwnedTable = std::unique_ptr<SerializedDeclMemberNamesTable>;
return OwnedTable(SerializedDeclMemberNamesTable::Create(base + tableOffset,
base + sizeof(uint32_t), base));
}
std::unique_ptr<ModuleFileSharedCore::SerializedDeclMembersTable>
ModuleFileSharedCore::readDeclMembersTable(ArrayRef<uint64_t> fields,
StringRef blobData) const {
uint32_t tableOffset;
decl_member_tables_block::DeclMembersLayout::readRecord(fields,
tableOffset);
auto base = reinterpret_cast<const uint8_t *>(blobData.data());
using OwnedTable = std::unique_ptr<SerializedDeclMembersTable>;
return OwnedTable(SerializedDeclMembersTable::Create(base + tableOffset,
base + sizeof(uint32_t), base));
}
std::unique_ptr<ModuleFileSharedCore::SerializedDeclFingerprintsTable>
ModuleFileSharedCore::readDeclFingerprintsTable(ArrayRef<uint64_t> fields,
StringRef blobData) const {
uint32_t tableOffset;
index_block::DeclFingerprintsLayout::readRecord(fields, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(blobData.data());
using OwnedTable = std::unique_ptr<SerializedDeclFingerprintsTable>;
return OwnedTable(SerializedDeclFingerprintsTable::Create(
base + tableOffset, base + sizeof(uint32_t), base));
}
std::unique_ptr<ModuleFileSharedCore::SerializedObjCMethodTable>
ModuleFileSharedCore::readObjCMethodTable(ArrayRef<uint64_t> fields,
StringRef blobData) const {
uint32_t tableOffset;
index_block::ObjCMethodTableLayout::readRecord(fields, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(blobData.data());
using OwnedTable = std::unique_ptr<SerializedObjCMethodTable>;
return OwnedTable(
SerializedObjCMethodTable::Create(base + tableOffset,
base + sizeof(uint32_t), base));
}
std::unique_ptr<ModuleFileSharedCore::SerializedDerivativeFunctionConfigTable>
ModuleFileSharedCore::readDerivativeFunctionConfigTable(
ArrayRef<uint64_t> fields, StringRef blobData) const {
uint32_t tableOffset;
index_block::DerivativeFunctionConfigTableLayout::readRecord(fields,
tableOffset);
auto base = reinterpret_cast<const uint8_t *>(blobData.data());
using OwnedTable = std::unique_ptr<SerializedDerivativeFunctionConfigTable>;
return OwnedTable(SerializedDerivativeFunctionConfigTable::Create(
base + tableOffset, base + sizeof(uint32_t), base));
}
bool ModuleFileSharedCore::readIndexBlock(llvm::BitstreamCursor &cursor) {
if (llvm::Error Err = cursor.EnterSubBlock(INDEX_BLOCK_ID)) {
// FIXME this drops the error on the floor.
consumeError(std::move(Err));
return false;
}
SmallVector<uint64_t, 4> scratch;
StringRef blobData;
while (!cursor.AtEndOfStream()) {
Expected<llvm::BitstreamEntry> maybeEntry = cursor.advance();
if (!maybeEntry) {
// FIXME this drops the error on the floor.
consumeError(maybeEntry.takeError());
return false;
}
llvm::BitstreamEntry entry = maybeEntry.get();
switch (entry.Kind) {
case llvm::BitstreamEntry::EndBlock:
return true;
case llvm::BitstreamEntry::Error:
return false;
case llvm::BitstreamEntry::SubBlock:
if (entry.ID == DECL_MEMBER_TABLES_BLOCK_ID) {
DeclMemberTablesCursor = cursor;
if (llvm::Error Err = DeclMemberTablesCursor.EnterSubBlock(
DECL_MEMBER_TABLES_BLOCK_ID)) {
// FIXME this drops the error on the floor.
consumeError(std::move(Err));
return false;
}
llvm::BitstreamEntry subentry;
do {
// Scan forward, to load the cursor with any abbrevs we'll need while
// seeking inside this block later.
Expected<llvm::BitstreamEntry> maybeEntry =
DeclMemberTablesCursor.advance(
llvm::BitstreamCursor::AF_DontPopBlockAtEnd);
if (!maybeEntry) {
// FIXME this drops the error on the floor.
consumeError(maybeEntry.takeError());
return false;
}
subentry = maybeEntry.get();
} while (!DeclMemberTablesCursor.AtEndOfStream() &&
subentry.Kind != llvm::BitstreamEntry::Record &&
subentry.Kind != llvm::BitstreamEntry::EndBlock);
}
if (cursor.SkipBlock())
return false;
break;
case llvm::BitstreamEntry::Record:
scratch.clear();
blobData = {};
Expected<unsigned> maybeKind =
cursor.readRecord(entry.ID, scratch, &blobData);
if (!maybeKind) {
// FIXME this drops the error on the floor.
consumeError(maybeKind.takeError());
return false;
}
unsigned kind = maybeKind.get();
switch (kind) {
case index_block::DECL_OFFSETS:
assert(blobData.empty());
allocateBuffer(Decls, scratch);
break;
case index_block::TYPE_OFFSETS:
assert(blobData.empty());
allocateBuffer(Types, scratch);
break;
case index_block::CLANG_TYPE_OFFSETS:
assert(blobData.empty());
allocateBuffer(ClangTypes, scratch);
break;
case index_block::IDENTIFIER_OFFSETS:
assert(blobData.empty());
allocateBuffer(Identifiers, scratch);
break;
case index_block::TOP_LEVEL_DECLS:
TopLevelDecls = readDeclTable(scratch, blobData);
break;
case index_block::OPERATORS:
OperatorDecls = readDeclTable(scratch, blobData);
break;
case index_block::PRECEDENCE_GROUPS:
PrecedenceGroupDecls = readDeclTable(scratch, blobData);
break;
case index_block::EXTENSIONS:
ExtensionDecls = readExtensionTable(scratch, blobData);
break;
case index_block::CLASS_MEMBERS_FOR_DYNAMIC_LOOKUP:
ClassMembersForDynamicLookup = readDeclTable(scratch, blobData);
break;
case index_block::OPERATOR_METHODS:
OperatorMethodDecls = readDeclTable(scratch, blobData);
break;
case index_block::OBJC_METHODS:
ObjCMethods = readObjCMethodTable(scratch, blobData);
break;
case index_block::DERIVATIVE_FUNCTION_CONFIGURATIONS:
DerivativeFunctionConfigurations =
readDerivativeFunctionConfigTable(scratch, blobData);
break;
case index_block::ENTRY_POINT:
assert(blobData.empty());
setEntryPointClassID(scratch.front());
break;
case index_block::ORDERED_TOP_LEVEL_DECLS:
allocateBuffer(OrderedTopLevelDecls, scratch);
break;
case index_block::EXPORTED_PRESPECIALIZATION_DECLS:
allocateBuffer(ExportedPrespecializationDecls, scratch);
break;
case index_block::LOCAL_TYPE_DECLS:
LocalTypeDecls = readLocalDeclTable(scratch, blobData);
break;
case index_block::OPAQUE_RETURN_TYPE_DECLS:
OpaqueReturnTypeDecls = readLocalDeclTable(scratch, blobData);
break;
case index_block::NESTED_TYPE_DECLS:
NestedTypeDecls = readNestedTypeDeclsTable(scratch, blobData);
break;
case index_block::DECL_MEMBER_NAMES:
DeclMemberNames = readDeclMemberNamesTable(scratch, blobData);
break;
case index_block::DECL_FINGERPRINTS:
DeclFingerprints = readDeclFingerprintsTable(scratch, blobData);
break;
case index_block::LOCAL_DECL_CONTEXT_OFFSETS:
assert(blobData.empty());
allocateBuffer(LocalDeclContexts, scratch);
break;
case index_block::GENERIC_SIGNATURE_OFFSETS:
assert(blobData.empty());
allocateBuffer(GenericSignatures, scratch);
break;
case index_block::SUBSTITUTION_MAP_OFFSETS:
assert(blobData.empty());
allocateBuffer(SubstitutionMaps, scratch);
break;
case index_block::NORMAL_CONFORMANCE_OFFSETS:
assert(blobData.empty());
allocateBuffer(NormalConformances, scratch);
break;
case index_block::SIL_LAYOUT_OFFSETS:
assert(blobData.empty());
allocateBuffer(SILLayouts, scratch);
break;
default:
// Unknown index kind, which this version of the compiler won't use.
break;
}
break;
}
}
return false;
}
std::unique_ptr<ModuleFileSharedCore::SerializedDeclCommentTable>
ModuleFileSharedCore::readDeclCommentTable(ArrayRef<uint64_t> fields,
StringRef blobData) const {
if (fields.empty() || blobData.empty())
return nullptr;
uint32_t tableOffset = static_cast<uint32_t>(fields.front());
auto base = reinterpret_cast<const uint8_t *>(blobData.data());
return std::unique_ptr<SerializedDeclCommentTable>(
SerializedDeclCommentTable::Create(base + tableOffset,
base + sizeof(uint32_t), base));
}
std::unique_ptr<ModuleFileSharedCore::GroupNameTable>
ModuleFileSharedCore::readGroupTable(ArrayRef<uint64_t> Fields,
StringRef BlobData) const {
auto pMap = std::make_unique<llvm::DenseMap<unsigned, StringRef>>();
auto Data = reinterpret_cast<const uint8_t *>(BlobData.data());
unsigned GroupCount = endian::readNext<uint32_t, little, unaligned>(Data);
for (unsigned I = 0; I < GroupCount; ++I) {
auto RawSize = endian::readNext<uint32_t, little, unaligned>(Data);
auto RawText = StringRef(reinterpret_cast<const char *>(Data), RawSize);
Data += RawSize;
(*pMap)[I] = RawText;
}
return std::unique_ptr<ModuleFileSharedCore::GroupNameTable>(pMap.release());
}
bool ModuleFileSharedCore::readCommentBlock(llvm::BitstreamCursor &cursor) {
if (llvm::Error Err = cursor.EnterSubBlock(COMMENT_BLOCK_ID)) {
// FIXME this drops the error on the floor.
consumeError(std::move(Err));
return false;
}
SmallVector<uint64_t, 4> scratch;
StringRef blobData;
while (!cursor.AtEndOfStream()) {
Expected<llvm::BitstreamEntry> maybeEntry = cursor.advance();
if (!maybeEntry) {
// FIXME this drops the error on the floor.
consumeError(maybeEntry.takeError());
return false;
}
llvm::BitstreamEntry entry = maybeEntry.get();
switch (entry.Kind) {
case llvm::BitstreamEntry::EndBlock:
return true;
case llvm::BitstreamEntry::Error:
return false;
case llvm::BitstreamEntry::SubBlock:
// Unknown sub-block, which this version of the compiler won't use.
if (cursor.SkipBlock())
return false;
break;
case llvm::BitstreamEntry::Record:
scratch.clear();
Expected<unsigned> maybeKind =
cursor.readRecord(entry.ID, scratch, &blobData);
if (!maybeKind) {
// FIXME this drops the error on the floor.
consumeError(maybeKind.takeError());
return false;
}
unsigned kind = maybeKind.get();
switch (kind) {
case comment_block::DECL_COMMENTS:
DeclCommentTable = readDeclCommentTable(scratch, blobData);
break;
case comment_block::GROUP_NAMES:
GroupNamesMap = readGroupTable(scratch, blobData);
break;
default:
// Unknown index kind, which this version of the compiler won't use.
break;
}
break;
}
}
return false;
}
static Optional<swift::LibraryKind> getActualLibraryKind(unsigned rawKind) {
auto stableKind = static_cast<serialization::LibraryKind>(rawKind);
if (stableKind != rawKind)
return None;
switch (stableKind) {
case serialization::LibraryKind::Library:
return swift::LibraryKind::Library;
case serialization::LibraryKind::Framework:
return swift::LibraryKind::Framework;
}
// If there's a new case value in the module file, ignore it.
return None;
}
static Optional<ModuleDecl::ImportFilterKind>
getActualImportControl(unsigned rawValue) {
// We switch on the raw value rather than the enum in order to handle future
// values.
switch (rawValue) {
case static_cast<unsigned>(serialization::ImportControl::Normal):
return ModuleDecl::ImportFilterKind::Default;
case static_cast<unsigned>(serialization::ImportControl::Exported):
return ModuleDecl::ImportFilterKind::Exported;
case static_cast<unsigned>(serialization::ImportControl::ImplementationOnly):
return ModuleDecl::ImportFilterKind::ImplementationOnly;
default:
return None;
}
}
bool ModuleFileSharedCore::readModuleDocIfPresent() {
if (!this->ModuleDocInputBuffer)
return true;
llvm::BitstreamCursor docCursor{ModuleDocInputBuffer->getMemBufferRef()};
if (!checkModuleSignature(docCursor, SWIFTDOC_SIGNATURE) ||
!enterTopLevelModuleBlock(docCursor, MODULE_DOC_BLOCK_ID)) {
return false;
}
SmallVector<uint64_t, 64> scratch;
llvm::BitstreamEntry topLevelEntry;
bool hasValidControlBlock = false;
ValidationInfo info;
while (!docCursor.AtEndOfStream()) {
Expected<llvm::BitstreamEntry> maybeEntry =
docCursor.advance(AF_DontPopBlockAtEnd);
if (!maybeEntry) {
// FIXME this drops the error on the floor.
consumeError(maybeEntry.takeError());
return false;
}
topLevelEntry = maybeEntry.get();
if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock)
break;
switch (topLevelEntry.ID) {
case CONTROL_BLOCK_ID: {
if (llvm::Error Err = docCursor.EnterSubBlock(CONTROL_BLOCK_ID)) {
// FIXME this drops the error on the floor.
consumeError(std::move(Err));
return false;
}
info = validateControlBlock(docCursor, scratch,
{SWIFTDOC_VERSION_MAJOR,
SWIFTDOC_VERSION_MINOR},
/*extendedInfo*/nullptr);
if (info.status != Status::Valid)
return false;
// Check that the swiftdoc is actually for this module.
if (info.name != Name)
return false;
hasValidControlBlock = true;
break;
}
case COMMENT_BLOCK_ID: {
if (!hasValidControlBlock || !readCommentBlock(docCursor))
return false;
break;
}
default:
// Unknown top-level block, possibly for use by a future version of the
// module format.
if (docCursor.SkipBlock())
return false;
break;
}
}
if (topLevelEntry.Kind != llvm::BitstreamEntry::EndBlock)
return false;
return true;
}
std::unique_ptr<ModuleFileSharedCore::SerializedDeclUSRTable>
ModuleFileSharedCore::readDeclUSRsTable(ArrayRef<uint64_t> fields,
StringRef blobData) const {
if (fields.empty() || blobData.empty())
return nullptr;
uint32_t tableOffset = static_cast<uint32_t>(fields.front());
auto base = reinterpret_cast<const uint8_t *>(blobData.data());
return std::unique_ptr<SerializedDeclUSRTable>(
SerializedDeclUSRTable::Create(base + tableOffset, base + sizeof(uint32_t),
base));
}
bool ModuleFileSharedCore::readDeclLocsBlock(llvm::BitstreamCursor &cursor) {
if (llvm::Error Err = cursor.EnterSubBlock(CONTROL_BLOCK_ID)) {
consumeError(std::move(Err));
return false;
}
SmallVector<uint64_t, 4> scratch;
StringRef blobData;
while (!cursor.AtEndOfStream()) {
Expected<llvm::BitstreamEntry> entry = cursor.advance();
if (!entry) {
consumeError(entry.takeError());
return false;
}
switch (entry->Kind) {
case llvm::BitstreamEntry::EndBlock:
return true;
case llvm::BitstreamEntry::Error:
return false;
case llvm::BitstreamEntry::SubBlock:
// Unknown sub-block, which this version of the compiler won't use.
if (cursor.SkipBlock())
return false;
break;
case llvm::BitstreamEntry::Record:
scratch.clear();
Expected<unsigned> kind =
cursor.readRecord(entry->ID, scratch, &blobData);
if (!kind) {
consumeError(kind.takeError());
return false;
}
switch (*kind) {
case decl_locs_block::BASIC_DECL_LOCS:
BasicDeclLocsData = blobData;
break;
case decl_locs_block::TEXT_DATA:
SourceLocsTextData = blobData;
break;
case decl_locs_block::DECL_USRS:
DeclUSRsTable = readDeclUSRsTable(scratch, blobData);
break;
case decl_locs_block::DOC_RANGES:
DocRangesData = blobData;
break;
default:
// Unknown index kind, which this version of the compiler won't use.
break;
}
break;
}
}
return false;
}
bool ModuleFileSharedCore::readModuleSourceInfoIfPresent() {
if (!this->ModuleSourceInfoInputBuffer)
return true;
llvm::BitstreamCursor infoCursor{
ModuleSourceInfoInputBuffer->getMemBufferRef()};
if (!checkModuleSignature(infoCursor, SWIFTSOURCEINFO_SIGNATURE) ||
!enterTopLevelModuleBlock(infoCursor, MODULE_SOURCEINFO_BLOCK_ID)) {
return false;
}
SmallVector<uint64_t, 64> scratch;
bool hasValidControlBlock = false;
ValidationInfo info;
unsigned kind = llvm::BitstreamEntry::Error;
while (!infoCursor.AtEndOfStream()) {
Expected<llvm::BitstreamEntry> topLevelEntry =
infoCursor.advance(AF_DontPopBlockAtEnd);
if (!topLevelEntry) {
consumeError(topLevelEntry.takeError());
return false;
}
kind = topLevelEntry->Kind;
if (kind != llvm::BitstreamEntry::SubBlock)
break;
switch (topLevelEntry->ID) {
case CONTROL_BLOCK_ID: {
if (llvm::Error Err = infoCursor.EnterSubBlock(CONTROL_BLOCK_ID)) {
consumeError(std::move(Err));
return false;
}
info = validateControlBlock(infoCursor, scratch,
{SWIFTSOURCEINFO_VERSION_MAJOR,
SWIFTSOURCEINFO_VERSION_MINOR},
/*extendedInfo*/nullptr);
if (info.status != Status::Valid)
return false;
// Check that the swiftsourceinfo is actually for this module.
if (info.name != Name)
return false;
hasValidControlBlock = true;
break;
}
case DECL_LOCS_BLOCK_ID: {
if (!hasValidControlBlock || !readDeclLocsBlock(infoCursor))
return false;
break;
}
default:
// Unknown top-level block, possibly for use by a future version of the
// module format.
if (infoCursor.SkipBlock())
return false;
break;
}
}
if (kind != llvm::BitstreamEntry::EndBlock)
return false;
return true;
}
StringRef ModuleFileSharedCore::getModuleNameFromID(ModuleID MID) const {
if (MID < NUM_SPECIAL_IDS) {
switch (static_cast<SpecialIdentifierID>(static_cast<uint8_t>(MID))) {
case BUILTIN_MODULE_ID:
return BUILTIN_NAME;
case CURRENT_MODULE_ID:
return Name;
case OBJC_HEADER_MODULE_ID:
return CLANG_HEADER_MODULE_NAME;
case SUBSCRIPT_ID:
case CONSTRUCTOR_ID:
case DESTRUCTOR_ID:
llvm_unreachable("Modules cannot be named with special names");
case NUM_SPECIAL_IDS:
llvm_unreachable("implementation detail only");
}
}
return getIdentifierText(MID);
}
StringRef ModuleFileSharedCore::getIdentifierText(IdentifierID IID) const {
if (IID == 0)
return StringRef();
assert(IID >= NUM_SPECIAL_IDS);
size_t rawID = IID - NUM_SPECIAL_IDS;
assert(rawID < Identifiers.size() && "invalid identifier ID");
auto offset = Identifiers[rawID];
assert(!IdentifierData.empty() && "no identifier data in module");
StringRef rawStrPtr = IdentifierData.substr(offset);
size_t terminatorOffset = rawStrPtr.find('\0');
assert(terminatorOffset != StringRef::npos &&
"unterminated identifier string data");
return rawStrPtr.slice(0, terminatorOffset);
}
ModuleFileSharedCore::ModuleFileSharedCore(
std::unique_ptr<llvm::MemoryBuffer> moduleInputBuffer,
std::unique_ptr<llvm::MemoryBuffer> moduleDocInputBuffer,
std::unique_ptr<llvm::MemoryBuffer> moduleSourceInfoInputBuffer,
bool isFramework, serialization::ValidationInfo &info)
: ModuleInputBuffer(std::move(moduleInputBuffer)),
ModuleDocInputBuffer(std::move(moduleDocInputBuffer)),
ModuleSourceInfoInputBuffer(std::move(moduleSourceInfoInputBuffer)) {
assert(!hasError());
Bits.IsFramework = isFramework;
PrettyStackTraceModuleFileCore stackEntry(*this);
llvm::BitstreamCursor cursor{ModuleInputBuffer->getMemBufferRef()};
if (!checkModuleSignature(cursor, SWIFTMODULE_SIGNATURE) ||
!enterTopLevelModuleBlock(cursor, MODULE_BLOCK_ID)) {
info.status = error(Status::Malformed);
return;
}
// Future-proofing: make sure we validate the control block before we try to
// read any other blocks.
bool hasValidControlBlock = false;
SmallVector<uint64_t, 64> scratch;
llvm::BitstreamEntry topLevelEntry;
while (!cursor.AtEndOfStream()) {
Expected<llvm::BitstreamEntry> maybeEntry =
cursor.advance(AF_DontPopBlockAtEnd);
if (!maybeEntry) {
// FIXME this drops the error diagnostic on the floor.
consumeError(maybeEntry.takeError());
info.status = error(Status::Malformed);
return;
}
topLevelEntry = maybeEntry.get();
if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock)
break;
switch (topLevelEntry.ID) {
case CONTROL_BLOCK_ID: {
if (llvm::Error Err = cursor.EnterSubBlock(CONTROL_BLOCK_ID)) {
// FIXME this drops the error on the floor.
consumeError(std::move(Err));
info.status = error(Status::Malformed);
return;
}
ExtendedValidationInfo extInfo;
info = validateControlBlock(cursor, scratch,
{SWIFTMODULE_VERSION_MAJOR,
SWIFTMODULE_VERSION_MINOR},
&extInfo);
if (info.status != Status::Valid) {
error(info.status);
return;
}
Name = info.name;
TargetTriple = info.targetTriple;
CompatibilityVersion = info.compatibilityVersion;
Bits.ArePrivateImportsEnabled = extInfo.arePrivateImportsEnabled();
Bits.IsSIB = extInfo.isSIB();
Bits.IsTestable = extInfo.isTestable();
Bits.ResilienceStrategy = unsigned(extInfo.getResilienceStrategy());
Bits.IsImplicitDynamicEnabled = extInfo.isImplicitDynamicEnabled();
Bits.IsAllowModuleWithCompilerErrorsEnabled =
extInfo.isAllowModuleWithCompilerErrorsEnabled();
MiscVersion = info.miscVersion;
hasValidControlBlock = true;
break;
}
case INPUT_BLOCK_ID: {
if (!hasValidControlBlock) {
info.status = error(Status::Malformed);
return;
}
if (llvm::Error Err = cursor.EnterSubBlock(INPUT_BLOCK_ID)) {
// FIXME this drops the error on the floor.
consumeError(std::move(Err));
info.status = error(Status::Malformed);
return;
}
Expected<llvm::BitstreamEntry> maybeNext = cursor.advance();
if (!maybeNext) {
// FIXME this drops the error on the floor.
consumeError(maybeNext.takeError());
info.status = error(Status::Malformed);
return;
}
llvm::BitstreamEntry next = maybeNext.get();
while (next.Kind == llvm::BitstreamEntry::Record) {
scratch.clear();
StringRef blobData;
Expected<unsigned> maybeKind =
cursor.readRecord(next.ID, scratch, &blobData);
if (!maybeKind) {
// FIXME this drops the error on the floor.
consumeError(maybeKind.takeError());
info.status = error(Status::Malformed);
return;
}
unsigned kind = maybeKind.get();
switch (kind) {
case input_block::IMPORTED_MODULE: {
unsigned rawImportControl;
bool scoped;
bool hasSPI;
input_block::ImportedModuleLayout::readRecord(scratch,
rawImportControl,
scoped, hasSPI);
auto importKind = getActualImportControl(rawImportControl);
if (!importKind) {
// We don't know how to import this dependency.
info.status = error(Status::Malformed);
return;
}
StringRef spiBlob;
if (hasSPI) {
scratch.clear();
llvm::BitstreamEntry entry =
fatalIfUnexpected(cursor.advance(AF_DontPopBlockAtEnd));
unsigned recordID = fatalIfUnexpected(
cursor.readRecord(entry.ID, scratch, &spiBlob));
assert(recordID == input_block::IMPORTED_MODULE_SPIS);
input_block::ImportedModuleLayoutSPI::readRecord(scratch);
(void) recordID;
} else {
spiBlob = StringRef();
}
Dependencies.push_back(
{blobData, spiBlob, importKind.getValue(), scoped});
break;
}
case input_block::LINK_LIBRARY: {
uint8_t rawKind;
bool shouldForceLink;
input_block::LinkLibraryLayout::readRecord(scratch, rawKind,
shouldForceLink);
if (auto libKind = getActualLibraryKind(rawKind))
LinkLibraries.push_back({blobData, *libKind, shouldForceLink});
// else ignore the dependency...it'll show up as a linker error.
break;
}
case input_block::IMPORTED_HEADER: {
assert(!importedHeaderInfo.fileSize && "only one header allowed");
bool exported;
input_block::ImportedHeaderLayout::readRecord(scratch,
exported, importedHeaderInfo.fileSize,
importedHeaderInfo.fileModTime);
Dependencies.push_back(Dependency::forHeader(blobData, exported));
break;
}
case input_block::IMPORTED_HEADER_CONTENTS: {
assert(Dependencies.back().isHeader() && "must follow header record");
assert(importedHeaderInfo.contents.empty() &&
"contents seen already");
importedHeaderInfo.contents = blobData;
break;
}
case input_block::SEARCH_PATH: {
bool isFramework;
bool isSystem;
input_block::SearchPathLayout::readRecord(scratch, isFramework,
isSystem);
SearchPaths.push_back({blobData, isFramework, isSystem});
break;
}
case input_block::MODULE_INTERFACE_PATH: {
ModuleInterfacePath = blobData;
break;
}
default:
// Unknown input kind, possibly for use by a future version of the
// module format.
// FIXME: Should we warn about this?
break;
}
maybeNext = cursor.advance();
if (!maybeNext) {
// FIXME this drops the error on the floor.
consumeError(maybeNext.takeError());
info.status = error(Status::Malformed);
return;
}
next = maybeNext.get();
}
if (next.Kind != llvm::BitstreamEntry::EndBlock)
info.status = error(Status::Malformed);
break;
}
case DECLS_AND_TYPES_BLOCK_ID: {
if (!hasValidControlBlock) {
info.status = error(Status::Malformed);
return;
}
// The decls-and-types block is lazily loaded. Save the cursor and load
// any abbrev records at the start of the block.
DeclTypeCursor = cursor;
if (llvm::Error Err =
DeclTypeCursor.EnterSubBlock(DECLS_AND_TYPES_BLOCK_ID)) {
// FIXME this drops the error on the floor.
consumeError(std::move(Err));
info.status = error(Status::Malformed);
return;
}
Expected<llvm::BitstreamEntry> maybeCursor = DeclTypeCursor.advance();
if (!maybeCursor) {
// FIXME this drops the error on the floor.
consumeError(maybeCursor.takeError());
info.status = error(Status::Malformed);
return;
}
if (maybeCursor.get().Kind == llvm::BitstreamEntry::Error)
info.status = error(Status::Malformed);
// With the main cursor, skip over the block and continue.
if (cursor.SkipBlock()) {
info.status = error(Status::Malformed);
return;
}
break;
}
case IDENTIFIER_DATA_BLOCK_ID: {
if (!hasValidControlBlock) {
info.status = error(Status::Malformed);
return;
}
if (llvm::Error Err = cursor.EnterSubBlock(IDENTIFIER_DATA_BLOCK_ID)) {
// FIXME this drops the error on the floor.
consumeError(std::move(Err));
info.status = error(Status::Malformed);
return;
}
Expected<llvm::BitstreamEntry> maybeNext =
cursor.advanceSkippingSubblocks();
if (!maybeNext) {
// FIXME this drops the error on the floor.
consumeError(maybeNext.takeError());
info.status = error(Status::Malformed);
return;
}
llvm::BitstreamEntry next = maybeNext.get();
while (next.Kind == llvm::BitstreamEntry::Record) {
scratch.clear();
StringRef blobData;
Expected<unsigned> maybeKind =
cursor.readRecord(next.ID, scratch, &blobData);
if (!maybeKind) {
// FIXME this drops the error on the floor.
consumeError(maybeKind.takeError());
info.status = error(Status::Malformed);
return;
}
unsigned kind = maybeKind.get();
switch (kind) {
case identifier_block::IDENTIFIER_DATA:
assert(scratch.empty());
IdentifierData = blobData;
break;
default:
// Unknown identifier data, which this version of the compiler won't
// use.
break;
}
maybeNext = cursor.advanceSkippingSubblocks();
if (!maybeNext) {
// FIXME this drops the error on the floor.
consumeError(maybeNext.takeError());
info.status = error(Status::Malformed);
return;
}
next = maybeNext.get();
}
if (next.Kind != llvm::BitstreamEntry::EndBlock) {
info.status = error(Status::Malformed);
return;
}
break;
}
case INDEX_BLOCK_ID: {
if (!hasValidControlBlock || !readIndexBlock(cursor)) {
info.status = error(Status::Malformed);
return;
}
break;
}
case SIL_INDEX_BLOCK_ID: {
// Save the cursor.
SILIndexCursor = cursor;
if (llvm::Error Err = SILIndexCursor.EnterSubBlock(SIL_INDEX_BLOCK_ID)) {
// FIXME this drops the error on the floor.
consumeError(std::move(Err));
info.status = error(Status::Malformed);
return;
}
// With the main cursor, skip over the block and continue.
if (cursor.SkipBlock()) {
info.status = error(Status::Malformed);
return;
}
break;
}
case SIL_BLOCK_ID: {
// Save the cursor.
SILCursor = cursor;
if (llvm::Error Err = SILCursor.EnterSubBlock(SIL_BLOCK_ID)) {
// FIXME this drops the error on the floor.
consumeError(std::move(Err));
info.status = error(Status::Malformed);
return;
}
// With the main cursor, skip over the block and continue.
if (cursor.SkipBlock()) {
info.status = error(Status::Malformed);
return;
}
break;
}
case INCREMENTAL_INFORMATION_BLOCK_ID: {
HasIncrementalInfo = true;
// Skip incremental info if present. The Frontend currently doesn't do
// anything with this.
if (cursor.SkipBlock()) {
info.status = error(Status::Malformed);
return;
}
break;
}
default:
// Unknown top-level block, possibly for use by a future version of the
// module format.
if (cursor.SkipBlock()) {
info.status = error(Status::Malformed);
return;
}
break;
}
}
if (topLevelEntry.Kind != llvm::BitstreamEntry::EndBlock) {
info.status = error(Status::Malformed);
return;
}
// Read source info file.
readModuleSourceInfoIfPresent();
if (!readModuleDocIfPresent()) {
info.status = error(Status::MalformedDocumentation);
return;
}
}