1c6cbf81cSCyndy Ishida //===- Frontend.cpp ---------------------------------------------*- C++ -*-===// 2c6cbf81cSCyndy Ishida // 3c6cbf81cSCyndy Ishida // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4c6cbf81cSCyndy Ishida // See https://llvm.org/LICENSE.txt for license information. 5c6cbf81cSCyndy Ishida // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6c6cbf81cSCyndy Ishida // 7c6cbf81cSCyndy Ishida //===----------------------------------------------------------------------===// 8c6cbf81cSCyndy Ishida 9c6cbf81cSCyndy Ishida #include "clang/InstallAPI/Frontend.h" 10c6cbf81cSCyndy Ishida #include "clang/AST/Availability.h" 11a38b7a43SCyndy Ishida #include "clang/InstallAPI/FrontendRecords.h" 12c6cbf81cSCyndy Ishida #include "llvm/ADT/SmallString.h" 13c6cbf81cSCyndy Ishida #include "llvm/ADT/StringRef.h" 14c6cbf81cSCyndy Ishida 15c6cbf81cSCyndy Ishida using namespace llvm; 16c6cbf81cSCyndy Ishida using namespace llvm::MachO; 17c6cbf81cSCyndy Ishida 18c6cbf81cSCyndy Ishida namespace clang::installapi { 19f2794cceSCyndy Ishida std::pair<GlobalRecord *, FrontendAttrs *> FrontendRecordsSlice::addGlobal( 2017ede03aSCyndy Ishida StringRef Name, RecordLinkage Linkage, GlobalRecord::Kind GV, 2117ede03aSCyndy Ishida const clang::AvailabilityInfo Avail, const Decl *D, const HeaderType Access, 2250ae8a2aSCyndy Ishida SymbolFlags Flags, bool Inlined) { 2317ede03aSCyndy Ishida 24f2794cceSCyndy Ishida GlobalRecord *GR = 2550ae8a2aSCyndy Ishida llvm::MachO::RecordsSlice::addGlobal(Name, Linkage, GV, Flags, Inlined); 26f04452deSCyndy Ishida auto Result = FrontendRecords.insert( 27f04452deSCyndy Ishida {GR, FrontendAttrs{Avail, D, D->getLocation(), Access}}); 28f2794cceSCyndy Ishida return {GR, &(Result.first->second)}; 2917ede03aSCyndy Ishida } 3017ede03aSCyndy Ishida 31f2794cceSCyndy Ishida std::pair<ObjCInterfaceRecord *, FrontendAttrs *> 32f2794cceSCyndy Ishida FrontendRecordsSlice::addObjCInterface(StringRef Name, RecordLinkage Linkage, 33f2794cceSCyndy Ishida const clang::AvailabilityInfo Avail, 34f2794cceSCyndy Ishida const Decl *D, HeaderType Access, 35f2794cceSCyndy Ishida bool IsEHType) { 3617ede03aSCyndy Ishida ObjCIFSymbolKind SymType = 3717ede03aSCyndy Ishida ObjCIFSymbolKind::Class | ObjCIFSymbolKind::MetaClass; 3817ede03aSCyndy Ishida if (IsEHType) 3917ede03aSCyndy Ishida SymType |= ObjCIFSymbolKind::EHType; 40f2794cceSCyndy Ishida 41f2794cceSCyndy Ishida ObjCInterfaceRecord *ObjCR = 4217ede03aSCyndy Ishida llvm::MachO::RecordsSlice::addObjCInterface(Name, Linkage, SymType); 43f04452deSCyndy Ishida auto Result = FrontendRecords.insert( 44f04452deSCyndy Ishida {ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}}); 45f2794cceSCyndy Ishida return {ObjCR, &(Result.first->second)}; 4617ede03aSCyndy Ishida } 4717ede03aSCyndy Ishida 48f2794cceSCyndy Ishida std::pair<ObjCCategoryRecord *, FrontendAttrs *> 49f2794cceSCyndy Ishida FrontendRecordsSlice::addObjCCategory(StringRef ClassToExtend, 50f2794cceSCyndy Ishida StringRef CategoryName, 51f2794cceSCyndy Ishida const clang::AvailabilityInfo Avail, 52f2794cceSCyndy Ishida const Decl *D, HeaderType Access) { 53f2794cceSCyndy Ishida ObjCCategoryRecord *ObjCR = 5410ccde30SCyndy Ishida llvm::MachO::RecordsSlice::addObjCCategory(ClassToExtend, CategoryName); 55f04452deSCyndy Ishida auto Result = FrontendRecords.insert( 56f04452deSCyndy Ishida {ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}}); 57f2794cceSCyndy Ishida return {ObjCR, &(Result.first->second)}; 5810ccde30SCyndy Ishida } 5910ccde30SCyndy Ishida 60f2794cceSCyndy Ishida std::pair<ObjCIVarRecord *, FrontendAttrs *> FrontendRecordsSlice::addObjCIVar( 6110ccde30SCyndy Ishida ObjCContainerRecord *Container, StringRef IvarName, RecordLinkage Linkage, 6210ccde30SCyndy Ishida const clang::AvailabilityInfo Avail, const Decl *D, HeaderType Access, 6310ccde30SCyndy Ishida const clang::ObjCIvarDecl::AccessControl AC) { 6410ccde30SCyndy Ishida // If the decl otherwise would have been exported, check their access control. 6510ccde30SCyndy Ishida // Ivar's linkage is also determined by this. 6610ccde30SCyndy Ishida if ((Linkage == RecordLinkage::Exported) && 6710ccde30SCyndy Ishida ((AC == ObjCIvarDecl::Private) || (AC == ObjCIvarDecl::Package))) 6810ccde30SCyndy Ishida Linkage = RecordLinkage::Internal; 69f2794cceSCyndy Ishida ObjCIVarRecord *ObjCR = 7010ccde30SCyndy Ishida llvm::MachO::RecordsSlice::addObjCIVar(Container, IvarName, Linkage); 71f04452deSCyndy Ishida auto Result = FrontendRecords.insert( 72f04452deSCyndy Ishida {ObjCR, FrontendAttrs{Avail, D, D->getLocation(), Access}}); 7310ccde30SCyndy Ishida 74f2794cceSCyndy Ishida return {ObjCR, &(Result.first->second)}; 7510ccde30SCyndy Ishida } 7610ccde30SCyndy Ishida 7717ede03aSCyndy Ishida std::optional<HeaderType> 7817ede03aSCyndy Ishida InstallAPIContext::findAndRecordFile(const FileEntry *FE, 7917ede03aSCyndy Ishida const Preprocessor &PP) { 8017ede03aSCyndy Ishida if (!FE) 8117ede03aSCyndy Ishida return std::nullopt; 8217ede03aSCyndy Ishida 8317ede03aSCyndy Ishida // Check if header has been looked up already and whether it is something 8417ede03aSCyndy Ishida // installapi should use. 8517ede03aSCyndy Ishida auto It = KnownFiles.find(FE); 8617ede03aSCyndy Ishida if (It != KnownFiles.end()) { 8717ede03aSCyndy Ishida if (It->second != HeaderType::Unknown) 8817ede03aSCyndy Ishida return It->second; 8917ede03aSCyndy Ishida else 9017ede03aSCyndy Ishida return std::nullopt; 9117ede03aSCyndy Ishida } 9217ede03aSCyndy Ishida 9317ede03aSCyndy Ishida // If file was not found, search by how the header was 9417ede03aSCyndy Ishida // included. This is primarily to resolve headers found 9517ede03aSCyndy Ishida // in a different location than what passed directly as input. 9617ede03aSCyndy Ishida StringRef IncludeName = PP.getHeaderSearchInfo().getIncludeNameForHeader(FE); 97*c714f928SKazu Hirata auto BackupIt = KnownIncludes.find(IncludeName); 9817ede03aSCyndy Ishida if (BackupIt != KnownIncludes.end()) { 9917ede03aSCyndy Ishida KnownFiles[FE] = BackupIt->second; 10017ede03aSCyndy Ishida return BackupIt->second; 10117ede03aSCyndy Ishida } 10217ede03aSCyndy Ishida 10317ede03aSCyndy Ishida // Record that the file was found to avoid future string searches for the 10417ede03aSCyndy Ishida // same file. 10517ede03aSCyndy Ishida KnownFiles.insert({FE, HeaderType::Unknown}); 10617ede03aSCyndy Ishida return std::nullopt; 10717ede03aSCyndy Ishida } 10817ede03aSCyndy Ishida 10917ede03aSCyndy Ishida void InstallAPIContext::addKnownHeader(const HeaderFile &H) { 110b1aea98cSJan Svoboda auto FE = FM->getOptionalFileRef(H.getPath()); 11117ede03aSCyndy Ishida if (!FE) 11217ede03aSCyndy Ishida return; // File does not exist. 11317ede03aSCyndy Ishida KnownFiles[*FE] = H.getType(); 11417ede03aSCyndy Ishida 11517ede03aSCyndy Ishida if (!H.useIncludeName()) 11617ede03aSCyndy Ishida return; 11717ede03aSCyndy Ishida 11817ede03aSCyndy Ishida KnownIncludes[H.getIncludeName()] = H.getType(); 11917ede03aSCyndy Ishida } 12017ede03aSCyndy Ishida 121c6cbf81cSCyndy Ishida static StringRef getFileExtension(clang::Language Lang) { 122c6cbf81cSCyndy Ishida switch (Lang) { 123c6cbf81cSCyndy Ishida default: 124c6cbf81cSCyndy Ishida llvm_unreachable("Unexpected language option."); 125c6cbf81cSCyndy Ishida case clang::Language::C: 126c6cbf81cSCyndy Ishida return ".c"; 127c6cbf81cSCyndy Ishida case clang::Language::CXX: 128c6cbf81cSCyndy Ishida return ".cpp"; 129c6cbf81cSCyndy Ishida case clang::Language::ObjC: 130c6cbf81cSCyndy Ishida return ".m"; 131c6cbf81cSCyndy Ishida case clang::Language::ObjCXX: 132c6cbf81cSCyndy Ishida return ".mm"; 133c6cbf81cSCyndy Ishida } 134c6cbf81cSCyndy Ishida } 135c6cbf81cSCyndy Ishida 13617ede03aSCyndy Ishida std::unique_ptr<MemoryBuffer> createInputBuffer(InstallAPIContext &Ctx) { 137c6cbf81cSCyndy Ishida assert(Ctx.Type != HeaderType::Unknown && 138c6cbf81cSCyndy Ishida "unexpected access level for parsing"); 139c6cbf81cSCyndy Ishida SmallString<4096> Contents; 140c6cbf81cSCyndy Ishida raw_svector_ostream OS(Contents); 141c6cbf81cSCyndy Ishida for (const HeaderFile &H : Ctx.InputHeaders) { 142487720fcSCyndy Ishida if (H.isExcluded()) 143487720fcSCyndy Ishida continue; 144c6cbf81cSCyndy Ishida if (H.getType() != Ctx.Type) 145c6cbf81cSCyndy Ishida continue; 146c6cbf81cSCyndy Ishida if (Ctx.LangMode == Language::C || Ctx.LangMode == Language::CXX) 147c6cbf81cSCyndy Ishida OS << "#include "; 148c6cbf81cSCyndy Ishida else 149c6cbf81cSCyndy Ishida OS << "#import "; 150c6cbf81cSCyndy Ishida if (H.useIncludeName()) 1512c93beccSCyndy Ishida OS << "<" << H.getIncludeName() << ">\n"; 152c6cbf81cSCyndy Ishida else 1532c93beccSCyndy Ishida OS << "\"" << H.getPath() << "\"\n"; 15417ede03aSCyndy Ishida 15517ede03aSCyndy Ishida Ctx.addKnownHeader(H); 156c6cbf81cSCyndy Ishida } 157c6cbf81cSCyndy Ishida if (Contents.empty()) 158c6cbf81cSCyndy Ishida return nullptr; 159c6cbf81cSCyndy Ishida 1608116dfb8SCyndy Ishida SmallString<64> BufferName( 1618116dfb8SCyndy Ishida {"installapi-includes-", Ctx.Slice->getTriple().str(), "-", 1628116dfb8SCyndy Ishida getName(Ctx.Type), getFileExtension(Ctx.LangMode)}); 1638116dfb8SCyndy Ishida return llvm::MemoryBuffer::getMemBufferCopy(Contents, BufferName); 164c6cbf81cSCyndy Ishida } 165c6cbf81cSCyndy Ishida 16627b2d7d4SCyndy Ishida std::string findLibrary(StringRef InstallName, FileManager &FM, 16727b2d7d4SCyndy Ishida ArrayRef<std::string> FrameworkSearchPaths, 16827b2d7d4SCyndy Ishida ArrayRef<std::string> LibrarySearchPaths, 16927b2d7d4SCyndy Ishida ArrayRef<std::string> SearchPaths) { 17027b2d7d4SCyndy Ishida auto getLibrary = 17127b2d7d4SCyndy Ishida [&](const StringRef FullPath) -> std::optional<std::string> { 17227b2d7d4SCyndy Ishida // Prefer TextAPI files when possible. 17327b2d7d4SCyndy Ishida SmallString<PATH_MAX> TextAPIFilePath = FullPath; 17427b2d7d4SCyndy Ishida replace_extension(TextAPIFilePath, ".tbd"); 17527b2d7d4SCyndy Ishida 17627b2d7d4SCyndy Ishida if (FM.getOptionalFileRef(TextAPIFilePath)) 17727b2d7d4SCyndy Ishida return std::string(TextAPIFilePath); 17827b2d7d4SCyndy Ishida 17927b2d7d4SCyndy Ishida if (FM.getOptionalFileRef(FullPath)) 18027b2d7d4SCyndy Ishida return std::string(FullPath); 18127b2d7d4SCyndy Ishida 18227b2d7d4SCyndy Ishida return std::nullopt; 18327b2d7d4SCyndy Ishida }; 18427b2d7d4SCyndy Ishida 18527b2d7d4SCyndy Ishida const StringRef Filename = sys::path::filename(InstallName); 18627b2d7d4SCyndy Ishida const bool IsFramework = sys::path::parent_path(InstallName) 18727b2d7d4SCyndy Ishida .ends_with((Filename + ".framework").str()); 18827b2d7d4SCyndy Ishida if (IsFramework) { 18927b2d7d4SCyndy Ishida for (const StringRef Path : FrameworkSearchPaths) { 19027b2d7d4SCyndy Ishida SmallString<PATH_MAX> FullPath(Path); 19127b2d7d4SCyndy Ishida sys::path::append(FullPath, Filename + StringRef(".framework"), Filename); 19227b2d7d4SCyndy Ishida if (auto LibOrNull = getLibrary(FullPath)) 19327b2d7d4SCyndy Ishida return *LibOrNull; 19427b2d7d4SCyndy Ishida } 19527b2d7d4SCyndy Ishida } else { 19627b2d7d4SCyndy Ishida // Copy Apple's linker behavior: If this is a .dylib inside a framework, do 19727b2d7d4SCyndy Ishida // not search -L paths. 19827b2d7d4SCyndy Ishida bool IsEmbeddedDylib = (sys::path::extension(InstallName) == ".dylib") && 19927b2d7d4SCyndy Ishida InstallName.contains(".framework/"); 20027b2d7d4SCyndy Ishida if (!IsEmbeddedDylib) { 20127b2d7d4SCyndy Ishida for (const StringRef Path : LibrarySearchPaths) { 20227b2d7d4SCyndy Ishida SmallString<PATH_MAX> FullPath(Path); 20327b2d7d4SCyndy Ishida sys::path::append(FullPath, Filename); 20427b2d7d4SCyndy Ishida if (auto LibOrNull = getLibrary(FullPath)) 20527b2d7d4SCyndy Ishida return *LibOrNull; 20627b2d7d4SCyndy Ishida } 20727b2d7d4SCyndy Ishida } 20827b2d7d4SCyndy Ishida } 20927b2d7d4SCyndy Ishida 21027b2d7d4SCyndy Ishida for (const StringRef Path : SearchPaths) { 21127b2d7d4SCyndy Ishida SmallString<PATH_MAX> FullPath(Path); 21227b2d7d4SCyndy Ishida sys::path::append(FullPath, InstallName); 21327b2d7d4SCyndy Ishida if (auto LibOrNull = getLibrary(FullPath)) 21427b2d7d4SCyndy Ishida return *LibOrNull; 21527b2d7d4SCyndy Ishida } 21627b2d7d4SCyndy Ishida 21727b2d7d4SCyndy Ishida return {}; 21827b2d7d4SCyndy Ishida } 21927b2d7d4SCyndy Ishida 220c6cbf81cSCyndy Ishida } // namespace clang::installapi 221