//===--- ModuleFile.cpp - Loading a serialized module ---------------------===// // // 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 // //===----------------------------------------------------------------------===// #include "ModuleFile.h" #include "BCReadingExtras.h" #include "DeserializationErrors.h" #include "DocFormat.h" #include "SourceInfoFormat.h" #include "ModuleFormat.h" #include "swift/Serialization/SerializationOptions.h" #include "swift/Subsystems.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/ModuleLoader.h" #include "swift/AST/NameLookup.h" #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/USRGeneration.h" #include "swift/Basic/Range.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/Serialization/SerializedModuleLoader.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/DJB.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; static_assert(IsTriviallyDestructible::value, "SerializedASTFiles are BumpPtrAllocated; d'tors are not called"); static bool checkModuleSignature(llvm::BitstreamCursor &cursor, ArrayRef signature) { for (unsigned char byte : signature) { if (cursor.AtEndOfStream()) return false; if (Expected 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 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 &scratch, ExtendedValidationInfo &extendedInfo) { while (!cursor.AtEndOfStream()) { Expected 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 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::RESILIENCE_STRATEGY: unsigned Strategy; options_block::ResilienceStrategyLayout::readRecord(scratch, Strategy); extendedInfo.setResilienceStrategy(ResilienceStrategy(Strategy)); 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 &scratch, std::pair 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 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 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 &scratch, SmallVectorImpl &dependencies) { SmallVector dependencyDirectories; SmallString<256> dependencyFullPathBuffer; while (!cursor.AtEndOfStream()) { Expected 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 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(SWIFTMODULE_SIGNATURE), llvm::array_lengthof(SWIFTMODULE_SIGNATURE)); return data.startswith(signatureStr); } ValidationInfo serialization::validateSerializedAST( StringRef data, ExtendedValidationInfo *extendedInfo, SmallVectorImpl *dependencies) { ValidationInfo result; // Check 32-bit alignment. if (data.size() % SWIFTMODULE_ALIGNMENT != 0 || reinterpret_cast(data.data()) % SWIFTMODULE_ALIGNMENT != 0) return result; llvm::BitstreamCursor cursor(data); SmallVector scratch; if (!checkModuleSignature(cursor, SWIFTMODULE_SIGNATURE) || !enterTopLevelModuleBlock(cursor, MODULE_BLOCK_ID, false)) return result; llvm::BitstreamEntry topLevelEntry; while (!cursor.AtEndOfStream()) { Expected 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 ModuleFile::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; } /// Used to deserialize entries in the on-disk decl hash table. class ModuleFile::DeclTableInfo { public: using internal_key_type = std::pair; using external_key_type = DeclBaseName; using data_type = SmallVector, 8>; using hash_value_type = uint32_t; using offset_type = unsigned; internal_key_type GetInternalKey(external_key_type ID) { if (ID.getKind() == DeclBaseName::Kind::Normal) { return {DeclBaseName::Kind::Normal, ID.getIdentifier().str()}; } else { return {ID.getKind(), StringRef()}; } } hash_value_type ComputeHash(internal_key_type key) { if (key.first == DeclBaseName::Kind::Normal) { return llvm::djbHash(key.second, SWIFTMODULE_HASH_SEED); } else { return (hash_value_type)key.first; } } static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { return lhs == rhs; } static std::pair ReadKeyDataLength(const uint8_t *&data) { unsigned keyLength = endian::readNext(data); unsigned dataLength = endian::readNext(data); return { keyLength, dataLength }; } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { uint8_t kind = endian::readNext(data); switch (kind) { case static_cast(DeclNameKind::Normal): { StringRef str(reinterpret_cast(data), length - sizeof(uint8_t)); return {DeclBaseName::Kind::Normal, str}; } case static_cast(DeclNameKind::Subscript): return {DeclBaseName::Kind::Subscript, StringRef()}; case static_cast(DeclNameKind::Destructor): return {DeclBaseName::Kind::Destructor, StringRef()}; default: llvm_unreachable("Unknown DeclNameKind"); } } static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { data_type result; while (length > 0) { uint8_t kind = *data++; DeclID offset = endian::readNext(data); result.push_back({ kind, offset }); length -= 5; } return result; } }; /// Used to deserialize entries in the on-disk decl hash table. class ModuleFile::ExtensionTableInfo { ModuleFile &File; public: using internal_key_type = StringRef; using external_key_type = Identifier; using data_type = SmallVector, 8>; using hash_value_type = uint32_t; using offset_type = unsigned; internal_key_type GetInternalKey(external_key_type ID) { return ID.str(); } hash_value_type ComputeHash(internal_key_type key) { return llvm::djbHash(key, SWIFTMODULE_HASH_SEED); } static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { return lhs == rhs; } static std::pair ReadKeyDataLength(const uint8_t *&data) { unsigned keyLength = endian::readNext(data); unsigned dataLength = endian::readNext(data); return { keyLength, dataLength }; } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { return StringRef(reinterpret_cast(data), length); } data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { data_type result; const uint8_t *limit = data + length; while (data < limit) { DeclID offset = endian::readNext(data); int32_t nameIDOrLength = endian::readNext(data); StringRef moduleNameOrMangledBase; if (nameIDOrLength < 0) { const ModuleDecl *module = File.getModule(-nameIDOrLength); if (module) moduleNameOrMangledBase = module->getName().str(); } else { moduleNameOrMangledBase = StringRef(reinterpret_cast(data), nameIDOrLength); data += nameIDOrLength; } result.push_back({ moduleNameOrMangledBase, offset }); } return result; } explicit ExtensionTableInfo(ModuleFile &file) : File(file) {} }; /// Used to deserialize entries in the on-disk decl hash table. class ModuleFile::LocalDeclTableInfo { public: using internal_key_type = StringRef; using external_key_type = internal_key_type; using data_type = DeclID; using hash_value_type = uint32_t; using offset_type = unsigned; internal_key_type GetInternalKey(external_key_type ID) { return ID; } external_key_type GetExternalKey(internal_key_type ID) { return ID; } hash_value_type ComputeHash(internal_key_type key) { return llvm::djbHash(key, SWIFTMODULE_HASH_SEED); } static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { return lhs == rhs; } static std::pair ReadKeyDataLength(const uint8_t *&data) { unsigned keyLength = endian::readNext(data); return { keyLength, sizeof(uint32_t) }; } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { return StringRef(reinterpret_cast(data), length); } static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { return endian::readNext(data); } }; class ModuleFile::NestedTypeDeclsTableInfo { public: using internal_key_type = StringRef; using external_key_type = Identifier; using data_type = SmallVector, 4>; using hash_value_type = uint32_t; using offset_type = unsigned; internal_key_type GetInternalKey(external_key_type ID) { return ID.str(); } hash_value_type ComputeHash(internal_key_type key) { return llvm::djbHash(key, SWIFTMODULE_HASH_SEED); } static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { return lhs == rhs; } static std::pair ReadKeyDataLength(const uint8_t *&data) { unsigned keyLength = endian::readNext(data); unsigned dataLength = endian::readNext(data); return { keyLength, dataLength }; } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { return StringRef(reinterpret_cast(data), length); } static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { data_type result; while (length > 0) { DeclID parentID = endian::readNext(data); DeclID childID = endian::readNext(data); result.push_back({ parentID, childID }); length -= sizeof(uint32_t) * 2; } return result; } }; // Indexing the members of all Decls (well, NominalTypeDecls anyway) is // accomplished by a 2-level hashtable scheme. The outer table here maps from // DeclBaseNames to serialization::BitOffsets of sub-tables. The sub-tables -- // SerializedDeclMembersTables, one table per name -- map from the enclosing // (NominalTypeDecl) DeclID to a vector of DeclIDs of members of the nominal // with the name. See DeclMembersTableInfo below. class ModuleFile::DeclMemberNamesTableInfo { public: using internal_key_type = std::pair; using external_key_type = DeclBaseName; using data_type = serialization::BitOffset; using hash_value_type = uint32_t; using offset_type = unsigned; internal_key_type GetInternalKey(external_key_type ID) { if (ID.getKind() == DeclBaseName::Kind::Normal) { return {DeclBaseName::Kind::Normal, ID.getIdentifier().str()}; } else { return {ID.getKind(), StringRef()}; } } hash_value_type ComputeHash(internal_key_type key) { if (key.first == DeclBaseName::Kind::Normal) { return llvm::djbHash(key.second, SWIFTMODULE_HASH_SEED); } else { return (hash_value_type)key.first; } } static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { return lhs == rhs; } static std::pair ReadKeyDataLength(const uint8_t *&data) { unsigned keyLength = endian::readNext(data); return { keyLength, sizeof(uint32_t) }; } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { uint8_t kind = endian::readNext(data); switch (kind) { case static_cast(DeclNameKind::Normal): { StringRef str(reinterpret_cast(data), length - sizeof(uint8_t)); return {DeclBaseName::Kind::Normal, str}; } case static_cast(DeclNameKind::Subscript): return {DeclBaseName::Kind::Subscript, StringRef()}; case static_cast(DeclNameKind::Destructor): return {DeclBaseName::Kind::Destructor, StringRef()}; case static_cast(DeclNameKind::Constructor): return {DeclBaseName::Kind::Constructor, StringRef()}; default: llvm_unreachable("Unknown DeclNameKind"); } } static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { assert(length == sizeof(uint32_t)); return endian::readNext(data); } }; // Second half of the 2-level member name lookup scheme, see // DeclMemberNamesTableInfo above. There is one of these tables for each member // DeclBaseName N in the module, and it maps from enclosing DeclIDs (say: each // NominalTypeDecl T that has members named N) to the set of N-named members of // T. In other words, there are no names in this table: the names are one level // up, this table just maps { Owner-DeclID => [Member-DeclID, ...] }. class ModuleFile::DeclMembersTableInfo { public: using internal_key_type = uint32_t; using external_key_type = DeclID; using data_type = SmallVector; using hash_value_type = uint32_t; using offset_type = unsigned; internal_key_type GetInternalKey(external_key_type ID) { return ID; } hash_value_type ComputeHash(internal_key_type key) { return llvm::hash_value(key); } static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { return lhs == rhs; } static std::pair ReadKeyDataLength(const uint8_t *&data) { unsigned dataLength = endian::readNext(data); return { sizeof(uint32_t), dataLength }; } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { return endian::readNext(data); } static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { data_type result; while (length > 0) { DeclID declID = endian::readNext(data); result.push_back(declID); length -= sizeof(uint32_t); } return result; } }; std::unique_ptr ModuleFile::readDeclTable(ArrayRef fields, StringRef blobData) { uint32_t tableOffset; index_block::DeclListLayout::readRecord(fields, tableOffset); auto base = reinterpret_cast(blobData.data()); using OwnedTable = std::unique_ptr; return OwnedTable(SerializedDeclTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); } std::unique_ptr ModuleFile::readExtensionTable(ArrayRef fields, StringRef blobData) { uint32_t tableOffset; index_block::DeclListLayout::readRecord(fields, tableOffset); auto base = reinterpret_cast(blobData.data()); using OwnedTable = std::unique_ptr; return OwnedTable(SerializedExtensionTable::Create(base + tableOffset, base + sizeof(uint32_t), base, ExtensionTableInfo(*this))); } std::unique_ptr ModuleFile::readLocalDeclTable(ArrayRef fields, StringRef blobData) { uint32_t tableOffset; index_block::DeclListLayout::readRecord(fields, tableOffset); auto base = reinterpret_cast(blobData.data()); using OwnedTable = std::unique_ptr; return OwnedTable(SerializedLocalDeclTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); } std::unique_ptr ModuleFile::readNestedTypeDeclsTable(ArrayRef fields, StringRef blobData) { uint32_t tableOffset; index_block::NestedTypeDeclsLayout::readRecord(fields, tableOffset); auto base = reinterpret_cast(blobData.data()); using OwnedTable = std::unique_ptr; return OwnedTable(SerializedNestedTypeDeclsTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); } std::unique_ptr ModuleFile::readDeclMemberNamesTable(ArrayRef fields, StringRef blobData) { uint32_t tableOffset; index_block::DeclMemberNamesLayout::readRecord(fields, tableOffset); auto base = reinterpret_cast(blobData.data()); using OwnedTable = std::unique_ptr; return OwnedTable(SerializedDeclMemberNamesTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); } std::unique_ptr ModuleFile::readDeclMembersTable(ArrayRef fields, StringRef blobData) { uint32_t tableOffset; decl_member_tables_block::DeclMembersLayout::readRecord(fields, tableOffset); auto base = reinterpret_cast(blobData.data()); using OwnedTable = std::unique_ptr; return OwnedTable(SerializedDeclMembersTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); } /// Used to deserialize entries in the on-disk Objective-C method table. class ModuleFile::ObjCMethodTableInfo { public: using internal_key_type = std::string; using external_key_type = ObjCSelector; using data_type = SmallVector, 8>; using hash_value_type = uint32_t; using offset_type = unsigned; internal_key_type GetInternalKey(external_key_type ID) { llvm::SmallString<32> scratch; return ID.getString(scratch).str(); } hash_value_type ComputeHash(internal_key_type key) { return llvm::djbHash(key, SWIFTMODULE_HASH_SEED); } static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { return lhs == rhs; } static std::pair ReadKeyDataLength(const uint8_t *&data) { unsigned keyLength = endian::readNext(data); unsigned dataLength = endian::readNext(data); return { keyLength, dataLength }; } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { return std::string(reinterpret_cast(data), length); } static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { const constexpr auto recordSize = sizeof(uint32_t) + 1 + sizeof(uint32_t); data_type result; while (length > 0) { unsigned ownerLen = endian::readNext(data); bool isInstanceMethod = *data++ != 0; DeclID methodID = endian::readNext(data); std::string ownerName((const char *)data, ownerLen); result.push_back( std::make_tuple(std::move(ownerName), isInstanceMethod, methodID)); data += ownerLen; length -= (recordSize + ownerLen); } return result; } }; std::unique_ptr ModuleFile::readObjCMethodTable(ArrayRef fields, StringRef blobData) { uint32_t tableOffset; index_block::ObjCMethodTableLayout::readRecord(fields, tableOffset); auto base = reinterpret_cast(blobData.data()); using OwnedTable = std::unique_ptr; return OwnedTable( SerializedObjCMethodTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); } /// Used to deserialize entries in the on-disk derivative function configuration /// table. class ModuleFile::DerivativeFunctionConfigTableInfo { public: using internal_key_type = StringRef; using external_key_type = internal_key_type; using data_type = SmallVector, 8>; using hash_value_type = uint32_t; using offset_type = unsigned; external_key_type GetExternalKey(internal_key_type ID) { return ID; } internal_key_type GetInternalKey(external_key_type ID) { return ID; } hash_value_type ComputeHash(internal_key_type key) { return llvm::djbHash(key, SWIFTMODULE_HASH_SEED); } static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { return lhs == rhs; } static std::pair ReadKeyDataLength(const uint8_t *&data) { unsigned keyLength = endian::readNext(data); unsigned dataLength = endian::readNext(data); return {keyLength, dataLength}; } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { return StringRef(reinterpret_cast(data), length); } static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { data_type result; const uint8_t *limit = data + length; while (data < limit) { DeclID genSigId = endian::readNext(data); int32_t nameLength = endian::readNext(data); StringRef mangledName(reinterpret_cast(data), nameLength); data += nameLength; result.push_back({mangledName, genSigId}); } return result; } }; std::unique_ptr ModuleFile::readDerivativeFunctionConfigTable(ArrayRef fields, StringRef blobData) { uint32_t tableOffset; index_block::DerivativeFunctionConfigTableLayout::readRecord(fields, tableOffset); auto base = reinterpret_cast(blobData.data()); using OwnedTable = std::unique_ptr; return OwnedTable(SerializedDerivativeFunctionConfigTable::Create( base + tableOffset, base + sizeof(uint32_t), base)); } bool ModuleFile::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 scratch; StringRef blobData; while (!cursor.AtEndOfStream()) { Expected 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 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 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::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::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; } class ModuleFile::DeclCommentTableInfo { ModuleFile &F; public: using internal_key_type = StringRef; using external_key_type = StringRef; using data_type = CommentInfo; using hash_value_type = uint32_t; using offset_type = unsigned; DeclCommentTableInfo(ModuleFile &F) : F(F) {} internal_key_type GetInternalKey(external_key_type key) { return key; } hash_value_type ComputeHash(internal_key_type key) { assert(!key.empty()); return llvm::djbHash(key, SWIFTDOC_HASH_SEED_5_1); } static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { return lhs == rhs; } static std::pair ReadKeyDataLength(const uint8_t *&data) { unsigned keyLength = endian::readNext(data); unsigned dataLength = endian::readNext(data); return { keyLength, dataLength }; } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { return StringRef(reinterpret_cast(data), length); } data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { data_type result; { unsigned BriefSize = endian::readNext(data); result.Brief = StringRef(reinterpret_cast(data), BriefSize); data += BriefSize; } unsigned NumComments = endian::readNext(data); MutableArrayRef Comments = F.getContext().AllocateUninitialized(NumComments); for (unsigned i = 0; i != NumComments; ++i) { unsigned StartColumn = endian::readNext(data); unsigned RawSize = endian::readNext(data); auto RawText = StringRef(reinterpret_cast(data), RawSize); data += RawSize; new (&Comments[i]) SingleRawComment(RawText, StartColumn); } result.Raw = RawComment(Comments); result.Group = endian::readNext(data); result.SourceOrder = endian::readNext(data); return result; } }; std::unique_ptr ModuleFile::readDeclCommentTable(ArrayRef fields, StringRef blobData) { if (fields.empty() || blobData.empty()) return nullptr; uint32_t tableOffset = static_cast(fields.front()); auto base = reinterpret_cast(blobData.data()); return std::unique_ptr( SerializedDeclCommentTable::Create(base + tableOffset, base + sizeof(uint32_t), base, DeclCommentTableInfo(*this))); } std::unique_ptr ModuleFile::readGroupTable(ArrayRef Fields, StringRef BlobData) { std::unique_ptr pMap( new ModuleFile::GroupNameTable); auto Data = reinterpret_cast(BlobData.data()); unsigned GroupCount = endian::readNext(Data); for (unsigned I = 0; I < GroupCount; ++I) { auto RawSize = endian::readNext(Data); auto RawText = StringRef(reinterpret_cast(Data), RawSize); Data += RawSize; (*pMap)[I] = RawText; } return pMap; } bool ModuleFile::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 scratch; StringRef blobData; while (!cursor.AtEndOfStream()) { Expected 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 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 getActualLibraryKind(unsigned rawKind) { auto stableKind = static_cast(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 getActualImportControl(unsigned rawValue) { // We switch on the raw value rather than the enum in order to handle future // values. switch (rawValue) { case static_cast(serialization::ImportControl::Normal): return ModuleDecl::ImportFilterKind::Private; case static_cast(serialization::ImportControl::Exported): return ModuleDecl::ImportFilterKind::Public; case static_cast(serialization::ImportControl::ImplementationOnly): return ModuleDecl::ImportFilterKind::ImplementationOnly; default: return None; } } static bool areCompatibleArchitectures(const llvm::Triple &moduleTarget, const llvm::Triple &ctxTarget) { if (moduleTarget.getArch() == ctxTarget.getArch()) return true; // Special case: ARM and Thumb are compatible. const llvm::Triple::ArchType moduleArch = moduleTarget.getArch(); const llvm::Triple::ArchType ctxArch = ctxTarget.getArch(); if ((moduleArch == llvm::Triple::arm && ctxArch == llvm::Triple::thumb) || (moduleArch == llvm::Triple::thumb && ctxArch == llvm::Triple::arm)) return true; if ((moduleArch == llvm::Triple::armeb && ctxArch == llvm::Triple::thumbeb) || (moduleArch == llvm::Triple::thumbeb && ctxArch == llvm::Triple::armeb)) return true; return false; } static bool areCompatibleOSs(const llvm::Triple &moduleTarget, const llvm::Triple &ctxTarget) { if ((!moduleTarget.hasEnvironment() && ctxTarget.isSimulatorEnvironment()) || (!ctxTarget.hasEnvironment() && moduleTarget.isSimulatorEnvironment())) return false; if (moduleTarget.getOS() == ctxTarget.getOS()) return true; // Special case: macOS and Darwin are compatible. const llvm::Triple::OSType moduleOS = moduleTarget.getOS(); const llvm::Triple::OSType ctxOS = ctxTarget.getOS(); if ((moduleOS == llvm::Triple::Darwin && ctxOS == llvm::Triple::MacOSX) || (moduleOS == llvm::Triple::MacOSX && ctxOS == llvm::Triple::Darwin)) return true; return false; } static bool isTargetTooNew(const llvm::Triple &moduleTarget, const llvm::Triple &ctxTarget) { unsigned major, minor, micro; if (moduleTarget.isMacOSX()) { moduleTarget.getMacOSXVersion(major, minor, micro); return ctxTarget.isMacOSXVersionLT(major, minor, micro); } moduleTarget.getOSVersion(major, minor, micro); return ctxTarget.isOSVersionLT(major, minor, micro); } bool ModuleFile::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 scratch; llvm::BitstreamEntry topLevelEntry; bool hasValidControlBlock = false; ValidationInfo info; while (!docCursor.AtEndOfStream()) { Expected 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; } class ModuleFile::DeclUSRTableInfo { public: using internal_key_type = StringRef; using external_key_type = StringRef; using data_type = uint32_t; using hash_value_type = uint32_t; using offset_type = unsigned; internal_key_type GetInternalKey(external_key_type key) { return key; } hash_value_type ComputeHash(internal_key_type key) { assert(!key.empty()); return llvm::djbHash(key, SWIFTSOURCEINFO_HASH_SEED); } static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { return lhs == rhs; } static std::pair ReadKeyDataLength(const uint8_t *&data) { unsigned keyLength = endian::readNext(data); unsigned dataLength = 4; return { keyLength, dataLength }; } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { return StringRef(reinterpret_cast(data), length); } data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { assert(length == 4); return endian::readNext(data); } }; std::unique_ptr ModuleFile::readDeclUSRsTable(ArrayRef fields, StringRef blobData) { if (fields.empty() || blobData.empty()) return nullptr; uint32_t tableOffset = static_cast(fields.front()); auto base = reinterpret_cast(blobData.data()); return std::unique_ptr( SerializedDeclUSRTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); } bool ModuleFile::readDeclLocsBlock(llvm::BitstreamCursor &cursor) { if (llvm::Error Err = cursor.EnterSubBlock(CONTROL_BLOCK_ID)) { consumeError(std::move(Err)); return false; } SmallVector scratch; StringRef blobData; while (!cursor.AtEndOfStream()) { Expected 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 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 ModuleFile::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 scratch; bool hasValidControlBlock = false; ValidationInfo info; unsigned kind = llvm::BitstreamEntry::Error; while (!infoCursor.AtEndOfStream()) { Expected 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; } ModuleFile::ModuleFile( std::unique_ptr moduleInputBuffer, std::unique_ptr moduleDocInputBuffer, std::unique_ptr moduleSourceInfoInputBuffer, bool isFramework, serialization::ValidationInfo &info, serialization::ExtendedValidationInfo *extInfo) : ModuleInputBuffer(std::move(moduleInputBuffer)), ModuleDocInputBuffer(std::move(moduleDocInputBuffer)), ModuleSourceInfoInputBuffer(std::move(moduleSourceInfoInputBuffer)), DeserializedTypeCallback([](Type ty) {}) { assert(!hasError()); Bits.IsFramework = isFramework; PrettyStackTraceModuleFile 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 scratch; llvm::BitstreamEntry topLevelEntry; while (!cursor.AtEndOfStream()) { Expected 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; } 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; IsSIB = extInfo->isSIB(); 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 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 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 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 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 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; } 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; } } Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc, bool treatAsPartialModule) { PrettyStackTraceModuleFile stackEntry(*this); assert(!hasError() && "error already detected; should not call this"); assert(!FileContext && "already associated with an AST module"); FileContext = file; ModuleDecl *M = file->getParentModule(); if (M->getName().str() != Name) return error(Status::NameMismatch); ASTContext &ctx = getContext(); llvm::Triple moduleTarget(llvm::Triple::normalize(TargetTriple)); if (!areCompatibleArchitectures(moduleTarget, ctx.LangOpts.Target) || !areCompatibleOSs(moduleTarget, ctx.LangOpts.Target)) { return error(Status::TargetIncompatible); } if (ctx.LangOpts.EnableTargetOSChecking && !M->isResilient() && isTargetTooNew(moduleTarget, ctx.LangOpts.Target)) { return error(Status::TargetTooNew); } for (const auto &searchPath : SearchPaths) ctx.addSearchPath(searchPath.Path, searchPath.IsFramework, searchPath.IsSystem); auto clangImporter = static_cast(ctx.getClangModuleLoader()); bool missingDependency = false; for (auto &dependency : Dependencies) { assert(!dependency.isLoaded() && "already loaded?"); if (dependency.isHeader()) { // The path may be empty if the file being loaded is a partial AST, // and the current compiler invocation is a merge-modules step. if (!dependency.RawPath.empty()) { bool hadError = clangImporter->importHeader(dependency.RawPath, file->getParentModule(), importedHeaderInfo.fileSize, importedHeaderInfo.fileModTime, importedHeaderInfo.contents, diagLoc); if (hadError) return error(Status::FailedToLoadBridgingHeader); } ModuleDecl *importedHeaderModule = clangImporter->getImportedHeaderModule(); dependency.Import = { {}, importedHeaderModule }; continue; } if (dependency.isImplementationOnly() && !(treatAsPartialModule || ctx.LangOpts.DebuggerSupport)) { // When building normally (and not merging partial modules), we don't // want to bring in the implementation-only module, because that might // change the set of visible declarations. However, when debugging we // want to allow getting at the internals of this module when possible, // and so we'll try to reference the implementation-only module if it's // available. continue; } StringRef modulePathStr = dependency.RawPath; StringRef scopePath; if (dependency.isScoped()) { auto splitPoint = modulePathStr.find_last_of('\0'); assert(splitPoint != StringRef::npos); scopePath = modulePathStr.substr(splitPoint+1); modulePathStr = modulePathStr.slice(0, splitPoint); } SmallVector modulePath; while (!modulePathStr.empty()) { StringRef nextComponent; std::tie(nextComponent, modulePathStr) = modulePathStr.split('\0'); modulePath.push_back(ctx.getIdentifier(nextComponent)); assert(!modulePath.back().empty() && "invalid module name (submodules not yet supported)"); } auto module = getModule(modulePath, /*allowLoading*/true); if (!module || module->failedToLoad()) { // If we're missing the module we're an overlay for, treat that specially. if (modulePath.size() == 1 && modulePath.front() == file->getParentModule()->getName()) { return error(Status::MissingUnderlyingModule); } // Otherwise, continue trying to load dependencies, so that we can list // everything that's missing. if (!(dependency.isImplementationOnly() && ctx.LangOpts.DebuggerSupport)) missingDependency = true; continue; } if (scopePath.empty()) { dependency.Import = { {}, module }; } else { auto scopeID = ctx.getIdentifier(scopePath); assert(!scopeID.empty() && "invalid decl name (non-top-level decls not supported)"); Located accessPathElem = { scopeID, SourceLoc() }; dependency.Import = {ctx.AllocateCopy(llvm::makeArrayRef(accessPathElem)), module}; } // SPI StringRef spisStr = dependency.RawSPIs; while (!spisStr.empty()) { StringRef nextComponent; std::tie(nextComponent, spisStr) = spisStr.split('\0'); dependency.spiGroups.push_back(ctx.getIdentifier(nextComponent)); } if (!module->hasResolvedImports()) { // Notice that we check this condition /after/ recording the module that // caused the problem. Clients need to be able to track down what the // cycle was. return error(Status::CircularDependency); } } if (missingDependency) { return error(Status::MissingDependency); } if (Bits.HasEntryPoint) { FileContext->getParentModule()->registerEntryPointFile(FileContext, SourceLoc(), None); } return Status::Valid; } std::unique_ptr ModuleFile::takeBufferForDiagnostics() { assert(hasError()); // Today, the only buffer that might have diagnostics in them is the input // buffer, and even then only if it has imported module contents. if (!importedHeaderInfo.contents.empty()) return std::move(ModuleInputBuffer); return nullptr; } ModuleFile::~ModuleFile() { } void ModuleFile::lookupValue(DeclName name, SmallVectorImpl &results) { PrettyStackTraceModuleFile stackEntry(*this); if (TopLevelDecls) { // Find top-level declarations with the given name. // FIXME: As a bit of a hack, do lookup by the simple name, then filter // compound decls, to avoid having to completely redo how modules are // serialized. auto iter = TopLevelDecls->find(name.getBaseName()); if (iter != TopLevelDecls->end()) { for (auto item : *iter) { Expected declOrError = getDeclChecked(item.second); if (!declOrError) { if (!getContext().LangOpts.EnableDeserializationRecovery) fatal(declOrError.takeError()); llvm::consumeError(declOrError.takeError()); continue; } auto VD = cast(declOrError.get()); if (name.isSimpleName() || VD->getName().matchesRef(name)) results.push_back(VD); } } } // If the name is an operator name, also look for operator methods. if (name.isOperator() && OperatorMethodDecls) { auto iter = OperatorMethodDecls->find(name.getBaseName()); if (iter != OperatorMethodDecls->end()) { for (auto item : *iter) { Expected declOrError = getDeclChecked(item.second); if (!declOrError) { if (!getContext().LangOpts.EnableDeserializationRecovery) fatal(declOrError.takeError()); llvm::consumeError(declOrError.takeError()); continue; } auto VD = cast(declOrError.get()); results.push_back(VD); } } } } TypeDecl *ModuleFile::lookupLocalType(StringRef MangledName) { PrettyStackTraceModuleFile stackEntry(*this); if (!LocalTypeDecls) return nullptr; auto iter = LocalTypeDecls->find(MangledName); if (iter == LocalTypeDecls->end()) return nullptr; return cast(getDecl(*iter)); } OpaqueTypeDecl *ModuleFile::lookupOpaqueResultType(StringRef MangledName) { PrettyStackTraceModuleFile stackEntry(*this); if (!OpaqueReturnTypeDecls) return nullptr; auto iter = OpaqueReturnTypeDecls->find(MangledName); if (iter == OpaqueReturnTypeDecls->end()) return nullptr; return cast(getDecl(*iter)); } TypeDecl *ModuleFile::lookupNestedType(Identifier name, const NominalTypeDecl *parent) { PrettyStackTraceModuleFile stackEntry(*this); if (NestedTypeDecls) { auto iter = NestedTypeDecls->find(name); if (iter != NestedTypeDecls->end()) { for (std::pair entry : *iter) { assert(entry.first); auto declOrOffset = Decls[entry.first - 1]; if (!declOrOffset.isComplete()) continue; Decl *decl = declOrOffset; if (decl != parent) continue; return cast(getDecl(entry.second)); } } } if (!UnderlyingModule) return nullptr; for (FileUnit *file : UnderlyingModule->getFiles()) if (auto *nestedType = file->lookupNestedType(name, parent)) return nestedType; return nullptr; } OperatorDecl *ModuleFile::lookupOperator(Identifier name, OperatorFixity fixity) { PrettyStackTraceModuleFile stackEntry(*this); if (!OperatorDecls) return nullptr; auto iter = OperatorDecls->find(name); if (iter == OperatorDecls->end()) return nullptr; for (auto item : *iter) { if (getStableFixity(fixity) == item.first) return cast(getDecl(item.second)); } // FIXME: operators re-exported from other modules? return nullptr; } PrecedenceGroupDecl *ModuleFile::lookupPrecedenceGroup(Identifier name) { PrettyStackTraceModuleFile stackEntry(*this); if (!PrecedenceGroupDecls) return nullptr; auto iter = PrecedenceGroupDecls->find(name); if (iter == PrecedenceGroupDecls->end()) return nullptr; auto data = *iter; assert(data.size() == 1); return cast(getDecl(data[0].second)); } void ModuleFile::getImportedModules( SmallVectorImpl &results, ModuleDecl::ImportFilter filter) { PrettyStackTraceModuleFile stackEntry(*this); for (auto &dep : Dependencies) { if (dep.isExported()) { if (!filter.contains(ModuleDecl::ImportFilterKind::Public)) continue; } else if (dep.isImplementationOnly()) { if (!filter.contains(ModuleDecl::ImportFilterKind::ImplementationOnly)) continue; if (!dep.isLoaded()) { // Pretend we didn't have this import if we weren't originally asked to // load it. continue; } } else { if (!filter.contains(ModuleDecl::ImportFilterKind::Private)) continue; } assert(dep.isLoaded()); results.push_back(dep.Import); } } void ModuleFile::getImportDecls(SmallVectorImpl &Results) { if (!Bits.ComputedImportDecls) { ASTContext &Ctx = getContext(); for (auto &Dep : Dependencies) { // FIXME: We need a better way to show headers, since they usually /are/ // re-exported. This isn't likely to come up much, though. if (Dep.isHeader()) continue; StringRef ModulePathStr = Dep.RawPath; StringRef ScopePath; if (Dep.isScoped()) std::tie(ModulePathStr, ScopePath) = ModulePathStr.rsplit('\0'); SmallVector, 1> AccessPath; while (!ModulePathStr.empty()) { StringRef NextComponent; std::tie(NextComponent, ModulePathStr) = ModulePathStr.split('\0'); AccessPath.push_back({Ctx.getIdentifier(NextComponent), SourceLoc()}); } if (AccessPath.size() == 1 && AccessPath[0].Item == Ctx.StdlibModuleName) continue; ModuleDecl *M = Ctx.getLoadedModule(AccessPath); auto Kind = ImportKind::Module; if (!ScopePath.empty()) { auto ScopeID = Ctx.getIdentifier(ScopePath); assert(!ScopeID.empty() && "invalid decl name (non-top-level decls not supported)"); if (!M) { // The dependency module could not be loaded. Just make a guess // about the import kind, we cannot do better. Kind = ImportKind::Func; } else { // Lookup the decl in the top-level module. ModuleDecl *TopLevelModule = M; if (AccessPath.size() > 1) TopLevelModule = Ctx.getLoadedModule(AccessPath.front().Item); SmallVector Decls; TopLevelModule->lookupQualified( TopLevelModule, DeclNameRef(ScopeID), NL_QualifiedDefault | NL_KnownNoDependency, Decls); Optional FoundKind = ImportDecl::findBestImportKind(Decls); assert(FoundKind.hasValue() && "deserialized imports should not be ambiguous"); Kind = *FoundKind; } AccessPath.push_back({ ScopeID, SourceLoc() }); } auto *ID = ImportDecl::create(Ctx, FileContext, SourceLoc(), Kind, SourceLoc(), AccessPath); ID->setModule(M); if (Dep.isExported()) ID->getAttrs().add( new (Ctx) ExportedAttr(/*IsImplicit=*/false)); ImportDecls.push_back(ID); } Bits.ComputedImportDecls = true; } Results.append(ImportDecls.begin(), ImportDecls.end()); } void ModuleFile::lookupVisibleDecls(ModuleDecl::AccessPathTy accessPath, VisibleDeclConsumer &consumer, NLKind lookupKind) { PrettyStackTraceModuleFile stackEntry(*this); assert(accessPath.size() <= 1 && "can only refer to top-level decls"); if (!TopLevelDecls) return; auto tryImport = [this, &consumer](DeclID ID) { Expected declOrError = getDeclChecked(ID); if (!declOrError) { if (!getContext().LangOpts.EnableDeserializationRecovery) fatal(declOrError.takeError()); llvm::consumeError(declOrError.takeError()); return; } consumer.foundDecl(cast(declOrError.get()), DeclVisibilityKind::VisibleAtTopLevel); }; if (!accessPath.empty()) { auto iter = TopLevelDecls->find(accessPath.front().Item); if (iter == TopLevelDecls->end()) return; for (auto item : *iter) tryImport(item.second); return; } for (auto entry : TopLevelDecls->data()) { for (auto item : entry) tryImport(item.second); } } void ModuleFile::loadExtensions(NominalTypeDecl *nominal) { PrettyStackTraceModuleFile stackEntry(*this); if (!ExtensionDecls) return; auto iter = ExtensionDecls->find(nominal->getName()); if (iter == ExtensionDecls->end()) return; if (nominal->getEffectiveAccess() < AccessLevel::Internal) { if (nominal->getModuleScopeContext() != getFile()) return; } if (nominal->getParent()->isModuleScopeContext()) { auto parentFile = cast(nominal->getParent()); StringRef moduleName = parentFile->getExportedModuleName(); for (auto item : *iter) { if (item.first != moduleName) continue; Expected declOrError = getDeclChecked(item.second); if (!declOrError) { if (!getContext().LangOpts.EnableDeserializationRecovery) fatal(declOrError.takeError()); llvm::consumeError(declOrError.takeError()); } } } else { std::string mangledName = Mangle::ASTMangler().mangleNominalType(nominal); for (auto item : *iter) { if (item.first != mangledName) continue; Expected declOrError = getDeclChecked(item.second); if (!declOrError) { if (!getContext().LangOpts.EnableDeserializationRecovery) fatal(declOrError.takeError()); llvm::consumeError(declOrError.takeError()); } } } } void ModuleFile::loadObjCMethods( ClassDecl *classDecl, ObjCSelector selector, bool isInstanceMethod, llvm::TinyPtrVector &methods) { // If we don't have an Objective-C method table, there's nothing to do. if (!ObjCMethods) return; // Look for all methods in the module file with this selector. auto known = ObjCMethods->find(selector); if (known == ObjCMethods->end()) { return; } std::string ownerName = Mangle::ASTMangler().mangleNominalType(classDecl); auto results = *known; for (const auto &result : results) { // If the method is the wrong kind (instance vs. class), skip it. if (isInstanceMethod != std::get<1>(result)) continue; // If the method isn't defined in the requested class, skip it. if (std::get<0>(result) != ownerName) continue; // Deserialize the method and add it to the list. if (auto func = dyn_cast_or_null( getDecl(std::get<2>(result)))) { methods.push_back(func); } } } void ModuleFile::loadDerivativeFunctionConfigurations( AbstractFunctionDecl *originalAFD, llvm::SetVector &results) { if (!DerivativeFunctionConfigurations) return; auto &ctx = originalAFD->getASTContext(); Mangle::ASTMangler Mangler; auto mangledName = Mangler.mangleDeclAsUSR(originalAFD, ""); auto configs = DerivativeFunctionConfigurations->find(mangledName); if (configs == DerivativeFunctionConfigurations->end()) return; for (auto entry : *configs) { auto *parameterIndices = IndexSubset::getFromString(ctx, entry.first); auto derivativeGenSigOrError = getGenericSignatureChecked(entry.second); if (!derivativeGenSigOrError) { if (!getContext().LangOpts.EnableDeserializationRecovery) fatal(derivativeGenSigOrError.takeError()); llvm::consumeError(derivativeGenSigOrError.takeError()); } auto derivativeGenSig = derivativeGenSigOrError.get(); // NOTE(TF-1038): Result indices are currently unsupported in derivative // registration attributes. In the meantime, always use `{0}` (wrt the // first and only result). auto resultIndices = IndexSubset::get(ctx, 1, {0}); results.insert({parameterIndices, resultIndices, derivativeGenSig}); } } TinyPtrVector ModuleFile::loadNamedMembers(const IterableDeclContext *IDC, DeclBaseName N, uint64_t contextData) { PrettyStackTraceDecl trace("loading members for", IDC->getDecl()); assert(IDC->wasDeserialized()); assert(DeclMemberNames); TinyPtrVector results; auto i = DeclMemberNames->find(N); if (i == DeclMemberNames->end()) return results; BitOffset subTableOffset = *i; std::unique_ptr &subTable = DeclMembersTables[subTableOffset]; if (!subTable) { BCOffsetRAII restoreOffset(DeclMemberTablesCursor); fatalIfNotSuccess(DeclMemberTablesCursor.JumpToBit(subTableOffset)); llvm::BitstreamEntry entry = fatalIfUnexpected(DeclMemberTablesCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) { fatal(); return results; } SmallVector scratch; StringRef blobData; unsigned kind = fatalIfUnexpected( DeclMemberTablesCursor.readRecord(entry.ID, scratch, &blobData)); assert(kind == decl_member_tables_block::DECL_MEMBERS); (void)kind; subTable = readDeclMembersTable(scratch, blobData); } assert(subTable); auto j = subTable->find(IDC->getDeclID()); if (j != subTable->end()) { for (DeclID d : *j) { Expected mem = getDeclChecked(d); if (mem) { assert(mem.get() && "unchecked error deserializing named member"); if (auto MVD = dyn_cast(mem.get())) { results.push_back(MVD); } } else { if (!getContext().LangOpts.EnableDeserializationRecovery) fatal(mem.takeError()); consumeError(mem.takeError()); } } } return results; } void ModuleFile::lookupClassMember(ModuleDecl::AccessPathTy accessPath, DeclName name, SmallVectorImpl &results) { PrettyStackTraceModuleFile stackEntry(*this); assert(accessPath.size() <= 1 && "can only refer to top-level decls"); if (!ClassMembersForDynamicLookup) return; auto iter = ClassMembersForDynamicLookup->find(name.getBaseName()); if (iter == ClassMembersForDynamicLookup->end()) return; if (!accessPath.empty()) { // As a hack to avoid completely redoing how the module is indexed, we take // the simple-name-based lookup then filter by the compound name if we have // one. if (name.isSimpleName()) { for (auto item : *iter) { auto vd = cast(getDecl(item.second)); auto dc = vd->getDeclContext(); while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) if (nominal->getName() == accessPath.front().Item) results.push_back(vd); } } else { for (auto item : *iter) { auto vd = cast(getDecl(item.second)); if (!vd->getName().matchesRef(name)) continue; auto dc = vd->getDeclContext(); while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) if (nominal->getName() == accessPath.front().Item) results.push_back(vd); } } return; } for (auto item : *iter) { auto vd = cast(getDecl(item.second)); results.push_back(vd); } } void ModuleFile::lookupClassMembers(ModuleDecl::AccessPathTy accessPath, VisibleDeclConsumer &consumer) { PrettyStackTraceModuleFile stackEntry(*this); assert(accessPath.size() <= 1 && "can only refer to top-level decls"); if (!ClassMembersForDynamicLookup) return; if (!accessPath.empty()) { for (const auto &list : ClassMembersForDynamicLookup->data()) { for (auto item : list) { auto vd = cast(getDecl(item.second)); auto dc = vd->getDeclContext(); while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) if (nominal->getName() == accessPath.front().Item) consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, DynamicLookupInfo::AnyObject); } } return; } for (const auto &list : ClassMembersForDynamicLookup->data()) { for (auto item : list) consumer.foundDecl(cast(getDecl(item.second)), DeclVisibilityKind::DynamicLookup, DynamicLookupInfo::AnyObject); } } void ModuleFile::lookupObjCMethods( ObjCSelector selector, SmallVectorImpl &results) { // If we don't have an Objective-C method table, there's nothing to do. if (!ObjCMethods) return; // Look for all methods in the module file with this selector. auto known = ObjCMethods->find(selector); if (known == ObjCMethods->end()) return; auto found = *known; for (const auto &result : found) { // Deserialize the method and add it to the list. if (auto func = dyn_cast_or_null( getDecl(std::get<2>(result)))) results.push_back(func); } } void ModuleFile::lookupImportedSPIGroups(const ModuleDecl *importedModule, SmallVectorImpl &spiGroups) const { for (auto &dep : Dependencies) { auto depSpis = dep.spiGroups; if (dep.Import.second == importedModule && !depSpis.empty()) { spiGroups.append(depSpis.begin(), depSpis.end()); } } } void ModuleFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const { for (auto &lib : LinkLibraries) callback(lib); if (Bits.IsFramework) callback(LinkLibrary(Name, LibraryKind::Framework)); } void ModuleFile::getTopLevelDecls( SmallVectorImpl &results, llvm::function_ref matchAttributes) { PrettyStackTraceModuleFile stackEntry(*this); for (DeclID entry : OrderedTopLevelDecls) { Expected declOrError = getDeclChecked(entry, matchAttributes); if (!declOrError) { if (declOrError.errorIsA()) { // Decl rejected by matchAttributes, ignore it. assert(matchAttributes); consumeError(declOrError.takeError()); continue; } if (!getContext().LangOpts.EnableDeserializationRecovery) fatal(declOrError.takeError()); consumeError(declOrError.takeError()); continue; } results.push_back(declOrError.get()); } } void ModuleFile::getOperatorDecls(SmallVectorImpl &results) { PrettyStackTraceModuleFile stackEntry(*this); if (!OperatorDecls) return; for (auto entry : OperatorDecls->data()) { for (auto item : entry) results.push_back(cast(getDecl(item.second))); } } void ModuleFile::getPrecedenceGroups( SmallVectorImpl &results) { PrettyStackTraceModuleFile stackEntry(*this); if (PrecedenceGroupDecls) { for (auto entry : PrecedenceGroupDecls->data()) { for (auto item : entry) results.push_back(cast(getDecl(item.second))); } } } void ModuleFile::getLocalTypeDecls(SmallVectorImpl &results) { PrettyStackTraceModuleFile stackEntry(*this); if (!LocalTypeDecls) return; for (auto DeclID : LocalTypeDecls->data()) { auto TD = cast(getDecl(DeclID)); results.push_back(TD); } } void ModuleFile::getOpaqueReturnTypeDecls(SmallVectorImpl &results) { PrettyStackTraceModuleFile stackEntry(*this); if (!OpaqueReturnTypeDecls) return; for (auto DeclID : OpaqueReturnTypeDecls->data()) { auto TD = cast(getDecl(DeclID)); results.push_back(TD); } } void ModuleFile::getDisplayDecls(SmallVectorImpl &results) { if (UnderlyingModule) UnderlyingModule->getDisplayDecls(results); PrettyStackTraceModuleFile stackEntry(*this); getImportDecls(results); getTopLevelDecls(results); } Optional ModuleFile::getCommentForDecl(const Decl *D) const { assert(D); // Keep these as assertions instead of early exits to ensure that we are not // doing extra work. These cases should be handled by clients of this API. assert(!D->hasClangNode() && "cannot find comments for Clang decls in Swift modules"); assert(D->getDeclContext()->getModuleScopeContext() == FileContext && "Decl is from a different serialized file"); if (!DeclCommentTable) return None; if (D->isImplicit()) return None; // Compute the USR. llvm::SmallString<128> USRBuffer; llvm::raw_svector_ostream OS(USRBuffer); if (ide::printDeclUSR(D, OS)) return None; return getCommentForDeclByUSR(USRBuffer.str()); } Optional ModuleFile::getBasicDeclLocsForDecl(const Decl *D) const { assert(D); // Keep these as assertions instead of early exits to ensure that we are not // doing extra work. These cases should be handled by clients of this API. assert(!D->hasClangNode() && "cannot find comments for Clang decls in Swift modules"); assert(D->getDeclContext()->getModuleScopeContext() == FileContext && "Decl is from a different serialized file"); if (!DeclUSRsTable) return None; // Future compilers may not provide BasicDeclLocsData anymore. if (BasicDeclLocsData.empty()) return None; if (D->isImplicit()) return None; // Compute the USR. llvm::SmallString<128> USRBuffer; llvm::raw_svector_ostream OS(USRBuffer); if (ide::printDeclUSR(D, OS)) return None; auto It = DeclUSRsTable->find(OS.str()); if (It == DeclUSRsTable->end()) return None; auto UsrId = *It; uint32_t NumSize = 4; // Size of BasicDeclLocs in the buffer. // FilePathOffset + LocNum * LineColumn uint32_t LineColumnCount = 3; uint32_t RecordSize = NumSize + // Offset into source filename blob NumSize + // Offset into doc ranges blob NumSize * 2 * LineColumnCount; // Line/column of: Loc, StartLoc, EndLoc uint32_t RecordOffset = RecordSize * UsrId; assert(RecordOffset < BasicDeclLocsData.size()); assert(BasicDeclLocsData.size() % RecordSize == 0); BasicDeclLocs Result; auto *Record = BasicDeclLocsData.data() + RecordOffset; auto ReadNext = [&Record]() { return endian::readNext(Record); }; auto FilePath = SourceLocsTextData.substr(ReadNext()); size_t TerminatorOffset = FilePath.find('\0'); assert(TerminatorOffset != StringRef::npos && "unterminated string data"); Result.SourceFilePath = FilePath.slice(0, TerminatorOffset); const auto DocRangesOffset = ReadNext(); if (DocRangesOffset) { assert(!DocRangesData.empty()); const auto *Data = DocRangesData.data() + DocRangesOffset; const auto NumLocs = endian::readNext(Data); assert(NumLocs); for (uint32_t i = 0; i < NumLocs; ++i) { LineColumn LC; LC.Line = endian::readNext(Data); LC.Column = endian::readNext(Data); auto Length = endian::readNext(Data); Result.DocRanges.push_back(std::make_pair(LC, Length)); } } #define READ_FIELD(X) \ Result.X.Line = ReadNext(); \ Result.X.Column = ReadNext(); READ_FIELD(Loc) READ_FIELD(StartLoc) READ_FIELD(EndLoc) #undef READ_FIELD return Result; } const static StringRef Separator = "/"; Optional ModuleFile::getGroupNameById(unsigned Id) const { if (!GroupNamesMap || GroupNamesMap->count(Id) == 0) return None; auto Original = (*GroupNamesMap)[Id]; if (Original.empty()) return None; auto SepPos = Original.find_last_of(Separator); assert(SepPos != StringRef::npos && "Cannot find Separator."); return StringRef(Original.data(), SepPos); } Optional ModuleFile::getSourceFileNameById(unsigned Id) const { if (!GroupNamesMap || GroupNamesMap->count(Id) == 0) return None; auto Original = (*GroupNamesMap)[Id]; if (Original.empty()) return None; auto SepPos = Original.find_last_of(Separator); assert(SepPos != StringRef::npos && "Cannot find Separator."); auto Start = Original.data() + SepPos + 1; auto Len = Original.size() - SepPos - 1; return StringRef(Start, Len); } Optional ModuleFile::getGroupNameForDecl(const Decl *D) const { auto Triple = getCommentForDecl(D); if (!Triple.hasValue()) { return None; } return getGroupNameById(Triple.getValue().Group); } Optional ModuleFile::getSourceFileNameForDecl(const Decl *D) const { auto Triple = getCommentForDecl(D); if (!Triple.hasValue()) { return None; } return getSourceFileNameById(Triple.getValue().Group); } Optional ModuleFile::getSourceOrderForDecl(const Decl *D) const { auto Triple = getCommentForDecl(D); if (!Triple.hasValue()) { return None; } return Triple.getValue().SourceOrder; } void ModuleFile::collectAllGroups(std::vector &Names) const { if (!GroupNamesMap) return; for (auto It = GroupNamesMap->begin(); It != GroupNamesMap->end(); ++ It) { StringRef FullGroupName = It->getSecond(); if (FullGroupName.empty()) continue; auto Sep = FullGroupName.find_last_of(Separator); assert(Sep != StringRef::npos); auto Group = FullGroupName.substr(0, Sep); auto Found = std::find(Names.begin(), Names.end(), Group); if (Found != Names.end()) continue; Names.push_back(Group); } } Optional ModuleFile::getCommentForDeclByUSR(StringRef USR) const { if (!DeclCommentTable) return None; auto I = DeclCommentTable->find(USR); if (I == DeclCommentTable->end()) return None; return *I; } Optional ModuleFile::getGroupNameByUSR(StringRef USR) const { if (auto Comment = getCommentForDeclByUSR(USR)) { return getGroupNameById(Comment.getValue().Group); } return None; } Identifier ModuleFile::getDiscriminatorForPrivateValue(const ValueDecl *D) { Identifier discriminator = PrivateDiscriminatorsByValue.lookup(D); assert(!discriminator.empty() && "no discriminator found for decl"); return discriminator; } void ModuleFile::verify() const { #ifndef NDEBUG const auto &Context = getContext(); for (const Serialized &next : Decls) if (next.isComplete() && swift::shouldVerify(next, Context)) swift::verify(next); #endif } bool SerializedASTFile::hasEntryPoint() const { return File.Bits.HasEntryPoint; } bool SerializedASTFile::getAllGenericSignatures( SmallVectorImpl &genericSignatures) { genericSignatures.clear(); for (unsigned index : indices(File.GenericSignatures)) { if (auto genericSig = File.getGenericSignature(index + 1)) genericSignatures.push_back(genericSig); } return true; } Decl *SerializedASTFile::getMainDecl() const { assert(hasEntryPoint()); return File.getDecl(File.Bits.EntryPointDeclID); } const version::Version &SerializedASTFile::getLanguageVersionBuiltWith() const { return File.CompatibilityVersion; } StringRef SerializedASTFile::getModuleDefiningPath() const { StringRef moduleFilename = getFilename(); StringRef parentDir = llvm::sys::path::parent_path(moduleFilename); if (llvm::sys::path::extension(parentDir) == ".swiftmodule") return parentDir; return moduleFilename; }