//===- 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 #include // 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 Architectures; std::vector AllowableClients; std::vector ReexportedLibraries; std::vector Symbols; std::vector Classes; std::vector ClassEHs; std::vector IVars; std::vector WeakDefSymbols; std::vector TLVSymbols; }; struct UndefinedSection { std::vector Architectures; std::vector Symbols; std::vector Classes; std::vector ClassEHs; std::vector IVars; std::vector WeakRefSymbols; }; // Sections for direct target mapping in TBDv4 struct SymbolSection { TargetList Targets; std::vector Symbols; std::vector Classes; std::vector ClassEHs; std::vector Ivars; std::vector WeakSymbols; std::vector TlvSymbols; }; struct MetadataSection { enum Option { Clients, Libraries }; std::vector Targets; std::vector Values; }; struct UmbrellaSection { std::vector 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) {} }; } // 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 { static void mapping(IO &IO, ExportSection &Section) { const auto *Ctx = reinterpret_cast(IO.getContext()); assert((!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 { static void mapping(IO &IO, UndefinedSection &Section) { const auto *Ctx = reinterpret_cast(IO.getContext()); assert((!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 { 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 { static void mapping(IO &IO, UmbrellaSection &Section) { IO.mapRequired("targets", Section.Targets); IO.mapRequired("umbrella", Section.Umbrella); } }; template <> struct MappingTraits { static void mapping(IO &IO, UUIDv4 &UUID) { IO.mapRequired("target", UUID.TargetID); IO.mapRequired("value", UUID.Value); } }; template <> struct MappingContextTraits { 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 { 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); IO.bitSetCase(Flags, "not_for_dyld_shared_cache", TBDFlags::OSLibNotForSharedCache); } }; template <> struct ScalarTraits { static void output(const Target &Value, void *, raw_ostream &OS) { OS << Value.Arch << "-"; switch (Value.Platform) { #define PLATFORM(platform, id, name, build_name, target, tapi_target, \ marketing) \ case PLATFORM_##platform: \ OS << #tapi_target; \ break; #include "llvm/BinaryFormat/MachO.def" } } 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 { struct NormalizedTBD { explicit NormalizedTBD(IO &IO) {} NormalizedTBD(IO &IO, const InterfaceFile *&File) { Architectures = File->getArchitectures(); 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->umbrellas().empty()) ParentUmbrella = File->umbrellas().begin()->second; std::set ArchSet; for (const auto &Library : File->allowableClients()) ArchSet.insert(Library.getArchitectures()); for (const auto &Library : File->reexportedLibraries()) ArchSet.insert(Library.getArchitectures()); std::map SymbolToArchSet; for (const auto *Symbol : File->symbols()) { 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 EncodeKind::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 EncodeKind::ObjectiveCClass: if (File->getFileType() != FileType::TBD_V3) Section.Classes.emplace_back( copyString("_" + Symbol->getName().str())); else Section.Classes.emplace_back(Symbol->getName()); break; case EncodeKind::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 EncodeKind::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 EncodeKind::GlobalSymbol: if (Symbol->isWeakReferenced()) Section.WeakRefSymbols.emplace_back(Symbol->getName()); else Section.Symbols.emplace_back(Symbol->getName()); break; case EncodeKind::ObjectiveCClass: if (File->getFileType() != FileType::TBD_V3) Section.Classes.emplace_back( copyString("_" + Symbol->getName().str())); else Section.Classes.emplace_back(Symbol->getName()); break; case EncodeKind::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 EncodeKind::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(IO.getContext()); assert(Ctx); auto *File = new InterfaceFile; File->setPath(Ctx->Path); File->setFileType(Ctx->FileKind); File->addTargets(synthesizeTargets(Architectures, Platforms)); 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)); } // For older file formats, the segment where the symbol // comes from is unknown, treat all symbols as Data // in these cases. const auto Flags = SymbolFlags::Data; 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.starts_with(ObjC2EHTypePrefix)) File->addSymbol(EncodeKind::ObjectiveCClassEHType, Symbol.value.drop_front(15), Targets, Flags); else File->addSymbol(EncodeKind::GlobalSymbol, Symbol, Targets, Flags); } for (auto &Symbol : Section.Classes) { auto Name = Symbol.value; if (Ctx->FileKind != FileType::TBD_V3) Name = Name.drop_front(); File->addSymbol(EncodeKind::ObjectiveCClass, Name, Targets, Flags); } for (auto &Symbol : Section.ClassEHs) File->addSymbol(EncodeKind::ObjectiveCClassEHType, Symbol, Targets, Flags); for (auto &Symbol : Section.IVars) { auto Name = Symbol.value; if (Ctx->FileKind != FileType::TBD_V3) Name = Name.drop_front(); File->addSymbol(EncodeKind::ObjectiveCInstanceVariable, Name, Targets, Flags); } for (auto &Symbol : Section.WeakDefSymbols) File->addSymbol(EncodeKind::GlobalSymbol, Symbol, Targets, SymbolFlags::WeakDefined | Flags); for (auto &Symbol : Section.TLVSymbols) File->addSymbol(EncodeKind::GlobalSymbol, Symbol, Targets, SymbolFlags::ThreadLocalValue | Flags); } 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.starts_with(ObjC2EHTypePrefix)) File->addSymbol(EncodeKind::ObjectiveCClassEHType, Symbol.value.drop_front(15), Targets, SymbolFlags::Undefined | Flags); else File->addSymbol(EncodeKind::GlobalSymbol, Symbol, Targets, SymbolFlags::Undefined | Flags); } for (auto &Symbol : Section.Classes) { auto Name = Symbol.value; if (Ctx->FileKind != FileType::TBD_V3) Name = Name.drop_front(); File->addSymbol(EncodeKind::ObjectiveCClass, Name, Targets, SymbolFlags::Undefined | Flags); } for (auto &Symbol : Section.ClassEHs) File->addSymbol(EncodeKind::ObjectiveCClassEHType, Symbol, Targets, SymbolFlags::Undefined | Flags); for (auto &Symbol : Section.IVars) { auto Name = Symbol.value; if (Ctx->FileKind != FileType::TBD_V3) Name = Name.drop_front(); File->addSymbol(EncodeKind::ObjectiveCInstanceVariable, Name, Targets, SymbolFlags::Undefined | Flags); } for (auto &Symbol : Section.WeakRefSymbols) File->addSymbol(EncodeKind::GlobalSymbol, Symbol, Targets, SymbolFlags::Undefined | SymbolFlags::WeakReferenced | Flags); } 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(Ptr), String.size()); } std::vector Architectures; std::vector UUIDs; PlatformSet Platforms; StringRef InstallName; PackedVersion CurrentVersion; PackedVersion CompatibilityVersion; SwiftVersion SwiftABIVersion{0}; ObjCConstraintType ObjCConstraint{ObjCConstraintType::None}; TBDFlags Flags{TBDFlags::None}; StringRef ParentUmbrella; std::vector Exports; std::vector 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(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; struct NormalizedTBD_V4 { explicit NormalizedTBD_V4(IO &IO) {} NormalizedTBD_V4(IO &IO, const InterfaceFile *&File) { auto Ctx = reinterpret_cast(IO.getContext()); assert(Ctx); TBDVersion = Ctx->FileKind >> 4; Targets.insert(Targets.begin(), File->targets().begin(), File->targets().end()); 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->isOSLibNotForSharedCache()) Flags |= TBDFlags::OSLibNotForSharedCache; { std::map 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::set TargetSet; std::map SymbolToTargetList; for (const auto *Symbol : Symbols) { 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 EncodeKind::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 EncodeKind::ObjectiveCClass: CurrentSection.Classes.emplace_back(Symbol->getName()); break; case EncodeKind::ObjectiveCClassEHType: CurrentSection.ClassEHs.emplace_back(Symbol->getName()); break; case EncodeKind::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()); handleSymbols(Reexports, File->reexports()); handleSymbols(Undefineds, File->undefineds()); } const InterfaceFile *denormalize(IO &IO) { auto Ctx = reinterpret_cast(IO.getContext()); assert(Ctx); auto *File = new InterfaceFile; File->setPath(Ctx->Path); File->setFileType(Ctx->FileKind); 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->setOSLibNotForSharedCache( (Flags & TBDFlags::OSLibNotForSharedCache)); 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 InputFlag = SymbolFlags::None) { // For older file formats, the segment where the symbol // comes from is unknown, treat all symbols as Data // in these cases. const SymbolFlags Flag = InputFlag | SymbolFlags::Data; for (const auto &CurrentSection : CurrentSections) { for (auto &sym : CurrentSection.Symbols) File->addSymbol(EncodeKind::GlobalSymbol, sym, CurrentSection.Targets, Flag); for (auto &sym : CurrentSection.Classes) File->addSymbol(EncodeKind::ObjectiveCClass, sym, CurrentSection.Targets, Flag); for (auto &sym : CurrentSection.ClassEHs) File->addSymbol(EncodeKind::ObjectiveCClassEHType, sym, CurrentSection.Targets, Flag); for (auto &sym : CurrentSection.Ivars) File->addSymbol(EncodeKind::ObjectiveCInstanceVariable, sym, CurrentSection.Targets, Flag); SymbolFlags SymFlag = ((Flag & SymbolFlags::Undefined) == SymbolFlags::Undefined) ? SymbolFlags::WeakReferenced : SymbolFlags::WeakDefined; for (auto &sym : CurrentSection.WeakSymbols) { File->addSymbol(EncodeKind::GlobalSymbol, sym, CurrentSection.Targets, Flag | SymFlag); } for (auto &sym : CurrentSection.TlvSymbols) File->addSymbol(EncodeKind::GlobalSymbol, sym, CurrentSection.Targets, Flag | SymbolFlags::ThreadLocalValue); } }; handleSymbols(Exports); handleSymbols(Reexports, SymbolFlags::Rexported); handleSymbols(Undefineds, SymbolFlags::Undefined); return File; } unsigned TBDVersion; std::vector UUIDs; TargetList Targets; StringRef InstallName; PackedVersion CurrentVersion; PackedVersion CompatibilityVersion; SwiftVersion SwiftABIVersion{0}; std::vector AllowableClients; std::vector ReexportedLibraries; TBDFlags Flags{TBDFlags::None}; std::vector ParentUmbrellas; SectionList Exports; SectionList Reexports; SectionList Undefineds; private: void assignTargetsToLibrary(const std::vector &Libraries, std::vector &Section) { std::set targetSet; std::map 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 Keys(IO, File); std::vector EmptyUUID; IO.mapRequired("archs", Keys->Architectures); if (FileKind != FileType::TBD_V1) IO.mapOptional("uuids", EmptyUUID); 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 Keys(IO, File); std::vector EmptyUUID; IO.mapTag("!tapi-tbd", true); IO.mapRequired("tbd-version", Keys->TBDVersion); IO.mapRequired("targets", Keys->Targets); IO.mapOptional("uuids", EmptyUUID); 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> { static size_t size(IO &IO, std::vector &Seq) { return Seq.size(); } static const InterfaceFile *& element(IO &IO, std::vector &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(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 TextAPIReader::canRead(MemoryBufferRef InputBuffer) { auto TAPIFile = InputBuffer.getBuffer().trim(); if (TAPIFile.starts_with("{") && TAPIFile.ends_with("}")) return FileType::TBD_V5; if (!TAPIFile.ends_with("...")) return createStringError(std::errc::not_supported, "unsupported file type"); if (TAPIFile.starts_with("--- !tapi-tbd")) return FileType::TBD_V4; if (TAPIFile.starts_with("--- !tapi-tbd-v3")) return FileType::TBD_V3; if (TAPIFile.starts_with("--- !tapi-tbd-v2")) return FileType::TBD_V2; if (TAPIFile.starts_with("--- !tapi-tbd-v1") || TAPIFile.starts_with("---\narchs:")) return FileType::TBD_V1; return createStringError(std::errc::not_supported, "unsupported file type"); } Expected> TextAPIReader::get(MemoryBufferRef InputBuffer) { TextAPIContext Ctx; Ctx.Path = std::string(InputBuffer.getBufferIdentifier()); if (auto FTOrErr = canRead(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(); (*FileOrErr)->setPath(Ctx.Path); return std::move(*FileOrErr); } yaml::Input YAMLIn(InputBuffer.getBuffer(), &Ctx, DiagHandler, &Ctx); // Fill vector with interface file objects created by parsing the YAML file. std::vector 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( const_cast(Files.front())); for (const InterfaceFile *FI : llvm::drop_begin(Files)) File->addDocument( std::shared_ptr(const_cast(FI))); if (YAMLIn.error()) return make_error(Ctx.ErrorMessage, YAMLIn.error()); return std::move(File); } Error TextAPIWriter::writeToStream(raw_ostream &OS, const InterfaceFile &File, const FileType FileKind, bool Compact) { TextAPIContext Ctx; Ctx.Path = std::string(File.getPath()); // Prefer parameter for format if passed, otherwise fallback to the File // FileType. Ctx.FileKind = (FileKind == FileType::Invalid) ? File.getFileType() : FileKind; // Write out in JSON format. if (Ctx.FileKind >= FileType::TBD_V5) { return serializeInterfaceFileToJSON(OS, File, Ctx.FileKind, Compact); } llvm::yaml::Output YAMLOut(OS, &Ctx, /*WrapColumn=*/80); std::vector Files; Files.emplace_back(&File); for (const auto &Document : File.documents()) Files.emplace_back(Document.get()); // Stream out yaml. YAMLOut << Files; return Error::success(); }