diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h index eab03a2a83012..dad44623e16ae 100644 --- a/clang/include/clang/APINotes/APINotesWriter.h +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -9,7 +9,9 @@ #ifndef LLVM_CLANG_APINOTES_WRITER_H #define LLVM_CLANG_APINOTES_WRITER_H +#include "clang/APINotes/Types.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/VersionTuple.h" #include "llvm/Support/raw_ostream.h" #include @@ -30,8 +32,79 @@ class APINotesWriter { APINotesWriter &operator=(const APINotesWriter &) = delete; void writeToStream(llvm::raw_ostream &OS); + + /// Add information about a specific Objective-C class or protocol or a C++ + /// namespace. + /// + /// \param Name The name of this class/protocol/namespace. + /// \param Kind Whether this is a class, a protocol, or a namespace. + /// \param Info Information about this class/protocol/namespace. + /// + /// \returns the ID of the class, protocol, or namespace, which can be used to + /// add properties and methods to the class/protocol/namespace. + ContextID addObjCContext(std::optional ParentCtxID, + llvm::StringRef Name, ContextKind Kind, + const ObjCContextInfo &Info, + llvm::VersionTuple SwiftVersion); + + /// Add information about a specific Objective-C property. + /// + /// \param CtxID The context in which this property resides. + /// \param Name The name of this property. + /// \param Info Information about this property. + void addObjCProperty(ContextID CtxID, llvm::StringRef Name, + bool IsInstanceProperty, const ObjCPropertyInfo &Info, + llvm::VersionTuple SwiftVersion); + + /// Add information about a specific Objective-C method. + /// + /// \param CtxID The context in which this method resides. + /// \param Selector The selector that names this method. + /// \param IsInstanceMethod Whether this method is an instance method + /// (vs. a class method). + /// \param Info Information about this method. + void addObjCMethod(ContextID CtxID, ObjCSelectorRef Selector, + bool IsInstanceMethod, const ObjCMethodInfo &Info, + llvm::VersionTuple SwiftVersion); + + /// Add information about a global variable. + /// + /// \param Name The name of this global variable. + /// \param Info Information about this global variable. + void addGlobalVariable(std::optional Ctx, llvm::StringRef Name, + const GlobalVariableInfo &Info, + llvm::VersionTuple SwiftVersion); + + /// Add information about a global function. + /// + /// \param Name The name of this global function. + /// \param Info Information about this global function. + void addGlobalFunction(std::optional Ctx, llvm::StringRef Name, + const GlobalFunctionInfo &Info, + llvm::VersionTuple SwiftVersion); + + /// Add information about an enumerator. + /// + /// \param Name The name of this enumerator. + /// \param Info Information about this enumerator. + void addEnumConstant(llvm::StringRef Name, const EnumConstantInfo &Info, + llvm::VersionTuple SwiftVersion); + + /// Add information about a tag (struct/union/enum/C++ class). + /// + /// \param Name The name of this tag. + /// \param Info Information about this tag. + void addTag(std::optional Ctx, llvm::StringRef Name, + const TagInfo &Info, llvm::VersionTuple SwiftVersion); + + /// Add information about a typedef. + /// + /// \param Name The name of this typedef. + /// \param Info Information about this typedef. + void addTypedef(std::optional Ctx, llvm::StringRef Name, + const TypedefInfo &Info, llvm::VersionTuple SwiftVersion); }; } // namespace api_notes } // namespace clang -#endif +#endif // LLVM_CLANG_APINOTES_WRITER_H diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 817d6ac1bb3fe..79c8079191fef 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_APINOTES_TYPES_H #include "clang/Basic/Specifiers.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include #include @@ -749,6 +750,16 @@ struct Context { Context(ContextID id, ContextKind kind) : id(id), kind(kind) {} }; + +/// A temporary reference to an Objective-C selector, suitable for +/// referencing selector data on the stack. +/// +/// Instances of this struct do not store references to any of the +/// data they contain; it is up to the user to ensure that the data +/// referenced by the identifier list persists. +struct ObjCSelectorRef { + llvm::ArrayRef Identifiers; +}; } // namespace api_notes } // namespace clang diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 1e960773074e2..b52f017901dbc 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -268,7 +268,8 @@ struct ContextTableKey { ContextTableKey(std::optional context, IdentifierID nameID) : parentContextID(context ? context->id.Value : (uint32_t)-1), - contextKind(context ? (uint8_t)context->kind : (uint8_t)-1), + contextKind(context ? static_cast(context->kind) + : static_cast(-1)), contextID(nameID) {} llvm::hash_code hashValue() const { diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index f357af90f949c..0126c5ec0798d 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -20,6 +20,8 @@ namespace clang { namespace api_notes { class APINotesWriter::Implementation { + friend class APINotesWriter; + template using VersionedSmallVector = llvm::SmallVector, 1>; @@ -48,6 +50,9 @@ class APINotesWriter::Implementation { /// Indexed by context ID, provides the parent context ID. llvm::DenseMap ParentContexts; + /// Mapping from context IDs to the identifier ID holding the name. + llvm::DenseMap ObjCContextNames; + /// Information about Objective-C properties. /// /// Indexed by the context ID, property name, and whether this is an @@ -105,6 +110,39 @@ class APINotesWriter::Implementation { llvm::SmallVector, 1>> Typedefs; + /// Retrieve the ID for the given identifier. + IdentifierID getIdentifier(StringRef Identifier) { + if (Identifier.empty()) + return 0; + + auto Known = IdentifierIDs.find(Identifier); + if (Known != IdentifierIDs.end()) + return Known->second; + + // Add to the identifier table. + Known = IdentifierIDs.insert({Identifier, IdentifierIDs.size() + 1}).first; + return Known->second; + } + + /// Retrieve the ID for the given selector. + SelectorID getSelector(ObjCSelectorRef SelectorRef) { + // Translate the selector reference into a stored selector. + StoredObjCSelector Selector; + Selector.Identifiers.reserve(SelectorRef.Identifiers.size()); + for (auto piece : SelectorRef.Identifiers) + Selector.Identifiers.push_back(getIdentifier(piece)); + + // Look for the stored selector. + auto Known = SelectorIDs.find(Selector); + if (Known != SelectorIDs.end()) + return Known->second; + + // Add to the selector table. + Known = SelectorIDs.insert({Selector, SelectorIDs.size()}).first; + return Known->second; + } + +private: void writeBlockInfoBlock(llvm::BitstreamWriter &Stream); void writeControlBlock(llvm::BitstreamWriter &Stream); void writeIdentifierBlock(llvm::BitstreamWriter &Stream); @@ -433,10 +471,6 @@ class VersionedTableInfo { using hash_value_type = size_t; using offset_type = unsigned; - hash_value_type ComputeHash(key_type_ref Key) { - return llvm::hash_value(Key); - } - std::pair EmitKeyDataLength(raw_ostream &OS, key_type_ref Key, data_type_ref Data) { uint32_t KeyLength = asDerived().getKeyLength(Key); @@ -1200,5 +1234,129 @@ APINotesWriter::~APINotesWriter() = default; void APINotesWriter::writeToStream(llvm::raw_ostream &OS) { Implementation->writeToStream(OS); } + +ContextID APINotesWriter::addObjCContext(std::optional 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(Kind), NameID); + auto Known = Implementation->ObjCContexts.find(Key); + if (Known == Implementation->ObjCContexts.end()) { + unsigned NextID = Implementation->ObjCContexts.size() + 1; + + Implementation::VersionedSmallVector 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{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(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 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 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 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 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