//===--- APINotesReader.cpp - API Notes Reader ------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/APINotes/APINotesReader.h" #include "APINotesFormat.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Bitstream/BitstreamReader.h" #include "llvm/Support/DJB.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/OnDiskHashTable.h" namespace clang { namespace api_notes { using namespace llvm::support; namespace { /// Deserialize a version tuple. llvm::VersionTuple ReadVersionTuple(const uint8_t *&Data) { uint8_t NumVersions = (*Data++) & 0x03; unsigned Major = endian::readNext(Data); if (NumVersions == 0) return llvm::VersionTuple(Major); unsigned Minor = endian::readNext(Data); if (NumVersions == 1) return llvm::VersionTuple(Major, Minor); unsigned Subminor = endian::readNext(Data); if (NumVersions == 2) return llvm::VersionTuple(Major, Minor, Subminor); unsigned Build = endian::readNext(Data); return llvm::VersionTuple(Major, Minor, Subminor, Build); } /// An on-disk hash table whose data is versioned based on the Swift version. template class VersionedTableInfo { public: using internal_key_type = KeyType; using external_key_type = KeyType; using data_type = llvm::SmallVector, 1>; using hash_value_type = size_t; using offset_type = unsigned; internal_key_type GetInternalKey(external_key_type Key) { return Key; } external_key_type GetExternalKey(internal_key_type Key) { return Key; } 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 data_type ReadData(internal_key_type Key, const uint8_t *Data, unsigned Length) { unsigned NumElements = endian::readNext(Data); data_type Result; Result.reserve(NumElements); for (unsigned i = 0; i != NumElements; ++i) { auto version = ReadVersionTuple(Data); const auto *DataBefore = Data; (void)DataBefore; assert(Data != DataBefore && "Unversioned data reader didn't move pointer"); auto UnversionedData = Derived::readUnversioned(Key, Data); Result.push_back({version, UnversionedData}); } return Result; } }; /// Read serialized CommonEntityInfo. void ReadCommonEntityInfo(const uint8_t *&Data, CommonEntityInfo &Info) { uint8_t UnavailableBits = *Data++; Info.Unavailable = (UnavailableBits >> 1) & 0x01; Info.UnavailableInSwift = UnavailableBits & 0x01; if ((UnavailableBits >> 2) & 0x01) Info.setSwiftPrivate(static_cast((UnavailableBits >> 3) & 0x01)); unsigned MsgLength = endian::readNext(Data); Info.UnavailableMsg = std::string(reinterpret_cast(Data), reinterpret_cast(Data) + MsgLength); Data += MsgLength; unsigned SwiftNameLength = endian::readNext(Data); Info.SwiftName = std::string(reinterpret_cast(Data), reinterpret_cast(Data) + SwiftNameLength); Data += SwiftNameLength; } /// Read serialized CommonTypeInfo. void ReadCommonTypeInfo(const uint8_t *&Data, CommonTypeInfo &Info) { ReadCommonEntityInfo(Data, Info); unsigned SwiftBridgeLength = endian::readNext(Data); if (SwiftBridgeLength > 0) { Info.setSwiftBridge(std::string(reinterpret_cast(Data), SwiftBridgeLength - 1)); Data += SwiftBridgeLength - 1; } unsigned ErrorDomainLength = endian::readNext(Data); if (ErrorDomainLength > 0) { Info.setNSErrorDomain(std::optional(std::string( reinterpret_cast(Data), ErrorDomainLength - 1))); Data += ErrorDomainLength - 1; } } /// Used to deserialize the on-disk identifier table. class IdentifierTableInfo { public: using internal_key_type = llvm::StringRef; using external_key_type = llvm::StringRef; using data_type = IdentifierID; using hash_value_type = uint32_t; using offset_type = unsigned; internal_key_type GetInternalKey(external_key_type Key) { return Key; } external_key_type GetExternalKey(internal_key_type Key) { return Key; } 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 KeyLength = endian::readNext(Data); unsigned DataLength = endian::readNext(Data); return {KeyLength, DataLength}; } static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { return llvm::StringRef(reinterpret_cast(Data), Length); } static data_type ReadData(internal_key_type key, const uint8_t *Data, unsigned Length) { return endian::readNext( Data); } }; /// Used to deserialize the on-disk Objective-C class table. class ObjCContextIDTableInfo { public: using internal_key_type = ContextTableKey; using external_key_type = internal_key_type; using data_type = unsigned; using hash_value_type = size_t; using offset_type = unsigned; internal_key_type GetInternalKey(external_key_type Key) { return Key; } external_key_type GetExternalKey(internal_key_type Key) { return Key; } hash_value_type ComputeHash(internal_key_type Key) { return static_cast(Key.hashValue()); } 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) { auto ParentCtxID = endian::readNext(Data); auto ContextKind = endian::readNext(Data); auto NameID = endian::readNext(Data); return {ParentCtxID, ContextKind, NameID}; } static data_type ReadData(internal_key_type Key, const uint8_t *Data, unsigned Length) { return endian::readNext( Data); } }; /// Used to deserialize the on-disk Objective-C property table. class ObjCContextInfoTableInfo : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { return endian::readNext( Data); } hash_value_type ComputeHash(internal_key_type Key) { return static_cast(llvm::hash_value(Key)); } static ObjCContextInfo readUnversioned(internal_key_type Key, const uint8_t *&Data) { ObjCContextInfo Info; ReadCommonTypeInfo(Data, Info); uint8_t Payload = *Data++; if (Payload & 0x01) Info.setHasDesignatedInits(true); Payload = Payload >> 1; if (Payload & 0x4) Info.setDefaultNullability(static_cast(Payload & 0x03)); Payload >>= 3; if (Payload & (1 << 1)) Info.setSwiftObjCMembers(Payload & 1); Payload >>= 2; if (Payload & (1 << 1)) Info.setSwiftImportAsNonGeneric(Payload & 1); return Info; } }; /// Read serialized VariableInfo. void ReadVariableInfo(const uint8_t *&Data, VariableInfo &Info) { ReadCommonEntityInfo(Data, Info); if (*Data++) { Info.setNullabilityAudited(static_cast(*Data)); } ++Data; auto TypeLen = endian::readNext(Data); Info.setType(std::string(Data, Data + TypeLen)); Data += TypeLen; } /// Used to deserialize the on-disk Objective-C property table. class ObjCPropertyTableInfo : public VersionedTableInfo, ObjCPropertyInfo> { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto ClassID = endian::readNext(Data); auto NameID = endian::readNext(Data); char IsInstance = endian::readNext(Data); return {ClassID, NameID, IsInstance}; } hash_value_type ComputeHash(internal_key_type Key) { return static_cast(llvm::hash_value(Key)); } static ObjCPropertyInfo readUnversioned(internal_key_type Key, const uint8_t *&Data) { ObjCPropertyInfo Info; ReadVariableInfo(Data, Info); uint8_t Flags = *Data++; if (Flags & (1 << 0)) Info.setSwiftImportAsAccessors(Flags & (1 << 1)); return Info; } }; /// Read serialized ParamInfo. void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) { ReadVariableInfo(Data, Info); uint8_t Payload = endian::readNext(Data); if (auto RawConvention = Payload & 0x7) { auto Convention = static_cast(RawConvention - 1); Info.setRetainCountConvention(Convention); } Payload >>= 3; if (Payload & 0x01) Info.setNoEscape(Payload & 0x02); Payload >>= 2; assert(Payload == 0 && "Bad API notes"); } /// Read serialized FunctionInfo. void ReadFunctionInfo(const uint8_t *&Data, FunctionInfo &Info) { ReadCommonEntityInfo(Data, Info); uint8_t Payload = endian::readNext(Data); if (auto RawConvention = Payload & 0x7) { auto Convention = static_cast(RawConvention - 1); Info.setRetainCountConvention(Convention); } Payload >>= 3; Info.NullabilityAudited = Payload & 0x1; Payload >>= 1; assert(Payload == 0 && "Bad API notes"); Info.NumAdjustedNullable = endian::readNext(Data); Info.NullabilityPayload = endian::readNext(Data); unsigned NumParams = endian::readNext(Data); while (NumParams > 0) { ParamInfo pi; ReadParamInfo(Data, pi); Info.Params.push_back(pi); --NumParams; } unsigned ResultTypeLen = endian::readNext(Data); Info.ResultType = std::string(Data, Data + ResultTypeLen); Data += ResultTypeLen; } /// Used to deserialize the on-disk Objective-C method table. class ObjCMethodTableInfo : public VersionedTableInfo, ObjCMethodInfo> { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto ClassID = endian::readNext(Data); auto SelectorID = endian::readNext(Data); auto IsInstance = endian::readNext(Data); return {ClassID, SelectorID, IsInstance}; } hash_value_type ComputeHash(internal_key_type Key) { return static_cast(llvm::hash_value(Key)); } static ObjCMethodInfo readUnversioned(internal_key_type Key, const uint8_t *&Data) { ObjCMethodInfo Info; uint8_t Payload = *Data++; Info.RequiredInit = Payload & 0x01; Payload >>= 1; Info.DesignatedInit = Payload & 0x01; Payload >>= 1; ReadFunctionInfo(Data, Info); return Info; } }; /// Used to deserialize the on-disk Objective-C selector table. class ObjCSelectorTableInfo { public: using internal_key_type = StoredObjCSelector; using external_key_type = internal_key_type; using data_type = SelectorID; using hash_value_type = unsigned; using offset_type = unsigned; internal_key_type GetInternalKey(external_key_type Key) { return Key; } external_key_type GetExternalKey(internal_key_type Key) { return Key; } hash_value_type ComputeHash(internal_key_type Key) { return llvm::DenseMapInfo::getHashValue(Key); } static bool EqualKey(internal_key_type LHS, internal_key_type RHS) { return llvm::DenseMapInfo::isEqual(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) { internal_key_type Key; Key.NumArgs = endian::readNext(Data); unsigned NumIdents = (Length - sizeof(uint16_t)) / sizeof(uint32_t); for (unsigned i = 0; i != NumIdents; ++i) { Key.Identifiers.push_back( endian::readNext( Data)); } return Key; } static data_type ReadData(internal_key_type Key, const uint8_t *Data, unsigned Length) { return endian::readNext( Data); } }; /// Used to deserialize the on-disk global variable table. class GlobalVariableTableInfo : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto CtxID = endian::readNext(Data); auto ContextKind = endian::readNext(Data); auto NameID = endian::readNext(Data); return {CtxID, ContextKind, NameID}; } hash_value_type ComputeHash(internal_key_type Key) { return static_cast(Key.hashValue()); } static GlobalVariableInfo readUnversioned(internal_key_type Key, const uint8_t *&Data) { GlobalVariableInfo Info; ReadVariableInfo(Data, Info); return Info; } }; /// Used to deserialize the on-disk global function table. class GlobalFunctionTableInfo : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto CtxID = endian::readNext(Data); auto ContextKind = endian::readNext(Data); auto NameID = endian::readNext(Data); return {CtxID, ContextKind, NameID}; } hash_value_type ComputeHash(internal_key_type Key) { return static_cast(Key.hashValue()); } static GlobalFunctionInfo readUnversioned(internal_key_type Key, const uint8_t *&Data) { GlobalFunctionInfo Info; ReadFunctionInfo(Data, Info); return Info; } }; /// Used to deserialize the on-disk enumerator table. class EnumConstantTableInfo : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto NameID = endian::readNext(Data); return NameID; } hash_value_type ComputeHash(internal_key_type Key) { return static_cast(llvm::hash_value(Key)); } static EnumConstantInfo readUnversioned(internal_key_type Key, const uint8_t *&Data) { EnumConstantInfo Info; ReadCommonEntityInfo(Data, Info); return Info; } }; /// Used to deserialize the on-disk tag table. class TagTableInfo : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto CtxID = endian::readNext(Data); auto ContextKind = endian::readNext(Data); auto NameID = endian::readNext( Data); return {CtxID, ContextKind, NameID}; } hash_value_type ComputeHash(internal_key_type Key) { return static_cast(Key.hashValue()); } static TagInfo readUnversioned(internal_key_type Key, const uint8_t *&Data) { TagInfo Info; uint8_t Payload = *Data++; if (Payload & 1) Info.setFlagEnum(Payload & 2); Payload >>= 2; if (Payload > 0) Info.EnumExtensibility = static_cast((Payload & 0x3) - 1); unsigned ImportAsLength = endian::readNext(Data); if (ImportAsLength > 0) { Info.SwiftImportAs = std::string(reinterpret_cast(Data), ImportAsLength - 1); Data += ImportAsLength - 1; } unsigned RetainOpLength = endian::readNext(Data); if (RetainOpLength > 0) { Info.SwiftRetainOp = std::string(reinterpret_cast(Data), RetainOpLength - 1); Data += RetainOpLength - 1; } unsigned ReleaseOpLength = endian::readNext(Data); if (ReleaseOpLength > 0) { Info.SwiftReleaseOp = std::string(reinterpret_cast(Data), ReleaseOpLength - 1); Data += ReleaseOpLength - 1; } ReadCommonTypeInfo(Data, Info); return Info; } }; /// Used to deserialize the on-disk typedef table. class TypedefTableInfo : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { auto CtxID = endian::readNext(Data); auto ContextKind = endian::readNext(Data); auto nameID = endian::readNext( Data); return {CtxID, ContextKind, nameID}; } hash_value_type ComputeHash(internal_key_type Key) { return static_cast(Key.hashValue()); } static TypedefInfo readUnversioned(internal_key_type Key, const uint8_t *&Data) { TypedefInfo Info; uint8_t Payload = *Data++; if (Payload > 0) Info.SwiftWrapper = static_cast((Payload & 0x3) - 1); ReadCommonTypeInfo(Data, Info); return Info; } }; } // end anonymous namespace class APINotesReader::Implementation { public: /// The input buffer for the API notes data. llvm::MemoryBuffer *InputBuffer; /// The Swift version to use for filtering. llvm::VersionTuple SwiftVersion; /// The name of the module that we read from the control block. std::string ModuleName; // The size and modification time of the source file from // which this API notes file was created, if known. std::optional> SourceFileSizeAndModTime; using SerializedIdentifierTable = llvm::OnDiskIterableChainedHashTable; /// The identifier table. std::unique_ptr IdentifierTable; using SerializedObjCContextIDTable = llvm::OnDiskIterableChainedHashTable; /// The Objective-C context ID table. std::unique_ptr ObjCContextIDTable; using SerializedObjCContextInfoTable = llvm::OnDiskIterableChainedHashTable; /// The Objective-C context info table. std::unique_ptr ObjCContextInfoTable; using SerializedObjCPropertyTable = llvm::OnDiskIterableChainedHashTable; /// The Objective-C property table. std::unique_ptr ObjCPropertyTable; using SerializedObjCMethodTable = llvm::OnDiskIterableChainedHashTable; /// The Objective-C method table. std::unique_ptr ObjCMethodTable; using SerializedObjCSelectorTable = llvm::OnDiskIterableChainedHashTable; /// The Objective-C selector table. std::unique_ptr ObjCSelectorTable; using SerializedGlobalVariableTable = llvm::OnDiskIterableChainedHashTable; /// The global variable table. std::unique_ptr GlobalVariableTable; using SerializedGlobalFunctionTable = llvm::OnDiskIterableChainedHashTable; /// The global function table. std::unique_ptr GlobalFunctionTable; using SerializedEnumConstantTable = llvm::OnDiskIterableChainedHashTable; /// The enumerator table. std::unique_ptr EnumConstantTable; using SerializedTagTable = llvm::OnDiskIterableChainedHashTable; /// The tag table. std::unique_ptr TagTable; using SerializedTypedefTable = llvm::OnDiskIterableChainedHashTable; /// The typedef table. std::unique_ptr TypedefTable; /// Retrieve the identifier ID for the given string, or an empty /// optional if the string is unknown. std::optional getIdentifier(llvm::StringRef Str); /// Retrieve the selector ID for the given selector, or an empty /// optional if the string is unknown. std::optional getSelector(ObjCSelectorRef Selector); bool readControlBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); bool readIdentifierBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); bool readObjCContextBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); bool readObjCPropertyBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); bool readObjCMethodBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); bool readObjCSelectorBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); bool readGlobalVariableBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); bool readGlobalFunctionBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); bool readEnumConstantBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); bool readTagBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); bool readTypedefBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch); }; std::optional APINotesReader::Implementation::getIdentifier(llvm::StringRef Str) { if (!IdentifierTable) return std::nullopt; if (Str.empty()) return IdentifierID(0); auto Known = IdentifierTable->find(Str); if (Known == IdentifierTable->end()) return std::nullopt; return *Known; } std::optional APINotesReader::Implementation::getSelector(ObjCSelectorRef Selector) { if (!ObjCSelectorTable || !IdentifierTable) return std::nullopt; // Translate the identifiers. StoredObjCSelector Key; Key.NumArgs = Selector.NumArgs; for (auto Ident : Selector.Identifiers) { if (auto IdentID = getIdentifier(Ident)) { Key.Identifiers.push_back(*IdentID); } else { return std::nullopt; } } auto Known = ObjCSelectorTable->find(Key); if (Known == ObjCSelectorTable->end()) return std::nullopt; return *Known; } bool APINotesReader::Implementation::readControlBlock( llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { if (Cursor.EnterSubBlock(CONTROL_BLOCK_ID)) return true; bool SawMetadata = false; llvm::Expected MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } llvm::BitstreamEntry Next = MaybeNext.get(); while (Next.Kind != llvm::BitstreamEntry::EndBlock) { if (Next.Kind == llvm::BitstreamEntry::Error) return true; if (Next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown metadata sub-block, possibly for use by a future version of the // API notes format. if (Cursor.SkipBlock()) return true; MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); continue; } Scratch.clear(); llvm::StringRef BlobData; llvm::Expected MaybeKind = Cursor.readRecord(Next.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 control_block::METADATA: // Already saw metadata. if (SawMetadata) return true; if (Scratch[0] != VERSION_MAJOR || Scratch[1] != VERSION_MINOR) return true; SawMetadata = true; break; case control_block::MODULE_NAME: ModuleName = BlobData.str(); break; case control_block::MODULE_OPTIONS: break; case control_block::SOURCE_FILE: SourceFileSizeAndModTime = {Scratch[0], Scratch[1]}; break; default: // Unknown metadata record, possibly for use by a future version of the // module format. break; } MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); } return !SawMetadata; } bool APINotesReader::Implementation::readIdentifierBlock( llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { if (Cursor.EnterSubBlock(IDENTIFIER_BLOCK_ID)) return true; llvm::Expected MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } llvm::BitstreamEntry Next = MaybeNext.get(); while (Next.Kind != llvm::BitstreamEntry::EndBlock) { if (Next.Kind == llvm::BitstreamEntry::Error) return true; if (Next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (Cursor.SkipBlock()) return true; MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); continue; } Scratch.clear(); llvm::StringRef BlobData; llvm::Expected MaybeKind = Cursor.readRecord(Next.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 identifier_block::IDENTIFIER_DATA: { // Already saw identifier table. if (IdentifierTable) return true; uint32_t tableOffset; identifier_block::IdentifierDataLayout::readRecord(Scratch, tableOffset); auto base = reinterpret_cast(BlobData.data()); IdentifierTable.reset(SerializedIdentifierTable::Create( base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); } return false; } bool APINotesReader::Implementation::readObjCContextBlock( llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { if (Cursor.EnterSubBlock(OBJC_CONTEXT_BLOCK_ID)) return true; llvm::Expected MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } llvm::BitstreamEntry Next = MaybeNext.get(); while (Next.Kind != llvm::BitstreamEntry::EndBlock) { if (Next.Kind == llvm::BitstreamEntry::Error) return true; if (Next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (Cursor.SkipBlock()) return true; MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); continue; } Scratch.clear(); llvm::StringRef BlobData; llvm::Expected MaybeKind = Cursor.readRecord(Next.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 objc_context_block::OBJC_CONTEXT_ID_DATA: { // Already saw Objective-C context ID table. if (ObjCContextIDTable) return true; uint32_t tableOffset; objc_context_block::ObjCContextIDLayout::readRecord(Scratch, tableOffset); auto base = reinterpret_cast(BlobData.data()); ObjCContextIDTable.reset(SerializedObjCContextIDTable::Create( base + tableOffset, base + sizeof(uint32_t), base)); break; } case objc_context_block::OBJC_CONTEXT_INFO_DATA: { // Already saw Objective-C context info table. if (ObjCContextInfoTable) return true; uint32_t tableOffset; objc_context_block::ObjCContextInfoLayout::readRecord(Scratch, tableOffset); auto base = reinterpret_cast(BlobData.data()); ObjCContextInfoTable.reset(SerializedObjCContextInfoTable::Create( base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); } return false; } bool APINotesReader::Implementation::readObjCPropertyBlock( llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { if (Cursor.EnterSubBlock(OBJC_PROPERTY_BLOCK_ID)) return true; llvm::Expected MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } llvm::BitstreamEntry Next = MaybeNext.get(); while (Next.Kind != llvm::BitstreamEntry::EndBlock) { if (Next.Kind == llvm::BitstreamEntry::Error) return true; if (Next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (Cursor.SkipBlock()) return true; MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); continue; } Scratch.clear(); llvm::StringRef BlobData; llvm::Expected MaybeKind = Cursor.readRecord(Next.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 objc_property_block::OBJC_PROPERTY_DATA: { // Already saw Objective-C property table. if (ObjCPropertyTable) return true; uint32_t tableOffset; objc_property_block::ObjCPropertyDataLayout::readRecord(Scratch, tableOffset); auto base = reinterpret_cast(BlobData.data()); ObjCPropertyTable.reset(SerializedObjCPropertyTable::Create( base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); } return false; } bool APINotesReader::Implementation::readObjCMethodBlock( llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { if (Cursor.EnterSubBlock(OBJC_METHOD_BLOCK_ID)) return true; llvm::Expected MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } llvm::BitstreamEntry Next = MaybeNext.get(); while (Next.Kind != llvm::BitstreamEntry::EndBlock) { if (Next.Kind == llvm::BitstreamEntry::Error) return true; if (Next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (Cursor.SkipBlock()) return true; MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); continue; } Scratch.clear(); llvm::StringRef BlobData; llvm::Expected MaybeKind = Cursor.readRecord(Next.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 objc_method_block::OBJC_METHOD_DATA: { // Already saw Objective-C method table. if (ObjCMethodTable) return true; uint32_t tableOffset; objc_method_block::ObjCMethodDataLayout::readRecord(Scratch, tableOffset); auto base = reinterpret_cast(BlobData.data()); ObjCMethodTable.reset(SerializedObjCMethodTable::Create( base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); } return false; } bool APINotesReader::Implementation::readObjCSelectorBlock( llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { if (Cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID)) return true; llvm::Expected MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } llvm::BitstreamEntry Next = MaybeNext.get(); while (Next.Kind != llvm::BitstreamEntry::EndBlock) { if (Next.Kind == llvm::BitstreamEntry::Error) return true; if (Next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (Cursor.SkipBlock()) return true; MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); continue; } Scratch.clear(); llvm::StringRef BlobData; llvm::Expected MaybeKind = Cursor.readRecord(Next.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 objc_selector_block::OBJC_SELECTOR_DATA: { // Already saw Objective-C selector table. if (ObjCSelectorTable) return true; uint32_t tableOffset; objc_selector_block::ObjCSelectorDataLayout::readRecord(Scratch, tableOffset); auto base = reinterpret_cast(BlobData.data()); ObjCSelectorTable.reset(SerializedObjCSelectorTable::Create( base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); } return false; } bool APINotesReader::Implementation::readGlobalVariableBlock( llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { if (Cursor.EnterSubBlock(GLOBAL_VARIABLE_BLOCK_ID)) return true; llvm::Expected MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } llvm::BitstreamEntry Next = MaybeNext.get(); while (Next.Kind != llvm::BitstreamEntry::EndBlock) { if (Next.Kind == llvm::BitstreamEntry::Error) return true; if (Next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (Cursor.SkipBlock()) return true; MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); continue; } Scratch.clear(); llvm::StringRef BlobData; llvm::Expected MaybeKind = Cursor.readRecord(Next.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 global_variable_block::GLOBAL_VARIABLE_DATA: { // Already saw global variable table. if (GlobalVariableTable) return true; uint32_t tableOffset; global_variable_block::GlobalVariableDataLayout::readRecord(Scratch, tableOffset); auto base = reinterpret_cast(BlobData.data()); GlobalVariableTable.reset(SerializedGlobalVariableTable::Create( base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); } return false; } bool APINotesReader::Implementation::readGlobalFunctionBlock( llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { if (Cursor.EnterSubBlock(GLOBAL_FUNCTION_BLOCK_ID)) return true; llvm::Expected MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } llvm::BitstreamEntry Next = MaybeNext.get(); while (Next.Kind != llvm::BitstreamEntry::EndBlock) { if (Next.Kind == llvm::BitstreamEntry::Error) return true; if (Next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (Cursor.SkipBlock()) return true; MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); continue; } Scratch.clear(); llvm::StringRef BlobData; llvm::Expected MaybeKind = Cursor.readRecord(Next.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 global_function_block::GLOBAL_FUNCTION_DATA: { // Already saw global function table. if (GlobalFunctionTable) return true; uint32_t tableOffset; global_function_block::GlobalFunctionDataLayout::readRecord(Scratch, tableOffset); auto base = reinterpret_cast(BlobData.data()); GlobalFunctionTable.reset(SerializedGlobalFunctionTable::Create( base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); } return false; } bool APINotesReader::Implementation::readEnumConstantBlock( llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { if (Cursor.EnterSubBlock(ENUM_CONSTANT_BLOCK_ID)) return true; llvm::Expected MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } llvm::BitstreamEntry Next = MaybeNext.get(); while (Next.Kind != llvm::BitstreamEntry::EndBlock) { if (Next.Kind == llvm::BitstreamEntry::Error) return true; if (Next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (Cursor.SkipBlock()) return true; MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); continue; } Scratch.clear(); llvm::StringRef BlobData; llvm::Expected MaybeKind = Cursor.readRecord(Next.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 enum_constant_block::ENUM_CONSTANT_DATA: { // Already saw enumerator table. if (EnumConstantTable) return true; uint32_t tableOffset; enum_constant_block::EnumConstantDataLayout::readRecord(Scratch, tableOffset); auto base = reinterpret_cast(BlobData.data()); EnumConstantTable.reset(SerializedEnumConstantTable::Create( base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); } return false; } bool APINotesReader::Implementation::readTagBlock( llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { if (Cursor.EnterSubBlock(TAG_BLOCK_ID)) return true; llvm::Expected MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } llvm::BitstreamEntry Next = MaybeNext.get(); while (Next.Kind != llvm::BitstreamEntry::EndBlock) { if (Next.Kind == llvm::BitstreamEntry::Error) return true; if (Next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (Cursor.SkipBlock()) return true; MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); continue; } Scratch.clear(); llvm::StringRef BlobData; llvm::Expected MaybeKind = Cursor.readRecord(Next.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 tag_block::TAG_DATA: { // Already saw tag table. if (TagTable) return true; uint32_t tableOffset; tag_block::TagDataLayout::readRecord(Scratch, tableOffset); auto base = reinterpret_cast(BlobData.data()); TagTable.reset(SerializedTagTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); } return false; } bool APINotesReader::Implementation::readTypedefBlock( llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl &Scratch) { if (Cursor.EnterSubBlock(TYPEDEF_BLOCK_ID)) return true; llvm::Expected MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } llvm::BitstreamEntry Next = MaybeNext.get(); while (Next.Kind != llvm::BitstreamEntry::EndBlock) { if (Next.Kind == llvm::BitstreamEntry::Error) return true; if (Next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (Cursor.SkipBlock()) return true; MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); continue; } Scratch.clear(); llvm::StringRef BlobData; llvm::Expected MaybeKind = Cursor.readRecord(Next.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 typedef_block::TYPEDEF_DATA: { // Already saw typedef table. if (TypedefTable) return true; uint32_t tableOffset; typedef_block::TypedefDataLayout::readRecord(Scratch, tableOffset); auto base = reinterpret_cast(BlobData.data()); TypedefTable.reset(SerializedTypedefTable::Create( base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } MaybeNext = Cursor.advance(); if (!MaybeNext) { // FIXME this drops the error on the floor. consumeError(MaybeNext.takeError()); return false; } Next = MaybeNext.get(); } return false; } APINotesReader::APINotesReader(llvm::MemoryBuffer *InputBuffer, llvm::VersionTuple SwiftVersion, bool &Failed) : Implementation(new class Implementation) { Failed = false; // Initialize the input buffer. Implementation->InputBuffer = InputBuffer; Implementation->SwiftVersion = SwiftVersion; llvm::BitstreamCursor Cursor(*Implementation->InputBuffer); // Validate signature. for (auto byte : API_NOTES_SIGNATURE) { if (Cursor.AtEndOfStream()) { Failed = true; return; } if (llvm::Expected maybeRead = Cursor.Read(8)) { if (maybeRead.get() != byte) { Failed = true; return; } } else { // FIXME this drops the error on the floor. consumeError(maybeRead.takeError()); Failed = true; return; } } // Look at all of the blocks. bool HasValidControlBlock = false; llvm::SmallVector Scratch; while (!Cursor.AtEndOfStream()) { llvm::Expected MaybeTopLevelEntry = Cursor.advance(); if (!MaybeTopLevelEntry) { // FIXME this drops the error on the floor. consumeError(MaybeTopLevelEntry.takeError()); Failed = true; return; } llvm::BitstreamEntry TopLevelEntry = MaybeTopLevelEntry.get(); if (TopLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) break; switch (TopLevelEntry.ID) { case llvm::bitc::BLOCKINFO_BLOCK_ID: if (!Cursor.ReadBlockInfoBlock()) { Failed = true; break; } break; case CONTROL_BLOCK_ID: // Only allow a single control block. if (HasValidControlBlock || Implementation->readControlBlock(Cursor, Scratch)) { Failed = true; return; } HasValidControlBlock = true; break; case IDENTIFIER_BLOCK_ID: if (!HasValidControlBlock || Implementation->readIdentifierBlock(Cursor, Scratch)) { Failed = true; return; } break; case OBJC_CONTEXT_BLOCK_ID: if (!HasValidControlBlock || Implementation->readObjCContextBlock(Cursor, Scratch)) { Failed = true; return; } break; case OBJC_PROPERTY_BLOCK_ID: if (!HasValidControlBlock || Implementation->readObjCPropertyBlock(Cursor, Scratch)) { Failed = true; return; } break; case OBJC_METHOD_BLOCK_ID: if (!HasValidControlBlock || Implementation->readObjCMethodBlock(Cursor, Scratch)) { Failed = true; return; } break; case OBJC_SELECTOR_BLOCK_ID: if (!HasValidControlBlock || Implementation->readObjCSelectorBlock(Cursor, Scratch)) { Failed = true; return; } break; case GLOBAL_VARIABLE_BLOCK_ID: if (!HasValidControlBlock || Implementation->readGlobalVariableBlock(Cursor, Scratch)) { Failed = true; return; } break; case GLOBAL_FUNCTION_BLOCK_ID: if (!HasValidControlBlock || Implementation->readGlobalFunctionBlock(Cursor, Scratch)) { Failed = true; return; } break; case ENUM_CONSTANT_BLOCK_ID: if (!HasValidControlBlock || Implementation->readEnumConstantBlock(Cursor, Scratch)) { Failed = true; return; } break; case TAG_BLOCK_ID: if (!HasValidControlBlock || Implementation->readTagBlock(Cursor, Scratch)) { Failed = true; return; } break; case TYPEDEF_BLOCK_ID: if (!HasValidControlBlock || Implementation->readTypedefBlock(Cursor, Scratch)) { Failed = true; return; } break; default: // Unknown top-level block, possibly for use by a future version of the // module format. if (Cursor.SkipBlock()) { Failed = true; return; } break; } } if (!Cursor.AtEndOfStream()) { Failed = true; return; } } APINotesReader::~APINotesReader() { delete Implementation->InputBuffer; } std::unique_ptr APINotesReader::Create(std::unique_ptr InputBuffer, llvm::VersionTuple SwiftVersion) { bool Failed = false; std::unique_ptr Reader( new APINotesReader(InputBuffer.release(), SwiftVersion, Failed)); if (Failed) return nullptr; return Reader; } template APINotesReader::VersionedInfo::VersionedInfo( llvm::VersionTuple Version, llvm::SmallVector, 1> Results) : Results(std::move(Results)) { assert(!Results.empty()); assert(std::is_sorted( Results.begin(), Results.end(), [](const std::pair &left, const std::pair &right) -> bool { assert(left.first != right.first && "two entries for the same version"); return left.first < right.first; })); Selected = std::nullopt; for (unsigned i = 0, n = Results.size(); i != n; ++i) { if (!Version.empty() && Results[i].first >= Version) { // If the current version is "4", then entries for 4 are better than // entries for 5, but both are valid. Because entries are sorted, we get // that behavior by picking the first match. Selected = i; break; } } // If we didn't find a match but we have an unversioned result, use the // unversioned result. This will always be the first entry because we encode // it as version 0. if (!Selected && Results[0].first.empty()) Selected = 0; } auto APINotesReader::lookupObjCClassID(llvm::StringRef Name) -> std::optional { if (!Implementation->ObjCContextIDTable) return std::nullopt; std::optional ClassID = Implementation->getIdentifier(Name); if (!ClassID) return std::nullopt; // ObjC classes can't be declared in C++ namespaces, so use -1 as the global // context. auto KnownID = Implementation->ObjCContextIDTable->find( ContextTableKey(-1, (uint8_t)ContextKind::ObjCClass, *ClassID)); if (KnownID == Implementation->ObjCContextIDTable->end()) return std::nullopt; return ContextID(*KnownID); } auto APINotesReader::lookupObjCClassInfo(llvm::StringRef Name) -> VersionedInfo { if (!Implementation->ObjCContextInfoTable) return std::nullopt; std::optional CtxID = lookupObjCClassID(Name); if (!CtxID) return std::nullopt; auto KnownInfo = Implementation->ObjCContextInfoTable->find(CtxID->Value); if (KnownInfo == Implementation->ObjCContextInfoTable->end()) return std::nullopt; return {Implementation->SwiftVersion, *KnownInfo}; } auto APINotesReader::lookupObjCProtocolID(llvm::StringRef Name) -> std::optional { if (!Implementation->ObjCContextIDTable) return std::nullopt; std::optional classID = Implementation->getIdentifier(Name); if (!classID) return std::nullopt; // ObjC classes can't be declared in C++ namespaces, so use -1 as the global // context. auto KnownID = Implementation->ObjCContextIDTable->find( ContextTableKey(-1, (uint8_t)ContextKind::ObjCProtocol, *classID)); if (KnownID == Implementation->ObjCContextIDTable->end()) return std::nullopt; return ContextID(*KnownID); } auto APINotesReader::lookupObjCProtocolInfo(llvm::StringRef Name) -> VersionedInfo { if (!Implementation->ObjCContextInfoTable) return std::nullopt; std::optional CtxID = lookupObjCProtocolID(Name); if (!CtxID) return std::nullopt; auto KnownInfo = Implementation->ObjCContextInfoTable->find(CtxID->Value); if (KnownInfo == Implementation->ObjCContextInfoTable->end()) return std::nullopt; return {Implementation->SwiftVersion, *KnownInfo}; } auto APINotesReader::lookupObjCProperty(ContextID CtxID, llvm::StringRef Name, bool IsInstance) -> VersionedInfo { if (!Implementation->ObjCPropertyTable) return std::nullopt; std::optional PropertyID = Implementation->getIdentifier(Name); if (!PropertyID) return std::nullopt; auto Known = Implementation->ObjCPropertyTable->find( std::make_tuple(CtxID.Value, *PropertyID, (char)IsInstance)); if (Known == Implementation->ObjCPropertyTable->end()) return std::nullopt; return {Implementation->SwiftVersion, *Known}; } auto APINotesReader::lookupObjCMethod(ContextID CtxID, ObjCSelectorRef Selector, bool IsInstanceMethod) -> VersionedInfo { if (!Implementation->ObjCMethodTable) return std::nullopt; std::optional SelID = Implementation->getSelector(Selector); if (!SelID) return std::nullopt; auto Known = Implementation->ObjCMethodTable->find( ObjCMethodTableInfo::internal_key_type{CtxID.Value, *SelID, IsInstanceMethod}); if (Known == Implementation->ObjCMethodTable->end()) return std::nullopt; return {Implementation->SwiftVersion, *Known}; } auto APINotesReader::lookupGlobalVariable(llvm::StringRef Name, std::optional Ctx) -> VersionedInfo { if (!Implementation->GlobalVariableTable) return std::nullopt; std::optional NameID = Implementation->getIdentifier(Name); if (!NameID) return std::nullopt; ContextTableKey Key(Ctx, *NameID); auto Known = Implementation->GlobalVariableTable->find(Key); if (Known == Implementation->GlobalVariableTable->end()) return std::nullopt; return {Implementation->SwiftVersion, *Known}; } auto APINotesReader::lookupGlobalFunction(llvm::StringRef Name, std::optional Ctx) -> VersionedInfo { if (!Implementation->GlobalFunctionTable) return std::nullopt; std::optional NameID = Implementation->getIdentifier(Name); if (!NameID) return std::nullopt; ContextTableKey Key(Ctx, *NameID); auto Known = Implementation->GlobalFunctionTable->find(Key); if (Known == Implementation->GlobalFunctionTable->end()) return std::nullopt; return {Implementation->SwiftVersion, *Known}; } auto APINotesReader::lookupEnumConstant(llvm::StringRef Name) -> VersionedInfo { if (!Implementation->EnumConstantTable) return std::nullopt; std::optional NameID = Implementation->getIdentifier(Name); if (!NameID) return std::nullopt; auto Known = Implementation->EnumConstantTable->find(*NameID); if (Known == Implementation->EnumConstantTable->end()) return std::nullopt; return {Implementation->SwiftVersion, *Known}; } auto APINotesReader::lookupTag(llvm::StringRef Name, std::optional Ctx) -> VersionedInfo { if (!Implementation->TagTable) return std::nullopt; std::optional NameID = Implementation->getIdentifier(Name); if (!NameID) return std::nullopt; ContextTableKey Key(Ctx, *NameID); auto Known = Implementation->TagTable->find(Key); if (Known == Implementation->TagTable->end()) return std::nullopt; return {Implementation->SwiftVersion, *Known}; } auto APINotesReader::lookupTypedef(llvm::StringRef Name, std::optional Ctx) -> VersionedInfo { if (!Implementation->TypedefTable) return std::nullopt; std::optional NameID = Implementation->getIdentifier(Name); if (!NameID) return std::nullopt; ContextTableKey Key(Ctx, *NameID); auto Known = Implementation->TypedefTable->find(Key); if (Known == Implementation->TypedefTable->end()) return std::nullopt; return {Implementation->SwiftVersion, *Known}; } auto APINotesReader::lookupNamespaceID( llvm::StringRef Name, std::optional ParentNamespaceID) -> std::optional { if (!Implementation->ObjCContextIDTable) return std::nullopt; std::optional NamespaceID = Implementation->getIdentifier(Name); if (!NamespaceID) return std::nullopt; uint32_t RawParentNamespaceID = ParentNamespaceID ? ParentNamespaceID->Value : -1; auto KnownID = Implementation->ObjCContextIDTable->find( {RawParentNamespaceID, (uint8_t)ContextKind::Namespace, *NamespaceID}); if (KnownID == Implementation->ObjCContextIDTable->end()) return std::nullopt; return ContextID(*KnownID); } } // namespace api_notes } // namespace clang