357 lines · cpp
1//===- RecordsSlice.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-exception6//7//===----------------------------------------------------------------------===//8//9// Implements the Records Slice APIs.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/TextAPI/RecordsSlice.h"14#include "llvm/ADT/SetVector.h"15#include "llvm/TextAPI/InterfaceFile.h"16#include "llvm/TextAPI/Record.h"17#include "llvm/TextAPI/Symbol.h"18#include <utility>19 20using namespace llvm;21using namespace llvm::MachO;22 23Record *RecordsSlice::addRecord(StringRef Name, SymbolFlags Flags,24 GlobalRecord::Kind GV, RecordLinkage Linkage) {25 // Find a specific Record type to capture.26 auto [APIName, SymKind, InterfaceType] = parseSymbol(Name);27 Name = APIName;28 switch (SymKind) {29 case EncodeKind::GlobalSymbol:30 return addGlobal(Name, Linkage, GV, Flags);31 case EncodeKind::ObjectiveCClass:32 return addObjCInterface(Name, Linkage, InterfaceType);33 case EncodeKind::ObjectiveCClassEHType: {34 ObjCInterfaceRecord *Rec = addObjCInterface(Name, Linkage, InterfaceType);35 // When classes without ehtype are used in try/catch blocks36 // a weak-defined symbol is exported.37 if ((Flags & SymbolFlags::WeakDefined) == SymbolFlags::WeakDefined)38 updateFlags(Rec, SymbolFlags::WeakDefined);39 return Rec;40 }41 case EncodeKind::ObjectiveCInstanceVariable: {42 auto [Super, IVar] = Name.split('.');43 // Attempt to find super class.44 ObjCContainerRecord *Container = findContainer(/*isIVar=*/false, Super);45 // If not found, create extension since there is no mapped class symbol.46 if (Container == nullptr)47 Container = addObjCCategory(Super, {});48 return addObjCIVar(Container, IVar, Linkage);49 }50 }51 52 llvm_unreachable("unexpected symbol kind when adding to Record Slice");53}54 55ObjCContainerRecord *RecordsSlice::findContainer(bool IsIVar,56 StringRef Name) const {57 StringRef Super = IsIVar ? Name.split('.').first : Name;58 ObjCContainerRecord *Container = findObjCInterface(Super);59 // Ivars can only exist with extensions, if they did not come from60 // class.61 if (Container == nullptr)62 Container = findObjCCategory(Super, "");63 return Container;64}65 66template <typename R, typename C = RecordMap<R>, typename K = StringRef>67R *findRecord(K Key, const C &Container) {68 const auto *Record = Container.find(Key);69 if (Record == Container.end())70 return nullptr;71 return Record->second.get();72}73 74GlobalRecord *RecordsSlice::findGlobal(StringRef Name,75 GlobalRecord::Kind GV) const {76 auto *Record = findRecord<GlobalRecord>(Name, Globals);77 if (!Record)78 return nullptr;79 80 switch (GV) {81 case GlobalRecord::Kind::Variable: {82 if (!Record->isVariable())83 return nullptr;84 break;85 }86 case GlobalRecord::Kind::Function: {87 if (!Record->isFunction())88 return nullptr;89 break;90 }91 case GlobalRecord::Kind::Unknown:92 return Record;93 }94 95 return Record;96}97 98RecordLinkage99ObjCInterfaceRecord::getLinkageForSymbol(ObjCIFSymbolKind CurrType) const {100 assert(CurrType <= ObjCIFSymbolKind::EHType &&101 "expected single ObjCIFSymbolKind enum value");102 if (CurrType == ObjCIFSymbolKind::Class)103 return Linkages.Class;104 105 if (CurrType == ObjCIFSymbolKind::MetaClass)106 return Linkages.MetaClass;107 108 if (CurrType == ObjCIFSymbolKind::EHType)109 return Linkages.EHType;110 111 llvm_unreachable("unexpected ObjCIFSymbolKind");112}113 114void ObjCInterfaceRecord::updateLinkageForSymbols(ObjCIFSymbolKind SymType,115 RecordLinkage Link) {116 if ((SymType & ObjCIFSymbolKind::Class) == ObjCIFSymbolKind::Class)117 Linkages.Class = std::max(Link, Linkages.Class);118 if ((SymType & ObjCIFSymbolKind::MetaClass) == ObjCIFSymbolKind::MetaClass)119 Linkages.MetaClass = std::max(Link, Linkages.MetaClass);120 if ((SymType & ObjCIFSymbolKind::EHType) == ObjCIFSymbolKind::EHType)121 Linkages.EHType = std::max(Link, Linkages.EHType);122 123 // Obj-C Classes represent multiple symbols that could have competing124 // linkages, in this case assign the largest one, when querying the linkage of125 // the record itself. This allows visitors pick whether they want to account126 // for complete symbol information.127 Linkage =128 std::max(Linkages.Class, std::max(Linkages.MetaClass, Linkages.EHType));129}130 131ObjCInterfaceRecord *RecordsSlice::findObjCInterface(StringRef Name) const {132 return findRecord<ObjCInterfaceRecord>(Name, Classes);133}134 135ObjCCategoryRecord *RecordsSlice::findObjCCategory(StringRef ClassToExtend,136 StringRef Category) const {137 return findRecord<ObjCCategoryRecord>(std::make_pair(ClassToExtend, Category),138 Categories);139}140 141ObjCIVarRecord *ObjCContainerRecord::findObjCIVar(StringRef IVar) const {142 return findRecord<ObjCIVarRecord>(IVar, IVars);143}144 145ObjCIVarRecord *RecordsSlice::findObjCIVar(bool IsScopedName,146 StringRef Name) const {147 // If scoped name, the name of the container is known.148 if (IsScopedName) {149 // IVar does not exist if there is not a container assigned to it.150 auto *Container = findContainer(/*IsIVar=*/true, Name);151 if (!Container)152 return nullptr;153 154 StringRef IVar = Name.substr(Name.find_first_of('.') + 1);155 return Container->findObjCIVar(IVar);156 }157 158 // Otherwise traverse through containers and attempt to find IVar.159 auto getIVar = [Name](auto &Records) -> ObjCIVarRecord * {160 for (const auto &[_, Container] : Records) {161 if (auto *IVarR = Container->findObjCIVar(Name))162 return IVarR;163 }164 return nullptr;165 };166 167 if (auto *IVarRecord = getIVar(Classes))168 return IVarRecord;169 170 return getIVar(Categories);171}172 173GlobalRecord *RecordsSlice::addGlobal(StringRef Name, RecordLinkage Linkage,174 GlobalRecord::Kind GV, SymbolFlags Flags,175 bool Inlined) {176 if (GV == GlobalRecord::Kind::Function)177 Flags |= SymbolFlags::Text;178 else if (GV == GlobalRecord::Kind::Variable)179 Flags |= SymbolFlags::Data;180 181 Name = copyString(Name);182 auto Result = Globals.try_emplace(Name);183 if (Result.second)184 Result.first->second =185 std::make_unique<GlobalRecord>(Name, Linkage, Flags, GV, Inlined);186 else {187 updateLinkage(Result.first->second.get(), Linkage);188 updateFlags(Result.first->second.get(), Flags);189 }190 return Result.first->second.get();191}192 193ObjCInterfaceRecord *RecordsSlice::addObjCInterface(StringRef Name,194 RecordLinkage Linkage,195 ObjCIFSymbolKind SymType) {196 Name = copyString(Name);197 auto Result = Classes.try_emplace(Name);198 if (Result.second)199 Result.first->second =200 std::make_unique<ObjCInterfaceRecord>(Name, Linkage, SymType);201 else202 Result.first->second->updateLinkageForSymbols(SymType, Linkage);203 return Result.first->second.get();204}205 206SymbolFlags Record::mergeFlags(SymbolFlags Flags, RecordLinkage Linkage) {207 // Add Linkage properties into Flags.208 switch (Linkage) {209 case RecordLinkage::Rexported:210 Flags |= SymbolFlags::Rexported;211 return Flags;212 case RecordLinkage::Undefined:213 Flags |= SymbolFlags::Undefined;214 return Flags;215 default:216 return Flags;217 }218}219 220bool ObjCInterfaceRecord::addObjCCategory(ObjCCategoryRecord *Record) {221 auto Result = Categories.insert({Name, Record});222 return Result.second;223}224 225ObjCCategoryRecord *RecordsSlice::addObjCCategory(StringRef ClassToExtend,226 StringRef Category) {227 Category = copyString(Category);228 ClassToExtend = copyString(ClassToExtend);229 230 // Add owning record first into record slice.231 auto Result = Categories.try_emplace(std::make_pair(ClassToExtend, Category));232 if (Result.second)233 Result.first->second =234 std::make_unique<ObjCCategoryRecord>(ClassToExtend, Category);235 236 // Then add reference to it in in the class.237 if (auto *ObjCClass = findObjCInterface(ClassToExtend))238 ObjCClass->addObjCCategory(Result.first->second.get());239 240 return Result.first->second.get();241}242 243std::vector<ObjCIVarRecord *> ObjCContainerRecord::getObjCIVars() const {244 std::vector<ObjCIVarRecord *> Records;245 Records.reserve(IVars.size());246 for (const auto &Record : IVars)247 Records.push_back(Record.second.get());248 return Records;249}250 251std::vector<ObjCCategoryRecord *>252ObjCInterfaceRecord::getObjCCategories() const {253 std::vector<ObjCCategoryRecord *> Records;254 Records.reserve(Categories.size());255 for (const auto &Record : Categories)256 Records.push_back(Record.second);257 return Records;258}259 260ObjCIVarRecord *ObjCContainerRecord::addObjCIVar(StringRef IVar,261 RecordLinkage Linkage) {262 auto Result = IVars.try_emplace(IVar);263 if (Result.second)264 Result.first->second = std::make_unique<ObjCIVarRecord>(IVar, Linkage);265 return Result.first->second.get();266}267 268ObjCIVarRecord *RecordsSlice::addObjCIVar(ObjCContainerRecord *Container,269 StringRef Name,270 RecordLinkage Linkage) {271 Name = copyString(Name);272 ObjCIVarRecord *Record = Container->addObjCIVar(Name, Linkage);273 updateLinkage(Record, Linkage);274 return Record;275}276 277StringRef RecordsSlice::copyString(StringRef String) {278 if (String.empty())279 return {};280 281 if (StringAllocator.identifyObject(String.data()))282 return String;283 284 void *Ptr = StringAllocator.Allocate(String.size(), 1);285 memcpy(Ptr, String.data(), String.size());286 return StringRef(reinterpret_cast<const char *>(Ptr), String.size());287}288 289RecordsSlice::BinaryAttrs &RecordsSlice::getBinaryAttrs() {290 if (!hasBinaryAttrs())291 BA = std::make_unique<BinaryAttrs>();292 return *BA;293}294 295void RecordsSlice::visit(RecordVisitor &V) const {296 for (auto &G : Globals)297 V.visitGlobal(*G.second);298 for (auto &C : Classes)299 V.visitObjCInterface(*C.second);300 for (auto &Cat : Categories)301 V.visitObjCCategory(*Cat.second);302}303 304static std::unique_ptr<InterfaceFile>305createInterfaceFile(const Records &Slices, StringRef InstallName) {306 // Pickup symbols first.307 auto Symbols = std::make_unique<SymbolSet>();308 for (auto &S : Slices) {309 if (S->empty())310 continue;311 auto &BA = S->getBinaryAttrs();312 if (BA.InstallName != InstallName)313 continue;314 315 SymbolConverter Converter(Symbols.get(), S->getTarget(),316 !BA.TwoLevelNamespace);317 S->visit(Converter);318 }319 320 auto File = std::make_unique<InterfaceFile>(std::move(Symbols));321 File->setInstallName(InstallName);322 // Assign other attributes.323 for (auto &S : Slices) {324 if (S->empty())325 continue;326 auto &BA = S->getBinaryAttrs();327 if (BA.InstallName != InstallName)328 continue;329 const Target &Targ = S->getTarget();330 File->addTarget(Targ);331 File->setFromBinaryAttrs(BA, Targ);332 }333 334 return File;335}336 337std::unique_ptr<InterfaceFile>338llvm::MachO::convertToInterfaceFile(const Records &Slices) {339 std::unique_ptr<InterfaceFile> File;340 if (Slices.empty())341 return File;342 343 SetVector<StringRef> InstallNames;344 for (auto &S : Slices) {345 auto Name = S->getBinaryAttrs().InstallName;346 if (Name.empty())347 continue;348 InstallNames.insert(Name);349 }350 351 File = createInterfaceFile(Slices, *InstallNames.begin());352 for (StringRef IN : llvm::drop_begin(InstallNames))353 File->addDocument(createInterfaceFile(Slices, IN));354 355 return File;356}357