Certain C++ types, such as `std::chrono::tzdb` in libstdc++, are non-copyable, but don't explicitly delete their copy constructor. Instead, they trigger template instantiation errors when trying to call their implicit copy constructor. The Swift compiler inserts implicit copies of value types in some cases, which trigger compiler errors for such types. This adds a Clang API Notes attribute that allows annotating C++ types as non-copyable in Swift. This lets the Swift compiler know that it should not try to instantiate the implicit copy constructor for a C++ struct. rdar://127049438
2036 lines
62 KiB
C++
2036 lines
62 KiB
C++
//===--- 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
|