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 {
addGlobal(StringRef Name,RecordLinkage Linkage,GlobalRecord::Kind GV,const clang::AvailabilityInfo Avail,const Decl * D,const HeaderType Access,SymbolFlags Flags,bool Inlined)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 *>
addObjCInterface(StringRef Name,RecordLinkage Linkage,const clang::AvailabilityInfo Avail,const Decl * D,HeaderType Access,bool IsEHType)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 *>
addObjCCategory(StringRef ClassToExtend,StringRef CategoryName,const clang::AvailabilityInfo Avail,const Decl * D,HeaderType Access)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
addObjCIVar(ObjCContainerRecord * Container,StringRef IvarName,RecordLinkage Linkage,const clang::AvailabilityInfo Avail,const Decl * D,HeaderType Access,const clang::ObjCIvarDecl::AccessControl AC)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>
findAndRecordFile(const FileEntry * FE,const Preprocessor & PP)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
addKnownHeader(const HeaderFile & H)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
getFileExtension(clang::Language Lang)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
createInputBuffer(InstallAPIContext & Ctx)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
findLibrary(StringRef InstallName,FileManager & FM,ArrayRef<std::string> FrameworkSearchPaths,ArrayRef<std::string> LibrarySearchPaths,ArrayRef<std::string> SearchPaths)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