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