import Foundation extension AnyKeyPath: CustomDumpStringConvertible { public var customDumpDescription: String { // NB: We can't currently rely on SE-0369 due to this crasher: // https://github.com/apple/swift/issues/64865 // // #if swift(>=5.8) // if #available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) { // return self.debugDescription // } // #endif #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) keyPathToNameLock.lock() defer { keyPathToNameLock.unlock() } guard let name = keyPathToName[self] else { func reflectName() -> String { var namedKeyPaths = Reflection.allNamedKeyPaths(forUnderlyingTypeOf: Self.rootType) while !namedKeyPaths.isEmpty { let (name, keyPath) = namedKeyPaths.removeFirst() if keyPath == self { return #"\\#(typeName(Self.rootType)).\#(name)"# } let valueType = type(of: keyPath).valueType let valueNamedKeyPaths = Reflection.allNamedKeyPaths(forUnderlyingTypeOf: valueType) for (valueName, valueKeyPath) in valueNamedKeyPaths { if let appendedKeyPath = keyPath.appending(path: valueKeyPath) { namedKeyPaths.append(("\(name).\(valueName)", appendedKeyPath)) } } } return """ \(typeName(Self.self))<\ \(typeName(Self.rootType, genericsAbbreviated: false)), \ \(typeName(Self.valueType, genericsAbbreviated: false))> """ } let name = reflectName() keyPathToName[self] = name return name } return name #else return """ \(typeName(Self.self))<\ \(typeName(Self.rootType, genericsAbbreviated: false)), \ \(typeName(Self.valueType, genericsAbbreviated: false))> """ #endif } } #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) private var keyPathToNameLock = NSRecursiveLock() private var keyPathToName: [AnyKeyPath: String] = [:] // The source code below was extracted from the "KeyPath Reflection" branch of Apple's // "Swift Evolution Staging" package: // // https://github.com/apple/swift-evolution-staging/tree/reflection //===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// private protocol RelativePointer { associatedtype Pointee var offset: Int32 { get } func address(from ptr: UnsafeRawPointer) -> UnsafePointer func pointee(from ptr: UnsafeRawPointer) -> Pointee? } extension RelativePointer { fileprivate func address(from ptr: UnsafeRawPointer) -> UnsafePointer { let newPtr = UnsafeRawPointer( bitPattern: UInt(bitPattern: ptr) &+ UInt(bitPattern: Int(offset)))! return newPtr.assumingMemoryBound(to: Pointee.self) } } private struct RelativeDirectPointer: RelativePointer { let offset: Int32 func pointee(from ptr: UnsafeRawPointer) -> Pointee? { guard offset != 0 else { return nil } return address(from: ptr).pointee } } extension UnsafeRawPointer { fileprivate func relativeDirect(as type: T.Type) -> UnsafePointer { let relativePointer = RelativeDirectPointer( offset: load(as: Int32.self) ) return relativePointer.address(from: self) } } private struct RelativeIndirectPointer: RelativePointer { typealias Pointee = UnsafePointer let offset: Int32 func pointee(from ptr: UnsafeRawPointer) -> Pointee? { guard offset != 0 else { return nil } return address(from: ptr).pointee } } private struct RelativeIndirectablePointer: RelativePointer { let offset: Int32 func address(from ptr: UnsafeRawPointer) -> UnsafePointer { UnsafePointer((ptr + Int(offset & ~1))._rawValue) } func pointee(from ptr: UnsafeRawPointer) -> Pointee? { guard offset != 0 else { return nil } if offset & 1 == 1 { let pointer = UnsafeRawPointer(address(from: ptr)) .load(as: UnsafePointer.self) return pointer.pointee } else { return address(from: ptr).pointee } } } //===----------------------------------------------------------------------===// // Metadata Structures //===----------------------------------------------------------------------===// // MetadataKind is the discriminator value found at the start of all metadata // records to determine what kind is a metadata. private enum MetadataKind: Int { case `class` = 0 case `struct` = 512 } // Metadata refers to the runtime representation of a type in Swift. This // protocol is the generic version handed out by various methods to retrieve // metadata from types. private protocol Metadata { // The required backing pointer which points at the metadata record. var pointer: UnsafeRawPointer { get } // The discriminator which determines what kind of metadata this is. var kind: MetadataKind { get } } extension Metadata { // The type representation of the metadata. fileprivate var type: Any.Type { unsafeBitCast(pointer, to: Any.Type.self) } } // Given an arbitrary type of anything, produce the metadata that represents // said type. // FIXME: Right now this only supports structs and class types, but in the // future if we ever want to produce keypaths for tuples, enums, etc. handle // that here. private func getMetadata(for type: Any.Type) -> Metadata? { let pointer = unsafeBitCast(type, to: UnsafeRawPointer.self) let int = pointer.load(as: Int.self) guard let kind = MetadataKind(rawValue: int) else { // If the metadata kind is greater than 2047, then it's an ISA pointer // meaning we have some class metadata. guard int > 2047 else { return nil } return ClassMetadata(pointer: pointer) } switch kind { case .class: return ClassMetadata(pointer: pointer) case .struct: return StructMetadata(pointer: pointer) } } // Type Metadata // Type Metadata is a more specialized metadata in that only struct, class, and // enum types conform to. There's more detail about the type and its properties // in the context descriptors, the generic types that make up said type, etc. private protocol TypeMetadata: Metadata {} extension TypeMetadata { // The context descriptors describes more in detail about the type. Some of // this information includes number of properties, the property names, the // name of this type, generic requirements, etc. fileprivate var contextDescriptor: TypeContextDescriptor { switch self { case let structMetadata as StructMetadata: return structMetadata.descriptor case let classMetadata as ClassMetadata: return classMetadata.descriptor default: fatalError("TypeMetadata conformance we don't know about?") } } // An array of integers that represent the offset to a certain field. This // corresponds to the index of fields in the field descriptor. fileprivate var fieldOffsets: [Int] { switch self { case let structMetadata as StructMetadata: return structMetadata.fieldOffsets case let classMetadata as ClassMetadata: return classMetadata.fieldOffsets default: fatalError("TypeMetadata conformance we don't know about?") } } // The pointer to the beginning of this type's generic arguments. fileprivate var genericArgumentPointer: UnsafeRawPointer { switch self { case is StructMetadata: return pointer + MemoryLayout<_StructMetadata>.size case let classMetadata as ClassMetadata: let descriptor = classMetadata.descriptor guard !descriptor.typeFlags.classHasResilientSuperclass else { let memberOffset = descriptor.resilientBounds._immediateMembersOffset return pointer + memberOffset } let negativeSize = descriptor.negativeSize let positiveSize = descriptor.positiveSize let numImmediateMembers = descriptor.numImmediateMembers if descriptor.typeFlags.classAreImmediateMembersNegative { return pointer + MemoryLayout.size * -negativeSize } else { return pointer + MemoryLayout.size * (positiveSize - numImmediateMembers) } default: fatalError("TypeMetadata conformance we don't know about?") } } // Given a mangled name (preferrably one of the property type name's), return // the type as represented by the mangled name within this type's context. fileprivate func type(of mangledName: UnsafePointer) -> Any.Type? { let type = _getTypeByMangledNameInContext( UnsafePointer(mangledName._rawValue), UInt(getSymbolicMangledNameLength(UnsafeRawPointer(mangledName))), genericContext: contextDescriptor.pointer, genericArguments: genericArgumentPointer ) return type } } // Struct Metadata // Struct Metadata refers to types whom are implemented via a struct. Consider // the standard library type 'Int', it's implemented using a struct, so getting // the type metadata for that type will return an instance of struct metadata. private struct StructMetadata: TypeMetadata, LayoutWrapper { typealias Layout = _StructMetadata // The backing metadata pointer. let pointer: UnsafeRawPointer // The metadata discriminator. var kind: MetadataKind { .struct } // The context descriptor of this struct. var descriptor: StructDescriptor { layout.descriptor } // An array of integers with the offsets for each stored field in this struct. var fieldOffsets: [Int] { let fieldOffsetVectorOffset = descriptor.fieldOffsetVectorOffset let start = pointer + MemoryLayout.size * fieldOffsetVectorOffset let buffer = UnsafeBufferPointer( start: UnsafePointer(start._rawValue), count: descriptor.numFields ) return Array(buffer).map { Int($0) } } } private struct _StructMetadata { let kind: Int let descriptor: StructDescriptor } // Class Metadata // Class Metadata refers to types whom are implemented via a class. Consider // the standard library type 'KeyPath', it's implemented using a class, so // getting the type metadata for that type will return an instance of class // metadata. private struct ClassMetadata: TypeMetadata, LayoutWrapper { typealias Layout = _ClassMetadata // The backing metadata pointer. let pointer: UnsafeRawPointer // The metadata discriminator. var kind: MetadataKind { .class } // The context descriptor of this class. var descriptor: ClassDescriptor { layout._descriptor } // An array of integers with the offsets for each stored field in this class. var fieldOffsets: [Int] { let fieldOffsetVectorOffset = descriptor.fieldOffsetVectorOffset let start = pointer + MemoryLayout.size * fieldOffsetVectorOffset let buffer = UnsafeBufferPointer( start: UnsafePointer(start._rawValue), count: descriptor.numFields ) return Array(buffer) } // The required size of instances of this type. var instanceSize: Int { Int(layout._instanceSize) } // The alignment mask of the address point for instances of this type. var instanceAlignMask: Int { Int(layout._instanceAlignMask) } } private struct _ClassMetadata { let _kind: Int let _superclass: Any.Type? let _reserved: (Int, Int) let _rodata: Int let _flags: UInt32 let _instanceAddressPoint: UInt32 let _instanceSize: UInt32 let _instanceAlignMask: UInt16 let _runtimeReserved: UInt16 let _classSize: UInt32 let _classAddressPoint: UInt32 let _descriptor: ClassDescriptor } //===----------------------------------------------------------------------===// // Context Descriptor Structures //===----------------------------------------------------------------------===// // A context descriptor describes in entity in Swift who declares some context // which other declarations can be declared within. private protocol ContextDescriptor { // The backing context descriptor pointer. var pointer: UnsafeRawPointer { get } } extension ContextDescriptor { // The base structural representation of a context descriptor. fileprivate var _contextDescriptor: _ContextDescriptor { pointer.load(as: _ContextDescriptor.self) } // Flags that describe this context which include what kind it is, whether // or not it's a generic context, whether or not it's unique, etc. fileprivate var flags: ContextDescriptorFlags { _contextDescriptor._flags } } private struct _ContextDescriptor { let _flags: ContextDescriptorFlags let _parent: RelativeIndirectablePointer<_ContextDescriptor> } // Flags that describe this context which include what kind it is, whether // or not it's a generic context, whether or not it's unique, etc. private struct ContextDescriptorFlags { // The backing integer representation of these flags. let bits: UInt32 // The reserved bits for other flags that are interpretted differently by // conforming context descriptor types. var kindSpecificFlags: UInt16 { UInt16((bits >> 0x10) & 0xFFFF) } } // Type Context Descriptor // A type context descriptor is a refined context descriptor who describes a // type in Swift. This includes structs, classes, and enums. Protocols also // define a new type in Swift, but aren't considered type contexts. private protocol TypeContextDescriptor: ContextDescriptor { // The field descriptor that describes the stored representation of this type. var fields: FieldDescriptor { get } } extension TypeContextDescriptor { // The base structural representation of a type context descriptor. fileprivate var _typeDescriptor: _TypeContextDescriptor { pointer.load(as: _TypeContextDescriptor.self) } // The field descriptor that describes the stored representation of this type. fileprivate var fields: FieldDescriptor { let offset = pointer.advanced(by: MemoryLayout.size * 4) let address = UnsafeRawPointer(_typeDescriptor._fields.address(from: offset)) return FieldDescriptor(signedPointer: address) } // Certain flags specific to types in Swift, such as whether or not a class // has a resilient superclass. fileprivate var typeFlags: TypeContextDescriptorFlags { TypeContextDescriptorFlags(bits: flags.kindSpecificFlags) } } private struct _TypeContextDescriptor { let _base: _ContextDescriptor let _name: RelativeDirectPointer let _accessor: RelativeDirectPointer let _fields: RelativeDirectPointer<_FieldDescriptor> } // Certain flags specific to types in Swift, such as whether or not a class // has a resilient superclass. private struct TypeContextDescriptorFlags { // The backing integer representation of these flags. let bits: UInt16 // Whether or not the class's members are negative. var classAreImmediateMembersNegative: Bool { bits & 0x1000 != 0 } // Whether or not the class has a resilient superclass. var classHasResilientSuperclass: Bool { bits & 0x2000 != 0 } } // Struct Descriptor // A struct descriptor that describes some structure context. private struct StructDescriptor: TypeContextDescriptor, PointerAuthenticatedLayoutWrapper { typealias Layout = _StructDescriptor // The backing context descriptor pointer. let signedPointer: UnsafeRawPointer // The offset to the field offset vector found in the metadata. var fieldOffsetVectorOffset: Int { Int(layout._fieldOffsetVectorOffset) } // The number of stored properties this struct has. var numFields: Int { Int(layout._numFields) } } private struct _StructDescriptor { let _base: _TypeContextDescriptor let _numFields: UInt32 let _fieldOffsetVectorOffset: UInt32 } // Class Descriptor // A class descriptor that descibes some class context. private struct ClassDescriptor: TypeContextDescriptor, PointerAuthenticatedLayoutWrapper { typealias Layout = _ClassDescriptor // The backing context descriptor pointer. let signedPointer: UnsafeRawPointer // The offset to the field offset vector found in the metadata. var fieldOffsetVectorOffset: Int { Int(layout._fieldOffsetVectorOffset) } // The negative size of the metadata objects in this class. var negativeSize: Int { assert(!typeFlags.classHasResilientSuperclass) return Int(layout._negativeSizeOrResilientBounds) } // The number of stored properties this class defines. var numFields: Int { Int(layout._numFields) } // The total number of members this class defines (not including it's // superclass, if it has one). var numImmediateMembers: Int { Int(layout._numImmediateMembers) } // The positive size of the metadata objects in this class. var positiveSize: Int { assert(!typeFlags.classHasResilientSuperclass) return Int(layout._positiveSizeOrExtraFlags) } // The resilient bounds for this class. var resilientBounds: _StoredClassMetadataBounds { let addr = address(for: \._negativeSizeOrResilientBounds) let pointer = UnsafeRawPointer(addr) return pointer.relativeDirect(as: _StoredClassMetadataBounds.self).pointee } } private struct _ClassDescriptor { let _base: _TypeContextDescriptor let _superclassMangledName: RelativeDirectPointer let _negativeSizeOrResilientBounds: Int32 let _positiveSizeOrExtraFlags: Int32 let _numImmediateMembers: UInt32 let _numFields: UInt32 let _fieldOffsetVectorOffset: UInt32 } private struct _StoredClassMetadataBounds { let _immediateMembersOffset: Int } // Field Descriptor // A special descriptor that describes a type's fields. private struct FieldDescriptor: PointerAuthenticatedLayoutWrapper { typealias Layout = _FieldDescriptor // The backing field descriptor pointer. let signedPointer: UnsafeRawPointer // The number of fields this type has. This could mean different things // depending on what kind of type this is found under. For example, this is // the number of stored properties found within a struct, but for enums this // is the number of cases. var numFields: Int { Int(layout._numFields) } // An array of the field record information. Field record information contains // things like it's mangled type, whether or not its a var, indirect, etc. var records: [FieldRecord] { var result = [FieldRecord]() result.reserveCapacity(numFields) for i in 0...size * i result.append(FieldRecord(signedPointer: address)) } return result } } // A record that describes a single stored property or an enum case. private struct FieldRecord: PointerAuthenticatedLayoutWrapper { typealias Layout = _FieldRecord // The backing field record pointer. let signedPointer: UnsafeRawPointer // The flags that describe this field record. var flags: FieldRecordFlags { layout._flags } // The mangled type name that demangles to the field's type. var mangledTypeName: UnsafePointer { address(for: \._mangledTypeName) } // The name of the stored property/enum case. var name: String { String(cString: address(for: \._fieldName)) } } private struct _FieldDescriptor { let _mangledTypeName: RelativeDirectPointer let _superclassMangledTypeName: RelativeDirectPointer let _kind: UInt16 let _recordSize: UInt16 let _numFields: UInt32 } private struct _FieldRecord { let _flags: FieldRecordFlags let _mangledTypeName: RelativeDirectPointer let _fieldName: RelativeDirectPointer } // The flags which describe a field record. private struct FieldRecordFlags { // The backing integer representation of these flags. let bits: UInt32 // Whether or not this stored property is a var. var isVar: Bool { bits & 0x2 != 0 } } //===----------------------------------------------------------------------===// // Misc. Utilities //===----------------------------------------------------------------------===// private protocol LayoutWrapper { associatedtype Layout var pointer: UnsafeRawPointer { get } } private protocol PointerAuthenticatedLayoutWrapper: LayoutWrapper { var signedPointer: UnsafeRawPointer { get } } extension PointerAuthenticatedLayoutWrapper { fileprivate var pointer: UnsafeRawPointer { signedPointer } } extension LayoutWrapper { fileprivate var layout: Layout { pointer.load(as: Layout.self) } fileprivate var trailing: UnsafeRawPointer { pointer + MemoryLayout.size } fileprivate func address(for field: KeyPath) -> UnsafePointer { let offset = MemoryLayout.offset(of: field)! return UnsafePointer((pointer + offset)._rawValue) } fileprivate func address( for field: KeyPath ) -> UnsafePointer where T.Pointee == U { let offset = MemoryLayout.offset(of: field)! return layout[keyPath: field].address(from: pointer + offset) } } // This is a utility within KeyPath.swift in the standard library. If this // gets moved into there, then this goes away, but will have to rethink if this // goes into a different module. private func getSymbolicMangledNameLength(_ base: UnsafeRawPointer) -> Int { var end = base while let current = Optional(end.load(as: UInt8.self)), current != 0 { // Skip the current character end = end + 1 // Skip over a symbolic reference if current >= 0x1 && current <= 0x17 { end += 4 } else if current >= 0x18 && current <= 0x1F { end += MemoryLayout.size } } return end - base } @_silgen_name("swift_allocObject") internal func _allocObject(_: UnsafeMutableRawPointer, _: Int, _: Int) -> AnyObject? // This is a utility within KeyPath.swift in the standard library. If this // gets moved into there, then this goes away, but will have to rethink if this // goes into a different module. extension AnyKeyPath { fileprivate static func _create( capacityInBytes bytes: Int, initializedBy body: (UnsafeMutableRawBufferPointer) -> Void ) -> Self { assert( bytes > 0 && bytes % 4 == 0, "capacity must be multiple of 4 bytes") let metadata = getMetadata(for: self) as! ClassMetadata var size = metadata.instanceSize let tailStride = MemoryLayout.stride let tailAlignMask = MemoryLayout.alignment - 1 size += tailAlignMask size &= ~tailAlignMask size += tailStride * (bytes / 4) let alignment = metadata.instanceAlignMask | tailAlignMask let object = _allocObject( UnsafeMutableRawPointer(mutating: metadata.pointer), size, alignment ) guard object != nil else { fatalError("Allocating \(self) instance failed for keypath reflection") } // This memory layout of Int by 2 is the size of a heap object which object // points to. Tail members appear immediately afterwards. let base = unsafeBitCast(object, to: UnsafeMutableRawPointer.self) + MemoryLayout.size * 2 // The first word is the kvc string pointer. Set it to 0 (nil). base.storeBytes(of: 0, as: Int.self) // Return an offseted base after the kvc string pointer. let newBase = base + MemoryLayout.size let newBytes = bytes - MemoryLayout.size body(UnsafeMutableRawBufferPointer(start: newBase, count: newBytes)) return unsafeBitCast(object, to: self) } } // Helper struct to represent the keypath buffer header. This structure is also // found within KeyPath.swift, so if this gets moved there this goes away. private struct KeyPathBufferHeader { let bits: UInt32 init(hasReferencePrefix: Bool, isTrivial: Bool, size: UInt32) { var bits = size if hasReferencePrefix { bits |= 0x4000_0000 } if isTrivial { bits |= 0x8000_0000 } self.bits = bits } } // This initializes the raw keypath buffer with the field offset information. private func instantiateKeyPathBuffer( _ metadata: TypeMetadata, _ leafIndex: Int, _ data: UnsafeMutableRawBufferPointer ) { let header = KeyPathBufferHeader( hasReferencePrefix: false, isTrivial: true, size: UInt32(MemoryLayout.size) ) data.storeBytes(of: header, as: KeyPathBufferHeader.self) var componentBits = UInt32(metadata.fieldOffsets[leafIndex]) componentBits |= metadata.kind == .struct ? 1 << 24 : 3 << 24 data.storeBytes( of: componentBits, toByteOffset: MemoryLayout.size, as: UInt32.self ) } // Returns a concrete type for which this keypath is going to be given a root // and leaf type. private func getKeyPathType( from root: TypeMetadata, for leaf: FieldRecord ) -> AnyKeyPath.Type { let leafType = root.type(of: leaf.mangledTypeName)! func openRoot(_: Root.Type) -> AnyKeyPath.Type { func openLeaf(_: Value.Type) -> AnyKeyPath.Type { if leaf.flags.isVar { return root.kind == .class ? ReferenceWritableKeyPath.self : WritableKeyPath.self } return KeyPath.self } return _openExistential(leafType, do: openLeaf) } return _openExistential(root.type, do: openRoot) } // Given a root type and a leaf index, create a concrete keypath object at // runtime. private func createKeyPath(root: TypeMetadata, leaf: Int) -> AnyKeyPath { let field = root.contextDescriptor.fields.records[leaf] let keyPathTy = getKeyPathType(from: root, for: field) let size = MemoryLayout.size * 3 let instance = keyPathTy._create(capacityInBytes: size) { instantiateKeyPathBuffer(root, leaf, $0) } let heapObj = UnsafeRawPointer(Unmanaged.passRetained(instance).autorelease().toOpaque()) let keyPath = unsafeBitCast(heapObj, to: AnyKeyPath.self) return keyPath } private enum Reflection { /// Returns the collection of all named key paths of this type. /// /// - Parameter value: A value of any type to return the stored key paths of. /// - Returns: An array of tuples with both the name and partial key path /// for this value. static func allNamedKeyPaths( forUnderlyingTypeOf type: Any.Type ) -> [(name: String, keyPath: AnyKeyPath)] { guard let metadata = getMetadata(for: type) as? TypeMetadata else { return [] } var result = [(name: String, keyPath: AnyKeyPath)]() result.reserveCapacity(metadata.contextDescriptor.fields.numFields) for i in 0..