[clang] [llvm] [InstallAPI] Collect symbols from ObjC Ivars (PR #83632)

Cyndy Ishida via cfe-commits cfe-commits at lists.llvm.org
Fri Mar 1 15:09:00 PST 2024


https://github.com/cyndyishida created https://github.com/llvm/llvm-project/pull/83632

None

>From 30f1fb6e8afbc0f793c3276ffda03fc50505ec73 Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Thu, 29 Feb 2024 20:52:02 -0800
Subject: [PATCH] [InstallAPI] Collect symbols from ObjC Ivars

---
 clang/include/clang/InstallAPI/Frontend.h | 35 ++++++++++++++++
 clang/include/clang/InstallAPI/Visitor.h  | 12 ++++++
 clang/lib/InstallAPI/Frontend.cpp         | 25 +++++++++++
 clang/lib/InstallAPI/Visitor.cpp          | 51 ++++++++++++++++++++++-
 clang/test/InstallAPI/objcclasses.test    | 39 ++++++++++++++++-
 llvm/include/llvm/TextAPI/Record.h        |  1 +
 llvm/lib/TextAPI/RecordsSlice.cpp         |  1 +
 7 files changed, 161 insertions(+), 3 deletions(-)

diff --git a/clang/include/clang/InstallAPI/Frontend.h b/clang/include/clang/InstallAPI/Frontend.h
index d72b4680fde400..8774321e990c13 100644
--- a/clang/include/clang/InstallAPI/Frontend.h
+++ b/clang/include/clang/InstallAPI/Frontend.h
@@ -28,7 +28,10 @@ namespace installapi {
 using SymbolFlags = llvm::MachO::SymbolFlags;
 using RecordLinkage = llvm::MachO::RecordLinkage;
 using GlobalRecord = llvm::MachO::GlobalRecord;
+using ObjCContainerRecord = llvm::MachO::ObjCContainerRecord;
 using ObjCInterfaceRecord = llvm::MachO::ObjCInterfaceRecord;
+using ObjCCategoryRecord = llvm::MachO::ObjCCategoryRecord;
+using ObjCIVarRecord = llvm::MachO::ObjCIVarRecord;
 
 // Represents a collection of frontend records for a library that are tied to a
 // darwin target triple.
@@ -69,6 +72,38 @@ class FrontendRecordsSlice : public llvm::MachO::RecordsSlice {
                                         const Decl *D, HeaderType Access,
                                         bool IsEHType);
 
+  /// Add ObjC Category record with attributes from AST.
+  ///
+  /// \param ClassToExtend The name of class that is extended by category, not
+  /// symbol.
+  /// \param CategoryName The name of category, not symbol.
+  /// \param Avail The availability information tied
+  /// to the active target triple.
+  /// \param D The pointer to the declaration from traversing AST.
+  /// \param Access The intended access level of symbol.
+  /// \return The non-owning pointer to added record in slice.
+  ObjCCategoryRecord *addObjCCategory(StringRef ClassToExtend,
+                                      StringRef CategoryName,
+                                      const clang::AvailabilityInfo Avail,
+                                      const Decl *D, HeaderType Access);
+
+  /// Add ObjC IVar record with attributes from AST.
+  ///
+  /// \param Container The owning pointer for instance variable.
+  /// \param Name The name of ivar, not symbol.
+  /// \param Linkage The linkage of symbol.
+  /// \param Avail The availability information tied to the active target
+  /// triple.
+  /// \param D The pointer to the declaration from traversing AST.
+  /// \param Access The intended access level of symbol.
+  /// \param AC The access control tied to the ivar declaration.
+  /// \return The non-owning pointer to added record in slice.
+  ObjCIVarRecord *addObjCIVar(ObjCContainerRecord *Container,
+                              StringRef IvarName, RecordLinkage Linkage,
+                              const clang::AvailabilityInfo Avail,
+                              const Decl *D, HeaderType Access,
+                              const clang::ObjCIvarDecl::AccessControl AC);
+
 private:
   /// Frontend information captured about records.
   struct FrontendAttrs {
diff --git a/clang/include/clang/InstallAPI/Visitor.h b/clang/include/clang/InstallAPI/Visitor.h
index 60a05005df841a..ff0a9957aa86bc 100644
--- a/clang/include/clang/InstallAPI/Visitor.h
+++ b/clang/include/clang/InstallAPI/Visitor.h
@@ -42,10 +42,22 @@ class InstallAPIVisitor final : public ASTConsumer,
   /// ivars, properties, and methods of the class.
   bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D);
 
+  /// Collect Objective-C Category/Extension declarations.
+  ///
+  /// The class that is being extended might come from a different library and
+  /// is therefore itself not collected.
+  bool VisitObjCCategoryDecl(const ObjCCategoryDecl *D);
+
 private:
   std::string getMangledName(const NamedDecl *D) const;
   std::string getBackendMangledName(llvm::Twine Name) const;
   std::optional<HeaderType> getAccessForDecl(const NamedDecl *D) const;
+  void recordObjCInstanceVariables(
+      const ASTContext &ASTCtx, llvm::MachO::ObjCContainerRecord *Record,
+      StringRef SuperClass,
+      const llvm::iterator_range<
+          DeclContext::specific_decl_iterator<ObjCIvarDecl>>
+          Ivars);
 
   InstallAPIContext &Ctx;
   SourceManager &SrcMgr;
diff --git a/clang/lib/InstallAPI/Frontend.cpp b/clang/lib/InstallAPI/Frontend.cpp
index caa6e7e8a40544..240a80e1d3d82c 100644
--- a/clang/lib/InstallAPI/Frontend.cpp
+++ b/clang/lib/InstallAPI/Frontend.cpp
@@ -39,6 +39,31 @@ ObjCInterfaceRecord *FrontendRecordsSlice::addObjCInterface(
   return ObjCR;
 }
 
+ObjCCategoryRecord *FrontendRecordsSlice::addObjCCategory(
+    StringRef ClassToExtend, StringRef CategoryName,
+    const clang::AvailabilityInfo Avail, const Decl *D, HeaderType Access) {
+  auto *ObjCR =
+      llvm::MachO::RecordsSlice::addObjCCategory(ClassToExtend, CategoryName);
+  FrontendRecords.insert({ObjCR, FrontendAttrs{Avail, D, Access}});
+  return ObjCR;
+}
+
+ObjCIVarRecord *FrontendRecordsSlice::addObjCIVar(
+    ObjCContainerRecord *Container, StringRef IvarName, RecordLinkage Linkage,
+    const clang::AvailabilityInfo Avail, const Decl *D, HeaderType Access,
+    const clang::ObjCIvarDecl::AccessControl AC) {
+  // If the decl otherwise would have been exported, check their access control.
+  // Ivar's linkage is also determined by this.
+  if ((Linkage == RecordLinkage::Exported) &&
+      ((AC == ObjCIvarDecl::Private) || (AC == ObjCIvarDecl::Package)))
+    Linkage = RecordLinkage::Internal;
+  auto *ObjCR =
+      llvm::MachO::RecordsSlice::addObjCIVar(Container, IvarName, Linkage);
+  FrontendRecords.insert({ObjCR, FrontendAttrs{Avail, D, Access}});
+
+  return nullptr;
+}
+
 std::optional<HeaderType>
 InstallAPIContext::findAndRecordFile(const FileEntry *FE,
                                      const Preprocessor &PP) {
diff --git a/clang/lib/InstallAPI/Visitor.cpp b/clang/lib/InstallAPI/Visitor.cpp
index 355a092520c3cd..fbe6f1dabe005d 100644
--- a/clang/lib/InstallAPI/Visitor.cpp
+++ b/clang/lib/InstallAPI/Visitor.cpp
@@ -99,6 +99,29 @@ static bool hasObjCExceptionAttribute(const ObjCInterfaceDecl *D) {
 
   return false;
 }
+void InstallAPIVisitor::recordObjCInstanceVariables(
+    const ASTContext &ASTCtx, ObjCContainerRecord *Record, StringRef SuperClass,
+    const llvm::iterator_range<
+        DeclContext::specific_decl_iterator<ObjCIvarDecl>>
+        Ivars) {
+  RecordLinkage Linkage = RecordLinkage::Exported;
+  const RecordLinkage ContainerLinkage = Record->getLinkage();
+  // If fragile, set to unknown.
+  if (ASTCtx.getLangOpts().ObjCRuntime.isFragile())
+    Linkage = RecordLinkage::Unknown;
+  // Linkage should be inherited from container.
+  else if (ContainerLinkage != RecordLinkage::Unknown)
+    Linkage = ContainerLinkage;
+  for (const auto *IV : Ivars) {
+    auto Access = getAccessForDecl(IV);
+    if (!Access)
+      continue;
+    StringRef Name = IV->getName();
+    const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(IV);
+    auto AC = IV->getCanonicalAccessControl();
+    Ctx.Slice->addObjCIVar(Record, Name, Linkage, Avail, IV, *Access, AC);
+  }
+}
 
 bool InstallAPIVisitor::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) {
   // Skip forward declaration for classes (@class)
@@ -118,7 +141,33 @@ bool InstallAPIVisitor::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) {
       (!D->getASTContext().getLangOpts().ObjCRuntime.isFragile() &&
        hasObjCExceptionAttribute(D));
 
-  Ctx.Slice->addObjCInterface(Name, Linkage, Avail, D, *Access, IsEHType);
+  ObjCInterfaceRecord *Class =
+      Ctx.Slice->addObjCInterface(Name, Linkage, Avail, D, *Access, IsEHType);
+
+  // Get base class.
+  StringRef SuperClassName;
+  if (const auto *SuperClass = D->getSuperClass())
+    SuperClassName = SuperClass->getObjCRuntimeNameAsString();
+
+  recordObjCInstanceVariables(D->getASTContext(), Class, SuperClassName,
+                              D->ivars());
+  return true;
+}
+
+bool InstallAPIVisitor::VisitObjCCategoryDecl(const ObjCCategoryDecl *D) {
+  StringRef CategoryName = D->getName();
+  // Skip over declarations that access could not be collected for.
+  auto Access = getAccessForDecl(D);
+  if (!Access)
+    return true;
+  const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
+  const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
+  const StringRef InterfaceName = InterfaceD->getName();
+
+  ObjCCategoryRecord *Category = Ctx.Slice->addObjCCategory(
+      InterfaceName, CategoryName, Avail, D, *Access);
+  recordObjCInstanceVariables(D->getASTContext(), Category, InterfaceName,
+                              D->ivars());
   return true;
 }
 
diff --git a/clang/test/InstallAPI/objcclasses.test b/clang/test/InstallAPI/objcclasses.test
index d32291c64c472d..c40a22d01056f2 100644
--- a/clang/test/InstallAPI/objcclasses.test
+++ b/clang/test/InstallAPI/objcclasses.test
@@ -27,12 +27,40 @@ __attribute__((objc_exception))
 @interface Exception 
 @end
 
+
+//--- Foo.framework/PrivateHeaders/Foo_Private.h
+#import <Foo/Foo.h>
+
+ at interface ClassWithIvars : Visible  {
+  char _ivar1;
+  char _ivar2;
+ at private
+  int _privateIVar;
+ at protected
+  int _externalIVar;
+ at package 
+  int _internalIVar;
+}
+ at end
+
+ at interface Exception () {
+ at public 
+  char _ivarFromExtension;
+}
+ at end
+
+
 //--- inputs.json.in
 {
   "headers": [ {
     "path" : "DSTROOT/Foo.framework/Headers/Foo.h",
     "type" : "public"
-  }],
+  }, 
+  {
+    "path" : "DSTROOT/Foo.framework/PrivateHeaders/Foo_Private.h",
+    "type" : "private"
+  }
+  ],
   "version": "3"
 }
 
