Skip to content

Commit 021c6a4

Browse files
authored
Merge pull request #59749 from ributzka/apigen
[TBDGen] Add support for Objective-C Categories.
2 parents b16e6d3 + a9e02e9 commit 021c6a4

File tree

8 files changed

+266
-65
lines changed

8 files changed

+266
-65
lines changed

lib/TBDGen/APIGen.cpp

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,30 +24,40 @@
2424
namespace swift {
2525
namespace apigen {
2626

27-
void API::addSymbol(llvm::StringRef symbol, APILoc loc, APILinkage linkage,
27+
void API::addSymbol(StringRef symbol, APILoc loc, APILinkage linkage,
2828
APIFlags flags, APIAccess access,
2929
APIAvailability availability) {
3030
auto *global = new (allocator) GlobalRecord(
3131
symbol, loc, linkage, flags, access, GVKind::Function, availability);
3232
globals.push_back(global);
3333
}
3434

35-
ObjCInterfaceRecord *API::addObjCClass(llvm::StringRef name, APILinkage linkage,
35+
ObjCInterfaceRecord *API::addObjCClass(StringRef name, APILinkage linkage,
3636
APILoc loc, APIAccess access,
3737
APIAvailability availability,
38-
llvm::StringRef superClassName) {
38+
StringRef superClassName) {
3939
auto *interface = new (allocator) ObjCInterfaceRecord(
4040
name, linkage, loc, access, availability, superClassName);
4141
interfaces.push_back(interface);
4242
return interface;
4343
}
4444

45-
void API::addObjCMethod(ObjCInterfaceRecord *cls, llvm::StringRef name,
46-
APILoc loc, APIAccess access, bool isInstanceMethod,
45+
ObjCCategoryRecord *API::addObjCCategory(StringRef name, APILinkage linkage,
46+
APILoc loc, APIAccess access,
47+
APIAvailability availability,
48+
StringRef interface) {
49+
auto *category = new (allocator)
50+
ObjCCategoryRecord(name, linkage, loc, access, availability, interface);
51+
categories.push_back(category);
52+
return category;
53+
}
54+
55+
void API::addObjCMethod(ObjCContainerRecord *record, StringRef name, APILoc loc,
56+
APIAccess access, bool isInstanceMethod,
4757
bool isOptional, APIAvailability availability) {
4858
auto method = new (allocator) ObjCMethodRecord(
4959
name, loc, access, isInstanceMethod, isOptional, availability);
50-
cls->methods.push_back(method);
60+
record->methods.push_back(method);
5161
}
5262

5363
static void serialize(llvm::json::OStream &OS, APIAccess access) {
@@ -151,6 +161,30 @@ static void serialize(llvm::json::OStream &OS,
151161
});
152162
}
153163

164+
static void serialize(llvm::json::OStream &OS,
165+
const ObjCCategoryRecord &record) {
166+
OS.object([&]() {
167+
OS.attribute("name", record.name);
168+
serialize(OS, record.access);
169+
serialize(OS, record.loc);
170+
serialize(OS, record.linkage);
171+
serialize(OS, record.availability);
172+
OS.attribute("interface", record.interface);
173+
OS.attributeArray("instanceMethods", [&]() {
174+
for (auto &method : record.methods) {
175+
if (method->isInstanceMethod)
176+
serialize(OS, *method);
177+
}
178+
});
179+
OS.attributeArray("classMethods", [&]() {
180+
for (auto &method : record.methods) {
181+
if (!method->isInstanceMethod)
182+
serialize(OS, *method);
183+
}
184+
});
185+
});
186+
}
187+
154188
void API::writeAPIJSONFile(llvm::raw_ostream &os, bool PrettyPrint) {
155189
unsigned indentSize = PrettyPrint ? 2 : 0;
156190
llvm::json::OStream JSON(os, indentSize);
@@ -167,6 +201,11 @@ void API::writeAPIJSONFile(llvm::raw_ostream &os, bool PrettyPrint) {
167201
for (const auto *i : interfaces)
168202
serialize(JSON, *i);
169203
});
204+
JSON.attributeArray("categories", [&]() {
205+
llvm::sort(categories, sortAPIRecords);
206+
for (const auto *c : categories)
207+
serialize(JSON, *c);
208+
});
170209
JSON.attribute("version", "1.0");
171210
});
172211
}

lib/TBDGen/APIGen.h

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#ifndef SWIFT_APIGEN_APIGEN_H
1313
#define SWIFT_APIGEN_APIGEN_H
1414

15+
#include "swift/Basic/LLVM.h"
1516
#include "llvm/ADT/BitmaskEnum.h"
1617
#include "llvm/ADT/MapVector.h"
1718
#include "llvm/ADT/Optional.h"
@@ -57,7 +58,7 @@ class APILoc {
5758
APILoc(std::string file, unsigned line, unsigned col)
5859
: file(file), line(line), col(col) {}
5960

60-
llvm::StringRef getFilename() const { return file; }
61+
StringRef getFilename() const { return file; }
6162
unsigned getLine() const { return line; }
6263
unsigned getColumn() const { return col; }
6364

@@ -85,10 +86,10 @@ struct APIRecord {
8586
APIAccess access;
8687
APIAvailability availability;
8788

88-
APIRecord(llvm::StringRef name, APILoc loc, APILinkage linkage,
89-
APIFlags flags, APIAccess access, APIAvailability availability)
90-
: name(name.data(), name.size()), loc(loc), linkage(linkage), flags(flags),
91-
access(access), availability(availability) {}
89+
APIRecord(StringRef name, APILoc loc, APILinkage linkage, APIFlags flags,
90+
APIAccess access, APIAvailability availability)
91+
: name(name.data(), name.size()), loc(loc), linkage(linkage),
92+
flags(flags), access(access), availability(availability) {}
9293

9394
bool isWeakDefined() const {
9495
return (flags & APIFlags::WeakDefined) == APIFlags::WeakDefined;
@@ -116,9 +117,8 @@ enum class GVKind : uint8_t {
116117
struct GlobalRecord : APIRecord {
117118
GVKind kind;
118119

119-
GlobalRecord(llvm::StringRef name, APILoc loc, APILinkage linkage,
120-
APIFlags flags, APIAccess access, GVKind kind,
121-
APIAvailability availability)
120+
GlobalRecord(StringRef name, APILoc loc, APILinkage linkage, APIFlags flags,
121+
APIAccess access, GVKind kind, APIAvailability availability)
122122
: APIRecord(name, loc, linkage, flags, access, availability), kind(kind) {
123123
}
124124
};
@@ -127,7 +127,7 @@ struct ObjCMethodRecord : APIRecord {
127127
bool isInstanceMethod;
128128
bool isOptional;
129129

130-
ObjCMethodRecord(llvm::StringRef name, APILoc loc, APIAccess access,
130+
ObjCMethodRecord(StringRef name, APILoc loc, APIAccess access,
131131
bool isInstanceMethod, bool isOptional,
132132
APIAvailability availability)
133133
: APIRecord(name, loc, APILinkage::Unknown, APIFlags::None, access,
@@ -138,47 +138,63 @@ struct ObjCMethodRecord : APIRecord {
138138
struct ObjCContainerRecord : APIRecord {
139139
std::vector<ObjCMethodRecord*> methods;
140140

141-
ObjCContainerRecord(llvm::StringRef name, APILinkage linkage, APILoc loc,
141+
ObjCContainerRecord(StringRef name, APILinkage linkage, APILoc loc,
142142
APIAccess access, const APIAvailability &availability)
143143
: APIRecord(name, loc, linkage, APIFlags::None, access, availability) {}
144144
};
145145

146146
struct ObjCInterfaceRecord : ObjCContainerRecord {
147147
std::string superClassName;
148-
ObjCInterfaceRecord(llvm::StringRef name, APILinkage linkage, APILoc loc,
148+
ObjCInterfaceRecord(StringRef name, APILinkage linkage, APILoc loc,
149149
APIAccess access, APIAvailability availability,
150-
llvm::StringRef superClassName)
150+
StringRef superClassName)
151151
: ObjCContainerRecord(name, linkage, loc, access, availability),
152152
superClassName(superClassName.data(), superClassName.size()) {}
153153
};
154154

155+
struct ObjCCategoryRecord : ObjCContainerRecord {
156+
std::string interface;
157+
158+
ObjCCategoryRecord(StringRef name, APILinkage linkage, APILoc loc,
159+
APIAccess access, APIAvailability availability,
160+
StringRef interface)
161+
: ObjCContainerRecord(name, linkage, loc, access, availability),
162+
interface(interface.data(), interface.size()) {}
163+
};
164+
155165
class API {
156166
public:
157167
API(const llvm::Triple &triple) : target(triple) {}
158168

159169
const llvm::Triple &getTarget() const { return target; }
160170

161-
void addSymbol(llvm::StringRef symbol, APILoc loc, APILinkage linkage,
171+
void addSymbol(StringRef symbol, APILoc loc, APILinkage linkage,
162172
APIFlags flags, APIAccess access,
163173
APIAvailability availability);
164174

165-
ObjCInterfaceRecord *addObjCClass(llvm::StringRef name, APILinkage linkage,
175+
ObjCInterfaceRecord *addObjCClass(StringRef name, APILinkage linkage,
166176
APILoc loc, APIAccess access,
167177
APIAvailability availability,
168-
llvm::StringRef superClassName);
178+
StringRef superClassName);
179+
180+
ObjCCategoryRecord *addObjCCategory(StringRef name, APILinkage linkage,
181+
APILoc loc, APIAccess access,
182+
APIAvailability availability,
183+
StringRef interface);
169184

170-
void addObjCMethod(ObjCInterfaceRecord *cls, llvm::StringRef name, APILoc loc,
185+
void addObjCMethod(ObjCContainerRecord *record, StringRef name, APILoc loc,
171186
APIAccess access, bool isInstanceMethod, bool isOptional,
172187
APIAvailability availability);
173188

174-
void writeAPIJSONFile(llvm::raw_ostream &os, bool PrettyPrint = false);
189+
void writeAPIJSONFile(raw_ostream &os, bool PrettyPrint = false);
175190

176191
private:
177192
const llvm::Triple target;
178193

179194
llvm::BumpPtrAllocator allocator;
180195
std::vector<GlobalRecord*> globals;
181196
std::vector<ObjCInterfaceRecord*> interfaces;
197+
std::vector<ObjCCategoryRecord *> categories;
182198
};
183199

184200
} // end namespace apigen

lib/TBDGen/TBDGen.cpp

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,15 @@ void TBDGenVisitor::visitAbstractFunctionDecl(AbstractFunctionDecl *AFD) {
747747
if (AFD->hasAsync()) {
748748
addAsyncFunctionPointerSymbol(SILDeclRef(AFD));
749749
}
750+
751+
// Skip non objc compatible methods or non-public methods.
752+
if (isa<DestructorDecl>(AFD) || !AFD->isObjC() ||
753+
AFD->getFormalAccess() != AccessLevel::Public)
754+
return;
755+
if (auto *CD = dyn_cast<ClassDecl>(AFD->getDeclContext()))
756+
recorder.addObjCMethod(CD, SILDeclRef(AFD));
757+
else if (auto *ED = dyn_cast<ExtensionDecl>(AFD->getDeclContext()))
758+
recorder.addObjCMethod(ED, SILDeclRef(AFD));
750759
}
751760

752761
void TBDGenVisitor::visitFuncDecl(FuncDecl *FD) {
@@ -956,30 +965,9 @@ void TBDGenVisitor::visitClassDecl(ClassDecl *CD) {
956965
}
957966

958967
TBD.addMethodDescriptor(method);
959-
960-
if (auto methodOrCtorOrDtor = method.getDecl()) {
961-
// Skip non objc compatible methods or non-public methods.
962-
if (!methodOrCtorOrDtor->isObjC() ||
963-
methodOrCtorOrDtor->getFormalAccess() != AccessLevel::Public)
964-
return;
965-
966-
// only handle FuncDecl here. Initializers are handled in
967-
// visitConstructorDecl.
968-
if (isa<FuncDecl>(methodOrCtorOrDtor))
969-
recorder.addObjCMethod(CD, method);
970-
}
971968
}
972969

973-
void addMethodOverride(SILDeclRef baseRef, SILDeclRef derivedRef) {
974-
if (auto methodOrCtorOrDtor = derivedRef.getDecl()) {
975-
if (!methodOrCtorOrDtor->isObjC() ||
976-
methodOrCtorOrDtor->getFormalAccess() != AccessLevel::Public)
977-
return;
978-
979-
if (isa<FuncDecl>(methodOrCtorOrDtor))
980-
recorder.addObjCMethod(CD, derivedRef);
981-
}
982-
}
970+
void addMethodOverride(SILDeclRef baseRef, SILDeclRef derivedRef) {}
983971

984972
void addPlaceholder(MissingMemberDecl *) {}
985973

@@ -1001,10 +989,6 @@ void TBDGenVisitor::visitConstructorDecl(ConstructorDecl *CD) {
1001989
addAsyncFunctionPointerSymbol(
1002990
SILDeclRef(CD, SILDeclRef::Kind::Initializer));
1003991
}
1004-
if (auto parentClass = CD->getParent()->getSelfClassDecl()) {
1005-
if (parentClass->isObjC() || CD->isObjC())
1006-
recorder.addObjCMethod(parentClass, SILDeclRef(CD));
1007-
}
1008992
}
1009993

1010994
visitAbstractFunctionDecl(CD);
@@ -1397,8 +1381,11 @@ class APIGenRecorder final : public APIRecorder {
13971381
addOrGetObjCInterface(decl);
13981382
}
13991383

1400-
void addObjCMethod(const ClassDecl *cls,
1401-
SILDeclRef method) override {
1384+
void addObjCCategory(const ExtensionDecl *decl) override {
1385+
addOrGetObjCCategory(decl);
1386+
}
1387+
1388+
void addObjCMethod(const GenericContext *ctx, SILDeclRef method) override {
14021389
SmallString<128> buffer;
14031390
StringRef name = getSelectorName(method, buffer);
14041391
apigen::APIAvailability availability;
@@ -1413,12 +1400,23 @@ class APIGenRecorder final : public APIRecorder {
14131400
access = apigen::APIAccess::Private;
14141401
}
14151402

1416-
auto *clsRecord = addOrGetObjCInterface(cls);
1417-
api.addObjCMethod(clsRecord, name, moduleLoc, access, isInstanceMethod,
1418-
false, availability);
1403+
apigen::ObjCContainerRecord *record = nullptr;
1404+
if (auto *cls = dyn_cast<ClassDecl>(ctx))
1405+
record = addOrGetObjCInterface(cls);
1406+
else if (auto *ext = dyn_cast<ExtensionDecl>(ctx))
1407+
record = addOrGetObjCCategory(ext);
1408+
1409+
if (record)
1410+
api.addObjCMethod(record, name, moduleLoc, access, isInstanceMethod,
1411+
false, availability);
14191412
}
14201413

14211414
private:
1415+
/// Follow the naming schema that IRGen uses for Categories (see
1416+
/// ClassDataBuilder).
1417+
using CategoryNameKey = std::pair<const ClassDecl *, const ModuleDecl *>;
1418+
llvm::DenseMap<CategoryNameKey, unsigned> CategoryCounts;
1419+
14221420
apigen::APIAvailability getAvailability(const Decl *decl) {
14231421
bool unavailable = false;
14241422
std::string introduced, obsoleted;
@@ -1475,11 +1473,46 @@ class APIGenRecorder final : public APIRecorder {
14751473
return cls;
14761474
}
14771475

1476+
void buildCategoryName(const ExtensionDecl *ext, const ClassDecl *cls,
1477+
SmallVectorImpl<char> &s) {
1478+
llvm::raw_svector_ostream os(s);
1479+
ModuleDecl *module = ext->getParentModule();
1480+
os << module->getName();
1481+
unsigned categoryCount = CategoryCounts[{cls, module}]++;
1482+
if (categoryCount > 0)
1483+
os << categoryCount;
1484+
}
1485+
1486+
apigen::ObjCCategoryRecord *addOrGetObjCCategory(const ExtensionDecl *decl) {
1487+
auto entry = categoryMap.find(decl);
1488+
if (entry != categoryMap.end())
1489+
return entry->second;
1490+
1491+
SmallString<128> interfaceBuffer;
1492+
SmallString<128> nameBuffer;
1493+
ClassDecl *cls = decl->getSelfClassDecl();
1494+
auto interface = cls->getObjCRuntimeName(interfaceBuffer);
1495+
buildCategoryName(decl, cls, nameBuffer);
1496+
apigen::APIAvailability availability = getAvailability(decl);
1497+
apigen::APIAccess access =
1498+
decl->isSPI() ? apigen::APIAccess::Private : apigen::APIAccess::Public;
1499+
apigen::APILinkage linkage =
1500+
decl->getMaxAccessLevel() == AccessLevel::Public
1501+
? apigen::APILinkage::Exported
1502+
: apigen::APILinkage::Internal;
1503+
auto category = api.addObjCCategory(nameBuffer, linkage, moduleLoc, access,
1504+
availability, interface);
1505+
categoryMap.try_emplace(decl, category);
1506+
return category;
1507+
}
1508+
14781509
apigen::API &api;
14791510
ModuleDecl *module;
14801511
apigen::APILoc moduleLoc;
14811512

14821513
llvm::DenseMap<const ClassDecl*, apigen::ObjCInterfaceRecord*> classMap;
1514+
llvm::DenseMap<const ExtensionDecl *, apigen::ObjCCategoryRecord *>
1515+
categoryMap;
14831516
};
14841517

14851518
apigen::API APIGenRequest::evaluate(Evaluator &evaluator,

lib/TBDGen/TBDGenVisitor.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ class APIRecorder {
6868
virtual void addSymbol(StringRef name, llvm::MachO::SymbolKind kind,
6969
SymbolSource source) {}
7070
virtual void addObjCInterface(const ClassDecl *decl) {}
71-
virtual void addObjCMethod(const ClassDecl *cls, SILDeclRef method) {}
71+
virtual void addObjCCategory(const ExtensionDecl *decl) {}
72+
virtual void addObjCMethod(const GenericContext *ctx, SILDeclRef method) {}
7273
};
7374

7475
class SimpleAPIRecorder final : public APIRecorder {

0 commit comments

Comments
 (0)