xref: /freebsd/contrib/llvm-project/llvm/lib/TextAPI/RecordsSlice.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
20 using namespace llvm;
21 using namespace llvm::MachO;
22 
addRecord(StringRef Name,SymbolFlags Flags,GlobalRecord::Kind GV,RecordLinkage Linkage)23 Record *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(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, InterfaceType);
33   case EncodeKind::ObjectiveCClassEHType: {
34     ObjCInterfaceRecord *Rec = addObjCInterface(Name, Linkage, 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(Rec, SymbolFlags::WeakDefined);
39     return Rec;
40   }
41   case EncodeKind::ObjectiveCInstanceVariable: {
42     auto [Super, IVar] = Name.split('.');
43     // Attempt to find super class.
44     ObjCContainerRecord *Container = findContainer(/*isIVar=*/false, Super);
45     // If not found, create extension since there is no mapped class symbol.
46     if (Container == nullptr)
47       Container = addObjCCategory(Super, {});
48     return addObjCIVar(Container, IVar, Linkage);
49   }
50   }
51 
52   llvm_unreachable("unexpected symbol kind when adding to Record Slice");
53 }
54 
findContainer(bool IsIVar,StringRef Name) const55 ObjCContainerRecord *RecordsSlice::findContainer(bool IsIVar,
56                                                  StringRef Name) const {
57   StringRef Super = IsIVar ? Name.split('.').first : Name;
58   ObjCContainerRecord *Container = findObjCInterface(Super);
59   // Ivars can only exist with extensions, if they did not come from
60   // class.
61   if (Container == nullptr)
62     Container = findObjCCategory(Super, "");
63   return Container;
64 }
65 
66 template <typename R, typename C = RecordMap<R>, typename K = StringRef>
findRecord(K Key,const C & Container)67 R *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 
findGlobal(StringRef Name,GlobalRecord::Kind GV) const74 GlobalRecord *RecordsSlice::findGlobal(StringRef Name,
75                                        GlobalRecord::Kind GV) const {
76   auto *Record = findRecord<GlobalRecord>(Name, 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 
98 RecordLinkage
getLinkageForSymbol(ObjCIFSymbolKind CurrType) const99 ObjCInterfaceRecord::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 
updateLinkageForSymbols(ObjCIFSymbolKind SymType,RecordLinkage Link)114 void ObjCInterfaceRecord::updateLinkageForSymbols(ObjCIFSymbolKind SymType,
115                                                   RecordLinkage Link) {
116   if ((SymType & ObjCIFSymbolKind::Class) == ObjCIFSymbolKind::Class)
117     Linkages.Class = std::max(Link, Linkages.Class);
118   if ((SymType & ObjCIFSymbolKind::MetaClass) == ObjCIFSymbolKind::MetaClass)
119     Linkages.MetaClass = std::max(Link, Linkages.MetaClass);
120   if ((SymType & ObjCIFSymbolKind::EHType) == ObjCIFSymbolKind::EHType)
121     Linkages.EHType = std::max(Link, 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 =
128       std::max(Linkages.Class, std::max(Linkages.MetaClass, Linkages.EHType));
129 }
130 
findObjCInterface(StringRef Name) const131 ObjCInterfaceRecord *RecordsSlice::findObjCInterface(StringRef Name) const {
132   return findRecord<ObjCInterfaceRecord>(Name, Classes);
133 }
134 
findObjCCategory(StringRef ClassToExtend,StringRef Category) const135 ObjCCategoryRecord *RecordsSlice::findObjCCategory(StringRef ClassToExtend,
136                                                    StringRef Category) const {
137   return findRecord<ObjCCategoryRecord>(std::make_pair(ClassToExtend, Category),
138                                         Categories);
139 }
140 
findObjCIVar(StringRef IVar) const141 ObjCIVarRecord *ObjCContainerRecord::findObjCIVar(StringRef IVar) const {
142   return findRecord<ObjCIVarRecord>(IVar, IVars);
143 }
144 
findObjCIVar(bool IsScopedName,StringRef Name) const145 ObjCIVarRecord *RecordsSlice::findObjCIVar(bool IsScopedName,
146                                            StringRef Name) const {
147   // If scoped name, the name of the container is known.
148   if (IsScopedName) {
149     // IVar does not exist if there is not a container assigned to it.
150     auto *Container = findContainer(/*IsIVar=*/true, Name);
151     if (!Container)
152       return nullptr;
153 
154     StringRef IVar = Name.substr(Name.find_first_of('.') + 1);
155     return Container->findObjCIVar(IVar);
156   }
157 
158   // Otherwise traverse through containers and attempt to find IVar.
159   auto getIVar = [Name](auto &Records) -> ObjCIVarRecord * {
160     for (const auto &[_, Container] : Records) {
161       if (auto *IVarR = Container->findObjCIVar(Name))
162         return IVarR;
163     }
164     return nullptr;
165   };
166 
167   if (auto *IVarRecord = getIVar(Classes))
168     return IVarRecord;
169 
170   return getIVar(Categories);
171 }
172 
addGlobal(StringRef Name,RecordLinkage Linkage,GlobalRecord::Kind GV,SymbolFlags Flags,bool Inlined)173 GlobalRecord *RecordsSlice::addGlobal(StringRef Name, RecordLinkage Linkage,
174                                       GlobalRecord::Kind GV, SymbolFlags Flags,
175                                       bool Inlined) {
176   if (GV == GlobalRecord::Kind::Function)
177     Flags |= SymbolFlags::Text;
178   else if (GV == GlobalRecord::Kind::Variable)
179     Flags |= SymbolFlags::Data;
180 
181   Name = copyString(Name);
182   auto Result = Globals.insert({Name, nullptr});
183   if (Result.second)
184     Result.first->second =
185         std::make_unique<GlobalRecord>(Name, Linkage, Flags, GV, Inlined);
186   else {
187     updateLinkage(Result.first->second.get(), Linkage);
188     updateFlags(Result.first->second.get(), Flags);
189   }
190   return Result.first->second.get();
191 }
192 
addObjCInterface(StringRef Name,RecordLinkage Linkage,ObjCIFSymbolKind SymType)193 ObjCInterfaceRecord *RecordsSlice::addObjCInterface(StringRef Name,
194                                                     RecordLinkage Linkage,
195                                                     ObjCIFSymbolKind SymType) {
196   Name = copyString(Name);
197   auto Result = Classes.insert({Name, nullptr});
198   if (Result.second)
199     Result.first->second =
200         std::make_unique<ObjCInterfaceRecord>(Name, Linkage, SymType);
201   else
202     Result.first->second->updateLinkageForSymbols(SymType, Linkage);
203   return Result.first->second.get();
204 }
205 
mergeFlags(SymbolFlags Flags,RecordLinkage Linkage)206 SymbolFlags Record::mergeFlags(SymbolFlags Flags, RecordLinkage Linkage) {
207   // Add Linkage properties into Flags.
208   switch (Linkage) {
209   case RecordLinkage::Rexported:
210     Flags |= SymbolFlags::Rexported;
211     return Flags;
212   case RecordLinkage::Undefined:
213     Flags |= SymbolFlags::Undefined;
214     return Flags;
215   default:
216     return Flags;
217   }
218 }
219 
addObjCCategory(ObjCCategoryRecord * Record)220 bool ObjCInterfaceRecord::addObjCCategory(ObjCCategoryRecord *Record) {
221   auto Result = Categories.insert({Name, Record});
222   return Result.second;
223 }
224 
addObjCCategory(StringRef ClassToExtend,StringRef Category)225 ObjCCategoryRecord *RecordsSlice::addObjCCategory(StringRef ClassToExtend,
226                                                   StringRef Category) {
227   Category = copyString(Category);
228   ClassToExtend = copyString(ClassToExtend);
229 
230   // Add owning record first into record slice.
231   auto Result =
232       Categories.insert({std::make_pair(ClassToExtend, Category), nullptr});
233   if (Result.second)
234     Result.first->second =
235         std::make_unique<ObjCCategoryRecord>(ClassToExtend, Category);
236 
237   // Then add reference to it in in the class.
238   if (auto *ObjCClass = findObjCInterface(ClassToExtend))
239     ObjCClass->addObjCCategory(Result.first->second.get());
240 
241   return Result.first->second.get();
242 }
243 
getObjCIVars() const244 std::vector<ObjCIVarRecord *> ObjCContainerRecord::getObjCIVars() const {
245   std::vector<ObjCIVarRecord *> Records;
246   llvm::for_each(IVars,
247                  [&](auto &Record) { Records.push_back(Record.second.get()); });
248   return Records;
249 }
250 
251 std::vector<ObjCCategoryRecord *>
getObjCCategories() const252 ObjCInterfaceRecord::getObjCCategories() const {
253   std::vector<ObjCCategoryRecord *> Records;
254   llvm::for_each(Categories,
255                  [&](auto &Record) { Records.push_back(Record.second); });
256   return Records;
257 }
258 
addObjCIVar(StringRef IVar,RecordLinkage Linkage)259 ObjCIVarRecord *ObjCContainerRecord::addObjCIVar(StringRef IVar,
260                                                  RecordLinkage Linkage) {
261   auto Result = IVars.insert({IVar, nullptr});
262   if (Result.second)
263     Result.first->second = std::make_unique<ObjCIVarRecord>(IVar, Linkage);
264   return Result.first->second.get();
265 }
266 
addObjCIVar(ObjCContainerRecord * Container,StringRef Name,RecordLinkage Linkage)267 ObjCIVarRecord *RecordsSlice::addObjCIVar(ObjCContainerRecord *Container,
268                                           StringRef Name,
269                                           RecordLinkage Linkage) {
270   Name = copyString(Name);
271   ObjCIVarRecord *Record = Container->addObjCIVar(Name, Linkage);
272   updateLinkage(Record, Linkage);
273   return Record;
274 }
275 
copyString(StringRef String)276 StringRef RecordsSlice::copyString(StringRef String) {
277   if (String.empty())
278     return {};
279 
280   if (StringAllocator.identifyObject(String.data()))
281     return String;
282 
283   void *Ptr = StringAllocator.Allocate(String.size(), 1);
284   memcpy(Ptr, String.data(), String.size());
285   return StringRef(reinterpret_cast<const char *>(Ptr), String.size());
286 }
287 
getBinaryAttrs()288 RecordsSlice::BinaryAttrs &RecordsSlice::getBinaryAttrs() {
289   if (!hasBinaryAttrs())
290     BA = std::make_unique<BinaryAttrs>();
291   return *BA;
292 }
293 
visit(RecordVisitor & V) const294 void 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 
303 static std::unique_ptr<InterfaceFile>
createInterfaceFile(const Records & Slices,StringRef InstallName)304 createInterfaceFile(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(Converter);
317   }
318 
319   auto File = std::make_unique<InterfaceFile>(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(Targ);
330     File->setFromBinaryAttrs(BA, Targ);
331   }
332 
333   return File;
334 }
335 
336 std::unique_ptr<InterfaceFile>
convertToInterfaceFile(const Records & Slices)337 llvm::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(Name);
348   }
349 
350   File = createInterfaceFile(Slices, *InstallNames.begin());
351   for (StringRef IN : llvm::drop_begin(InstallNames))
352     File->addDocument(createInterfaceFile(Slices, IN));
353 
354   return File;
355 }
356