[clang] [APINotes] Upstream APINotesReader (PR #66769)
Egor Zhdan via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 27 10:08:38 PDT 2023
https://github.com/egorzhdan updated https://github.com/llvm/llvm-project/pull/66769
>From 4561ed86480e89d0e36560adab13f22a59627d81 Mon Sep 17 00:00:00 2001
From: Egor Zhdan <e_zhdan at apple.com>
Date: Tue, 19 Sep 2023 13:42:16 +0100
Subject: [PATCH] [APINotes] Upstream APINotesReader
This upstreams more of the Clang API Notes functionality that is currently implemented in the Apple fork: https://github.com/apple/llvm-project/tree/next/clang/lib/APINotes
---
clang/include/clang/APINotes/APINotesReader.h | 202 ++
clang/include/clang/APINotes/Types.h | 8 +-
clang/lib/APINotes/APINotesReader.cpp | 1997 +++++++++++++++++
clang/lib/APINotes/CMakeLists.txt | 3 +
4 files changed, 2203 insertions(+), 7 deletions(-)
create mode 100644 clang/include/clang/APINotes/APINotesReader.h
create mode 100644 clang/lib/APINotes/APINotesReader.cpp
diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h
new file mode 100644
index 000000000000000..0bb3b3c5c22517d
--- /dev/null
+++ b/clang/include/clang/APINotes/APINotesReader.h
@@ -0,0 +1,202 @@
+//===--- APINotesReader.h - 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 defines 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.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_APINOTES_READER_H
+#define LLVM_CLANG_APINOTES_READER_H
+
+#include "clang/APINotes/Types.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/VersionTuple.h"
+#include <memory>
+
+namespace clang {
+namespace api_notes {
+
+/// A class that reads API notes data from a binary file that was written by
+/// the \c APINotesWriter.
+class APINotesReader {
+ class Implementation;
+ std::unique_ptr<Implementation> Implementation;
+
+ APINotesReader(llvm::MemoryBuffer *InputBuffer,
+ llvm::VersionTuple SwiftVersion, bool &Failed);
+
+public:
+ /// Create a new API notes reader from the given member buffer, which
+ /// contains the contents of a binary API notes file.
+ ///
+ /// \returns the new API notes reader, or null if an error occurred.
+ static std::unique_ptr<APINotesReader>
+ Create(std::unique_ptr<llvm::MemoryBuffer> InputBuffer,
+ llvm::VersionTuple SwiftVersion);
+
+ ~APINotesReader();
+
+ APINotesReader(const APINotesReader &) = delete;
+ APINotesReader &operator=(const APINotesReader &) = delete;
+
+ /// Captures the completed versioned information for a particular part of
+ /// API notes, including both unversioned API notes and each versioned API
+ /// note for that particular entity.
+ template <typename T> class VersionedInfo {
+ /// The complete set of results.
+ llvm::SmallVector<std::pair<llvm::VersionTuple, T>, 1> Results;
+
+ /// The index of the result that is the "selected" set based on the desired
+ /// Swift version, or null if nothing matched.
+ std::optional<unsigned> Selected;
+
+ public:
+ /// Form an empty set of versioned information.
+ VersionedInfo(std::nullopt_t) : Selected(std::nullopt) {}
+
+ /// Form a versioned info set given the desired version and a set of
+ /// results.
+ VersionedInfo(
+ llvm::VersionTuple Version,
+ llvm::SmallVector<std::pair<llvm::VersionTuple, T>, 1> Results);
+
+ /// Retrieve the selected index in the result set.
+ std::optional<unsigned> getSelected() const {
+ return Selected;
+ }
+
+ /// Return the number of versioned results we know about.
+ unsigned size() const { return Results.size(); }
+
+ /// Access all versioned results.
+ const std::pair<llvm::VersionTuple, T> *begin() const {
+ assert(!Results.empty());
+ return Results.begin();
+ }
+ const std::pair<llvm::VersionTuple, T> *end() const {
+ return Results.end();
+ }
+
+ /// Access a specific versioned result.
+ const std::pair<llvm::VersionTuple, T> &operator[](unsigned index) const {
+ assert(index < Results.size());
+ return Results[index];
+ }
+ };
+
+ /// Look for the context ID of the given Objective-C class.
+ ///
+ /// \param Name The name of the class we're looking for.
+ ///
+ /// \returns The ID, if known.
+ std::optional<ContextID> lookupObjCClassID(llvm::StringRef Name);
+
+ /// Look for information regarding the given Objective-C class.
+ ///
+ /// \param Name The name of the class we're looking for.
+ ///
+ /// \returns The information about the class, if known.
+ VersionedInfo<ObjCContextInfo> lookupObjCClassInfo(llvm::StringRef Name);
+
+ /// Look for the context ID of the given Objective-C protocol.
+ ///
+ /// \param Name The name of the protocol we're looking for.
+ ///
+ /// \returns The ID of the protocol, if known.
+ std::optional<ContextID> lookupObjCProtocolID(llvm::StringRef Name);
+
+ /// Look for information regarding the given Objective-C protocol.
+ ///
+ /// \param Name The name of the protocol we're looking for.
+ ///
+ /// \returns The information about the protocol, if known.
+ VersionedInfo<ObjCContextInfo> lookupObjCProtocolInfo(llvm::StringRef Name);
+
+ /// Look for information regarding the given Objective-C property in
+ /// the given context.
+ ///
+ /// \param CtxID The ID that references the context we are looking for.
+ /// \param Name The name of the property we're looking for.
+ /// \param IsInstance Whether we are looking for an instance property (vs.
+ /// a class property).
+ ///
+ /// \returns Information about the property, if known.
+ VersionedInfo<ObjCPropertyInfo>
+ lookupObjCProperty(ContextID CtxID, llvm::StringRef Name, bool IsInstance);
+
+ /// Look for information regarding the given Objective-C method in
+ /// the given context.
+ ///
+ /// \param CtxID The ID that references the context we are looking for.
+ /// \param Selector The selector naming the method we're looking for.
+ /// \param IsInstanceMethod Whether we are looking for an instance method.
+ ///
+ /// \returns Information about the method, if known.
+ VersionedInfo<ObjCMethodInfo> lookupObjCMethod(ContextID CtxID,
+ ObjCSelectorRef Selector,
+ bool IsInstanceMethod);
+
+ /// Look for information regarding the given global variable.
+ ///
+ /// \param Name The name of the global variable.
+ ///
+ /// \returns information about the global variable, if known.
+ VersionedInfo<GlobalVariableInfo>
+ lookupGlobalVariable(llvm::StringRef Name,
+ std::optional<Context> Ctx = std::nullopt);
+
+ /// Look for information regarding the given global function.
+ ///
+ /// \param Name The name of the global function.
+ ///
+ /// \returns information about the global function, if known.
+ VersionedInfo<GlobalFunctionInfo>
+ lookupGlobalFunction(llvm::StringRef Name,
+ std::optional<Context> Ctx = std::nullopt);
+
+ /// Look for information regarding the given enumerator.
+ ///
+ /// \param Name The name of the enumerator.
+ ///
+ /// \returns information about the enumerator, if known.
+ VersionedInfo<EnumConstantInfo> lookupEnumConstant(llvm::StringRef Name);
+
+ /// Look for information regarding the given tag
+ /// (struct/union/enum/C++ class).
+ ///
+ /// \param Name The name of the tag.
+ ///
+ /// \returns information about the tag, if known.
+ VersionedInfo<TagInfo> lookupTag(llvm::StringRef Name,
+ std::optional<Context> Ctx = std::nullopt);
+
+ /// Look for information regarding the given typedef.
+ ///
+ /// \param Name The name of the typedef.
+ ///
+ /// \returns information about the typedef, if known.
+ VersionedInfo<TypedefInfo>
+ lookupTypedef(llvm::StringRef Name,
+ std::optional<Context> Ctx = std::nullopt);
+
+ /// Look for the context ID of the given C++ namespace.
+ ///
+ /// \param Name The name of the class we're looking for.
+ ///
+ /// \returns The ID, if known.
+ std::optional<ContextID>
+ lookupNamespaceID(llvm::StringRef Name,
+ std::optional<ContextID> ParentNamespaceID = std::nullopt);
+};
+
+} // end namespace api_notes
+} // end namespace clang
+
+#endif // LLVM_CLANG_APINOTES_READER_H
diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h
index 354458588e30932..b74244bc8f1cbd3 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -144,16 +144,10 @@ class CommonTypeInfo : public CommonEntityInfo {
return SwiftBridge;
}
- void setSwiftBridge(const std::optional<std::string> &SwiftType) {
+ void setSwiftBridge(std::optional<std::string> SwiftType) {
SwiftBridge = SwiftType;
}
- void setSwiftBridge(const std::optional<llvm::StringRef> &SwiftType) {
- SwiftBridge = SwiftType
- ? std::optional<std::string>(std::string(*SwiftType))
- : std::nullopt;
- }
-
const std::optional<std::string> &getNSErrorDomain() const {
return NSErrorDomain;
}
diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp
new file mode 100644
index 000000000000000..43288a6d3e24d49
--- /dev/null
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -0,0 +1,1997 @@
+//===--- 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<uint32_t, little, unaligned>(Data);
+ if (NumVersions == 0)
+ return llvm::VersionTuple(Major);
+
+ unsigned Minor = endian::readNext<uint32_t, little, unaligned>(Data);
+ if (NumVersions == 1)
+ return llvm::VersionTuple(Major, Minor);
+
+ unsigned Subminor = endian::readNext<uint32_t, little, unaligned>(Data);
+ if (NumVersions == 2)
+ return llvm::VersionTuple(Major, Minor, Subminor);
+
+ unsigned Build = endian::readNext<uint32_t, little, unaligned>(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, little, unaligned>(Data);
+ unsigned DataLength = endian::readNext<uint16_t, little, unaligned>(Data);
+ return {KeyLength, DataLength};
+ }
+
+ static data_type ReadData(internal_key_type Key, const uint8_t *Data,
+ unsigned Length) {
+ unsigned NumElements = endian::readNext<uint16_t, little, unaligned>(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<bool>((UnavailableBits >> 3) & 0x01));
+
+ unsigned MsgLength = endian::readNext<uint16_t, little, unaligned>(Data);
+ Info.UnavailableMsg =
+ std::string(reinterpret_cast<const char *>(Data),
+ reinterpret_cast<const char *>(Data) + MsgLength);
+ Data += MsgLength;
+
+ unsigned SwiftNameLength =
+ endian::readNext<uint16_t, little, unaligned>(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, little, unaligned>(Data);
+ if (SwiftBridgeLength > 0) {
+ Info.setSwiftBridge(std::string(reinterpret_cast<const char *>(Data),
+ SwiftBridgeLength - 1));
+ Data += SwiftBridgeLength - 1;
+ }
+
+ unsigned ErrorDomainLength =
+ endian::readNext<uint16_t, little, unaligned>(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::hash_value(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, little, unaligned>(Data);
+ unsigned DataLength = endian::readNext<uint16_t, little, unaligned>(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, little, unaligned>(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, little, unaligned>(Data);
+ unsigned DataLength = endian::readNext<uint16_t, little, unaligned>(Data);
+ return {KeyLength, DataLength};
+ }
+
+ static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
+ auto ParentCtxID = endian::readNext<uint32_t, little, unaligned>(Data);
+ auto ContextKind = endian::readNext<uint8_t, little, unaligned>(Data);
+ auto NameID = endian::readNext<uint32_t, little, unaligned>(Data);
+ return {ParentCtxID, ContextKind, NameID};
+ }
+
+ static data_type ReadData(internal_key_type Key, const uint8_t *Data,
+ unsigned Length) {
+ return endian::readNext<uint32_t, little, unaligned>(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, little, unaligned>(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, little, unaligned>(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, little, unaligned>(Data);
+ auto NameID = endian::readNext<uint32_t, little, unaligned>(Data);
+ char IsInstance = endian::readNext<uint8_t, little, unaligned>(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, little, unaligned>(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, little, unaligned>(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, little, unaligned>(Data);
+ Info.NullabilityPayload = endian::readNext<uint64_t, little, unaligned>(Data);
+
+ unsigned NumParams = endian::readNext<uint16_t, little, unaligned>(Data);
+ while (NumParams > 0) {
+ ParamInfo pi;
+ ReadParamInfo(Data, pi);
+ Info.Params.push_back(pi);
+ --NumParams;
+ }
+
+ unsigned ResultTypeLen = endian::readNext<uint16_t, little, unaligned>(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, little, unaligned>(Data);
+ auto SelectorID = endian::readNext<uint32_t, little, unaligned>(Data);
+ auto IsInstance = endian::readNext<uint8_t, little, unaligned>(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, little, unaligned>(Data);
+ unsigned DataLength = endian::readNext<uint16_t, little, unaligned>(Data);
+ return {KeyLength, DataLength};
+ }
+
+ static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
+ internal_key_type Key;
+ Key.NumPieces = endian::readNext<uint16_t, little, unaligned>(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, little, unaligned>(Data));
+ }
+ return Key;
+ }
+
+ static data_type ReadData(internal_key_type Key, const uint8_t *Data,
+ unsigned Length) {
+ return endian::readNext<uint32_t, little, unaligned>(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, little, unaligned>(Data);
+ auto ContextKind = endian::readNext<uint8_t, little, unaligned>(Data);
+ auto NameID = endian::readNext<uint32_t, little, unaligned>(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, little, unaligned>(Data);
+ auto ContextKind = endian::readNext<uint8_t, little, unaligned>(Data);
+ auto NameID = endian::readNext<uint32_t, little, unaligned>(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, little, unaligned>(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, little, unaligned>(Data);
+ auto ContextKind = endian::readNext<uint8_t, little, unaligned>(Data);
+ auto NameID = endian::readNext<IdentifierID, little, unaligned>(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);
+
+ unsigned ImportAsLength =
+ endian::readNext<uint16_t, little, unaligned>(Data);
+ if (ImportAsLength > 0) {
+ Info.SwiftImportAs =
+ std::string(reinterpret_cast<const char *>(Data), ImportAsLength - 1);
+ Data += ImportAsLength - 1;
+ }
+ unsigned RetainOpLength =
+ endian::readNext<uint16_t, little, unaligned>(Data);
+ if (RetainOpLength > 0) {
+ Info.SwiftRetainOp =
+ std::string(reinterpret_cast<const char *>(Data), RetainOpLength - 1);
+ Data += RetainOpLength - 1;
+ }
+ unsigned ReleaseOpLength =
+ endian::readNext<uint16_t, little, unaligned>(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, little, unaligned>(Data);
+ auto ContextKind = endian::readNext<uint8_t, little, unaligned>(Data);
+ auto nameID = endian::readNext<IdentifierID, little, unaligned>(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;
+ 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> Results)
+ : Results(std::move(Results)) {
+
+ 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
diff --git a/clang/lib/APINotes/CMakeLists.txt b/clang/lib/APINotes/CMakeLists.txt
index c34168876a42e25..dec596ea160c68f 100644
--- a/clang/lib/APINotes/CMakeLists.txt
+++ b/clang/lib/APINotes/CMakeLists.txt
@@ -1,6 +1,9 @@
set(LLVM_LINK_COMPONENTS
+ BitReader
+ BitstreamReader
Support)
add_clang_library(clangAPINotes
+ APINotesReader.cpp
APINotesTypes.cpp
APINotesWriter.cpp
APINotesYAMLCompiler.cpp
More information about the cfe-commits
mailing list