aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/APINotes/APINotesWriter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/APINotes/APINotesWriter.cpp')
-rw-r--r--clang/lib/APINotes/APINotesWriter.cpp1384
1 files changed, 1384 insertions, 0 deletions
diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp
new file mode 100644
index 000000000000..62a2ab179991
--- /dev/null
+++ b/clang/lib/APINotes/APINotesWriter.cpp
@@ -0,0 +1,1384 @@
+//===-- 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<ObjCContextInfo>>>
+ ObjCContexts;
+
+ /// 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> ObjCContextNames;
+
+ /// 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 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;
+
+ /// Mapping from selectors to selector ID.
+ llvm::DenseMap<StoredObjCSelector, SelectorID> SelectorIDs;
+
+ /// Information about global variables.
+ ///
+ /// Indexed by the context ID, contextKind, identifier ID.
+ llvm::DenseMap<
+ ContextTableKey,
+ llvm::SmallVector<std::pair<VersionTuple, GlobalVariableInfo>, 1>>
+ GlobalVariables;
+
+ /// Information about global functions.
+ ///
+ /// Indexed by the context ID, contextKind, identifier ID.
+ llvm::DenseMap<
+ ContextTableKey,
+ 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, contextKind, identifier ID.
+ llvm::DenseMap<ContextTableKey,
+ llvm::SmallVector<std::pair<VersionTuple, TagInfo>, 1>>
+ Tags;
+
+ /// Information about typedefs.
+ ///
+ /// Indexed by the context ID, contextKind, identifier ID.
+ llvm::DenseMap<ContextTableKey,
+ llvm::SmallVector<std::pair<VersionTuple, TypedefInfo>, 1>>
+ Typedefs;
+
+ /// Retrieve the ID for the given identifier.
+ IdentifierID getIdentifier(StringRef Identifier) {
+ if (Identifier.empty())
+ return 0;
+
+ auto Known = IdentifierIDs.find(Identifier);
+ if (Known != IdentifierIDs.end())
+ return Known->second;
+
+ // Add to the identifier table.
+ Known = IdentifierIDs.insert({Identifier, IdentifierIDs.size() + 1}).first;
+ return Known->second;
+ }
+
+ /// Retrieve the ID for the given selector.
+ SelectorID getSelector(ObjCSelectorRef SelectorRef) {
+ // Translate the selector reference into a stored selector.
+ StoredObjCSelector Selector;
+ Selector.Identifiers.reserve(SelectorRef.Identifiers.size());
+ for (auto piece : SelectorRef.Identifiers)
+ Selector.Identifiers.push_back(getIdentifier(piece));
+
+ // Look for the stored selector.
+ auto Known = SelectorIDs.find(Selector);
+ if (Known != SelectorIDs.end())
+ return Known->second;
+
+ // Add to the selector table.
+ Known = SelectorIDs.insert({Selector, SelectorIDs.size()}).first;
+ return Known->second;
+ }
+
+private:
+ void writeBlockInfoBlock(llvm::BitstreamWriter &Stream);
+ void writeControlBlock(llvm::BitstreamWriter &Stream);
+ void writeIdentifierBlock(llvm::BitstreamWriter &Stream);
+ void writeObjCContextBlock(llvm::BitstreamWriter &Stream);
+ void writeObjCPropertyBlock(llvm::BitstreamWriter &Stream);
+ void writeObjCMethodBlock(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);
+ writeObjCContextBlock(Stream);
+ writeObjCPropertyBlock(Stream);
+ writeObjCMethodBlock(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(objc_context_block, OBJC_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 ObjCContextIDTableInfo {
+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.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 ObjCContextInfoTableInfo
+ : public VersionedTableInfo<ObjCContextInfoTableInfo, unsigned,
+ ObjCContextInfo> {
+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 ObjCContextInfo &OCI) {
+ return getCommonTypeInfoSize(OCI) + 1;
+ }
+
+ void emitUnversionedInfo(raw_ostream &OS, const ObjCContextInfo &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::writeObjCContextBlock(
+ llvm::BitstreamWriter &Stream) {
+ llvm::BCBlockRAII restoreBlock(Stream, OBJC_CONTEXT_BLOCK_ID, 3);
+
+ if (ObjCContexts.empty())
+ return;
+
+ {
+ llvm::SmallString<4096> HashTableBlob;
+ uint32_t Offset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<ObjCContextIDTableInfo> Generator;
+ for (auto &OC : ObjCContexts)
+ 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);
+ }
+
+ objc_context_block::ObjCContextIDLayout ObjCContextID(Stream);
+ ObjCContextID.emit(Scratch, Offset, HashTableBlob);
+ }
+
+ {
+ llvm::SmallString<4096> HashTableBlob;
+ uint32_t Offset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<ObjCContextInfoTableInfo> Generator;
+ for (auto &OC : ObjCContexts)
+ 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);
+ }
+
+ objc_context_block::ObjCContextInfoLayout ObjCContextInfo(Stream);
+ ObjCContextInfo.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();
+}
+
+/// 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 &);
+
+/// 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) {
+ return getFunctionInfoSize(OMI) + 1;
+ }
+
+ 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;
+ writer.write<uint8_t>(flags);
+
+ emitFunctionInfo(OS, OMI);
+ }
+};
+} // 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);
+ }
+}
+
+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, ContextTableKey,
+ GlobalVariableInfo> {
+public:
+ unsigned getKeyLength(key_type_ref) {
+ return sizeof(uint32_t) + sizeof(uint8_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<uint8_t>(Key.contextKind);
+ writer.write<uint32_t>(Key.contextID);
+ }
+
+ 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 <<= 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();
+ 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()});
+}
+
+/// Used to serialize the on-disk global function table.
+class GlobalFunctionTableInfo
+ : public VersionedTableInfo<GlobalFunctionTableInfo, ContextTableKey,
+ GlobalFunctionInfo> {
+public:
+ unsigned getKeyLength(key_type_ref) {
+ return sizeof(uint32_t) + sizeof(uint8_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<uint8_t>(Key.contextKind);
+ writer.write<uint32_t>(Key.contextID);
+ }
+
+ 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, ContextTableKey, 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(uint8_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<uint8_t>(Key.contextKind);
+ writer.write<IdentifierID>(Key.contextID);
+ }
+
+ 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) {
+ return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) +
+ 2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) +
+ 2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) +
+ 1 + getCommonTypeInfoSize(TI);
+ }
+
+ 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 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);
+ }
+
+ 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::addObjCContext(std::optional<ContextID> ParentCtxID,
+ StringRef Name, ContextKind Kind,
+ const ObjCContextInfo &Info,
+ 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->ObjCContexts.find(Key);
+ if (Known == Implementation->ObjCContexts.end()) {
+ unsigned NextID = Implementation->ObjCContexts.size() + 1;
+
+ Implementation::VersionedSmallVector<ObjCContextInfo> EmptyVersionedInfo;
+ Known = Implementation->ObjCContexts
+ .insert(std::make_pair(
+ Key, std::make_pair(NextID, EmptyVersionedInfo)))
+ .first;
+
+ Implementation->ObjCContextNames[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->ObjCContextNames[CtxID.Value]);
+ assert(Implementation->ObjCContexts.contains(CtxKey));
+ auto &VersionedVec = Implementation->ObjCContexts[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, ObjCContextInfo()});
+ VersionedVec.back().second.setHasDesignatedInits(true);
+ }
+ }
+}
+
+void APINotesWriter::addGlobalVariable(std::optional<Context> Ctx,
+ llvm::StringRef Name,
+ const GlobalVariableInfo &Info,
+ VersionTuple SwiftVersion) {
+ IdentifierID VariableID = Implementation->getIdentifier(Name);
+ ContextTableKey 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);
+ ContextTableKey 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);
+ ContextTableKey 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);
+ ContextTableKey Key(Ctx, TypedefID);
+ Implementation->Typedefs[Key].push_back({SwiftVersion, Info});
+}
+} // namespace api_notes
+} // namespace clang