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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Implements the Records Slice APIs. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/TextAPI/RecordsSlice.h" 14 #include "llvm/TextAPI/Record.h" 15 #include "llvm/TextAPI/Symbol.h" 16 #include <utility> 17 18 using namespace llvm; 19 using namespace llvm::MachO; 20 21 Record *RecordsSlice::addRecord(StringRef Name, SymbolFlags Flags, 22 GlobalRecord::Kind GV, RecordLinkage Linkage) { 23 // Find a specific Record type to capture. 24 auto [APIName, SymKind] = parseSymbol(Name, Flags); 25 Name = APIName; 26 switch (SymKind) { 27 case SymbolKind::GlobalSymbol: 28 return addGlobal(Name, Linkage, GV, Flags); 29 case SymbolKind::ObjectiveCClass: 30 return addObjCInterface(Name, Linkage); 31 case SymbolKind::ObjectiveCClassEHType: 32 return addObjCInterface(Name, Linkage, /*HasEHType=*/true); 33 case SymbolKind::ObjectiveCInstanceVariable: { 34 auto [Super, IVar] = Name.split('.'); 35 // Attempt to find super class. 36 ObjCContainerRecord *Container = findContainer(/*isIVar=*/false, Super); 37 // If not found, create extension since there is no mapped class symbol. 38 if (Container == nullptr) 39 Container = addObjCCategory(Super, {}); 40 return addObjCIVar(Container, IVar, Linkage); 41 } 42 } 43 44 llvm_unreachable("unexpected symbol kind when adding to Record Slice"); 45 } 46 47 ObjCContainerRecord *RecordsSlice::findContainer(bool IsIVar, 48 StringRef Name) const { 49 StringRef Super = IsIVar ? Name.split('.').first : Name; 50 ObjCContainerRecord *Container = findObjCInterface(Super); 51 // Ivars can only exist with extensions, if they did not come from 52 // class. 53 if (Container == nullptr) 54 Container = findObjCCategory(Super, ""); 55 return Container; 56 } 57 58 template <typename R, typename C = RecordMap<R>, typename K = StringRef> 59 R *findRecord(K Key, const C &Container) { 60 const auto *Record = Container.find(Key); 61 if (Record == Container.end()) 62 return nullptr; 63 return Record->second.get(); 64 } 65 66 GlobalRecord *RecordsSlice::findGlobal(StringRef Name, 67 GlobalRecord::Kind GV) const { 68 auto *Record = findRecord<GlobalRecord>(Name, Globals); 69 if (!Record) 70 return nullptr; 71 72 switch (GV) { 73 case GlobalRecord::Kind::Variable: { 74 if (!Record->isVariable()) 75 return nullptr; 76 break; 77 } 78 case GlobalRecord::Kind::Function: { 79 if (!Record->isFunction()) 80 return nullptr; 81 break; 82 } 83 case GlobalRecord::Kind::Unknown: 84 return Record; 85 } 86 87 return Record; 88 } 89 90 ObjCInterfaceRecord *RecordsSlice::findObjCInterface(StringRef Name) const { 91 return findRecord<ObjCInterfaceRecord>(Name, Classes); 92 } 93 94 ObjCCategoryRecord *RecordsSlice::findObjCCategory(StringRef ClassToExtend, 95 StringRef Category) const { 96 return findRecord<ObjCCategoryRecord>(std::make_pair(ClassToExtend, Category), 97 Categories); 98 } 99 100 ObjCIVarRecord *ObjCContainerRecord::findObjCIVar(StringRef IVar) const { 101 return findRecord<ObjCIVarRecord>(IVar, IVars); 102 } 103 104 ObjCIVarRecord *RecordsSlice::findObjCIVar(bool IsScopedName, 105 StringRef Name) const { 106 // If scoped name, the name of the container is known. 107 if (IsScopedName) { 108 // IVar does not exist if there is not a container assigned to it. 109 auto *Container = findContainer(/*IsIVar=*/true, Name); 110 if (!Container) 111 return nullptr; 112 113 StringRef IVar = Name.substr(Name.find_first_of('.') + 1); 114 return Container->findObjCIVar(IVar); 115 } 116 117 // Otherwise traverse through containers and attempt to find IVar. 118 auto getIVar = [Name](auto &Records) -> ObjCIVarRecord * { 119 for (const auto &[_, Container] : Records) { 120 if (auto *IVarR = Container->findObjCIVar(Name)) 121 return IVarR; 122 } 123 return nullptr; 124 }; 125 126 if (auto *IVarRecord = getIVar(Classes)) 127 return IVarRecord; 128 129 return getIVar(Categories); 130 } 131 132 GlobalRecord *RecordsSlice::addGlobal(StringRef Name, RecordLinkage Linkage, 133 GlobalRecord::Kind GV, 134 SymbolFlags Flags) { 135 if (GV == GlobalRecord::Kind::Function) 136 Flags |= SymbolFlags::Text; 137 else if (GV == GlobalRecord::Kind::Variable) 138 Flags |= SymbolFlags::Data; 139 140 Name = copyString(Name); 141 auto Result = Globals.insert({Name, nullptr}); 142 if (Result.second) 143 Result.first->second = 144 std::make_unique<GlobalRecord>(Name, Linkage, Flags, GV); 145 else 146 updateLinkage(Result.first->second.get(), Linkage); 147 return Result.first->second.get(); 148 } 149 150 ObjCInterfaceRecord *RecordsSlice::addObjCInterface(StringRef Name, 151 RecordLinkage Linkage, 152 bool HasEHType) { 153 Name = copyString(Name); 154 auto Result = Classes.insert({Name, nullptr}); 155 if (Result.second) { 156 Result.first->second = 157 std::make_unique<ObjCInterfaceRecord>(Name, Linkage, HasEHType); 158 } else { 159 // ObjC classes represent multiple symbols that could have competing 160 // linkages, in those cases assign the largest one. 161 if (Linkage >= RecordLinkage::Rexported) 162 updateLinkage(Result.first->second.get(), Linkage); 163 } 164 165 return Result.first->second.get(); 166 } 167 168 bool ObjCInterfaceRecord::addObjCCategory(ObjCCategoryRecord *Record) { 169 auto Result = Categories.insert({Name, Record}); 170 return Result.second; 171 } 172 173 ObjCCategoryRecord *RecordsSlice::addObjCCategory(StringRef ClassToExtend, 174 StringRef Category) { 175 Category = copyString(Category); 176 177 // Add owning record first into record slice. 178 auto Result = 179 Categories.insert({std::make_pair(ClassToExtend, Category), nullptr}); 180 if (Result.second) 181 Result.first->second = 182 std::make_unique<ObjCCategoryRecord>(ClassToExtend, Category); 183 184 // Then add reference to it in in the class. 185 if (auto *ObjCClass = findObjCInterface(ClassToExtend)) 186 ObjCClass->addObjCCategory(Result.first->second.get()); 187 188 return Result.first->second.get(); 189 } 190 191 ObjCIVarRecord *ObjCContainerRecord::addObjCIVar(StringRef IVar, 192 RecordLinkage Linkage) { 193 auto Result = IVars.insert({IVar, nullptr}); 194 if (Result.second) 195 Result.first->second = std::make_unique<ObjCIVarRecord>(Name, Linkage); 196 return Result.first->second.get(); 197 } 198 199 ObjCIVarRecord *RecordsSlice::addObjCIVar(ObjCContainerRecord *Container, 200 StringRef Name, 201 RecordLinkage Linkage) { 202 Name = copyString(Name); 203 ObjCIVarRecord *Record = Container->addObjCIVar(Name, Linkage); 204 updateLinkage(Record, Linkage); 205 return Record; 206 } 207 208 StringRef RecordsSlice::copyString(StringRef String) { 209 if (String.empty()) 210 return {}; 211 212 if (StringAllocator.identifyObject(String.data())) 213 return String; 214 215 void *Ptr = StringAllocator.Allocate(String.size(), 1); 216 memcpy(Ptr, String.data(), String.size()); 217 return StringRef(reinterpret_cast<const char *>(Ptr), String.size()); 218 } 219 220 RecordsSlice::BinaryAttrs &RecordsSlice::getBinaryAttrs() { 221 if (!hasBinaryAttrs()) 222 BA = std::make_unique<BinaryAttrs>(); 223 return *BA; 224 } 225