//===- llvm/TextAPI/TextStub.cpp - Text Stub --------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief 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/MachO/Architecture.h" #include "llvm/TextAPI/MachO/ArchitectureSet.h" #include "llvm/TextAPI/MachO/InterfaceFile.h" #include "llvm/TextAPI/MachO/PackedVersion.h" #include "llvm/TextAPI/MachO/TextAPIReader.h" #include "llvm/TextAPI/MachO/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 */ // 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; }; // 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) namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &IO, ExportSection &Section) { const auto *Ctx = reinterpret_cast(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 { static void mapping(IO &IO, UndefinedSection &Section) { const auto *Ctx = reinterpret_cast(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 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); } }; template <> struct MappingTraits { struct NormalizedTBD { explicit NormalizedTBD(IO &IO) {} NormalizedTBD(IO &IO, const InterfaceFile *&File) { Architectures = File->getArchitectures(); UUIDs = File->uuids(); Platform = File->getPlatform(); 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; ParentUmbrella = File->getParentUmbrella(); 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->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.begin(), Section.Symbols.end()); llvm::sort(Section.Classes.begin(), Section.Classes.end()); llvm::sort(Section.ClassEHs.begin(), Section.ClassEHs.end()); llvm::sort(Section.IVars.begin(), Section.IVars.end()); llvm::sort(Section.WeakDefSymbols.begin(), Section.WeakDefSymbols.end()); llvm::sort(Section.TLVSymbols.begin(), Section.TLVSymbols.end()); 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.begin(), Section.Symbols.end()); llvm::sort(Section.Classes.begin(), Section.Classes.end()); llvm::sort(Section.ClassEHs.begin(), Section.ClassEHs.end()); llvm::sort(Section.IVars.begin(), Section.IVars.end()); llvm::sort(Section.WeakRefSymbols.begin(), Section.WeakRefSymbols.end()); Undefineds.emplace_back(std::move(Section)); } } 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); for (auto &ID : UUIDs) File->addUUID(ID.first, ID.second); File->setPlatform(Platform); File->setArchitectures(Architectures); File->setInstallName(InstallName); File->setCurrentVersion(CurrentVersion); File->setCompatibilityVersion(CompatibilityVersion); File->setSwiftABIVersion(SwiftABIVersion); File->setObjCConstraint(ObjCConstraint); File->setParentUmbrella(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) { for (const auto &Library : Section.AllowableClients) File->addAllowableClient(Library, Section.Architectures); for (const auto &Library : Section.ReexportedLibraries) File->addReexportedLibrary(Library, Section.Architectures); 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), Section.Architectures); else File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Section.Architectures); } for (auto &Symbol : Section.Classes) { auto Name = Symbol.value; if (Ctx->FileKind != FileType::TBD_V3) Name = Name.drop_front(); File->addSymbol(SymbolKind::ObjectiveCClass, Name, Section.Architectures); } for (auto &Symbol : Section.ClassEHs) File->addSymbol(SymbolKind::ObjectiveCClassEHType, Symbol, Section.Architectures); for (auto &Symbol : Section.IVars) { auto Name = Symbol.value; if (Ctx->FileKind != FileType::TBD_V3) Name = Name.drop_front(); File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, Name, Section.Architectures); } for (auto &Symbol : Section.WeakDefSymbols) File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Section.Architectures, SymbolFlags::WeakDefined); for (auto &Symbol : Section.TLVSymbols) File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Section.Architectures, SymbolFlags::ThreadLocalValue); } for (const auto &Section : Undefineds) { 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), Section.Architectures, SymbolFlags::Undefined); else File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Section.Architectures, 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, Section.Architectures, SymbolFlags::Undefined); } for (auto &Symbol : Section.ClassEHs) File->addSymbol(SymbolKind::ObjectiveCClassEHType, Symbol, Section.Architectures, 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, Section.Architectures, SymbolFlags::Undefined); } for (auto &Symbol : Section.WeakRefSymbols) File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Section.Architectures, 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(Ptr), String.size()); } std::vector Architectures; std::vector UUIDs; PlatformType Platform; StringRef InstallName; PackedVersion CurrentVersion; PackedVersion CompatibilityVersion; SwiftVersion SwiftABIVersion; ObjCConstraintType ObjCConstraint{ObjCConstraintType::None}; TBDFlags Flags{TBDFlags::None}; StringRef ParentUmbrella; std::vector Exports; std::vector Undefineds; }; 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"); MappingNormalization Keys(IO, File); // prope file type when reading. if (!IO.outputting()) { if (IO.mapTag("!tapi-tbd-v2", false)) Ctx->FileKind = FileType::TBD_V2; else if (IO.mapTag("!tapi-tbd-v3", 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 { IO.setError("unsupported file type"); return; } } // Set file tyoe when writing. if (IO.outputting()) { switch (Ctx->FileKind) { default: llvm_unreachable("unexpected file type"); case FileType::TBD_V1: // Don't write the tag into the .tbd file for TBD v1. break; case FileType::TBD_V2: IO.mapTag("!tapi-tbd-v2", true); break; case FileType::TBD_V3: IO.mapTag("!tapi-tbd-v3", true); break; } } IO.mapRequired("archs", Keys->Architectures); if (Ctx->FileKind != FileType::TBD_V1) IO.mapOptional("uuids", Keys->UUIDs); IO.mapRequired("platform", Keys->Platform); if (Ctx->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 (Ctx->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, (Ctx->FileKind == FileType::TBD_V1) ? ObjCConstraintType::None : ObjCConstraintType::Retain_Release); if (Ctx->FileKind != FileType::TBD_V1) IO.mapOptional("parent-umbrella", Keys->ParentUmbrella, StringRef()); IO.mapOptional("exports", Keys->Exports); if (Ctx->FileKind != FileType::TBD_V1) 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 MachO { 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::get(std::unique_ptr InputBuffer) { TextAPIContext Ctx; Ctx.Path = InputBuffer->getBufferIdentifier(); 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; if (YAMLIn.error()) return make_error(Ctx.ErrorMessage, YAMLIn.error()); auto *File = const_cast(Files.front()); return std::unique_ptr(File); } Error TextAPIWriter::writeToStream(raw_ostream &OS, const InterfaceFile &File) { TextAPIContext Ctx; Ctx.Path = File.getPath(); Ctx.FileKind = File.getFileType(); llvm::yaml::Output YAMLOut(OS, &Ctx, /*WrapColumn=*/80); std::vector Files; Files.emplace_back(&File); // Stream out yaml. YAMLOut << Files; return Error::success(); } } // end namespace MachO. } // end namespace llvm.