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 { |
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 | KV: {GR, FrontendAttrs{.Avail: Avail, .D: D, .Loc: D->getLocation(), .Access: Access}}); |
27 | return {GR, &(Result.first->second)}; |
28 | } |
29 | |
30 | std::pair<ObjCInterfaceRecord *, FrontendAttrs *> |
31 | FrontendRecordsSlice::(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 | KV: {ObjCR, FrontendAttrs{.Avail: Avail, .D: D, .Loc: D->getLocation(), .Access: Access}}); |
44 | return {ObjCR, &(Result.first->second)}; |
45 | } |
46 | |
47 | std::pair<ObjCCategoryRecord *, FrontendAttrs *> |
48 | FrontendRecordsSlice::(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, Category: CategoryName); |
54 | auto Result = FrontendRecords.insert( |
55 | KV: {ObjCR, FrontendAttrs{.Avail: Avail, .D: D, .Loc: D->getLocation(), .Access: Access}}); |
56 | return {ObjCR, &(Result.first->second)}; |
57 | } |
58 | |
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, Name: IvarName, Linkage); |
70 | auto Result = FrontendRecords.insert( |
71 | KV: {ObjCR, FrontendAttrs{.Avail: Avail, .D: D, .Loc: D->getLocation(), .Access: Access}}); |
72 | |
73 | return {ObjCR, &(Result.first->second)}; |
74 | } |
75 | |
76 | std::optional<HeaderType> |
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(Val: 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(File: FE); |
96 | auto BackupIt = KnownIncludes.find(Val: 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(KV: {FE, HeaderType::Unknown}); |
105 | return std::nullopt; |
106 | } |
107 | |
108 | void InstallAPIContext::(const HeaderFile &H) { |
109 | auto FE = FM->getOptionalFileRef(Filename: 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 | |
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 | |
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(T: Ctx.Type), getFileExtension(Lang: Ctx.LangMode)}); |
162 | return llvm::MemoryBuffer::getMemBufferCopy(InputData: Contents, BufferName); |
163 | } |
164 | |
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(Path&: TextAPIFilePath, Extension: ".tbd" ); |
174 | |
175 | if (FM.getOptionalFileRef(Filename: TextAPIFilePath)) |
176 | return std::string(TextAPIFilePath); |
177 | |
178 | if (FM.getOptionalFileRef(Filename: FullPath)) |
179 | return std::string(FullPath); |
180 | |
181 | return std::nullopt; |
182 | }; |
183 | |
184 | const StringRef Filename = sys::path::filename(path: InstallName); |
185 | const bool IsFramework = sys::path::parent_path(path: InstallName) |
186 | .ends_with(Suffix: (Filename + ".framework" ).str()); |
187 | if (IsFramework) { |
188 | for (const StringRef Path : FrameworkSearchPaths) { |
189 | SmallString<PATH_MAX> FullPath(Path); |
190 | sys::path::append(path&: FullPath, a: Filename + StringRef(".framework" ), b: 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(path: InstallName) == ".dylib" ) && |
198 | InstallName.contains(Other: ".framework/" ); |
199 | if (!IsEmbeddedDylib) { |
200 | for (const StringRef Path : LibrarySearchPaths) { |
201 | SmallString<PATH_MAX> FullPath(Path); |
202 | sys::path::append(path&: FullPath, a: 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(path&: FullPath, a: InstallName); |
212 | if (auto LibOrNull = getLibrary(FullPath)) |
213 | return *LibOrNull; |
214 | } |
215 | |
216 | return {}; |
217 | } |
218 | |
219 | } // namespace clang::installapi |
220 | |