Skip to content

[TextAPI] Add support to convert RecordSlices -> InterfaceFile #75007

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions llvm/include/llvm/TextAPI/DylibReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ struct ParseOption {
/// \return List of record slices.
Expected<Records> readFile(MemoryBufferRef Buffer, const ParseOption &Opt);

/// Get TAPI file representation of binary dylib.
///
/// \param Buffer Data that points to dylib.
Expected<std::unique_ptr<InterfaceFile>> get(MemoryBufferRef Buffer);

} // namespace llvm::MachO::DylibReader

#endif // LLVM_TEXTAPI_DYLIBREADER_H
9 changes: 8 additions & 1 deletion llvm/include/llvm/TextAPI/Record.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#ifndef LLVM_TEXTAPI_RECORD_H
#define LLVM_TEXTAPI_RECORD_H

#include "llvm/ADT/MapVector.h"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this include needed in the header file?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it was transitively included before in RecordSlice.h but is actually used in this file, so I moved it.

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/TextAPI/Symbol.h"
Expand Down Expand Up @@ -50,7 +51,7 @@ class Record {
public:
Record() = default;
Record(StringRef Name, RecordLinkage Linkage, SymbolFlags Flags)
: Name(Name), Linkage(Linkage), Flags(Flags) {}
: Name(Name), Linkage(Linkage), Flags(mergeFlags(Flags, Linkage)) {}

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

StringRef getName() const { return Name; }
SymbolFlags getFlags() const { return Flags; }

private:
SymbolFlags mergeFlags(SymbolFlags Flags, RecordLinkage Linkage);

protected:
StringRef Name;
Expand Down Expand Up @@ -137,6 +142,7 @@ class ObjCContainerRecord : public Record {

ObjCIVarRecord *addObjCIVar(StringRef IVar, RecordLinkage Linkage);
ObjCIVarRecord *findObjCIVar(StringRef IVar) const;
std::vector<ObjCIVarRecord *> getObjCIVars() const;

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

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

private:
bool HasEHType;
Expand Down
54 changes: 54 additions & 0 deletions llvm/include/llvm/TextAPI/RecordVisitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//===- llvm/TextAPI/RecordSlice.h - TAPI RecordSlice ------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// Defines the TAPI Record Visitor.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_TEXTAPI_RECORDVISITOR_H
#define LLVM_TEXTAPI_RECORDVISITOR_H

#include "llvm/TextAPI/Record.h"
#include "llvm/TextAPI/SymbolSet.h"

namespace llvm {
namespace MachO {

/// Base class for any usage of traversing over collected Records.
class RecordVisitor {
public:
virtual ~RecordVisitor();

virtual void visitGlobal(const GlobalRecord &) = 0;
virtual void visitObjCInterface(const ObjCInterfaceRecord &);
virtual void visitObjCCategory(const ObjCCategoryRecord &);
};

/// Specialized RecordVisitor for collecting exported symbols
/// and undefined symbols if RecordSlice being visited represents a
/// flat-namespaced library.
class SymbolConverter : public RecordVisitor {
public:
SymbolConverter(SymbolSet *Symbols, const Target &T,
const bool RecordUndefs = false)
: Symbols(Symbols), Targ(T), RecordUndefs(RecordUndefs) {}
void visitGlobal(const GlobalRecord &) override;
void visitObjCInterface(const ObjCInterfaceRecord &) override;
void visitObjCCategory(const ObjCCategoryRecord &) override;

private:
void addIVars(const ArrayRef<ObjCIVarRecord *>, StringRef ContainerName);
SymbolSet *Symbols;
const Target Targ;
const bool RecordUndefs;
};

} // end namespace MachO.
} // end namespace llvm.

#endif // LLVM_TEXTAPI_RECORDVISITOR_H
12 changes: 11 additions & 1 deletion llvm/include/llvm/TextAPI/RecordsSlice.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
#ifndef LLVM_TEXTAPI_RECORDSLICE_H
#define LLVM_TEXTAPI_RECORDSLICE_H

#include "llvm/ADT/MapVector.h"
#include "llvm/Support/Allocator.h"
#include "llvm/TextAPI/InterfaceFile.h"
#include "llvm/TextAPI/PackedVersion.h"
#include "llvm/TextAPI/Record.h"
#include "llvm/TextAPI/RecordVisitor.h"

namespace llvm {
namespace MachO {
Expand Down Expand Up @@ -133,6 +133,9 @@ class RecordsSlice {
Categories.empty();
}

// Visit all records known to RecordsSlice.
void visit(RecordVisitor &V) const;

struct BinaryAttrs {
std::vector<StringRef> AllowableClients;
std::vector<StringRef> RexportedLibraries;
Expand Down Expand Up @@ -174,6 +177,12 @@ class RecordsSlice {
R->Linkage = std::max(R->Linkage, L);
}

/// Update set flags of requested record.
///
/// \param R The global record to update.
/// \param F Flags to update to.
void updateFlags(GlobalRecord *R, SymbolFlags F) { R->Flags = F; }

RecordMap<GlobalRecord> Globals;
RecordMap<ObjCInterfaceRecord> Classes;
RecordMap<ObjCCategoryRecord, std::pair<StringRef, StringRef>> Categories;
Expand All @@ -182,6 +191,7 @@ class RecordsSlice {
};

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

} // namespace MachO
} // namespace llvm
Expand Down
10 changes: 10 additions & 0 deletions llvm/lib/TextAPI/BinaryReader/DylibReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,3 +404,13 @@ Expected<Records> DylibReader::readFile(MemoryBufferRef Buffer,
return make_error<TextAPIError>(TextAPIErrorCode::EmptyResults);
return Results;
}

Expected<std::unique_ptr<InterfaceFile>>
DylibReader::get(MemoryBufferRef Buffer) {
ParseOption Options;
auto SlicesOrErr = readFile(Buffer, Options);
if (!SlicesOrErr)
return SlicesOrErr.takeError();

return convertToInterfaceFile(*SlicesOrErr);
}
1 change: 1 addition & 0 deletions llvm/lib/TextAPI/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ add_llvm_component_library(LLVMTextAPI
PackedVersion.cpp
Platform.cpp
RecordsSlice.cpp
RecordVisitor.cpp
Symbol.cpp
SymbolSet.cpp
Target.cpp
Expand Down
65 changes: 65 additions & 0 deletions llvm/lib/TextAPI/RecordVisitor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//===- RecordVisitor.cpp --------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// Implements the TAPI Record Visitor.
///
//===----------------------------------------------------------------------===//

#include "llvm/TextAPI/RecordVisitor.h"

using namespace llvm;
using namespace llvm::MachO;

RecordVisitor::~RecordVisitor() {}
void RecordVisitor::visitObjCInterface(const ObjCInterfaceRecord &) {}
void RecordVisitor::visitObjCCategory(const ObjCCategoryRecord &) {}

static bool shouldSkipRecord(const Record &R, const bool RecordUndefs) {
if (R.isExported())
return false;

// Skip non exported symbols unless for flat namespace libraries.
return !(RecordUndefs && R.isUndefined());
}

void SymbolConverter::visitGlobal(const GlobalRecord &GR) {
auto [SymName, SymKind] = parseSymbol(GR.getName(), GR.getFlags());
if (shouldSkipRecord(GR, RecordUndefs))
return;
Symbols->addGlobal(SymKind, SymName, GR.getFlags(), Targ);
}

void SymbolConverter::addIVars(const ArrayRef<ObjCIVarRecord *> IVars,
StringRef ContainerName) {
for (auto *IV : IVars) {
if (shouldSkipRecord(*IV, RecordUndefs))
continue;
std::string Name =
ObjCIVarRecord::createScopedName(ContainerName, IV->getName());
Symbols->addGlobal(SymbolKind::ObjectiveCInstanceVariable, Name,
IV->getFlags(), Targ);
}
}

void SymbolConverter::visitObjCInterface(const ObjCInterfaceRecord &ObjCR) {
if (!shouldSkipRecord(ObjCR, RecordUndefs)) {
Symbols->addGlobal(SymbolKind::ObjectiveCClass, ObjCR.getName(),
ObjCR.getFlags(), Targ);
if (ObjCR.hasExceptionAttribute())
Symbols->addGlobal(SymbolKind::ObjectiveCClassEHType, ObjCR.getName(),
ObjCR.getFlags(), Targ);
}

addIVars(ObjCR.getObjCIVars(), ObjCR.getName());
for (const auto *Cat : ObjCR.getObjCCategories())
addIVars(Cat->getObjCIVars(), ObjCR.getName());
}

void SymbolConverter::visitObjCCategory(const ObjCCategoryRecord &Cat) {
addIVars(Cat.getObjCIVars(), Cat.getName());
}
120 changes: 118 additions & 2 deletions llvm/lib/TextAPI/RecordsSlice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//

#include "llvm/TextAPI/RecordsSlice.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/TextAPI/Record.h"
#include "llvm/TextAPI/Symbol.h"
#include <utility>
Expand Down Expand Up @@ -142,8 +143,10 @@ GlobalRecord *RecordsSlice::addGlobal(StringRef Name, RecordLinkage Linkage,
if (Result.second)
Result.first->second =
std::make_unique<GlobalRecord>(Name, Linkage, Flags, GV);
else
else {
updateLinkage(Result.first->second.get(), Linkage);
updateFlags(Result.first->second.get(), Flags);
}
return Result.first->second.get();
}

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

return Result.first->second.get();
}
SymbolFlags Record::mergeFlags(SymbolFlags Flags, RecordLinkage Linkage) {
// Add Linkage properties into Flags.
switch (Linkage) {
case RecordLinkage::Rexported:
Flags |= SymbolFlags::Rexported;
return Flags;
case RecordLinkage::Undefined:
Flags |= SymbolFlags::Undefined;
return Flags;
default:
return Flags;
}
}

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

std::vector<ObjCIVarRecord *> ObjCContainerRecord::getObjCIVars() const {
std::vector<ObjCIVarRecord *> Records;
llvm::for_each(IVars,
[&](auto &Record) { Records.push_back(Record.second.get()); });
return Records;
}

std::vector<ObjCCategoryRecord *>
ObjCInterfaceRecord::getObjCCategories() const {
std::vector<ObjCCategoryRecord *> Records;
llvm::for_each(Categories,
[&](auto &Record) { Records.push_back(Record.second); });
return Records;
}

ObjCIVarRecord *ObjCContainerRecord::addObjCIVar(StringRef IVar,
RecordLinkage Linkage) {
auto Result = IVars.insert({IVar, nullptr});
if (Result.second)
Result.first->second = std::make_unique<ObjCIVarRecord>(Name, Linkage);
Result.first->second = std::make_unique<ObjCIVarRecord>(IVar, Linkage);
return Result.first->second.get();
}

Expand Down Expand Up @@ -222,3 +253,88 @@ RecordsSlice::BinaryAttrs &RecordsSlice::getBinaryAttrs() {
BA = std::make_unique<BinaryAttrs>();
return *BA;
}

void RecordsSlice::visit(RecordVisitor &V) const {
for (auto &G : Globals)
V.visitGlobal(*G.second);
for (auto &C : Classes)
V.visitObjCInterface(*C.second);
for (auto &Cat : Categories)
V.visitObjCCategory(*Cat.second);
}

static std::unique_ptr<InterfaceFile>
createInterfaceFile(const Records &Slices, StringRef InstallName) {
// Pickup symbols first.
auto Symbols = std::make_unique<SymbolSet>();
for (auto &S : Slices) {
if (S->empty())
continue;
auto &BA = S->getBinaryAttrs();
if (BA.InstallName != InstallName)
continue;

SymbolConverter Converter(Symbols.get(), S->getTarget(),
!BA.TwoLevelNamespace);
S->visit(Converter);
}

auto File = std::make_unique<InterfaceFile>(std::move(Symbols));
File->setInstallName(InstallName);
// Assign other attributes.
for (auto &S : Slices) {
if (S->empty())
continue;
auto &BA = S->getBinaryAttrs();
if (BA.InstallName != InstallName)
continue;
const Target &Targ = S->getTarget();
File->addTarget(Targ);
if (File->getFileType() == FileType::Invalid)
File->setFileType(BA.File);
if (BA.AppExtensionSafe && !File->isApplicationExtensionSafe())
File->setApplicationExtensionSafe();
if (BA.TwoLevelNamespace && !File->isTwoLevelNamespace())
File->setTwoLevelNamespace();
if (BA.OSLibNotForSharedCache && !File->isOSLibNotForSharedCache())
File->setOSLibNotForSharedCache();
if (File->getCurrentVersion().empty())
File->setCurrentVersion(BA.CurrentVersion);
if (File->getCompatibilityVersion().empty())
File->setCompatibilityVersion(BA.CompatVersion);
if (File->getSwiftABIVersion() == 0)
File->setSwiftABIVersion(BA.SwiftABI);
if (File->getPath().empty())
File->setPath(BA.Path);
if (!BA.ParentUmbrella.empty())
File->addParentUmbrella(Targ, BA.ParentUmbrella);
for (const auto &Client : BA.AllowableClients)
File->addAllowableClient(Client, Targ);
for (const auto &Lib : BA.RexportedLibraries)
File->addReexportedLibrary(Lib, Targ);
}

return File;
}

std::unique_ptr<InterfaceFile>
llvm::MachO::convertToInterfaceFile(const Records &Slices) {
std::unique_ptr<InterfaceFile> File;
if (Slices.empty())
return File;

SetVector<StringRef> InstallNames;
for (auto &S : Slices) {
auto Name = S->getBinaryAttrs().InstallName;
if (Name.empty())
continue;
InstallNames.insert(Name);
}

File = createInterfaceFile(Slices, *InstallNames.begin());
for (auto it = std::next(InstallNames.begin()); it != InstallNames.end();
++it)
File->addDocument(createInterfaceFile(Slices, *it));

return File;
}