[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