llvm-project/llvm/lib/TextAPI/TextStub.cpp
Juergen Ributzka 3025c3eded Replace PlatformKind with PlatformType.
The PlatformKind/PlatformType enums contain the same information, which requires
them to be kept in-sync. This commit changes over to PlatformType as the sole
source of truth, which allows the removal of the redundant PlatformKind.

The majority of the changes were in LLD and TextAPI.

Reviewed By: cishida

Differential Revision: https://reviews.llvm.org/D117163
2022-01-13 09:23:49 -08:00

1150 lines
43 KiB
C++

//===- TextStub.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 the text stub file reader/writer.
//
//===----------------------------------------------------------------------===//
#include "TextAPIContext.h"
#include "TextStubCommon.h"
#include "llvm/ADT/BitmaskEnum.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TextAPI/Architecture.h"
#include "llvm/TextAPI/ArchitectureSet.h"
#include "llvm/TextAPI/InterfaceFile.h"
#include "llvm/TextAPI/PackedVersion.h"
#include "llvm/TextAPI/TextAPIReader.h"
#include "llvm/TextAPI/TextAPIWriter.h"
#include <algorithm>
#include <set>
// clang-format off
/*
YAML Format specification.
The TBD v1 format only support two level address libraries and is per
definition application extension safe.
--- # the tag !tapi-tbd-v1 is optional and
# shouldn't be emitted to support older linker.
archs: [ armv7, armv7s, arm64 ] # the list of architecture slices that are
# supported by this file.
platform: ios # Specifies the platform (macosx, ios, etc)
install-name: /u/l/libfoo.dylib #
current-version: 1.2.3 # Optional: defaults to 1.0
compatibility-version: 1.0 # Optional: defaults to 1.0
swift-version: 0 # Optional: defaults to 0
objc-constraint: none # Optional: defaults to none
exports: # List of export sections
...
Each export section is defined as following:
- archs: [ arm64 ] # the list of architecture slices
allowed-clients: [ client ] # Optional: List of clients
re-exports: [ ] # Optional: List of re-exports
symbols: [ _sym ] # Optional: List of symbols
objc-classes: [] # Optional: List of Objective-C classes
objc-ivars: [] # Optional: List of Objective C Instance
# Variables
weak-def-symbols: [] # Optional: List of weak defined symbols
thread-local-symbols: [] # Optional: List of thread local symbols
*/
/*
YAML Format specification.
--- !tapi-tbd-v2
archs: [ armv7, armv7s, arm64 ] # the list of architecture slices that are
# supported by this file.
uuids: [ armv7:... ] # Optional: List of architecture and UUID pairs.
platform: ios # Specifies the platform (macosx, ios, etc)
flags: [] # Optional:
install-name: /u/l/libfoo.dylib #
current-version: 1.2.3 # Optional: defaults to 1.0
compatibility-version: 1.0 # Optional: defaults to 1.0
swift-version: 0 # Optional: defaults to 0
objc-constraint: retain_release # Optional: defaults to retain_release
parent-umbrella: # Optional:
exports: # List of export sections
...
undefineds: # List of undefineds sections
...
Each export section is defined as following:
- archs: [ arm64 ] # the list of architecture slices
allowed-clients: [ client ] # Optional: List of clients
re-exports: [ ] # Optional: List of re-exports
symbols: [ _sym ] # Optional: List of symbols
objc-classes: [] # Optional: List of Objective-C classes
objc-ivars: [] # Optional: List of Objective C Instance
# Variables
weak-def-symbols: [] # Optional: List of weak defined symbols
thread-local-symbols: [] # Optional: List of thread local symbols
Each undefineds section is defined as following:
- archs: [ arm64 ] # the list of architecture slices
symbols: [ _sym ] # Optional: List of symbols
objc-classes: [] # Optional: List of Objective-C classes
objc-ivars: [] # Optional: List of Objective C Instance Variables
weak-ref-symbols: [] # Optional: List of weak defined symbols
*/
/*
YAML Format specification.
--- !tapi-tbd-v3
archs: [ armv7, armv7s, arm64 ] # the list of architecture slices that are
# supported by this file.
uuids: [ armv7:... ] # Optional: List of architecture and UUID pairs.
platform: ios # Specifies the platform (macosx, ios, etc)
flags: [] # Optional:
install-name: /u/l/libfoo.dylib #
current-version: 1.2.3 # Optional: defaults to 1.0
compatibility-version: 1.0 # Optional: defaults to 1.0
swift-abi-version: 0 # Optional: defaults to 0
objc-constraint: retain_release # Optional: defaults to retain_release
parent-umbrella: # Optional:
exports: # List of export sections
...
undefineds: # List of undefineds sections
...
Each export section is defined as following:
- archs: [ arm64 ] # the list of architecture slices
allowed-clients: [ client ] # Optional: List of clients
re-exports: [ ] # Optional: List of re-exports
symbols: [ _sym ] # Optional: List of symbols
objc-classes: [] # Optional: List of Objective-C classes
objc-eh-types: [] # Optional: List of Objective-C classes
# with EH
objc-ivars: [] # Optional: List of Objective C Instance
# Variables
weak-def-symbols: [] # Optional: List of weak defined symbols
thread-local-symbols: [] # Optional: List of thread local symbols
Each undefineds section is defined as following:
- archs: [ arm64 ] # the list of architecture slices
symbols: [ _sym ] # Optional: List of symbols
objc-classes: [] # Optional: List of Objective-C classes
objc-eh-types: [] # Optional: List of Objective-C classes
# with EH
objc-ivars: [] # Optional: List of Objective C Instance Variables
weak-ref-symbols: [] # Optional: List of weak defined symbols
*/
/*
YAML Format specification.
--- !tapi-tbd
tbd-version: 4 # The tbd version for format
targets: [ armv7-ios, x86_64-maccatalyst ] # The list of applicable tapi supported target triples
uuids: # Optional: List of target and UUID pairs.
- target: armv7-ios
value: ...
- target: x86_64-maccatalyst
value: ...
flags: [] # Optional:
install-name: /u/l/libfoo.dylib #
current-version: 1.2.3 # Optional: defaults to 1.0
compatibility-version: 1.0 # Optional: defaults to 1.0
swift-abi-version: 0 # Optional: defaults to 0
parent-umbrella: # Optional:
allowable-clients:
- targets: [ armv7-ios ] # Optional:
clients: [ clientA ]
exports: # List of export sections
...
re-exports: # List of reexport sections
...
undefineds: # List of undefineds sections
...
Each export and reexport section is defined as following:
- targets: [ arm64-macos ] # The list of target triples associated with symbols
symbols: [ _symA ] # Optional: List of symbols
objc-classes: [] # Optional: List of Objective-C classes
objc-eh-types: [] # Optional: List of Objective-C classes
# with EH
objc-ivars: [] # Optional: List of Objective C Instance
# Variables
weak-symbols: [] # Optional: List of weak defined symbols
thread-local-symbols: [] # Optional: List of thread local symbols
- targets: [ arm64-macos, x86_64-maccatalyst ] # Optional: Targets for applicable additional symbols
symbols: [ _symB ] # Optional: List of symbols
Each undefineds section is defined as following:
- targets: [ arm64-macos ] # The list of target triples associated with symbols
symbols: [ _symC ] # Optional: List of symbols
objc-classes: [] # Optional: List of Objective-C classes
objc-eh-types: [] # Optional: List of Objective-C classes
# with EH
objc-ivars: [] # Optional: List of Objective C Instance Variables
weak-symbols: [] # Optional: List of weak defined symbols
*/
// clang-format on
using namespace llvm;
using namespace llvm::yaml;
using namespace llvm::MachO;
namespace {
struct ExportSection {
std::vector<Architecture> Architectures;
std::vector<FlowStringRef> AllowableClients;
std::vector<FlowStringRef> ReexportedLibraries;
std::vector<FlowStringRef> Symbols;
std::vector<FlowStringRef> Classes;
std::vector<FlowStringRef> ClassEHs;
std::vector<FlowStringRef> IVars;
std::vector<FlowStringRef> WeakDefSymbols;
std::vector<FlowStringRef> TLVSymbols;
};
struct UndefinedSection {
std::vector<Architecture> Architectures;
std::vector<FlowStringRef> Symbols;
std::vector<FlowStringRef> Classes;
std::vector<FlowStringRef> ClassEHs;
std::vector<FlowStringRef> IVars;
std::vector<FlowStringRef> WeakRefSymbols;
};
// Sections for direct target mapping in TBDv4
struct SymbolSection {
TargetList Targets;
std::vector<FlowStringRef> Symbols;
std::vector<FlowStringRef> Classes;
std::vector<FlowStringRef> ClassEHs;
std::vector<FlowStringRef> Ivars;
std::vector<FlowStringRef> WeakSymbols;
std::vector<FlowStringRef> TlvSymbols;
};
struct MetadataSection {
enum Option { Clients, Libraries };
std::vector<Target> Targets;
std::vector<FlowStringRef> Values;
};
struct UmbrellaSection {
std::vector<Target> Targets;
std::string Umbrella;
};
// UUID's for TBDv4 are mapped to target not arch
struct UUIDv4 {
Target TargetID;
std::string Value;
UUIDv4() = default;
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)
LLVM_YAML_IS_SEQUENCE_VECTOR(ExportSection)
LLVM_YAML_IS_SEQUENCE_VECTOR(UndefinedSection)
// Specific to TBDv4
LLVM_YAML_IS_SEQUENCE_VECTOR(SymbolSection)
LLVM_YAML_IS_SEQUENCE_VECTOR(MetadataSection)
LLVM_YAML_IS_SEQUENCE_VECTOR(UmbrellaSection)
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(Target)
LLVM_YAML_IS_SEQUENCE_VECTOR(UUIDv4)
namespace llvm {
namespace yaml {
template <> struct MappingTraits<ExportSection> {
static void mapping(IO &IO, ExportSection &Section) {
const auto *Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext());
assert((!Ctx || (Ctx && Ctx->FileKind != FileType::Invalid)) &&
"File type is not set in YAML context");
IO.mapRequired("archs", Section.Architectures);
if (Ctx->FileKind == FileType::TBD_V1)
IO.mapOptional("allowed-clients", Section.AllowableClients);
else
IO.mapOptional("allowable-clients", Section.AllowableClients);
IO.mapOptional("re-exports", Section.ReexportedLibraries);
IO.mapOptional("symbols", Section.Symbols);
IO.mapOptional("objc-classes", Section.Classes);
if (Ctx->FileKind == FileType::TBD_V3)
IO.mapOptional("objc-eh-types", Section.ClassEHs);
IO.mapOptional("objc-ivars", Section.IVars);
IO.mapOptional("weak-def-symbols", Section.WeakDefSymbols);
IO.mapOptional("thread-local-symbols", Section.TLVSymbols);
}
};
template <> struct MappingTraits<UndefinedSection> {
static void mapping(IO &IO, UndefinedSection &Section) {
const auto *Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext());
assert((!Ctx || (Ctx && Ctx->FileKind != FileType::Invalid)) &&
"File type is not set in YAML context");
IO.mapRequired("archs", Section.Architectures);
IO.mapOptional("symbols", Section.Symbols);
IO.mapOptional("objc-classes", Section.Classes);
if (Ctx->FileKind == FileType::TBD_V3)
IO.mapOptional("objc-eh-types", Section.ClassEHs);
IO.mapOptional("objc-ivars", Section.IVars);
IO.mapOptional("weak-ref-symbols", Section.WeakRefSymbols);
}
};
template <> struct MappingTraits<SymbolSection> {
static void mapping(IO &IO, SymbolSection &Section) {
IO.mapRequired("targets", Section.Targets);
IO.mapOptional("symbols", Section.Symbols);
IO.mapOptional("objc-classes", Section.Classes);
IO.mapOptional("objc-eh-types", Section.ClassEHs);
IO.mapOptional("objc-ivars", Section.Ivars);
IO.mapOptional("weak-symbols", Section.WeakSymbols);
IO.mapOptional("thread-local-symbols", Section.TlvSymbols);
}
};
template <> struct MappingTraits<UmbrellaSection> {
static void mapping(IO &IO, UmbrellaSection &Section) {
IO.mapRequired("targets", Section.Targets);
IO.mapRequired("umbrella", Section.Umbrella);
}
};
template <> struct MappingTraits<UUIDv4> {
static void mapping(IO &IO, UUIDv4 &UUID) {
IO.mapRequired("target", UUID.TargetID);
IO.mapRequired("value", UUID.Value);
}
};
template <>
struct MappingContextTraits<MetadataSection, MetadataSection::Option> {
static void mapping(IO &IO, MetadataSection &Section,
MetadataSection::Option &OptionKind) {
IO.mapRequired("targets", Section.Targets);
switch (OptionKind) {
case MetadataSection::Option::Clients:
IO.mapRequired("clients", Section.Values);
return;
case MetadataSection::Option::Libraries:
IO.mapRequired("libraries", Section.Values);
return;
}
llvm_unreachable("unexpected option for metadata");
}
};
template <> struct ScalarBitSetTraits<TBDFlags> {
static void bitset(IO &IO, TBDFlags &Flags) {
IO.bitSetCase(Flags, "flat_namespace", TBDFlags::FlatNamespace);
IO.bitSetCase(Flags, "not_app_extension_safe",
TBDFlags::NotApplicationExtensionSafe);
IO.bitSetCase(Flags, "installapi", TBDFlags::InstallAPI);
}
};
template <> struct ScalarTraits<Target> {
static void output(const Target &Value, void *, raw_ostream &OS) {
OS << Value.Arch << "-";
switch (Value.Platform) {
default:
OS << "unknown";
break;
case PLATFORM_MACOS:
OS << "macos";
break;
case PLATFORM_IOS:
OS << "ios";
break;
case PLATFORM_TVOS:
OS << "tvos";
break;
case PLATFORM_WATCHOS:
OS << "watchos";
break;
case PLATFORM_BRIDGEOS:
OS << "bridgeos";
break;
case PLATFORM_MACCATALYST:
OS << "maccatalyst";
break;
case PLATFORM_IOSSIMULATOR:
OS << "ios-simulator";
break;
case PLATFORM_TVOSSIMULATOR:
OS << "tvos-simulator";
break;
case PLATFORM_WATCHOSSIMULATOR:
OS << "watchos-simulator";
break;
case PLATFORM_DRIVERKIT:
OS << "driverkit";
break;
}
}
static StringRef input(StringRef Scalar, void *, Target &Value) {
auto Result = Target::create(Scalar);
if (!Result) {
consumeError(Result.takeError());
return "unparsable target";
}
Value = *Result;
if (Value.Arch == AK_unknown)
return "unknown architecture";
if (Value.Platform == PLATFORM_UNKNOWN)
return "unknown platform";
return {};
}
static QuotingType mustQuote(StringRef) { return QuotingType::None; }
};
template <> struct MappingTraits<const InterfaceFile *> {
struct NormalizedTBD {
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());
CompatibilityVersion = PackedVersion(File->getCompatibilityVersion());
SwiftABIVersion = File->getSwiftABIVersion();
ObjCConstraint = File->getObjCConstraint();
Flags = TBDFlags::None;
if (!File->isApplicationExtensionSafe())
Flags |= TBDFlags::NotApplicationExtensionSafe;
if (!File->isTwoLevelNamespace())
Flags |= TBDFlags::FlatNamespace;
if (File->isInstallAPI())
Flags |= TBDFlags::InstallAPI;
if (!File->umbrellas().empty())
ParentUmbrella = File->umbrellas().begin()->second;
std::set<ArchitectureSet> ArchSet;
for (const auto &Library : File->allowableClients())
ArchSet.insert(Library.getArchitectures());
for (const auto &Library : File->reexportedLibraries())
ArchSet.insert(Library.getArchitectures());
std::map<const Symbol *, ArchitectureSet> SymbolToArchSet;
for (const auto *Symbol : File->exports()) {
auto Architectures = Symbol->getArchitectures();
SymbolToArchSet[Symbol] = Architectures;
ArchSet.insert(Architectures);
}
for (auto Architectures : ArchSet) {
ExportSection Section;
Section.Architectures = Architectures;
for (const auto &Library : File->allowableClients())
if (Library.getArchitectures() == Architectures)
Section.AllowableClients.emplace_back(Library.getInstallName());
for (const auto &Library : File->reexportedLibraries())
if (Library.getArchitectures() == Architectures)
Section.ReexportedLibraries.emplace_back(Library.getInstallName());
for (const auto &SymArch : SymbolToArchSet) {
if (SymArch.second != Architectures)
continue;
const auto *Symbol = SymArch.first;
switch (Symbol->getKind()) {
case SymbolKind::GlobalSymbol:
if (Symbol->isWeakDefined())
Section.WeakDefSymbols.emplace_back(Symbol->getName());
else if (Symbol->isThreadLocalValue())
Section.TLVSymbols.emplace_back(Symbol->getName());
else
Section.Symbols.emplace_back(Symbol->getName());
break;
case SymbolKind::ObjectiveCClass:
if (File->getFileType() != FileType::TBD_V3)
Section.Classes.emplace_back(
copyString("_" + Symbol->getName().str()));
else
Section.Classes.emplace_back(Symbol->getName());
break;
case SymbolKind::ObjectiveCClassEHType:
if (File->getFileType() != FileType::TBD_V3)
Section.Symbols.emplace_back(
copyString("_OBJC_EHTYPE_$_" + Symbol->getName().str()));
else
Section.ClassEHs.emplace_back(Symbol->getName());
break;
case SymbolKind::ObjectiveCInstanceVariable:
if (File->getFileType() != FileType::TBD_V3)
Section.IVars.emplace_back(
copyString("_" + Symbol->getName().str()));
else
Section.IVars.emplace_back(Symbol->getName());
break;
}
}
llvm::sort(Section.Symbols);
llvm::sort(Section.Classes);
llvm::sort(Section.ClassEHs);
llvm::sort(Section.IVars);
llvm::sort(Section.WeakDefSymbols);
llvm::sort(Section.TLVSymbols);
Exports.emplace_back(std::move(Section));
}
ArchSet.clear();
SymbolToArchSet.clear();
for (const auto *Symbol : File->undefineds()) {
auto Architectures = Symbol->getArchitectures();
SymbolToArchSet[Symbol] = Architectures;
ArchSet.insert(Architectures);
}
for (auto Architectures : ArchSet) {
UndefinedSection Section;
Section.Architectures = Architectures;
for (const auto &SymArch : SymbolToArchSet) {
if (SymArch.second != Architectures)
continue;
const auto *Symbol = SymArch.first;
switch (Symbol->getKind()) {
case SymbolKind::GlobalSymbol:
if (Symbol->isWeakReferenced())
Section.WeakRefSymbols.emplace_back(Symbol->getName());
else
Section.Symbols.emplace_back(Symbol->getName());
break;
case SymbolKind::ObjectiveCClass:
if (File->getFileType() != FileType::TBD_V3)
Section.Classes.emplace_back(
copyString("_" + Symbol->getName().str()));
else
Section.Classes.emplace_back(Symbol->getName());
break;
case SymbolKind::ObjectiveCClassEHType:
if (File->getFileType() != FileType::TBD_V3)
Section.Symbols.emplace_back(
copyString("_OBJC_EHTYPE_$_" + Symbol->getName().str()));
else
Section.ClassEHs.emplace_back(Symbol->getName());
break;
case SymbolKind::ObjectiveCInstanceVariable:
if (File->getFileType() != FileType::TBD_V3)
Section.IVars.emplace_back(
copyString("_" + Symbol->getName().str()));
else
Section.IVars.emplace_back(Symbol->getName());
break;
}
}
llvm::sort(Section.Symbols);
llvm::sort(Section.Classes);
llvm::sort(Section.ClassEHs);
llvm::sort(Section.IVars);
llvm::sort(Section.WeakRefSymbols);
Undefineds.emplace_back(std::move(Section));
}
}
// TBD v1 - TBD v3 files only support one platform and several
// architectures. It is possible to have more than one platform for TBD v3
// files, but the architectures don't apply to all
// platforms, specifically to filter out the i386 slice from
// platform macCatalyst.
TargetList synthesizeTargets(ArchitectureSet Architectures,
const PlatformSet &Platforms) {
TargetList Targets;
for (auto Platform : Platforms) {
Platform = mapToPlatformType(Platform, Architectures.hasX86());
for (const auto &&Architecture : Architectures) {
if ((Architecture == AK_i386) && (Platform == PLATFORM_MACCATALYST))
continue;
Targets.emplace_back(Architecture, Platform);
}
}
return Targets;
}
const InterfaceFile *denormalize(IO &IO) {
auto Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext());
assert(Ctx);
auto *File = new 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);
File->setSwiftABIVersion(SwiftABIVersion);
File->setObjCConstraint(ObjCConstraint);
for (const auto &Target : File->targets())
File->addParentUmbrella(Target, ParentUmbrella);
if (Ctx->FileKind == FileType::TBD_V1) {
File->setTwoLevelNamespace();
File->setApplicationExtensionSafe();
} else {
File->setTwoLevelNamespace(!(Flags & TBDFlags::FlatNamespace));
File->setApplicationExtensionSafe(
!(Flags & TBDFlags::NotApplicationExtensionSafe));
File->setInstallAPI(Flags & TBDFlags::InstallAPI);
}
for (const auto &Section : Exports) {
const auto Targets =
synthesizeTargets(Section.Architectures, Platforms);
for (const auto &Lib : Section.AllowableClients)
for (const auto &Target : Targets)
File->addAllowableClient(Lib, Target);
for (const auto &Lib : Section.ReexportedLibraries)
for (const auto &Target : Targets)
File->addReexportedLibrary(Lib, Target);
for (const auto &Symbol : Section.Symbols) {
if (Ctx->FileKind != FileType::TBD_V3 &&
Symbol.value.startswith("_OBJC_EHTYPE_$_"))
File->addSymbol(SymbolKind::ObjectiveCClassEHType,
Symbol.value.drop_front(15), Targets);
else
File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets);
}
for (auto &Symbol : Section.Classes) {
auto Name = Symbol.value;
if (Ctx->FileKind != FileType::TBD_V3)
Name = Name.drop_front();
File->addSymbol(SymbolKind::ObjectiveCClass, Name, Targets);
}
for (auto &Symbol : Section.ClassEHs)
File->addSymbol(SymbolKind::ObjectiveCClassEHType, Symbol, Targets);
for (auto &Symbol : Section.IVars) {
auto Name = Symbol.value;
if (Ctx->FileKind != FileType::TBD_V3)
Name = Name.drop_front();
File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, Name,
Targets);
}
for (auto &Symbol : Section.WeakDefSymbols)
File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets,
SymbolFlags::WeakDefined);
for (auto &Symbol : Section.TLVSymbols)
File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets,
SymbolFlags::ThreadLocalValue);
}
for (const auto &Section : Undefineds) {
const auto Targets =
synthesizeTargets(Section.Architectures, Platforms);
for (auto &Symbol : Section.Symbols) {
if (Ctx->FileKind != FileType::TBD_V3 &&
Symbol.value.startswith("_OBJC_EHTYPE_$_"))
File->addSymbol(SymbolKind::ObjectiveCClassEHType,
Symbol.value.drop_front(15), Targets,
SymbolFlags::Undefined);
else
File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets,
SymbolFlags::Undefined);
}
for (auto &Symbol : Section.Classes) {
auto Name = Symbol.value;
if (Ctx->FileKind != FileType::TBD_V3)
Name = Name.drop_front();
File->addSymbol(SymbolKind::ObjectiveCClass, Name, Targets,
SymbolFlags::Undefined);
}
for (auto &Symbol : Section.ClassEHs)
File->addSymbol(SymbolKind::ObjectiveCClassEHType, Symbol, Targets,
SymbolFlags::Undefined);
for (auto &Symbol : Section.IVars) {
auto Name = Symbol.value;
if (Ctx->FileKind != FileType::TBD_V3)
Name = Name.drop_front();
File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, Name, Targets,
SymbolFlags::Undefined);
}
for (auto &Symbol : Section.WeakRefSymbols)
File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets,
SymbolFlags::Undefined | SymbolFlags::WeakReferenced);
}
return File;
}
llvm::BumpPtrAllocator Allocator;
StringRef copyString(StringRef String) {
if (String.empty())
return {};
void *Ptr = Allocator.Allocate(String.size(), 1);
memcpy(Ptr, String.data(), String.size());
return StringRef(reinterpret_cast<const char *>(Ptr), String.size());
}
std::vector<Architecture> Architectures;
std::vector<UUID> UUIDs;
PlatformSet Platforms;
StringRef InstallName;
PackedVersion CurrentVersion;
PackedVersion CompatibilityVersion;
SwiftVersion SwiftABIVersion{0};
ObjCConstraintType ObjCConstraint{ObjCConstraintType::None};
TBDFlags Flags{TBDFlags::None};
StringRef ParentUmbrella;
std::vector<ExportSection> Exports;
std::vector<UndefinedSection> Undefineds;
};
static void setFileTypeForInput(TextAPIContext *Ctx, IO &IO) {
if (IO.mapTag("!tapi-tbd", false))
Ctx->FileKind = FileType::TBD_V4;
else if (IO.mapTag("!tapi-tbd-v3", false))
Ctx->FileKind = FileType::TBD_V3;
else if (IO.mapTag("!tapi-tbd-v2", false))
Ctx->FileKind = FileType::TBD_V2;
else if (IO.mapTag("!tapi-tbd-v1", false) ||
IO.mapTag("tag:yaml.org,2002:map", false))
Ctx->FileKind = FileType::TBD_V1;
else {
Ctx->FileKind = FileType::Invalid;
return;
}
}
static void mapping(IO &IO, const InterfaceFile *&File) {
auto *Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext());
assert((!Ctx || !IO.outputting() ||
(Ctx && Ctx->FileKind != FileType::Invalid)) &&
"File type is not set in YAML context");
if (!IO.outputting()) {
setFileTypeForInput(Ctx, IO);
switch (Ctx->FileKind) {
default:
break;
case FileType::TBD_V4:
mapKeysToValuesV4(IO, File);
return;
case FileType::Invalid:
IO.setError("unsupported file type");
return;
}
} else {
// Set file type when writing.
switch (Ctx->FileKind) {
default:
llvm_unreachable("unexpected file type");
case FileType::TBD_V4:
mapKeysToValuesV4(IO, File);
return;
case FileType::TBD_V3:
IO.mapTag("!tapi-tbd-v3", true);
break;
case FileType::TBD_V2:
IO.mapTag("!tapi-tbd-v2", true);
break;
case FileType::TBD_V1:
// Don't write the tag into the .tbd file for TBD v1
break;
}
}
mapKeysToValues(Ctx->FileKind, IO, File);
}
using SectionList = std::vector<SymbolSection>;
struct NormalizedTBD_V4 {
explicit NormalizedTBD_V4(IO &IO) {}
NormalizedTBD_V4(IO &IO, const InterfaceFile *&File) {
auto Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext());
assert(Ctx);
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();
SwiftABIVersion = File->getSwiftABIVersion();
Flags = TBDFlags::None;
if (!File->isApplicationExtensionSafe())
Flags |= TBDFlags::NotApplicationExtensionSafe;
if (!File->isTwoLevelNamespace())
Flags |= TBDFlags::FlatNamespace;
if (File->isInstallAPI())
Flags |= TBDFlags::InstallAPI;
{
std::map<std::string, TargetList> valueToTargetList;
for (const auto &it : File->umbrellas())
valueToTargetList[it.second].emplace_back(it.first);
for (const auto &it : valueToTargetList) {
UmbrellaSection CurrentSection;
CurrentSection.Targets.insert(CurrentSection.Targets.begin(),
it.second.begin(), it.second.end());
CurrentSection.Umbrella = it.first;
ParentUmbrellas.emplace_back(std::move(CurrentSection));
}
}
assignTargetsToLibrary(File->allowableClients(), AllowableClients);
assignTargetsToLibrary(File->reexportedLibraries(), ReexportedLibraries);
auto handleSymbols =
[](SectionList &CurrentSections,
InterfaceFile::const_filtered_symbol_range Symbols,
std::function<bool(const Symbol *)> Pred) {
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));
}
for (const auto &TargetIDs : TargetSet) {
SymbolSection CurrentSection;
CurrentSection.Targets.insert(CurrentSection.Targets.begin(),
TargetIDs.begin(), TargetIDs.end());
for (const auto &IT : SymbolToTargetList) {
if (IT.second != TargetIDs)
continue;
const auto *Symbol = IT.first;
switch (Symbol->getKind()) {
case SymbolKind::GlobalSymbol:
if (Symbol->isWeakDefined())
CurrentSection.WeakSymbols.emplace_back(Symbol->getName());
else if (Symbol->isThreadLocalValue())
CurrentSection.TlvSymbols.emplace_back(Symbol->getName());
else
CurrentSection.Symbols.emplace_back(Symbol->getName());
break;
case SymbolKind::ObjectiveCClass:
CurrentSection.Classes.emplace_back(Symbol->getName());
break;
case SymbolKind::ObjectiveCClassEHType:
CurrentSection.ClassEHs.emplace_back(Symbol->getName());
break;
case SymbolKind::ObjectiveCInstanceVariable:
CurrentSection.Ivars.emplace_back(Symbol->getName());
break;
}
}
sort(CurrentSection.Symbols);
sort(CurrentSection.Classes);
sort(CurrentSection.ClassEHs);
sort(CurrentSection.Ivars);
sort(CurrentSection.WeakSymbols);
sort(CurrentSection.TlvSymbols);
CurrentSections.emplace_back(std::move(CurrentSection));
}
};
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; });
}
const InterfaceFile *denormalize(IO &IO) {
auto Ctx = reinterpret_cast<TextAPIContext *>(IO.getContext());
assert(Ctx);
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);
File->setCompatibilityVersion(CompatibilityVersion);
File->setSwiftABIVersion(SwiftABIVersion);
for (const auto &CurrentSection : ParentUmbrellas)
for (const auto &target : CurrentSection.Targets)
File->addParentUmbrella(target, CurrentSection.Umbrella);
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)
for (const auto &Target : CurrentSection.Targets)
File->addAllowableClient(lib, Target);
}
for (const auto &CurrentSection : ReexportedLibraries) {
for (const auto &Lib : CurrentSection.Values)
for (const auto &Target : CurrentSection.Targets)
File->addReexportedLibrary(Lib, Target);
}
auto handleSymbols = [File](const SectionList &CurrentSections,
SymbolFlags Flag = SymbolFlags::None) {
for (const auto &CurrentSection : CurrentSections) {
for (auto &sym : CurrentSection.Symbols)
File->addSymbol(SymbolKind::GlobalSymbol, sym,
CurrentSection.Targets, Flag);
for (auto &sym : CurrentSection.Classes)
File->addSymbol(SymbolKind::ObjectiveCClass, sym,
CurrentSection.Targets);
for (auto &sym : CurrentSection.ClassEHs)
File->addSymbol(SymbolKind::ObjectiveCClassEHType, sym,
CurrentSection.Targets);
for (auto &sym : CurrentSection.Ivars)
File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, sym,
CurrentSection.Targets);
for (auto &sym : CurrentSection.WeakSymbols)
File->addSymbol(SymbolKind::GlobalSymbol, sym,
CurrentSection.Targets, SymbolFlags::WeakDefined);
for (auto &sym : CurrentSection.TlvSymbols)
File->addSymbol(SymbolKind::GlobalSymbol, sym,
CurrentSection.Targets,
SymbolFlags::ThreadLocalValue);
}
};
handleSymbols(Exports);
handleSymbols(Reexports, SymbolFlags::Rexported);
handleSymbols(Undefineds, SymbolFlags::Undefined);
return File;
}
unsigned TBDVersion;
std::vector<UUIDv4> UUIDs;
TargetList Targets;
StringRef InstallName;
PackedVersion CurrentVersion;
PackedVersion CompatibilityVersion;
SwiftVersion SwiftABIVersion{0};
std::vector<MetadataSection> AllowableClients;
std::vector<MetadataSection> ReexportedLibraries;
TBDFlags Flags{TBDFlags::None};
std::vector<UmbrellaSection> ParentUmbrellas;
SectionList Exports;
SectionList Reexports;
SectionList Undefineds;
private:
void assignTargetsToLibrary(const std::vector<InterfaceFileRef> &Libraries,
std::vector<MetadataSection> &Section) {
std::set<TargetList> targetSet;
std::map<const InterfaceFileRef *, TargetList> valueToTargetList;
for (const auto &library : Libraries) {
TargetList targets(library.targets());
valueToTargetList[&library] = targets;
targetSet.emplace(std::move(targets));
}
for (const auto &targets : targetSet) {
MetadataSection CurrentSection;
CurrentSection.Targets.insert(CurrentSection.Targets.begin(),
targets.begin(), targets.end());
for (const auto &it : valueToTargetList) {
if (it.second != targets)
continue;
CurrentSection.Values.emplace_back(it.first->getInstallName());
}
llvm::sort(CurrentSection.Values);
Section.emplace_back(std::move(CurrentSection));
}
}
};
static void mapKeysToValues(FileType FileKind, IO &IO,
const InterfaceFile *&File) {
MappingNormalization<NormalizedTBD, const InterfaceFile *> Keys(IO, File);
IO.mapRequired("archs", Keys->Architectures);
if (FileKind != FileType::TBD_V1)
IO.mapOptional("uuids", Keys->UUIDs);
IO.mapRequired("platform", Keys->Platforms);
if (FileKind != FileType::TBD_V1)
IO.mapOptional("flags", Keys->Flags, TBDFlags::None);
IO.mapRequired("install-name", Keys->InstallName);
IO.mapOptional("current-version", Keys->CurrentVersion,
PackedVersion(1, 0, 0));
IO.mapOptional("compatibility-version", Keys->CompatibilityVersion,
PackedVersion(1, 0, 0));
if (FileKind != FileType::TBD_V3)
IO.mapOptional("swift-version", Keys->SwiftABIVersion, SwiftVersion(0));
else
IO.mapOptional("swift-abi-version", Keys->SwiftABIVersion,
SwiftVersion(0));
IO.mapOptional("objc-constraint", Keys->ObjCConstraint,
(FileKind == FileType::TBD_V1)
? ObjCConstraintType::None
: ObjCConstraintType::Retain_Release);
if (FileKind != FileType::TBD_V1)
IO.mapOptional("parent-umbrella", Keys->ParentUmbrella, StringRef());
IO.mapOptional("exports", Keys->Exports);
if (FileKind != FileType::TBD_V1)
IO.mapOptional("undefineds", Keys->Undefineds);
}
static void mapKeysToValuesV4(IO &IO, const InterfaceFile *&File) {
MappingNormalization<NormalizedTBD_V4, const InterfaceFile *> Keys(IO,
File);
IO.mapTag("!tapi-tbd", true);
IO.mapRequired("tbd-version", Keys->TBDVersion);
IO.mapRequired("targets", Keys->Targets);
IO.mapOptional("uuids", Keys->UUIDs);
IO.mapOptional("flags", Keys->Flags, TBDFlags::None);
IO.mapRequired("install-name", Keys->InstallName);
IO.mapOptional("current-version", Keys->CurrentVersion,
PackedVersion(1, 0, 0));
IO.mapOptional("compatibility-version", Keys->CompatibilityVersion,
PackedVersion(1, 0, 0));
IO.mapOptional("swift-abi-version", Keys->SwiftABIVersion, SwiftVersion(0));
IO.mapOptional("parent-umbrella", Keys->ParentUmbrellas);
auto OptionKind = MetadataSection::Option::Clients;
IO.mapOptionalWithContext("allowable-clients", Keys->AllowableClients,
OptionKind);
OptionKind = MetadataSection::Option::Libraries;
IO.mapOptionalWithContext("reexported-libraries", Keys->ReexportedLibraries,
OptionKind);
IO.mapOptional("exports", Keys->Exports);
IO.mapOptional("reexports", Keys->Reexports);
IO.mapOptional("undefineds", Keys->Undefineds);
}
};
template <>
struct DocumentListTraits<std::vector<const MachO::InterfaceFile *>> {
static size_t size(IO &IO, std::vector<const MachO::InterfaceFile *> &Seq) {
return Seq.size();
}
static const InterfaceFile *&
element(IO &IO, std::vector<const InterfaceFile *> &Seq, size_t Index) {
if (Index >= Seq.size())
Seq.resize(Index + 1);
return Seq[Index];
}
};
} // end namespace yaml.
} // namespace llvm
static void DiagHandler(const SMDiagnostic &Diag, void *Context) {
auto *File = static_cast<TextAPIContext *>(Context);
SmallString<1024> Message;
raw_svector_ostream S(Message);
SMDiagnostic NewDiag(*Diag.getSourceMgr(), Diag.getLoc(), File->Path,
Diag.getLineNo(), Diag.getColumnNo(), Diag.getKind(),
Diag.getMessage(), Diag.getLineContents(),
Diag.getRanges(), Diag.getFixIts());
NewDiag.print(nullptr, S);
File->ErrorMessage = ("malformed file\n" + Message).str();
}
Expected<std::unique_ptr<InterfaceFile>>
TextAPIReader::get(MemoryBufferRef InputBuffer) {
TextAPIContext Ctx;
Ctx.Path = std::string(InputBuffer.getBufferIdentifier());
yaml::Input YAMLIn(InputBuffer.getBuffer(), &Ctx, DiagHandler, &Ctx);
// Fill vector with interface file objects created by parsing the YAML file.
std::vector<const InterfaceFile *> Files;
YAMLIn >> Files;
// YAMLIn dynamically allocates for Interface file and in case of error,
// memory leak will occur unless wrapped around unique_ptr
auto File = std::unique_ptr<InterfaceFile>(
const_cast<InterfaceFile *>(Files.front()));
for (const InterfaceFile *FI : llvm::drop_begin(Files))
File->addDocument(
std::shared_ptr<InterfaceFile>(const_cast<InterfaceFile *>(FI)));
if (YAMLIn.error())
return make_error<StringError>(Ctx.ErrorMessage, YAMLIn.error());
return std::move(File);
}
Error TextAPIWriter::writeToStream(raw_ostream &OS, const InterfaceFile &File) {
TextAPIContext Ctx;
Ctx.Path = std::string(File.getPath());
Ctx.FileKind = File.getFileType();
llvm::yaml::Output YAMLOut(OS, &Ctx, /*WrapColumn=*/80);
std::vector<const InterfaceFile *> Files;
Files.emplace_back(&File);
for (auto Document : File.documents())
Files.emplace_back(Document.get());
// Stream out yaml.
YAMLOut << Files;
return Error::success();
}