mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-02 08:06:07 +00:00

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
2049 lines
63 KiB
C++
2049 lines
63 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#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, unaligned>(Data);
|
|
if (NumVersions == 0)
|
|
return llvm::VersionTuple(Major);
|
|
|
|
unsigned Minor =
|
|
endian::readNext<uint32_t, llvm::endianness::little, unaligned>(Data);
|
|
if (NumVersions == 1)
|
|
return llvm::VersionTuple(Major, Minor);
|
|
|
|
unsigned Subminor =
|
|
endian::readNext<uint32_t, llvm::endianness::little, unaligned>(Data);
|
|
if (NumVersions == 2)
|
|
return llvm::VersionTuple(Major, Minor, Subminor);
|
|
|
|
unsigned Build =
|
|
endian::readNext<uint32_t, llvm::endianness::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, llvm::endianness::little, unaligned>(Data);
|
|
unsigned DataLength =
|
|
endian::readNext<uint16_t, llvm::endianness::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, llvm::endianness::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, llvm::endianness::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, llvm::endianness::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, llvm::endianness::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, llvm::endianness::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, llvm::endianness::little, unaligned>(Data);
|
|
unsigned DataLength =
|
|
endian::readNext<uint16_t, llvm::endianness::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, llvm::endianness::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, llvm::endianness::little, unaligned>(Data);
|
|
unsigned DataLength =
|
|
endian::readNext<uint16_t, llvm::endianness::little, unaligned>(Data);
|
|
return {KeyLength, DataLength};
|
|
}
|
|
|
|
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
|
|
auto ParentCtxID =
|
|
endian::readNext<uint32_t, llvm::endianness::little, unaligned>(Data);
|
|
auto ContextKind =
|
|
endian::readNext<uint8_t, llvm::endianness::little, unaligned>(Data);
|
|
auto NameID =
|
|
endian::readNext<uint32_t, llvm::endianness::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, llvm::endianness::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, llvm::endianness::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, llvm::endianness::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, llvm::endianness::little, unaligned>(Data);
|
|
auto NameID =
|
|
endian::readNext<uint32_t, llvm::endianness::little, unaligned>(Data);
|
|
char IsInstance =
|
|
endian::readNext<uint8_t, llvm::endianness::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, llvm::endianness::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, llvm::endianness::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, llvm::endianness::little, unaligned>(Data);
|
|
Info.NullabilityPayload =
|
|
endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Data);
|
|
|
|
unsigned NumParams =
|
|
endian::readNext<uint16_t, llvm::endianness::little, unaligned>(Data);
|
|
while (NumParams > 0) {
|
|
ParamInfo pi;
|
|
ReadParamInfo(Data, pi);
|
|
Info.Params.push_back(pi);
|
|
--NumParams;
|
|
}
|
|
|
|
unsigned ResultTypeLen =
|
|
endian::readNext<uint16_t, llvm::endianness::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, llvm::endianness::little, unaligned>(Data);
|
|
auto SelectorID =
|
|
endian::readNext<uint32_t, llvm::endianness::little, unaligned>(Data);
|
|
auto IsInstance =
|
|
endian::readNext<uint8_t, llvm::endianness::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, llvm::endianness::little, unaligned>(Data);
|
|
unsigned DataLength =
|
|
endian::readNext<uint16_t, llvm::endianness::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, llvm::endianness::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, llvm::endianness::little, unaligned>(
|
|
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, 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, llvm::endianness::little, unaligned>(Data);
|
|
auto ContextKind =
|
|
endian::readNext<uint8_t, llvm::endianness::little, unaligned>(Data);
|
|
auto NameID =
|
|
endian::readNext<uint32_t, llvm::endianness::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, llvm::endianness::little, unaligned>(Data);
|
|
auto ContextKind =
|
|
endian::readNext<uint8_t, llvm::endianness::little, unaligned>(Data);
|
|
auto NameID =
|
|
endian::readNext<uint32_t, llvm::endianness::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, llvm::endianness::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, llvm::endianness::little, unaligned>(Data);
|
|
auto ContextKind =
|
|
endian::readNext<uint8_t, llvm::endianness::little, unaligned>(Data);
|
|
auto NameID =
|
|
endian::readNext<IdentifierID, llvm::endianness::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, llvm::endianness::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, llvm::endianness::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, llvm::endianness::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, llvm::endianness::little, unaligned>(Data);
|
|
auto ContextKind =
|
|
endian::readNext<uint8_t, llvm::endianness::little, unaligned>(Data);
|
|
auto nameID =
|
|
endian::readNext<IdentifierID, llvm::endianness::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
|