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
18using namespace llvm;
19using namespace llvm::MachO;
20
21void InterfaceFileRef::addTarget(const Target &Target) {
22  addEntry(Targets, Target);
23}
24
25void 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
33void 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
41void 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
56void 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
71void InterfaceFile::addTarget(const Target &Target) {
72  addEntry(Targets, Target);
73}
74
75InterfaceFile::const_filtered_target_range
76InterfaceFile::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
83void 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
93void 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
122Expected<std::unique_ptr<InterfaceFile>>
123InterfaceFile::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
217Expected<std::unique_ptr<InterfaceFile>>
218InterfaceFile::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
297Expected<std::unique_ptr<InterfaceFile>>
298InterfaceFile::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
354static bool isYAMLTextStub(const FileType &Kind) {
355  return (Kind >= FileType::TBD_V1) && (Kind < FileType::TBD_V5);
356}
357
358bool 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