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