| //===-- 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 |