|  | //===-- 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.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. | 
|  | 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 == &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 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) + | 
|  | 2 + 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 Copyable = TI.isSwiftCopyable()) | 
|  | writer.write<uint8_t>(*Copyable ? kSwiftCopyable : kSwiftNonCopyable); | 
|  | 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); | 
|  | } | 
|  |  | 
|  | 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 |