1//===- RecordsSlice.cpp --------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Implements the Records Slice APIs.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm/TextAPI/RecordsSlice.h"
14#include "llvm/ADT/SetVector.h"
15#include "llvm/TextAPI/InterfaceFile.h"
16#include "llvm/TextAPI/Record.h"
17#include "llvm/TextAPI/Symbol.h"
18#include <utility>
19
20using namespace llvm;
21using namespace llvm::MachO;
22
23Record *RecordsSlice::addRecord(StringRef Name, SymbolFlags Flags,
24 GlobalRecord::Kind GV, RecordLinkage Linkage) {
25 // Find a specific Record type to capture.
26 auto [APIName, SymKind, InterfaceType] = parseSymbol(SymName: Name);
27 Name = APIName;
28 switch (SymKind) {
29 case EncodeKind::GlobalSymbol:
30 return addGlobal(Name, Linkage, GV, Flags);
31 case EncodeKind::ObjectiveCClass:
32 return addObjCInterface(Name, Linkage, SymType: InterfaceType);
33 case EncodeKind::ObjectiveCClassEHType: {
34 ObjCInterfaceRecord *Rec = addObjCInterface(Name, Linkage, SymType: InterfaceType);
35 // When classes without ehtype are used in try/catch blocks
36 // a weak-defined symbol is exported.
37 if ((Flags & SymbolFlags::WeakDefined) == SymbolFlags::WeakDefined)
38 updateFlags(R: Rec, F: SymbolFlags::WeakDefined);
39 return Rec;
40 }
41 case EncodeKind::ObjectiveCInstanceVariable: {
42 auto [Super, IVar] = Name.split(Separator: '.');
43 // Attempt to find super class.
44 ObjCContainerRecord *Container = findContainer(/*isIVar=*/IsIVar: false, Name: Super);
45 // If not found, create extension since there is no mapped class symbol.
46 if (Container == nullptr)
47 Container = addObjCCategory(ClassToExtend: Super, Category: {});
48 return addObjCIVar(Container, Name: IVar, Linkage);
49 }
50 }
51
52 llvm_unreachable("unexpected symbol kind when adding to Record Slice");
53}
54
55ObjCContainerRecord *RecordsSlice::findContainer(bool IsIVar,
56 StringRef Name) const {
57 StringRef Super = IsIVar ? Name.split(Separator: '.').first : Name;
58 ObjCContainerRecord *Container = findObjCInterface(Name: Super);
59 // Ivars can only exist with extensions, if they did not come from
60 // class.
61 if (Container == nullptr)
62 Container = findObjCCategory(ClassToExtend: Super, Category: "");
63 return Container;
64}
65
66template <typename R, typename C = RecordMap<R>, typename K = StringRef>
67R *findRecord(K Key, const C &Container) {
68 const auto *Record = Container.find(Key);
69 if (Record == Container.end())
70 return nullptr;
71 return Record->second.get();
72}
73
74GlobalRecord *RecordsSlice::findGlobal(StringRef Name,
75 GlobalRecord::Kind GV) const {
76 auto *Record = findRecord<GlobalRecord>(Key: Name, Container: Globals);
77 if (!Record)
78 return nullptr;
79
80 switch (GV) {
81 case GlobalRecord::Kind::Variable: {
82 if (!Record->isVariable())
83 return nullptr;
84 break;
85 }
86 case GlobalRecord::Kind::Function: {
87 if (!Record->isFunction())
88 return nullptr;
89 break;
90 }
91 case GlobalRecord::Kind::Unknown:
92 return Record;
93 }
94
95 return Record;
96}
97
98RecordLinkage
99ObjCInterfaceRecord::getLinkageForSymbol(ObjCIFSymbolKind CurrType) const {
100 assert(CurrType <= ObjCIFSymbolKind::EHType &&
101 "expected single ObjCIFSymbolKind enum value");
102 if (CurrType == ObjCIFSymbolKind::Class)
103 return Linkages.Class;
104
105 if (CurrType == ObjCIFSymbolKind::MetaClass)
106 return Linkages.MetaClass;
107
108 if (CurrType == ObjCIFSymbolKind::EHType)
109 return Linkages.EHType;
110
111 llvm_unreachable("unexpected ObjCIFSymbolKind");
112}
113
114void ObjCInterfaceRecord::updateLinkageForSymbols(ObjCIFSymbolKind SymType,
115 RecordLinkage Link) {
116 if ((SymType & ObjCIFSymbolKind::Class) == ObjCIFSymbolKind::Class)
117 Linkages.Class = std::max(a: Link, b: Linkages.Class);
118 if ((SymType & ObjCIFSymbolKind::MetaClass) == ObjCIFSymbolKind::MetaClass)
119 Linkages.MetaClass = std::max(a: Link, b: Linkages.MetaClass);
120 if ((SymType & ObjCIFSymbolKind::EHType) == ObjCIFSymbolKind::EHType)
121 Linkages.EHType = std::max(a: Link, b: Linkages.EHType);
122
123 // Obj-C Classes represent multiple symbols that could have competing
124 // linkages, in this case assign the largest one, when querying the linkage of
125 // the record itself. This allows visitors pick whether they want to account
126 // for complete symbol information.
127 Linkage = std::max(l: {Linkages.Class, Linkages.MetaClass, Linkages.EHType});
128}
129
130ObjCInterfaceRecord *RecordsSlice::findObjCInterface(StringRef Name) const {
131 return findRecord<ObjCInterfaceRecord>(Key: Name, Container: Classes);
132}
133
134ObjCCategoryRecord *RecordsSlice::findObjCCategory(StringRef ClassToExtend,
135 StringRef Category) const {
136 return findRecord<ObjCCategoryRecord>(Key: std::make_pair(x&: ClassToExtend, y&: Category),
137 Container: Categories);
138}
139
140ObjCIVarRecord *ObjCContainerRecord::findObjCIVar(StringRef IVar) const {
141 return findRecord<ObjCIVarRecord>(Key: IVar, Container: IVars);
142}
143
144ObjCIVarRecord *RecordsSlice::findObjCIVar(bool IsScopedName,
145 StringRef Name) const {
146 // If scoped name, the name of the container is known.
147 if (IsScopedName) {
148 // IVar does not exist if there is not a container assigned to it.
149 auto *Container = findContainer(/*IsIVar=*/true, Name);
150 if (!Container)
151 return nullptr;
152
153 StringRef IVar = Name.substr(Start: Name.find_first_of(C: '.') + 1);
154 return Container->findObjCIVar(IVar);
155 }
156
157 // Otherwise traverse through containers and attempt to find IVar.
158 auto getIVar = [Name](auto &Records) -> ObjCIVarRecord * {
159 for (const auto &[_, Container] : Records) {
160 if (auto *IVarR = Container->findObjCIVar(Name))
161 return IVarR;
162 }
163 return nullptr;
164 };
165
166 if (auto *IVarRecord = getIVar(Classes))
167 return IVarRecord;
168
169 return getIVar(Categories);
170}
171
172GlobalRecord *RecordsSlice::addGlobal(StringRef Name, RecordLinkage Linkage,
173 GlobalRecord::Kind GV, SymbolFlags Flags,
174 bool Inlined) {
175 if (GV == GlobalRecord::Kind::Function)
176 Flags |= SymbolFlags::Text;
177 else if (GV == GlobalRecord::Kind::Variable)
178 Flags |= SymbolFlags::Data;
179
180 Name = copyString(String: Name);
181 auto Result = Globals.try_emplace(Key: Name);
182 if (Result.second)
183 Result.first->second =
184 std::make_unique<GlobalRecord>(args&: Name, args&: Linkage, args&: Flags, args&: GV, args&: Inlined);
185 else {
186 updateLinkage(R: Result.first->second.get(), L: Linkage);
187 updateFlags(R: Result.first->second.get(), F: Flags);
188 }
189 return Result.first->second.get();
190}
191
192ObjCInterfaceRecord *RecordsSlice::addObjCInterface(StringRef Name,
193 RecordLinkage Linkage,
194 ObjCIFSymbolKind SymType) {
195 Name = copyString(String: Name);
196 auto Result = Classes.try_emplace(Key: Name);
197 if (Result.second)
198 Result.first->second =
199 std::make_unique<ObjCInterfaceRecord>(args&: Name, args&: Linkage, args&: SymType);
200 else
201 Result.first->second->updateLinkageForSymbols(SymType, Link: Linkage);
202 return Result.first->second.get();
203}
204
205SymbolFlags Record::mergeFlags(SymbolFlags Flags, RecordLinkage Linkage) {
206 // Add Linkage properties into Flags.
207 switch (Linkage) {
208 case RecordLinkage::Rexported:
209 Flags |= SymbolFlags::Rexported;
210 return Flags;
211 case RecordLinkage::Undefined:
212 Flags |= SymbolFlags::Undefined;
213 return Flags;
214 default:
215 return Flags;
216 }
217}
218
219bool ObjCInterfaceRecord::addObjCCategory(ObjCCategoryRecord *Record) {
220 auto Result = Categories.insert(KV: {Name, Record});
221 return Result.second;
222}
223
224ObjCCategoryRecord *RecordsSlice::addObjCCategory(StringRef ClassToExtend,
225 StringRef Category) {
226 Category = copyString(String: Category);
227 ClassToExtend = copyString(String: ClassToExtend);
228
229 // Add owning record first into record slice.
230 auto Result = Categories.try_emplace(Key: std::make_pair(x&: ClassToExtend, y&: Category));
231 if (Result.second)
232 Result.first->second =
233 std::make_unique<ObjCCategoryRecord>(args&: ClassToExtend, args&: Category);
234
235 // Then add reference to it in in the class.
236 if (auto *ObjCClass = findObjCInterface(Name: ClassToExtend))
237 ObjCClass->addObjCCategory(Record: Result.first->second.get());
238
239 return Result.first->second.get();
240}
241
242std::vector<ObjCIVarRecord *> ObjCContainerRecord::getObjCIVars() const {
243 std::vector<ObjCIVarRecord *> Records;
244 Records.reserve(n: IVars.size());
245 for (const auto &Record : IVars)
246 Records.push_back(x: Record.second.get());
247 return Records;
248}
249
250std::vector<ObjCCategoryRecord *>
251ObjCInterfaceRecord::getObjCCategories() const {
252 std::vector<ObjCCategoryRecord *> Records;
253 Records.reserve(n: Categories.size());
254 for (const auto &Record : Categories)
255 Records.push_back(x: Record.second);
256 return Records;
257}
258
259ObjCIVarRecord *ObjCContainerRecord::addObjCIVar(StringRef IVar,
260 RecordLinkage Linkage) {
261 auto Result = IVars.try_emplace(Key: IVar);
262 if (Result.second)
263 Result.first->second = std::make_unique<ObjCIVarRecord>(args&: IVar, args&: Linkage);
264 return Result.first->second.get();
265}
266
267ObjCIVarRecord *RecordsSlice::addObjCIVar(ObjCContainerRecord *Container,
268 StringRef Name,
269 RecordLinkage Linkage) {
270 Name = copyString(String: Name);
271 ObjCIVarRecord *Record = Container->addObjCIVar(IVar: Name, Linkage);
272 updateLinkage(R: Record, L: Linkage);
273 return Record;
274}
275
276StringRef RecordsSlice::copyString(StringRef String) {
277 if (String.empty())
278 return {};
279
280 if (StringAllocator.identifyObject(Ptr: String.data()))
281 return String;
282
283 void *Ptr = StringAllocator.Allocate(Size: String.size(), Alignment: 1);
284 memcpy(dest: Ptr, src: String.data(), n: String.size());
285 return StringRef(reinterpret_cast<const char *>(Ptr), String.size());
286}
287
288RecordsSlice::BinaryAttrs &RecordsSlice::getBinaryAttrs() {
289 if (!hasBinaryAttrs())
290 BA = std::make_unique<BinaryAttrs>();
291 return *BA;
292}
293
294void RecordsSlice::visit(RecordVisitor &V) const {
295 for (auto &G : Globals)
296 V.visitGlobal(*G.second);
297 for (auto &C : Classes)
298 V.visitObjCInterface(*C.second);
299 for (auto &Cat : Categories)
300 V.visitObjCCategory(*Cat.second);
301}
302
303static std::unique_ptr<InterfaceFile>
304createInterfaceFile(const Records &Slices, StringRef InstallName) {
305 // Pickup symbols first.
306 auto Symbols = std::make_unique<SymbolSet>();
307 for (auto &S : Slices) {
308 if (S->empty())
309 continue;
310 auto &BA = S->getBinaryAttrs();
311 if (BA.InstallName != InstallName)
312 continue;
313
314 SymbolConverter Converter(Symbols.get(), S->getTarget(),
315 !BA.TwoLevelNamespace);
316 S->visit(V&: Converter);
317 }
318
319 auto File = std::make_unique<InterfaceFile>(args: std::move(Symbols));
320 File->setInstallName(InstallName);
321 // Assign other attributes.
322 for (auto &S : Slices) {
323 if (S->empty())
324 continue;
325 auto &BA = S->getBinaryAttrs();
326 if (BA.InstallName != InstallName)
327 continue;
328 const Target &Targ = S->getTarget();
329 File->addTarget(Target: Targ);
330 File->setFromBinaryAttrs(BA, Targ);
331 }
332
333 return File;
334}
335
336std::unique_ptr<InterfaceFile>
337llvm::MachO::convertToInterfaceFile(const Records &Slices) {
338 std::unique_ptr<InterfaceFile> File;
339 if (Slices.empty())
340 return File;
341
342 SetVector<StringRef> InstallNames;
343 for (auto &S : Slices) {
344 auto Name = S->getBinaryAttrs().InstallName;
345 if (Name.empty())
346 continue;
347 InstallNames.insert(X: Name);
348 }
349
350 File = createInterfaceFile(Slices, InstallName: *InstallNames.begin());
351 for (StringRef IN : llvm::drop_begin(RangeOrContainer&: InstallNames))
352 File->addDocument(Document: createInterfaceFile(Slices, InstallName: IN));
353
354 return File;
355}
356