//===- 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 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 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(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(Name, Classes); } ObjCCategoryRecord *RecordsSlice::findObjCCategory(StringRef ClassToExtend, StringRef Category) const { return findRecord(std::make_pair(ClassToExtend, Category), Categories); } ObjCIVarRecord *ObjCContainerRecord::findObjCIVar(StringRef IVar) const { return findRecord(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(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(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(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 ObjCContainerRecord::getObjCIVars() const { std::vector Records; llvm::for_each(IVars, [&](auto &Record) { Records.push_back(Record.second.get()); }); return Records; } std::vector ObjCInterfaceRecord::getObjCCategories() const { std::vector 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(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(Ptr), String.size()); } RecordsSlice::BinaryAttrs &RecordsSlice::getBinaryAttrs() { if (!hasBinaryAttrs()) BA = std::make_unique(); 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 createInterfaceFile(const Records &Slices, StringRef InstallName) { // Pickup symbols first. auto Symbols = std::make_unique(); 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(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 llvm::MachO::convertToInterfaceFile(const Records &Slices) { std::unique_ptr File; if (Slices.empty()) return File; SetVector 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; }