[clang] [APINotes] Upstream APINotesWriter (PR #65187)

Egor Zhdan via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 4 11:25:24 PDT 2023


https://github.com/egorzhdan updated https://github.com/llvm/llvm-project/pull/65187:

>From a3e72323513a5ac102abb6c28ae4071955cc2373 Mon Sep 17 00:00:00 2001
From: Egor Zhdan <e_zhdan at apple.com>
Date: Fri, 1 Sep 2023 22:07:47 +0100
Subject: [PATCH] [APINotes] Upstream APINotesWriter

This upstreams more of the Clang API Notes functionality that is currently implemented in the Apple fork: https://github.com/apple/llvm-project/tree/next/clang/lib/APINotes
---
 clang/include/clang/APINotes/APINotesWriter.h |  75 +++++++-
 clang/include/clang/APINotes/Types.h          |  11 ++
 clang/lib/APINotes/APINotesFormat.h           |   3 +-
 clang/lib/APINotes/APINotesWriter.cpp         | 166 +++++++++++++++++-
 4 files changed, 249 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h
index eab03a2a830125d..dad44623e16aeb2 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 <memory>
@@ -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<ContextID> 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<Context> 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<Context> 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<Context> 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<Context> 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 817d6ac1bb3fe82..79c8079191fef3b 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 <climits>
 #include <optional>
@@ -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<llvm::StringRef> Identifiers;
+};
 } // namespace api_notes
 } // namespace clang
 
diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h
index 1e960773074e26f..b52f017901dbcab 100644
--- a/clang/lib/APINotes/APINotesFormat.h
+++ b/clang/lib/APINotes/APINotesFormat.h
@@ -268,7 +268,8 @@ struct ContextTableKey {
 
   ContextTableKey(std::optional<Context> context, IdentifierID nameID)
       : parentContextID(context ? context->id.Value : (uint32_t)-1),
-        contextKind(context ? (uint8_t)context->kind : (uint8_t)-1),
+        contextKind(context ? static_cast<uint8_t>(context->kind)
+                            : static_cast<uint8_t>(-1)),
         contextID(nameID) {}
 
   llvm::hash_code hashValue() const {
diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp
index f357af90f949c8a..0126c5ec0798dc5 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 <typename T>
   using VersionedSmallVector =
       llvm::SmallVector<std::pair<llvm::VersionTuple, T>, 1>;
@@ -48,6 +50,9 @@ class APINotesWriter::Implementation {
   /// 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
@@ -105,6 +110,39 @@ class APINotesWriter::Implementation {
                  llvm::SmallVector<std::pair<VersionTuple, TypedefInfo>, 1>>
       Typedefs;
 
+  /// Retrieve the ID for the given identifier.
+  IdentifierID getIdentifier(StringRef Identifier) {
+    if (Identifier.empty())
+      return 0;
+
+    auto Known = IdentifierIDs.find(Identifier);
+    if (Known != IdentifierIDs.end())
+      return Known->second;
+
+    // Add to the identifier table.
+    Known = IdentifierIDs.insert({Identifier, IdentifierIDs.size() + 1}).first;
+    return Known->second;
+  }
+
+  /// Retrieve the ID for the given selector.
+  SelectorID getSelector(ObjCSelectorRef SelectorRef) {
+    // Translate the selector reference into a stored selector.
+    StoredObjCSelector Selector;
+    Selector.Identifiers.reserve(SelectorRef.Identifiers.size());
+    for (auto piece : SelectorRef.Identifiers)
+      Selector.Identifiers.push_back(getIdentifier(piece));
+
+    // Look for the stored selector.
+    auto Known = SelectorIDs.find(Selector);
+    if (Known != SelectorIDs.end())
+      return Known->second;
+
+    // Add to the selector table.
+    Known = SelectorIDs.insert({Selector, SelectorIDs.size()}).first;
+    return Known->second;
+  }
+
+private:
   void writeBlockInfoBlock(llvm::BitstreamWriter &Stream);
   void writeControlBlock(llvm::BitstreamWriter &Stream);
   void writeIdentifierBlock(llvm::BitstreamWriter &Stream);
@@ -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<unsigned, unsigned>
   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<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



More information about the cfe-commits mailing list