| //===--- 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements the \c APINotesReader class that reads source |
| // API notes data providing additional information about source code as |
| // a separate input, such as the non-nil/nilable annotations for |
| // method parameters. |
| // |
| //===----------------------------------------------------------------------===// |
| #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<uint32_t, llvm::endianness::little>(Data); |
| if (NumVersions == 0) |
| return llvm::VersionTuple(Major); |
| |
| unsigned Minor = endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| if (NumVersions == 1) |
| return llvm::VersionTuple(Major, Minor); |
| |
| unsigned Subminor = |
| endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| if (NumVersions == 2) |
| return llvm::VersionTuple(Major, Minor, Subminor); |
| |
| unsigned Build = endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| return llvm::VersionTuple(Major, Minor, Subminor, Build); |
| } |
| |
| /// An on-disk hash table whose data is versioned based on the Swift version. |
| template <typename Derived, typename KeyType, typename UnversionedDataType> |
| class VersionedTableInfo { |
| public: |
| using internal_key_type = KeyType; |
| using external_key_type = KeyType; |
| using data_type = |
| llvm::SmallVector<std::pair<llvm::VersionTuple, UnversionedDataType>, 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<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&Data) { |
| unsigned KeyLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| unsigned DataLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| return {KeyLength, DataLength}; |
| } |
| |
| static data_type ReadData(internal_key_type Key, const uint8_t *Data, |
| unsigned Length) { |
| unsigned NumElements = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| data_type Result; |
| Result.reserve(NumElements); |
| for (unsigned i = 0; i != NumElements; ++i) { |
| auto version = ReadVersionTuple(Data); |
| const auto *DataBefore = Data; |
| (void)DataBefore; |
| auto UnversionedData = Derived::readUnversioned(Key, Data); |
| assert(Data != DataBefore && |
| "Unversioned data reader didn't move pointer"); |
| 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<bool>((UnavailableBits >> 3) & 0x01)); |
| |
| unsigned MsgLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| Info.UnavailableMsg = |
| std::string(reinterpret_cast<const char *>(Data), |
| reinterpret_cast<const char *>(Data) + MsgLength); |
| Data += MsgLength; |
| |
| unsigned SwiftNameLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| Info.SwiftName = |
| std::string(reinterpret_cast<const char *>(Data), |
| reinterpret_cast<const char *>(Data) + SwiftNameLength); |
| Data += SwiftNameLength; |
| } |
| |
| /// Read serialized CommonTypeInfo. |
| void ReadCommonTypeInfo(const uint8_t *&Data, CommonTypeInfo &Info) { |
| ReadCommonEntityInfo(Data, Info); |
| |
| unsigned SwiftBridgeLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| if (SwiftBridgeLength > 0) { |
| Info.setSwiftBridge(std::string(reinterpret_cast<const char *>(Data), |
| SwiftBridgeLength - 1)); |
| Data += SwiftBridgeLength - 1; |
| } |
| |
| unsigned ErrorDomainLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| if (ErrorDomainLength > 0) { |
| Info.setNSErrorDomain(std::optional<std::string>(std::string( |
| reinterpret_cast<const char *>(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::djbHash(Key); |
| } |
| |
| static bool EqualKey(internal_key_type LHS, internal_key_type RHS) { |
| return LHS == RHS; |
| } |
| |
| static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&Data) { |
| unsigned KeyLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| unsigned DataLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| return {KeyLength, DataLength}; |
| } |
| |
| static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { |
| return llvm::StringRef(reinterpret_cast<const char *>(Data), Length); |
| } |
| |
| static data_type ReadData(internal_key_type key, const uint8_t *Data, |
| unsigned Length) { |
| return endian::readNext<uint32_t, llvm::endianness::little>(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<size_t>(Key.hashValue()); |
| } |
| |
| static bool EqualKey(internal_key_type LHS, internal_key_type RHS) { |
| return LHS == RHS; |
| } |
| |
| static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&Data) { |
| unsigned KeyLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| unsigned DataLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| return {KeyLength, DataLength}; |
| } |
| |
| static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { |
| auto ParentCtxID = |
| endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| auto ContextKind = |
| endian::readNext<uint8_t, llvm::endianness::little>(Data); |
| auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| return {ParentCtxID, ContextKind, NameID}; |
| } |
| |
| static data_type ReadData(internal_key_type Key, const uint8_t *Data, |
| unsigned Length) { |
| return endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| } |
| }; |
| |
| /// Used to deserialize the on-disk Objective-C property table. |
| class ObjCContextInfoTableInfo |
| : public VersionedTableInfo<ObjCContextInfoTableInfo, unsigned, |
| ObjCContextInfo> { |
| public: |
| static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { |
| return endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| } |
| |
| hash_value_type ComputeHash(internal_key_type Key) { |
| return static_cast<size_t>(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<NullabilityKind>(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<NullabilityKind>(*Data)); |
| } |
| ++Data; |
| |
| auto TypeLen = endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| Info.setType(std::string(Data, Data + TypeLen)); |
| Data += TypeLen; |
| } |
| |
| /// Used to deserialize the on-disk Objective-C property table. |
| class ObjCPropertyTableInfo |
| : public VersionedTableInfo<ObjCPropertyTableInfo, |
| std::tuple<uint32_t, uint32_t, uint8_t>, |
| ObjCPropertyInfo> { |
| public: |
| static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { |
| auto ClassID = endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| char IsInstance = endian::readNext<uint8_t, llvm::endianness::little>(Data); |
| return {ClassID, NameID, IsInstance}; |
| } |
| |
| hash_value_type ComputeHash(internal_key_type Key) { |
| return static_cast<size_t>(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<uint8_t, llvm::endianness::little>(Data); |
| if (auto RawConvention = Payload & 0x7) { |
| auto Convention = static_cast<RetainCountConventionKind>(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<uint8_t, llvm::endianness::little>(Data); |
| if (auto RawConvention = Payload & 0x7) { |
| auto Convention = static_cast<RetainCountConventionKind>(RawConvention - 1); |
| Info.setRetainCountConvention(Convention); |
| } |
| Payload >>= 3; |
| Info.NullabilityAudited = Payload & 0x1; |
| Payload >>= 1; |
| assert(Payload == 0 && "Bad API notes"); |
| |
| Info.NumAdjustedNullable = |
| endian::readNext<uint8_t, llvm::endianness::little>(Data); |
| Info.NullabilityPayload = |
| endian::readNext<uint64_t, llvm::endianness::little>(Data); |
| |
| unsigned NumParams = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| while (NumParams > 0) { |
| ParamInfo pi; |
| ReadParamInfo(Data, pi); |
| Info.Params.push_back(pi); |
| --NumParams; |
| } |
| |
| unsigned ResultTypeLen = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| Info.ResultType = std::string(Data, Data + ResultTypeLen); |
| Data += ResultTypeLen; |
| } |
| |
| /// Used to deserialize the on-disk Objective-C method table. |
| class ObjCMethodTableInfo |
| : public VersionedTableInfo<ObjCMethodTableInfo, |
| std::tuple<uint32_t, uint32_t, uint8_t>, |
| ObjCMethodInfo> { |
| public: |
| static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { |
| auto ClassID = endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| auto SelectorID = |
| endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| auto IsInstance = endian::readNext<uint8_t, llvm::endianness::little>(Data); |
| return {ClassID, SelectorID, IsInstance}; |
| } |
| |
| hash_value_type ComputeHash(internal_key_type Key) { |
| return static_cast<size_t>(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<StoredObjCSelector>::getHashValue(Key); |
| } |
| |
| static bool EqualKey(internal_key_type LHS, internal_key_type RHS) { |
| return llvm::DenseMapInfo<StoredObjCSelector>::isEqual(LHS, RHS); |
| } |
| |
| static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&Data) { |
| unsigned KeyLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| unsigned DataLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| return {KeyLength, DataLength}; |
| } |
| |
| static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { |
| internal_key_type Key; |
| Key.NumArgs = endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| unsigned NumIdents = (Length - sizeof(uint16_t)) / sizeof(uint32_t); |
| for (unsigned i = 0; i != NumIdents; ++i) { |
| Key.Identifiers.push_back( |
| endian::readNext<uint32_t, llvm::endianness::little>(Data)); |
| } |
| return Key; |
| } |
| |
| static data_type ReadData(internal_key_type Key, const uint8_t *Data, |
| unsigned Length) { |
| return endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| } |
| }; |
| |
| /// Used to deserialize the on-disk global variable table. |
| class GlobalVariableTableInfo |
| : public VersionedTableInfo<GlobalVariableTableInfo, ContextTableKey, |
| GlobalVariableInfo> { |
| public: |
| static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { |
| auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| auto ContextKind = |
| endian::readNext<uint8_t, llvm::endianness::little>(Data); |
| auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| return {CtxID, ContextKind, NameID}; |
| } |
| |
| hash_value_type ComputeHash(internal_key_type Key) { |
| return static_cast<size_t>(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<GlobalFunctionTableInfo, ContextTableKey, |
| GlobalFunctionInfo> { |
| public: |
| static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { |
| auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| auto ContextKind = |
| endian::readNext<uint8_t, llvm::endianness::little>(Data); |
| auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| return {CtxID, ContextKind, NameID}; |
| } |
| |
| hash_value_type ComputeHash(internal_key_type Key) { |
| return static_cast<size_t>(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<EnumConstantTableInfo, uint32_t, |
| EnumConstantInfo> { |
| public: |
| static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { |
| auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| return NameID; |
| } |
| |
| hash_value_type ComputeHash(internal_key_type Key) { |
| return static_cast<size_t>(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<TagTableInfo, ContextTableKey, TagInfo> { |
| public: |
| static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { |
| auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| auto ContextKind = |
| endian::readNext<uint8_t, llvm::endianness::little>(Data); |
| auto NameID = |
| endian::readNext<IdentifierID, llvm::endianness::little>(Data); |
| return {CtxID, ContextKind, NameID}; |
| } |
| |
| hash_value_type ComputeHash(internal_key_type Key) { |
| return static_cast<size_t>(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<EnumExtensibilityKind>((Payload & 0x3) - 1); |
| |
| uint8_t Copyable = |
| endian::readNext<uint8_t, llvm::endianness::little>(Data); |
| if (Copyable == kSwiftNonCopyable) |
| Info.setSwiftCopyable(std::optional(false)); |
| else if (Copyable == kSwiftCopyable) |
| Info.setSwiftCopyable(std::optional(true)); |
| |
| unsigned ImportAsLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| if (ImportAsLength > 0) { |
| Info.SwiftImportAs = |
| std::string(reinterpret_cast<const char *>(Data), ImportAsLength - 1); |
| Data += ImportAsLength - 1; |
| } |
| unsigned RetainOpLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| if (RetainOpLength > 0) { |
| Info.SwiftRetainOp = |
| std::string(reinterpret_cast<const char *>(Data), RetainOpLength - 1); |
| Data += RetainOpLength - 1; |
| } |
| unsigned ReleaseOpLength = |
| endian::readNext<uint16_t, llvm::endianness::little>(Data); |
| if (ReleaseOpLength > 0) { |
| Info.SwiftReleaseOp = std::string(reinterpret_cast<const char *>(Data), |
| ReleaseOpLength - 1); |
| Data += ReleaseOpLength - 1; |
| } |
| |
| ReadCommonTypeInfo(Data, Info); |
| return Info; |
| } |
| }; |
| |
| /// Used to deserialize the on-disk typedef table. |
| class TypedefTableInfo |
| : public VersionedTableInfo<TypedefTableInfo, ContextTableKey, |
| TypedefInfo> { |
| public: |
| static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { |
| auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data); |
| auto ContextKind = |
| endian::readNext<uint8_t, llvm::endianness::little>(Data); |
| auto nameID = |
| endian::readNext<IdentifierID, llvm::endianness::little>(Data); |
| return {CtxID, ContextKind, nameID}; |
| } |
| |
| hash_value_type ComputeHash(internal_key_type Key) { |
| return static_cast<size_t>(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<SwiftNewTypeKind>((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<std::pair<off_t, time_t>> SourceFileSizeAndModTime; |
| |
| using SerializedIdentifierTable = |
| llvm::OnDiskIterableChainedHashTable<IdentifierTableInfo>; |
| |
| /// The identifier table. |
| std::unique_ptr<SerializedIdentifierTable> IdentifierTable; |
| |
| using SerializedObjCContextIDTable = |
| llvm::OnDiskIterableChainedHashTable<ObjCContextIDTableInfo>; |
| |
| /// The Objective-C context ID table. |
| std::unique_ptr<SerializedObjCContextIDTable> ObjCContextIDTable; |
| |
| using SerializedObjCContextInfoTable = |
| llvm::OnDiskIterableChainedHashTable<ObjCContextInfoTableInfo>; |
| |
| /// The Objective-C context info table. |
| std::unique_ptr<SerializedObjCContextInfoTable> ObjCContextInfoTable; |
| |
| using SerializedObjCPropertyTable = |
| llvm::OnDiskIterableChainedHashTable<ObjCPropertyTableInfo>; |
| |
| /// The Objective-C property table. |
| std::unique_ptr<SerializedObjCPropertyTable> ObjCPropertyTable; |
| |
| using SerializedObjCMethodTable = |
| llvm::OnDiskIterableChainedHashTable<ObjCMethodTableInfo>; |
| |
| /// The Objective-C method table. |
| std::unique_ptr<SerializedObjCMethodTable> ObjCMethodTable; |
| |
| using SerializedObjCSelectorTable = |
| llvm::OnDiskIterableChainedHashTable<ObjCSelectorTableInfo>; |
| |
| /// The Objective-C selector table. |
| std::unique_ptr<SerializedObjCSelectorTable> ObjCSelectorTable; |
| |
| using SerializedGlobalVariableTable = |
| llvm::OnDiskIterableChainedHashTable<GlobalVariableTableInfo>; |
| |
| /// The global variable table. |
| std::unique_ptr<SerializedGlobalVariableTable> GlobalVariableTable; |
| |
| using SerializedGlobalFunctionTable = |
| llvm::OnDiskIterableChainedHashTable<GlobalFunctionTableInfo>; |
| |
| /// The global function table. |
| std::unique_ptr<SerializedGlobalFunctionTable> GlobalFunctionTable; |
| |
| using SerializedEnumConstantTable = |
| llvm::OnDiskIterableChainedHashTable<EnumConstantTableInfo>; |
| |
| /// The enumerator table. |
| std::unique_ptr<SerializedEnumConstantTable> EnumConstantTable; |
| |
| using SerializedTagTable = llvm::OnDiskIterableChainedHashTable<TagTableInfo>; |
| |
| /// The tag table. |
| std::unique_ptr<SerializedTagTable> TagTable; |
| |
| using SerializedTypedefTable = |
| llvm::OnDiskIterableChainedHashTable<TypedefTableInfo>; |
| |
| /// The typedef table. |
| std::unique_ptr<SerializedTypedefTable> TypedefTable; |
| |
| /// Retrieve the identifier ID for the given string, or an empty |
| /// optional if the string is unknown. |
| std::optional<IdentifierID> getIdentifier(llvm::StringRef Str); |
| |
| /// Retrieve the selector ID for the given selector, or an empty |
| /// optional if the string is unknown. |
| std::optional<SelectorID> getSelector(ObjCSelectorRef Selector); |
| |
| bool readControlBlock(llvm::BitstreamCursor &Cursor, |
| llvm::SmallVectorImpl<uint64_t> &Scratch); |
| bool readIdentifierBlock(llvm::BitstreamCursor &Cursor, |
| llvm::SmallVectorImpl<uint64_t> &Scratch); |
| bool readObjCContextBlock(llvm::BitstreamCursor &Cursor, |
| llvm::SmallVectorImpl<uint64_t> &Scratch); |
| bool readObjCPropertyBlock(llvm::BitstreamCursor &Cursor, |
| llvm::SmallVectorImpl<uint64_t> &Scratch); |
| bool readObjCMethodBlock(llvm::BitstreamCursor &Cursor, |
| llvm::SmallVectorImpl<uint64_t> &Scratch); |
| bool readObjCSelectorBlock(llvm::BitstreamCursor &Cursor, |
| llvm::SmallVectorImpl<uint64_t> &Scratch); |
| bool readGlobalVariableBlock(llvm::BitstreamCursor &Cursor, |
| llvm::SmallVectorImpl<uint64_t> &Scratch); |
| bool readGlobalFunctionBlock(llvm::BitstreamCursor &Cursor, |
| llvm::SmallVectorImpl<uint64_t> &Scratch); |
| bool readEnumConstantBlock(llvm::BitstreamCursor &Cursor, |
| llvm::SmallVectorImpl<uint64_t> &Scratch); |
| bool readTagBlock(llvm::BitstreamCursor &Cursor, |
| llvm::SmallVectorImpl<uint64_t> &Scratch); |
| bool readTypedefBlock(llvm::BitstreamCursor &Cursor, |
| llvm::SmallVectorImpl<uint64_t> &Scratch); |
| }; |
| |
| std::optional<IdentifierID> |
| 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<SelectorID> |
| 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<uint64_t> &Scratch) { |
| if (Cursor.EnterSubBlock(CONTROL_BLOCK_ID)) |
| return true; |
| |
| bool SawMetadata = false; |
| |
| llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance(); |
| if (!MaybeNext) { |
| // FIXME this drops the error on the floor. |
| consumeError(MaybeNext.takeError()); |
| return false; |
| } |
| llvm::BitstreamEntry Next = MaybeNext.get(); |
| |
| 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<unsigned> 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<uint64_t> &Scratch) { |
| if (Cursor.EnterSubBlock(IDENTIFIER_BLOCK_ID)) |
| return true; |
| |
| llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance(); |
| if (!MaybeNext) { |
| // FIXME this drops the error on the floor. |
| consumeError(MaybeNext.takeError()); |
| return false; |
| } |
| llvm::BitstreamEntry Next = MaybeNext.get(); |
| |
| 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<unsigned> 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<const uint8_t *>(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<uint64_t> &Scratch) { |
| if (Cursor.EnterSubBlock(OBJC_CONTEXT_BLOCK_ID)) |
| return true; |
| |
| llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance(); |
| if (!MaybeNext) { |
| // FIXME this drops the error on the floor. |
| consumeError(MaybeNext.takeError()); |
| return false; |
| } |
| llvm::BitstreamEntry Next = MaybeNext.get(); |
| |
| 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<unsigned> 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<const uint8_t *>(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<const uint8_t *>(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<uint64_t> &Scratch) { |
| if (Cursor.EnterSubBlock(OBJC_PROPERTY_BLOCK_ID)) |
| return true; |
| |
| llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance(); |
| if (!MaybeNext) { |
| // FIXME this drops the error on the floor. |
| consumeError(MaybeNext.takeError()); |
| return false; |
| } |
| llvm::BitstreamEntry Next = MaybeNext.get(); |
| |
| 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<unsigned> 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<const uint8_t *>(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<uint64_t> &Scratch) { |
| if (Cursor.EnterSubBlock(OBJC_METHOD_BLOCK_ID)) |
| return true; |
| |
| llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance(); |
| if (!MaybeNext) { |
| // FIXME this drops the error on the floor. |
| consumeError(MaybeNext.takeError()); |
| return false; |
| } |
| llvm::BitstreamEntry Next = MaybeNext.get(); |
| 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<unsigned> 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<const uint8_t *>(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<uint64_t> &Scratch) { |
| if (Cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID)) |
| return true; |
| |
| llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance(); |
| if (!MaybeNext) { |
| // FIXME this drops the error on the floor. |
| consumeError(MaybeNext.takeError()); |
| return false; |
| } |
| llvm::BitstreamEntry Next = MaybeNext.get(); |
| 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<unsigned> 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<const uint8_t *>(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<uint64_t> &Scratch) { |
| if (Cursor.EnterSubBlock(GLOBAL_VARIABLE_BLOCK_ID)) |
| return true; |
| |
| llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance(); |
| if (!MaybeNext) { |
| // FIXME this drops the error on the floor. |
| consumeError(MaybeNext.takeError()); |
| return false; |
| } |
| llvm::BitstreamEntry Next = MaybeNext.get(); |
| 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<unsigned> 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<const uint8_t *>(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<uint64_t> &Scratch) { |
| if (Cursor.EnterSubBlock(GLOBAL_FUNCTION_BLOCK_ID)) |
| return true; |
| |
| llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance(); |
| if (!MaybeNext) { |
| // FIXME this drops the error on the floor. |
| consumeError(MaybeNext.takeError()); |
| return false; |
| } |
| llvm::BitstreamEntry Next = MaybeNext.get(); |
| 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<unsigned> 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<const uint8_t *>(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<uint64_t> &Scratch) { |
| if (Cursor.EnterSubBlock(ENUM_CONSTANT_BLOCK_ID)) |
| return true; |
| |
| llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance(); |
| if (!MaybeNext) { |
| // FIXME this drops the error on the floor. |
| consumeError(MaybeNext.takeError()); |
| return false; |
| } |
| llvm::BitstreamEntry Next = MaybeNext.get(); |
| 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<unsigned> 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<const uint8_t *>(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<uint64_t> &Scratch) { |
| if (Cursor.EnterSubBlock(TAG_BLOCK_ID)) |
| return true; |
| |
| llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance(); |
| if (!MaybeNext) { |
| // FIXME this drops the error on the floor. |
| consumeError(MaybeNext.takeError()); |
| return false; |
| } |
| llvm::BitstreamEntry Next = MaybeNext.get(); |
| 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<unsigned> 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<const uint8_t *>(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<uint64_t> &Scratch) { |
| if (Cursor.EnterSubBlock(TYPEDEF_BLOCK_ID)) |
| return true; |
| |
| llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance(); |
| if (!MaybeNext) { |
| // FIXME this drops the error on the floor. |
| consumeError(MaybeNext.takeError()); |
| return false; |
| } |
| llvm::BitstreamEntry Next = MaybeNext.get(); |
| 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<unsigned> 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<const uint8_t *>(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<llvm::SimpleBitstreamCursor::word_t> 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<uint64_t, 64> Scratch; |
| while (!Cursor.AtEndOfStream()) { |
| llvm::Expected<llvm::BitstreamEntry> 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> |
| APINotesReader::Create(std::unique_ptr<llvm::MemoryBuffer> InputBuffer, |
| llvm::VersionTuple SwiftVersion) { |
| bool Failed = false; |
| std::unique_ptr<APINotesReader> Reader( |
| new APINotesReader(InputBuffer.release(), SwiftVersion, Failed)); |
| if (Failed) |
| return nullptr; |
| |
| return Reader; |
| } |
| |
| template <typename T> |
| APINotesReader::VersionedInfo<T>::VersionedInfo( |
| llvm::VersionTuple Version, |
| llvm::SmallVector<std::pair<llvm::VersionTuple, T>, 1> R) |
| : Results(std::move(R)) { |
| |
| assert(!Results.empty()); |
| assert(std::is_sorted( |
| Results.begin(), Results.end(), |
| [](const std::pair<llvm::VersionTuple, T> &left, |
| const std::pair<llvm::VersionTuple, T> &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<ContextID> { |
| if (!Implementation->ObjCContextIDTable) |
| return std::nullopt; |
| |
| std::optional<IdentifierID> 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<ObjCContextInfo> { |
| if (!Implementation->ObjCContextInfoTable) |
| return std::nullopt; |
| |
| std::optional<ContextID> 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<ContextID> { |
| if (!Implementation->ObjCContextIDTable) |
| return std::nullopt; |
| |
| std::optional<IdentifierID> 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<ObjCContextInfo> { |
| if (!Implementation->ObjCContextInfoTable) |
| return std::nullopt; |
| |
| std::optional<ContextID> 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<ObjCPropertyInfo> { |
| if (!Implementation->ObjCPropertyTable) |
| return std::nullopt; |
| |
| std::optional<IdentifierID> 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<ObjCMethodInfo> { |
| if (!Implementation->ObjCMethodTable) |
| return std::nullopt; |
| |
| std::optional<SelectorID> 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<Context> Ctx) |
| -> VersionedInfo<GlobalVariableInfo> { |
| if (!Implementation->GlobalVariableTable) |
| return std::nullopt; |
| |
| std::optional<IdentifierID> 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<Context> Ctx) |
| -> VersionedInfo<GlobalFunctionInfo> { |
| if (!Implementation->GlobalFunctionTable) |
| return std::nullopt; |
| |
| std::optional<IdentifierID> 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<EnumConstantInfo> { |
| if (!Implementation->EnumConstantTable) |
| return std::nullopt; |
| |
| std::optional<IdentifierID> 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<Context> Ctx) |
| -> VersionedInfo<TagInfo> { |
| if (!Implementation->TagTable) |
| return std::nullopt; |
| |
| std::optional<IdentifierID> 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<Context> Ctx) |
| -> VersionedInfo<TypedefInfo> { |
| if (!Implementation->TypedefTable) |
| return std::nullopt; |
| |
| std::optional<IdentifierID> 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<ContextID> ParentNamespaceID) |
| -> std::optional<ContextID> { |
| if (!Implementation->ObjCContextIDTable) |
| return std::nullopt; |
| |
| std::optional<IdentifierID> 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 |