aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/TextAPI
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/TextAPI')
-rw-r--r--llvm/lib/TextAPI/Architecture.cpp4
-rw-r--r--llvm/lib/TextAPI/InterfaceFile.cpp74
-rw-r--r--llvm/lib/TextAPI/PackedVersion.cpp7
-rw-r--r--llvm/lib/TextAPI/Platform.cpp10
-rw-r--r--llvm/lib/TextAPI/Symbol.cpp18
-rw-r--r--llvm/lib/TextAPI/SymbolSet.cpp36
-rw-r--r--llvm/lib/TextAPI/Target.cpp17
-rw-r--r--llvm/lib/TextAPI/TextStub.cpp113
-rw-r--r--llvm/lib/TextAPI/TextStubCommon.cpp18
-rw-r--r--llvm/lib/TextAPI/TextStubCommon.h25
-rw-r--r--llvm/lib/TextAPI/TextStubV5.cpp1021
11 files changed, 1221 insertions, 122 deletions
diff --git a/llvm/lib/TextAPI/Architecture.cpp b/llvm/lib/TextAPI/Architecture.cpp
index bb349b21774e..51ca91db1300 100644
--- a/llvm/lib/TextAPI/Architecture.cpp
+++ b/llvm/lib/TextAPI/Architecture.cpp
@@ -12,10 +12,10 @@
#include "llvm/TextAPI/Architecture.h"
#include "llvm/ADT/StringSwitch.h"
-#include "llvm/ADT/Triple.h"
#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/Triple.h"
namespace llvm {
namespace MachO {
diff --git a/llvm/lib/TextAPI/InterfaceFile.cpp b/llvm/lib/TextAPI/InterfaceFile.cpp
index 1156a39228e7..b7f967aa754e 100644
--- a/llvm/lib/TextAPI/InterfaceFile.cpp
+++ b/llvm/lib/TextAPI/InterfaceFile.cpp
@@ -17,31 +17,6 @@
using namespace llvm;
using namespace llvm::MachO;
-namespace {
-template <typename C>
-typename C::iterator addEntry(C &Container, StringRef InstallName) {
- auto I = partition_point(Container, [=](const InterfaceFileRef &O) {
- return O.getInstallName() < InstallName;
- });
- if (I != Container.end() && I->getInstallName() == InstallName)
- return I;
-
- return Container.emplace(I, InstallName);
-}
-
-template <typename C>
-typename C::iterator addEntry(C &Container, const Target &Target_) {
- auto Iter =
- lower_bound(Container, Target_, [](const Target &LHS, const Target &RHS) {
- return LHS < RHS;
- });
- if ((Iter != std::end(Container)) && !(Target_ < *Iter))
- return Iter;
-
- return Container.insert(Iter, Target_);
-}
-} // end namespace
-
void InterfaceFileRef::addTarget(const Target &Target) {
addEntry(Targets, Target);
}
@@ -71,28 +46,17 @@ void InterfaceFile::addParentUmbrella(const Target &Target_, StringRef Parent) {
ParentUmbrellas.emplace(Iter, Target_, std::string(Parent));
}
-void InterfaceFile::addUUID(const Target &Target_, StringRef UUID) {
- auto Iter = lower_bound(UUIDs, Target_,
+void InterfaceFile::addRPath(const Target &InputTarget, StringRef RPath) {
+ auto Iter = lower_bound(RPaths, InputTarget,
[](const std::pair<Target, std::string> &LHS,
Target RHS) { return LHS.first < RHS; });
- if ((Iter != UUIDs.end()) && !(Target_ < Iter->first)) {
- Iter->second = std::string(UUID);
+ if ((Iter != RPaths.end()) && !(InputTarget < Iter->first)) {
+ Iter->second = std::string(RPath);
return;
}
- UUIDs.emplace(Iter, Target_, std::string(UUID));
-}
-
-void InterfaceFile::addUUID(const Target &Target, uint8_t UUID[16]) {
- std::stringstream Stream;
- for (unsigned i = 0; i < 16; ++i) {
- if (i == 4 || i == 6 || i == 8 || i == 10)
- Stream << '-';
- Stream << std::setfill('0') << std::setw(2) << std::uppercase << std::hex
- << static_cast<int>(UUID[i]);
- }
- addUUID(Target, Stream.str());
+ RPaths.emplace(Iter, InputTarget, std::string(RPath));
}
void InterfaceFile::addTarget(const Target &Target) {
@@ -107,17 +71,6 @@ InterfaceFile::targets(ArchitectureSet Archs) const {
return make_filter_range(Targets, fn);
}
-void InterfaceFile::addSymbol(SymbolKind Kind, StringRef Name,
- const TargetList &Targets, SymbolFlags Flags) {
- Name = copyString(Name);
- auto result = Symbols.try_emplace(SymbolsMapKey{Kind, Name}, nullptr);
- if (result.second)
- result.first->second = new (Allocator) Symbol{Kind, Name, Targets, Flags};
- else
- for (const auto &Target : Targets)
- result.first->second->addTarget(Target);
-}
-
void InterfaceFile::addDocument(std::shared_ptr<InterfaceFile> &&Document) {
auto Pos = llvm::lower_bound(Documents, Document,
[](const std::shared_ptr<InterfaceFile> &LHS,
@@ -128,6 +81,10 @@ void InterfaceFile::addDocument(std::shared_ptr<InterfaceFile> &&Document) {
Documents.insert(Pos, Document);
}
+static bool isYAMLTextStub(const FileType &Kind) {
+ return (Kind >= FileType::TBD_V1) && (Kind < FileType::TBD_V5);
+}
+
bool InterfaceFile::operator==(const InterfaceFile &O) const {
if (Targets != O.Targets)
return false;
@@ -142,16 +99,23 @@ bool InterfaceFile::operator==(const InterfaceFile &O) const {
return false;
if (IsAppExtensionSafe != O.IsAppExtensionSafe)
return false;
- if (IsInstallAPI != O.IsInstallAPI)
- return false;
if (ParentUmbrellas != O.ParentUmbrellas)
return false;
if (AllowableClients != O.AllowableClients)
return false;
if (ReexportedLibraries != O.ReexportedLibraries)
return false;
- if (Symbols != O.Symbols)
+ if (*SymbolsSet != *O.SymbolsSet)
return false;
+ // Don't compare run search paths for older filetypes that cannot express
+ // them.
+ if (!(isYAMLTextStub(FileKind)) && !(isYAMLTextStub(O.FileKind))) {
+ if (RPaths != O.RPaths)
+ return false;
+ if (mapToPlatformVersionSet(Targets) != mapToPlatformVersionSet(O.Targets))
+ return false;
+ }
+
if (!std::equal(Documents.begin(), Documents.end(), O.Documents.begin(),
O.Documents.end(),
[](const std::shared_ptr<InterfaceFile> LHS,
diff --git a/llvm/lib/TextAPI/PackedVersion.cpp b/llvm/lib/TextAPI/PackedVersion.cpp
index 67fb30aeb127..22960c33e9ee 100644
--- a/llvm/lib/TextAPI/PackedVersion.cpp
+++ b/llvm/lib/TextAPI/PackedVersion.cpp
@@ -100,6 +100,13 @@ std::pair<bool, bool> PackedVersion::parse64(StringRef Str) {
return std::make_pair(true, Truncated);
}
+PackedVersion::operator std::string() const {
+ SmallString<32> Str;
+ raw_svector_ostream OS(Str);
+ print(OS);
+ return std::string(Str);
+}
+
void PackedVersion::print(raw_ostream &OS) const {
OS << format("%d", getMajor());
if (getMinor() || getSubminor())
diff --git a/llvm/lib/TextAPI/Platform.cpp b/llvm/lib/TextAPI/Platform.cpp
index c3c74252301e..d0575847a876 100644
--- a/llvm/lib/TextAPI/Platform.cpp
+++ b/llvm/lib/TextAPI/Platform.cpp
@@ -13,7 +13,7 @@
#include "llvm/TextAPI/Platform.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringSwitch.h"
-#include "llvm/ADT/Triple.h"
+#include "llvm/TargetParser/Triple.h"
namespace llvm {
namespace MachO {
@@ -90,6 +90,7 @@ StringRef getPlatformName(PlatformType Platform) {
PlatformType getPlatformFromName(StringRef Name) {
return StringSwitch<PlatformType>(Name)
+ .Case("osx", PLATFORM_MACOS)
.Case("macos", PLATFORM_MACOS)
.Case("ios", PLATFORM_IOS)
.Case("tvos", PLATFORM_TVOS)
@@ -132,5 +133,12 @@ std::string getOSAndEnvironmentName(PlatformType Platform,
llvm_unreachable("Unknown llvm::MachO::PlatformType enum");
}
+VersionTuple mapToSupportedOSVersion(const Triple &Triple) {
+ const VersionTuple MinSupportedOS = Triple.getMinimumSupportedOSVersion();
+ if (MinSupportedOS > Triple.getOSVersion())
+ return MinSupportedOS;
+ return Triple.getOSVersion();
+}
+
} // end namespace MachO.
} // end namespace llvm.
diff --git a/llvm/lib/TextAPI/Symbol.cpp b/llvm/lib/TextAPI/Symbol.cpp
index 041f553c66f3..20fa6362716a 100644
--- a/llvm/lib/TextAPI/Symbol.cpp
+++ b/llvm/lib/TextAPI/Symbol.cpp
@@ -54,5 +54,23 @@ Symbol::targets(ArchitectureSet Architectures) const {
return make_filter_range(Targets, FN);
}
+bool Symbol::operator==(const Symbol &O) const {
+ // Older Tapi files do not express all these symbol flags. In those
+ // cases, ignore those differences.
+ auto RemoveFlag = [](const Symbol &Sym, SymbolFlags &Flag) {
+ if (Sym.isData())
+ Flag &= ~SymbolFlags::Data;
+ if (Sym.isText())
+ Flag &= ~SymbolFlags::Text;
+ };
+ SymbolFlags LHSFlags = Flags;
+ SymbolFlags RHSFlags = O.Flags;
+ // Ignore Text and Data for now.
+ RemoveFlag(*this, LHSFlags);
+ RemoveFlag(O, RHSFlags);
+ return std::tie(Name, Kind, Targets, LHSFlags) ==
+ std::tie(O.Name, O.Kind, O.Targets, RHSFlags);
+}
+
} // end namespace MachO.
} // end namespace llvm.
diff --git a/llvm/lib/TextAPI/SymbolSet.cpp b/llvm/lib/TextAPI/SymbolSet.cpp
new file mode 100644
index 000000000000..157e13749729
--- /dev/null
+++ b/llvm/lib/TextAPI/SymbolSet.cpp
@@ -0,0 +1,36 @@
+//===- lib/TextAPI/SymbolSet.cpp - TAPI Symbol Set ------------*- C++-*----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/TextAPI/SymbolSet.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+
+Symbol *SymbolSet::addGlobalImpl(SymbolKind Kind, StringRef Name,
+ SymbolFlags Flags) {
+ Name = copyString(Name);
+ auto Result = Symbols.try_emplace(SymbolsMapKey{Kind, Name}, nullptr);
+ if (Result.second)
+ Result.first->second =
+ new (Allocator) Symbol{Kind, Name, TargetList(), Flags};
+ return Result.first->second;
+}
+
+Symbol *SymbolSet::addGlobal(SymbolKind Kind, StringRef Name, SymbolFlags Flags,
+ const Target &Targ) {
+ auto *Sym = addGlobalImpl(Kind, Name, Flags);
+ Sym->addTarget(Targ);
+ return Sym;
+}
+
+const Symbol *SymbolSet::findSymbol(SymbolKind Kind, StringRef Name) const {
+ auto It = Symbols.find({Kind, Name});
+ if (It != Symbols.end())
+ return It->second;
+ return nullptr;
+}
diff --git a/llvm/lib/TextAPI/Target.cpp b/llvm/lib/TextAPI/Target.cpp
index c54c3bd66b9d..e20842498331 100644
--- a/llvm/lib/TextAPI/Target.cpp
+++ b/llvm/lib/TextAPI/Target.cpp
@@ -46,7 +46,10 @@ Expected<Target> Target::create(StringRef TargetValue) {
}
Target::operator std::string() const {
- return (getArchitectureName(Arch) + " (" + getPlatformName(Platform) + ")")
+ auto Version = MinDeployment.empty() ? "" : MinDeployment.getAsString();
+
+ return (getArchitectureName(Arch) + " (" + getPlatformName(Platform) +
+ Version + ")")
.str();
}
@@ -55,6 +58,13 @@ raw_ostream &operator<<(raw_ostream &OS, const Target &Target) {
return OS;
}
+PlatformVersionSet mapToPlatformVersionSet(ArrayRef<Target> Targets) {
+ PlatformVersionSet Result;
+ for (const auto &Target : Targets)
+ Result.insert({Target.Platform, Target.MinDeployment});
+ return Result;
+}
+
PlatformSet mapToPlatformSet(ArrayRef<Target> Targets) {
PlatformSet Result;
for (const auto &Target : Targets)
@@ -70,8 +80,11 @@ ArchitectureSet mapToArchitectureSet(ArrayRef<Target> Targets) {
}
std::string getTargetTripleName(const Target &Targ) {
+ auto Version =
+ Targ.MinDeployment.empty() ? "" : Targ.MinDeployment.getAsString();
+
return (getArchitectureName(Targ.Arch) + "-apple-" +
- getOSAndEnvironmentName(Targ.Platform))
+ getOSAndEnvironmentName(Targ.Platform, Version))
.str();
}
diff --git a/llvm/lib/TextAPI/TextStub.cpp b/llvm/lib/TextAPI/TextStub.cpp
index ff93e43356f7..78de3ebf3f3a 100644
--- a/llvm/lib/TextAPI/TextStub.cpp
+++ b/llvm/lib/TextAPI/TextStub.cpp
@@ -258,16 +258,6 @@ struct UUIDv4 {
UUIDv4(const Target &TargetID, const std::string &Value)
: TargetID(TargetID), Value(Value) {}
};
-
-// clang-format off
-enum TBDFlags : unsigned {
- None = 0U,
- FlatNamespace = 1U << 0,
- NotApplicationExtensionSafe = 1U << 1,
- InstallAPI = 1U << 2,
- LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/InstallAPI),
-};
-// clang-format on
} // end anonymous namespace.
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(Architecture)
@@ -437,7 +427,6 @@ template <> struct MappingTraits<const InterfaceFile *> {
explicit NormalizedTBD(IO &IO) {}
NormalizedTBD(IO &IO, const InterfaceFile *&File) {
Architectures = File->getArchitectures();
- UUIDs = File->uuids();
Platforms = File->getPlatforms();
InstallName = File->getInstallName();
CurrentVersion = PackedVersion(File->getCurrentVersion());
@@ -452,9 +441,6 @@ template <> struct MappingTraits<const InterfaceFile *> {
if (!File->isTwoLevelNamespace())
Flags |= TBDFlags::FlatNamespace;
- if (File->isInstallAPI())
- Flags |= TBDFlags::InstallAPI;
-
if (!File->umbrellas().empty())
ParentUmbrella = File->umbrellas().begin()->second;
@@ -466,7 +452,7 @@ template <> struct MappingTraits<const InterfaceFile *> {
ArchSet.insert(Library.getArchitectures());
std::map<const Symbol *, ArchitectureSet> SymbolToArchSet;
- for (const auto *Symbol : File->exports()) {
+ for (const auto *Symbol : File->symbols()) {
auto Architectures = Symbol->getArchitectures();
SymbolToArchSet[Symbol] = Architectures;
ArchSet.insert(Architectures);
@@ -617,8 +603,6 @@ template <> struct MappingTraits<const InterfaceFile *> {
File->setPath(Ctx->Path);
File->setFileType(Ctx->FileKind);
File->addTargets(synthesizeTargets(Architectures, Platforms));
- for (auto &ID : UUIDs)
- File->addUUID(ID.first, ID.second);
File->setInstallName(InstallName);
File->setCurrentVersion(CurrentVersion);
File->setCompatibilityVersion(CompatibilityVersion);
@@ -634,7 +618,6 @@ template <> struct MappingTraits<const InterfaceFile *> {
File->setTwoLevelNamespace(!(Flags & TBDFlags::FlatNamespace));
File->setApplicationExtensionSafe(
!(Flags & TBDFlags::NotApplicationExtensionSafe));
- File->setInstallAPI(Flags & TBDFlags::InstallAPI);
}
for (const auto &Section : Exports) {
@@ -807,8 +790,6 @@ template <> struct MappingTraits<const InterfaceFile *> {
TBDVersion = Ctx->FileKind >> 1;
Targets.insert(Targets.begin(), File->targets().begin(),
File->targets().end());
- for (const auto &IT : File->uuids())
- UUIDs.emplace_back(IT.first, IT.second);
InstallName = File->getInstallName();
CurrentVersion = File->getCurrentVersion();
CompatibilityVersion = File->getCompatibilityVersion();
@@ -821,9 +802,6 @@ template <> struct MappingTraits<const InterfaceFile *> {
if (!File->isTwoLevelNamespace())
Flags |= TBDFlags::FlatNamespace;
- if (File->isInstallAPI())
- Flags |= TBDFlags::InstallAPI;
-
{
std::map<std::string, TargetList> valueToTargetList;
for (const auto &it : File->umbrellas())
@@ -843,13 +821,10 @@ template <> struct MappingTraits<const InterfaceFile *> {
auto handleSymbols =
[](SectionList &CurrentSections,
- InterfaceFile::const_filtered_symbol_range Symbols,
- std::function<bool(const Symbol *)> Pred) {
+ InterfaceFile::const_filtered_symbol_range Symbols) {
std::set<TargetList> TargetSet;
std::map<const Symbol *, TargetList> SymbolToTargetList;
for (const auto *Symbol : Symbols) {
- if (!Pred(Symbol))
- continue;
TargetList Targets(Symbol->targets());
SymbolToTargetList[Symbol] = Targets;
TargetSet.emplace(std::move(Targets));
@@ -894,14 +869,9 @@ template <> struct MappingTraits<const InterfaceFile *> {
}
};
- handleSymbols(Exports, File->exports(), [](const Symbol *Symbol) {
- return !Symbol->isReexported();
- });
- handleSymbols(Reexports, File->exports(), [](const Symbol *Symbol) {
- return Symbol->isReexported();
- });
- handleSymbols(Undefineds, File->undefineds(),
- [](const Symbol *Symbol) { return true; });
+ handleSymbols(Exports, File->exports());
+ handleSymbols(Reexports, File->reexports());
+ handleSymbols(Undefineds, File->undefineds());
}
const InterfaceFile *denormalize(IO &IO) {
@@ -911,8 +881,6 @@ template <> struct MappingTraits<const InterfaceFile *> {
auto *File = new InterfaceFile;
File->setPath(Ctx->Path);
File->setFileType(Ctx->FileKind);
- for (auto &id : UUIDs)
- File->addUUID(id.TargetID, id.Value);
File->addTargets(Targets);
File->setInstallName(InstallName);
File->setCurrentVersion(CurrentVersion);
@@ -924,7 +892,6 @@ template <> struct MappingTraits<const InterfaceFile *> {
File->setTwoLevelNamespace(!(Flags & TBDFlags::FlatNamespace));
File->setApplicationExtensionSafe(
!(Flags & TBDFlags::NotApplicationExtensionSafe));
- File->setInstallAPI(Flags & TBDFlags::InstallAPI);
for (const auto &CurrentSection : AllowableClients) {
for (const auto &lib : CurrentSection.Values)
@@ -947,24 +914,28 @@ template <> struct MappingTraits<const InterfaceFile *> {
for (auto &sym : CurrentSection.Classes)
File->addSymbol(SymbolKind::ObjectiveCClass, sym,
- CurrentSection.Targets);
+ CurrentSection.Targets, Flag);
for (auto &sym : CurrentSection.ClassEHs)
File->addSymbol(SymbolKind::ObjectiveCClassEHType, sym,
- CurrentSection.Targets);
+ CurrentSection.Targets, Flag);
for (auto &sym : CurrentSection.Ivars)
File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, sym,
- CurrentSection.Targets);
+ CurrentSection.Targets, Flag);
- for (auto &sym : CurrentSection.WeakSymbols)
+ SymbolFlags SymFlag = (Flag == SymbolFlags::Undefined)
+ ? SymbolFlags::WeakReferenced
+ : SymbolFlags::WeakDefined;
+ for (auto &sym : CurrentSection.WeakSymbols) {
File->addSymbol(SymbolKind::GlobalSymbol, sym,
- CurrentSection.Targets, SymbolFlags::WeakDefined);
+ CurrentSection.Targets, Flag | SymFlag);
+ }
for (auto &sym : CurrentSection.TlvSymbols)
File->addSymbol(SymbolKind::GlobalSymbol, sym,
CurrentSection.Targets,
- SymbolFlags::ThreadLocalValue);
+ Flag | SymbolFlags::ThreadLocalValue);
}
};
@@ -1021,9 +992,10 @@ template <> struct MappingTraits<const InterfaceFile *> {
static void mapKeysToValues(FileType FileKind, IO &IO,
const InterfaceFile *&File) {
MappingNormalization<NormalizedTBD, const InterfaceFile *> Keys(IO, File);
+ std::vector<UUID> EmptyUUID;
IO.mapRequired("archs", Keys->Architectures);
if (FileKind != FileType::TBD_V1)
- IO.mapOptional("uuids", Keys->UUIDs);
+ IO.mapOptional("uuids", EmptyUUID);
IO.mapRequired("platform", Keys->Platforms);
if (FileKind != FileType::TBD_V1)
IO.mapOptional("flags", Keys->Flags, TBDFlags::None);
@@ -1051,10 +1023,11 @@ template <> struct MappingTraits<const InterfaceFile *> {
static void mapKeysToValuesV4(IO &IO, const InterfaceFile *&File) {
MappingNormalization<NormalizedTBD_V4, const InterfaceFile *> Keys(IO,
File);
+ std::vector<UUIDv4> EmptyUUID;
IO.mapTag("!tapi-tbd", true);
IO.mapRequired("tbd-version", Keys->TBDVersion);
IO.mapRequired("targets", Keys->Targets);
- IO.mapOptional("uuids", Keys->UUIDs);
+ IO.mapOptional("uuids", EmptyUUID);
IO.mapOptional("flags", Keys->Flags, TBDFlags::None);
IO.mapRequired("install-name", Keys->InstallName);
IO.mapOptional("current-version", Keys->CurrentVersion,
@@ -1105,10 +1078,49 @@ static void DiagHandler(const SMDiagnostic &Diag, void *Context) {
File->ErrorMessage = ("malformed file\n" + Message).str();
}
+namespace {
+
+Expected<FileType> canReadFileType(MemoryBufferRef InputBuffer) {
+ auto TAPIFile = InputBuffer.getBuffer().trim();
+ if (TAPIFile.startswith("{") && TAPIFile.endswith("}"))
+ return FileType::TBD_V5;
+
+ if (!TAPIFile.endswith("..."))
+ return createStringError(std::errc::not_supported, "unsupported file type");
+
+ if (TAPIFile.startswith("--- !tapi-tbd\n"))
+ return FileType::TBD_V4;
+
+ if (TAPIFile.startswith("--- !tapi-tbd-v3\n"))
+ return FileType::TBD_V3;
+
+ if (TAPIFile.startswith("--- !tapi-tbd-v2\n"))
+ return FileType::TBD_V2;
+
+ if (TAPIFile.startswith("--- !tapi-tbd-v1\n") ||
+ TAPIFile.startswith("---\narchs:"))
+ return FileType::TBD_V1;
+
+ return createStringError(std::errc::not_supported, "unsupported file type");
+}
+} // namespace
+
Expected<std::unique_ptr<InterfaceFile>>
TextAPIReader::get(MemoryBufferRef InputBuffer) {
TextAPIContext Ctx;
Ctx.Path = std::string(InputBuffer.getBufferIdentifier());
+ if (auto FTOrErr = canReadFileType(InputBuffer))
+ Ctx.FileKind = *FTOrErr;
+ else
+ return FTOrErr.takeError();
+
+ // Handle JSON Format.
+ if (Ctx.FileKind >= FileType::TBD_V5) {
+ auto FileOrErr = getInterfaceFileFromJSON(InputBuffer.getBuffer());
+ if (!FileOrErr)
+ return FileOrErr.takeError();
+ return std::move(*FileOrErr);
+ }
yaml::Input YAMLIn(InputBuffer.getBuffer(), &Ctx, DiagHandler, &Ctx);
// Fill vector with interface file objects created by parsing the YAML file.
@@ -1130,10 +1142,17 @@ TextAPIReader::get(MemoryBufferRef InputBuffer) {
return std::move(File);
}
-Error TextAPIWriter::writeToStream(raw_ostream &OS, const InterfaceFile &File) {
+Error TextAPIWriter::writeToStream(raw_ostream &OS, const InterfaceFile &File,
+ bool Compact) {
TextAPIContext Ctx;
Ctx.Path = std::string(File.getPath());
Ctx.FileKind = File.getFileType();
+
+ // Write out in JSON format.
+ if (Ctx.FileKind >= FileType::TBD_V5) {
+ return serializeInterfaceFileToJSON(OS, File, Compact);
+ }
+
llvm::yaml::Output YAMLOut(OS, &Ctx, /*WrapColumn=*/80);
std::vector<const InterfaceFile *> Files;
diff --git a/llvm/lib/TextAPI/TextStubCommon.cpp b/llvm/lib/TextAPI/TextStubCommon.cpp
index 01a90078e150..0b710b0790b3 100644
--- a/llvm/lib/TextAPI/TextStubCommon.cpp
+++ b/llvm/lib/TextAPI/TextStubCommon.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Implememts common Text Stub YAML mappings.
+// Implements common Text Stub YAML mappings.
//
//===----------------------------------------------------------------------===//
@@ -82,7 +82,7 @@ void ScalarTraits<PlatformSet>::output(const PlatformSet &Values, void *IO,
OS << "bridgeos";
break;
case PLATFORM_MACCATALYST:
- OS << "iosmac";
+ OS << "maccatalyst";
break;
case PLATFORM_DRIVERKIT:
OS << "driverkit";
@@ -112,6 +112,7 @@ StringRef ScalarTraits<PlatformSet>::input(StringRef Scalar, void *IO,
.Case("tvos", PLATFORM_TVOS)
.Case("bridgeos", PLATFORM_BRIDGEOS)
.Case("iosmac", PLATFORM_MACCATALYST)
+ .Case("maccatalyst", PLATFORM_MACCATALYST)
.Case("driverkit", PLATFORM_DRIVERKIT)
.Default(PLATFORM_UNKNOWN);
@@ -216,17 +217,10 @@ QuotingType ScalarTraits<SwiftVersion>::mustQuote(StringRef) {
return QuotingType::None;
}
-void ScalarTraits<UUID>::output(const UUID &Value, void *, raw_ostream &OS) {
- OS << Value.first << ": " << Value.second;
-}
+void ScalarTraits<UUID>::output(const UUID &Value, void *, raw_ostream &OS) {}
+
StringRef ScalarTraits<UUID>::input(StringRef Scalar, void *, UUID &Value) {
- auto Split = Scalar.split(':');
- auto Arch = Split.first.trim();
- auto UUID = Split.second.trim();
- if (UUID.empty())
- return "invalid uuid string pair";
- Value.second = std::string(UUID);
- Value.first = Target{getArchitectureFromName(Arch), PLATFORM_UNKNOWN};
+ Value = {};
return {};
}
diff --git a/llvm/lib/TextAPI/TextStubCommon.h b/llvm/lib/TextAPI/TextStubCommon.h
index aac27221b5ff..d4dcd3af447a 100644
--- a/llvm/lib/TextAPI/TextStubCommon.h
+++ b/llvm/lib/TextAPI/TextStubCommon.h
@@ -22,6 +22,16 @@
using UUID = std::pair<llvm::MachO::Target, std::string>;
+// clang-format off
+enum TBDFlags : unsigned {
+ None = 0U,
+ FlatNamespace = 1U << 0,
+ NotApplicationExtensionSafe = 1U << 1,
+ InstallAPI = 1U << 2,
+ LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/InstallAPI),
+};
+// clang-format on
+
LLVM_YAML_STRONG_TYPEDEF(llvm::StringRef, FlowStringRef)
LLVM_YAML_STRONG_TYPEDEF(uint8_t, SwiftVersion)
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(UUID)
@@ -30,9 +40,16 @@ LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FlowStringRef)
namespace llvm {
namespace MachO {
- class ArchitectureSet;
- class PackedVersion;
-}
+class ArchitectureSet;
+class PackedVersion;
+
+Expected<std::unique_ptr<InterfaceFile>>
+getInterfaceFileFromJSON(StringRef JSON);
+
+Error serializeInterfaceFileToJSON(raw_ostream &OS, const InterfaceFile &File,
+ bool Compact);
+} // namespace MachO
+
namespace yaml {
template <> struct ScalarTraits<FlowStringRef> {
@@ -73,6 +90,8 @@ template <> struct ScalarTraits<SwiftVersion> {
static QuotingType mustQuote(StringRef);
};
+// UUIDs are no longer respected but kept in the YAML parser
+// to keep reading in older TBDs.
template <> struct ScalarTraits<UUID> {
static void output(const UUID &, void *, raw_ostream &);
static StringRef input(StringRef, void *, UUID &);
diff --git a/llvm/lib/TextAPI/TextStubV5.cpp b/llvm/lib/TextAPI/TextStubV5.cpp
new file mode 100644
index 000000000000..5b3d69b8d94a
--- /dev/null
+++ b/llvm/lib/TextAPI/TextStubV5.cpp
@@ -0,0 +1,1021 @@
+//===- TextStubV5.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements Text Stub JSON mappings.
+//
+//===----------------------------------------------------------------------===//
+#include "TextStubCommon.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/JSON.h"
+#include <utility>
+
+// clang-format off
+/*
+
+JSON Format specification.
+
+All library level keys, accept target values and are defaulted if not specified.
+
+{
+"tapi_tbd_version": 5, # Required: TBD version for all documents in file
+"main_library": { # Required: top level library
+ "target_info": [ # Required: target information
+ {
+ "target": "x86_64-macos",
+ "min_deployment": "10.14" # Required: minimum OS deployment version
+ },
+ {
+ "target": "arm64-macos",
+ "min_deployment": "10.14"
+ },
+ {
+ "target": "arm64-maccatalyst",
+ "min_deployment": "12.1"
+ }],
+ "flags":[{"attributes": ["flat_namespace"]}], # Optional:
+ "install_names":[{"name":"/S/L/F/Foo.fwk/Foo"}], # Required: library install name
+ "current_versions":[{"version": "1.2"}], # Optional: defaults to 1
+ "compatibility_versions":[{ "version": "1.1"}], # Optional: defaults to 1
+ "rpaths": [ # Optional:
+ {
+ "targets": ["x86_64-macos"], # Optional: defaults to targets in `target-info`
+ "paths": ["@executable_path/.../Frameworks"]
+ }],
+ "parent_umbrellas": [{"umbrella": "System"}],
+ "allowable_clients": [{"clients": ["ClientA"]}],
+ "reexported_libraries": [{"names": ["/u/l/l/foo.dylib"]}],
+ "exported_symbols": [{ # List of export symbols section
+ "targets": ["x86_64-macos", "arm64-macos"], # Optional: defaults to targets in `target-info`
+ "text": { # List of Text segment symbols
+ "global": [ "_func" ],
+ "weak": [],
+ "thread_local": []
+ },
+ "data": { ... }, # List of Data segment symbols
+ }],
+ "reexported_symbols": [{ ... }], # List of reexported symbols section
+ "undefined_symbols": [{ ... }] # List of undefined symbols section
+},
+"libraries": [ # Optional: Array of inlined libraries
+ {...}, {...}, {...}
+]
+}
+*/
+// clang-format on
+
+using namespace llvm;
+using namespace llvm::json;
+using namespace llvm::MachO;
+
+namespace {
+struct JSONSymbol {
+ SymbolKind Kind;
+ std::string Name;
+ SymbolFlags Flags;
+};
+
+using AttrToTargets = std::map<std::string, TargetList>;
+using TargetsToSymbols =
+ SmallVector<std::pair<TargetList, std::vector<JSONSymbol>>>;
+
+enum TBDKey : size_t {
+ TBDVersion = 0U,
+ MainLibrary,
+ Documents,
+ TargetInfo,
+ Targets,
+ Target,
+ Deployment,
+ Flags,
+ Attributes,
+ InstallName,
+ CurrentVersion,
+ CompatibilityVersion,
+ Version,
+ SwiftABI,
+ ABI,
+ ParentUmbrella,
+ Umbrella,
+ AllowableClients,
+ Clients,
+ ReexportLibs,
+ Names,
+ Name,
+ Exports,
+ Reexports,
+ Undefineds,
+ Data,
+ Text,
+ Weak,
+ ThreadLocal,
+ Globals,
+ ObjCClass,
+ ObjCEHType,
+ ObjCIvar,
+ RPath,
+ Paths,
+};
+
+std::array<StringRef, 64> Keys = {
+ "tapi_tbd_version",
+ "main_library",
+ "libraries",
+ "target_info",
+ "targets",
+ "target",
+ "min_deployment",
+ "flags",
+ "attributes",
+ "install_names",
+ "current_versions",
+ "compatibility_versions",
+ "version",
+ "swift_abi",
+ "abi",
+ "parent_umbrellas",
+ "umbrella",
+ "allowable_clients",
+ "clients",
+ "reexported_libraries",
+ "names",
+ "name",
+ "exported_symbols",
+ "reexported_symbols",
+ "undefined_symbols",
+ "data",
+ "text",
+ "weak",
+ "thread_local",
+ "global",
+ "objc_class",
+ "objc_eh_type",
+ "objc_ivar",
+ "rpaths",
+ "paths",
+};
+
+static llvm::SmallString<128> getParseErrorMsg(TBDKey Key) {
+ return {"invalid ", Keys[Key], " section"};
+}
+
+static llvm::SmallString<128> getSerializeErrorMsg(TBDKey Key) {
+ return {"missing ", Keys[Key], " information"};
+}
+
+class JSONStubError : public llvm::ErrorInfo<llvm::json::ParseError> {
+public:
+ JSONStubError(Twine ErrMsg) : Message(ErrMsg.str()) {}
+
+ void log(llvm::raw_ostream &OS) const override { OS << Message << "\n"; }
+ std::error_code convertToErrorCode() const override {
+ return llvm::inconvertibleErrorCode();
+ }
+
+private:
+ std::string Message;
+};
+
+template <typename JsonT, typename StubT = JsonT>
+Expected<StubT> getRequiredValue(
+ TBDKey Key, const Object *Obj,
+ std::function<std::optional<JsonT>(const Object *, StringRef)> GetValue,
+ std::function<std::optional<StubT>(JsonT)> Validate = nullptr) {
+ std::optional<JsonT> Val = GetValue(Obj, Keys[Key]);
+ if (!Val)
+ return make_error<JSONStubError>(getParseErrorMsg(Key));
+
+ if (Validate == nullptr)
+ return static_cast<StubT>(*Val);
+
+ std::optional<StubT> Result = Validate(*Val);
+ if (!Result.has_value())
+ return make_error<JSONStubError>(getParseErrorMsg(Key));
+ return Result.value();
+}
+
+template <typename JsonT, typename StubT = JsonT>
+Expected<StubT> getRequiredValue(
+ TBDKey Key, const Object *Obj,
+ std::function<std::optional<JsonT>(const Object *, StringRef)> GetValue,
+ StubT DefaultValue, std::function<std::optional<StubT>(JsonT)> Validate) {
+ std::optional<JsonT> Val = GetValue(Obj, Keys[Key]);
+ if (!Val)
+ return DefaultValue;
+
+ std::optional<StubT> Result;
+ Result = Validate(*Val);
+ if (!Result.has_value())
+ return make_error<JSONStubError>(getParseErrorMsg(Key));
+ return Result.value();
+}
+
+Error collectFromArray(TBDKey Key, const Object *Obj,
+ std::function<void(StringRef)> Append,
+ bool IsRequired = false) {
+ const auto *Values = Obj->getArray(Keys[Key]);
+ if (!Values) {
+ if (IsRequired)
+ return make_error<JSONStubError>(getParseErrorMsg(Key));
+ return Error::success();
+ }
+
+ for (const Value &Val : *Values) {
+ auto ValStr = Val.getAsString();
+ if (!ValStr.has_value())
+ return make_error<JSONStubError>(getParseErrorMsg(Key));
+ Append(ValStr.value());
+ }
+
+ return Error::success();
+}
+
+namespace StubParser {
+
+Expected<FileType> getVersion(const Object *File) {
+ auto VersionOrErr = getRequiredValue<int64_t, FileType>(
+ TBDKey::TBDVersion, File, &Object::getInteger,
+ [](int64_t Val) -> std::optional<FileType> {
+ unsigned Result = Val;
+ if (Result != 5)
+ return std::nullopt;
+ return FileType::TBD_V5;
+ });
+
+ if (!VersionOrErr)
+ return VersionOrErr.takeError();
+ return *VersionOrErr;
+}
+
+Expected<TargetList> getTargets(const Object *Section) {
+ const auto *Targets = Section->getArray(Keys[TBDKey::Targets]);
+ if (!Targets)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Targets));
+
+ TargetList IFTargets;
+ for (const Value &JSONTarget : *Targets) {
+ auto TargetStr = JSONTarget.getAsString();
+ if (!TargetStr.has_value())
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
+ auto TargetOrErr = Target::create(TargetStr.value());
+ if (!TargetOrErr)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
+ IFTargets.push_back(*TargetOrErr);
+ }
+ return std::move(IFTargets);
+}
+
+Expected<TargetList> getTargetsSection(const Object *Section) {
+ const Array *Targets = Section->getArray(Keys[TBDKey::TargetInfo]);
+ if (!Targets)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Targets));
+
+ TargetList IFTargets;
+ for (const Value &JSONTarget : *Targets) {
+ const auto *Obj = JSONTarget.getAsObject();
+ if (!Obj)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
+ auto TargetStr =
+ getRequiredValue<StringRef>(TBDKey::Target, Obj, &Object::getString);
+ if (!TargetStr)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
+ auto VersionStr = getRequiredValue<StringRef>(TBDKey::Deployment, Obj,
+ &Object::getString);
+ if (!VersionStr)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Deployment));
+ VersionTuple Version;
+ if (Version.tryParse(*VersionStr))
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Deployment));
+ auto TargetOrErr = Target::create(*TargetStr);
+ if (!TargetOrErr)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
+ TargetOrErr->MinDeployment = Version;
+ // Convert to LLVM::Triple to accurately compute minOS + platform + arch
+ // pairing.
+ IFTargets.push_back(
+ MachO::Target(Triple(getTargetTripleName(*TargetOrErr))));
+ }
+ return std::move(IFTargets);
+}
+
+Error collectSymbolsFromSegment(const Object *Segment, TargetsToSymbols &Result,
+ SymbolFlags SectionFlag) {
+ auto Err = collectFromArray(
+ TBDKey::Globals, Segment, [&Result, &SectionFlag](StringRef Name) {
+ JSONSymbol Sym = {SymbolKind::GlobalSymbol, Name.str(), SectionFlag};
+ Result.back().second.emplace_back(Sym);
+ });
+ if (Err)
+ return Err;
+
+ Err = collectFromArray(
+ TBDKey::ObjCClass, Segment, [&Result, &SectionFlag](StringRef Name) {
+ JSONSymbol Sym = {SymbolKind::ObjectiveCClass, Name.str(), SectionFlag};
+ Result.back().second.emplace_back(Sym);
+ });
+ if (Err)
+ return Err;
+
+ Err = collectFromArray(TBDKey::ObjCEHType, Segment,
+ [&Result, &SectionFlag](StringRef Name) {
+ JSONSymbol Sym = {SymbolKind::ObjectiveCClassEHType,
+ Name.str(), SectionFlag};
+ Result.back().second.emplace_back(Sym);
+ });
+ if (Err)
+ return Err;
+
+ Err = collectFromArray(
+ TBDKey::ObjCIvar, Segment, [&Result, &SectionFlag](StringRef Name) {
+ JSONSymbol Sym = {SymbolKind::ObjectiveCInstanceVariable, Name.str(),
+ SectionFlag};
+ Result.back().second.emplace_back(Sym);
+ });
+ if (Err)
+ return Err;
+
+ SymbolFlags WeakFlag =
+ SectionFlag |
+ (((SectionFlag & SymbolFlags::Undefined) == SymbolFlags::Undefined)
+ ? SymbolFlags::WeakReferenced
+ : SymbolFlags::WeakDefined);
+ Err = collectFromArray(
+ TBDKey::Weak, Segment, [&Result, WeakFlag](StringRef Name) {
+ JSONSymbol Sym = {SymbolKind::GlobalSymbol, Name.str(), WeakFlag};
+ Result.back().second.emplace_back(Sym);
+ });
+ if (Err)
+ return Err;
+
+ Err = collectFromArray(
+ TBDKey::ThreadLocal, Segment, [&Result, SectionFlag](StringRef Name) {
+ JSONSymbol Sym = {SymbolKind::GlobalSymbol, Name.str(),
+ SymbolFlags::ThreadLocalValue | SectionFlag};
+ Result.back().second.emplace_back(Sym);
+ });
+ if (Err)
+ return Err;
+
+ return Error::success();
+}
+
+Expected<StringRef> getNameSection(const Object *File) {
+ const Array *Section = File->getArray(Keys[TBDKey::InstallName]);
+ if (!Section)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::InstallName));
+
+ assert(!Section->empty() && "unexpected missing install name");
+ // TODO: Just take first for now.
+ const auto *Obj = Section->front().getAsObject();
+ if (!Obj)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::InstallName));
+
+ return getRequiredValue<StringRef>(TBDKey::Name, Obj, &Object::getString);
+}
+
+Expected<TargetsToSymbols> getSymbolSection(const Object *File, TBDKey Key,
+ TargetList &Targets) {
+
+ const Array *Section = File->getArray(Keys[Key]);
+ if (!Section)
+ return TargetsToSymbols();
+
+ SymbolFlags SectionFlag;
+ switch (Key) {
+ case TBDKey::Reexports:
+ SectionFlag = SymbolFlags::Rexported;
+ break;
+ case TBDKey::Undefineds:
+ SectionFlag = SymbolFlags::Undefined;
+ break;
+ default:
+ SectionFlag = SymbolFlags::None;
+ break;
+ };
+
+ TargetsToSymbols Result;
+ TargetList MappedTargets;
+ for (auto Val : *Section) {
+ auto *Obj = Val.getAsObject();
+ if (!Obj)
+ continue;
+
+ auto TargetsOrErr = getTargets(Obj);
+ if (!TargetsOrErr) {
+ MappedTargets = Targets;
+ consumeError(TargetsOrErr.takeError());
+ } else {
+ MappedTargets = *TargetsOrErr;
+ }
+ Result.emplace_back(
+ std::make_pair(std::move(MappedTargets), std::vector<JSONSymbol>()));
+
+ auto *DataSection = Obj->getObject(Keys[TBDKey::Data]);
+ auto *TextSection = Obj->getObject(Keys[TBDKey::Text]);
+ // There should be at least one valid section.
+ if (!DataSection && !TextSection)
+ return make_error<JSONStubError>(getParseErrorMsg(Key));
+
+ if (DataSection) {
+ auto Err = collectSymbolsFromSegment(DataSection, Result,
+ SectionFlag | SymbolFlags::Data);
+ if (Err)
+ return std::move(Err);
+ }
+ if (TextSection) {
+ auto Err = collectSymbolsFromSegment(TextSection, Result,
+ SectionFlag | SymbolFlags::Text);
+ if (Err)
+ return std::move(Err);
+ }
+ }
+
+ return std::move(Result);
+}
+
+Expected<AttrToTargets> getLibSection(const Object *File, TBDKey Key,
+ TBDKey SubKey,
+ const TargetList &Targets) {
+ auto *Section = File->getArray(Keys[Key]);
+ if (!Section)
+ return AttrToTargets();
+
+ AttrToTargets Result;
+ TargetList MappedTargets;
+ for (auto Val : *Section) {
+ auto *Obj = Val.getAsObject();
+ if (!Obj)
+ continue;
+
+ auto TargetsOrErr = getTargets(Obj);
+ if (!TargetsOrErr) {
+ MappedTargets = Targets;
+ consumeError(TargetsOrErr.takeError());
+ } else {
+ MappedTargets = *TargetsOrErr;
+ }
+ auto Err =
+ collectFromArray(SubKey, Obj, [&Result, &MappedTargets](StringRef Key) {
+ Result[Key.str()] = MappedTargets;
+ });
+ if (Err)
+ return std::move(Err);
+ }
+
+ return std::move(Result);
+}
+
+Expected<AttrToTargets> getUmbrellaSection(const Object *File,
+ const TargetList &Targets) {
+ const auto *Umbrella = File->getArray(Keys[TBDKey::ParentUmbrella]);
+ if (!Umbrella)
+ return AttrToTargets();
+
+ AttrToTargets Result;
+ TargetList MappedTargets;
+ for (auto Val : *Umbrella) {
+ auto *Obj = Val.getAsObject();
+ if (!Obj)
+ return make_error<JSONStubError>(
+ getParseErrorMsg(TBDKey::ParentUmbrella));
+
+ // Get Targets section.
+ auto TargetsOrErr = getTargets(Obj);
+ if (!TargetsOrErr) {
+ MappedTargets = Targets;
+ consumeError(TargetsOrErr.takeError());
+ } else {
+ MappedTargets = *TargetsOrErr;
+ }
+
+ auto UmbrellaOrErr =
+ getRequiredValue<StringRef>(TBDKey::Umbrella, Obj, &Object::getString);
+ if (!UmbrellaOrErr)
+ return UmbrellaOrErr.takeError();
+ Result[UmbrellaOrErr->str()] = Targets;
+ }
+ return std::move(Result);
+}
+
+Expected<uint8_t> getSwiftVersion(const Object *File) {
+ const Array *Versions = File->getArray(Keys[TBDKey::SwiftABI]);
+ if (!Versions)
+ return 0;
+
+ for (const auto &Val : *Versions) {
+ const auto *Obj = Val.getAsObject();
+ if (!Obj)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::SwiftABI));
+
+ // TODO: Take first for now.
+ return getRequiredValue<int64_t, uint8_t>(TBDKey::ABI, Obj,
+ &Object::getInteger);
+ }
+
+ return 0;
+}
+
+Expected<PackedVersion> getPackedVersion(const Object *File, TBDKey Key) {
+ const Array *Versions = File->getArray(Keys[Key]);
+ if (!Versions)
+ return PackedVersion(1, 0, 0);
+
+ for (const auto &Val : *Versions) {
+ const auto *Obj = Val.getAsObject();
+ if (!Obj)
+ return make_error<JSONStubError>(getParseErrorMsg(Key));
+
+ auto ValidatePV = [](StringRef Version) -> std::optional<PackedVersion> {
+ PackedVersion PV;
+ auto [success, truncated] = PV.parse64(Version);
+ if (!success || truncated)
+ return std::nullopt;
+ return PV;
+ };
+ // TODO: Take first for now.
+ return getRequiredValue<StringRef, PackedVersion>(
+ TBDKey::Version, Obj, &Object::getString, PackedVersion(1, 0, 0),
+ ValidatePV);
+ }
+
+ return PackedVersion(1, 0, 0);
+}
+
+Expected<TBDFlags> getFlags(const Object *File) {
+ TBDFlags Flags = TBDFlags::None;
+ const Array *Section = File->getArray(Keys[TBDKey::Flags]);
+ if (!Section)
+ return Flags;
+
+ for (auto &Val : *Section) {
+ // TODO: Just take first for now.
+ const auto *Obj = Val.getAsObject();
+ if (!Obj)
+ return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Flags));
+
+ auto FlagsOrErr =
+ collectFromArray(TBDKey::Attributes, Obj, [&Flags](StringRef Flag) {
+ TBDFlags TBDFlag =
+ StringSwitch<TBDFlags>(Flag)
+ .Case("flat_namespace", TBDFlags::FlatNamespace)
+ .Case("not_app_extension_safe",
+ TBDFlags::NotApplicationExtensionSafe)
+ .Default(TBDFlags::None);
+ Flags |= TBDFlag;
+ });
+
+ if (FlagsOrErr)
+ return std::move(FlagsOrErr);
+
+ return Flags;
+ }
+
+ return Flags;
+}
+
+using IFPtr = std::unique_ptr<InterfaceFile>;
+Expected<IFPtr> parseToInterfaceFile(const Object *File) {
+ auto TargetsOrErr = getTargetsSection(File);
+ if (!TargetsOrErr)
+ return TargetsOrErr.takeError();
+ TargetList Targets = *TargetsOrErr;
+
+ auto NameOrErr = getNameSection(File);
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+ StringRef Name = *NameOrErr;
+
+ auto CurrVersionOrErr = getPackedVersion(File, TBDKey::CurrentVersion);
+ if (!CurrVersionOrErr)
+ return CurrVersionOrErr.takeError();
+ PackedVersion CurrVersion = *CurrVersionOrErr;
+
+ auto CompVersionOrErr = getPackedVersion(File, TBDKey::CompatibilityVersion);
+ if (!CompVersionOrErr)
+ return CompVersionOrErr.takeError();
+ PackedVersion CompVersion = *CompVersionOrErr;
+
+ auto SwiftABIOrErr = getSwiftVersion(File);
+ if (!SwiftABIOrErr)
+ return SwiftABIOrErr.takeError();
+ uint8_t SwiftABI = *SwiftABIOrErr;
+
+ auto FlagsOrErr = getFlags(File);
+ if (!FlagsOrErr)
+ return FlagsOrErr.takeError();
+ TBDFlags Flags = *FlagsOrErr;
+
+ auto UmbrellasOrErr = getUmbrellaSection(File, Targets);
+ if (!UmbrellasOrErr)
+ return UmbrellasOrErr.takeError();
+ AttrToTargets Umbrellas = *UmbrellasOrErr;
+
+ auto ClientsOrErr =
+ getLibSection(File, TBDKey::AllowableClients, TBDKey::Clients, Targets);
+ if (!ClientsOrErr)
+ return ClientsOrErr.takeError();
+ AttrToTargets Clients = *ClientsOrErr;
+
+ auto RLOrErr =
+ getLibSection(File, TBDKey::ReexportLibs, TBDKey::Names, Targets);
+ if (!RLOrErr)
+ return RLOrErr.takeError();
+ AttrToTargets ReexportLibs = std::move(*RLOrErr);
+
+ auto RPathsOrErr = getLibSection(File, TBDKey::RPath, TBDKey::Paths, Targets);
+ if (!RPathsOrErr)
+ return RPathsOrErr.takeError();
+ AttrToTargets RPaths = std::move(*RPathsOrErr);
+
+ auto ExportsOrErr = getSymbolSection(File, TBDKey::Exports, Targets);
+ if (!ExportsOrErr)
+ return ExportsOrErr.takeError();
+ TargetsToSymbols Exports = std::move(*ExportsOrErr);
+
+ auto ReexportsOrErr = getSymbolSection(File, TBDKey::Reexports, Targets);
+ if (!ReexportsOrErr)
+ return ReexportsOrErr.takeError();
+ TargetsToSymbols Reexports = std::move(*ReexportsOrErr);
+
+ auto UndefinedsOrErr = getSymbolSection(File, TBDKey::Undefineds, Targets);
+ if (!UndefinedsOrErr)
+ return UndefinedsOrErr.takeError();
+ TargetsToSymbols Undefineds = std::move(*UndefinedsOrErr);
+
+ IFPtr F(new InterfaceFile);
+ F->setInstallName(Name);
+ F->setCurrentVersion(CurrVersion);
+ F->setCompatibilityVersion(CompVersion);
+ F->setSwiftABIVersion(SwiftABI);
+ F->setTwoLevelNamespace(!(Flags & TBDFlags::FlatNamespace));
+ F->setApplicationExtensionSafe(
+ !(Flags & TBDFlags::NotApplicationExtensionSafe));
+ for (auto &T : Targets)
+ F->addTarget(T);
+ for (auto &[Lib, Targets] : Clients)
+ for (auto Target : Targets)
+ F->addAllowableClient(Lib, Target);
+ for (auto &[Lib, Targets] : ReexportLibs)
+ for (auto Target : Targets)
+ F->addReexportedLibrary(Lib, Target);
+ for (auto &[Lib, Targets] : Umbrellas)
+ for (auto Target : Targets)
+ F->addParentUmbrella(Target, Lib);
+ for (auto &[Path, Targets] : RPaths)
+ for (auto Target : Targets)
+ F->addRPath(Target, Path);
+ for (auto &[Targets, Symbols] : Exports)
+ for (auto &Sym : Symbols)
+ F->addSymbol(Sym.Kind, Sym.Name, Targets, Sym.Flags);
+ for (auto &[Targets, Symbols] : Reexports)
+ for (auto &Sym : Symbols)
+ F->addSymbol(Sym.Kind, Sym.Name, Targets, Sym.Flags);
+ for (auto &[Targets, Symbols] : Undefineds)
+ for (auto &Sym : Symbols)
+ F->addSymbol(Sym.Kind, Sym.Name, Targets, Sym.Flags);
+
+ return std::move(F);
+}
+
+Expected<std::vector<IFPtr>> getInlinedLibs(const Object *File) {
+ std::vector<IFPtr> IFs;
+ const Array *Files = File->getArray(Keys[TBDKey::Documents]);
+ if (!Files)
+ return std::move(IFs);
+
+ for (auto Lib : *Files) {
+ auto IFOrErr = parseToInterfaceFile(Lib.getAsObject());
+ if (!IFOrErr)
+ return IFOrErr.takeError();
+ auto IF = std::move(*IFOrErr);
+ IFs.emplace_back(std::move(IF));
+ }
+ return std::move(IFs);
+}
+
+} // namespace StubParser
+} // namespace
+
+Expected<std::unique_ptr<InterfaceFile>>
+MachO::getInterfaceFileFromJSON(StringRef JSON) {
+ auto ValOrErr = parse(JSON);
+ if (!ValOrErr)
+ return ValOrErr.takeError();
+
+ auto *Root = ValOrErr->getAsObject();
+ auto VersionOrErr = StubParser::getVersion(Root);
+ if (!VersionOrErr)
+ return VersionOrErr.takeError();
+ FileType Version = *VersionOrErr;
+
+ Object *MainLib = Root->getObject(Keys[TBDKey::MainLibrary]);
+ auto IFOrErr = StubParser::parseToInterfaceFile(MainLib);
+ if (!IFOrErr)
+ return IFOrErr.takeError();
+ (*IFOrErr)->setFileType(Version);
+ std::unique_ptr<InterfaceFile> IF(std::move(*IFOrErr));
+
+ auto IFsOrErr = StubParser::getInlinedLibs(Root);
+ if (!IFsOrErr)
+ return IFsOrErr.takeError();
+ for (auto &File : *IFsOrErr) {
+ File->setFileType(Version);
+ IF->addDocument(std::shared_ptr<InterfaceFile>(std::move(File)));
+ }
+ return std::move(IF);
+}
+
+namespace {
+
+template <typename ContainerT = Array>
+bool insertNonEmptyValues(Object &Obj, TBDKey Key, ContainerT &&Contents) {
+ if (Contents.empty())
+ return false;
+ Obj[Keys[Key]] = std::move(Contents);
+ return true;
+}
+
+std::string getFormattedStr(const MachO::Target &Targ) {
+ std::string PlatformStr = Targ.Platform == PLATFORM_MACCATALYST
+ ? "maccatalyst"
+ : getOSAndEnvironmentName(Targ.Platform);
+ return (getArchitectureName(Targ.Arch) + "-" + PlatformStr).str();
+}
+
+template <typename AggregateT>
+std::vector<std::string> serializeTargets(const AggregateT Targets,
+ const TargetList &ActiveTargets) {
+ std::vector<std::string> TargetsStr;
+ if (Targets.size() == ActiveTargets.size())
+ return TargetsStr;
+
+ llvm::for_each(Targets, [&TargetsStr](const MachO::Target &Target) {
+ TargetsStr.emplace_back(getFormattedStr(Target));
+ });
+ return TargetsStr;
+}
+
+Array serializeTargetInfo(const TargetList &ActiveTargets) {
+ Array Targets;
+ for (const auto Targ : ActiveTargets) {
+ Object TargetInfo;
+ TargetInfo[Keys[TBDKey::Deployment]] = Targ.MinDeployment.getAsString();
+ TargetInfo[Keys[TBDKey::Target]] = getFormattedStr(Targ);
+ Targets.emplace_back(std::move(TargetInfo));
+ }
+ return Targets;
+}
+
+template <typename ValueT, typename EntryT = ValueT>
+Array serializeScalar(TBDKey Key, ValueT Value, ValueT Default = ValueT()) {
+ if (Value == Default)
+ return {};
+ Array Container;
+ Object ScalarObj({Object::KV({Keys[Key], EntryT(Value)})});
+
+ Container.emplace_back(std::move(ScalarObj));
+ return Container;
+}
+
+using TargetsToValuesMap =
+ std::map<std::vector<std::string>, std::vector<std::string>>;
+
+template <typename AggregateT = TargetsToValuesMap>
+Array serializeAttrToTargets(AggregateT &Entries, TBDKey Key) {
+ Array Container;
+ for (const auto &[Targets, Values] : Entries) {
+ Object Obj;
+ insertNonEmptyValues(Obj, TBDKey::Targets, std::move(Targets));
+ Obj[Keys[Key]] = Values;
+ Container.emplace_back(std::move(Obj));
+ }
+ return Container;
+}
+
+template <typename ValueT = std::string,
+ typename AggregateT = std::vector<std::pair<MachO::Target, ValueT>>>
+Array serializeField(TBDKey Key, const AggregateT &Values,
+ const TargetList &ActiveTargets, bool IsArray = true) {
+ std::map<ValueT, std::set<MachO::Target>> Entries;
+ for (const auto &[Target, Val] : Values)
+ Entries[Val].insert(Target);
+
+ if (!IsArray) {
+ std::map<std::vector<std::string>, std::string> FinalEntries;
+ for (const auto &[Val, Targets] : Entries)
+ FinalEntries[serializeTargets(Targets, ActiveTargets)] = Val;
+ return serializeAttrToTargets(FinalEntries, Key);
+ }
+
+ TargetsToValuesMap FinalEntries;
+ for (const auto &[Val, Targets] : Entries)
+ FinalEntries[serializeTargets(Targets, ActiveTargets)].emplace_back(Val);
+ return serializeAttrToTargets(FinalEntries, Key);
+}
+
+Array serializeField(TBDKey Key, const std::vector<InterfaceFileRef> &Values,
+ const TargetList &ActiveTargets) {
+ TargetsToValuesMap FinalEntries;
+ for (const auto &Ref : Values) {
+ TargetList Targets{Ref.targets().begin(), Ref.targets().end()};
+ FinalEntries[serializeTargets(Targets, ActiveTargets)].emplace_back(
+ Ref.getInstallName());
+ }
+ return serializeAttrToTargets(FinalEntries, Key);
+}
+
+struct SymbolFields {
+ struct SymbolTypes {
+ std::vector<StringRef> Weaks;
+ std::vector<StringRef> Globals;
+ std::vector<StringRef> TLV;
+ std::vector<StringRef> ObjCClasses;
+ std::vector<StringRef> IVars;
+ std::vector<StringRef> EHTypes;
+
+ bool empty() const {
+ return Weaks.empty() && Globals.empty() && TLV.empty() &&
+ ObjCClasses.empty() && IVars.empty() && EHTypes.empty();
+ }
+ };
+ SymbolTypes Data;
+ SymbolTypes Text;
+};
+
+Array serializeSymbols(InterfaceFile::const_filtered_symbol_range Symbols,
+ const TargetList &ActiveTargets) {
+ auto AssignForSymbolType = [](SymbolFields::SymbolTypes &Assignment,
+ const Symbol *Sym) {
+ switch (Sym->getKind()) {
+ case SymbolKind::ObjectiveCClass:
+ Assignment.ObjCClasses.emplace_back(Sym->getName());
+ return;
+ case SymbolKind::ObjectiveCClassEHType:
+ Assignment.EHTypes.emplace_back(Sym->getName());
+ return;
+ case SymbolKind::ObjectiveCInstanceVariable:
+ Assignment.IVars.emplace_back(Sym->getName());
+ return;
+ case SymbolKind::GlobalSymbol: {
+ if (Sym->isWeakReferenced() || Sym->isWeakDefined())
+ Assignment.Weaks.emplace_back(Sym->getName());
+ else if (Sym->isThreadLocalValue())
+ Assignment.TLV.emplace_back(Sym->getName());
+ else
+ Assignment.Globals.emplace_back(Sym->getName());
+ return;
+ }
+ }
+ };
+
+ std::map<std::vector<std::string>, SymbolFields> Entries;
+ for (const auto *Sym : Symbols) {
+ std::set<MachO::Target> Targets{Sym->targets().begin(),
+ Sym->targets().end()};
+ auto JSONTargets = serializeTargets(Targets, ActiveTargets);
+ if (Sym->isData())
+ AssignForSymbolType(Entries[std::move(JSONTargets)].Data, Sym);
+ else if (Sym->isText())
+ AssignForSymbolType(Entries[std::move(JSONTargets)].Text, Sym);
+ else
+ llvm_unreachable("unexpected symbol type");
+ }
+
+ auto InsertSymbolsToJSON = [](Object &SymSection, TBDKey SegmentKey,
+ SymbolFields::SymbolTypes &SymField) {
+ if (SymField.empty())
+ return;
+ Object Segment;
+ insertNonEmptyValues(Segment, TBDKey::Globals, std::move(SymField.Globals));
+ insertNonEmptyValues(Segment, TBDKey::ThreadLocal, std::move(SymField.TLV));
+ insertNonEmptyValues(Segment, TBDKey::Weak, std::move(SymField.Weaks));
+ insertNonEmptyValues(Segment, TBDKey::ObjCClass,
+ std::move(SymField.ObjCClasses));
+ insertNonEmptyValues(Segment, TBDKey::ObjCEHType,
+ std::move(SymField.EHTypes));
+ insertNonEmptyValues(Segment, TBDKey::ObjCIvar, std::move(SymField.IVars));
+ insertNonEmptyValues(SymSection, SegmentKey, std::move(Segment));
+ };
+
+ Array SymbolSection;
+ for (auto &[Targets, Fields] : Entries) {
+ Object AllSyms;
+ insertNonEmptyValues(AllSyms, TBDKey::Targets, std::move(Targets));
+ InsertSymbolsToJSON(AllSyms, TBDKey::Data, Fields.Data);
+ InsertSymbolsToJSON(AllSyms, TBDKey::Text, Fields.Text);
+ SymbolSection.emplace_back(std::move(AllSyms));
+ }
+
+ return SymbolSection;
+}
+
+Array serializeFlags(const InterfaceFile *File) {
+ // TODO: Give all Targets the same flags for now.
+ Array Flags;
+ if (!File->isTwoLevelNamespace())
+ Flags.emplace_back("flat_namespace");
+ if (!File->isApplicationExtensionSafe())
+ Flags.emplace_back("not_app_extension_safe");
+ return serializeScalar(TBDKey::Attributes, std::move(Flags));
+}
+
+Expected<Object> serializeIF(const InterfaceFile *File) {
+ Object Library;
+
+ // Handle required keys.
+ TargetList ActiveTargets{File->targets().begin(), File->targets().end()};
+ if (!insertNonEmptyValues(Library, TBDKey::TargetInfo,
+ serializeTargetInfo(ActiveTargets)))
+ return make_error<JSONStubError>(getSerializeErrorMsg(TBDKey::TargetInfo));
+
+ Array Name = serializeScalar<StringRef>(TBDKey::Name, File->getInstallName());
+ if (!insertNonEmptyValues(Library, TBDKey::InstallName, std::move(Name)))
+ return make_error<JSONStubError>(getSerializeErrorMsg(TBDKey::InstallName));
+
+ // Handle optional keys.
+ Array Flags = serializeFlags(File);
+ insertNonEmptyValues(Library, TBDKey::Flags, std::move(Flags));
+
+ Array CurrentV = serializeScalar<PackedVersion, std::string>(
+ TBDKey::Version, File->getCurrentVersion(), PackedVersion(1, 0, 0));
+ insertNonEmptyValues(Library, TBDKey::CurrentVersion, std::move(CurrentV));
+
+ Array CompatV = serializeScalar<PackedVersion, std::string>(
+ TBDKey::Version, File->getCompatibilityVersion(), PackedVersion(1, 0, 0));
+ insertNonEmptyValues(Library, TBDKey::CompatibilityVersion,
+ std::move(CompatV));
+
+ Array SwiftABI = serializeScalar<uint8_t, int64_t>(
+ TBDKey::ABI, File->getSwiftABIVersion(), 0u);
+ insertNonEmptyValues(Library, TBDKey::SwiftABI, std::move(SwiftABI));
+
+ Array RPaths = serializeField(TBDKey::Paths, File->rpaths(), ActiveTargets);
+ insertNonEmptyValues(Library, TBDKey::RPath, std::move(RPaths));
+
+ Array Umbrellas = serializeField(TBDKey::Umbrella, File->umbrellas(),
+ ActiveTargets, /*IsArray=*/false);
+ insertNonEmptyValues(Library, TBDKey::ParentUmbrella, std::move(Umbrellas));
+
+ Array Clients =
+ serializeField(TBDKey::Clients, File->allowableClients(), ActiveTargets);
+ insertNonEmptyValues(Library, TBDKey::AllowableClients, std::move(Clients));
+
+ Array ReexportLibs =
+ serializeField(TBDKey::Names, File->reexportedLibraries(), ActiveTargets);
+ insertNonEmptyValues(Library, TBDKey::ReexportLibs, std::move(ReexportLibs));
+
+ // Handle symbols.
+ Array Exports = serializeSymbols(File->exports(), ActiveTargets);
+ insertNonEmptyValues(Library, TBDKey::Exports, std::move(Exports));
+
+ Array Reexports = serializeSymbols(File->reexports(), ActiveTargets);
+ insertNonEmptyValues(Library, TBDKey::Reexports, std::move(Reexports));
+
+ if (!File->isTwoLevelNamespace()) {
+ Array Undefineds = serializeSymbols(File->undefineds(), ActiveTargets);
+ insertNonEmptyValues(Library, TBDKey::Undefineds, std::move(Undefineds));
+ }
+
+ return std::move(Library);
+}
+
+Expected<Object> getJSON(const InterfaceFile *File) {
+ assert(File->getFileType() == FileType::TBD_V5 &&
+ "unexpected json file format version");
+ Object Root;
+
+ auto MainLibOrErr = serializeIF(File);
+ if (!MainLibOrErr)
+ return MainLibOrErr;
+ Root[Keys[TBDKey::MainLibrary]] = std::move(*MainLibOrErr);
+ Array Documents;
+ for (const auto &Doc : File->documents()) {
+ auto LibOrErr = serializeIF(Doc.get());
+ if (!LibOrErr)
+ return LibOrErr;
+ Documents.emplace_back(std::move(*LibOrErr));
+ }
+
+ Root[Keys[TBDKey::TBDVersion]] = 5;
+ insertNonEmptyValues(Root, TBDKey::Documents, std::move(Documents));
+ return std::move(Root);
+}
+
+} // namespace
+
+Error MachO::serializeInterfaceFileToJSON(raw_ostream &OS,
+ const InterfaceFile &File,
+ bool Compact) {
+ auto TextFile = getJSON(&File);
+ if (!TextFile)
+ return TextFile.takeError();
+ if (Compact)
+ OS << formatv("{0}", Value(std::move(*TextFile))) << "\n";
+ else
+ OS << formatv("{0:2}", Value(std::move(*TextFile))) << "\n";
+ return Error::success();
+}