blob: 111a1fa6eaf43b28b2d43672a282d69e157d25f8 [file] [log] [blame]
//===- RecordsSlice.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 Records Slice APIs.
//
//===----------------------------------------------------------------------===//
#include "llvm/TextAPI/RecordsSlice.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/TextAPI/InterfaceFile.h"
#include "llvm/TextAPI/Record.h"
#include "llvm/TextAPI/Symbol.h"
#include <utility>
using namespace llvm;
using namespace llvm::MachO;
Record *RecordsSlice::addRecord(StringRef Name, SymbolFlags Flags,
GlobalRecord::Kind GV, RecordLinkage Linkage) {
// Find a specific Record type to capture.
auto [APIName, SymKind, InterfaceType] = parseSymbol(Name);
Name = APIName;
switch (SymKind) {
case EncodeKind::GlobalSymbol:
return addGlobal(Name, Linkage, GV, Flags);
case EncodeKind::ObjectiveCClass:
return addObjCInterface(Name, Linkage, InterfaceType);
case EncodeKind::ObjectiveCClassEHType: {
ObjCInterfaceRecord *Rec = addObjCInterface(Name, Linkage, InterfaceType);
// When classes without ehtype are used in try/catch blocks
// a weak-defined symbol is exported.
if ((Flags & SymbolFlags::WeakDefined) == SymbolFlags::WeakDefined)
updateFlags(Rec, SymbolFlags::WeakDefined);
return Rec;
}
case EncodeKind::ObjectiveCInstanceVariable: {
auto [Super, IVar] = Name.split('.');
// Attempt to find super class.
ObjCContainerRecord *Container = findContainer(/*isIVar=*/false, Super);
// If not found, create extension since there is no mapped class symbol.
if (Container == nullptr)
Container = addObjCCategory(Super, {});
return addObjCIVar(Container, IVar, Linkage);
}
}
llvm_unreachable("unexpected symbol kind when adding to Record Slice");
}
ObjCContainerRecord *RecordsSlice::findContainer(bool IsIVar,
StringRef Name) const {
StringRef Super = IsIVar ? Name.split('.').first : Name;
ObjCContainerRecord *Container = findObjCInterface(Super);
// Ivars can only exist with extensions, if they did not come from
// class.
if (Container == nullptr)
Container = findObjCCategory(Super, "");
return Container;
}
template <typename R, typename C = RecordMap<R>, typename K = StringRef>
R *findRecord(K Key, const C &Container) {
const auto *Record = Container.find(Key);
if (Record == Container.end())
return nullptr;
return Record->second.get();
}
GlobalRecord *RecordsSlice::findGlobal(StringRef Name,
GlobalRecord::Kind GV) const {
auto *Record = findRecord<GlobalRecord>(Name, Globals);
if (!Record)
return nullptr;
switch (GV) {
case GlobalRecord::Kind::Variable: {
if (!Record->isVariable())
return nullptr;
break;
}
case GlobalRecord::Kind::Function: {
if (!Record->isFunction())
return nullptr;
break;
}
case GlobalRecord::Kind::Unknown:
return Record;
}
return Record;
}
RecordLinkage
ObjCInterfaceRecord::getLinkageForSymbol(ObjCIFSymbolKind CurrType) const {
assert(CurrType <= ObjCIFSymbolKind::EHType &&
"expected single ObjCIFSymbolKind enum value");
if (CurrType == ObjCIFSymbolKind::Class)
return Linkages.Class;
if (CurrType == ObjCIFSymbolKind::MetaClass)
return Linkages.MetaClass;
if (CurrType == ObjCIFSymbolKind::EHType)
return Linkages.EHType;
llvm_unreachable("unexpected ObjCIFSymbolKind");
}
void ObjCInterfaceRecord::updateLinkageForSymbols(ObjCIFSymbolKind SymType,
RecordLinkage Link) {
if ((SymType & ObjCIFSymbolKind::Class) == ObjCIFSymbolKind::Class)
Linkages.Class = std::max(Link, Linkages.Class);
if ((SymType & ObjCIFSymbolKind::MetaClass) == ObjCIFSymbolKind::MetaClass)
Linkages.MetaClass = std::max(Link, Linkages.MetaClass);
if ((SymType & ObjCIFSymbolKind::EHType) == ObjCIFSymbolKind::EHType)
Linkages.EHType = std::max(Link, Linkages.EHType);
// Obj-C Classes represent multiple symbols that could have competing
// linkages, in this case assign the largest one, when querying the linkage of
// the record itself. This allows visitors pick whether they want to account
// for complete symbol information.
Linkage =
std::max(Linkages.Class, std::max(Linkages.MetaClass, Linkages.EHType));
}
ObjCInterfaceRecord *RecordsSlice::findObjCInterface(StringRef Name) const {
return findRecord<ObjCInterfaceRecord>(Name, Classes);
}
ObjCCategoryRecord *RecordsSlice::findObjCCategory(StringRef ClassToExtend,
StringRef Category) const {
return findRecord<ObjCCategoryRecord>(std::make_pair(ClassToExtend, Category),
Categories);
}
ObjCIVarRecord *ObjCContainerRecord::findObjCIVar(StringRef IVar) const {
return findRecord<ObjCIVarRecord>(IVar, IVars);
}
ObjCIVarRecord *RecordsSlice::findObjCIVar(bool IsScopedName,
StringRef Name) const {
// If scoped name, the name of the container is known.
if (IsScopedName) {
// IVar does not exist if there is not a container assigned to it.
auto *Container = findContainer(/*IsIVar=*/true, Name);
if (!Container)
return nullptr;
StringRef IVar = Name.substr(Name.find_first_of('.') + 1);
return Container->findObjCIVar(IVar);
}
// Otherwise traverse through containers and attempt to find IVar.
auto getIVar = [Name](auto &Records) -> ObjCIVarRecord * {
for (const auto &[_, Container] : Records) {
if (auto *IVarR = Container->findObjCIVar(Name))
return IVarR;
}
return nullptr;
};
if (auto *IVarRecord = getIVar(Classes))
return IVarRecord;
return getIVar(Categories);
}
GlobalRecord *RecordsSlice::addGlobal(StringRef Name, RecordLinkage Linkage,
GlobalRecord::Kind GV, SymbolFlags Flags,
bool Inlined) {
if (GV == GlobalRecord::Kind::Function)
Flags |= SymbolFlags::Text;
else if (GV == GlobalRecord::Kind::Variable)
Flags |= SymbolFlags::Data;
Name = copyString(Name);
auto Result = Globals.insert({Name, nullptr});
if (Result.second)
Result.first->second =
std::make_unique<GlobalRecord>(Name, Linkage, Flags, GV, Inlined);
else {
updateLinkage(Result.first->second.get(), Linkage);
updateFlags(Result.first->second.get(), Flags);
}
return Result.first->second.get();
}
ObjCInterfaceRecord *RecordsSlice::addObjCInterface(StringRef Name,
RecordLinkage Linkage,
ObjCIFSymbolKind SymType) {
Name = copyString(Name);
auto Result = Classes.insert({Name, nullptr});
if (Result.second)
Result.first->second =
std::make_unique<ObjCInterfaceRecord>(Name, Linkage, SymType);
else
Result.first->second->updateLinkageForSymbols(SymType, Linkage);
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});
return Result.second;
}
ObjCCategoryRecord *RecordsSlice::addObjCCategory(StringRef ClassToExtend,
StringRef Category) {
Category = copyString(Category);
ClassToExtend = copyString(ClassToExtend);
// Add owning record first into record slice.
auto Result =
Categories.insert({std::make_pair(ClassToExtend, Category), nullptr});
if (Result.second)
Result.first->second =
std::make_unique<ObjCCategoryRecord>(ClassToExtend, Category);
// Then add reference to it in in the class.
if (auto *ObjCClass = findObjCInterface(ClassToExtend))
ObjCClass->addObjCCategory(Result.first->second.get());
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>(IVar, Linkage);
return Result.first->second.get();
}
ObjCIVarRecord *RecordsSlice::addObjCIVar(ObjCContainerRecord *Container,
StringRef Name,
RecordLinkage Linkage) {
Name = copyString(Name);
ObjCIVarRecord *Record = Container->addObjCIVar(Name, Linkage);
updateLinkage(Record, Linkage);
return Record;
}
StringRef RecordsSlice::copyString(StringRef String) {
if (String.empty())
return {};
if (StringAllocator.identifyObject(String.data()))
return String;
void *Ptr = StringAllocator.Allocate(String.size(), 1);
memcpy(Ptr, String.data(), String.size());
return StringRef(reinterpret_cast<const char *>(Ptr), String.size());
}
RecordsSlice::BinaryAttrs &RecordsSlice::getBinaryAttrs() {
if (!hasBinaryAttrs())
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);
File->setFromBinaryAttrs(BA, 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 (StringRef IN : llvm::drop_begin(InstallNames))
File->addDocument(createInterfaceFile(Slices, IN));
return File;
}