This patch adds support in APINotes for annotating C++ user-defined
types with: `swift_attr("returned_as_unretained_by_default")`
This attribute allows to specify a default ownership convention for
return values of `SWIFT_SHARED_REFERENCE` c++ types. Specifically, it
marks all unannotated return values of this type as `unretained` (`+0`)
by default, unless explicitly overridden at the API level using
`swift_attr("returns_retained")` or `swift_attr("returns_unretained")`.
The corresponding Swift compiler support for this annotation enables
developers to suppress warnings about unannotated return ownership in
large codebases while maintaining safe and predictable ownership
semantics. By enabling this in APINotes, library authors can define this
behavior externally without needing to modify C++ source headers
directly.
### Example usage in APINotes:
```
- Name: RefCountedTypeWithDefaultConvention
SwiftImportAs: reference
SwiftDefaultOwnership: unretained
```
rdar://150764491
1573 lines
51 KiB
C++
1573 lines
51 KiB
C++
//===-- APINotesWriter.cpp - API Notes Writer -------------------*- 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/APINotesWriter.h"
|
|
#include "APINotesFormat.h"
|
|
#include "clang/APINotes/Types.h"
|
|
#include "clang/Basic/FileManager.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/Bitstream/BitstreamWriter.h"
|
|
#include "llvm/Support/DJB.h"
|
|
#include "llvm/Support/OnDiskHashTable.h"
|
|
#include "llvm/Support/VersionTuple.h"
|
|
|
|
namespace clang {
|
|
namespace api_notes {
|
|
class APINotesWriter::Implementation {
|
|
friend class APINotesWriter;
|
|
|
|
template <typename T>
|
|
using VersionedSmallVector =
|
|
llvm::SmallVector<std::pair<llvm::VersionTuple, T>, 1>;
|
|
|
|
std::string ModuleName;
|
|
const FileEntry *SourceFile;
|
|
|
|
/// Scratch space for bitstream writing.
|
|
llvm::SmallVector<uint64_t, 64> Scratch;
|
|
|
|
/// Mapping from strings to identifier IDs.
|
|
llvm::StringMap<IdentifierID> IdentifierIDs;
|
|
|
|
/// Information about contexts (Objective-C classes or protocols or C++
|
|
/// namespaces).
|
|
///
|
|
/// Indexed by the parent context ID, context kind and the identifier ID of
|
|
/// this context and provides both the context ID and information describing
|
|
/// the context within that module.
|
|
llvm::DenseMap<ContextTableKey,
|
|
std::pair<unsigned, VersionedSmallVector<ContextInfo>>>
|
|
Contexts;
|
|
|
|
/// Information about parent contexts for each context.
|
|
///
|
|
/// Indexed by context ID, provides the parent context ID.
|
|
llvm::DenseMap<uint32_t, uint32_t> ParentContexts;
|
|
|
|
/// Mapping from context IDs to the identifier ID holding the name.
|
|
llvm::DenseMap<unsigned, unsigned> ContextNames;
|
|
|
|
/// Information about Objective-C properties.
|
|
///
|
|
/// Indexed by the context ID, property name, and whether this is an
|
|
/// instance property.
|
|
llvm::DenseMap<
|
|
std::tuple<unsigned, unsigned, char>,
|
|
llvm::SmallVector<std::pair<VersionTuple, ObjCPropertyInfo>, 1>>
|
|
ObjCProperties;
|
|
|
|
/// Information about C record fields.
|
|
///
|
|
/// Indexed by the context ID and name ID.
|
|
llvm::DenseMap<SingleDeclTableKey,
|
|
llvm::SmallVector<std::pair<VersionTuple, FieldInfo>, 1>>
|
|
Fields;
|
|
|
|
/// Information about Objective-C methods.
|
|
///
|
|
/// Indexed by the context ID, selector ID, and Boolean (stored as a char)
|
|
/// indicating whether this is a class or instance method.
|
|
llvm::DenseMap<std::tuple<unsigned, unsigned, char>,
|
|
llvm::SmallVector<std::pair<VersionTuple, ObjCMethodInfo>, 1>>
|
|
ObjCMethods;
|
|
|
|
/// Information about C++ methods.
|
|
///
|
|
/// Indexed by the context ID and name ID.
|
|
llvm::DenseMap<SingleDeclTableKey,
|
|
llvm::SmallVector<std::pair<VersionTuple, CXXMethodInfo>, 1>>
|
|
CXXMethods;
|
|
|
|
/// Mapping from selectors to selector ID.
|
|
llvm::DenseMap<StoredObjCSelector, SelectorID> SelectorIDs;
|
|
|
|
/// Information about global variables.
|
|
///
|
|
/// Indexed by the context ID, identifier ID.
|
|
llvm::DenseMap<
|
|
SingleDeclTableKey,
|
|
llvm::SmallVector<std::pair<VersionTuple, GlobalVariableInfo>, 1>>
|
|
GlobalVariables;
|
|
|
|
/// Information about global functions.
|
|
///
|
|
/// Indexed by the context ID, identifier ID.
|
|
llvm::DenseMap<
|
|
SingleDeclTableKey,
|
|
llvm::SmallVector<std::pair<VersionTuple, GlobalFunctionInfo>, 1>>
|
|
GlobalFunctions;
|
|
|
|
/// Information about enumerators.
|
|
///
|
|
/// Indexed by the identifier ID.
|
|
llvm::DenseMap<
|
|
unsigned, llvm::SmallVector<std::pair<VersionTuple, EnumConstantInfo>, 1>>
|
|
EnumConstants;
|
|
|
|
/// Information about tags.
|
|
///
|
|
/// Indexed by the context ID, identifier ID.
|
|
llvm::DenseMap<SingleDeclTableKey,
|
|
llvm::SmallVector<std::pair<VersionTuple, TagInfo>, 1>>
|
|
Tags;
|
|
|
|
/// Information about typedefs.
|
|
///
|
|
/// Indexed by the context ID, identifier ID.
|
|
llvm::DenseMap<SingleDeclTableKey,
|
|
llvm::SmallVector<std::pair<VersionTuple, TypedefInfo>, 1>>
|
|
Typedefs;
|
|
|
|
/// Retrieve the ID for the given identifier.
|
|
IdentifierID getIdentifier(StringRef Identifier) {
|
|
if (Identifier.empty())
|
|
return 0;
|
|
|
|
// Add to the identifier table if missing.
|
|
return IdentifierIDs.try_emplace(Identifier, IdentifierIDs.size() + 1)
|
|
.first->second;
|
|
}
|
|
|
|
/// Retrieve the ID for the given selector.
|
|
SelectorID getSelector(ObjCSelectorRef SelectorRef) {
|
|
// Translate the selector reference into a stored selector.
|
|
StoredObjCSelector Selector;
|
|
Selector.NumArgs = SelectorRef.NumArgs;
|
|
Selector.Identifiers.reserve(SelectorRef.Identifiers.size());
|
|
for (auto piece : SelectorRef.Identifiers)
|
|
Selector.Identifiers.push_back(getIdentifier(piece));
|
|
|
|
// Look for the stored selector. Add to the selector table if missing.
|
|
return SelectorIDs.try_emplace(Selector, SelectorIDs.size()).first->second;
|
|
}
|
|
|
|
private:
|
|
void writeBlockInfoBlock(llvm::BitstreamWriter &Stream);
|
|
void writeControlBlock(llvm::BitstreamWriter &Stream);
|
|
void writeIdentifierBlock(llvm::BitstreamWriter &Stream);
|
|
void writeContextBlock(llvm::BitstreamWriter &Stream);
|
|
void writeObjCPropertyBlock(llvm::BitstreamWriter &Stream);
|
|
void writeObjCMethodBlock(llvm::BitstreamWriter &Stream);
|
|
void writeCXXMethodBlock(llvm::BitstreamWriter &Stream);
|
|
void writeFieldBlock(llvm::BitstreamWriter &Stream);
|
|
void writeObjCSelectorBlock(llvm::BitstreamWriter &Stream);
|
|
void writeGlobalVariableBlock(llvm::BitstreamWriter &Stream);
|
|
void writeGlobalFunctionBlock(llvm::BitstreamWriter &Stream);
|
|
void writeEnumConstantBlock(llvm::BitstreamWriter &Stream);
|
|
void writeTagBlock(llvm::BitstreamWriter &Stream);
|
|
void writeTypedefBlock(llvm::BitstreamWriter &Stream);
|
|
|
|
public:
|
|
Implementation(llvm::StringRef ModuleName, const FileEntry *SF)
|
|
: ModuleName(std::string(ModuleName)), SourceFile(SF) {}
|
|
|
|
void writeToStream(llvm::raw_ostream &OS);
|
|
};
|
|
|
|
void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &OS) {
|
|
llvm::SmallVector<char, 0> Buffer;
|
|
|
|
{
|
|
llvm::BitstreamWriter Stream(Buffer);
|
|
|
|
// Emit the signature.
|
|
for (unsigned char Byte : API_NOTES_SIGNATURE)
|
|
Stream.Emit(Byte, 8);
|
|
|
|
// Emit the blocks.
|
|
writeBlockInfoBlock(Stream);
|
|
writeControlBlock(Stream);
|
|
writeIdentifierBlock(Stream);
|
|
writeContextBlock(Stream);
|
|
writeObjCPropertyBlock(Stream);
|
|
writeObjCMethodBlock(Stream);
|
|
writeCXXMethodBlock(Stream);
|
|
writeFieldBlock(Stream);
|
|
writeObjCSelectorBlock(Stream);
|
|
writeGlobalVariableBlock(Stream);
|
|
writeGlobalFunctionBlock(Stream);
|
|
writeEnumConstantBlock(Stream);
|
|
writeTagBlock(Stream);
|
|
writeTypedefBlock(Stream);
|
|
}
|
|
|
|
OS.write(Buffer.data(), Buffer.size());
|
|
OS.flush();
|
|
}
|
|
|
|
namespace {
|
|
/// Record the name of a block.
|
|
void emitBlockID(llvm::BitstreamWriter &Stream, unsigned ID,
|
|
llvm::StringRef Name) {
|
|
Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID,
|
|
llvm::ArrayRef<unsigned>{ID});
|
|
|
|
// Emit the block name if present.
|
|
if (Name.empty())
|
|
return;
|
|
Stream.EmitRecord(
|
|
llvm::bitc::BLOCKINFO_CODE_BLOCKNAME,
|
|
llvm::ArrayRef<unsigned char>(
|
|
const_cast<unsigned char *>(
|
|
reinterpret_cast<const unsigned char *>(Name.data())),
|
|
Name.size()));
|
|
}
|
|
|
|
/// Record the name of a record within a block.
|
|
void emitRecordID(llvm::BitstreamWriter &Stream, unsigned ID,
|
|
llvm::StringRef Name) {
|
|
assert(ID < 256 && "can't fit record ID in next to name");
|
|
|
|
llvm::SmallVector<unsigned char, 64> Buffer;
|
|
Buffer.resize(Name.size() + 1);
|
|
Buffer[0] = ID;
|
|
memcpy(Buffer.data() + 1, Name.data(), Name.size());
|
|
|
|
Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Buffer);
|
|
}
|
|
} // namespace
|
|
|
|
void APINotesWriter::Implementation::writeBlockInfoBlock(
|
|
llvm::BitstreamWriter &Stream) {
|
|
llvm::BCBlockRAII Scope(Stream, llvm::bitc::BLOCKINFO_BLOCK_ID, 2);
|
|
|
|
#define BLOCK(Block) emitBlockID(Stream, Block##_ID, #Block)
|
|
#define BLOCK_RECORD(NameSpace, Block) \
|
|
emitRecordID(Stream, NameSpace::Block, #Block)
|
|
BLOCK(CONTROL_BLOCK);
|
|
BLOCK_RECORD(control_block, METADATA);
|
|
BLOCK_RECORD(control_block, MODULE_NAME);
|
|
|
|
BLOCK(IDENTIFIER_BLOCK);
|
|
BLOCK_RECORD(identifier_block, IDENTIFIER_DATA);
|
|
|
|
BLOCK(OBJC_CONTEXT_BLOCK);
|
|
BLOCK_RECORD(context_block, CONTEXT_ID_DATA);
|
|
|
|
BLOCK(OBJC_PROPERTY_BLOCK);
|
|
BLOCK_RECORD(objc_property_block, OBJC_PROPERTY_DATA);
|
|
|
|
BLOCK(OBJC_METHOD_BLOCK);
|
|
BLOCK_RECORD(objc_method_block, OBJC_METHOD_DATA);
|
|
|
|
BLOCK(OBJC_SELECTOR_BLOCK);
|
|
BLOCK_RECORD(objc_selector_block, OBJC_SELECTOR_DATA);
|
|
|
|
BLOCK(GLOBAL_VARIABLE_BLOCK);
|
|
BLOCK_RECORD(global_variable_block, GLOBAL_VARIABLE_DATA);
|
|
|
|
BLOCK(GLOBAL_FUNCTION_BLOCK);
|
|
BLOCK_RECORD(global_function_block, GLOBAL_FUNCTION_DATA);
|
|
#undef BLOCK_RECORD
|
|
#undef BLOCK
|
|
}
|
|
|
|
void APINotesWriter::Implementation::writeControlBlock(
|
|
llvm::BitstreamWriter &Stream) {
|
|
llvm::BCBlockRAII Scope(Stream, CONTROL_BLOCK_ID, 3);
|
|
|
|
control_block::MetadataLayout Metadata(Stream);
|
|
Metadata.emit(Scratch, VERSION_MAJOR, VERSION_MINOR);
|
|
|
|
control_block::ModuleNameLayout ModuleName(Stream);
|
|
ModuleName.emit(Scratch, this->ModuleName);
|
|
|
|
if (SourceFile) {
|
|
control_block::SourceFileLayout SourceFile(Stream);
|
|
SourceFile.emit(Scratch, this->SourceFile->getSize(),
|
|
this->SourceFile->getModificationTime());
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
/// Used to serialize the on-disk identifier table.
|
|
class IdentifierTableInfo {
|
|
public:
|
|
using key_type = StringRef;
|
|
using key_type_ref = key_type;
|
|
using data_type = IdentifierID;
|
|
using data_type_ref = const data_type &;
|
|
using hash_value_type = uint32_t;
|
|
using offset_type = unsigned;
|
|
|
|
hash_value_type ComputeHash(key_type_ref Key) { return llvm::djbHash(Key); }
|
|
|
|
std::pair<unsigned, unsigned>
|
|
EmitKeyDataLength(raw_ostream &OS, key_type_ref Key, data_type_ref) {
|
|
uint32_t KeyLength = Key.size();
|
|
uint32_t DataLength = sizeof(uint32_t);
|
|
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint16_t>(KeyLength);
|
|
writer.write<uint16_t>(DataLength);
|
|
return {KeyLength, DataLength};
|
|
}
|
|
|
|
void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { OS << Key; }
|
|
|
|
void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint32_t>(Data);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void APINotesWriter::Implementation::writeIdentifierBlock(
|
|
llvm::BitstreamWriter &Stream) {
|
|
llvm::BCBlockRAII restoreBlock(Stream, IDENTIFIER_BLOCK_ID, 3);
|
|
|
|
if (IdentifierIDs.empty())
|
|
return;
|
|
|
|
llvm::SmallString<4096> HashTableBlob;
|
|
uint32_t Offset;
|
|
{
|
|
llvm::OnDiskChainedHashTableGenerator<IdentifierTableInfo> Generator;
|
|
for (auto &II : IdentifierIDs)
|
|
Generator.insert(II.first(), II.second);
|
|
|
|
llvm::raw_svector_ostream BlobStream(HashTableBlob);
|
|
// Make sure that no bucket is at offset 0
|
|
llvm::support::endian::write<uint32_t>(BlobStream, 0,
|
|
llvm::endianness::little);
|
|
Offset = Generator.Emit(BlobStream);
|
|
}
|
|
|
|
identifier_block::IdentifierDataLayout IdentifierData(Stream);
|
|
IdentifierData.emit(Scratch, Offset, HashTableBlob);
|
|
}
|
|
|
|
namespace {
|
|
/// Used to serialize the on-disk Objective-C context table.
|
|
class ContextIDTableInfo {
|
|
public:
|
|
using key_type = ContextTableKey;
|
|
using key_type_ref = key_type;
|
|
using data_type = unsigned;
|
|
using data_type_ref = const data_type &;
|
|
using hash_value_type = size_t;
|
|
using offset_type = unsigned;
|
|
|
|
hash_value_type ComputeHash(key_type_ref Key) {
|
|
return static_cast<size_t>(Key.hashValue());
|
|
}
|
|
|
|
std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &OS, key_type_ref,
|
|
data_type_ref) {
|
|
uint32_t KeyLength = sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t);
|
|
uint32_t DataLength = sizeof(uint32_t);
|
|
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint16_t>(KeyLength);
|
|
writer.write<uint16_t>(DataLength);
|
|
return {KeyLength, DataLength};
|
|
}
|
|
|
|
void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint32_t>(Key.parentContextID);
|
|
writer.write<uint8_t>(Key.contextKind);
|
|
writer.write<uint32_t>(Key.contextID);
|
|
}
|
|
|
|
void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint32_t>(Data);
|
|
}
|
|
};
|
|
|
|
/// Localized helper to make a type dependent, thwarting template argument
|
|
/// deduction.
|
|
template <typename T> struct MakeDependent { typedef T Type; };
|
|
|
|
/// Retrieve the serialized size of the given VersionTuple, for use in
|
|
/// on-disk hash tables.
|
|
unsigned getVersionTupleSize(const VersionTuple &VT) {
|
|
unsigned size = sizeof(uint8_t) + /*major*/ sizeof(uint32_t);
|
|
if (VT.getMinor())
|
|
size += sizeof(uint32_t);
|
|
if (VT.getSubminor())
|
|
size += sizeof(uint32_t);
|
|
if (VT.getBuild())
|
|
size += sizeof(uint32_t);
|
|
return size;
|
|
}
|
|
|
|
/// Determine the size of an array of versioned information,
|
|
template <typename T>
|
|
unsigned getVersionedInfoSize(
|
|
const llvm::SmallVectorImpl<std::pair<llvm::VersionTuple, T>> &VI,
|
|
llvm::function_ref<unsigned(const typename MakeDependent<T>::Type &)>
|
|
getInfoSize) {
|
|
unsigned result = sizeof(uint16_t); // # of elements
|
|
for (const auto &E : VI) {
|
|
result += getVersionTupleSize(E.first);
|
|
result += getInfoSize(E.second);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// Emit a serialized representation of a version tuple.
|
|
void emitVersionTuple(raw_ostream &OS, const VersionTuple &VT) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
|
|
// First byte contains the number of components beyond the 'major' component.
|
|
uint8_t descriptor;
|
|
if (VT.getBuild())
|
|
descriptor = 3;
|
|
else if (VT.getSubminor())
|
|
descriptor = 2;
|
|
else if (VT.getMinor())
|
|
descriptor = 1;
|
|
else
|
|
descriptor = 0;
|
|
writer.write<uint8_t>(descriptor);
|
|
|
|
// Write the components.
|
|
writer.write<uint32_t>(VT.getMajor());
|
|
if (auto minor = VT.getMinor())
|
|
writer.write<uint32_t>(*minor);
|
|
if (auto subminor = VT.getSubminor())
|
|
writer.write<uint32_t>(*subminor);
|
|
if (auto build = VT.getBuild())
|
|
writer.write<uint32_t>(*build);
|
|
}
|
|
|
|
/// Emit versioned information.
|
|
template <typename T>
|
|
void emitVersionedInfo(
|
|
raw_ostream &OS, llvm::SmallVectorImpl<std::pair<VersionTuple, T>> &VI,
|
|
llvm::function_ref<void(raw_ostream &,
|
|
const typename MakeDependent<T>::Type &)>
|
|
emitInfo) {
|
|
std::sort(VI.begin(), VI.end(),
|
|
[](const std::pair<VersionTuple, T> &LHS,
|
|
const std::pair<VersionTuple, T> &RHS) -> bool {
|
|
assert((&LHS == &RHS || LHS.first != RHS.first) &&
|
|
"two entries for the same version");
|
|
return LHS.first < RHS.first;
|
|
});
|
|
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint16_t>(VI.size());
|
|
for (const auto &E : VI) {
|
|
emitVersionTuple(OS, E.first);
|
|
emitInfo(OS, E.second);
|
|
}
|
|
}
|
|
|
|
/// On-disk hash table info key base for handling versioned data.
|
|
template <typename Derived, typename KeyType, typename UnversionedDataType>
|
|
class VersionedTableInfo {
|
|
Derived &asDerived() { return *static_cast<Derived *>(this); }
|
|
|
|
const Derived &asDerived() const {
|
|
return *static_cast<const Derived *>(this);
|
|
}
|
|
|
|
public:
|
|
using key_type = KeyType;
|
|
using key_type_ref = key_type;
|
|
using data_type =
|
|
llvm::SmallVector<std::pair<llvm::VersionTuple, UnversionedDataType>, 1>;
|
|
using data_type_ref = data_type &;
|
|
using hash_value_type = size_t;
|
|
using offset_type = unsigned;
|
|
|
|
std::pair<unsigned, unsigned>
|
|
EmitKeyDataLength(raw_ostream &OS, key_type_ref Key, data_type_ref Data) {
|
|
uint32_t KeyLength = asDerived().getKeyLength(Key);
|
|
uint32_t DataLength =
|
|
getVersionedInfoSize(Data, [this](const UnversionedDataType &UI) {
|
|
return asDerived().getUnversionedInfoSize(UI);
|
|
});
|
|
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint16_t>(KeyLength);
|
|
writer.write<uint16_t>(DataLength);
|
|
return {KeyLength, DataLength};
|
|
}
|
|
|
|
void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) {
|
|
emitVersionedInfo(
|
|
OS, Data, [this](llvm::raw_ostream &OS, const UnversionedDataType &UI) {
|
|
asDerived().emitUnversionedInfo(OS, UI);
|
|
});
|
|
}
|
|
};
|
|
|
|
/// Emit a serialized representation of the common entity information.
|
|
void emitCommonEntityInfo(raw_ostream &OS, const CommonEntityInfo &CEI) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
|
|
uint8_t payload = 0;
|
|
if (auto swiftPrivate = CEI.isSwiftPrivate()) {
|
|
payload |= 0x01;
|
|
if (*swiftPrivate)
|
|
payload |= 0x02;
|
|
}
|
|
payload <<= 1;
|
|
payload |= CEI.Unavailable;
|
|
payload <<= 1;
|
|
payload |= CEI.UnavailableInSwift;
|
|
|
|
writer.write<uint8_t>(payload);
|
|
|
|
writer.write<uint16_t>(CEI.UnavailableMsg.size());
|
|
OS.write(CEI.UnavailableMsg.c_str(), CEI.UnavailableMsg.size());
|
|
|
|
writer.write<uint16_t>(CEI.SwiftName.size());
|
|
OS.write(CEI.SwiftName.c_str(), CEI.SwiftName.size());
|
|
}
|
|
|
|
/// Retrieve the serialized size of the given CommonEntityInfo, for use in
|
|
/// on-disk hash tables.
|
|
unsigned getCommonEntityInfoSize(const CommonEntityInfo &CEI) {
|
|
return 5 + CEI.UnavailableMsg.size() + CEI.SwiftName.size();
|
|
}
|
|
|
|
// Retrieve the serialized size of the given CommonTypeInfo, for use
|
|
// in on-disk hash tables.
|
|
unsigned getCommonTypeInfoSize(const CommonTypeInfo &CTI) {
|
|
return 2 + (CTI.getSwiftBridge() ? CTI.getSwiftBridge()->size() : 0) + 2 +
|
|
(CTI.getNSErrorDomain() ? CTI.getNSErrorDomain()->size() : 0) +
|
|
getCommonEntityInfoSize(CTI);
|
|
}
|
|
|
|
/// Emit a serialized representation of the common type information.
|
|
void emitCommonTypeInfo(raw_ostream &OS, const CommonTypeInfo &CTI) {
|
|
emitCommonEntityInfo(OS, CTI);
|
|
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
if (auto swiftBridge = CTI.getSwiftBridge()) {
|
|
writer.write<uint16_t>(swiftBridge->size() + 1);
|
|
OS.write(swiftBridge->c_str(), swiftBridge->size());
|
|
} else {
|
|
writer.write<uint16_t>(0);
|
|
}
|
|
if (auto nsErrorDomain = CTI.getNSErrorDomain()) {
|
|
writer.write<uint16_t>(nsErrorDomain->size() + 1);
|
|
OS.write(nsErrorDomain->c_str(), CTI.getNSErrorDomain()->size());
|
|
} else {
|
|
writer.write<uint16_t>(0);
|
|
}
|
|
}
|
|
|
|
/// Used to serialize the on-disk Objective-C property table.
|
|
class ContextInfoTableInfo
|
|
: public VersionedTableInfo<ContextInfoTableInfo, unsigned, ContextInfo> {
|
|
public:
|
|
unsigned getKeyLength(key_type_ref) { return sizeof(uint32_t); }
|
|
|
|
void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint32_t>(Key);
|
|
}
|
|
|
|
hash_value_type ComputeHash(key_type_ref Key) {
|
|
return static_cast<size_t>(llvm::hash_value(Key));
|
|
}
|
|
|
|
unsigned getUnversionedInfoSize(const ContextInfo &OCI) {
|
|
return getCommonTypeInfoSize(OCI) + 1;
|
|
}
|
|
|
|
void emitUnversionedInfo(raw_ostream &OS, const ContextInfo &OCI) {
|
|
emitCommonTypeInfo(OS, OCI);
|
|
|
|
uint8_t payload = 0;
|
|
if (auto swiftImportAsNonGeneric = OCI.getSwiftImportAsNonGeneric())
|
|
payload |= (0x01 << 1) | (uint8_t)swiftImportAsNonGeneric.value();
|
|
payload <<= 2;
|
|
if (auto swiftObjCMembers = OCI.getSwiftObjCMembers())
|
|
payload |= (0x01 << 1) | (uint8_t)swiftObjCMembers.value();
|
|
payload <<= 3;
|
|
if (auto nullable = OCI.getDefaultNullability())
|
|
payload |= (0x01 << 2) | static_cast<uint8_t>(*nullable);
|
|
payload = (payload << 1) | (OCI.hasDesignatedInits() ? 1 : 0);
|
|
|
|
OS << payload;
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void APINotesWriter::Implementation::writeContextBlock(
|
|
llvm::BitstreamWriter &Stream) {
|
|
llvm::BCBlockRAII restoreBlock(Stream, OBJC_CONTEXT_BLOCK_ID, 3);
|
|
|
|
if (Contexts.empty())
|
|
return;
|
|
|
|
{
|
|
llvm::SmallString<4096> HashTableBlob;
|
|
uint32_t Offset;
|
|
{
|
|
llvm::OnDiskChainedHashTableGenerator<ContextIDTableInfo> Generator;
|
|
for (auto &OC : Contexts)
|
|
Generator.insert(OC.first, OC.second.first);
|
|
|
|
llvm::raw_svector_ostream BlobStream(HashTableBlob);
|
|
// Make sure that no bucket is at offset 0
|
|
llvm::support::endian::write<uint32_t>(BlobStream, 0,
|
|
llvm::endianness::little);
|
|
Offset = Generator.Emit(BlobStream);
|
|
}
|
|
|
|
context_block::ContextIDLayout ContextID(Stream);
|
|
ContextID.emit(Scratch, Offset, HashTableBlob);
|
|
}
|
|
|
|
{
|
|
llvm::SmallString<4096> HashTableBlob;
|
|
uint32_t Offset;
|
|
{
|
|
llvm::OnDiskChainedHashTableGenerator<ContextInfoTableInfo> Generator;
|
|
for (auto &OC : Contexts)
|
|
Generator.insert(OC.second.first, OC.second.second);
|
|
|
|
llvm::raw_svector_ostream BlobStream(HashTableBlob);
|
|
// Make sure that no bucket is at offset 0
|
|
llvm::support::endian::write<uint32_t>(BlobStream, 0,
|
|
llvm::endianness::little);
|
|
Offset = Generator.Emit(BlobStream);
|
|
}
|
|
|
|
context_block::ContextInfoLayout ContextInfo(Stream);
|
|
ContextInfo.emit(Scratch, Offset, HashTableBlob);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
/// Retrieve the serialized size of the given VariableInfo, for use in
|
|
/// on-disk hash tables.
|
|
unsigned getVariableInfoSize(const VariableInfo &VI) {
|
|
return 2 + getCommonEntityInfoSize(VI) + 2 + VI.getType().size();
|
|
}
|
|
unsigned getParamInfoSize(const ParamInfo &PI);
|
|
|
|
/// Emit a serialized representation of the variable information.
|
|
void emitVariableInfo(raw_ostream &OS, const VariableInfo &VI) {
|
|
emitCommonEntityInfo(OS, VI);
|
|
|
|
uint8_t bytes[2] = {0, 0};
|
|
if (auto nullable = VI.getNullability()) {
|
|
bytes[0] = 1;
|
|
bytes[1] = static_cast<uint8_t>(*nullable);
|
|
} else {
|
|
// Nothing to do.
|
|
}
|
|
|
|
OS.write(reinterpret_cast<const char *>(bytes), 2);
|
|
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint16_t>(VI.getType().size());
|
|
OS.write(VI.getType().data(), VI.getType().size());
|
|
}
|
|
|
|
/// Used to serialize the on-disk Objective-C property table.
|
|
class ObjCPropertyTableInfo
|
|
: public VersionedTableInfo<ObjCPropertyTableInfo,
|
|
std::tuple<unsigned, unsigned, char>,
|
|
ObjCPropertyInfo> {
|
|
public:
|
|
unsigned getKeyLength(key_type_ref) {
|
|
return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t);
|
|
}
|
|
|
|
void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint32_t>(std::get<0>(Key));
|
|
writer.write<uint32_t>(std::get<1>(Key));
|
|
writer.write<uint8_t>(std::get<2>(Key));
|
|
}
|
|
|
|
hash_value_type ComputeHash(key_type_ref Key) {
|
|
return static_cast<size_t>(llvm::hash_value(Key));
|
|
}
|
|
|
|
unsigned getUnversionedInfoSize(const ObjCPropertyInfo &OPI) {
|
|
return getVariableInfoSize(OPI) + 1;
|
|
}
|
|
|
|
void emitUnversionedInfo(raw_ostream &OS, const ObjCPropertyInfo &OPI) {
|
|
emitVariableInfo(OS, OPI);
|
|
|
|
uint8_t flags = 0;
|
|
if (auto value = OPI.getSwiftImportAsAccessors()) {
|
|
flags |= 1 << 0;
|
|
flags |= value.value() << 1;
|
|
}
|
|
OS << flags;
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void APINotesWriter::Implementation::writeObjCPropertyBlock(
|
|
llvm::BitstreamWriter &Stream) {
|
|
llvm::BCBlockRAII Scope(Stream, OBJC_PROPERTY_BLOCK_ID, 3);
|
|
|
|
if (ObjCProperties.empty())
|
|
return;
|
|
|
|
{
|
|
llvm::SmallString<4096> HashTableBlob;
|
|
uint32_t Offset;
|
|
{
|
|
llvm::OnDiskChainedHashTableGenerator<ObjCPropertyTableInfo> Generator;
|
|
for (auto &OP : ObjCProperties)
|
|
Generator.insert(OP.first, OP.second);
|
|
|
|
llvm::raw_svector_ostream BlobStream(HashTableBlob);
|
|
// Make sure that no bucket is at offset 0
|
|
llvm::support::endian::write<uint32_t>(BlobStream, 0,
|
|
llvm::endianness::little);
|
|
Offset = Generator.Emit(BlobStream);
|
|
}
|
|
|
|
objc_property_block::ObjCPropertyDataLayout ObjCPropertyData(Stream);
|
|
ObjCPropertyData.emit(Scratch, Offset, HashTableBlob);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
unsigned getFunctionInfoSize(const FunctionInfo &);
|
|
void emitFunctionInfo(llvm::raw_ostream &, const FunctionInfo &);
|
|
void emitParamInfo(raw_ostream &OS, const ParamInfo &PI);
|
|
|
|
/// Used to serialize the on-disk Objective-C method table.
|
|
class ObjCMethodTableInfo
|
|
: public VersionedTableInfo<ObjCMethodTableInfo,
|
|
std::tuple<unsigned, unsigned, char>,
|
|
ObjCMethodInfo> {
|
|
public:
|
|
unsigned getKeyLength(key_type_ref) {
|
|
return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t);
|
|
}
|
|
|
|
void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint32_t>(std::get<0>(Key));
|
|
writer.write<uint32_t>(std::get<1>(Key));
|
|
writer.write<uint8_t>(std::get<2>(Key));
|
|
}
|
|
|
|
hash_value_type ComputeHash(key_type_ref key) {
|
|
return static_cast<size_t>(llvm::hash_value(key));
|
|
}
|
|
|
|
unsigned getUnversionedInfoSize(const ObjCMethodInfo &OMI) {
|
|
auto size = getFunctionInfoSize(OMI) + 1;
|
|
if (OMI.Self)
|
|
size += getParamInfoSize(*OMI.Self);
|
|
return size;
|
|
}
|
|
|
|
void emitUnversionedInfo(raw_ostream &OS, const ObjCMethodInfo &OMI) {
|
|
uint8_t flags = 0;
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
flags = (flags << 1) | OMI.DesignatedInit;
|
|
flags = (flags << 1) | OMI.RequiredInit;
|
|
flags = (flags << 1) | static_cast<bool>(OMI.Self);
|
|
writer.write<uint8_t>(flags);
|
|
|
|
emitFunctionInfo(OS, OMI);
|
|
|
|
if (OMI.Self)
|
|
emitParamInfo(OS, *OMI.Self);
|
|
}
|
|
};
|
|
|
|
/// Used to serialize the on-disk C++ method table.
|
|
class CXXMethodTableInfo
|
|
: public VersionedTableInfo<CXXMethodTableInfo, SingleDeclTableKey,
|
|
CXXMethodInfo> {
|
|
public:
|
|
unsigned getKeyLength(key_type_ref) {
|
|
return sizeof(uint32_t) + sizeof(uint32_t);
|
|
}
|
|
|
|
void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint32_t>(Key.parentContextID);
|
|
writer.write<uint32_t>(Key.nameID);
|
|
}
|
|
|
|
hash_value_type ComputeHash(key_type_ref key) {
|
|
return static_cast<size_t>(key.hashValue());
|
|
}
|
|
|
|
unsigned getUnversionedInfoSize(const CXXMethodInfo &MI) {
|
|
auto size = getFunctionInfoSize(MI) + 1;
|
|
if (MI.This)
|
|
size += getParamInfoSize(*MI.This);
|
|
return size;
|
|
}
|
|
|
|
void emitUnversionedInfo(raw_ostream &OS, const CXXMethodInfo &MI) {
|
|
uint8_t flags = 0;
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
flags = (flags << 1) | static_cast<bool>(MI.This);
|
|
writer.write<uint8_t>(flags);
|
|
|
|
emitFunctionInfo(OS, MI);
|
|
if (MI.This)
|
|
emitParamInfo(OS, *MI.This);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void APINotesWriter::Implementation::writeObjCMethodBlock(
|
|
llvm::BitstreamWriter &Stream) {
|
|
llvm::BCBlockRAII Scope(Stream, OBJC_METHOD_BLOCK_ID, 3);
|
|
|
|
if (ObjCMethods.empty())
|
|
return;
|
|
|
|
{
|
|
llvm::SmallString<4096> HashTableBlob;
|
|
uint32_t Offset;
|
|
{
|
|
llvm::OnDiskChainedHashTableGenerator<ObjCMethodTableInfo> Generator;
|
|
for (auto &OM : ObjCMethods)
|
|
Generator.insert(OM.first, OM.second);
|
|
|
|
llvm::raw_svector_ostream BlobStream(HashTableBlob);
|
|
// Make sure that no bucket is at offset 0
|
|
llvm::support::endian::write<uint32_t>(BlobStream, 0,
|
|
llvm::endianness::little);
|
|
Offset = Generator.Emit(BlobStream);
|
|
}
|
|
|
|
objc_method_block::ObjCMethodDataLayout ObjCMethodData(Stream);
|
|
ObjCMethodData.emit(Scratch, Offset, HashTableBlob);
|
|
}
|
|
}
|
|
|
|
void APINotesWriter::Implementation::writeCXXMethodBlock(
|
|
llvm::BitstreamWriter &Stream) {
|
|
llvm::BCBlockRAII Scope(Stream, CXX_METHOD_BLOCK_ID, 3);
|
|
|
|
if (CXXMethods.empty())
|
|
return;
|
|
|
|
{
|
|
llvm::SmallString<4096> HashTableBlob;
|
|
uint32_t Offset;
|
|
{
|
|
llvm::OnDiskChainedHashTableGenerator<CXXMethodTableInfo> Generator;
|
|
for (auto &MD : CXXMethods)
|
|
Generator.insert(MD.first, MD.second);
|
|
|
|
llvm::raw_svector_ostream BlobStream(HashTableBlob);
|
|
// Make sure that no bucket is at offset 0
|
|
llvm::support::endian::write<uint32_t>(BlobStream, 0,
|
|
llvm::endianness::little);
|
|
Offset = Generator.Emit(BlobStream);
|
|
}
|
|
|
|
cxx_method_block::CXXMethodDataLayout CXXMethodData(Stream);
|
|
CXXMethodData.emit(Scratch, Offset, HashTableBlob);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
/// Used to serialize the on-disk C field table.
|
|
class FieldTableInfo
|
|
: public VersionedTableInfo<FieldTableInfo, SingleDeclTableKey, FieldInfo> {
|
|
public:
|
|
unsigned getKeyLength(key_type_ref) {
|
|
return sizeof(uint32_t) + sizeof(uint32_t);
|
|
}
|
|
|
|
void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint32_t>(Key.parentContextID);
|
|
writer.write<uint32_t>(Key.nameID);
|
|
}
|
|
|
|
hash_value_type ComputeHash(key_type_ref key) {
|
|
return static_cast<size_t>(key.hashValue());
|
|
}
|
|
|
|
unsigned getUnversionedInfoSize(const FieldInfo &FI) {
|
|
return getVariableInfoSize(FI);
|
|
}
|
|
|
|
void emitUnversionedInfo(raw_ostream &OS, const FieldInfo &FI) {
|
|
emitVariableInfo(OS, FI);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void APINotesWriter::Implementation::writeFieldBlock(
|
|
llvm::BitstreamWriter &Stream) {
|
|
llvm::BCBlockRAII Scope(Stream, FIELD_BLOCK_ID, 3);
|
|
|
|
if (Fields.empty())
|
|
return;
|
|
|
|
{
|
|
llvm::SmallString<4096> HashTableBlob;
|
|
uint32_t Offset;
|
|
{
|
|
llvm::OnDiskChainedHashTableGenerator<FieldTableInfo> Generator;
|
|
for (auto &FD : Fields)
|
|
Generator.insert(FD.first, FD.second);
|
|
|
|
llvm::raw_svector_ostream BlobStream(HashTableBlob);
|
|
// Make sure that no bucket is at offset 0
|
|
llvm::support::endian::write<uint32_t>(BlobStream, 0,
|
|
llvm::endianness::little);
|
|
Offset = Generator.Emit(BlobStream);
|
|
}
|
|
|
|
field_block::FieldDataLayout FieldData(Stream);
|
|
FieldData.emit(Scratch, Offset, HashTableBlob);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
/// Used to serialize the on-disk Objective-C selector table.
|
|
class ObjCSelectorTableInfo {
|
|
public:
|
|
using key_type = StoredObjCSelector;
|
|
using key_type_ref = const key_type &;
|
|
using data_type = SelectorID;
|
|
using data_type_ref = data_type;
|
|
using hash_value_type = unsigned;
|
|
using offset_type = unsigned;
|
|
|
|
hash_value_type ComputeHash(key_type_ref Key) {
|
|
return llvm::DenseMapInfo<StoredObjCSelector>::getHashValue(Key);
|
|
}
|
|
|
|
std::pair<unsigned, unsigned>
|
|
EmitKeyDataLength(raw_ostream &OS, key_type_ref Key, data_type_ref) {
|
|
uint32_t KeyLength =
|
|
sizeof(uint16_t) + sizeof(uint32_t) * Key.Identifiers.size();
|
|
uint32_t DataLength = sizeof(uint32_t);
|
|
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint16_t>(KeyLength);
|
|
writer.write<uint16_t>(DataLength);
|
|
return {KeyLength, DataLength};
|
|
}
|
|
|
|
void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint16_t>(Key.NumArgs);
|
|
for (auto Identifier : Key.Identifiers)
|
|
writer.write<uint32_t>(Identifier);
|
|
}
|
|
|
|
void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint32_t>(Data);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void APINotesWriter::Implementation::writeObjCSelectorBlock(
|
|
llvm::BitstreamWriter &Stream) {
|
|
llvm::BCBlockRAII Scope(Stream, OBJC_SELECTOR_BLOCK_ID, 3);
|
|
|
|
if (SelectorIDs.empty())
|
|
return;
|
|
|
|
{
|
|
llvm::SmallString<4096> HashTableBlob;
|
|
uint32_t Offset;
|
|
{
|
|
llvm::OnDiskChainedHashTableGenerator<ObjCSelectorTableInfo> Generator;
|
|
for (auto &S : SelectorIDs)
|
|
Generator.insert(S.first, S.second);
|
|
|
|
llvm::raw_svector_ostream BlobStream(HashTableBlob);
|
|
// Make sure that no bucket is at offset 0
|
|
llvm::support::endian::write<uint32_t>(BlobStream, 0,
|
|
llvm::endianness::little);
|
|
Offset = Generator.Emit(BlobStream);
|
|
}
|
|
|
|
objc_selector_block::ObjCSelectorDataLayout ObjCSelectorData(Stream);
|
|
ObjCSelectorData.emit(Scratch, Offset, HashTableBlob);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
/// Used to serialize the on-disk global variable table.
|
|
class GlobalVariableTableInfo
|
|
: public VersionedTableInfo<GlobalVariableTableInfo, SingleDeclTableKey,
|
|
GlobalVariableInfo> {
|
|
public:
|
|
unsigned getKeyLength(key_type_ref) {
|
|
return sizeof(uint32_t) + sizeof(uint32_t);
|
|
}
|
|
|
|
void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint32_t>(Key.parentContextID);
|
|
writer.write<uint32_t>(Key.nameID);
|
|
}
|
|
|
|
hash_value_type ComputeHash(key_type_ref Key) {
|
|
return static_cast<size_t>(Key.hashValue());
|
|
}
|
|
|
|
unsigned getUnversionedInfoSize(const GlobalVariableInfo &GVI) {
|
|
return getVariableInfoSize(GVI);
|
|
}
|
|
|
|
void emitUnversionedInfo(raw_ostream &OS, const GlobalVariableInfo &GVI) {
|
|
emitVariableInfo(OS, GVI);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void APINotesWriter::Implementation::writeGlobalVariableBlock(
|
|
llvm::BitstreamWriter &Stream) {
|
|
llvm::BCBlockRAII Scope(Stream, GLOBAL_VARIABLE_BLOCK_ID, 3);
|
|
|
|
if (GlobalVariables.empty())
|
|
return;
|
|
|
|
{
|
|
llvm::SmallString<4096> HashTableBlob;
|
|
uint32_t Offset;
|
|
{
|
|
llvm::OnDiskChainedHashTableGenerator<GlobalVariableTableInfo> Generator;
|
|
for (auto &GV : GlobalVariables)
|
|
Generator.insert(GV.first, GV.second);
|
|
|
|
llvm::raw_svector_ostream BlobStream(HashTableBlob);
|
|
// Make sure that no bucket is at offset 0
|
|
llvm::support::endian::write<uint32_t>(BlobStream, 0,
|
|
llvm::endianness::little);
|
|
Offset = Generator.Emit(BlobStream);
|
|
}
|
|
|
|
global_variable_block::GlobalVariableDataLayout GlobalVariableData(Stream);
|
|
GlobalVariableData.emit(Scratch, Offset, HashTableBlob);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
unsigned getParamInfoSize(const ParamInfo &PI) {
|
|
return getVariableInfoSize(PI) + 1;
|
|
}
|
|
|
|
void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) {
|
|
emitVariableInfo(OS, PI);
|
|
|
|
uint8_t flags = 0;
|
|
if (auto noescape = PI.isNoEscape()) {
|
|
flags |= 0x01;
|
|
if (*noescape)
|
|
flags |= 0x02;
|
|
}
|
|
flags <<= 2;
|
|
if (auto lifetimebound = PI.isLifetimebound()) {
|
|
flags |= 0x01;
|
|
if (*lifetimebound)
|
|
flags |= 0x02;
|
|
}
|
|
flags <<= 3;
|
|
if (auto RCC = PI.getRetainCountConvention())
|
|
flags |= static_cast<uint8_t>(RCC.value()) + 1;
|
|
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint8_t>(flags);
|
|
}
|
|
|
|
/// Retrieve the serialized size of the given FunctionInfo, for use in on-disk
|
|
/// hash tables.
|
|
unsigned getFunctionInfoSize(const FunctionInfo &FI) {
|
|
unsigned size = getCommonEntityInfoSize(FI) + 2 + sizeof(uint64_t);
|
|
size += sizeof(uint16_t);
|
|
for (const auto &P : FI.Params)
|
|
size += getParamInfoSize(P);
|
|
size += sizeof(uint16_t) + FI.ResultType.size();
|
|
size += sizeof(uint16_t) + FI.SwiftReturnOwnership.size();
|
|
return size;
|
|
}
|
|
|
|
/// Emit a serialized representation of the function information.
|
|
void emitFunctionInfo(raw_ostream &OS, const FunctionInfo &FI) {
|
|
emitCommonEntityInfo(OS, FI);
|
|
|
|
uint8_t flags = 0;
|
|
flags |= FI.NullabilityAudited;
|
|
flags <<= 3;
|
|
if (auto RCC = FI.getRetainCountConvention())
|
|
flags |= static_cast<uint8_t>(RCC.value()) + 1;
|
|
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
|
|
writer.write<uint8_t>(flags);
|
|
writer.write<uint8_t>(FI.NumAdjustedNullable);
|
|
writer.write<uint64_t>(FI.NullabilityPayload);
|
|
|
|
writer.write<uint16_t>(FI.Params.size());
|
|
for (const auto &PI : FI.Params)
|
|
emitParamInfo(OS, PI);
|
|
|
|
writer.write<uint16_t>(FI.ResultType.size());
|
|
writer.write(ArrayRef<char>{FI.ResultType.data(), FI.ResultType.size()});
|
|
writer.write<uint16_t>(FI.SwiftReturnOwnership.size());
|
|
writer.write(ArrayRef<char>{FI.SwiftReturnOwnership.data(),
|
|
FI.SwiftReturnOwnership.size()});
|
|
}
|
|
|
|
/// Used to serialize the on-disk global function table.
|
|
class GlobalFunctionTableInfo
|
|
: public VersionedTableInfo<GlobalFunctionTableInfo, SingleDeclTableKey,
|
|
GlobalFunctionInfo> {
|
|
public:
|
|
unsigned getKeyLength(key_type_ref) {
|
|
return sizeof(uint32_t) + sizeof(uint32_t);
|
|
}
|
|
|
|
void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint32_t>(Key.parentContextID);
|
|
writer.write<uint32_t>(Key.nameID);
|
|
}
|
|
|
|
hash_value_type ComputeHash(key_type_ref Key) {
|
|
return static_cast<size_t>(Key.hashValue());
|
|
}
|
|
|
|
unsigned getUnversionedInfoSize(const GlobalFunctionInfo &GFI) {
|
|
return getFunctionInfoSize(GFI);
|
|
}
|
|
|
|
void emitUnversionedInfo(raw_ostream &OS, const GlobalFunctionInfo &GFI) {
|
|
emitFunctionInfo(OS, GFI);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void APINotesWriter::Implementation::writeGlobalFunctionBlock(
|
|
llvm::BitstreamWriter &Stream) {
|
|
llvm::BCBlockRAII Scope(Stream, GLOBAL_FUNCTION_BLOCK_ID, 3);
|
|
|
|
if (GlobalFunctions.empty())
|
|
return;
|
|
|
|
{
|
|
llvm::SmallString<4096> HashTableBlob;
|
|
uint32_t Offset;
|
|
{
|
|
llvm::OnDiskChainedHashTableGenerator<GlobalFunctionTableInfo> Generator;
|
|
for (auto &F : GlobalFunctions)
|
|
Generator.insert(F.first, F.second);
|
|
|
|
llvm::raw_svector_ostream BlobStream(HashTableBlob);
|
|
// Make sure that no bucket is at offset 0
|
|
llvm::support::endian::write<uint32_t>(BlobStream, 0,
|
|
llvm::endianness::little);
|
|
Offset = Generator.Emit(BlobStream);
|
|
}
|
|
|
|
global_function_block::GlobalFunctionDataLayout GlobalFunctionData(Stream);
|
|
GlobalFunctionData.emit(Scratch, Offset, HashTableBlob);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
/// Used to serialize the on-disk global enum constant.
|
|
class EnumConstantTableInfo
|
|
: public VersionedTableInfo<EnumConstantTableInfo, unsigned,
|
|
EnumConstantInfo> {
|
|
public:
|
|
unsigned getKeyLength(key_type_ref) { return sizeof(uint32_t); }
|
|
|
|
void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint32_t>(Key);
|
|
}
|
|
|
|
hash_value_type ComputeHash(key_type_ref Key) {
|
|
return static_cast<size_t>(llvm::hash_value(Key));
|
|
}
|
|
|
|
unsigned getUnversionedInfoSize(const EnumConstantInfo &ECI) {
|
|
return getCommonEntityInfoSize(ECI);
|
|
}
|
|
|
|
void emitUnversionedInfo(raw_ostream &OS, const EnumConstantInfo &ECI) {
|
|
emitCommonEntityInfo(OS, ECI);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void APINotesWriter::Implementation::writeEnumConstantBlock(
|
|
llvm::BitstreamWriter &Stream) {
|
|
llvm::BCBlockRAII Scope(Stream, ENUM_CONSTANT_BLOCK_ID, 3);
|
|
|
|
if (EnumConstants.empty())
|
|
return;
|
|
|
|
{
|
|
llvm::SmallString<4096> HashTableBlob;
|
|
uint32_t Offset;
|
|
{
|
|
llvm::OnDiskChainedHashTableGenerator<EnumConstantTableInfo> Generator;
|
|
for (auto &EC : EnumConstants)
|
|
Generator.insert(EC.first, EC.second);
|
|
|
|
llvm::raw_svector_ostream BlobStream(HashTableBlob);
|
|
// Make sure that no bucket is at offset 0
|
|
llvm::support::endian::write<uint32_t>(BlobStream, 0,
|
|
llvm::endianness::little);
|
|
Offset = Generator.Emit(BlobStream);
|
|
}
|
|
|
|
enum_constant_block::EnumConstantDataLayout EnumConstantData(Stream);
|
|
EnumConstantData.emit(Scratch, Offset, HashTableBlob);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
template <typename Derived, typename UnversionedDataType>
|
|
class CommonTypeTableInfo
|
|
: public VersionedTableInfo<Derived, SingleDeclTableKey,
|
|
UnversionedDataType> {
|
|
public:
|
|
using key_type_ref = typename CommonTypeTableInfo::key_type_ref;
|
|
using hash_value_type = typename CommonTypeTableInfo::hash_value_type;
|
|
|
|
unsigned getKeyLength(key_type_ref) {
|
|
return sizeof(uint32_t) + sizeof(IdentifierID);
|
|
}
|
|
|
|
void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
writer.write<uint32_t>(Key.parentContextID);
|
|
writer.write<IdentifierID>(Key.nameID);
|
|
}
|
|
|
|
hash_value_type ComputeHash(key_type_ref Key) {
|
|
return static_cast<size_t>(Key.hashValue());
|
|
}
|
|
|
|
unsigned getUnversionedInfoSize(const UnversionedDataType &UDT) {
|
|
return getCommonTypeInfoSize(UDT);
|
|
}
|
|
|
|
void emitUnversionedInfo(raw_ostream &OS, const UnversionedDataType &UDT) {
|
|
emitCommonTypeInfo(OS, UDT);
|
|
}
|
|
};
|
|
|
|
/// Used to serialize the on-disk tag table.
|
|
class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
|
|
public:
|
|
unsigned getUnversionedInfoSize(const TagInfo &TI) {
|
|
// clang-format off
|
|
return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) +
|
|
2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) +
|
|
2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) +
|
|
2 + (TI.SwiftDefaultOwnership ? TI.SwiftDefaultOwnership->size() : 0) +
|
|
2 + (TI.SwiftConformance ? TI.SwiftConformance->size() : 0) +
|
|
3 + getCommonTypeInfoSize(TI);
|
|
// clang-format on
|
|
}
|
|
|
|
void emitUnversionedInfo(raw_ostream &OS, const TagInfo &TI) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
|
|
uint8_t Flags = 0;
|
|
if (auto extensibility = TI.EnumExtensibility) {
|
|
Flags |= static_cast<uint8_t>(extensibility.value()) + 1;
|
|
assert((Flags < (1 << 2)) && "must fit in two bits");
|
|
}
|
|
|
|
Flags <<= 2;
|
|
if (auto value = TI.isFlagEnum())
|
|
Flags |= (value.value() << 1 | 1 << 0);
|
|
|
|
writer.write<uint8_t>(Flags);
|
|
|
|
if (auto Copyable = TI.isSwiftCopyable())
|
|
writer.write<uint8_t>(*Copyable ? kSwiftConforms : kSwiftDoesNotConform);
|
|
else
|
|
writer.write<uint8_t>(0);
|
|
|
|
if (auto Escapable = TI.isSwiftEscapable())
|
|
writer.write<uint8_t>(*Escapable ? kSwiftConforms : kSwiftDoesNotConform);
|
|
else
|
|
writer.write<uint8_t>(0);
|
|
|
|
if (auto ImportAs = TI.SwiftImportAs) {
|
|
writer.write<uint16_t>(ImportAs->size() + 1);
|
|
OS.write(ImportAs->c_str(), ImportAs->size());
|
|
} else {
|
|
writer.write<uint16_t>(0);
|
|
}
|
|
if (auto RetainOp = TI.SwiftRetainOp) {
|
|
writer.write<uint16_t>(RetainOp->size() + 1);
|
|
OS.write(RetainOp->c_str(), RetainOp->size());
|
|
} else {
|
|
writer.write<uint16_t>(0);
|
|
}
|
|
if (auto ReleaseOp = TI.SwiftReleaseOp) {
|
|
writer.write<uint16_t>(ReleaseOp->size() + 1);
|
|
OS.write(ReleaseOp->c_str(), ReleaseOp->size());
|
|
} else {
|
|
writer.write<uint16_t>(0);
|
|
}
|
|
if (auto DefaultOwnership = TI.SwiftDefaultOwnership) {
|
|
writer.write<uint16_t>(DefaultOwnership->size() + 1);
|
|
OS.write(DefaultOwnership->c_str(), DefaultOwnership->size());
|
|
} else {
|
|
writer.write<uint16_t>(0);
|
|
}
|
|
if (auto Conformance = TI.SwiftConformance) {
|
|
writer.write<uint16_t>(Conformance->size() + 1);
|
|
OS.write(Conformance->c_str(), Conformance->size());
|
|
} else {
|
|
writer.write<uint16_t>(0);
|
|
}
|
|
|
|
emitCommonTypeInfo(OS, TI);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void APINotesWriter::Implementation::writeTagBlock(
|
|
llvm::BitstreamWriter &Stream) {
|
|
llvm::BCBlockRAII Scope(Stream, TAG_BLOCK_ID, 3);
|
|
|
|
if (Tags.empty())
|
|
return;
|
|
|
|
{
|
|
llvm::SmallString<4096> HashTableBlob;
|
|
uint32_t Offset;
|
|
{
|
|
llvm::OnDiskChainedHashTableGenerator<TagTableInfo> Generator;
|
|
for (auto &T : Tags)
|
|
Generator.insert(T.first, T.second);
|
|
|
|
llvm::raw_svector_ostream BlobStream(HashTableBlob);
|
|
// Make sure that no bucket is at offset 0
|
|
llvm::support::endian::write<uint32_t>(BlobStream, 0,
|
|
llvm::endianness::little);
|
|
Offset = Generator.Emit(BlobStream);
|
|
}
|
|
|
|
tag_block::TagDataLayout TagData(Stream);
|
|
TagData.emit(Scratch, Offset, HashTableBlob);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
/// Used to serialize the on-disk typedef table.
|
|
class TypedefTableInfo
|
|
: public CommonTypeTableInfo<TypedefTableInfo, TypedefInfo> {
|
|
public:
|
|
unsigned getUnversionedInfoSize(const TypedefInfo &TI) {
|
|
return 1 + getCommonTypeInfoSize(TI);
|
|
}
|
|
|
|
void emitUnversionedInfo(raw_ostream &OS, const TypedefInfo &TI) {
|
|
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
|
|
|
uint8_t Flags = 0;
|
|
if (auto swiftWrapper = TI.SwiftWrapper)
|
|
Flags |= static_cast<uint8_t>(*swiftWrapper) + 1;
|
|
|
|
writer.write<uint8_t>(Flags);
|
|
|
|
emitCommonTypeInfo(OS, TI);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void APINotesWriter::Implementation::writeTypedefBlock(
|
|
llvm::BitstreamWriter &Stream) {
|
|
llvm::BCBlockRAII Scope(Stream, TYPEDEF_BLOCK_ID, 3);
|
|
|
|
if (Typedefs.empty())
|
|
return;
|
|
|
|
{
|
|
llvm::SmallString<4096> HashTableBlob;
|
|
uint32_t Offset;
|
|
{
|
|
llvm::OnDiskChainedHashTableGenerator<TypedefTableInfo> Generator;
|
|
for (auto &T : Typedefs)
|
|
Generator.insert(T.first, T.second);
|
|
|
|
llvm::raw_svector_ostream BlobStream(HashTableBlob);
|
|
// Make sure that no bucket is at offset 0
|
|
llvm::support::endian::write<uint32_t>(BlobStream, 0,
|
|
llvm::endianness::little);
|
|
Offset = Generator.Emit(BlobStream);
|
|
}
|
|
|
|
typedef_block::TypedefDataLayout TypedefData(Stream);
|
|
TypedefData.emit(Scratch, Offset, HashTableBlob);
|
|
}
|
|
}
|
|
|
|
// APINotesWriter
|
|
|
|
APINotesWriter::APINotesWriter(llvm::StringRef ModuleName, const FileEntry *SF)
|
|
: Implementation(new class Implementation(ModuleName, SF)) {}
|
|
|
|
APINotesWriter::~APINotesWriter() = default;
|
|
|
|
void APINotesWriter::writeToStream(llvm::raw_ostream &OS) {
|
|
Implementation->writeToStream(OS);
|
|
}
|
|
|
|
ContextID APINotesWriter::addContext(std::optional<ContextID> ParentCtxID,
|
|
llvm::StringRef Name, ContextKind Kind,
|
|
const ContextInfo &Info,
|
|
llvm::VersionTuple SwiftVersion) {
|
|
IdentifierID NameID = Implementation->getIdentifier(Name);
|
|
|
|
uint32_t RawParentCtxID = ParentCtxID ? ParentCtxID->Value : -1;
|
|
ContextTableKey Key(RawParentCtxID, static_cast<uint8_t>(Kind), NameID);
|
|
auto Known = Implementation->Contexts.find(Key);
|
|
if (Known == Implementation->Contexts.end()) {
|
|
unsigned NextID = Implementation->Contexts.size() + 1;
|
|
|
|
Implementation::VersionedSmallVector<ContextInfo> EmptyVersionedInfo;
|
|
Known = Implementation->Contexts
|
|
.insert(std::make_pair(
|
|
Key, std::make_pair(NextID, EmptyVersionedInfo)))
|
|
.first;
|
|
|
|
Implementation->ContextNames[NextID] = NameID;
|
|
Implementation->ParentContexts[NextID] = RawParentCtxID;
|
|
}
|
|
|
|
// Add this version information.
|
|
auto &VersionedVec = Known->second.second;
|
|
bool Found = false;
|
|
for (auto &Versioned : VersionedVec) {
|
|
if (Versioned.first == SwiftVersion) {
|
|
Versioned.second |= Info;
|
|
Found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Found)
|
|
VersionedVec.push_back({SwiftVersion, Info});
|
|
|
|
return ContextID(Known->second.first);
|
|
}
|
|
|
|
void APINotesWriter::addObjCProperty(ContextID CtxID, StringRef Name,
|
|
bool IsInstanceProperty,
|
|
const ObjCPropertyInfo &Info,
|
|
VersionTuple SwiftVersion) {
|
|
IdentifierID NameID = Implementation->getIdentifier(Name);
|
|
Implementation
|
|
->ObjCProperties[std::make_tuple(CtxID.Value, NameID, IsInstanceProperty)]
|
|
.push_back({SwiftVersion, Info});
|
|
}
|
|
|
|
void APINotesWriter::addObjCMethod(ContextID CtxID, ObjCSelectorRef Selector,
|
|
bool IsInstanceMethod,
|
|
const ObjCMethodInfo &Info,
|
|
VersionTuple SwiftVersion) {
|
|
SelectorID SelID = Implementation->getSelector(Selector);
|
|
auto Key = std::tuple<unsigned, unsigned, char>{CtxID.Value, SelID,
|
|
IsInstanceMethod};
|
|
Implementation->ObjCMethods[Key].push_back({SwiftVersion, Info});
|
|
|
|
// If this method is a designated initializer, update the class to note that
|
|
// it has designated initializers.
|
|
if (Info.DesignatedInit) {
|
|
assert(Implementation->ParentContexts.contains(CtxID.Value));
|
|
uint32_t ParentCtxID = Implementation->ParentContexts[CtxID.Value];
|
|
ContextTableKey CtxKey(ParentCtxID,
|
|
static_cast<uint8_t>(ContextKind::ObjCClass),
|
|
Implementation->ContextNames[CtxID.Value]);
|
|
assert(Implementation->Contexts.contains(CtxKey));
|
|
auto &VersionedVec = Implementation->Contexts[CtxKey].second;
|
|
bool Found = false;
|
|
for (auto &Versioned : VersionedVec) {
|
|
if (Versioned.first == SwiftVersion) {
|
|
Versioned.second.setHasDesignatedInits(true);
|
|
Found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Found) {
|
|
VersionedVec.push_back({SwiftVersion, ContextInfo()});
|
|
VersionedVec.back().second.setHasDesignatedInits(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void APINotesWriter::addCXXMethod(ContextID CtxID, llvm::StringRef Name,
|
|
const CXXMethodInfo &Info,
|
|
VersionTuple SwiftVersion) {
|
|
IdentifierID NameID = Implementation->getIdentifier(Name);
|
|
SingleDeclTableKey Key(CtxID.Value, NameID);
|
|
Implementation->CXXMethods[Key].push_back({SwiftVersion, Info});
|
|
}
|
|
|
|
void APINotesWriter::addField(ContextID CtxID, llvm::StringRef Name,
|
|
const FieldInfo &Info,
|
|
VersionTuple SwiftVersion) {
|
|
IdentifierID NameID = Implementation->getIdentifier(Name);
|
|
SingleDeclTableKey Key(CtxID.Value, NameID);
|
|
Implementation->Fields[Key].push_back({SwiftVersion, Info});
|
|
}
|
|
|
|
void APINotesWriter::addGlobalVariable(std::optional<Context> Ctx,
|
|
llvm::StringRef Name,
|
|
const GlobalVariableInfo &Info,
|
|
VersionTuple SwiftVersion) {
|
|
IdentifierID VariableID = Implementation->getIdentifier(Name);
|
|
SingleDeclTableKey Key(Ctx, VariableID);
|
|
Implementation->GlobalVariables[Key].push_back({SwiftVersion, Info});
|
|
}
|
|
|
|
void APINotesWriter::addGlobalFunction(std::optional<Context> Ctx,
|
|
llvm::StringRef Name,
|
|
const GlobalFunctionInfo &Info,
|
|
VersionTuple SwiftVersion) {
|
|
IdentifierID NameID = Implementation->getIdentifier(Name);
|
|
SingleDeclTableKey Key(Ctx, NameID);
|
|
Implementation->GlobalFunctions[Key].push_back({SwiftVersion, Info});
|
|
}
|
|
|
|
void APINotesWriter::addEnumConstant(llvm::StringRef Name,
|
|
const EnumConstantInfo &Info,
|
|
VersionTuple SwiftVersion) {
|
|
IdentifierID EnumConstantID = Implementation->getIdentifier(Name);
|
|
Implementation->EnumConstants[EnumConstantID].push_back({SwiftVersion, Info});
|
|
}
|
|
|
|
void APINotesWriter::addTag(std::optional<Context> Ctx, llvm::StringRef Name,
|
|
const TagInfo &Info, VersionTuple SwiftVersion) {
|
|
IdentifierID TagID = Implementation->getIdentifier(Name);
|
|
SingleDeclTableKey Key(Ctx, TagID);
|
|
Implementation->Tags[Key].push_back({SwiftVersion, Info});
|
|
}
|
|
|
|
void APINotesWriter::addTypedef(std::optional<Context> Ctx,
|
|
llvm::StringRef Name, const TypedefInfo &Info,
|
|
VersionTuple SwiftVersion) {
|
|
IdentifierID TypedefID = Implementation->getIdentifier(Name);
|
|
SingleDeclTableKey Key(Ctx, TypedefID);
|
|
Implementation->Typedefs[Key].push_back({SwiftVersion, Info});
|
|
}
|
|
} // namespace api_notes
|
|
} // namespace clang
|