@@ -54,10 +82,17 @@ __attribute__((objc_exception))
         "data": {
           "objc_class": [
             "Exception",
-            "Visible"
+            "Visible",
+            "ClassWithIvars"
           ],
           "objc_eh_type": [
             "Exception"
+          ],
+          "objc_ivar": [
+            "Exception._ivarFromExtension",
+            "ClassWithIvars._ivar2",
+            "ClassWithIvars._ivar1",
+            "ClassWithIvars._externalIVar"
           ]
         }
       }
diff --git a/llvm/include/llvm/TextAPI/Record.h b/llvm/include/llvm/TextAPI/Record.h
index 3b30e6c8c26762..867d6a23588326 100644
--- a/llvm/include/llvm/TextAPI/Record.h
+++ b/llvm/include/llvm/TextAPI/Record.h
@@ -143,6 +143,7 @@ class ObjCContainerRecord : public Record {
   ObjCIVarRecord *addObjCIVar(StringRef IVar, RecordLinkage Linkage);
   ObjCIVarRecord *findObjCIVar(StringRef IVar) const;
   std::vector<ObjCIVarRecord *> getObjCIVars() const;
+  RecordLinkage getLinkage() const { return Linkage; }
 
 private:
   RecordMap<ObjCIVarRecord> IVars;
diff --git a/llvm/lib/TextAPI/RecordsSlice.cpp b/llvm/lib/TextAPI/RecordsSlice.cpp
index fb961a91cc656c..db52a2cdd85c9c 100644
--- a/llvm/lib/TextAPI/RecordsSlice.cpp
+++ b/llvm/lib/TextAPI/RecordsSlice.cpp
@@ -225,6 +225,7 @@ bool ObjCInterfaceRecord::addObjCCategory(ObjCCategoryRecord *Record) {
 ObjCCategoryRecord *RecordsSlice::addObjCCategory(StringRef ClassToExtend,
                                                   StringRef Category) {
   Category = copyString(Category);
+  ClassToExtend = copyString(ClassToExtend);
 
   // Add owning record first into record slice.
   auto Result =



More information about the cfe-commits mailing list