diff --git a/Package.resolved b/Package.resolved index 1a46105..4ea5273 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,6 +1,15 @@ { "object": { "pins": [ + { + "package": "AssociatedTypeRequirementsKit", + "repositoryURL": "https://github.com/nerdsupremacist/AssociatedTypeRequirementsKit.git", + "state": { + "branch": null, + "revision": "5c0b95758d2d65fdb98977ee1af5648d9d192a32", + "version": "0.2.0" + } + }, { "package": "SnapshotTesting", "repositoryURL": "https://github.com/pointfreeco/swift-snapshot-testing.git", diff --git a/Package.swift b/Package.swift index 6852aa2..2f64906 100644 --- a/Package.swift +++ b/Package.swift @@ -14,14 +14,12 @@ let package = Package( ], dependencies: [ .package(name: "SnapshotTesting", url: "https://github.com/pointfreeco/swift-snapshot-testing.git", from: "1.8.1"), + .package(url: "https://github.com/nerdsupremacist/AssociatedTypeRequirementsKit.git", from: "0.2.0"), ], targets: [ - .target( - name: "CSymbols", - dependencies: []), .target( name: "SwiftShortcuts", - dependencies: ["CSymbols"]), + dependencies: ["AssociatedTypeRequirementsKit"]), .testTarget( name: "SwiftShortcutsTests", dependencies: ["SwiftShortcuts", "SnapshotTesting"], diff --git a/Sources/CSymbols/CSymbols.c b/Sources/CSymbols/CSymbols.c deleted file mode 100644 index b0bdee1..0000000 --- a/Sources/CSymbols/CSymbols.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -void *makeAnyShortcutSymbol(void) { - extern void _swift_shortcuts_makeAnyShortcut(void); - return &_swift_shortcuts_makeAnyShortcut; -} - -void *decomposeIntoActionsSymbol(void) { - extern void _swift_shortcuts_decomposeIntoActions(void); - return &_swift_shortcuts_decomposeIntoActions; -} diff --git a/Sources/CSymbols/include/CSymbols.h b/Sources/CSymbols/include/CSymbols.h deleted file mode 100644 index d7c2511..0000000 --- a/Sources/CSymbols/include/CSymbols.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef CSymbols_h -#define CSymbols_h - -void *_Nonnull makeAnyShortcutSymbol(void); -void *_Nonnull decomposeIntoActionsSymbol(void); - -#endif /* CSymbols_h */ diff --git a/Sources/SwiftShortcuts/Internal/ActionDecomposable.swift b/Sources/SwiftShortcuts/Internal/ActionDecomposable.swift index 36d2c50..7cbc464 100644 --- a/Sources/SwiftShortcuts/Internal/ActionDecomposable.swift +++ b/Sources/SwiftShortcuts/Internal/ActionDecomposable.swift @@ -83,6 +83,6 @@ extension Optional: ActionDecomposable where Wrapped: Shortcut { extension TupleShortcut: ActionDecomposable { func decompose() -> [Action] { let mirror = Mirror(reflecting: value) - return mirror.children.flatMap { _, value in AnyShortcut(_fromValue: value)!.decompose() } + return mirror.children.flatMap { _, value in actionComponents(from: value)! } } } diff --git a/Sources/SwiftShortcuts/Internal/FromAny.swift b/Sources/SwiftShortcuts/Internal/FromAny.swift index ae35f17..9cb4606 100644 --- a/Sources/SwiftShortcuts/Internal/FromAny.swift +++ b/Sources/SwiftShortcuts/Internal/FromAny.swift @@ -1,142 +1,47 @@ -#if os(Linux) -import CSymbols -#else -import Foundation -#endif + +import AssociatedTypeRequirementsVisitor + +private let typeEraser = ShortcutTypeEraser() +private let decomposer = ShortcutDecomposer() // MARK: - AnyShortcut from Any extension AnyShortcut { init?(_fromValue value: Any) { - // Synthesize a fake protocol conformance record to AnyShortcutConvertible - let conformance = ProtocolConformanceRecord(type: type(of: value), witnessTable: 0) - let type = unsafeBitCast(conformance, to: AnyShortcutConvertible.Type.self) - - guard let action = type.anyShortcut(from: value) else { - return nil - } + guard let action = typeEraser(value) else { return nil } self = action } } -// MARK: - AnyViewConvertible - -private let actionMetadata: ProtocolMetadata = { - let module = "SwiftShortcuts" - let name = "Shortcut" - let postfix = "_p" - let mangled = "\(module.count)\(module)\(name.count)\(name)\(postfix)" - return ProtocolMetadata(type: _typeByName(mangled)!) -}() - -private protocol AnyShortcutConvertible {} - -extension AnyShortcutConvertible { - static func anyShortcut(from action: Any) -> AnyShortcut? { - guard let witnessTable = _conformsToProtocol(Self.self, actionMetadata.protocolDescriptorVector) else { - return nil - } - - let conformanceRecord = ProtocolConformanceRecord(type: Self.self, witnessTable: Int(bitPattern: witnessTable)) - return withUnsafePointer(to: action as! Self) { pointer in makeAnyShortcut(pointer, conformanceRecord) } - } -} +// MARK: - Actions from Any -@_silgen_name("_swift_shortcuts_makeAnyShortcut") -@available(*, unavailable) -public func makeAnyShortcut(from shortcut: S) -> AnyShortcut { - return AnyShortcut(shortcut) +func actionComponents(from value: Any) -> [Action]? { + return decomposer(value) } -private typealias ShortcutToAnyShortcutFunction = @convention(thin) (UnsafeRawPointer, ProtocolConformanceRecord) -> AnyShortcut - -#if os(Linux) -private let makeAnyShortcut = unsafeBitCast(makeAnyShortcutSymbol(), to: ShortcutToAnyShortcutFunction.self) -#else -private let makeAnyShortcut: ShortcutToAnyShortcutFunction = { - let symbolName = "_swift_shortcuts_makeAnyShortcut" - let handle = dlopen(nil, RTLD_GLOBAL) - let pointer = dlsym(handle, symbolName) - return unsafeBitCast(pointer, to: ShortcutToAnyShortcutFunction.self) -}() -#endif - -// MARK: - [ActionComponent] from Any - -private protocol ActionsConvertible {} - -extension ActionsConvertible { - static func actions(from shortcut: Any) -> [Action]? { - guard let witnessTable = _conformsToProtocol(Self.self, actionMetadata.protocolDescriptorVector) else { - return nil - } +// MARK: - ShortcutVisitor - let conformanceRecord = ProtocolConformanceRecord(type: Self.self, witnessTable: Int(bitPattern: witnessTable)) - return withUnsafePointer(to: shortcut as! Self) { pointer in decomposeIntoActions(pointer, conformanceRecord) } - } -} +private protocol ShortcutVisitor: AssociatedTypeRequirementsVisitor { + associatedtype Visitor = ShortcutVisitor + associatedtype Input = Shortcut + associatedtype Output -@_silgen_name("_swift_shortcuts_decomposeIntoActions") -@available(*, unavailable) -public func decomposeIntoActions(shortcut: S) -> [Action] { - _decomposeIntoActions(shortcut: shortcut) + func callAsFunction(_ shortcut: S) -> Output } -private func _decomposeIntoActions(shortcut: S) -> [Action] { - if S.Body.self == Never.self { - return shortcut.decompose() - } +private struct ShortcutTypeEraser : ShortcutVisitor { - let body = shortcut.body - if let component = body as? Action { - return [component] - } else { - return _decomposeIntoActions(shortcut: body) + func callAsFunction(_ shortcut: S) -> AnyShortcut { + return AnyShortcut(shortcut) } -} - -private typealias ShortcutToActionsFunction = @convention(thin) (UnsafeRawPointer, ProtocolConformanceRecord) -> [Action] -#if os(Linux) -private let decomposeIntoActions = unsafeBitCast(decomposeIntoActionsSymbol(), to: ShortcutToActionsFunction.self) -#else -private let decomposeIntoActions: ShortcutToActionsFunction = { - let symbolName = "_swift_shortcuts_decomposeIntoActions" - let handle = dlopen(nil, RTLD_GLOBAL) - let pointer = dlsym(handle, symbolName) - return unsafeBitCast(pointer, to: ShortcutToActionsFunction.self) -}() -#endif - -func actionComponents(from value: Any) -> [Action]? { - // Synthesize a fake protocol conformance record to ActionStepsConvertible - let conformance = ProtocolConformanceRecord(type: type(of: value), witnessTable: 0) - let type = unsafeBitCast(conformance, to: ActionsConvertible.Type.self) - return type.actions(from: value) -} -// MARK: - Protocol Runtime Information - -private struct ProtocolConformanceRecord { - let type: Any.Type - let witnessTable: Int } -private struct ProtocolDescriptor {} - -private struct ProtocolMetadata { - let kind: Int - let layoutFlags: UInt32 - let numberOfProtocols: UInt32 - let protocolDescriptorVector: UnsafeMutablePointer +private struct ShortcutDecomposer : ShortcutVisitor { - init(type: Any.Type) { - self = unsafeBitCast(type, to: UnsafeMutablePointer.self).pointee + func callAsFunction(_ shortcut: S) -> [Action] { + return shortcut.decompose() } -} -@_silgen_name("swift_conformsToProtocol") -private func _conformsToProtocol( - _ type: Any.Type, - _ protocolDescriptor: UnsafeMutablePointer -) -> UnsafeRawPointer? +}