Skip to content

Commit 91efdeb

Browse files
committed
[TextAPI] Add support to convert RecordSlices -> InterfaceFile (llvm#75007)
Introduce RecordVisitor. This is used for different clients that want to extract information out of RecordSlice types. The first and immediate use case is for serializing symbol information into TBD files. (cherry picked from commit 5ea15fa)
1 parent 307080b commit 91efdeb

File tree

8 files changed

+272
-4
lines changed

8 files changed

+272
-4
lines changed

llvm/include/llvm/TextAPI/DylibReader.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ struct ParseOption {
3838
/// \return List of record slices.
3939
Expected<Records> readFile(MemoryBufferRef Buffer, const ParseOption &Opt);
4040

41+
/// Get TAPI file representation of binary dylib.
42+
///
43+
/// \param Buffer Data that points to dylib.
44+
Expected<std::unique_ptr<InterfaceFile>> get(MemoryBufferRef Buffer);
45+
4146
} // namespace llvm::MachO::DylibReader
4247

4348
#endif // LLVM_TEXTAPI_DYLIBREADER_H

llvm/include/llvm/TextAPI/Record.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef LLVM_TEXTAPI_RECORD_H
1515
#define LLVM_TEXTAPI_RECORD_H
1616

17+
#include "llvm/ADT/MapVector.h"
1718
#include "llvm/ADT/StringRef.h"
1819
#include "llvm/Support/Casting.h"
1920
#include "llvm/TextAPI/Symbol.h"
@@ -50,7 +51,7 @@ class Record {
5051
public:
5152
Record() = default;
5253
Record(StringRef Name, RecordLinkage Linkage, SymbolFlags Flags)
53-
: Name(Name), Linkage(Linkage), Flags(Flags) {}
54+
: Name(Name), Linkage(Linkage), Flags(mergeFlags(Flags, Linkage)) {}
5455

5556
bool isWeakDefined() const {
5657
return (Flags & SymbolFlags::WeakDefined) == SymbolFlags::WeakDefined;
@@ -79,6 +80,10 @@ class Record {
7980
bool isRexported() const { return Linkage == RecordLinkage::Rexported; }
8081

8182
StringRef getName() const { return Name; }
83+
SymbolFlags getFlags() const { return Flags; }
84+
85+
private:
86+
SymbolFlags mergeFlags(SymbolFlags Flags, RecordLinkage Linkage);
8287

8388
protected:
8489
StringRef Name;
@@ -137,6 +142,7 @@ class ObjCContainerRecord : public Record {
137142

138143
ObjCIVarRecord *addObjCIVar(StringRef IVar, RecordLinkage Linkage);
139144
ObjCIVarRecord *findObjCIVar(StringRef IVar) const;
145+
std::vector<ObjCIVarRecord *> getObjCIVars() const;
140146

141147
private:
142148
RecordMap<ObjCIVarRecord> IVars;
@@ -163,6 +169,7 @@ class ObjCInterfaceRecord : public ObjCContainerRecord {
163169

164170
bool hasExceptionAttribute() const { return HasEHType; }
165171
bool addObjCCategory(ObjCCategoryRecord *Record);
172+
std::vector<ObjCCategoryRecord *> getObjCCategories() const;
166173

167174
private:
168175
bool HasEHType;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//===- llvm/TextAPI/RecordSlice.h - TAPI RecordSlice ------------*- C++ -*-===//
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+
/// Defines the TAPI Record Visitor.
10+
///
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_TEXTAPI_RECORDVISITOR_H
14+
#define LLVM_TEXTAPI_RECORDVISITOR_H
15+
16+
#include "llvm/TextAPI/Record.h"
17+
#include "llvm/TextAPI/SymbolSet.h"
18+
19+
namespace llvm {
20+
namespace MachO {
21+
22+
/// Base class for any usage of traversing over collected Records.
23+
class RecordVisitor {
24+
public:
25+
virtual ~RecordVisitor();
26+
27+
virtual void visitGlobal(const GlobalRecord &) = 0;
28+
virtual void visitObjCInterface(const ObjCInterfaceRecord &);
29+
virtual void visitObjCCategory(const ObjCCategoryRecord &);
30+
};
31+
32+
/// Specialized RecordVisitor for collecting exported symbols
33+
/// and undefined symbols if RecordSlice being visited represents a
34+
/// flat-namespaced library.
35+
class SymbolConverter : public RecordVisitor {
36+
public:
37+
SymbolConverter(SymbolSet *Symbols, const Target &T,
38+
const bool RecordUndefs = false)
39+
: Symbols(Symbols), Targ(T), RecordUndefs(RecordUndefs) {}
40+
void visitGlobal(const GlobalRecord &) override;
41+
void visitObjCInterface(const ObjCInterfaceRecord &) override;
42+
void visitObjCCategory(const ObjCCategoryRecord &) override;
43+
44+
private:
45+
void addIVars(const ArrayRef<ObjCIVarRecord *>, StringRef ContainerName);
46+
SymbolSet *Symbols;
47+
const Target Targ;
48+
const bool RecordUndefs;
49+
};
50+
51+
} // end namespace MachO.
52+
} // end namespace llvm.
53+
54+
#endif // LLVM_TEXTAPI_RECORDVISITOR_H

llvm/include/llvm/TextAPI/RecordsSlice.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
#ifndef LLVM_TEXTAPI_RECORDSLICE_H
1515
#define LLVM_TEXTAPI_RECORDSLICE_H
1616

17-
#include "llvm/ADT/MapVector.h"
1817
#include "llvm/Support/Allocator.h"
1918
#include "llvm/TextAPI/InterfaceFile.h"
2019
#include "llvm/TextAPI/PackedVersion.h"
2120
#include "llvm/TextAPI/Record.h"
21+
#include "llvm/TextAPI/RecordVisitor.h"
2222

2323
namespace llvm {
2424
namespace MachO {
@@ -133,6 +133,9 @@ class RecordsSlice {
133133
Categories.empty();
134134
}
135135

136+
// Visit all records known to RecordsSlice.
137+
void visit(RecordVisitor &V) const;
138+
136139
struct BinaryAttrs {
137140
std::vector<StringRef> AllowableClients;
138141
std::vector<StringRef> RexportedLibraries;
@@ -174,6 +177,12 @@ class RecordsSlice {
174177
R->Linkage = std::max(R->Linkage, L);
175178
}
176179

180+
/// Update set flags of requested record.
181+
///
182+
/// \param R The global record to update.
183+
/// \param F Flags to update to.
184+
void updateFlags(GlobalRecord *R, SymbolFlags F) { R->Flags = F; }
185+
177186
RecordMap<GlobalRecord> Globals;
178187
RecordMap<ObjCInterfaceRecord> Classes;
179188
RecordMap<ObjCCategoryRecord, std::pair<StringRef, StringRef>> Categories;
@@ -182,6 +191,7 @@ class RecordsSlice {
182191
};
183192

184193
using Records = llvm::SmallVector<std::shared_ptr<RecordsSlice>, 4>;
194+
std::unique_ptr<InterfaceFile> convertToInterfaceFile(const Records &Slices);
185195

186196
} // namespace MachO
187197
} // namespace llvm

llvm/lib/TextAPI/BinaryReader/DylibReader.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,3 +417,13 @@ Expected<Records> DylibReader::readFile(MemoryBufferRef Buffer,
417417
return make_error<TextAPIError>(TextAPIErrorCode::EmptyResults);
418418
return Results;
419419
}
420+
421+
Expected<std::unique_ptr<InterfaceFile>>
422+
DylibReader::get(MemoryBufferRef Buffer) {
423+
ParseOption Options;
424+
auto SlicesOrErr = readFile(Buffer, Options);
425+
if (!SlicesOrErr)
426+
return SlicesOrErr.takeError();
427+
428+
return convertToInterfaceFile(*SlicesOrErr);
429+
}

llvm/lib/TextAPI/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ add_llvm_component_library(LLVMTextAPI
66
PackedVersion.cpp
77
Platform.cpp
88
RecordsSlice.cpp
9+
RecordVisitor.cpp
910
Symbol.cpp
1011
SymbolSet.cpp
1112
Target.cpp

llvm/lib/TextAPI/RecordVisitor.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//===- RecordVisitor.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 TAPI Record Visitor.
10+
///
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "llvm/TextAPI/RecordVisitor.h"
14+
15+
using namespace llvm;
16+
using namespace llvm::MachO;
17+
18+
RecordVisitor::~RecordVisitor() {}
19+
void RecordVisitor::visitObjCInterface(const ObjCInterfaceRecord &) {}
20+
void RecordVisitor::visitObjCCategory(const ObjCCategoryRecord &) {}
21+
22+
static bool shouldSkipRecord(const Record &R, const bool RecordUndefs) {
23+
if (R.isExported())
24+
return false;
25+
26+
// Skip non exported symbols unless for flat namespace libraries.
27+
return !(RecordUndefs && R.isUndefined());
28+
}
29+
30+
void SymbolConverter::visitGlobal(const GlobalRecord &GR) {
31+
auto [SymName, SymKind] = parseSymbol(GR.getName(), GR.getFlags());
32+
if (shouldSkipRecord(GR, RecordUndefs))
33+
return;
34+
Symbols->addGlobal(SymKind, SymName, GR.getFlags(), Targ);
35+
}
36+
37+
void SymbolConverter::addIVars(const ArrayRef<ObjCIVarRecord *> IVars,
38+
StringRef ContainerName) {
39+
for (auto *IV : IVars) {
40+
if (shouldSkipRecord(*IV, RecordUndefs))
41+
continue;
42+
std::string Name =
43+
ObjCIVarRecord::createScopedName(ContainerName, IV->getName());
44+
Symbols->addGlobal(SymbolKind::ObjectiveCInstanceVariable, Name,
45+
IV->getFlags(), Targ);
46+
}
47+
}
48+
49+
void SymbolConverter::visitObjCInterface(const ObjCInterfaceRecord &ObjCR) {
50+
if (!shouldSkipRecord(ObjCR, RecordUndefs)) {
51+
Symbols->addGlobal(SymbolKind::ObjectiveCClass, ObjCR.getName(),
52+
ObjCR.getFlags(), Targ);
53+
if (ObjCR.hasExceptionAttribute())
54+
Symbols->addGlobal(SymbolKind::ObjectiveCClassEHType, ObjCR.getName(),
55+
ObjCR.getFlags(), Targ);
56+
}
57+
58+
addIVars(ObjCR.getObjCIVars(), ObjCR.getName());
59+
for (const auto *Cat : ObjCR.getObjCCategories())
60+
addIVars(Cat->getObjCIVars(), ObjCR.getName());
61+
}
62+
63+
void SymbolConverter::visitObjCCategory(const ObjCCategoryRecord &Cat) {
64+
addIVars(Cat.getObjCIVars(), Cat.getName());
65+
}

llvm/lib/TextAPI/RecordsSlice.cpp

Lines changed: 118 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "llvm/TextAPI/RecordsSlice.h"
14+
#include "llvm/ADT/SetVector.h"
1415
#include "llvm/TextAPI/Record.h"
1516
#include "llvm/TextAPI/Symbol.h"
1617
#include <utility>
@@ -142,8 +143,10 @@ GlobalRecord *RecordsSlice::addGlobal(StringRef Name, RecordLinkage Linkage,
142143
if (Result.second)
143144
Result.first->second =
144145
std::make_unique<GlobalRecord>(Name, Linkage, Flags, GV);
145-
else
146+
else {
146147
updateLinkage(Result.first->second.get(), Linkage);
148+
updateFlags(Result.first->second.get(), Flags);
149+
}
147150
return Result.first->second.get();
148151
}
149152

@@ -164,6 +167,19 @@ ObjCInterfaceRecord *RecordsSlice::addObjCInterface(StringRef Name,
164167

165168
return Result.first->second.get();
166169
}
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+
}
167183

168184
bool ObjCInterfaceRecord::addObjCCategory(ObjCCategoryRecord *Record) {
169185
auto Result = Categories.insert({Name, Record});
@@ -188,11 +204,26 @@ ObjCCategoryRecord *RecordsSlice::addObjCCategory(StringRef ClassToExtend,
188204
return Result.first->second.get();
189205
}
190206

207+
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 *>
215+
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+
191222
ObjCIVarRecord *ObjCContainerRecord::addObjCIVar(StringRef IVar,
192223
RecordLinkage Linkage) {
193224
auto Result = IVars.insert({IVar, nullptr});
194225
if (Result.second)
195-
Result.first->second = std::make_unique<ObjCIVarRecord>(Name, Linkage);
226+
Result.first->second = std::make_unique<ObjCIVarRecord>(IVar, Linkage);
196227
return Result.first->second.get();
197228
}
198229

@@ -222,3 +253,88 @@ RecordsSlice::BinaryAttrs &RecordsSlice::getBinaryAttrs() {
222253
BA = std::make_unique<BinaryAttrs>();
223254
return *BA;
224255
}
256+
257+
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>
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>
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 (auto it = std::next(InstallNames.begin()); it != InstallNames.end();
336+
++it)
337+
File->addDocument(createInterfaceFile(Slices, *it));
338+
339+
return File;
340+
}

0 commit comments

Comments
 (0)