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/Record.h"
16 #include "llvm/TextAPI/Symbol.h"
17 #include <utility>
18
19 using namespace llvm;
20 using namespace llvm::MachO;
21
addRecord(StringRef Name,SymbolFlags Flags,GlobalRecord::Kind GV,RecordLinkage Linkage)22 Record *RecordsSlice::addRecord(StringRef Name, SymbolFlags Flags,
23 GlobalRecord::Kind GV, RecordLinkage Linkage) {
24 // Find a specific Record type to capture.
25 auto [APIName, SymKind] = parseSymbol(Name, Flags);
26 Name = APIName;
27 switch (SymKind) {
28 case SymbolKind::GlobalSymbol:
29 return addGlobal(Name, Linkage, GV, Flags);
30 case SymbolKind::ObjectiveCClass:
31 return addObjCInterface(Name, Linkage);
32 case SymbolKind::ObjectiveCClassEHType:
33 return addObjCInterface(Name, Linkage, /*HasEHType=*/true);
34 case SymbolKind::ObjectiveCInstanceVariable: {
35 auto [Super, IVar] = Name.split('.');
36 // Attempt to find super class.
37 ObjCContainerRecord *Container = findContainer(/*isIVar=*/false, Super);
38 // If not found, create extension since there is no mapped class symbol.
39 if (Container == nullptr)
40 Container = addObjCCategory(Super, {});
41 return addObjCIVar(Container, IVar, Linkage);
42 }
43 }
44
45 llvm_unreachable("unexpected symbol kind when adding to Record Slice");
46 }
47
findContainer(bool IsIVar,StringRef Name) const48 ObjCContainerRecord *RecordsSlice::findContainer(bool IsIVar,
49 StringRef Name) const {
50 StringRef Super = IsIVar ? Name.split('.').first : Name;
51 ObjCContainerRecord *Container = findObjCInterface(Super);
52 // Ivars can only exist with extensions, if they did not come from
53 // class.
54 if (Container == nullptr)
55 Container = findObjCCategory(Super, "");
56 return Container;
57 }
58
59 template <typename R, typename C = RecordMap<R>, typename K = StringRef>
findRecord(K Key,const C & Container)60 R *findRecord(K Key, const C &Container) {
61 const auto *Record = Container.find(Key);
62 if (Record == Container.end())
63 return nullptr;
64 return Record->second.get();
65 }
66
findGlobal(StringRef Name,GlobalRecord::Kind GV) const67 GlobalRecord *RecordsSlice::findGlobal(StringRef Name,
68 GlobalRecord::Kind GV) const {
69 auto *Record = findRecord<GlobalRecord>(Name, Globals);
70 if (!Record)
71 return nullptr;
72
73 switch (GV) {
74 case GlobalRecord::Kind::Variable: {
75 if (!Record->isVariable())
76 return nullptr;
77 break;
78 }
79 case GlobalRecord::Kind::Function: {
80 if (!Record->isFunction())
81 return nullptr;
82 break;
83 }
84 case GlobalRecord::Kind::Unknown:
85 return Record;
86 }
87
88 return Record;
89 }
90
findObjCInterface(StringRef Name) const91 ObjCInterfaceRecord *RecordsSlice::findObjCInterface(StringRef Name) const {
92 return findRecord<ObjCInterfaceRecord>(Name, Classes);
93 }
94
findObjCCategory(StringRef ClassToExtend,StringRef Category) const95 ObjCCategoryRecord *RecordsSlice::findObjCCategory(StringRef ClassToExtend,
96 StringRef Category) const {
97 return findRecord<ObjCCategoryRecord>(std::make_pair(ClassToExtend, Category),
98 Categories);
99 }
100
findObjCIVar(StringRef IVar) const101 ObjCIVarRecord *ObjCContainerRecord::findObjCIVar(StringRef IVar) const {
102 return findRecord<ObjCIVarRecord>(IVar, IVars);
103 }
104
findObjCIVar(bool IsScopedName,StringRef Name) const105 ObjCIVarRecord *RecordsSlice::findObjCIVar(bool IsScopedName,
106 StringRef Name) const {
107 // If scoped name, the name of the container is known.
108 if (IsScopedName) {
109 // IVar does not exist if there is not a container assigned to it.
110 auto *Container = findContainer(/*IsIVar=*/true, Name);
111 if (!Container)
112 return nullptr;
113
114 StringRef IVar = Name.substr(Name.find_first_of('.') + 1);
115 return Container->findObjCIVar(IVar);
116 }
117
118 // Otherwise traverse through containers and attempt to find IVar.
119 auto getIVar = [Name](auto &Records) -> ObjCIVarRecord * {
120 for (const auto &[_, Container] : Records) {
121 if (auto *IVarR = Container->findObjCIVar(Name))
122 return IVarR;
123 }
124 return nullptr;
125 };
126
127 if (auto *IVarRecord = getIVar(Classes))
128 return IVarRecord;
129
130 return getIVar(Categories);
131 }
132
addGlobal(StringRef Name,RecordLinkage Linkage,GlobalRecord::Kind GV,SymbolFlags Flags)133 GlobalRecord *RecordsSlice::addGlobal(StringRef Name, RecordLinkage Linkage,
134 GlobalRecord::Kind GV,
135 SymbolFlags Flags) {
136 if (GV == GlobalRecord::Kind::Function)
137 Flags |= SymbolFlags::Text;
138 else if (GV == GlobalRecord::Kind::Variable)
139 Flags |= SymbolFlags::Data;
140
141 Name = copyString(Name);
142 auto Result = Globals.insert({Name, nullptr});
143 if (Result.second)
144 Result.first->second =
145 std::make_unique<GlobalRecord>(Name, Linkage, Flags, GV);
146 else {
147 updateLinkage(Result.first->second.get(), Linkage);
148 updateFlags(Result.first->second.get(), Flags);
149 }
150 return Result.first->second.get();
151 }
152
addObjCInterface(StringRef Name,RecordLinkage Linkage,bool HasEHType)153 ObjCInterfaceRecord *RecordsSlice::addObjCInterface(StringRef Name,
154 RecordLinkage Linkage,
155 bool HasEHType) {
156 Name = copyString(Name);
157 auto Result = Classes.insert({Name, nullptr});
158 if (Result.second) {
159 Result.first->second =
160 std::make_unique<ObjCInterfaceRecord>(Name, Linkage, HasEHType);
161 } else {
162 // ObjC classes represent multiple symbols that could have competing
163 // linkages, in those cases assign the largest one.
164 if (Linkage >= RecordLinkage::Rexported)
165 updateLinkage(Result.first->second.get(), Linkage);
166 }
167
168 return Result.first->second.get();
169 }
mergeFlags(SymbolFlags Flags,RecordLinkage Linkage)170 SymbolFlags Record::mergeFlags(SymbolFlags Flags, RecordLinkage Linkage) {
171 // Add Linkage properties into Flags.
172 switch (Linkage) {
173 case RecordLinkage::Rexported:
174 Flags |= SymbolFlags::Rexported;
175 return Flags;
176 case RecordLinkage::Undefined:
177 Flags |= SymbolFlags::Undefined;
178 return Flags;
179 default:
180 return Flags;
181 }
182 }
183
addObjCCategory(ObjCCategoryRecord * Record)184 bool ObjCInterfaceRecord::addObjCCategory(ObjCCategoryRecord *Record) {
185 auto Result = Categories.insert({Name, Record});
186 return Result.second;
187 }
188
addObjCCategory(StringRef ClassToExtend,StringRef Category)189 ObjCCategoryRecord *RecordsSlice::addObjCCategory(StringRef ClassToExtend,
190 StringRef Category) {
191 Category = copyString(Category);
192
193 // Add owning record first into record slice.
194 auto Result =
195 Categories.insert({std::make_pair(ClassToExtend, Category), nullptr});
196 if (Result.second)
197 Result.first->second =
198 std::make_unique<ObjCCategoryRecord>(ClassToExtend, Category);
199
200 // Then add reference to it in in the class.
201 if (auto *ObjCClass = findObjCInterface(ClassToExtend))
202 ObjCClass->addObjCCategory(Result.first->second.get());
203
204 return Result.first->second.get();
205 }
206
getObjCIVars() const207 std::vector<ObjCIVarRecord *> ObjCContainerRecord::getObjCIVars() const {
208 std::vector<ObjCIVarRecord *> Records;
209 llvm::for_each(IVars,
210 [&](auto &Record) { Records.push_back(Record.second.get()); });
211 return Records;
212 }
213
214 std::vector<ObjCCategoryRecord *>
getObjCCategories() const215 ObjCInterfaceRecord::getObjCCategories() const {
216 std::vector<ObjCCategoryRecord *> Records;
217 llvm::for_each(Categories,
218 [&](auto &Record) { Records.push_back(Record.second); });
219 return Records;
220 }
221
addObjCIVar(StringRef IVar,RecordLinkage Linkage)222 ObjCIVarRecord *ObjCContainerRecord::addObjCIVar(StringRef IVar,
223 RecordLinkage Linkage) {
224 auto Result = IVars.insert({IVar, nullptr});
225 if (Result.second)
226 Result.first->second = std::make_unique<ObjCIVarRecord>(IVar, Linkage);
227 return Result.first->second.get();
228 }
229
addObjCIVar(ObjCContainerRecord * Container,StringRef Name,RecordLinkage Linkage)230 ObjCIVarRecord *RecordsSlice::addObjCIVar(ObjCContainerRecord *Container,
231 StringRef Name,
232 RecordLinkage Linkage) {
233 Name = copyString(Name);
234 ObjCIVarRecord *Record = Container->addObjCIVar(Name, Linkage);
235 updateLinkage(Record, Linkage);
236 return Record;
237 }
238
copyString(StringRef String)239 StringRef RecordsSlice::copyString(StringRef String) {
240 if (String.empty())
241 return {};
242
243 if (StringAllocator.identifyObject(String.data()))
244 return String;
245
246 void *Ptr = StringAllocator.Allocate(String.size(), 1);
247 memcpy(Ptr, String.data(), String.size());
248 return StringRef(reinterpret_cast<const char *>(Ptr), String.size());
249 }
250
getBinaryAttrs()251 RecordsSlice::BinaryAttrs &RecordsSlice::getBinaryAttrs() {
252 if (!hasBinaryAttrs())
253 BA = std::make_unique<BinaryAttrs>();
254 return *BA;
255 }
256
visit(RecordVisitor & V) const257 void RecordsSlice::visit(RecordVisitor &V) const {
258 for (auto &G : Globals)
259 V.visitGlobal(*G.second);
260 for (auto &C : Classes)
261 V.visitObjCInterface(*C.second);
262 for (auto &Cat : Categories)
263 V.visitObjCCategory(*Cat.second);
264 }
265
266 static std::unique_ptr<InterfaceFile>
createInterfaceFile(const Records & Slices,StringRef InstallName)267 createInterfaceFile(const Records &Slices, StringRef InstallName) {
268 // Pickup symbols first.
269 auto Symbols = std::make_unique<SymbolSet>();
270 for (auto &S : Slices) {
271 if (S->empty())
272 continue;
273 auto &BA = S->getBinaryAttrs();
274 if (BA.InstallName != InstallName)
275 continue;
276
277 SymbolConverter Converter(Symbols.get(), S->getTarget(),
278 !BA.TwoLevelNamespace);
279 S->visit(Converter);
280 }
281
282 auto File = std::make_unique<InterfaceFile>(std::move(Symbols));
283 File->setInstallName(InstallName);
284 // Assign other attributes.
285 for (auto &S : Slices) {
286 if (S->empty())
287 continue;
288 auto &BA = S->getBinaryAttrs();
289 if (BA.InstallName != InstallName)
290 continue;
291 const Target &Targ = S->getTarget();
292 File->addTarget(Targ);
293 if (File->getFileType() == FileType::Invalid)
294 File->setFileType(BA.File);
295 if (BA.AppExtensionSafe && !File->isApplicationExtensionSafe())
296 File->setApplicationExtensionSafe();
297 if (BA.TwoLevelNamespace && !File->isTwoLevelNamespace())
298 File->setTwoLevelNamespace();
299 if (BA.OSLibNotForSharedCache && !File->isOSLibNotForSharedCache())
300 File->setOSLibNotForSharedCache();
301 if (File->getCurrentVersion().empty())
302 File->setCurrentVersion(BA.CurrentVersion);
303 if (File->getCompatibilityVersion().empty())
304 File->setCompatibilityVersion(BA.CompatVersion);
305 if (File->getSwiftABIVersion() == 0)
306 File->setSwiftABIVersion(BA.SwiftABI);
307 if (File->getPath().empty())
308 File->setPath(BA.Path);
309 if (!BA.ParentUmbrella.empty())
310 File->addParentUmbrella(Targ, BA.ParentUmbrella);
311 for (const auto &Client : BA.AllowableClients)
312 File->addAllowableClient(Client, Targ);
313 for (const auto &Lib : BA.RexportedLibraries)
314 File->addReexportedLibrary(Lib, Targ);
315 }
316
317 return File;
318 }
319
320 std::unique_ptr<InterfaceFile>
convertToInterfaceFile(const Records & Slices)321 llvm::MachO::convertToInterfaceFile(const Records &Slices) {
322 std::unique_ptr<InterfaceFile> File;
323 if (Slices.empty())
324 return File;
325
326 SetVector<StringRef> InstallNames;
327 for (auto &S : Slices) {
328 auto Name = S->getBinaryAttrs().InstallName;
329 if (Name.empty())
330 continue;
331 InstallNames.insert(Name);
332 }
333
334 File = createInterfaceFile(Slices, *InstallNames.begin());
335 for (StringRef IN : llvm::drop_begin(InstallNames))
336 File->addDocument(createInterfaceFile(Slices, IN));
337
338 return File;
339 }
340