1 //===- Frontend.cpp ---------------------------------------------*- C++ -*-===// 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 #include "clang/InstallAPI/Frontend.h" 10 #include "clang/AST/Availability.h" 11 #include "clang/InstallAPI/FrontendRecords.h" 12 #include "llvm/ADT/SmallString.h" 13 #include "llvm/ADT/StringRef.h" 14 15 using namespace llvm; 16 using namespace llvm::MachO; 17 18 namespace clang::installapi { 19 std::pair<GlobalRecord *, FrontendAttrs *> FrontendRecordsSlice::addGlobal( 20 StringRef Name, RecordLinkage Linkage, GlobalRecord::Kind GV, 21 const clang::AvailabilityInfo Avail, const Decl *D, const HeaderType Access, 22 SymbolFlags Flags, bool Inlined) { 23 24 GlobalRecord *GR = 25 llvm::MachO::RecordsSlice::addGlobal(Name, Linkage, GV, Flags, Inlined); 26 auto Result = FrontendRecords.insert( 27 {GR, FrontendAttrs{Avail, D, D->getLocation(), Access}}); 28 return {GR, &(Result.first->second)}; 29 } 30 31 std::pair<ObjCInterfaceRecord *, FrontendAttrs *> 32 FrontendRecordsSlice::addObjCInterface(StringRef Name, RecordLinkage Linkage, 33 const clang::AvailabilityInfo Avail, 34 const Decl *D, HeaderType Access, 35 bool IsEHType) { 36 ObjCIFSymbolKind SymType = 37 ObjCIFSymbolKind::Class | ObjCIFSymbolKind::MetaClass; 38 if (IsEHType) 39 SymType |= ObjCIFSymbolKind::EHType; 40 41 ObjCInterfaceRecord *ObjCR = 42 llvm::MachO::RecordsSlice::addObjCInterface(Name, Linkage, SymType); 43 auto Result = FrontendRecords.insert( 44 {ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}}); 45 return {ObjCR, &(Result.first->second)}; 46 } 47 48 std::pair<ObjCCategoryRecord *, FrontendAttrs *> 49 FrontendRecordsSlice::addObjCCategory(StringRef ClassToExtend, 50 StringRef CategoryName, 51 const clang::AvailabilityInfo Avail, 52 const Decl *D, HeaderType Access) { 53 ObjCCategoryRecord *ObjCR = 54 llvm::MachO::RecordsSlice::addObjCCategory(ClassToExtend, CategoryName); 55 auto Result = FrontendRecords.insert( 56 {ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}}); 57 return {ObjCR, &(Result.first->second)}; 58 } 59 60 std::pair<ObjCIVarRecord *, FrontendAttrs *> FrontendRecordsSlice::addObjCIVar( 61 ObjCContainerRecord *Container, StringRef IvarName, RecordLinkage Linkage, 62 const clang::AvailabilityInfo Avail, const Decl *D, HeaderType Access, 63 const clang::ObjCIvarDecl::AccessControl AC) { 64 // If the decl otherwise would have been exported, check their access control. 65 // Ivar's linkage is also determined by this. 66 if ((Linkage == RecordLinkage::Exported) && 67 ((AC == ObjCIvarDecl::Private) || (AC == ObjCIvarDecl::Package))) 68 Linkage = RecordLinkage::Internal; 69 ObjCIVarRecord *ObjCR = 70 llvm::MachO::RecordsSlice::addObjCIVar(Container, IvarName, Linkage); 71 auto Result = FrontendRecords.insert( 72 {ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}}); 73 74 return {ObjCR, &(Result.first->second)}; 75 } 76 77 std::optional<HeaderType> 78 InstallAPIContext::findAndRecordFile(const FileEntry *FE, 79 const Preprocessor &PP) { 80 if (!FE) 81 return std::nullopt; 82 83 // Check if header has been looked up already and whether it is something 84 // installapi should use. 85 auto It = KnownFiles.find(FE); 86 if (It != KnownFiles.end()) { 87 if (It->second != HeaderType::Unknown) 88 return It->second; 89 else 90 return std::nullopt; 91 } 92 93 // If file was not found, search by how the header was 94 // included. This is primarily to resolve headers found 95 // in a different location than what passed directly as input. 96 StringRef IncludeName = PP.getHeaderSearchInfo().getIncludeNameForHeader(FE); 97 auto BackupIt = KnownIncludes.find(IncludeName.str()); 98 if (BackupIt != KnownIncludes.end()) { 99 KnownFiles[FE] = BackupIt->second; 100 return BackupIt->second; 101 } 102 103 // Record that the file was found to avoid future string searches for the 104 // same file. 105 KnownFiles.insert({FE, HeaderType::Unknown}); 106 return std::nullopt; 107 } 108 109 void InstallAPIContext::addKnownHeader(const HeaderFile &H) { 110 auto FE = FM->getFile(H.getPath()); 111 if (!FE) 112 return; // File does not exist. 113 KnownFiles[*FE] = H.getType(); 114 115 if (!H.useIncludeName()) 116 return; 117 118 KnownIncludes[H.getIncludeName()] = H.getType(); 119 } 120 121 static StringRef getFileExtension(clang::Language Lang) { 122 switch (Lang) { 123 default: 124 llvm_unreachable("Unexpected language option."); 125 case clang::Language::C: 126 return ".c"; 127 case clang::Language::CXX: 128 return ".cpp"; 129 case clang::Language::ObjC: 130 return ".m"; 131 case clang::Language::ObjCXX: 132 return ".mm"; 133 } 134 } 135 136 std::unique_ptr<MemoryBuffer> createInputBuffer(InstallAPIContext &Ctx) { 137 assert(Ctx.Type != HeaderType::Unknown && 138 "unexpected access level for parsing"); 139 SmallString<4096> Contents; 140 raw_svector_ostream OS(Contents); 141 for (const HeaderFile &H : Ctx.InputHeaders) { 142 if (H.isExcluded()) 143 continue; 144 if (H.getType() != Ctx.Type) 145 continue; 146 if (Ctx.LangMode == Language::C || Ctx.LangMode == Language::CXX) 147 OS << "#include "; 148 else 149 OS << "#import "; 150 if (H.useIncludeName()) 151 OS << "<" << H.getIncludeName() << ">\n"; 152 else 153 OS << "\"" << H.getPath() << "\"\n"; 154 155 Ctx.addKnownHeader(H); 156 } 157 if (Contents.empty()) 158 return nullptr; 159 160 SmallString<64> BufferName( 161 {"installapi-includes-", Ctx.Slice->getTriple().str(), "-", 162 getName(Ctx.Type), getFileExtension(Ctx.LangMode)}); 163 return llvm::MemoryBuffer::getMemBufferCopy(Contents, BufferName); 164 } 165 166 std::string findLibrary(StringRef InstallName, FileManager &FM, 167 ArrayRef<std::string> FrameworkSearchPaths, 168 ArrayRef<std::string> LibrarySearchPaths, 169 ArrayRef<std::string> SearchPaths) { 170 auto getLibrary = 171 [&](const StringRef FullPath) -> std::optional<std::string> { 172 // Prefer TextAPI files when possible. 173 SmallString<PATH_MAX> TextAPIFilePath = FullPath; 174 replace_extension(TextAPIFilePath, ".tbd"); 175 176 if (FM.getOptionalFileRef(TextAPIFilePath)) 177 return std::string(TextAPIFilePath); 178 179 if (FM.getOptionalFileRef(FullPath)) 180 return std::string(FullPath); 181 182 return std::nullopt; 183 }; 184 185 const StringRef Filename = sys::path::filename(InstallName); 186 const bool IsFramework = sys::path::parent_path(InstallName) 187 .ends_with((Filename + ".framework").str()); 188 if (IsFramework) { 189 for (const StringRef Path : FrameworkSearchPaths) { 190 SmallString<PATH_MAX> FullPath(Path); 191 sys::path::append(FullPath, Filename + StringRef(".framework"), Filename); 192 if (auto LibOrNull = getLibrary(FullPath)) 193 return *LibOrNull; 194 } 195 } else { 196 // Copy Apple's linker behavior: If this is a .dylib inside a framework, do 197 // not search -L paths. 198 bool IsEmbeddedDylib = (sys::path::extension(InstallName) == ".dylib") && 199 InstallName.contains(".framework/"); 200 if (!IsEmbeddedDylib) { 201 for (const StringRef Path : LibrarySearchPaths) { 202 SmallString<PATH_MAX> FullPath(Path); 203 sys::path::append(FullPath, Filename); 204 if (auto LibOrNull = getLibrary(FullPath)) 205 return *LibOrNull; 206 } 207 } 208 } 209 210 for (const StringRef Path : SearchPaths) { 211 SmallString<PATH_MAX> FullPath(Path); 212 sys::path::append(FullPath, InstallName); 213 if (auto LibOrNull = getLibrary(FullPath)) 214 return *LibOrNull; 215 } 216 217 return {}; 218 } 219 220 } // namespace clang::installapi 221