Index: llvm/include/llvm/TextAPI/Architecture.h =================================================================== --- /dev/null +++ llvm/include/llvm/TextAPI/Architecture.h @@ -0,0 +1,47 @@ +//===- llvm/TextAPI/Architecture.h - Architecture ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines the architecture enum and helper methods. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TEXTAPI_ARCHITECTURE_H +#define LLVM_TEXTAPI_ARCHITECTURE_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +/// Defines the architecture slices that are supported by Text-based Stub files. +enum class Architecture : uint8_t { +#define ARCHINFO(Arch, Type, SubType) Arch, +#include "llvm/TextAPI/Architecture.def" +#undef ARCHINFO + unknown, // this has to go last. +}; + +/// Convert a CPU Type and Subtype pair to an architecture slice. +Architecture getArchitectureFromCpuType(uint32_t CPUType, uint32_t CPUSubType); + +/// Convert a name to an architecture slice. +Architecture getArchitectureFromName(StringRef Name); + +/// Convert an architecture slice to a string. +StringRef getArchitectureName(Architecture Arch); + +/// Convert an architecture slice to a CPU Type and Subtype pair. +std::pair getCPUTypeFromArchitecture(Architecture Arch); + +raw_ostream &operator<<(raw_ostream &OS, Architecture Arch); + +} // end namespace llvm. + +#endif // LLVM_TEXTAPI_ARCHITECTURE_H Index: llvm/include/llvm/TextAPI/Architecture.def =================================================================== --- /dev/null +++ llvm/include/llvm/TextAPI/Architecture.def @@ -0,0 +1,30 @@ +#ifndef ARCHINFO +#define ARCHINFO(arch) +#endif + +/// +/// X86 architectures sorted by cpu type and sub type id. +/// +ARCHINFO(i386, MachO::CPU_TYPE_I386, MachO::CPU_SUBTYPE_I386_ALL) +ARCHINFO(x86_64, MachO::CPU_TYPE_X86_64, MachO::CPU_SUBTYPE_X86_64_ALL) +ARCHINFO(x86_64h, MachO::CPU_TYPE_X86_64, MachO::CPU_SUBTYPE_X86_64_H) + + +/// +/// ARM architectures sorted by cpu sub type id. +/// +ARCHINFO(armv4t, MachO::CPU_TYPE_ARM, MachO::CPU_SUBTYPE_ARM_V4T) +ARCHINFO(armv6, MachO::CPU_TYPE_ARM, MachO::CPU_SUBTYPE_ARM_V6) +ARCHINFO(armv5, MachO::CPU_TYPE_ARM, MachO::CPU_SUBTYPE_ARM_V5TEJ) +ARCHINFO(armv7, MachO::CPU_TYPE_ARM, MachO::CPU_SUBTYPE_ARM_V7) +ARCHINFO(armv7s, MachO::CPU_TYPE_ARM, MachO::CPU_SUBTYPE_ARM_V7S) +ARCHINFO(armv7k, MachO::CPU_TYPE_ARM, MachO::CPU_SUBTYPE_ARM_V7K) +ARCHINFO(armv6m, MachO::CPU_TYPE_ARM, MachO::CPU_SUBTYPE_ARM_V6M) +ARCHINFO(armv7m, MachO::CPU_TYPE_ARM, MachO::CPU_SUBTYPE_ARM_V7M) +ARCHINFO(armv7em, MachO::CPU_TYPE_ARM, MachO::CPU_SUBTYPE_ARM_V7EM) + + +/// +/// ARM64 architectures sorted by cpu sub type id. +/// +ARCHINFO(arm64, MachO::CPU_TYPE_ARM64, MachO::CPU_SUBTYPE_ARM64_ALL) Index: llvm/include/llvm/TextAPI/ArchitectureSet.h =================================================================== --- /dev/null +++ llvm/include/llvm/TextAPI/ArchitectureSet.h @@ -0,0 +1,174 @@ +//===- llvm/TextAPI/ArchitectureSet.h - ArchitectureSet ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines the architecture set. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TEXTAPI_ARCHITECTURE_SET_H +#define LLVM_TEXTAPI_ARCHITECTURE_SET_H + +#include "llvm/Support/raw_ostream.h" +#include "llvm/TextAPI/Architecture.h" +#include +#include +#include +#include +#include + +namespace llvm { + +class ArchitectureSet { +private: + using ArchSetType = uint32_t; + + const static ArchSetType EndIndexVal = + std::numeric_limits::max(); + ArchSetType ArchSet{0}; + +public: + constexpr ArchitectureSet() = default; + ArchitectureSet(ArchSetType Raw) : ArchSet(Raw) {} + ArchitectureSet(Architecture Arch) : ArchitectureSet() { set(Arch); } + ArchitectureSet(const std::vector &Archs) : ArchitectureSet() { + for (auto Arch : Archs) { + if (Arch == Architecture::unknown) + continue; + set(Arch); + } + } + + void set(Architecture Arch) { + if (Arch == Architecture::unknown) + return; + ArchSet |= 1U << static_cast(Arch); + } + void clear(Architecture Arch) { ArchSet &= ~(1U << static_cast(Arch)); } + bool has(Architecture Arch) const { + return ArchSet & (1U << static_cast(Arch)); + } + bool contains(ArchitectureSet Archs) const { + return (ArchSet & Archs.ArchSet) == Archs.ArchSet; + } + + size_t count() const { + // popcnt + size_t Cnt = 0; + for (unsigned i = 0; i < sizeof(ArchSetType) * 8; ++i) + if (ArchSet & (1U << i)) + ++Cnt; + return Cnt; + } + + bool empty() const { return ArchSet == 0; } + + ArchSetType rawValue() const { return ArchSet; } + + template + class arch_iterator + : public std::iterator { + private: + ArchSetType Index; + Ty *ArchSet; + + void findNextSetBit() { + if (Index == EndIndexVal) + return; + + do { + if (*ArchSet & (1UL << ++Index)) + return; + } while (Index < sizeof(Ty) * 8); + + Index = EndIndexVal; + } + + public: + arch_iterator(Ty *ArchSet, ArchSetType Index = 0) + : Index(Index), ArchSet(ArchSet) { + if (Index != EndIndexVal && !(*ArchSet & (1UL << Index))) + findNextSetBit(); + } + + Architecture operator*() const { return static_cast(Index); } + + arch_iterator &operator++() { + findNextSetBit(); + return *this; + } + + arch_iterator operator++(int) { + auto tmp = *this; + findNextSetBit(); + return tmp; + } + + bool operator==(const arch_iterator &o) const { + return std::tie(Index, ArchSet) == std::tie(o.Index, o.ArchSet); + } + + bool operator!=(const arch_iterator &o) const { return !(*this == o); } + }; + + bool hasABICompatibleSlice(Architecture Arch) const; + Architecture getABICompatibleSlice(Architecture Arch) const; + + ArchitectureSet operator&(const ArchitectureSet &o) { + return {ArchSet & o.ArchSet}; + } + + ArchitectureSet operator|(const ArchitectureSet &o) { + return {ArchSet | o.ArchSet}; + } + + ArchitectureSet &operator|=(const ArchitectureSet &o) { + ArchSet |= o.ArchSet; + return *this; + } + + ArchitectureSet &operator|=(const Architecture &Arch) { + set(Arch); + return *this; + } + + bool operator==(const ArchitectureSet &o) const { + return ArchSet == o.ArchSet; + } + + bool operator!=(const ArchitectureSet &o) const { + return ArchSet != o.ArchSet; + } + + bool operator<(const ArchitectureSet &o) const { return ArchSet < o.ArchSet; } + + using iterator = arch_iterator; + using const_iterator = arch_iterator; + + iterator begin() { return {&ArchSet}; } + iterator end() { return {&ArchSet, EndIndexVal}; } + + const_iterator begin() const { return {&ArchSet}; } + const_iterator end() const { return {&ArchSet, EndIndexVal}; } + + operator std::string() const; + operator std::vector() const; + void print(raw_ostream &OS) const; +}; + +inline ArchitectureSet operator|(const Architecture &lhs, + const Architecture &rhs) { + return ArchitectureSet(lhs) | ArchitectureSet(rhs); +} + +raw_ostream &operator<<(raw_ostream &OS, ArchitectureSet Set); + +} // end namespace llvm. + +#endif // LLVM_TEXTAPI_ARCHITECTURE_SET_H Index: llvm/include/llvm/TextAPI/InterfaceFile.h =================================================================== --- /dev/null +++ llvm/include/llvm/TextAPI/InterfaceFile.h @@ -0,0 +1,432 @@ +//===- llvm/TextAPI/IntefaceFile.h - TAPI Interface File --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief A generic and abstract interface representation for linkable objects. +/// This could be an MachO executable, bundle, dylib, or text-based stub +/// file. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TEXTAPI_INTERFACE_FILE_H +#define LLVM_TEXTAPI_INTERFACE_FILE_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Error.h" +#include "llvm/TextAPI/Architecture.h" +#include "llvm/TextAPI/ArchitectureSet.h" +#include "llvm/TextAPI/PackedVersion.h" +#include "llvm/TextAPI/Symbol.h" + +namespace llvm { + +/// Defines the list of MachO platforms. +enum class Platform : unsigned { + unknown, + macOS = MachO::PLATFORM_MACOS, + iOS = MachO::PLATFORM_IOS, + tvOS = MachO::PLATFORM_TVOS, + watchOS = MachO::PLATFORM_WATCHOS, + bridgeOS = MachO::PLATFORM_BRIDGEOS, +}; + +/// Defines a list of Objective-C constraints. +enum class ObjCConstraint : unsigned { + /// No constraint. + None = 0, + + /// Retain/Release. + Retain_Release = 1, + + /// Retain/Release for Simulator. + Retain_Release_For_Simulator = 2, + + /// Retain/Release or Garbage Collection. + Retain_Release_Or_GC = 3, + + /// Garbage Collection. + GC = 4, +}; + +// clang-format off + +/// Defines the file type this file represents. +enum FileType : unsigned { + /// Invalid file type. + Invalid = 0U, + + /// Text-based stub file (.tbd) version 1.0 + TBD_V1 = 1U << 0, + + /// Text-based stub file (.tbd) version 2.0 + TBD_V2 = 1U << 1, + + /// Text-based stub file (.tbd) version 3.0 + TBD_V3 = 1U << 2, + + All = ~0U, +}; + +// clang-format on + +inline FileType operator&(const FileType lhs, const FileType rhs) { + return static_cast(static_cast(lhs) & + static_cast(rhs)); +} + +inline FileType operator|(const FileType lhs, const FileType rhs) { + return static_cast(static_cast(lhs) | + static_cast(rhs)); +} + +/// Reference to an interface file. +class InterfaceFileRef { +public: + InterfaceFileRef() = default; + + InterfaceFileRef(StringRef InstallName) : InstallName(InstallName) {} + + InterfaceFileRef(StringRef InstallName, ArchitectureSet Archs) + : InstallName(InstallName), Architectures(Archs) {} + + StringRef getInstallName() const { return InstallName; }; + void setArchitectures(ArchitectureSet Archs) { Architectures |= Archs; } + ArchitectureSet getArchitectures() const { return Architectures; } + bool hasArchitecture(Architecture Arch) const { + return Architectures.has(Arch); + } + + bool operator==(const InterfaceFileRef &O) const { + return std::tie(InstallName, Architectures) == + std::tie(O.InstallName, O.Architectures); + } + + bool operator<(const InterfaceFileRef &O) const { + return std::tie(InstallName, Architectures) < + std::tie(O.InstallName, O.Architectures); + } + +private: + std::string InstallName; + ArchitectureSet Architectures; +}; + +struct SymbolsMapKey { + SymbolKind Kind; + StringRef Name; + + SymbolsMapKey(SymbolKind Kind, StringRef Name) : Kind(Kind), Name(Name) {} +}; +template <> struct DenseMapInfo { + static inline SymbolsMapKey getEmptyKey() { + return SymbolsMapKey(SymbolKind::GlobalSymbol, StringRef{}); + } + + static inline SymbolsMapKey getTombstoneKey() { + return SymbolsMapKey(SymbolKind::ObjectiveCInstanceVariable, StringRef{}); + } + + static unsigned getHashValue(const SymbolsMapKey &Key) { + return hash_combine(hash_value(Key.Kind), hash_value(Key.Name)); + } + + static bool isEqual(const SymbolsMapKey &LHS, const SymbolsMapKey &RHS) { + return std::tie(LHS.Kind, LHS.Name) == std::tie(RHS.Kind, RHS.Name); + } +}; + +/// Defines the interface file. +class InterfaceFile { +public: + /// Set the path from which this file was generated (if applicable). + /// + /// \param Path_ The path to the source file. + void setPath(StringRef Path_) { Path = Path_; } + + /// Get the path from which this file was generated (if applicable). + /// + /// \return The path to the source file or empty. + StringRef getPath() const { return Path; } + + /// Set the file type. + /// + /// This is used by the YAML writter to identify the specification it should. + /// + /// \param Type The file type. + void setFileType(FileType Type) { FileType = Type; } + + /// Get the file type. + /// + /// \return The file type. + FileType getFileType() const { return FileType; } + + /// Set the platform. + void setPlatform(Platform Platform_) { Platform = Platform_; } + + /// Get the platform. + Platform getPlatform() const { return Platform; } + + /// Specify the set of supported architectures by this file. + void setArchitectures(ArchitectureSet Architectures_) { + Architectures |= Architectures_; + } + + /// Get the set of supported architectures. + void setArch(Architecture Arch) { Architectures.set(Arch); } + + /// Get the set of supported architectures. + ArchitectureSet getArchitectures() const { return Architectures; } + + /// Set the install name of the library. + void setInstallName(StringRef InstallName_) { InstallName = InstallName_; } + + /// Get the install name of the library. + StringRef getInstallName() const { return InstallName; } + + /// Set the current version of the library. + void setCurrentVersion(PackedVersion Version) { CurrentVersion = Version; } + + /// Get the current version of the library. + PackedVersion getCurrentVersion() const { return CurrentVersion; } + + /// Set the compatibility version of the library. + void setCompatibilityVersion(PackedVersion Version) { + CompatibilityVersion = Version; + } + + /// Get the compatibility version of the library. + PackedVersion getCompatibilityVersion() const { return CompatibilityVersion; } + + /// Set the Swift ABI version of the library. + void setSwiftABIVersion(uint8_t Version) { SwiftABIVersion = Version; } + + /// Get the Swift ABI version of the library. + uint8_t getSwiftABIVersion() const { return SwiftABIVersion; } + + /// Specify if the library uses two-level namespace (or flat namespace). + void setTwoLevelNamespace(bool V = true) { IsTwoLevelNamespace = V; } + + /// Check if the library uses two-level namespace. + bool isTwoLevelNamespace() const { return IsTwoLevelNamespace; } + + /// Specify if the library is application extension safe (or not). + void setApplicationExtensionSafe(bool V = true) { IsAppExtensionSafe = V; } + + /// Check if the library is application extension safe. + bool isApplicationExtensionSafe() const { return IsAppExtensionSafe; } + + /// Set the Objective-C constraint. + void setObjCConstraint(ObjCConstraint Constraint) { + ObjcConstraint = Constraint; + } + + /// Get the Objective-C constraint. + ObjCConstraint getObjCConstraint() const { return ObjcConstraint; } + + /// Specify if this file was generated during InstallAPI (or not). + void setInstallAPI(bool V = true) { IsInstallAPI = V; } + + /// Check if this file was generated during InstallAPI. + bool isInstallAPI() const { return IsInstallAPI; } + + /// Set the parent umbrella framework. + void setParentUmbrella(StringRef Parent) { ParentUmbrella = Parent; } + + /// Get the parent umbrella framework. + StringRef getParentUmbrella() const { return ParentUmbrella; } + + /// Add an allowable client. + /// + /// Mach-O Dynamic libraries have the concept of allowable clients that are + /// checked during static link time. The name of the application or library + /// that is being generated needs to match one of the allowable clients or the + /// linker refuses to link this library. + /// + /// \param Name The name of the client that is allowed to link this library. + /// \param Architectures The set of architecture for which this applies. + void addAllowableClient(StringRef Name, ArchitectureSet Architectures); + + /// Get the list of allowable clients. + /// + /// \return Returns a list of allowable clients. + const std::vector &allowableClients() const { + return AllowableClients; + } + + /// Add a re-exported library. + /// + /// \param Name The name of the library to re-export. + /// \param Architectures The set of architecture for which this applies. + void addReexportedLibrary(StringRef InstallName, + ArchitectureSet Architectures); + + /// Get the list of re-exported libraries. + /// + /// \return Returns a list of re-exported libraries. + const std::vector &reexportedLibraries() const { + return ReexportedLibraries; + } + + /// Add an architecture/UUID pair. + /// + /// \param Arch The architecture for which this applies. + /// \param UUID The UUID of the library for the specified architecture. + void addUUID(Architecture Arch, StringRef UUID); + + /// Add an architecture/UUID pair. + /// + /// \param Arch The architecture for which this applies. + /// \param UUID The UUID of the library for the specified architecture. + void addUUID(Architecture Arch, uint8_t UUID[16]); + + /// Get the list of architecture/UUID pairs. + /// + /// \return Returns a list of architecture/UUID pairs. + const std::vector> &uuids() const { + return UUIDs; + } + + /// Add a symbol to the symbols list or extend an existing one. + void addSymbol(SymbolKind Kind, StringRef Name, ArchitectureSet Architectures, + SymbolFlags Flags = SymbolFlags::None); + + using SymbolMapType = DenseMap; + struct const_symbol_iterator + : public iterator_adaptor_base< + const_symbol_iterator, SymbolMapType::const_iterator, + std::forward_iterator_tag, const Symbol *, ptrdiff_t, + const Symbol *, const Symbol *> { + const_symbol_iterator() = default; + + template + const_symbol_iterator(U &&u) + : iterator_adaptor_base(std::forward(u)) {} + + reference operator*() const { return I->second; } + pointer operator->() const { return I->second; } + }; + using const_symbol_range = iterator_range; + + // Custom iterator to return only exported symbols. + struct const_export_iterator + : public iterator_adaptor_base< + const_export_iterator, const_symbol_iterator, + std::forward_iterator_tag, const Symbol *> { + const_symbol_iterator _end; + + void skipToNextSymbol() { + while (I != _end && I->isUndefined()) + ++I; + } + + const_export_iterator() = default; + template + const_export_iterator(U &&it, U &&end) + : iterator_adaptor_base(std::forward(it)), + _end(std::forward(end)) { + skipToNextSymbol(); + } + + const_export_iterator &operator++() { + ++I; + skipToNextSymbol(); + return *this; + } + + const_export_iterator operator++(int) { + const_export_iterator tmp(*this); + ++(*this); + return tmp; + } + }; + using const_export_range = llvm::iterator_range; + + // Custom iterator to return only undefined symbols. + struct const_undefined_iterator + : public iterator_adaptor_base< + const_undefined_iterator, const_symbol_iterator, + std::forward_iterator_tag, const Symbol *> { + const_symbol_iterator _end; + + void skipToNextSymbol() { + while (I != _end && !I->isUndefined()) + ++I; + } + + const_undefined_iterator() = default; + template + const_undefined_iterator(U &&it, U &&end) + : iterator_adaptor_base(std::forward(it)), + _end(std::forward(end)) { + skipToNextSymbol(); + } + + const_undefined_iterator &operator++() { + ++I; + skipToNextSymbol(); + return *this; + } + + const_undefined_iterator operator++(int) { + const_undefined_iterator tmp(*this); + ++(*this); + return tmp; + } + }; + using const_undefined_range = llvm::iterator_range; + + const_symbol_range symbols() const { + return {Symbols.begin(), Symbols.end()}; + } + const_export_range exports() const { + return {{Symbols.begin(), Symbols.end()}, {Symbols.end(), Symbols.end()}}; + } + const_undefined_range undefineds() const { + return {{Symbols.begin(), Symbols.end()}, {Symbols.end(), Symbols.end()}}; + } + +private: + 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::string Path; + FileType FileType; + Platform Platform = Platform::unknown; + ArchitectureSet Architectures; + std::string InstallName; + PackedVersion CurrentVersion; + PackedVersion CompatibilityVersion; + uint8_t SwiftABIVersion{0}; + bool IsTwoLevelNamespace{false}; + bool IsAppExtensionSafe{false}; + bool IsInstallAPI{false}; + ObjCConstraint ObjcConstraint = ObjCConstraint::None; + std::string ParentUmbrella; + std::vector AllowableClients; + std::vector ReexportedLibraries; + std::vector> UUIDs; + SymbolMapType Symbols; +}; + +} // end namespace llvm. + +#endif // LLVM_TEXTAPI_INTERFACE_FILE_H Index: llvm/include/llvm/TextAPI/PackedVersion.h =================================================================== --- /dev/null +++ llvm/include/llvm/TextAPI/PackedVersion.h @@ -0,0 +1,64 @@ +//===- llvm/TextAPI/PackedVersion.h - PackedVersion -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines the Mach-O packed version format. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TEXTAPI_PACKED_VERSION_H +#define LLVM_TEXTAPI_PACKED_VERSION_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +class PackedVersion { + uint32_t Version{0}; + +public: + constexpr PackedVersion() = default; + explicit constexpr PackedVersion(uint32_t RawVersion) : Version(RawVersion) {} + PackedVersion(unsigned Major, unsigned Minor, unsigned Subminor) + : Version((Major << 16) | ((Minor & 0xff) << 8) | (Subminor & 0xff)) {} + + bool empty() const { return Version == 0; } + + /// Retrieve the major version number. + unsigned getMajor() const { return Version >> 16; } + + /// Retrieve the minor version number, if provided. + unsigned getMinor() const { return (Version >> 8) & 0xff; } + + /// Retrieve the subminor version number, if provided. + unsigned getSubminor() const { return Version & 0xff; } + + bool parse32(StringRef Str); + std::pair parse64(StringRef Str); + + bool operator<(const PackedVersion &O) const { return Version < O.Version; } + + bool operator==(const PackedVersion &O) const { return Version == O.Version; } + + bool operator!=(const PackedVersion &O) const { return Version != O.Version; } + + uint32_t rawValue() const { return Version; } + + void print(raw_ostream &OS) const; +}; + +inline raw_ostream &operator<<(raw_ostream &OS, const PackedVersion &Version) { + Version.print(OS); + return OS; +} + +} // end namespace llvm. + +#endif // LLVM_TEXTAPI_PACKED_VERSION_H Index: llvm/include/llvm/TextAPI/Registry.h =================================================================== --- /dev/null +++ llvm/include/llvm/TextAPI/Registry.h @@ -0,0 +1,91 @@ +//===- llvm/TextAPI/Registry.h - LLVM TAPI Registry -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief The TAPI registry keeps track of the supported file formats and +/// provides a generic interface for reading and writing those formats. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TEXTAPI_REGISTRY_H +#define LLVM_TEXTAPI_REGISTRY_H + +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/TextAPI/InterfaceFile.h" + +namespace llvm { + +namespace yaml { +class IO; +} +class Registry; + +/// Abstract Reader class - all readers need to inherit from this class and +/// implement the interface. +class Reader { +public: + virtual ~Reader(); + virtual bool canRead(file_magic Magic, MemoryBufferRef BufferRef, + const Registry &) const = 0; + virtual Expected> + readFile(MemoryBufferRef BufferRef, const Registry &) const = 0; +}; + +/// Abstract Writer class - all writers need to inherit from this class and +/// implement the interface. +class Writer { +public: + virtual ~Writer(); + virtual bool canWrite(const InterfaceFile *File, const Registry &) const = 0; + virtual Error writeFile(raw_ostream &OS, const InterfaceFile *File, + const Registry &) const = 0; +}; + +class YAMLDocumentHandler { +public: + virtual ~YAMLDocumentHandler(); + virtual bool canRead(file_magic Magic, MemoryBufferRef BufferRef) const = 0; + virtual bool canWrite(const InterfaceFile *File) const = 0; + virtual bool handleDocument(yaml::IO &IO, + const InterfaceFile *&File) const = 0; +}; + +/// Registry for readers and writers of interface files. +class Registry { +public: + static bool canRead(MemoryBufferRef BufferRef); + static bool canWrite(const InterfaceFile *File); + + static Expected> + readFile(MemoryBufferRef BufferRef); + static Error writeFile(raw_ostream &OS, const InterfaceFile *File); + + std::vector> Readers; + std::vector> Writers; + std::vector> DocumentHandlers; + +private: + Registry(); + void add(std::unique_ptr); + void add(std::unique_ptr); + void add(std::unique_ptr); + + void addYAMLReaderWriter(); + static ManagedStatic GlobalRegistry; + + friend struct object_creator; + friend struct object_deleter; +}; + +} // end namespace llvm. + +#endif // LLVM_TEXTAPI_REGISTRY_H Index: llvm/include/llvm/TextAPI/Symbol.h =================================================================== --- /dev/null +++ llvm/include/llvm/TextAPI/Symbol.h @@ -0,0 +1,109 @@ +//===- llvm/TextAPI/Symbol.h - TAPI Symbol ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Symbol +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TEXTAPI_SYMBOL_H +#define LLVM_TEXTAPI_SYMBOL_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TextAPI/ArchitectureSet.h" + +namespace llvm { + +// clang-format off + +/// Symbol flags. +enum class SymbolFlags : uint8_t { + /// No flags + None = 0, + + /// Thread-local value symbol + ThreadLocalValue = 1U << 0, + + /// Weak defined symbol + WeakDefined = 1U << 1, + + /// Weak referenced symbol + WeakReferenced = 1U << 2, + + /// Undefined + Undefined = 1U << 3, +}; + +// clang-format on + +inline SymbolFlags operator&(const SymbolFlags &lhs, + const SymbolFlags &rhs) noexcept { + return static_cast(static_cast(lhs) & + static_cast(rhs)); +} + +inline SymbolFlags operator|(const SymbolFlags lhs, + const SymbolFlags &rhs) noexcept { + return static_cast(static_cast(lhs) | + static_cast(rhs)); +} + +enum class SymbolKind : uint8_t { + GlobalSymbol, + ObjectiveCClass, + ObjectiveCClassEHType, + ObjectiveCInstanceVariable, +}; + +class Symbol { +public: + constexpr Symbol(SymbolKind Kind, StringRef Name, + ArchitectureSet Architectures, SymbolFlags Flags) + : Name(Name), Architectures(Architectures), Kind(Kind), Flags(Flags) {} + + SymbolKind getKind() const { return Kind; } + StringRef getName() const { return Name; } + ArchitectureSet getArchitectures() const { return Architectures; } + void setArchitectures(ArchitectureSet Archs) { Architectures |= Archs; } + SymbolFlags getFlags() const { return Flags; } + + bool isWeakDefined() const { + return (Flags & SymbolFlags::WeakDefined) == SymbolFlags::WeakDefined; + } + + bool isWeakReferenced() const { + return (Flags & SymbolFlags::WeakReferenced) == SymbolFlags::WeakReferenced; + } + + bool isThreadLocalValue() const { + return (Flags & SymbolFlags::ThreadLocalValue) == + SymbolFlags::ThreadLocalValue; + } + + bool isUndefined() const { + return (Flags & SymbolFlags::Undefined) == SymbolFlags::Undefined; + } + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump(raw_ostream &OS) const; + void dump() const { dump(llvm::errs()); } +#endif + +private: + StringRef Name; + ArchitectureSet Architectures; + SymbolKind Kind; + SymbolFlags Flags; +}; + +} // end namespace llvm. + +#endif // LLVM_TEXTAPI_SYMBOL_H Index: llvm/lib/CMakeLists.txt =================================================================== --- llvm/lib/CMakeLists.txt +++ llvm/lib/CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(Analysis) add_subdirectory(LTO) add_subdirectory(MC) +add_subdirectory(TextAPI) add_subdirectory(Object) add_subdirectory(ObjectYAML) add_subdirectory(Option) Index: llvm/lib/LLVMBuild.txt =================================================================== --- llvm/lib/LLVMBuild.txt +++ llvm/lib/LLVMBuild.txt @@ -42,6 +42,7 @@ TableGen Target Testing + TextAPI ToolDrivers Transforms WindowsManifest Index: llvm/lib/TextAPI/Architecture.cpp =================================================================== --- /dev/null +++ llvm/lib/TextAPI/Architecture.cpp @@ -0,0 +1,69 @@ +//===- llvm/TextAPI/Architecture.cpp - Architecture -------------*- 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 architecture helper functions. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TextAPI/Architecture.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/BinaryFormat/MachO.h" + +namespace llvm { + +Architecture getArchitectureFromCpuType(uint32_t CPUType, uint32_t CPUSubType) { +#define ARCHINFO(Arch, Type, Subtype) \ + if (CPUType == (Type) && \ + (CPUSubType & ~MachO::CPU_SUBTYPE_MASK) == (Subtype)) \ + return Architecture::Arch; +#include "llvm/TextAPI/Architecture.def" +#undef ARCHINFO + + return Architecture::unknown; +} + +Architecture getArchitectureFromName(StringRef Name) { + return StringSwitch(Name) +#define ARCHINFO(Arch, Type, Subtype) .Case(#Arch, Architecture::Arch) +#include "llvm/TextAPI/Architecture.def" +#undef ARCHINFO + .Default(Architecture::unknown); +} + +StringRef getArchitectureName(Architecture Arch) { + switch (Arch) { +#define ARCHINFO(Arch, Type, Subtype) \ + case Architecture::Arch: \ + return #Arch; +#include "llvm/TextAPI/Architecture.def" +#undef ARCHINFO + case Architecture::unknown: + return "unknown"; + } +} + +std::pair getCPUTypeFromArchitecture(Architecture Arch) { + switch (Arch) { +#define ARCHINFO(Arch, Type, Subtype) \ + case Architecture::Arch: \ + return std::make_pair(Type, Subtype); +#include "llvm/TextAPI/Architecture.def" +#undef ARCHINFO + case Architecture::unknown: + return std::make_pair(0, 0); + } +} + +raw_ostream &operator<<(raw_ostream &OS, Architecture Arch) { + OS << getArchitectureName(Arch); + return OS; +} + +} // end namespace llvm. Index: llvm/lib/TextAPI/ArchitectureSet.cpp =================================================================== --- /dev/null +++ llvm/lib/TextAPI/ArchitectureSet.cpp @@ -0,0 +1,70 @@ +//===- llvm/TextAPI/ArchitectureSet.cpp - Architecture Set ------*- 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 architecture set. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TextAPI/ArchitectureSet.h" + +namespace llvm { + +bool ArchitectureSet::hasABICompatibleSlice(Architecture Arch) const { + return getABICompatibleSlice(Arch) != Architecture::unknown; +} + +Architecture ArchitectureSet::getABICompatibleSlice(Architecture Arch) const { + uint32_t CpuType; + std::tie(CpuType, std::ignore) = getCPUTypeFromArchitecture(Arch); + + for (auto Arch2 : *this) { + uint32_t CpuType2; + std::tie(CpuType2, std::ignore) = getCPUTypeFromArchitecture(Arch2); + + if (CpuType == CpuType2) + return Arch2; + } + + return Architecture::unknown; +} + +ArchitectureSet::operator std::string() const { + if (empty()) + return "[(empty)]"; + + std::string result; + auto size = count(); + for (auto arch : *this) { + result.append(getArchitectureName(arch)); + size -= 1; + if (size) + result.append(" "); + } + return result; +} + +ArchitectureSet::operator std::vector() const { + std::vector archs; + for (auto arch : *this) { + if (arch == Architecture::unknown) + continue; + archs.emplace_back(arch); + } + return archs; +} + +void ArchitectureSet::print(raw_ostream &os) const { os << std::string(*this); } + +raw_ostream &operator<<(raw_ostream &os, ArchitectureSet set) { + set.print(os); + return os; +} + +} // end namespace llvm. Index: llvm/lib/TextAPI/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/lib/TextAPI/CMakeLists.txt @@ -0,0 +1,14 @@ +add_llvm_library(LLVMTextAPI + Architecture.cpp + ArchitectureSet.cpp + InterfaceFile.cpp + PackedVersion.cpp + Registry.cpp + Symbol.cpp + TextAPIReaderWriter.cpp + TextStub.cpp + TextStubCommon.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/TextAPI + ) Index: llvm/lib/TextAPI/InterfaceFile.cpp =================================================================== --- /dev/null +++ llvm/lib/TextAPI/InterfaceFile.cpp @@ -0,0 +1,85 @@ +//===- lib/TextAPI/InterfaceFile.cpp - Interface File -----------*- 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 Interface File. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TextAPI/InterfaceFile.h" +#include "llvm/TextAPI/Registry.h" +#include +#include + +using namespace llvm; + +namespace llvm { +namespace detail { +template +typename C::iterator addEntry(C &Container, StringRef InstallName) { + auto I = + std::lower_bound(std::begin(Container), std::end(Container), InstallName, + [](const InterfaceFileRef &LHS, const StringRef &RHS) { + return LHS.getInstallName() < RHS; + }); + if ((I != std::end(Container)) && !(InstallName < I->getInstallName())) + return I; + + return Container.emplace(I, InstallName); +} +} // end namespace detail. + +void InterfaceFile::addAllowableClient(StringRef Name, + ArchitectureSet Architectures) { + auto Client = detail::addEntry(AllowableClients, Name); + Client->setArchitectures(Architectures); +} + +void InterfaceFile::addReexportedLibrary(StringRef InstallName, + ArchitectureSet Architectures) { + auto Lib = detail::addEntry(ReexportedLibraries, InstallName); + Lib->setArchitectures(Architectures); +} + +void InterfaceFile::addUUID(Architecture Arch, StringRef UUID) { + auto I = std::lower_bound(UUIDs.begin(), UUIDs.end(), Arch, + [](const std::pair &LHS, + Architecture RHS) { return LHS.first < RHS; }); + + if ((I != UUIDs.end()) && !(Arch < I->first)) { + I->second = UUID; + return; + } + + UUIDs.emplace(I, Arch, UUID); + return; +} + +void InterfaceFile::addUUID(Architecture Arch, uint8_t UUID[16]) { + std::stringstream Stream; + for (unsigned i = 0; i < 16; ++i) { + if (i == 4 || i == 6 || i == 8 || i == 10) + Stream << '-'; + Stream << std::setfill('0') << std::setw(2) << std::uppercase << std::hex + << static_cast(UUID[i]); + } + addUUID(Arch, Stream.str()); +} + +void InterfaceFile::addSymbol(SymbolKind Kind, StringRef Name, + ArchitectureSet Archs, SymbolFlags Flags) { + Name = copyString(Name); + auto result = Symbols.try_emplace(SymbolsMapKey{Kind, Name}, nullptr); + if (result.second) + result.first->second = new (Allocator) Symbol{Kind, Name, Archs, Flags}; + else + result.first->second->setArchitectures(Archs); +} + +} // end namespace llvm. Index: llvm/lib/TextAPI/LLVMBuild.txt =================================================================== --- /dev/null +++ llvm/lib/TextAPI/LLVMBuild.txt @@ -0,0 +1,14 @@ +;===------------------------------------------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = TextAPI +parent = Libraries +required_libraries = Support BinaryFormat Index: llvm/lib/TextAPI/PackedVersion.cpp =================================================================== --- /dev/null +++ llvm/lib/TextAPI/PackedVersion.cpp @@ -0,0 +1,113 @@ +//===- llvm/TextAPI/PackedVersion.cpp - PackedVersion -----------*- 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 Mach-O packed version. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TextAPI/PackedVersion.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +bool PackedVersion::parse32(StringRef Str) { + Version = 0; + + if (Str.empty()) + return false; + + SmallVector Parts; + SplitString(Str, Parts, "."); + + if (Parts.size() > 3) + return false; + + unsigned long long Num; + if (getAsUnsignedInteger(Parts[0], 10, Num)) + return false; + + if (Num > UINT16_MAX) + return false; + + Version = Num << 16; + + for (unsigned i = 1, ShiftNum = 8; i < Parts.size(); ++i, ShiftNum -= 8) { + if (getAsUnsignedInteger(Parts[i], 10, Num)) + return false; + + if (Num > UINT8_MAX) + return false; + + Version |= (Num << ShiftNum); + } + + return true; +} + +std::pair PackedVersion::parse64(StringRef Str) { + bool Truncated = false; + Version = 0; + + if (Str.empty()) + return std::make_pair(false, Truncated); + + SmallVector Parts; + SplitString(Str, Parts, "."); + + if (Parts.size() > 5) + return std::make_pair(false, Truncated); + + unsigned long long Num; + if (getAsUnsignedInteger(Parts[0], 10, Num)) + return std::make_pair(false, Truncated); + + if (Num > 0xFFFFFFULL) + return std::make_pair(false, Truncated); + + if (Num > 0xFFFFULL) { + Num = 0xFFFFULL; + Truncated = true; + } + Version = Num << 16; + + for (unsigned i = 1, ShiftNum = 8; i < Parts.size() && i < 3; + ++i, ShiftNum -= 8) { + if (getAsUnsignedInteger(Parts[i], 10, Num)) + return std::make_pair(false, Truncated); + + if (Num > 0x3FFULL) + return std::make_pair(false, Truncated); + + if (Num > 0xFFULL) { + Num = 0xFFULL; + Truncated = true; + } + Version |= (Num << ShiftNum); + } + + if (Parts.size() > 3) + Truncated = true; + + return std::make_pair(true, Truncated); +} + +void PackedVersion::print(raw_ostream &OS) const { + OS << format("%d", getMajor()); + if (getMinor() || getSubminor()) + OS << format(".%d", getMinor()); + if (getSubminor()) + OS << format(".%d", getSubminor()); +} + +} // end namespace llvm. Index: llvm/lib/TextAPI/Registry.cpp =================================================================== --- /dev/null +++ llvm/lib/TextAPI/Registry.cpp @@ -0,0 +1,87 @@ +//===- lib/TextAPI/Registry.cpp - TAPI Registry -----------------*- 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 TAPI Registry. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TextAPI/Registry.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +Reader::~Reader() = default; +Writer::~Writer() = default; +YAMLDocumentHandler::~YAMLDocumentHandler() = default; + +ManagedStatic Registry::GlobalRegistry; + +Registry::Registry() { addYAMLReaderWriter(); } + +void Registry::add(std::unique_ptr R) { + Readers.emplace_back(std::move(R)); +} + +void Registry::add(std::unique_ptr W) { + Writers.emplace_back(std::move(W)); +} + +void Registry::add(std::unique_ptr H) { + DocumentHandlers.emplace_back(std::move(H)); +} + +bool Registry::canRead(MemoryBufferRef BufferRef) { + auto Data = BufferRef.getBuffer(); + auto Magic = identify_magic(Data); + + for (const auto &Reader : GlobalRegistry->Readers) + if (Reader->canRead(Magic, BufferRef, *GlobalRegistry)) + return true; + + return false; +} + +bool Registry::canWrite(const InterfaceFile *File) { + for (const auto &Writer : GlobalRegistry->Writers) + if (Writer->canWrite(File, *GlobalRegistry)) + return true; + + return false; +} + +Expected> +Registry::readFile(MemoryBufferRef BufferRef) { + auto Data = BufferRef.getBuffer(); + auto Magic = identify_magic(Data); + + for (const auto &Reader : GlobalRegistry->Readers) { + if (!Reader->canRead(Magic, BufferRef, *GlobalRegistry)) + continue; + return Reader->readFile(BufferRef, *GlobalRegistry); + } + + return make_error( + "unsupported file type", std::make_error_code(std::errc::not_supported)); +} + +Error Registry::writeFile(raw_ostream &OS, const InterfaceFile *File) { + for (const auto &Writer : GlobalRegistry->Writers) { + if (!Writer->canWrite(File, *GlobalRegistry)) + continue; + return Writer->writeFile(OS, File, *GlobalRegistry); + } + + return make_error( + "unsupported file type", std::make_error_code(std::errc::not_supported)); +} + +} // end namespace llvm. Index: llvm/lib/TextAPI/Symbol.cpp =================================================================== --- /dev/null +++ llvm/lib/TextAPI/Symbol.cpp @@ -0,0 +1,49 @@ +//===- lib/TextAPI/Symbol.cpp - Symbol --------------------------*- 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 Symbol +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TextAPI/Symbol.h" +#include + +namespace llvm { + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void Symbol::dump(raw_ostream &OS) const { + std::string Result; + if (isUndefined()) + Result += "(undef) "; + if (isWeakDefined()) + Result += "(weak-def) "; + if (isWeakReferenced()) + Result += "(weak-ref) "; + if (isThreadLocalValue()) + Result += "(tlv) "; + switch (Kind) { + case SymbolKind::GlobalSymbol: + Result + Name.str(); + break; + case SymbolKind::ObjectiveCClass: + Result + "(ObjC Class) " + Name.str(); + break; + case SymbolKind::ObjectiveCClassEHType: + Result + "(ObjC Class EH) " + Name.str(); + break; + case SymbolKind::ObjectiveCInstanceVariable: + Result + "(ObjC IVar) " + Name.str(); + break; + } + OS << Result; +} +#endif + +} // end namespace llvm. Index: llvm/lib/TextAPI/TextAPIContext.h =================================================================== --- /dev/null +++ llvm/lib/TextAPI/TextAPIContext.h @@ -0,0 +1,37 @@ +//===- llvm/TextAPI/YAMLContext.h - YAML Context ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines the YAML Context for the TextAPI Reader/Writer +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TEXTAPI_YAML_CONTEXT_H +#define LLVM_TEXTAPI_YAML_CONTEXT_H + +#include "llvm/Support/MemoryBuffer.h" +#include + +namespace llvm { + +class Registry; +enum FileType : unsigned; + +struct TextAPIContext { + const Registry &Reg; + std::string ErrorMessage; + std::string Path; + FileType FileType; + + TextAPIContext(const Registry &Reg) : Reg(Reg) {} +}; + +} // end namespace llvm. + +#endif // LLVM_TEXTAPI_YAML_CONTEXT_H Index: llvm/lib/TextAPI/TextAPIReaderWriter.h =================================================================== --- /dev/null +++ llvm/lib/TextAPI/TextAPIReaderWriter.h @@ -0,0 +1,77 @@ +//===- llvm/TextAPI/TextAPIReader.h - TextAPI Reader ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines the TextAPI Reader. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TEXTAPI_TEXTAPI_READER_H +#define LLVM_TEXTAPI_TEXTAPI_READER_H + +#include "llvm/Support/Error.h" +#include "llvm/TextAPI/InterfaceFile.h" +#include "llvm/TextAPI/Registry.h" + +namespace llvm { + +struct file_magic; +class raw_ostream; + +class TextAPIReader : public Reader { +public: + bool canRead(file_magic Magic, MemoryBufferRef MemBufferRef, + const Registry &) const override; + Expected> + readFile(MemoryBufferRef BufferRef, const Registry &) const override; +}; + +class TextAPIWriter : public Writer { +public: + bool canWrite(const InterfaceFile *File, const Registry &) const override; + Error writeFile(raw_ostream &OS, const InterfaceFile *File, + const Registry &) const override; +}; + +namespace text_stub { +namespace v1 { +class TBDDocumentHandler : public YAMLDocumentHandler { + bool canRead(file_magic Magic, MemoryBufferRef BufferRef) const override; + bool canWrite(const InterfaceFile *file) const override; + bool handleDocument(llvm::yaml::IO &io, + const InterfaceFile *&file) const override; +}; +} // end namespace v1. +} // end namespace text_stub. + +namespace text_stub { +namespace v2 { +class TBDDocumentHandler : public YAMLDocumentHandler { + bool canRead(file_magic Magic, MemoryBufferRef BufferRef) const override; + bool canWrite(const InterfaceFile *file) const override; + bool handleDocument(llvm::yaml::IO &io, + const InterfaceFile *&file) const override; +}; +} // end namespace v2. +} // end namespace text_stub. + +namespace text_stub { +namespace v3 { +class TBDDocumentHandler : public YAMLDocumentHandler { + bool canRead(file_magic Magic, MemoryBufferRef BufferRef) const override; + bool canWrite(const InterfaceFile *file) const override; + bool handleDocument(llvm::yaml::IO &io, + const InterfaceFile *&file) const override; +}; +} // end namespace v3. +} // end namespace text_stub. + +} // end namespace llvm. + +#endif // LLVM_TEXTAPI_TEXTAPI_READER_H Index: llvm/lib/TextAPI/TextAPIReaderWriter.cpp =================================================================== --- /dev/null +++ llvm/lib/TextAPI/TextAPIReaderWriter.cpp @@ -0,0 +1,126 @@ +//===- TextAPIReaderWriter.cpp - TextAPI Reader/Writer ----------*- 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 TextAPI YAML reader/writer. +/// +//===----------------------------------------------------------------------===// + +#include "TextAPIReaderWriter.h" +#include "TextAPIContext.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::yaml; + +namespace llvm { +namespace yaml { + +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]; + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, const InterfaceFile *&File) { + auto Ctx = reinterpret_cast(IO.getContext()); + assert(Ctx != nullptr); + for (const auto &Handler : Ctx->Reg.DocumentHandlers) + if (Handler->handleDocument(IO, File)) + break; + } +}; +} // namespace yaml + +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 = Message.str(); +} + +bool TextAPIReader::canRead(file_magic Magic, MemoryBufferRef BufferRef, + const Registry &Reg) const { + for (const auto &Handler : Reg.DocumentHandlers) { + if (Handler->canRead(Magic, BufferRef)) + return true; + } + return false; +} + +Expected> +TextAPIReader::readFile(MemoryBufferRef BufferRef, const Registry &Reg) const { + TextAPIContext Ctx(Reg); + Ctx.Path = BufferRef.getBufferIdentifier(); + yaml::Input YAMLIn(BufferRef.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("malformed file\n" + Ctx.ErrorMessage, + YAMLIn.error()); + + // For now only allow one document per YAML file. This will change in the + // future. + if (Files.size() != 1) + return nullptr; + + auto *File = const_cast(Files.front()); + return std::unique_ptr(File); +} + +bool TextAPIWriter::canWrite(const InterfaceFile *File, + const Registry &Reg) const { + for (const auto &Handler : Reg.DocumentHandlers) { + if (Handler->canWrite(File)) + return true; + } + return false; +} + +Error TextAPIWriter::writeFile(raw_ostream &OS, const InterfaceFile *File, + const Registry &Reg) const { + TextAPIContext Ctx(Reg); + Ctx.Path = File->getPath(); + llvm::yaml::Output YAMLOut(OS, &Ctx, /*WrapColumn=*/80); + + // Stream out yaml. + YAMLOut << File; + + return Error::success(); +} + +void Registry::addYAMLReaderWriter() { + add(make_unique()); + add(make_unique()); + add(make_unique()); + add(make_unique()); + add(make_unique()); +} + +} // end namespace llvm. Index: llvm/lib/TextAPI/TextStub.cpp =================================================================== --- /dev/null +++ llvm/lib/TextAPI/TextStub.cpp @@ -0,0 +1,705 @@ +//===- 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 "TextAPIReaderWriter.h" +#include "TextStubCommon.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/TextAPI/Architecture.h" +#include "llvm/TextAPI/ArchitectureSet.h" +#include "llvm/TextAPI/InterfaceFile.h" +#include "llvm/TextAPI/PackedVersion.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; + +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 Flags : unsigned { + None = 0U, + FlatNamespace = 1U << 0, + NotApplicationExtensionSafe = 1U << 1, + InstallAPI = 1U << 2, +}; +// clang-format on + +inline Flags operator|(const Flags a, const Flags b) { + return static_cast(static_cast(a) | + static_cast(b)); +} + +inline Flags operator|=(Flags &a, const Flags b) { + a = static_cast(static_cast(a) | static_cast(b)); + return a; +} +} // 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->FileType != FileType::Invalid) && + "File type is not set in YAML context"); + + IO.mapRequired("archs", Section.Architectures); + if (Ctx->FileType == 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->FileType == 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->FileType != 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->FileType == 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, Flags &Flags) { + IO.bitSetCase(Flags, "flat_namespace", Flags::FlatNamespace); + IO.bitSetCase(Flags, "not_app_extension_safe", + Flags::NotApplicationExtensionSafe); + IO.bitSetCase(Flags, "installapi", Flags::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()); + SwiftVersion = File->getSwiftABIVersion(); + ObjCConstraint = File->getObjCConstraint(); + + Flags = Flags::None; + if (!File->isApplicationExtensionSafe()) + Flags |= Flags::NotApplicationExtensionSafe; + + if (!File->isTwoLevelNamespace()) + Flags |= Flags::FlatNamespace; + + if (File->isInstallAPI()) + Flags |= Flags::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->FileType); + 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(SwiftVersion); + File->setObjCConstraint(ObjCConstraint); + File->setParentUmbrella(ParentUmbrella); + + if (Ctx->FileType == FileType::TBD_V1) { + File->setTwoLevelNamespace(); + File->setApplicationExtensionSafe(); + } else { + File->setTwoLevelNamespace(!(Flags & Flags::FlatNamespace)); + File->setApplicationExtensionSafe( + !(Flags & Flags::NotApplicationExtensionSafe)); + File->setInstallAPI(Flags & Flags::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->FileType != 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->FileType != 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->FileType != 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->FileType != 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->FileType != 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->FileType != 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; + Platform Platform; + StringRef InstallName; + PackedVersion CurrentVersion; + PackedVersion CompatibilityVersion; + SwiftVersion SwiftVersion; + ObjCConstraint ObjCConstraint; + Flags Flags; + StringRef ParentUmbrella; + std::vector Exports; + std::vector Undefineds; + }; + + static void mappingTBD(IO &IO, const InterfaceFile *&File) { + const auto *Ctx = reinterpret_cast(IO.getContext()); + assert((!Ctx || Ctx && Ctx->FileType != FileType::Invalid) && + "File type is not set in YAML context"); + MappingNormalization Keys(IO, File); + + switch (Ctx->FileType) { + default: + llvm_unreachable("unexpected file type"); + case FileType::TBD_V1: + // Don't write the tag into the .tbd file for TBD v1. + if (!IO.outputting()) + IO.mapTag("tapi-tbd-v1", true); + 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->FileType != FileType::TBD_V1) + IO.mapOptional("uuids", Keys->UUIDs); + IO.mapRequired("platform", Keys->Platform); + if (Ctx->FileType != FileType::TBD_V1) + IO.mapOptional("flags", Keys->Flags, Flags::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->FileType != FileType::TBD_V3) + IO.mapOptional("swift-version", Keys->SwiftVersion, SwiftVersion(0)); + else + IO.mapOptional("swift-abi-version", Keys->SwiftVersion, SwiftVersion(0)); + IO.mapOptional("objc-constraint", Keys->ObjCConstraint, + (Ctx->FileType == FileType::TBD_V1) + ? ObjCConstraint::None + : ObjCConstraint::Retain_Release); + if (Ctx->FileType != FileType::TBD_V1) + IO.mapOptional("parent-umbrella", Keys->ParentUmbrella, StringRef()); + IO.mapOptional("exports", Keys->Exports); + if (Ctx->FileType != FileType::TBD_V1) + IO.mapOptional("undefineds", Keys->Undefineds); + } +}; + +} // end namespace yaml. + +namespace text_stub { +namespace v1 { + +bool TBDDocumentHandler::canRead(file_magic Magic, + MemoryBufferRef BufferRef) const { + if (Magic != file_magic::tapi_file) + return false; + + auto Str = BufferRef.getBuffer().trim(); + if (!(Str.startswith("---\narchs:") || + Str.startswith("--- !tapi-tbd-v1\n")) || + !Str.endswith("...")) + return false; + + return true; +} + +bool TBDDocumentHandler::canWrite(const InterfaceFile *File) const { + if (File->getFileType() != FileType::TBD_V1) + return false; + + return true; +} + +bool TBDDocumentHandler::handleDocument(IO &IO, + const InterfaceFile *&File) const { + if (IO.outputting() && File->getFileType() != FileType::TBD_V1) + return false; + + if (!IO.outputting() && !IO.mapTag("!tapi-tbd-v1") && + !IO.mapTag("tag:yaml.org,2002:map")) + return false; + + auto *Ctx = reinterpret_cast(IO.getContext()); + Ctx->FileType = FileType::TBD_V1; + MappingTraits::mappingTBD(IO, File); + + return true; +} + +} // end namespace v1. +} // end namespace text_stub. + +namespace text_stub { +namespace v2 { + +bool TBDDocumentHandler::canRead(file_magic Magic, + MemoryBufferRef BufferRef) const { + if (Magic != file_magic::tapi_file) + return false; + + auto Str = BufferRef.getBuffer().trim(); + if (!Str.startswith("--- !tapi-tbd-v2\n") || !Str.endswith("...")) + return false; + + return true; +} + +bool TBDDocumentHandler::canWrite(const InterfaceFile *File) const { + if (File->getFileType() != FileType::TBD_V2) + return false; + + return true; +} + +bool TBDDocumentHandler::handleDocument(IO &IO, + const InterfaceFile *&File) const { + if (IO.outputting() && File->getFileType() != FileType::TBD_V2) + return false; + + if (!IO.outputting() && !IO.mapTag("!tapi-tbd-v2")) + return false; + + auto *Ctx = reinterpret_cast(IO.getContext()); + Ctx->FileType = FileType::TBD_V2; + MappingTraits::mappingTBD(IO, File); + + return true; +} + +} // end namespace v2. +} // end namespace text_stub. + +namespace text_stub { +namespace v3 { + +bool TBDDocumentHandler::canRead(file_magic Magic, + MemoryBufferRef BufferRef) const { + if (Magic != file_magic::tapi_file) + return false; + + auto Str = BufferRef.getBuffer().trim(); + if (!Str.startswith("--- !tapi-tbd-v3\n") || !Str.endswith("...")) + return false; + + return true; +} + +bool TBDDocumentHandler::canWrite(const InterfaceFile *File) const { + if (File->getFileType() != FileType::TBD_V3) + return false; + + return true; +} + +bool TBDDocumentHandler::handleDocument(IO &IO, + const InterfaceFile *&File) const { + if (IO.outputting() && File->getFileType() != FileType::TBD_V3) + return false; + + if (!IO.outputting() && !IO.mapTag("!tapi-tbd-v3")) + return false; + + auto *Ctx = reinterpret_cast(IO.getContext()); + Ctx->FileType = FileType::TBD_V3; + MappingTraits::mappingTBD(IO, File); + + return true; +} + +} // end namespace v3. +} // end namespace text_stub. + +} // end namespace llvm. Index: llvm/lib/TextAPI/TextStubCommon.h =================================================================== --- /dev/null +++ llvm/lib/TextAPI/TextStubCommon.h @@ -0,0 +1,83 @@ +//===- llvm/TextAPI/TextStubCommon.h - Text Stub Common ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines common Text Stub YAML mappings. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TEXTAPI_TEXT_STUB_COMMON_H +#define LLVM_TEXTAPI_TEXT_STUB_COMMON_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/TextAPI/Architecture.h" +#include "llvm/TextAPI/ArchitectureSet.h" +#include "llvm/TextAPI/InterfaceFile.h" +#include "llvm/TextAPI/PackedVersion.h" + +using UUID = std::pair; + +LLVM_YAML_STRONG_TYPEDEF(llvm::StringRef, FlowStringRef) +LLVM_YAML_STRONG_TYPEDEF(uint8_t, SwiftVersion) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(UUID) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FlowStringRef) + +namespace llvm { +namespace yaml { + +template <> struct ScalarTraits { + static void output(const FlowStringRef &, void *, raw_ostream &); + static StringRef input(StringRef, void *, FlowStringRef &); + static QuotingType mustQuote(StringRef); +}; + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &, ObjCConstraint &); +}; + +template <> struct ScalarTraits { + static void output(const Platform &, void *, raw_ostream &); + static StringRef input(StringRef, void *, Platform &); + static QuotingType mustQuote(StringRef); +}; + +template <> struct ScalarBitSetTraits { + static void bitset(IO &, ArchitectureSet &); +}; + +template <> struct ScalarTraits { + static void output(const Architecture &, void *, raw_ostream &); + static StringRef input(StringRef, void *, Architecture &); + static QuotingType mustQuote(StringRef); +}; + +template <> struct ScalarTraits { + static void output(const PackedVersion &, void *, raw_ostream &); + static StringRef input(StringRef, void *, PackedVersion &); + static QuotingType mustQuote(StringRef); +}; + +template <> struct ScalarTraits { + static void output(const SwiftVersion &, void *, raw_ostream &); + static StringRef input(StringRef, void *, SwiftVersion &); + static QuotingType mustQuote(StringRef); +}; + +template <> struct ScalarTraits { + static void output(const UUID &, void *, raw_ostream &); + static StringRef input(StringRef, void *, UUID &); + static QuotingType mustQuote(StringRef); +}; + +} // end namespace yaml. +} // end namespace llvm. + +#endif // LLVM_TEXTAPI_TEXT_STUB_COMMON_H Index: llvm/lib/TextAPI/TextStubCommon.cpp =================================================================== --- /dev/null +++ llvm/lib/TextAPI/TextStubCommon.cpp @@ -0,0 +1,178 @@ +//===- lib/TextAPI/TextStubCommon.cpp - Text Stub Common --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Implememts common Text Stub YAML mappings. +/// +//===----------------------------------------------------------------------===// + +#include "TextStubCommon.h" +#include "TextAPIContext.h" + +namespace llvm { +namespace yaml { + +void ScalarTraits::output(const FlowStringRef &Value, void *Ctx, + raw_ostream &OS) { + ScalarTraits::output(Value, Ctx, OS); +} +StringRef ScalarTraits::input(StringRef Value, void *Ctx, + FlowStringRef &Out) { + return ScalarTraits::input(Value, Ctx, Out.value); +} +QuotingType ScalarTraits::mustQuote(StringRef Name) { + return ScalarTraits::mustQuote(Name); +} + +void ScalarEnumerationTraits::enumeration( + IO &IO, ObjCConstraint &Constraint) { + IO.enumCase(Constraint, "none", ObjCConstraint::None); + IO.enumCase(Constraint, "retain_release", ObjCConstraint::Retain_Release); + IO.enumCase(Constraint, "retain_release_for_simulator", + ObjCConstraint::Retain_Release_For_Simulator); + IO.enumCase(Constraint, "retain_release_or_gc", + ObjCConstraint::Retain_Release_Or_GC); + IO.enumCase(Constraint, "gc", ObjCConstraint::GC); +} + +void ScalarTraits::output(const Platform &Value, void *, + raw_ostream &OS) { + switch (Value) { + default: + llvm_unreachable("unexpected platform"); + break; + case Platform::macOS: + OS << "macosx"; + break; + case Platform::iOS: + OS << "ios"; + break; + case Platform::watchOS: + OS << "watchos"; + break; + case Platform::tvOS: + OS << "tvos"; + break; + case Platform::bridgeOS: + OS << "bridgeos"; + break; + } +} +StringRef ScalarTraits::input(StringRef Scalar, void *, + Platform &Value) { + Value = StringSwitch(Scalar) + .Case("macosx", Platform::macOS) + .Case("ios", Platform::iOS) + .Case("watchos", Platform::watchOS) + .Case("tvos", Platform::tvOS) + .Case("bridgeos", Platform::bridgeOS) + .Default(Platform::unknown); + + if (Value == Platform::unknown) + return "unknown platform"; + return {}; +} +QuotingType ScalarTraits::mustQuote(StringRef) { + return QuotingType::None; +} + +void ScalarBitSetTraits::bitset(IO &IO, + ArchitectureSet &Archs) { +#define ARCHINFO(arch, type, subtype) \ + IO.bitSetCase(Archs, #arch, 1U << static_cast(Architecture::arch)); +#include "llvm/TextAPI/Architecture.def" +#undef ARCHINFO +} + +void ScalarTraits::output(const Architecture &Value, void *, + raw_ostream &OS) { + OS << Value; +} +StringRef ScalarTraits::input(StringRef Scalar, void *, + Architecture &Value) { + Value = getArchitectureFromName(Scalar); + return {}; +} +QuotingType ScalarTraits::mustQuote(StringRef) { + return QuotingType::None; +} + +void ScalarTraits::output(const PackedVersion &Value, void *, + raw_ostream &OS) { + OS << Value; +} +StringRef ScalarTraits::input(StringRef Scalar, void *, + PackedVersion &Value) { + if (!Value.parse32(Scalar)) + return "invalid packed version string."; + return {}; +} +QuotingType ScalarTraits::mustQuote(StringRef) { + return QuotingType::None; +} + +void ScalarTraits::output(const SwiftVersion &Value, void *, + raw_ostream &OS) { + switch (Value) { + case 1: + OS << "1.0"; + break; + case 2: + OS << "1.1"; + break; + case 3: + OS << "2.0"; + break; + case 4: + OS << "3.0"; + break; + default: + OS << (unsigned)Value; + break; + } +} +StringRef ScalarTraits::input(StringRef Scalar, void *, + SwiftVersion &Value) { + Value = StringSwitch(Scalar) + .Case("1.0", 1) + .Case("1.1", 2) + .Case("2.0", 3) + .Case("3.0", 4) + .Default(0); + if (Value != SwiftVersion(0)) + return {}; + + if (Scalar.getAsInteger(10, Value)) + return "invalid Swift ABI version."; + + return StringRef(); +} +QuotingType ScalarTraits::mustQuote(StringRef) { + return QuotingType::None; +} + +void ScalarTraits::output(const UUID &Value, void *, raw_ostream &OS) { + OS << Value.first << ": " << Value.second; +} +StringRef ScalarTraits::input(StringRef Scalar, void *, UUID &Value) { + auto Split = Scalar.split(':'); + auto Arch = Split.first.trim(); + auto UUID = Split.second.trim(); + if (UUID.empty()) + return "invalid uuid string pair"; + Value.first = getArchitectureFromName(Arch); + Value.second = UUID; + return {}; +} +QuotingType ScalarTraits::mustQuote(StringRef) { + return QuotingType::Single; +} + +} // end namespace yaml. +} // end namespace llvm. Index: llvm/unittests/CMakeLists.txt =================================================================== --- llvm/unittests/CMakeLists.txt +++ llvm/unittests/CMakeLists.txt @@ -31,6 +31,7 @@ add_subdirectory(ProfileData) add_subdirectory(Support) add_subdirectory(Target) +add_subdirectory(TextAPI) add_subdirectory(Transforms) add_subdirectory(XRay) add_subdirectory(tools) Index: llvm/unittests/TextAPI/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/unittests/TextAPI/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + Object + TextAPI + BinaryFormat + ) + +add_llvm_unittest(TextAPITests + TextStubV1Tests.cpp + TextStubV2Tests.cpp + ) Index: llvm/unittests/TextAPI/TextStubV1Tests.cpp =================================================================== --- /dev/null +++ llvm/unittests/TextAPI/TextStubV1Tests.cpp @@ -0,0 +1,454 @@ +//===-- TextStubV1Tests.cpp - TBD V1 File Test ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "llvm/TextAPI/Registry.h" +#include "gtest/gtest.h" +#include +#include + +using namespace llvm; + +using ExportedSymbol = std::tuple; +using ExportedSymbolSeq = std::vector; + +inline bool operator<(const ExportedSymbol &lhs, const ExportedSymbol &rhs) { + return std::get<1>(lhs) < std::get<1>(rhs); +} + +static ExportedSymbolSeq TBDv1Symbols = { + {SymbolKind::GlobalSymbol, "$ld$hide$os9.0$_sym1", false, false}, + {SymbolKind::GlobalSymbol, "_sym1", false, false}, + {SymbolKind::GlobalSymbol, "_sym2", false, false}, + {SymbolKind::GlobalSymbol, "_sym3", false, false}, + {SymbolKind::GlobalSymbol, "_sym4", false, false}, + {SymbolKind::GlobalSymbol, "_sym5", false, false}, + {SymbolKind::GlobalSymbol, "_tlv1", false, true}, + {SymbolKind::GlobalSymbol, "_tlv2", false, true}, + {SymbolKind::GlobalSymbol, "_tlv3", false, true}, + {SymbolKind::GlobalSymbol, "_weak1", true, false}, + {SymbolKind::GlobalSymbol, "_weak2", true, false}, + {SymbolKind::GlobalSymbol, "_weak3", true, false}, + {SymbolKind::ObjectiveCClass, "class1", false, false}, + {SymbolKind::ObjectiveCClass, "class2", false, false}, + {SymbolKind::ObjectiveCClass, "class3", false, false}, + {SymbolKind::ObjectiveCInstanceVariable, "class1._ivar1", false, false}, + {SymbolKind::ObjectiveCInstanceVariable, "class1._ivar2", false, false}, + {SymbolKind::ObjectiveCInstanceVariable, "class1._ivar3", false, false}, +}; + +namespace TBDv1 { + +TEST(TBDv1, ReadFile) { + static const char tbd_v1_file1[] = + "---\n" + "archs: [ armv7, armv7s, armv7k, arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "current-version: 2.3.4\n" + "compatibility-version: 1.0\n" + "swift-version: 1.1\n" + "exports:\n" + " - archs: [ armv7, armv7s, armv7k, arm64 ]\n" + " allowed-clients: [ clientA ]\n" + " re-exports: [ /usr/lib/libfoo.dylib ]\n" + " symbols: [ _sym1, _sym2, _sym3, _sym4, $ld$hide$os9.0$_sym1 ]\n" + " objc-classes: [ _class1, _class2 ]\n" + " objc-ivars: [ _class1._ivar1, _class1._ivar2 ]\n" + " weak-def-symbols: [ _weak1, _weak2 ]\n" + " thread-local-symbols: [ _tlv1, _tlv2 ]\n" + " - archs: [ armv7, armv7s, armv7k ]\n" + " symbols: [ _sym5 ]\n" + " objc-classes: [ _class3 ]\n" + " objc-ivars: [ _class1._ivar3 ]\n" + " weak-def-symbols: [ _weak3 ]\n" + " thread-local-symbols: [ _tlv3 ]\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_file1, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V1, File->getFileType()); + auto Archs = Architecture::armv7 | Architecture::armv7s | + Architecture::armv7k | Architecture::arm64; + EXPECT_EQ(Archs, File->getArchitectures()); + EXPECT_EQ(Platform::iOS, File->getPlatform()); + EXPECT_EQ(std::string("Test.dylib"), File->getInstallName()); + EXPECT_EQ(PackedVersion(2, 3, 4), File->getCurrentVersion()); + EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion()); + EXPECT_EQ(2U, File->getSwiftABIVersion()); + EXPECT_EQ(ObjCConstraint::None, File->getObjCConstraint()); + EXPECT_TRUE(File->isTwoLevelNamespace()); + EXPECT_TRUE(File->isApplicationExtensionSafe()); + EXPECT_FALSE(File->isInstallAPI()); + InterfaceFileRef client("clientA", Archs); + InterfaceFileRef reexport("/usr/lib/libfoo.dylib", Archs); + EXPECT_EQ(1U, File->allowableClients().size()); + EXPECT_EQ(client, File->allowableClients().front()); + EXPECT_EQ(1U, File->reexportedLibraries().size()); + EXPECT_EQ(reexport, File->reexportedLibraries().front()); + + ExportedSymbolSeq Exports; + for (const auto *Sym : File->symbols()) { + EXPECT_FALSE(Sym->isWeakReferenced()); + EXPECT_FALSE(Sym->isUndefined()); + Exports.emplace_back(Sym->getKind(), Sym->getName(), Sym->isWeakDefined(), + Sym->isThreadLocalValue()); + } + llvm::sort(Exports.begin(), Exports.end()); + + EXPECT_EQ(TBDv1Symbols.size(), Exports.size()); + EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(), TBDv1Symbols.begin())); +} + +TEST(TBDv1, ReadFile2) { + static const char tbd_v1_file2[] = "--- !tapi-tbd-v1\n" + "archs: [ armv7, armv7s, armv7k, arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_file2, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V1, File->getFileType()); + auto Archs = Architecture::armv7 | Architecture::armv7s | + Architecture::armv7k | Architecture::arm64; + EXPECT_EQ(Archs, File->getArchitectures()); + EXPECT_EQ(Platform::iOS, File->getPlatform()); + EXPECT_EQ(std::string("Test.dylib"), File->getInstallName()); + EXPECT_EQ(PackedVersion(1, 0, 0), File->getCurrentVersion()); + EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion()); + EXPECT_EQ(0U, File->getSwiftABIVersion()); + EXPECT_EQ(ObjCConstraint::None, File->getObjCConstraint()); + EXPECT_TRUE(File->isTwoLevelNamespace()); + EXPECT_TRUE(File->isApplicationExtensionSafe()); + EXPECT_FALSE(File->isInstallAPI()); + EXPECT_EQ(0U, File->allowableClients().size()); + EXPECT_EQ(0U, File->reexportedLibraries().size()); +} + +TEST(TBDv1, WriteFile) { + static const char tbd_v1_file3[] = + "---\n" + "archs: [ i386, x86_64 ]\n" + "platform: macosx\n" + "install-name: '/usr/lib/libfoo.dylib'\n" + "current-version: 1.2.3\n" + "compatibility-version: 0\n" + "swift-version: 5\n" + "objc-constraint: retain_release\n" + "exports: \n" + " - archs: [ i386 ]\n" + " symbols: [ _sym1 ]\n" + " weak-def-symbols: [ _sym2 ]\n" + " thread-local-symbols: [ _sym3 ]\n" + " - archs: [ x86_64 ]\n" + " allowed-clients: [ clientA ]\n" + " re-exports: [ '/usr/lib/libfoo.dylib' ]\n" + " symbols: [ '_OBJC_EHTYPE_$_Class1' ]\n" + " objc-classes: [ _Class1 ]\n" + " objc-ivars: [ _Class1._ivar1 ]\n" + "...\n"; + + InterfaceFile File; + File.setPath("libfoo.dylib"); + File.setInstallName("/usr/lib/libfoo.dylib"); + File.setFileType(FileType::TBD_V1); + File.setArchitectures(Architecture::i386 | Architecture::x86_64); + File.setPlatform(Platform::macOS); + File.setCurrentVersion(PackedVersion(1, 2, 3)); + File.setSwiftABIVersion(5); + File.setObjCConstraint(ObjCConstraint::Retain_Release); + File.addAllowableClient("clientA", Architecture::x86_64); + File.addReexportedLibrary("/usr/lib/libfoo.dylib", Architecture::x86_64); + File.addSymbol(SymbolKind::GlobalSymbol, "_sym1", Architecture::i386); + File.addSymbol(SymbolKind::GlobalSymbol, "_sym2", Architecture::i386, + SymbolFlags::WeakDefined); + File.addSymbol(SymbolKind::GlobalSymbol, "_sym3", Architecture::i386, + SymbolFlags::ThreadLocalValue); + File.addSymbol(SymbolKind::ObjectiveCClass, "Class1", Architecture::x86_64); + File.addSymbol(SymbolKind::ObjectiveCClassEHType, "Class1", + Architecture::x86_64); + File.addSymbol(SymbolKind::ObjectiveCInstanceVariable, "Class1._ivar1", + Architecture::x86_64); + + SmallString<4096> Buffer; + raw_svector_ostream OS(Buffer); + auto Result = Registry::writeFile(OS, &File); + EXPECT_FALSE(Result); + EXPECT_STREQ(tbd_v1_file3, Buffer.c_str()); +} + +TEST(TBDv1, Platform_macOS) { + static const char tbd_v1_platform_macos[] = "---\n" + "archs: [ x86_64 ]\n" + "platform: macosx\n" + "install-name: Test.dylib\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_platform_macos, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V1, File->getFileType()); + EXPECT_EQ(Platform::macOS, File->getPlatform()); +} + +TEST(TBDv1, Platform_iOS) { + static const char tbd_v1_platform_ios[] = "---\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_platform_ios, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V1, File->getFileType()); + EXPECT_EQ(Platform::iOS, File->getPlatform()); +} + +TEST(TBDv1, Platform_watchOS) { + static const char tbd_v1_platform_watchos[] = "---\n" + "archs: [ armv7k ]\n" + "platform: watchos\n" + "install-name: Test.dylib\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_platform_watchos, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V1, File->getFileType()); + EXPECT_EQ(Platform::watchOS, File->getPlatform()); +} + +TEST(TBDv1, Platform_tvOS) { + static const char tbd_v1_platform_tvos[] = "---\n" + "archs: [ arm64 ]\n" + "platform: tvos\n" + "install-name: Test.dylib\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_platform_tvos, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V1, File->getFileType()); + EXPECT_EQ(Platform::tvOS, File->getPlatform()); +} + +TEST(TBDv1, Platform_bridgeOS) { + static const char tbd_v1_platform_bridgeos[] = "---\n" + "archs: [ armv7k ]\n" + "platform: bridgeos\n" + "install-name: Test.dylib\n" + "...\n"; + + auto Buffer = + MemoryBuffer::getMemBuffer(tbd_v1_platform_bridgeos, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V1, File->getFileType()); + EXPECT_EQ(Platform::bridgeOS, File->getPlatform()); +} + +TEST(TBDv1, Swift_1_0) { + static const char tbd_v1_swift_1_0[] = "---\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "swift-version: 1.0\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_swift_1_0, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V1, File->getFileType()); + EXPECT_EQ(1U, File->getSwiftABIVersion()); +} + +TEST(TBDv1, Swift_1_1) { + static const char tbd_v1_swift_1_1[] = "---\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "swift-version: 1.1\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_swift_1_1, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V1, File->getFileType()); + EXPECT_EQ(2U, File->getSwiftABIVersion()); +} + +TEST(TBDv1, Swift_2_0) { + static const char tbd_v1_swift_2_0[] = "---\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "swift-version: 2.0\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_swift_2_0, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V1, File->getFileType()); + EXPECT_EQ(3U, File->getSwiftABIVersion()); +} + +TEST(TBDv1, Swift_3_0) { + static const char tbd_v1_swift_3_0[] = "---\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "swift-version: 3.0\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_swift_3_0, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V1, File->getFileType()); + EXPECT_EQ(4U, File->getSwiftABIVersion()); +} + +TEST(TBDv1, Swift_4_0) { + static const char tbd_v1_swift_4_0[] = "---\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "swift-version: 4.0\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_swift_4_0, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_FALSE(!!Result); + auto errorMessage = toString(Result.takeError()); + EXPECT_EQ("malformed file\nTest.tbd:5:16: error: invalid Swift ABI " + "version.\nswift-version: 4.0\n ^~~\n", + errorMessage); +} + +TEST(TBDv1, Swift_5) { + static const char tbd_v1_swift_5[] = "---\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "swift-version: 5\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_swift_5, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V1, File->getFileType()); + EXPECT_EQ(5U, File->getSwiftABIVersion()); +} + +TEST(TBDv1, Swift_99) { + static const char tbd_v1_swift_99[] = "---\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "swift-version: 99\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_swift_99, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V1, File->getFileType()); + EXPECT_EQ(99U, File->getSwiftABIVersion()); +} + +TEST(TBDv1, UnknownArchitecture) { + static const char tbd_v1_file_unknown_architecture[] = + "---\n" + "archs: [ foo ]\n" + "platform: macosx\n" + "install-name: Test.dylib\n" + "...\n"; + + auto Buffer = + MemoryBuffer::getMemBuffer(tbd_v1_file_unknown_architecture, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); +} + +TEST(TBDv1, UnknownPlatform) { + static const char tbd_v1_file_unknown_platform[] = "---\n" + "archs: [ i386 ]\n" + "platform: newOS\n" + "...\n"; + + auto Buffer = + MemoryBuffer::getMemBuffer(tbd_v1_file_unknown_platform, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_FALSE(!!Result); + auto errorMessage = toString(Result.takeError()); + EXPECT_EQ("malformed file\nTest.tbd:3:11: error: unknown platform\nplatform: " + "newOS\n ^~~~~\n", + errorMessage); +} + +// Test for invalid files. +TEST(TBDv1, UnsupportedFileType) { + static const unsigned char unsupported_file[] = { + 0xcf, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00}; + + auto Buffer = MemoryBuffer::getMemBuffer( + reinterpret_cast(unsupported_file), "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_FALSE(!!Result); + auto errorMessage = toString(Result.takeError()); + ASSERT_EQ("unsupported file type", errorMessage); +} + +TEST(TBDv1, MalformedFile1) { + static const char malformed_file1[] = "---\n" + "archs: [ arm64 ]\n" + "foobar: \"Unsupported key\"\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(malformed_file1, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_FALSE(!!Result); + auto errorMessage = toString(Result.takeError()); + ASSERT_EQ("malformed file\nTest.tbd:2:1: error: missing required key " + "'platform'\narchs: [ arm64 ]\n^\n", + errorMessage); +} + +TEST(TBDv1, MalformedFile2) { + static const char malformed_file2[] = "---\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "foobar: \"Unsupported key\"\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(malformed_file2, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_FALSE(!!Result); + auto errorMessage = toString(Result.takeError()); + ASSERT_EQ( + "malformed file\nTest.tbd:5:9: error: unknown key 'foobar'\nfoobar: " + "\"Unsupported key\"\n ^~~~~~~~~~~~~~~~~\n", + errorMessage); +} + +} // end namespace TBDv1. Index: llvm/unittests/TextAPI/TextStubV2Tests.cpp =================================================================== --- /dev/null +++ llvm/unittests/TextAPI/TextStubV2Tests.cpp @@ -0,0 +1,479 @@ +//===-- TextStubV2Tests.cpp - TBD V2 File Test ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "llvm/TextAPI/Registry.h" +#include "gtest/gtest.h" +#include +#include + +using namespace llvm; + +using ExportedSymbol = std::tuple; +using ExportedSymbolSeq = std::vector; + +inline bool operator<(const ExportedSymbol &lhs, const ExportedSymbol &rhs) { + return std::get<1>(lhs) < std::get<1>(rhs); +} + +static ExportedSymbolSeq TBDv2Symbols = { + {SymbolKind::GlobalSymbol, "$ld$hide$os9.0$_sym1", false, false}, + {SymbolKind::GlobalSymbol, "_sym1", false, false}, + {SymbolKind::GlobalSymbol, "_sym2", false, false}, + {SymbolKind::GlobalSymbol, "_sym3", false, false}, + {SymbolKind::GlobalSymbol, "_sym4", false, false}, + {SymbolKind::GlobalSymbol, "_sym5", false, false}, + {SymbolKind::GlobalSymbol, "_tlv1", false, true}, + {SymbolKind::GlobalSymbol, "_tlv2", false, true}, + {SymbolKind::GlobalSymbol, "_tlv3", false, true}, + {SymbolKind::GlobalSymbol, "_weak1", true, false}, + {SymbolKind::GlobalSymbol, "_weak2", true, false}, + {SymbolKind::GlobalSymbol, "_weak3", true, false}, + {SymbolKind::ObjectiveCClass, "class1", false, false}, + {SymbolKind::ObjectiveCClass, "class2", false, false}, + {SymbolKind::ObjectiveCClass, "class3", false, false}, + {SymbolKind::ObjectiveCInstanceVariable, "class1._ivar1", false, false}, + {SymbolKind::ObjectiveCInstanceVariable, "class1._ivar2", false, false}, + {SymbolKind::ObjectiveCInstanceVariable, "class1._ivar3", false, false}, +}; + +namespace TBDv2 { + +TEST(TBDv2, ReadFile) { + static const char tbd_v2_file1[] = + "--- !tapi-tbd-v2\n" + "archs: [ armv7, armv7s, armv7k, arm64 ]\n" + "platform: ios\n" + "flags: [ installapi ]\n" + "install-name: Test.dylib\n" + "current-version: 2.3.4\n" + "compatibility-version: 1.0\n" + "swift-version: 1.1\n" + "parent-umbrella: Umbrella.dylib\n" + "exports:\n" + " - archs: [ armv7, armv7s, armv7k, arm64 ]\n" + " allowable-clients: [ clientA ]\n" + " re-exports: [ /usr/lib/libfoo.dylib ]\n" + " symbols: [ _sym1, _sym2, _sym3, _sym4, $ld$hide$os9.0$_sym1 ]\n" + " objc-classes: [ _class1, _class2 ]\n" + " objc-ivars: [ _class1._ivar1, _class1._ivar2 ]\n" + " weak-def-symbols: [ _weak1, _weak2 ]\n" + " thread-local-symbols: [ _tlv1, _tlv2 ]\n" + " - archs: [ armv7, armv7s, armv7k ]\n" + " symbols: [ _sym5 ]\n" + " objc-classes: [ _class3 ]\n" + " objc-ivars: [ _class1._ivar3 ]\n" + " weak-def-symbols: [ _weak3 ]\n" + " thread-local-symbols: [ _tlv3 ]\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v2_file1, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V2, File->getFileType()); + auto Archs = Architecture::armv7 | Architecture::armv7s | + Architecture::armv7k | Architecture::arm64; + EXPECT_EQ(Archs, File->getArchitectures()); + EXPECT_EQ(Platform::iOS, File->getPlatform()); + EXPECT_EQ(std::string("Test.dylib"), File->getInstallName()); + EXPECT_EQ(PackedVersion(2, 3, 4), File->getCurrentVersion()); + EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion()); + EXPECT_EQ(2U, File->getSwiftABIVersion()); + EXPECT_EQ(ObjCConstraint::Retain_Release, File->getObjCConstraint()); + EXPECT_TRUE(File->isTwoLevelNamespace()); + EXPECT_TRUE(File->isApplicationExtensionSafe()); + EXPECT_TRUE(File->isInstallAPI()); + InterfaceFileRef client("clientA", Archs); + InterfaceFileRef reexport("/usr/lib/libfoo.dylib", Archs); + EXPECT_EQ(1U, File->allowableClients().size()); + EXPECT_EQ(client, File->allowableClients().front()); + EXPECT_EQ(1U, File->reexportedLibraries().size()); + EXPECT_EQ(reexport, File->reexportedLibraries().front()); + + ExportedSymbolSeq Exports; + for (const auto *Sym : File->symbols()) { + EXPECT_FALSE(Sym->isWeakReferenced()); + EXPECT_FALSE(Sym->isUndefined()); + Exports.emplace_back(Sym->getKind(), Sym->getName(), Sym->isWeakDefined(), + Sym->isThreadLocalValue()); + } + llvm::sort(Exports.begin(), Exports.end()); + + EXPECT_EQ(TBDv2Symbols.size(), Exports.size()); + EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(), TBDv2Symbols.begin())); +} + +TEST(TBDv2, ReadFile2) { + static const char tbd_v2_file2[] = + "--- !tapi-tbd-v2\n" + "archs: [ armv7, armv7s, armv7k, arm64 ]\n" + "platform: ios\n" + "flags: [ flat_namespace, not_app_extension_safe ]\n" + "install-name: Test.dylib\n" + "swift-version: 1.1\n" + "exports:\n" + " - archs: [ armv7, armv7s, armv7k, arm64 ]\n" + " symbols: [ _sym1, _sym2, _sym3, _sym4, $ld$hide$os9.0$_sym1 ]\n" + " objc-classes: [ _class1, _class2 ]\n" + " objc-ivars: [ _class1._ivar1, _class1._ivar2 ]\n" + " weak-def-symbols: [ _weak1, _weak2 ]\n" + " thread-local-symbols: [ _tlv1, _tlv2 ]\n" + " - archs: [ armv7, armv7s, armv7k ]\n" + " symbols: [ _sym5 ]\n" + " objc-classes: [ _class3 ]\n" + " objc-ivars: [ _class1._ivar3 ]\n" + " weak-def-symbols: [ _weak3 ]\n" + " thread-local-symbols: [ _tlv3 ]\n" + "undefineds:\n" + " - archs: [ armv7, armv7s, armv7k, arm64 ]\n" + " symbols: [ _undefSym1, _undefSym2, _undefSym3 ]\n" + " objc-classes: [ _undefClass1, _undefClass2 ]\n" + " objc-ivars: [ _undefClass1._ivar1, _undefClass1._ivar2 ]\n" + " weak-ref-symbols: [ _undefWeak1, _undefWeak2 ]\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v2_file2, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V2, File->getFileType()); + auto Archs = Architecture::armv7 | Architecture::armv7s | + Architecture::armv7k | Architecture::arm64; + EXPECT_EQ(Archs, File->getArchitectures()); + EXPECT_EQ(Platform::iOS, File->getPlatform()); + EXPECT_EQ(std::string("Test.dylib"), File->getInstallName()); + EXPECT_EQ(PackedVersion(1, 0, 0), File->getCurrentVersion()); + EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion()); + EXPECT_EQ(2U, File->getSwiftABIVersion()); + EXPECT_EQ(ObjCConstraint::Retain_Release, File->getObjCConstraint()); + EXPECT_FALSE(File->isTwoLevelNamespace()); + EXPECT_FALSE(File->isApplicationExtensionSafe()); + EXPECT_FALSE(File->isInstallAPI()); + EXPECT_EQ(0U, File->allowableClients().size()); + EXPECT_EQ(0U, File->reexportedLibraries().size()); +} + +TEST(TBDv2, WriteFile) { + static const char tbd_v2_file3[] = + "--- !tapi-tbd-v2\n" + "archs: [ i386, x86_64 ]\n" + "platform: macosx\n" + "install-name: '/usr/lib/libfoo.dylib'\n" + "current-version: 1.2.3\n" + "compatibility-version: 0\n" + "swift-version: 5\n" + "exports: \n" + " - archs: [ i386 ]\n" + " symbols: [ _sym1 ]\n" + " weak-def-symbols: [ _sym2 ]\n" + " thread-local-symbols: [ _sym3 ]\n" + " - archs: [ x86_64 ]\n" + " allowable-clients: [ clientA ]\n" + " re-exports: [ '/usr/lib/libfoo.dylib' ]\n" + " symbols: [ '_OBJC_EHTYPE_$_Class1' ]\n" + " objc-classes: [ _Class1 ]\n" + " objc-ivars: [ _Class1._ivar1 ]\n" + "...\n"; + + InterfaceFile File; + File.setPath("libfoo.dylib"); + File.setInstallName("/usr/lib/libfoo.dylib"); + File.setFileType(FileType::TBD_V2); + File.setArchitectures(Architecture::i386 | Architecture::x86_64); + File.setPlatform(Platform::macOS); + File.setCurrentVersion(PackedVersion(1, 2, 3)); + File.setTwoLevelNamespace(); + File.setApplicationExtensionSafe(); + File.setSwiftABIVersion(5); + File.setObjCConstraint(ObjCConstraint::Retain_Release); + File.addAllowableClient("clientA", Architecture::x86_64); + File.addReexportedLibrary("/usr/lib/libfoo.dylib", Architecture::x86_64); + File.addSymbol(SymbolKind::GlobalSymbol, "_sym1", Architecture::i386); + File.addSymbol(SymbolKind::GlobalSymbol, "_sym2", Architecture::i386, + SymbolFlags::WeakDefined); + File.addSymbol(SymbolKind::GlobalSymbol, "_sym3", Architecture::i386, + SymbolFlags::ThreadLocalValue); + File.addSymbol(SymbolKind::ObjectiveCClass, "Class1", Architecture::x86_64); + File.addSymbol(SymbolKind::ObjectiveCClassEHType, "Class1", + Architecture::x86_64); + File.addSymbol(SymbolKind::ObjectiveCInstanceVariable, "Class1._ivar1", + Architecture::x86_64); + + SmallString<4096> Buffer; + raw_svector_ostream OS(Buffer); + auto Result = Registry::writeFile(OS, &File); + EXPECT_FALSE(Result); + EXPECT_STREQ(tbd_v2_file3, Buffer.c_str()); +} + +TEST(TBDv2, Platform_macOS) { + static const char tbd_v1_platform_macos[] = "--- !tapi-tbd-v2\n" + "archs: [ x86_64 ]\n" + "platform: macosx\n" + "install-name: Test.dylib\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_platform_macos, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V2, File->getFileType()); + EXPECT_EQ(Platform::macOS, File->getPlatform()); +} + +TEST(TBDv2, Platform_iOS) { + static const char tbd_v1_platform_ios[] = "--- !tapi-tbd-v2\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_platform_ios, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V2, File->getFileType()); + EXPECT_EQ(Platform::iOS, File->getPlatform()); +} + +TEST(TBDv2, Platform_watchOS) { + static const char tbd_v1_platform_watchos[] = "--- !tapi-tbd-v2\n" + "archs: [ armv7k ]\n" + "platform: watchos\n" + "install-name: Test.dylib\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_platform_watchos, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V2, File->getFileType()); + EXPECT_EQ(Platform::watchOS, File->getPlatform()); +} + +TEST(TBDv2, Platform_tvOS) { + static const char tbd_v1_platform_tvos[] = "--- !tapi-tbd-v2\n" + "archs: [ arm64 ]\n" + "platform: tvos\n" + "install-name: Test.dylib\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_platform_tvos, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V2, File->getFileType()); + EXPECT_EQ(Platform::tvOS, File->getPlatform()); +} + +TEST(TBDv2, Platform_bridgeOS) { + static const char tbd_v1_platform_bridgeos[] = "--- !tapi-tbd-v2\n" + "archs: [ armv7k ]\n" + "platform: bridgeos\n" + "install-name: Test.dylib\n" + "...\n"; + + auto Buffer = + MemoryBuffer::getMemBuffer(tbd_v1_platform_bridgeos, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V2, File->getFileType()); + EXPECT_EQ(Platform::bridgeOS, File->getPlatform()); +} + +TEST(TBDv2, Swift_1_0) { + static const char tbd_v1_swift_1_0[] = "--- !tapi-tbd-v2\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "swift-version: 1.0\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_swift_1_0, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V2, File->getFileType()); + EXPECT_EQ(1U, File->getSwiftABIVersion()); +} + +TEST(TBDv2, Swift_1_1) { + static const char tbd_v1_swift_1_1[] = "--- !tapi-tbd-v2\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "swift-version: 1.1\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_swift_1_1, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V2, File->getFileType()); + EXPECT_EQ(2U, File->getSwiftABIVersion()); +} + +TEST(TBDv2, Swift_2_0) { + static const char tbd_v1_swift_2_0[] = "--- !tapi-tbd-v2\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "swift-version: 2.0\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_swift_2_0, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V2, File->getFileType()); + EXPECT_EQ(3U, File->getSwiftABIVersion()); +} + +TEST(TBDv2, Swift_3_0) { + static const char tbd_v1_swift_3_0[] = "--- !tapi-tbd-v2\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "swift-version: 3.0\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_swift_3_0, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V2, File->getFileType()); + EXPECT_EQ(4U, File->getSwiftABIVersion()); +} + +TEST(TBDv2, Swift_4_0) { + static const char tbd_v1_swift_4_0[] = "--- !tapi-tbd-v2\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "swift-version: 4.0\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_swift_4_0, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_FALSE(!!Result); + auto errorMessage = toString(Result.takeError()); + EXPECT_EQ("malformed file\nTest.tbd:5:16: error: invalid Swift ABI " + "version.\nswift-version: 4.0\n ^~~\n", + errorMessage); +} + +TEST(TBDv2, Swift_5) { + static const char tbd_v1_swift_5[] = "--- !tapi-tbd-v2\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "swift-version: 5\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_swift_5, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V2, File->getFileType()); + EXPECT_EQ(5U, File->getSwiftABIVersion()); +} + +TEST(TBDv2, Swift_99) { + static const char tbd_v1_swift_99[] = "--- !tapi-tbd-v2\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "swift-version: 99\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(tbd_v1_swift_99, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); + auto File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V2, File->getFileType()); + EXPECT_EQ(99U, File->getSwiftABIVersion()); +} + +TEST(TBDv2, UnknownArchitecture) { + static const char tbd_v2_file_unknown_architecture[] = + "--- !tapi-tbd-v2\n" + "archs: [ foo ]\n" + "platform: macosx\n" + "install-name: Test.dylib\n" + "...\n"; + + auto Buffer = + MemoryBuffer::getMemBuffer(tbd_v2_file_unknown_architecture, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_TRUE(!!Result); +} + +TEST(TBDv2, UnknownPlatform) { + static const char tbd_v2_file_unknown_platform[] = "--- !tapi-tbd-v2\n" + "archs: [ i386 ]\n" + "platform: newOS\n" + "...\n"; + + auto Buffer = + MemoryBuffer::getMemBuffer(tbd_v2_file_unknown_platform, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_FALSE(!!Result); + auto errorMessage = toString(Result.takeError()); + EXPECT_EQ("malformed file\nTest.tbd:3:11: error: unknown platform\nplatform: " + "newOS\n ^~~~~\n", + errorMessage); +} + +// Test for invalid files. +TEST(TBDv2, UnsupportedFileType) { + static const unsigned char unsupported_file[] = { + 0xcf, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00}; + + auto Buffer = MemoryBuffer::getMemBuffer( + reinterpret_cast(unsupported_file), "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_FALSE(!!Result); + auto errorMessage = toString(Result.takeError()); + ASSERT_EQ("unsupported file type", errorMessage); +} + +TEST(TBDv2, MalformedFile1) { + static const char malformed_file1[] = "--- !tapi-tbd-v2\n" + "archs: [ arm64 ]\n" + "foobar: \"Unsupported key\"\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(malformed_file1, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_FALSE(!!Result); + auto errorMessage = toString(Result.takeError()); + ASSERT_EQ("malformed file\nTest.tbd:2:1: error: missing required key " + "'platform'\narchs: [ arm64 ]\n^\n", + errorMessage); +} + +TEST(TBDv2, MalformedFile2) { + static const char malformed_file2[] = "--- !tapi-tbd-v2\n" + "archs: [ arm64 ]\n" + "platform: ios\n" + "install-name: Test.dylib\n" + "foobar: \"Unsupported key\"\n" + "...\n"; + + auto Buffer = MemoryBuffer::getMemBuffer(malformed_file2, "Test.tbd"); + auto Result = Registry::readFile(Buffer->getMemBufferRef()); + EXPECT_FALSE(!!Result); + auto errorMessage = toString(Result.takeError()); + ASSERT_EQ( + "malformed file\nTest.tbd:5:9: error: unknown key 'foobar'\nfoobar: " + "\"Unsupported key\"\n ^~~~~~~~~~~~~~~~~\n", + errorMessage); +} + +} // namespace TBDv2