1 //===- InterfaceFile.cpp --------------------------------------------------===//
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 // Implements the Interface File.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/TextAPI/InterfaceFile.h"
14 #include "llvm/TextAPI/TextAPIError.h"
15 #include <iomanip>
16 #include <sstream>
17 
18 using namespace llvm;
19 using namespace llvm::MachO;
20 
addTarget(const Target & Target)21 void InterfaceFileRef::addTarget(const Target &Target) {
22   addEntry(Targets, Target);
23 }
24 
addAllowableClient(StringRef InstallName,const Target & Target)25 void InterfaceFile::addAllowableClient(StringRef InstallName,
26                                        const Target &Target) {
27   if (InstallName.empty())
28     return;
29   auto Client = addEntry(AllowableClients, InstallName);
30   Client->addTarget(Target);
31 }
32 
addReexportedLibrary(StringRef InstallName,const Target & Target)33 void InterfaceFile::addReexportedLibrary(StringRef InstallName,
34                                          const Target &Target) {
35   if (InstallName.empty())
36     return;
37   auto Lib = addEntry(ReexportedLibraries, InstallName);
38   Lib->addTarget(Target);
39 }
40 
addParentUmbrella(const Target & Target_,StringRef Parent)41 void InterfaceFile::addParentUmbrella(const Target &Target_, StringRef Parent) {
42   if (Parent.empty())
43     return;
44   auto Iter = lower_bound(ParentUmbrellas, Target_,
45                           [](const std::pair<Target, std::string> &LHS,
46                              Target RHS) { return LHS.first < RHS; });
47 
48   if ((Iter != ParentUmbrellas.end()) && !(Target_ < Iter->first)) {
49     Iter->second = std::string(Parent);
50     return;
51   }
52 
53   ParentUmbrellas.emplace(Iter, Target_, std::string(Parent));
54 }
55 
addRPath(const Target & InputTarget,StringRef RPath)56 void InterfaceFile::addRPath(const Target &InputTarget, StringRef RPath) {
57   if (RPath.empty())
58     return;
59   using RPathEntryT = const std::pair<Target, std::string>;
60   RPathEntryT Entry(InputTarget, RPath);
61   auto Iter =
62       lower_bound(RPaths, Entry,
63                   [](RPathEntryT &LHS, RPathEntryT &RHS) { return LHS < RHS; });
64 
65   if ((Iter != RPaths.end()) && (*Iter == Entry))
66     return;
67 
68   RPaths.emplace(Iter, Entry);
69 }
70 
addTarget(const Target & Target)71 void InterfaceFile::addTarget(const Target &Target) {
72   addEntry(Targets, Target);
73 }
74 
75 InterfaceFile::const_filtered_target_range
targets(ArchitectureSet Archs) const76 InterfaceFile::targets(ArchitectureSet Archs) const {
77   std::function<bool(const Target &)> fn = [Archs](const Target &Target_) {
78     return Archs.has(Target_.Arch);
79   };
80   return make_filter_range(Targets, fn);
81 }
82 
addDocument(std::shared_ptr<InterfaceFile> && Document)83 void InterfaceFile::addDocument(std::shared_ptr<InterfaceFile> &&Document) {
84   auto Pos = llvm::lower_bound(Documents, Document,
85                                [](const std::shared_ptr<InterfaceFile> &LHS,
86                                   const std::shared_ptr<InterfaceFile> &RHS) {
87                                  return LHS->InstallName < RHS->InstallName;
88                                });
89   Document->Parent = this;
90   Documents.insert(Pos, Document);
91 }
92 
inlineLibrary(std::shared_ptr<InterfaceFile> Library,bool Overwrite)93 void InterfaceFile::inlineLibrary(std::shared_ptr<InterfaceFile> Library,
94                                   bool Overwrite) {
95   auto AddFwk = [&](std::shared_ptr<InterfaceFile> &&Reexport) {
96     auto It = lower_bound(
97         Documents, Reexport->getInstallName(),
98         [](std::shared_ptr<InterfaceFile> &Lhs, const StringRef Rhs) {
99           return Lhs->getInstallName() < Rhs;
100         });
101 
102     if (Overwrite && It != Documents.end() &&
103         Reexport->getInstallName() == (*It)->getInstallName()) {
104       std::replace(Documents.begin(), Documents.end(), *It,
105                    std::move(Reexport));
106       return;
107     }
108 
109     if ((It != Documents.end()) &&
110         !(Reexport->getInstallName() < (*It)->getInstallName()))
111       return;
112 
113     Documents.emplace(It, std::move(Reexport));
114   };
115   for (auto Doc : Library->documents())
116     AddFwk(std::move(Doc));
117 
118   Library->Documents.clear();
119   AddFwk(std::move(Library));
120 }
121 
122 Expected<std::unique_ptr<InterfaceFile>>
merge(const InterfaceFile * O) const123 InterfaceFile::merge(const InterfaceFile *O) const {
124   // Verify files can be merged.
125   if (getInstallName() != O->getInstallName()) {
126     return make_error<StringError>("install names do not match",
127                                    inconvertibleErrorCode());
128   }
129 
130   if (getCurrentVersion() != O->getCurrentVersion()) {
131     return make_error<StringError>("current versions do not match",
132                                    inconvertibleErrorCode());
133   }
134 
135   if (getCompatibilityVersion() != O->getCompatibilityVersion()) {
136     return make_error<StringError>("compatibility versions do not match",
137                                    inconvertibleErrorCode());
138   }
139 
140   if ((getSwiftABIVersion() != 0) && (O->getSwiftABIVersion() != 0) &&
141       (getSwiftABIVersion() != O->getSwiftABIVersion())) {
142     return make_error<StringError>("swift ABI versions do not match",
143                                    inconvertibleErrorCode());
144   }
145 
146   if (isTwoLevelNamespace() != O->isTwoLevelNamespace()) {
147     return make_error<StringError>("two level namespace flags do not match",
148                                    inconvertibleErrorCode());
149   }
150 
151   if (isApplicationExtensionSafe() != O->isApplicationExtensionSafe()) {
152     return make_error<StringError>(
153         "application extension safe flags do not match",
154         inconvertibleErrorCode());
155   }
156 
157   std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
158   IF->setFileType(std::max(getFileType(), O->getFileType()));
159   IF->setPath(getPath());
160   IF->setInstallName(getInstallName());
161   IF->setCurrentVersion(getCurrentVersion());
162   IF->setCompatibilityVersion(getCompatibilityVersion());
163 
164   if (getSwiftABIVersion() == 0)
165     IF->setSwiftABIVersion(O->getSwiftABIVersion());
166   else
167     IF->setSwiftABIVersion(getSwiftABIVersion());
168 
169   IF->setTwoLevelNamespace(isTwoLevelNamespace());
170   IF->setApplicationExtensionSafe(isApplicationExtensionSafe());
171 
172   for (const auto &It : umbrellas()) {
173     if (!It.second.empty())
174       IF->addParentUmbrella(It.first, It.second);
175   }
176   for (const auto &It : O->umbrellas()) {
177     if (!It.second.empty())
178       IF->addParentUmbrella(It.first, It.second);
179   }
180   IF->addTargets(targets());
181   IF->addTargets(O->targets());
182 
183   for (const auto &Lib : allowableClients())
184     for (const auto &Target : Lib.targets())
185       IF->addAllowableClient(Lib.getInstallName(), Target);
186 
187   for (const auto &Lib : O->allowableClients())
188     for (const auto &Target : Lib.targets())
189       IF->addAllowableClient(Lib.getInstallName(), Target);
190 
191   for (const auto &Lib : reexportedLibraries())
192     for (const auto &Target : Lib.targets())
193       IF->addReexportedLibrary(Lib.getInstallName(), Target);
194 
195   for (const auto &Lib : O->reexportedLibraries())
196     for (const auto &Target : Lib.targets())
197       IF->addReexportedLibrary(Lib.getInstallName(), Target);
198 
199   for (const auto &[Target, Path] : rpaths())
200     IF->addRPath(Target, Path);
201   for (const auto &[Target, Path] : O->rpaths())
202     IF->addRPath(Target, Path);
203 
204   for (const auto *Sym : symbols()) {
205     IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(),
206                   Sym->getFlags());
207   }
208 
209   for (const auto *Sym : O->symbols()) {
210     IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(),
211                   Sym->getFlags());
212   }
213 
214   return std::move(IF);
215 }
216 
217 Expected<std::unique_ptr<InterfaceFile>>
remove(Architecture Arch) const218 InterfaceFile::remove(Architecture Arch) const {
219   if (getArchitectures() == Arch)
220     return make_error<StringError>("cannot remove last architecture slice '" +
221                                        getArchitectureName(Arch) + "'",
222                                    inconvertibleErrorCode());
223 
224   if (!getArchitectures().has(Arch)) {
225     bool Found = false;
226     for (auto &Doc : Documents) {
227       if (Doc->getArchitectures().has(Arch)) {
228         Found = true;
229         break;
230       }
231     }
232 
233     if (!Found)
234       return make_error<TextAPIError>(TextAPIErrorCode::NoSuchArchitecture);
235   }
236 
237   std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
238   IF->setFileType(getFileType());
239   IF->setPath(getPath());
240   IF->addTargets(targets(ArchitectureSet::All().clear(Arch)));
241   IF->setInstallName(getInstallName());
242   IF->setCurrentVersion(getCurrentVersion());
243   IF->setCompatibilityVersion(getCompatibilityVersion());
244   IF->setSwiftABIVersion(getSwiftABIVersion());
245   IF->setTwoLevelNamespace(isTwoLevelNamespace());
246   IF->setApplicationExtensionSafe(isApplicationExtensionSafe());
247   for (const auto &It : umbrellas())
248     if (It.first.Arch != Arch)
249       IF->addParentUmbrella(It.first, It.second);
250 
251   for (const auto &Lib : allowableClients()) {
252     for (const auto &Target : Lib.targets())
253       if (Target.Arch != Arch)
254         IF->addAllowableClient(Lib.getInstallName(), Target);
255   }
256 
257   for (const auto &Lib : reexportedLibraries()) {
258     for (const auto &Target : Lib.targets())
259       if (Target.Arch != Arch)
260         IF->addReexportedLibrary(Lib.getInstallName(), Target);
261   }
262 
263   for (const auto *Sym : symbols()) {
264     auto Archs = Sym->getArchitectures();
265     Archs.clear(Arch);
266     if (Archs.empty())
267       continue;
268 
269     IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(Archs),
270                   Sym->getFlags());
271   }
272 
273   for (auto &Doc : Documents) {
274     // Skip the inlined document if the to be removed architecture is the
275     // only one left.
276     if (Doc->getArchitectures() == Arch)
277       continue;
278 
279     // If the document doesn't contain the arch, then no work is to be done
280     // and it can be copied over.
281     if (!Doc->getArchitectures().has(Arch)) {
282       auto NewDoc = Doc;
283       IF->addDocument(std::move(NewDoc));
284       continue;
285     }
286 
287     auto Result = Doc->remove(Arch);
288     if (!Result)
289       return Result;
290 
291     IF->addDocument(std::move(Result.get()));
292   }
293 
294   return std::move(IF);
295 }
296 
297 Expected<std::unique_ptr<InterfaceFile>>
extract(Architecture Arch) const298 InterfaceFile::extract(Architecture Arch) const {
299   if (!getArchitectures().has(Arch)) {
300     return make_error<StringError>("file doesn't have architecture '" +
301                                        getArchitectureName(Arch) + "'",
302                                    inconvertibleErrorCode());
303   }
304 
305   std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
306   IF->setFileType(getFileType());
307   IF->setPath(getPath());
308   IF->addTargets(targets(Arch));
309   IF->setInstallName(getInstallName());
310   IF->setCurrentVersion(getCurrentVersion());
311   IF->setCompatibilityVersion(getCompatibilityVersion());
312   IF->setSwiftABIVersion(getSwiftABIVersion());
313   IF->setTwoLevelNamespace(isTwoLevelNamespace());
314   IF->setApplicationExtensionSafe(isApplicationExtensionSafe());
315   for (const auto &It : umbrellas())
316     if (It.first.Arch == Arch)
317       IF->addParentUmbrella(It.first, It.second);
318 
319   for (const auto &It : rpaths())
320     if (It.first.Arch == Arch)
321       IF->addRPath(It.first, It.second);
322 
323   for (const auto &Lib : allowableClients())
324     for (const auto &Target : Lib.targets())
325       if (Target.Arch == Arch)
326         IF->addAllowableClient(Lib.getInstallName(), Target);
327 
328   for (const auto &Lib : reexportedLibraries())
329     for (const auto &Target : Lib.targets())
330       if (Target.Arch == Arch)
331         IF->addReexportedLibrary(Lib.getInstallName(), Target);
332 
333   for (const auto *Sym : symbols()) {
334     if (Sym->hasArchitecture(Arch))
335       IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(Arch),
336                     Sym->getFlags());
337   }
338 
339   for (auto &Doc : Documents) {
340     // Skip documents that don't have the requested architecture.
341     if (!Doc->getArchitectures().has(Arch))
342       continue;
343 
344     auto Result = Doc->extract(Arch);
345     if (!Result)
346       return Result;
347 
348     IF->addDocument(std::move(Result.get()));
349   }
350 
351   return std::move(IF);
352 }
353 
isYAMLTextStub(const FileType & Kind)354 static bool isYAMLTextStub(const FileType &Kind) {
355   return (Kind >= FileType::TBD_V1) && (Kind < FileType::TBD_V5);
356 }
357 
operator ==(const InterfaceFile & O) const358 bool InterfaceFile::operator==(const InterfaceFile &O) const {
359   if (Targets != O.Targets)
360     return false;
361   if (InstallName != O.InstallName)
362     return false;
363   if ((CurrentVersion != O.CurrentVersion) ||
364       (CompatibilityVersion != O.CompatibilityVersion))
365     return false;
366   if (SwiftABIVersion != O.SwiftABIVersion)
367     return false;
368   if (IsTwoLevelNamespace != O.IsTwoLevelNamespace)
369     return false;
370   if (IsAppExtensionSafe != O.IsAppExtensionSafe)
371     return false;
372   if (IsOSLibNotForSharedCache != O.IsOSLibNotForSharedCache)
373     return false;
374   if (HasSimSupport != O.HasSimSupport)
375     return false;
376   if (ParentUmbrellas != O.ParentUmbrellas)
377     return false;
378   if (AllowableClients != O.AllowableClients)
379     return false;
380   if (ReexportedLibraries != O.ReexportedLibraries)
381     return false;
382   if (*SymbolsSet != *O.SymbolsSet)
383     return false;
384   // Don't compare run search paths for older filetypes that cannot express
385   // them.
386   if (!(isYAMLTextStub(FileKind)) && !(isYAMLTextStub(O.FileKind))) {
387     if (RPaths != O.RPaths)
388       return false;
389     if (mapToPlatformVersionSet(Targets) != mapToPlatformVersionSet(O.Targets))
390       return false;
391   }
392 
393   if (!std::equal(Documents.begin(), Documents.end(), O.Documents.begin(),
394                   O.Documents.end(),
395                   [](const std::shared_ptr<InterfaceFile> LHS,
396                      const std::shared_ptr<InterfaceFile> RHS) {
397                     return *LHS == *RHS;
398                   }))
399     return false;
400   return true;
401 }
402