Skip to content

Commit d5e9757

Browse files
committed
[TextAPI] Add support to convert RecordSlices -> InterfaceFile
Introduce RecordVisitor. This is used for different clients that want to extract information out of RecordSlice types. The first and immediate usecase is for serializing symbol information into TBD files.
1 parent c336d8b commit d5e9757

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
@@ -42,6 +42,11 @@ struct ParseOption {
4242
/// \return List of record slices.
4343
Expected<Records> readFile(MemoryBufferRef Buffer, const ParseOption &Opt);
4444

45+
/// Get TAPI file representation of binary dylib.
46+
///
47+
/// \param Buffer Data that points to dylib.
48+
Expected<std::unique_ptr<InterfaceFile>> get(MemoryBufferRef Buffer);
49+
4550
} // namespace DylibReader
4651

4752
} // end namespace MachO.

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: 5 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;
@@ -182,6 +185,7 @@ class RecordsSlice {
182185
};
183186

184187
using Records = llvm::SmallVector<std::shared_ptr<RecordsSlice>, 4>;
188+
std::unique_ptr<InterfaceFile> convertToInterfaceFile(const Records &Slices);
185189

186190
} // namespace MachO
187191
} // namespace llvm

llvm/lib/TextAPI/CMakeLists.txt

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

llvm/lib/TextAPI/DylibReader.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,3 +409,13 @@ Expected<Records> DylibReader::readFile(MemoryBufferRef Buffer,
409409
return make_error<TextAPIError>(TextAPIErrorCode::EmptyResults);
410410
return Results;
411411
}
412+
413+
Expected<std::unique_ptr<InterfaceFile>>
414+
DylibReader::get(MemoryBufferRef Buffer) {
415+
ParseOption Options;
416+
auto SlicesOrErr = readFile(Buffer, Options);
417+
if (!SlicesOrErr)
418+
return SlicesOrErr.takeError();
419+
420+
return convertToInterfaceFile(*SlicesOrErr);
421+
}

llvm/lib/TextAPI/RecordVisitor.cpp

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

llvm/lib/TextAPI/RecordsSlice.cpp

Lines changed: 122 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+
Result.first->second.get()->Flags = 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,92 @@ 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+
namespace {
267+
268+
std::unique_ptr<InterfaceFile> createInterfaceFile(const Records &Slices,
269+
StringRef InstallName) {
270+
// Pickup symbols first.
271+
auto Symbols = std::make_unique<SymbolSet>();
272+
for (auto &S : Slices) {
273+
if (S->empty())
274+
continue;
275+
auto &BA = S->getBinaryAttrs();
276+
if (BA.InstallName != InstallName)
277+
continue;
278+
279+
SymbolConverter Converter(Symbols.get(), S->getTarget(),
280+
!BA.TwoLevelNamespace);
281+
S->visit(Converter);
282+
}
283+
284+
auto File = std::make_unique<InterfaceFile>(std::move(Symbols));
285+
File->setInstallName(InstallName);
286+
// Assign other attributes.
287+
for (auto &S : Slices) {
288+
if (S->empty())
289+
continue;
290+
auto &BA = S->getBinaryAttrs();
291+
if (BA.InstallName != InstallName)
292+
continue;
293+
const Target &Targ = S->getTarget();
294+
File->addTarget(Targ);
295+
if (File->getFileType() == FileType::Invalid)
296+
File->setFileType(BA.File);
297+
if (BA.AppExtensionSafe && !File->isApplicationExtensionSafe())
298+
File->setApplicationExtensionSafe();
299+
if (BA.TwoLevelNamespace && !File->isTwoLevelNamespace())
300+
File->setTwoLevelNamespace();
301+
if (BA.OSLibNotForSharedCache && !File->isOSLibNotForSharedCache())
302+
File->setOSLibNotForSharedCache();
303+
if (File->getCurrentVersion().empty())
304+
File->setCurrentVersion(BA.CurrentVersion);
305+
if (File->getCompatibilityVersion().empty())
306+
File->setCompatibilityVersion(BA.CompatVersion);
307+
if (File->getSwiftABIVersion() == 0)
308+
File->setSwiftABIVersion(BA.SwiftABI);
309+
if (File->getPath().empty())
310+
File->setPath(BA.Path);
311+
if (!BA.ParentUmbrella.empty())
312+
File->addParentUmbrella(Targ, BA.ParentUmbrella);
313+
for (const auto &Client : BA.AllowableClients)
314+
File->addAllowableClient(Client, Targ);
315+
for (const auto &Lib : BA.RexportedLibraries)
316+
File->addReexportedLibrary(Lib, Targ);
317+
}
318+
319+
return File;
320+
}
321+
322+
} // namespace
323+
324+
std::unique_ptr<InterfaceFile>
325+
llvm::MachO::convertToInterfaceFile(const Records &Slices) {
326+
std::unique_ptr<InterfaceFile> File;
327+
if (Slices.empty())
328+
return File;
329+
330+
SetVector<StringRef> InstallNames;
331+
for (auto &S : Slices) {
332+
auto Name = S->getBinaryAttrs().InstallName;
333+
if (Name.empty())
334+
continue;
335+
InstallNames.insert(Name);
336+
}
337+
338+
File = createInterfaceFile(Slices, *InstallNames.begin());
339+
for (auto it = std::next(InstallNames.begin()); it != InstallNames.end();
340+
++it)
341+
File->addDocument(createInterfaceFile(Slices, *it));
342+
343+
return File;
344+
}

0 commit comments

Comments
 (0)