1//===- TextStub.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 text stub file reader/writer.
10//
11//===----------------------------------------------------------------------===//
12
13#include "TextAPIContext.h"
14#include "TextStubCommon.h"
15#include "llvm/ADT/BitmaskEnum.h"
16#include "llvm/ADT/SmallString.h"
17#include "llvm/ADT/StringRef.h"
18#include "llvm/Support/Allocator.h"
19#include "llvm/Support/SourceMgr.h"
20#include "llvm/Support/YAMLTraits.h"
21#include "llvm/Support/raw_ostream.h"
22#include "llvm/TextAPI/MachO/Architecture.h"
23#include "llvm/TextAPI/MachO/ArchitectureSet.h"
24#include "llvm/TextAPI/MachO/InterfaceFile.h"
25#include "llvm/TextAPI/MachO/PackedVersion.h"
26#include "llvm/TextAPI/MachO/TextAPIReader.h"
27#include "llvm/TextAPI/MachO/TextAPIWriter.h"
28#include <algorithm>
29#include <set>
30
31// clang-format off
32/*
33
34 YAML Format specification.
35
36 The TBD v1 format only support two level address libraries and is per
37 definition application extension safe.
38
39---                              # the tag !tapi-tbd-v1 is optional and
40                                 # shouldn't be emitted to support older linker.
41archs: [ armv7, armv7s, arm64 ]  # the list of architecture slices that are
42                                 # supported by this file.
43platform: ios                    # Specifies the platform (macosx, ios, etc)
44install-name: /u/l/libfoo.dylib  #
45current-version: 1.2.3           # Optional: defaults to 1.0
46compatibility-version: 1.0       # Optional: defaults to 1.0
47swift-version: 0                 # Optional: defaults to 0
48objc-constraint: none            # Optional: defaults to none
49exports:                         # List of export sections
50...
51
52Each export section is defined as following:
53
54 - archs: [ arm64 ]                   # the list of architecture slices
55   allowed-clients: [ client ]        # Optional: List of clients
56   re-exports: [ ]                    # Optional: List of re-exports
57   symbols: [ _sym ]                  # Optional: List of symbols
58   objc-classes: []                   # Optional: List of Objective-C classes
59   objc-ivars: []                     # Optional: List of Objective C Instance
60                                      #           Variables
61   weak-def-symbols: []               # Optional: List of weak defined symbols
62   thread-local-symbols: []           # Optional: List of thread local symbols
63*/
64
65/*
66
67 YAML Format specification.
68
69--- !tapi-tbd-v2
70archs: [ armv7, armv7s, arm64 ]  # the list of architecture slices that are
71                                 # supported by this file.
72uuids: [ armv7:... ]             # Optional: List of architecture and UUID pairs.
73platform: ios                    # Specifies the platform (macosx, ios, etc)
74flags: []                        # Optional:
75install-name: /u/l/libfoo.dylib  #
76current-version: 1.2.3           # Optional: defaults to 1.0
77compatibility-version: 1.0       # Optional: defaults to 1.0
78swift-version: 0                 # Optional: defaults to 0
79objc-constraint: retain_release  # Optional: defaults to retain_release
80parent-umbrella:                 # Optional:
81exports:                         # List of export sections
82...
83undefineds:                      # List of undefineds sections
84...
85
86Each export section is defined as following:
87
88- archs: [ arm64 ]                   # the list of architecture slices
89  allowed-clients: [ client ]        # Optional: List of clients
90  re-exports: [ ]                    # Optional: List of re-exports
91  symbols: [ _sym ]                  # Optional: List of symbols
92  objc-classes: []                   # Optional: List of Objective-C classes
93  objc-ivars: []                     # Optional: List of Objective C Instance
94                                     #           Variables
95  weak-def-symbols: []               # Optional: List of weak defined symbols
96  thread-local-symbols: []           # Optional: List of thread local symbols
97
98Each undefineds section is defined as following:
99- archs: [ arm64 ]     # the list of architecture slices
100  symbols: [ _sym ]    # Optional: List of symbols
101  objc-classes: []     # Optional: List of Objective-C classes
102  objc-ivars: []       # Optional: List of Objective C Instance Variables
103  weak-ref-symbols: [] # Optional: List of weak defined symbols
104*/
105
106/*
107
108 YAML Format specification.
109
110--- !tapi-tbd-v3
111archs: [ armv7, armv7s, arm64 ]  # the list of architecture slices that are
112                                 # supported by this file.
113uuids: [ armv7:... ]             # Optional: List of architecture and UUID pairs.
114platform: ios                    # Specifies the platform (macosx, ios, etc)
115flags: []                        # Optional:
116install-name: /u/l/libfoo.dylib  #
117current-version: 1.2.3           # Optional: defaults to 1.0
118compatibility-version: 1.0       # Optional: defaults to 1.0
119swift-abi-version: 0             # Optional: defaults to 0
120objc-constraint: retain_release  # Optional: defaults to retain_release
121parent-umbrella:                 # Optional:
122exports:                         # List of export sections
123...
124undefineds:                      # List of undefineds sections
125...
126
127Each export section is defined as following:
128
129- archs: [ arm64 ]                   # the list of architecture slices
130  allowed-clients: [ client ]        # Optional: List of clients
131  re-exports: [ ]                    # Optional: List of re-exports
132  symbols: [ _sym ]                  # Optional: List of symbols
133  objc-classes: []                   # Optional: List of Objective-C classes
134  objc-eh-types: []                  # Optional: List of Objective-C classes
135                                     #           with EH
136  objc-ivars: []                     # Optional: List of Objective C Instance
137                                     #           Variables
138  weak-def-symbols: []               # Optional: List of weak defined symbols
139  thread-local-symbols: []           # Optional: List of thread local symbols
140
141Each undefineds section is defined as following:
142- archs: [ arm64 ]     # the list of architecture slices
143  symbols: [ _sym ]    # Optional: List of symbols
144  objc-classes: []     # Optional: List of Objective-C classes
145  objc-eh-types: []                  # Optional: List of Objective-C classes
146                                     #           with EH
147  objc-ivars: []       # Optional: List of Objective C Instance Variables
148  weak-ref-symbols: [] # Optional: List of weak defined symbols
149*/
150
151/*
152
153 YAML Format specification.
154
155--- !tapi-tbd
156tbd-version: 4                              # The tbd version for format
157targets: [ armv7-ios, x86_64-maccatalyst ]  # The list of applicable tapi supported target triples
158uuids:                                      # Optional: List of target and UUID pairs.
159  - target: armv7-ios
160    value: ...
161  - target: x86_64-maccatalyst
162    value: ...
163flags: []                        # Optional:
164install-name: /u/l/libfoo.dylib  #
165current-version: 1.2.3           # Optional: defaults to 1.0
166compatibility-version: 1.0       # Optional: defaults to 1.0
167swift-abi-version: 0             # Optional: defaults to 0
168parent-umbrella:                 # Optional:
169allowable-clients:
170  - targets: [ armv7-ios ]       # Optional:
171    clients: [ clientA ]
172exports:                         # List of export sections
173...
174re-exports:                      # List of reexport sections
175...
176undefineds:                      # List of undefineds sections
177...
178
179Each export and reexport  section is defined as following:
180
181- targets: [ arm64-macos ]                        # The list of target triples associated with symbols
182  symbols: [ _symA ]                              # Optional: List of symbols
183  objc-classes: []                                # Optional: List of Objective-C classes
184  objc-eh-types: []                               # Optional: List of Objective-C classes
185                                                  #           with EH
186  objc-ivars: []                                  # Optional: List of Objective C Instance
187                                                  #           Variables
188  weak-symbols: []                                # Optional: List of weak defined symbols
189  thread-local-symbols: []                        # Optional: List of thread local symbols
190- targets: [ arm64-macos, x86_64-maccatalyst ]    # Optional: Targets for applicable additional symbols
191  symbols: [ _symB ]                              # Optional: List of symbols
192
193Each undefineds section is defined as following:
194- targets: [ arm64-macos ]    # The list of target triples associated with symbols
195  symbols: [ _symC ]          # Optional: List of symbols
196  objc-classes: []            # Optional: List of Objective-C classes
197  objc-eh-types: []           # Optional: List of Objective-C classes
198                              #           with EH
199  objc-ivars: []              # Optional: List of Objective C Instance Variables
200  weak-symbols: []            # Optional: List of weak defined symbols
201*/
202// clang-format on
203
204using namespace llvm;
205using namespace llvm::yaml;
206using namespace llvm::MachO;
207
208namespace {
209struct ExportSection {
210  std::vector<Architecture> Architectures;
211  std::vector<FlowStringRef> AllowableClients;
212  std::vector<FlowStringRef> ReexportedLibraries;
213  std::vector<FlowStringRef> Symbols;
214  std::vector<FlowStringRef> Classes;
215  std::vector<FlowStringRef> ClassEHs;
216  std::vector<FlowStringRef> IVars;
217  std::vector<FlowStringRef> WeakDefSymbols;
218  std::vector<FlowStringRef> TLVSymbols;
219};
220
221struct UndefinedSection {
222  std::vector<Architecture> Architectures;
223  std::vector<FlowStringRef> Symbols;
224  std::vector<FlowStringRef> Classes;
225  std::vector<FlowStringRef> ClassEHs;
226  std::vector<FlowStringRef> IVars;
227  std::vector<FlowStringRef> WeakRefSymbols;
228};
229
230// Sections for direct target mapping in TBDv4
231struct SymbolSection {
232  TargetList Targets;
233  std::vector<FlowStringRef> Symbols;
234  std::vector<FlowStringRef> Classes;
235  std::vector<FlowStringRef> ClassEHs;
236  std::vector<FlowStringRef> Ivars;
237  std::vector<FlowStringRef> WeakSymbols;
238  std::vector<FlowStringRef> TlvSymbols;
239};
240
241struct MetadataSection {
242  enum Option { Clients, Libraries };
243  std::vector<Target> Targets;
244  std::vector<FlowStringRef> Values;
245};
246
247struct UmbrellaSection {
248  std::vector<Target> Targets;
249  std::string Umbrella;
250};
251
252// UUID's for TBDv4 are mapped to target not arch
253struct UUIDv4 {
254  Target TargetID;
255  std::string Value;
256
257  UUIDv4() = default;
258  UUIDv4(const Target &TargetID, const std::string &Value)
259      : TargetID(TargetID), Value(Value) {}
260};
261
262// clang-format off
263enum TBDFlags : unsigned {
264  None                         = 0U,
265  FlatNamespace                = 1U << 0,
266  NotApplicationExtensionSafe  = 1U << 1,
267  InstallAPI                   = 1U << 2,
268  LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/InstallAPI),
269};
270// clang-format on
271} // end anonymous namespace.
272
273LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(Architecture)
274LLVM_YAML_IS_SEQUENCE_VECTOR(ExportSection)
275LLVM_YAML_IS_SEQUENCE_VECTOR(UndefinedSection)
276// Specific to TBDv4
277LLVM_YAML_IS_SEQUENCE_VECTOR(SymbolSection)
278LLVM_YAML_IS_SEQUENCE_VECTOR(MetadataSection)
279LLVM_YAML_IS_SEQUENCE_VECTOR(UmbrellaSection)
280LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(Target)
281LLVM_YAML_IS_SEQUENCE_VECTOR(UUIDv4)
282
283namespace llvm {
284namespace yaml {
285
286template <> struct MappingTraits<ExportSection> {
287  static void mapping(IO &IO, ExportSection &Section) {
288    const auto *Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext());
289    assert((!Ctx || (Ctx && Ctx->FileKind != FileType::Invalid)) &&
290           "File type is not set in YAML context");
291
292    IO.mapRequired("archs", Section.Architectures);
293    if (Ctx->FileKind == FileType::TBD_V1)
294      IO.mapOptional("allowed-clients", Section.AllowableClients);
295    else
296      IO.mapOptional("allowable-clients", Section.AllowableClients);
297    IO.mapOptional("re-exports", Section.ReexportedLibraries);
298    IO.mapOptional("symbols", Section.Symbols);
299    IO.mapOptional("objc-classes", Section.Classes);
300    if (Ctx->FileKind == FileType::TBD_V3)
301      IO.mapOptional("objc-eh-types", Section.ClassEHs);
302    IO.mapOptional("objc-ivars", Section.IVars);
303    IO.mapOptional("weak-def-symbols", Section.WeakDefSymbols);
304    IO.mapOptional("thread-local-symbols", Section.TLVSymbols);
305  }
306};
307
308template <> struct MappingTraits<UndefinedSection> {
309  static void mapping(IO &IO, UndefinedSection &Section) {
310    const auto *Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext());
311    assert((!Ctx || (Ctx && Ctx->FileKind != FileType::Invalid)) &&
312           "File type is not set in YAML context");
313
314    IO.mapRequired("archs", Section.Architectures);
315    IO.mapOptional("symbols", Section.Symbols);
316    IO.mapOptional("objc-classes", Section.Classes);
317    if (Ctx->FileKind == FileType::TBD_V3)
318      IO.mapOptional("objc-eh-types", Section.ClassEHs);
319    IO.mapOptional("objc-ivars", Section.IVars);
320    IO.mapOptional("weak-ref-symbols", Section.WeakRefSymbols);
321  }
322};
323
324template <> struct MappingTraits<SymbolSection> {
325  static void mapping(IO &IO, SymbolSection &Section) {
326    IO.mapRequired("targets", Section.Targets);
327    IO.mapOptional("symbols", Section.Symbols);
328    IO.mapOptional("objc-classes", Section.Classes);
329    IO.mapOptional("objc-eh-types", Section.ClassEHs);
330    IO.mapOptional("objc-ivars", Section.Ivars);
331    IO.mapOptional("weak-symbols", Section.WeakSymbols);
332    IO.mapOptional("thread-local-symbols", Section.TlvSymbols);
333  }
334};
335
336template <> struct MappingTraits<UmbrellaSection> {
337  static void mapping(IO &IO, UmbrellaSection &Section) {
338    IO.mapRequired("targets", Section.Targets);
339    IO.mapRequired("umbrella", Section.Umbrella);
340  }
341};
342
343template <> struct MappingTraits<UUIDv4> {
344  static void mapping(IO &IO, UUIDv4 &UUID) {
345    IO.mapRequired("target", UUID.TargetID);
346    IO.mapRequired("value", UUID.Value);
347  }
348};
349
350template <>
351struct MappingContextTraits<MetadataSection, MetadataSection::Option> {
352  static void mapping(IO &IO, MetadataSection &Section,
353                      MetadataSection::Option &OptionKind) {
354    IO.mapRequired("targets", Section.Targets);
355    switch (OptionKind) {
356    case MetadataSection::Option::Clients:
357      IO.mapRequired("clients", Section.Values);
358      return;
359    case MetadataSection::Option::Libraries:
360      IO.mapRequired("libraries", Section.Values);
361      return;
362    }
363    llvm_unreachable("unexpected option for metadata");
364  }
365};
366
367template <> struct ScalarBitSetTraits<TBDFlags> {
368  static void bitset(IO &IO, TBDFlags &Flags) {
369    IO.bitSetCase(Flags, "flat_namespace", TBDFlags::FlatNamespace);
370    IO.bitSetCase(Flags, "not_app_extension_safe",
371                  TBDFlags::NotApplicationExtensionSafe);
372    IO.bitSetCase(Flags, "installapi", TBDFlags::InstallAPI);
373  }
374};
375
376template <> struct ScalarTraits<Target> {
377  static void output(const Target &Value, void *, raw_ostream &OS) {
378    OS << Value.Arch << "-";
379    switch (Value.Platform) {
380    default:
381      OS << "unknown";
382      break;
383    case PlatformKind::macOS:
384      OS << "macos";
385      break;
386    case PlatformKind::iOS:
387      OS << "ios";
388      break;
389    case PlatformKind::tvOS:
390      OS << "tvos";
391      break;
392    case PlatformKind::watchOS:
393      OS << "watchos";
394      break;
395    case PlatformKind::bridgeOS:
396      OS << "bridgeos";
397      break;
398    case PlatformKind::macCatalyst:
399      OS << "maccatalyst";
400      break;
401    case PlatformKind::iOSSimulator:
402      OS << "ios-simulator";
403      break;
404    case PlatformKind::tvOSSimulator:
405      OS << "tvos-simulator";
406      break;
407    case PlatformKind::watchOSSimulator:
408      OS << "watchos-simulator";
409      break;
410    }
411  }
412
413  static StringRef input(StringRef Scalar, void *, Target &Value) {
414    auto Result = Target::create(Scalar);
415    if (!Result) {
416      consumeError(Result.takeError());
417      return "unparsable target";
418    }
419
420    Value = *Result;
421    if (Value.Arch == AK_unknown)
422      return "unknown architecture";
423    if (Value.Platform == PlatformKind::unknown)
424      return "unknown platform";
425
426    return {};
427  }
428
429  static QuotingType mustQuote(StringRef) { return QuotingType::None; }
430};
431
432template <> struct MappingTraits<const InterfaceFile *> {
433  struct NormalizedTBD {
434    explicit NormalizedTBD(IO &IO) {}
435    NormalizedTBD(IO &IO, const InterfaceFile *&File) {
436      Architectures = File->getArchitectures();
437      UUIDs = File->uuids();
438      Platforms = File->getPlatforms();
439      InstallName = File->getInstallName();
440      CurrentVersion = PackedVersion(File->getCurrentVersion());
441      CompatibilityVersion = PackedVersion(File->getCompatibilityVersion());
442      SwiftABIVersion = File->getSwiftABIVersion();
443      ObjCConstraint = File->getObjCConstraint();
444
445      Flags = TBDFlags::None;
446      if (!File->isApplicationExtensionSafe())
447        Flags |= TBDFlags::NotApplicationExtensionSafe;
448
449      if (!File->isTwoLevelNamespace())
450        Flags |= TBDFlags::FlatNamespace;
451
452      if (File->isInstallAPI())
453        Flags |= TBDFlags::InstallAPI;
454
455      if (!File->umbrellas().empty())
456        ParentUmbrella = File->umbrellas().begin()->second;
457
458      std::set<ArchitectureSet> ArchSet;
459      for (const auto &Library : File->allowableClients())
460        ArchSet.insert(Library.getArchitectures());
461
462      for (const auto &Library : File->reexportedLibraries())
463        ArchSet.insert(Library.getArchitectures());
464
465      std::map<const Symbol *, ArchitectureSet> SymbolToArchSet;
466      for (const auto *Symbol : File->exports()) {
467        auto Architectures = Symbol->getArchitectures();
468        SymbolToArchSet[Symbol] = Architectures;
469        ArchSet.insert(Architectures);
470      }
471
472      for (auto Architectures : ArchSet) {
473        ExportSection Section;
474        Section.Architectures = Architectures;
475
476        for (const auto &Library : File->allowableClients())
477          if (Library.getArchitectures() == Architectures)
478            Section.AllowableClients.emplace_back(Library.getInstallName());
479
480        for (const auto &Library : File->reexportedLibraries())
481          if (Library.getArchitectures() == Architectures)
482            Section.ReexportedLibraries.emplace_back(Library.getInstallName());
483
484        for (const auto &SymArch : SymbolToArchSet) {
485          if (SymArch.second != Architectures)
486            continue;
487
488          const auto *Symbol = SymArch.first;
489          switch (Symbol->getKind()) {
490          case SymbolKind::GlobalSymbol:
491            if (Symbol->isWeakDefined())
492              Section.WeakDefSymbols.emplace_back(Symbol->getName());
493            else if (Symbol->isThreadLocalValue())
494              Section.TLVSymbols.emplace_back(Symbol->getName());
495            else
496              Section.Symbols.emplace_back(Symbol->getName());
497            break;
498          case SymbolKind::ObjectiveCClass:
499            if (File->getFileType() != FileType::TBD_V3)
500              Section.Classes.emplace_back(
501                  copyString("_" + Symbol->getName().str()));
502            else
503              Section.Classes.emplace_back(Symbol->getName());
504            break;
505          case SymbolKind::ObjectiveCClassEHType:
506            if (File->getFileType() != FileType::TBD_V3)
507              Section.Symbols.emplace_back(
508                  copyString("_OBJC_EHTYPE_$_" + Symbol->getName().str()));
509            else
510              Section.ClassEHs.emplace_back(Symbol->getName());
511            break;
512          case SymbolKind::ObjectiveCInstanceVariable:
513            if (File->getFileType() != FileType::TBD_V3)
514              Section.IVars.emplace_back(
515                  copyString("_" + Symbol->getName().str()));
516            else
517              Section.IVars.emplace_back(Symbol->getName());
518            break;
519          }
520        }
521        llvm::sort(Section.Symbols.begin(), Section.Symbols.end());
522        llvm::sort(Section.Classes.begin(), Section.Classes.end());
523        llvm::sort(Section.ClassEHs.begin(), Section.ClassEHs.end());
524        llvm::sort(Section.IVars.begin(), Section.IVars.end());
525        llvm::sort(Section.WeakDefSymbols.begin(),
526                   Section.WeakDefSymbols.end());
527        llvm::sort(Section.TLVSymbols.begin(), Section.TLVSymbols.end());
528        Exports.emplace_back(std::move(Section));
529      }
530
531      ArchSet.clear();
532      SymbolToArchSet.clear();
533
534      for (const auto *Symbol : File->undefineds()) {
535        auto Architectures = Symbol->getArchitectures();
536        SymbolToArchSet[Symbol] = Architectures;
537        ArchSet.insert(Architectures);
538      }
539
540      for (auto Architectures : ArchSet) {
541        UndefinedSection Section;
542        Section.Architectures = Architectures;
543
544        for (const auto &SymArch : SymbolToArchSet) {
545          if (SymArch.second != Architectures)
546            continue;
547
548          const auto *Symbol = SymArch.first;
549          switch (Symbol->getKind()) {
550          case SymbolKind::GlobalSymbol:
551            if (Symbol->isWeakReferenced())
552              Section.WeakRefSymbols.emplace_back(Symbol->getName());
553            else
554              Section.Symbols.emplace_back(Symbol->getName());
555            break;
556          case SymbolKind::ObjectiveCClass:
557            if (File->getFileType() != FileType::TBD_V3)
558              Section.Classes.emplace_back(
559                  copyString("_" + Symbol->getName().str()));
560            else
561              Section.Classes.emplace_back(Symbol->getName());
562            break;
563          case SymbolKind::ObjectiveCClassEHType:
564            if (File->getFileType() != FileType::TBD_V3)
565              Section.Symbols.emplace_back(
566                  copyString("_OBJC_EHTYPE_$_" + Symbol->getName().str()));
567            else
568              Section.ClassEHs.emplace_back(Symbol->getName());
569            break;
570          case SymbolKind::ObjectiveCInstanceVariable:
571            if (File->getFileType() != FileType::TBD_V3)
572              Section.IVars.emplace_back(
573                  copyString("_" + Symbol->getName().str()));
574            else
575              Section.IVars.emplace_back(Symbol->getName());
576            break;
577          }
578        }
579        llvm::sort(Section.Symbols.begin(), Section.Symbols.end());
580        llvm::sort(Section.Classes.begin(), Section.Classes.end());
581        llvm::sort(Section.ClassEHs.begin(), Section.ClassEHs.end());
582        llvm::sort(Section.IVars.begin(), Section.IVars.end());
583        llvm::sort(Section.WeakRefSymbols.begin(),
584                   Section.WeakRefSymbols.end());
585        Undefineds.emplace_back(std::move(Section));
586      }
587    }
588
589    // TBD v1 - TBD v3 files only support one platform and several
590    // architectures. It is possible to have more than one platform for TBD v3
591    // files, but the architectures don't apply to all
592    // platforms, specifically to filter out the i386 slice from
593    // platform macCatalyst.
594    TargetList synthesizeTargets(ArchitectureSet Architectures,
595                                          const PlatformSet &Platforms) {
596      TargetList Targets;
597
598      for (auto Platform : Platforms) {
599        Platform = mapToPlatformKind(Platform, Architectures.hasX86());
600
601        for (const auto &&Architecture : Architectures) {
602          if ((Architecture == AK_i386) &&
603              (Platform == PlatformKind::macCatalyst))
604            continue;
605
606          Targets.emplace_back(Architecture, Platform);
607        }
608      }
609      return Targets;
610    }
611
612    const InterfaceFile *denormalize(IO &IO) {
613      auto Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext());
614      assert(Ctx);
615
616      auto *File = new InterfaceFile;
617      File->setPath(Ctx->Path);
618      File->setFileType(Ctx->FileKind);
619      File->addTargets(synthesizeTargets(Architectures, Platforms));
620      for (auto &ID : UUIDs)
621        File->addUUID(ID.first, ID.second);
622      File->setInstallName(InstallName);
623      File->setCurrentVersion(CurrentVersion);
624      File->setCompatibilityVersion(CompatibilityVersion);
625      File->setSwiftABIVersion(SwiftABIVersion);
626      File->setObjCConstraint(ObjCConstraint);
627      for (const auto &Target : File->targets())
628        File->addParentUmbrella(Target, ParentUmbrella);
629
630      if (Ctx->FileKind == FileType::TBD_V1) {
631        File->setTwoLevelNamespace();
632        File->setApplicationExtensionSafe();
633      } else {
634        File->setTwoLevelNamespace(!(Flags & TBDFlags::FlatNamespace));
635        File->setApplicationExtensionSafe(
636            !(Flags & TBDFlags::NotApplicationExtensionSafe));
637        File->setInstallAPI(Flags & TBDFlags::InstallAPI);
638      }
639
640      for (const auto &Section : Exports) {
641        const auto Targets =
642            synthesizeTargets(Section.Architectures, Platforms);
643
644        for (const auto &Lib : Section.AllowableClients)
645          for (const auto &Target : Targets)
646            File->addAllowableClient(Lib, Target);
647
648        for (const auto &Lib : Section.ReexportedLibraries)
649          for (const auto &Target : Targets)
650            File->addReexportedLibrary(Lib, Target);
651
652        for (const auto &Symbol : Section.Symbols) {
653          if (Ctx->FileKind != FileType::TBD_V3 &&
654              Symbol.value.startswith("_OBJC_EHTYPE_$_"))
655            File->addSymbol(SymbolKind::ObjectiveCClassEHType,
656                            Symbol.value.drop_front(15), Targets);
657          else
658            File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets);
659        }
660        for (auto &Symbol : Section.Classes) {
661          auto Name = Symbol.value;
662          if (Ctx->FileKind != FileType::TBD_V3)
663            Name = Name.drop_front();
664          File->addSymbol(SymbolKind::ObjectiveCClass, Name, Targets);
665        }
666        for (auto &Symbol : Section.ClassEHs)
667          File->addSymbol(SymbolKind::ObjectiveCClassEHType, Symbol, Targets);
668        for (auto &Symbol : Section.IVars) {
669          auto Name = Symbol.value;
670          if (Ctx->FileKind != FileType::TBD_V3)
671            Name = Name.drop_front();
672          File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, Name,
673                          Targets);
674        }
675        for (auto &Symbol : Section.WeakDefSymbols)
676          File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets,
677                          SymbolFlags::WeakDefined);
678        for (auto &Symbol : Section.TLVSymbols)
679          File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets,
680                          SymbolFlags::ThreadLocalValue);
681      }
682
683      for (const auto &Section : Undefineds) {
684        const auto Targets =
685            synthesizeTargets(Section.Architectures, Platforms);
686        for (auto &Symbol : Section.Symbols) {
687          if (Ctx->FileKind != FileType::TBD_V3 &&
688              Symbol.value.startswith("_OBJC_EHTYPE_$_"))
689            File->addSymbol(SymbolKind::ObjectiveCClassEHType,
690                            Symbol.value.drop_front(15), Targets,
691                            SymbolFlags::Undefined);
692          else
693            File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets,
694                            SymbolFlags::Undefined);
695        }
696        for (auto &Symbol : Section.Classes) {
697          auto Name = Symbol.value;
698          if (Ctx->FileKind != FileType::TBD_V3)
699            Name = Name.drop_front();
700          File->addSymbol(SymbolKind::ObjectiveCClass, Name, Targets,
701                          SymbolFlags::Undefined);
702        }
703        for (auto &Symbol : Section.ClassEHs)
704          File->addSymbol(SymbolKind::ObjectiveCClassEHType, Symbol, Targets,
705                          SymbolFlags::Undefined);
706        for (auto &Symbol : Section.IVars) {
707          auto Name = Symbol.value;
708          if (Ctx->FileKind != FileType::TBD_V3)
709            Name = Name.drop_front();
710          File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, Name, Targets,
711                          SymbolFlags::Undefined);
712        }
713        for (auto &Symbol : Section.WeakRefSymbols)
714          File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets,
715                          SymbolFlags::Undefined | SymbolFlags::WeakReferenced);
716      }
717
718      return File;
719    }
720
721    llvm::BumpPtrAllocator Allocator;
722    StringRef copyString(StringRef String) {
723      if (String.empty())
724        return {};
725
726      void *Ptr = Allocator.Allocate(String.size(), 1);
727      memcpy(Ptr, String.data(), String.size());
728      return StringRef(reinterpret_cast<const char *>(Ptr), String.size());
729    }
730
731    std::vector<Architecture> Architectures;
732    std::vector<UUID> UUIDs;
733    PlatformSet Platforms;
734    StringRef InstallName;
735    PackedVersion CurrentVersion;
736    PackedVersion CompatibilityVersion;
737    SwiftVersion SwiftABIVersion{0};
738    ObjCConstraintType ObjCConstraint{ObjCConstraintType::None};
739    TBDFlags Flags{TBDFlags::None};
740    StringRef ParentUmbrella;
741    std::vector<ExportSection> Exports;
742    std::vector<UndefinedSection> Undefineds;
743  };
744
745  static void setFileTypeForInput(TextAPIContext *Ctx, IO &IO) {
746    if (IO.mapTag("!tapi-tbd", false))
747      Ctx->FileKind = FileType::TBD_V4;
748    else if (IO.mapTag("!tapi-tbd-v3", false))
749      Ctx->FileKind = FileType::TBD_V3;
750    else if (IO.mapTag("!tapi-tbd-v2", false))
751      Ctx->FileKind = FileType::TBD_V2;
752    else if (IO.mapTag("!tapi-tbd-v1", false) ||
753             IO.mapTag("tag:yaml.org,2002:map", false))
754      Ctx->FileKind = FileType::TBD_V1;
755    else {
756      Ctx->FileKind = FileType::Invalid;
757      return;
758    }
759  }
760
761  static void mapping(IO &IO, const InterfaceFile *&File) {
762    auto *Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext());
763    assert((!Ctx || !IO.outputting() ||
764            (Ctx && Ctx->FileKind != FileType::Invalid)) &&
765           "File type is not set in YAML context");
766
767    if (!IO.outputting()) {
768      setFileTypeForInput(Ctx, IO);
769      switch (Ctx->FileKind) {
770      default:
771        break;
772      case FileType::TBD_V4:
773        mapKeysToValuesV4(IO, File);
774        return;
775      case FileType::Invalid:
776        IO.setError("unsupported file type");
777        return;
778      }
779    } else {
780      // Set file type when writing.
781      switch (Ctx->FileKind) {
782      default:
783        llvm_unreachable("unexpected file type");
784      case FileType::TBD_V4:
785        mapKeysToValuesV4(IO, File);
786        return;
787      case FileType::TBD_V3:
788        IO.mapTag("!tapi-tbd-v3", true);
789        break;
790      case FileType::TBD_V2:
791        IO.mapTag("!tapi-tbd-v2", true);
792        break;
793      case FileType::TBD_V1:
794        // Don't write the tag into the .tbd file for TBD v1
795        break;
796      }
797    }
798    mapKeysToValues(Ctx->FileKind, IO, File);
799  }
800
801  using SectionList = std::vector<SymbolSection>;
802  struct NormalizedTBD_V4 {
803    explicit NormalizedTBD_V4(IO &IO) {}
804    NormalizedTBD_V4(IO &IO, const InterfaceFile *&File) {
805      auto Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext());
806      assert(Ctx);
807      TBDVersion = Ctx->FileKind >> 1;
808      Targets.insert(Targets.begin(), File->targets().begin(),
809                     File->targets().end());
810      for (const auto &IT : File->uuids())
811        UUIDs.emplace_back(IT.first, IT.second);
812      InstallName = File->getInstallName();
813      CurrentVersion = File->getCurrentVersion();
814      CompatibilityVersion = File->getCompatibilityVersion();
815      SwiftABIVersion = File->getSwiftABIVersion();
816
817      Flags = TBDFlags::None;
818      if (!File->isApplicationExtensionSafe())
819        Flags |= TBDFlags::NotApplicationExtensionSafe;
820
821      if (!File->isTwoLevelNamespace())
822        Flags |= TBDFlags::FlatNamespace;
823
824      if (File->isInstallAPI())
825        Flags |= TBDFlags::InstallAPI;
826
827      {
828        std::map<std::string, TargetList> valueToTargetList;
829        for (const auto &it : File->umbrellas())
830          valueToTargetList[it.second].emplace_back(it.first);
831
832        for (const auto &it : valueToTargetList) {
833          UmbrellaSection CurrentSection;
834          CurrentSection.Targets.insert(CurrentSection.Targets.begin(),
835                                        it.second.begin(), it.second.end());
836          CurrentSection.Umbrella = it.first;
837          ParentUmbrellas.emplace_back(std::move(CurrentSection));
838        }
839      }
840
841      assignTargetsToLibrary(File->allowableClients(), AllowableClients);
842      assignTargetsToLibrary(File->reexportedLibraries(), ReexportedLibraries);
843
844      auto handleSymbols =
845          [](SectionList &CurrentSections,
846             InterfaceFile::const_filtered_symbol_range Symbols,
847             std::function<bool(const Symbol *)> Pred) {
848            std::set<TargetList> TargetSet;
849            std::map<const Symbol *, TargetList> SymbolToTargetList;
850            for (const auto *Symbol : Symbols) {
851              if (!Pred(Symbol))
852                continue;
853              TargetList Targets(Symbol->targets());
854              SymbolToTargetList[Symbol] = Targets;
855              TargetSet.emplace(std::move(Targets));
856            }
857            for (const auto &TargetIDs : TargetSet) {
858              SymbolSection CurrentSection;
859              CurrentSection.Targets.insert(CurrentSection.Targets.begin(),
860                                            TargetIDs.begin(), TargetIDs.end());
861
862              for (const auto &IT : SymbolToTargetList) {
863                if (IT.second != TargetIDs)
864                  continue;
865
866                const auto *Symbol = IT.first;
867                switch (Symbol->getKind()) {
868                case SymbolKind::GlobalSymbol:
869                  if (Symbol->isWeakDefined())
870                    CurrentSection.WeakSymbols.emplace_back(Symbol->getName());
871                  else if (Symbol->isThreadLocalValue())
872                    CurrentSection.TlvSymbols.emplace_back(Symbol->getName());
873                  else
874                    CurrentSection.Symbols.emplace_back(Symbol->getName());
875                  break;
876                case SymbolKind::ObjectiveCClass:
877                  CurrentSection.Classes.emplace_back(Symbol->getName());
878                  break;
879                case SymbolKind::ObjectiveCClassEHType:
880                  CurrentSection.ClassEHs.emplace_back(Symbol->getName());
881                  break;
882                case SymbolKind::ObjectiveCInstanceVariable:
883                  CurrentSection.Ivars.emplace_back(Symbol->getName());
884                  break;
885                }
886              }
887              sort(CurrentSection.Symbols);
888              sort(CurrentSection.Classes);
889              sort(CurrentSection.ClassEHs);
890              sort(CurrentSection.Ivars);
891              sort(CurrentSection.WeakSymbols);
892              sort(CurrentSection.TlvSymbols);
893              CurrentSections.emplace_back(std::move(CurrentSection));
894            }
895          };
896
897      handleSymbols(Exports, File->exports(), [](const Symbol *Symbol) {
898        return !Symbol->isReexported();
899      });
900      handleSymbols(Reexports, File->exports(), [](const Symbol *Symbol) {
901        return Symbol->isReexported();
902      });
903      handleSymbols(Undefineds, File->undefineds(),
904                    [](const Symbol *Symbol) { return true; });
905    }
906
907    const InterfaceFile *denormalize(IO &IO) {
908      auto Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext());
909      assert(Ctx);
910
911      auto *File = new InterfaceFile;
912      File->setPath(Ctx->Path);
913      File->setFileType(Ctx->FileKind);
914      for (auto &id : UUIDs)
915        File->addUUID(id.TargetID, id.Value);
916      File->addTargets(Targets);
917      File->setInstallName(InstallName);
918      File->setCurrentVersion(CurrentVersion);
919      File->setCompatibilityVersion(CompatibilityVersion);
920      File->setSwiftABIVersion(SwiftABIVersion);
921      for (const auto &CurrentSection : ParentUmbrellas)
922        for (const auto &target : CurrentSection.Targets)
923          File->addParentUmbrella(target, CurrentSection.Umbrella);
924      File->setTwoLevelNamespace(!(Flags & TBDFlags::FlatNamespace));
925      File->setApplicationExtensionSafe(
926          !(Flags & TBDFlags::NotApplicationExtensionSafe));
927      File->setInstallAPI(Flags & TBDFlags::InstallAPI);
928
929      for (const auto &CurrentSection : AllowableClients) {
930        for (const auto &lib : CurrentSection.Values)
931          for (const auto &Target : CurrentSection.Targets)
932            File->addAllowableClient(lib, Target);
933      }
934
935      for (const auto &CurrentSection : ReexportedLibraries) {
936        for (const auto &Lib : CurrentSection.Values)
937          for (const auto &Target : CurrentSection.Targets)
938            File->addReexportedLibrary(Lib, Target);
939      }
940
941      auto handleSymbols = [File](const SectionList &CurrentSections,
942                                  SymbolFlags Flag = SymbolFlags::None) {
943        for (const auto &CurrentSection : CurrentSections) {
944          for (auto &sym : CurrentSection.Symbols)
945            File->addSymbol(SymbolKind::GlobalSymbol, sym,
946                            CurrentSection.Targets, Flag);
947
948          for (auto &sym : CurrentSection.Classes)
949            File->addSymbol(SymbolKind::ObjectiveCClass, sym,
950                            CurrentSection.Targets);
951
952          for (auto &sym : CurrentSection.ClassEHs)
953            File->addSymbol(SymbolKind::ObjectiveCClassEHType, sym,
954                            CurrentSection.Targets);
955
956          for (auto &sym : CurrentSection.Ivars)
957            File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, sym,
958                            CurrentSection.Targets);
959
960          for (auto &sym : CurrentSection.WeakSymbols)
961            File->addSymbol(SymbolKind::GlobalSymbol, sym,
962                            CurrentSection.Targets, SymbolFlags::WeakDefined);
963
964          for (auto &sym : CurrentSection.TlvSymbols)
965            File->addSymbol(SymbolKind::GlobalSymbol, sym,
966                            CurrentSection.Targets,
967                            SymbolFlags::ThreadLocalValue);
968        }
969      };
970
971      handleSymbols(Exports);
972      handleSymbols(Reexports, SymbolFlags::Rexported);
973      handleSymbols(Undefineds, SymbolFlags::Undefined);
974
975      return File;
976    }
977
978    unsigned TBDVersion;
979    std::vector<UUIDv4> UUIDs;
980    TargetList Targets;
981    StringRef InstallName;
982    PackedVersion CurrentVersion;
983    PackedVersion CompatibilityVersion;
984    SwiftVersion SwiftABIVersion{0};
985    std::vector<MetadataSection> AllowableClients;
986    std::vector<MetadataSection> ReexportedLibraries;
987    TBDFlags Flags{TBDFlags::None};
988    std::vector<UmbrellaSection> ParentUmbrellas;
989    SectionList Exports;
990    SectionList Reexports;
991    SectionList Undefineds;
992
993  private:
994    void assignTargetsToLibrary(const std::vector<InterfaceFileRef> &Libraries,
995                                std::vector<MetadataSection> &Section) {
996      std::set<TargetList> targetSet;
997      std::map<const InterfaceFileRef *, TargetList> valueToTargetList;
998      for (const auto &library : Libraries) {
999        TargetList targets(library.targets());
1000        valueToTargetList[&library] = targets;
1001        targetSet.emplace(std::move(targets));
1002      }
1003
1004      for (const auto &targets : targetSet) {
1005        MetadataSection CurrentSection;
1006        CurrentSection.Targets.insert(CurrentSection.Targets.begin(),
1007                                      targets.begin(), targets.end());
1008
1009        for (const auto &it : valueToTargetList) {
1010          if (it.second != targets)
1011            continue;
1012
1013          CurrentSection.Values.emplace_back(it.first->getInstallName());
1014        }
1015        llvm::sort(CurrentSection.Values);
1016        Section.emplace_back(std::move(CurrentSection));
1017      }
1018    }
1019  };
1020
1021  static void mapKeysToValues(FileType FileKind, IO &IO,
1022                              const InterfaceFile *&File) {
1023    MappingNormalization<NormalizedTBD, const InterfaceFile *> Keys(IO, File);
1024    IO.mapRequired("archs", Keys->Architectures);
1025    if (FileKind != FileType::TBD_V1)
1026      IO.mapOptional("uuids", Keys->UUIDs);
1027    IO.mapRequired("platform", Keys->Platforms);
1028    if (FileKind != FileType::TBD_V1)
1029      IO.mapOptional("flags", Keys->Flags, TBDFlags::None);
1030    IO.mapRequired("install-name", Keys->InstallName);
1031    IO.mapOptional("current-version", Keys->CurrentVersion,
1032                   PackedVersion(1, 0, 0));
1033    IO.mapOptional("compatibility-version", Keys->CompatibilityVersion,
1034                   PackedVersion(1, 0, 0));
1035    if (FileKind != FileType::TBD_V3)
1036      IO.mapOptional("swift-version", Keys->SwiftABIVersion, SwiftVersion(0));
1037    else
1038      IO.mapOptional("swift-abi-version", Keys->SwiftABIVersion,
1039                     SwiftVersion(0));
1040    IO.mapOptional("objc-constraint", Keys->ObjCConstraint,
1041                   (FileKind == FileType::TBD_V1)
1042                       ? ObjCConstraintType::None
1043                       : ObjCConstraintType::Retain_Release);
1044    if (FileKind != FileType::TBD_V1)
1045      IO.mapOptional("parent-umbrella", Keys->ParentUmbrella, StringRef());
1046    IO.mapOptional("exports", Keys->Exports);
1047    if (FileKind != FileType::TBD_V1)
1048      IO.mapOptional("undefineds", Keys->Undefineds);
1049  }
1050
1051  static void mapKeysToValuesV4(IO &IO, const InterfaceFile *&File) {
1052    MappingNormalization<NormalizedTBD_V4, const InterfaceFile *> Keys(IO,
1053                                                                       File);
1054    IO.mapTag("!tapi-tbd", true);
1055    IO.mapRequired("tbd-version", Keys->TBDVersion);
1056    IO.mapRequired("targets", Keys->Targets);
1057    IO.mapOptional("uuids", Keys->UUIDs);
1058    IO.mapOptional("flags", Keys->Flags, TBDFlags::None);
1059    IO.mapRequired("install-name", Keys->InstallName);
1060    IO.mapOptional("current-version", Keys->CurrentVersion,
1061                   PackedVersion(1, 0, 0));
1062    IO.mapOptional("compatibility-version", Keys->CompatibilityVersion,
1063                   PackedVersion(1, 0, 0));
1064    IO.mapOptional("swift-abi-version", Keys->SwiftABIVersion, SwiftVersion(0));
1065    IO.mapOptional("parent-umbrella", Keys->ParentUmbrellas);
1066    auto OptionKind = MetadataSection::Option::Clients;
1067    IO.mapOptionalWithContext("allowable-clients", Keys->AllowableClients,
1068                              OptionKind);
1069    OptionKind = MetadataSection::Option::Libraries;
1070    IO.mapOptionalWithContext("reexported-libraries", Keys->ReexportedLibraries,
1071                              OptionKind);
1072    IO.mapOptional("exports", Keys->Exports);
1073    IO.mapOptional("reexports", Keys->Reexports);
1074    IO.mapOptional("undefineds", Keys->Undefineds);
1075  }
1076};
1077
1078template <>
1079struct DocumentListTraits<std::vector<const MachO::InterfaceFile *>> {
1080  static size_t size(IO &IO, std::vector<const MachO::InterfaceFile *> &Seq) {
1081    return Seq.size();
1082  }
1083  static const InterfaceFile *&
1084  element(IO &IO, std::vector<const InterfaceFile *> &Seq, size_t Index) {
1085    if (Index >= Seq.size())
1086      Seq.resize(Index + 1);
1087    return Seq[Index];
1088  }
1089};
1090
1091} // end namespace yaml.
1092} // namespace llvm
1093
1094static void DiagHandler(const SMDiagnostic &Diag, void *Context) {
1095  auto *File = static_cast<TextAPIContext *>(Context);
1096  SmallString<1024> Message;
1097  raw_svector_ostream S(Message);
1098
1099  SMDiagnostic NewDiag(*Diag.getSourceMgr(), Diag.getLoc(), File->Path,
1100                       Diag.getLineNo(), Diag.getColumnNo(), Diag.getKind(),
1101                       Diag.getMessage(), Diag.getLineContents(),
1102                       Diag.getRanges(), Diag.getFixIts());
1103
1104  NewDiag.print(nullptr, S);
1105  File->ErrorMessage = ("malformed file\n" + Message).str();
1106}
1107
1108Expected<std::unique_ptr<InterfaceFile>>
1109TextAPIReader::get(MemoryBufferRef InputBuffer) {
1110  TextAPIContext Ctx;
1111  Ctx.Path = std::string(InputBuffer.getBufferIdentifier());
1112  yaml::Input YAMLIn(InputBuffer.getBuffer(), &Ctx, DiagHandler, &Ctx);
1113
1114  // Fill vector with interface file objects created by parsing the YAML file.
1115  std::vector<const InterfaceFile *> Files;
1116  YAMLIn >> Files;
1117
1118  // YAMLIn dynamically allocates for Interface file and in case of error,
1119  // memory leak will occur unless wrapped around unique_ptr
1120  auto File = std::unique_ptr<InterfaceFile>(
1121      const_cast<InterfaceFile *>(Files.front()));
1122
1123  for (auto Iter = std::next(Files.begin()); Iter != Files.end(); ++Iter)
1124    File->addDocument(
1125        std::shared_ptr<InterfaceFile>(const_cast<InterfaceFile *>(*Iter)));
1126
1127  if (YAMLIn.error())
1128    return make_error<StringError>(Ctx.ErrorMessage, YAMLIn.error());
1129
1130  return std::move(File);
1131}
1132
1133Error TextAPIWriter::writeToStream(raw_ostream &OS, const InterfaceFile &File) {
1134  TextAPIContext Ctx;
1135  Ctx.Path = std::string(File.getPath());
1136  Ctx.FileKind = File.getFileType();
1137  llvm::yaml::Output YAMLOut(OS, &Ctx, /*WrapColumn=*/80);
1138
1139  std::vector<const InterfaceFile *> Files;
1140  Files.emplace_back(&File);
1141
1142  for (auto Document : File.documents())
1143    Files.emplace_back(Document.get());
1144
1145  // Stream out yaml.
1146  YAMLOut << Files;
1147
1148  return Error::success();
1149}
1150