//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 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 // //===----------------------------------------------------------------------===// import SwiftShims internal func _abstract( methodName: StaticString = #function, file: StaticString = #file, line: UInt = #line ) -> Never { #if INTERNAL_CHECKS_ENABLED _fatalErrorMessage("abstract method", methodName, file: file, line: line, flags: _fatalErrorFlags()) #else _conditionallyUnreachable() #endif } // MARK: Type-erased abstract base classes // NOTE: older runtimes had Swift.AnyKeyPath as the ObjC name. // The two must coexist, so it was renamed. The old name must not be // used in the new runtime. _TtCs11_AnyKeyPath is the mangled name for // Swift._AnyKeyPath. /// A type-erased key path, from any root type to any resulting value /// type. @_objcRuntimeName(_TtCs11_AnyKeyPath) public class AnyKeyPath: Hashable, _AppendKeyPath { /// The root type for this key path. @inlinable public static var rootType: Any.Type { return _rootAndValueType.root } /// The value type for this key path. @inlinable public static var valueType: Any.Type { return _rootAndValueType.value } internal final var _kvcKeyPathStringPtr: UnsafePointer? /// The hash value. final public var hashValue: Int { return _hashValue(for: self) } /// Hashes the essential components of this value by feeding them into the /// given hasher. /// /// - Parameter hasher: The hasher to use when combining the components /// of this instance. @_effects(releasenone) final public func hash(into hasher: inout Hasher) { ObjectIdentifier(type(of: self)).hash(into: &hasher) return withBuffer { var buffer = $0 if buffer.data.isEmpty { return } while true { let (component, type) = buffer.next() hasher.combine(component.value) if let type = type { hasher.combine(unsafeBitCast(type, to: Int.self)) } else { break } } } } public static func ==(a: AnyKeyPath, b: AnyKeyPath) -> Bool { // Fast-path identical objects if a === b { return true } // Short-circuit differently-typed key paths if type(of: a) != type(of: b) { return false } return a.withBuffer { var aBuffer = $0 return b.withBuffer { var bBuffer = $0 // Two equivalent key paths should have the same reference prefix if aBuffer.hasReferencePrefix != bBuffer.hasReferencePrefix { return false } // Identity is equal to identity if aBuffer.data.isEmpty { return bBuffer.data.isEmpty } while true { let (aComponent, aType) = aBuffer.next() let (bComponent, bType) = bBuffer.next() if aComponent.header.endOfReferencePrefix != bComponent.header.endOfReferencePrefix || aComponent.value != bComponent.value || aType != bType { return false } if aType == nil { return true } } } } } // SPI for the Foundation overlay to allow interop with KVC keypath-based // APIs. public var _kvcKeyPathString: String? { @_semantics("keypath.kvcKeyPathString") get { guard let ptr = _kvcKeyPathStringPtr else { return nil } return String(validatingUTF8: ptr) } } // MARK: Implementation details // Prevent normal initialization. We use tail allocation via // allocWithTailElems(). @available(*, unavailable) internal init() { _internalInvariantFailure("use _create(...)") } @usableFromInline internal class var _rootAndValueType: (root: Any.Type, value: Any.Type) { _abstract() } internal static func _create( capacityInBytes bytes: Int, initializedBy body: (UnsafeMutableRawBufferPointer) -> Void ) -> Self { _internalInvariant(bytes > 0 && bytes % 4 == 0, "capacity must be multiple of 4 bytes") let result = Builtin.allocWithTailElems_1(self, (bytes/4)._builtinWordValue, Int32.self) result._kvcKeyPathStringPtr = nil let base = UnsafeMutableRawPointer(Builtin.projectTailElems(result, Int32.self)) body(UnsafeMutableRawBufferPointer(start: base, count: bytes)) return result } final internal func withBuffer(_ f: (KeyPathBuffer) throws -> T) rethrows -> T { defer { _fixLifetime(self) } let base = UnsafeRawPointer(Builtin.projectTailElems(self, Int32.self)) return try f(KeyPathBuffer(base: base)) } @usableFromInline // Exposed as public API by MemoryLayout.offset(of:) internal var _storedInlineOffset: Int? { return withBuffer { var buffer = $0 // The identity key path is effectively a stored keypath of type Self // at offset zero if buffer.data.isEmpty { return 0 } var offset = 0 while true { let (rawComponent, optNextType) = buffer.next() switch rawComponent.header.kind { case .struct: offset += rawComponent._structOrClassOffset case .class, .computed, .optionalChain, .optionalForce, .optionalWrap, .external: return .none } if optNextType == nil { return .some(offset) } } } } } /// A partially type-erased key path, from a concrete root type to any /// resulting value type. public class PartialKeyPath: AnyKeyPath { } // MARK: Concrete implementations internal enum KeyPathKind { case readOnly, value, reference } /// A key path from a specific root type to a specific resulting value type. public class KeyPath: PartialKeyPath { @usableFromInline internal final override class var _rootAndValueType: ( root: Any.Type, value: Any.Type ) { return (Root.self, Value.self) } // MARK: Implementation internal typealias Kind = KeyPathKind internal class var kind: Kind { return .readOnly } internal static func appendedType( with t: KeyPath.Type ) -> KeyPath.Type { let resultKind: Kind switch (self.kind, t.kind) { case (_, .reference): resultKind = .reference case (let x, .value): resultKind = x default: resultKind = .readOnly } switch resultKind { case .readOnly: return KeyPath.self case .value: return WritableKeyPath.self case .reference: return ReferenceWritableKeyPath.self } } @usableFromInline internal final func _projectReadOnly(from root: Root) -> Value { // TODO: For perf, we could use a local growable buffer instead of Any var curBase: Any = root return withBuffer { var buffer = $0 if buffer.data.isEmpty { return unsafeBitCast(root, to: Value.self) } while true { let (rawComponent, optNextType) = buffer.next() let valueType = optNextType ?? Value.self let isLast = optNextType == nil func project(_ base: CurValue) -> Value? { func project2(_: NewValue.Type) -> Value? { switch rawComponent._projectReadOnly(base, to: NewValue.self, endingWith: Value.self) { case .continue(let newBase): if isLast { _internalInvariant(NewValue.self == Value.self, "key path does not terminate in correct type") return unsafeBitCast(newBase, to: Value.self) } else { curBase = newBase return nil } case .break(let result): return result } } return _openExistential(valueType, do: project2) } if let result = _openExistential(curBase, do: project) { return result } } } } deinit { withBuffer { $0.destroy() } } } /// A key path that supports reading from and writing to the resulting value. public class WritableKeyPath: KeyPath { // MARK: Implementation detail internal override class var kind: Kind { return .value } // `base` is assumed to be undergoing a formal access for the duration of the // call, so must not be mutated by an alias @usableFromInline internal func _projectMutableAddress(from base: UnsafePointer) -> (pointer: UnsafeMutablePointer, owner: AnyObject?) { var p = UnsafeRawPointer(base) var type: Any.Type = Root.self var keepAlive: AnyObject? return withBuffer { var buffer = $0 _internalInvariant(!buffer.hasReferencePrefix, "WritableKeyPath should not have a reference prefix") if buffer.data.isEmpty { return ( UnsafeMutablePointer( mutating: p.assumingMemoryBound(to: Value.self)), nil) } while true { let (rawComponent, optNextType) = buffer.next() let nextType = optNextType ?? Value.self func project(_: CurValue.Type) { func project2(_: NewValue.Type) { p = rawComponent._projectMutableAddress(p, from: CurValue.self, to: NewValue.self, isRoot: p == UnsafeRawPointer(base), keepAlive: &keepAlive) } _openExistential(nextType, do: project2) } _openExistential(type, do: project) if optNextType == nil { break } type = nextType } // TODO: With coroutines, it would be better to yield here, so that // we don't need the hack of the keepAlive reference to manage closing // accesses. let typedPointer = p.assumingMemoryBound(to: Value.self) return (pointer: UnsafeMutablePointer(mutating: typedPointer), owner: keepAlive) } } } /// A key path that supports reading from and writing to the resulting value /// with reference semantics. public class ReferenceWritableKeyPath< Root, Value >: WritableKeyPath { // MARK: Implementation detail internal final override class var kind: Kind { return .reference } @usableFromInline internal final func _projectMutableAddress(from origBase: Root) -> (pointer: UnsafeMutablePointer, owner: AnyObject?) { var keepAlive: AnyObject? let address: UnsafeMutablePointer = withBuffer { var buffer = $0 // Project out the reference prefix. var base: Any = origBase while buffer.hasReferencePrefix { let (rawComponent, optNextType) = buffer.next() _internalInvariant(optNextType != nil, "reference prefix should not go to end of buffer") let nextType = optNextType.unsafelyUnwrapped func project(_: NewValue.Type) -> Any { func project2(_ base: CurValue) -> Any { return rawComponent._projectReadOnly( base, to: NewValue.self, endingWith: Value.self) .assumingContinue } return _openExistential(base, do: project2) } base = _openExistential(nextType, do: project) } // Start formal access to the mutable value, based on the final base // value. func formalMutation(_ base: MutationRoot) -> UnsafeMutablePointer { var base2 = base return withUnsafeBytes(of: &base2) { baseBytes in var p = baseBytes.baseAddress.unsafelyUnwrapped var curType: Any.Type = MutationRoot.self while true { let (rawComponent, optNextType) = buffer.next() let nextType = optNextType ?? Value.self func project(_: CurValue.Type) { func project2(_: NewValue.Type) { p = rawComponent._projectMutableAddress(p, from: CurValue.self, to: NewValue.self, isRoot: p == baseBytes.baseAddress, keepAlive: &keepAlive) } _openExistential(nextType, do: project2) } _openExistential(curType, do: project) if optNextType == nil { break } curType = nextType } let typedPointer = p.assumingMemoryBound(to: Value.self) return UnsafeMutablePointer(mutating: typedPointer) } } return _openExistential(base, do: formalMutation) } return (address, keepAlive) } } // MARK: Implementation details internal enum KeyPathComponentKind { /// The keypath references an externally-defined property or subscript whose /// component describes how to interact with the key path. case external /// The keypath projects within the storage of the outer value, like a /// stored property in a struct. case `struct` /// The keypath projects from the referenced pointer, like a /// stored property in a class. case `class` /// The keypath projects using a getter/setter pair. case computed /// The keypath optional-chains, returning nil immediately if the input is /// nil, or else proceeding by projecting the value inside. case optionalChain /// The keypath optional-forces, trapping if the input is /// nil, or else proceeding by projecting the value inside. case optionalForce /// The keypath wraps a value in an optional. case optionalWrap } internal struct ComputedPropertyID: Hashable { internal var value: Int internal var kind: KeyPathComputedIDKind internal static func ==( x: ComputedPropertyID, y: ComputedPropertyID ) -> Bool { return x.value == y.value && x.kind == y.kind } internal func hash(into hasher: inout Hasher) { hasher.combine(value) hasher.combine(kind) } } internal struct ComputedAccessorsPtr { #if INTERNAL_CHECKS_ENABLED internal let header: RawKeyPathComponent.Header #endif internal let _value: UnsafeRawPointer init(header: RawKeyPathComponent.Header, value: UnsafeRawPointer) { #if INTERNAL_CHECKS_ENABLED self.header = header #endif self._value = value } @_transparent static var getterPtrAuthKey: UInt64 { return UInt64(_SwiftKeyPath_ptrauth_Getter) } @_transparent static var nonmutatingSetterPtrAuthKey: UInt64 { return UInt64(_SwiftKeyPath_ptrauth_NonmutatingSetter) } @_transparent static var mutatingSetterPtrAuthKey: UInt64 { return UInt64(_SwiftKeyPath_ptrauth_MutatingSetter) } internal typealias Getter = @convention(thin) (CurValue, UnsafeRawPointer, Int) -> NewValue internal typealias NonmutatingSetter = @convention(thin) (NewValue, CurValue, UnsafeRawPointer, Int) -> () internal typealias MutatingSetter = @convention(thin) (NewValue, inout CurValue, UnsafeRawPointer, Int) -> () internal var getterPtr: UnsafeRawPointer { #if INTERNAL_CHECKS_ENABLED _internalInvariant(header.kind == .computed, "not a computed property") #endif return _value } internal var setterPtr: UnsafeRawPointer { #if INTERNAL_CHECKS_ENABLED _internalInvariant(header.isComputedSettable, "not a settable property") #endif return _value + MemoryLayout.size } internal func getter() -> Getter { return getterPtr._loadAddressDiscriminatedFunctionPointer( as: Getter.self, discriminator: ComputedAccessorsPtr.getterPtrAuthKey) } internal func nonmutatingSetter() -> NonmutatingSetter { #if INTERNAL_CHECKS_ENABLED _internalInvariant(header.isComputedSettable && !header.isComputedMutating, "not a nonmutating settable property") #endif return setterPtr._loadAddressDiscriminatedFunctionPointer( as: NonmutatingSetter.self, discriminator: ComputedAccessorsPtr.nonmutatingSetterPtrAuthKey) } internal func mutatingSetter() -> MutatingSetter { #if INTERNAL_CHECKS_ENABLED _internalInvariant(header.isComputedSettable && header.isComputedMutating, "not a mutating settable property") #endif return setterPtr._loadAddressDiscriminatedFunctionPointer( as: MutatingSetter.self, discriminator: ComputedAccessorsPtr.mutatingSetterPtrAuthKey) } } internal struct ComputedArgumentWitnessesPtr { internal let _value: UnsafeRawPointer init(_ value: UnsafeRawPointer) { self._value = value } @_transparent static var destroyPtrAuthKey: UInt64 { return UInt64(_SwiftKeyPath_ptrauth_ArgumentDestroy) } @_transparent static var copyPtrAuthKey: UInt64 { return UInt64(_SwiftKeyPath_ptrauth_ArgumentCopy) } @_transparent static var equalsPtrAuthKey: UInt64 { return UInt64(_SwiftKeyPath_ptrauth_ArgumentEquals) } @_transparent static var hashPtrAuthKey: UInt64 { return UInt64(_SwiftKeyPath_ptrauth_ArgumentHash) } @_transparent static var layoutPtrAuthKey: UInt64 { return UInt64(_SwiftKeyPath_ptrauth_ArgumentLayout) } @_transparent static var initPtrAuthKey: UInt64 { return UInt64(_SwiftKeyPath_ptrauth_ArgumentInit) } internal typealias Destroy = @convention(thin) (_ instanceArguments: UnsafeMutableRawPointer, _ size: Int) -> () internal typealias Copy = @convention(thin) (_ srcInstanceArguments: UnsafeRawPointer, _ destInstanceArguments: UnsafeMutableRawPointer, _ size: Int) -> () internal typealias Equals = @convention(thin) (_ xInstanceArguments: UnsafeRawPointer, _ yInstanceArguments: UnsafeRawPointer, _ size: Int) -> Bool // FIXME(hasher) Combine to an inout Hasher instead internal typealias Hash = @convention(thin) (_ instanceArguments: UnsafeRawPointer, _ size: Int) -> Int // The witnesses are stored as address-discriminated authenticated // pointers. internal var destroy: Destroy? { return _value._loadAddressDiscriminatedFunctionPointer( as: Optional.self, discriminator: ComputedArgumentWitnessesPtr.destroyPtrAuthKey) } internal var copy: Copy { return _value._loadAddressDiscriminatedFunctionPointer( fromByteOffset: MemoryLayout.size, as: Copy.self, discriminator: ComputedArgumentWitnessesPtr.copyPtrAuthKey) } internal var equals: Equals { return _value._loadAddressDiscriminatedFunctionPointer( fromByteOffset: 2*MemoryLayout.size, as: Equals.self, discriminator: ComputedArgumentWitnessesPtr.equalsPtrAuthKey) } internal var hash: Hash { return _value._loadAddressDiscriminatedFunctionPointer( fromByteOffset: 3*MemoryLayout.size, as: Hash.self, discriminator: ComputedArgumentWitnessesPtr.hashPtrAuthKey) } } internal enum KeyPathComponent: Hashable { internal struct ArgumentRef { internal var data: UnsafeRawBufferPointer internal var witnesses: ComputedArgumentWitnessesPtr internal var witnessSizeAdjustment: Int internal init( data: UnsafeRawBufferPointer, witnesses: ComputedArgumentWitnessesPtr, witnessSizeAdjustment: Int ) { self.data = data self.witnesses = witnesses self.witnessSizeAdjustment = witnessSizeAdjustment } } /// The keypath projects within the storage of the outer value, like a /// stored property in a struct. case `struct`(offset: Int) /// The keypath projects from the referenced pointer, like a /// stored property in a class. case `class`(offset: Int) /// The keypath projects using a getter. case get(id: ComputedPropertyID, accessors: ComputedAccessorsPtr, argument: ArgumentRef?) /// The keypath projects using a getter/setter pair. The setter can mutate /// the base value in-place. case mutatingGetSet(id: ComputedPropertyID, accessors: ComputedAccessorsPtr, argument: ArgumentRef?) /// The keypath projects using a getter/setter pair that does not mutate its /// base. case nonmutatingGetSet(id: ComputedPropertyID, accessors: ComputedAccessorsPtr, argument: ArgumentRef?) /// The keypath optional-chains, returning nil immediately if the input is /// nil, or else proceeding by projecting the value inside. case optionalChain /// The keypath optional-forces, trapping if the input is /// nil, or else proceeding by projecting the value inside. case optionalForce /// The keypath wraps a value in an optional. case optionalWrap internal static func ==(a: KeyPathComponent, b: KeyPathComponent) -> Bool { switch (a, b) { case (.struct(offset: let a), .struct(offset: let b)), (.class (offset: let a), .class (offset: let b)): return a == b case (.optionalChain, .optionalChain), (.optionalForce, .optionalForce), (.optionalWrap, .optionalWrap): return true case (.get(id: let id1, accessors: _, argument: let argument1), .get(id: let id2, accessors: _, argument: let argument2)), (.mutatingGetSet(id: let id1, accessors: _, argument: let argument1), .mutatingGetSet(id: let id2, accessors: _, argument: let argument2)), (.nonmutatingGetSet(id: let id1, accessors: _, argument: let argument1), .nonmutatingGetSet(id: let id2, accessors: _, argument: let argument2)): if id1 != id2 { return false } if let arg1 = argument1, let arg2 = argument2 { return arg1.witnesses.equals( arg1.data.baseAddress.unsafelyUnwrapped, arg2.data.baseAddress.unsafelyUnwrapped, arg1.data.count - arg1.witnessSizeAdjustment) } // If only one component has arguments, that should indicate that the // only arguments in that component were generic captures and therefore // not affecting equality. return true case (.struct, _), (.class, _), (.optionalChain, _), (.optionalForce, _), (.optionalWrap, _), (.get, _), (.mutatingGetSet, _), (.nonmutatingGetSet, _): return false } } @_effects(releasenone) internal func hash(into hasher: inout Hasher) { func appendHashFromArgument( _ argument: KeyPathComponent.ArgumentRef? ) { if let argument = argument { let hash = argument.witnesses.hash( argument.data.baseAddress.unsafelyUnwrapped, argument.data.count - argument.witnessSizeAdjustment) // Returning 0 indicates that the arguments should not impact the // hash value of the overall key path. // FIXME(hasher): hash witness should just mutate hasher directly if hash != 0 { hasher.combine(hash) } } } switch self { case .struct(offset: let a): hasher.combine(0) hasher.combine(a) case .class(offset: let b): hasher.combine(1) hasher.combine(b) case .optionalChain: hasher.combine(2) case .optionalForce: hasher.combine(3) case .optionalWrap: hasher.combine(4) case .get(id: let id, accessors: _, argument: let argument): hasher.combine(5) hasher.combine(id) appendHashFromArgument(argument) case .mutatingGetSet(id: let id, accessors: _, argument: let argument): hasher.combine(6) hasher.combine(id) appendHashFromArgument(argument) case .nonmutatingGetSet(id: let id, accessors: _, argument: let argument): hasher.combine(7) hasher.combine(id) appendHashFromArgument(argument) } } } // A class that maintains ownership of another object while a mutable projection // into it is underway. The lifetime of the instance of this class is also used // to begin and end exclusive 'modify' access to the projected address. internal final class ClassHolder { /// The type of the scratch record passed to the runtime to record /// accesses to guarantee exclusive access. internal typealias AccessRecord = Builtin.UnsafeValueBuffer internal var previous: AnyObject? internal var instance: AnyObject internal init(previous: AnyObject?, instance: AnyObject) { self.previous = previous self.instance = instance } internal final class func _create( previous: AnyObject?, instance: AnyObject, accessingAddress address: UnsafeRawPointer, type: ProjectionType.Type ) -> ClassHolder { // Tail allocate the UnsafeValueBuffer used as the AccessRecord. // This avoids a second heap allocation since there is no source-level way to // initialize a Builtin.UnsafeValueBuffer type and thus we cannot have a // stored property of that type. let holder: ClassHolder = Builtin.allocWithTailElems_1(self, 1._builtinWordValue, AccessRecord.self) // Initialize the ClassHolder's instance variables. This is done via // withUnsafeMutablePointer(to:) because the instance was just allocated with // allocWithTailElems_1 and so we need to make sure to use an initialization // rather than an assignment. withUnsafeMutablePointer(to: &holder.previous) { $0.initialize(to: previous) } withUnsafeMutablePointer(to: &holder.instance) { $0.initialize(to: instance) } let accessRecordPtr = Builtin.projectTailElems(holder, AccessRecord.self) // Begin a 'modify' access to the address. This access is ended in // ClassHolder's deinitializer. Builtin.beginUnpairedModifyAccess(address._rawValue, accessRecordPtr, type) return holder } deinit { let accessRecordPtr = Builtin.projectTailElems(self, AccessRecord.self) // Ends the access begun in _create(). Builtin.endUnpairedAccess(accessRecordPtr) } } // A class that triggers writeback to a pointer when destroyed. internal final class MutatingWritebackBuffer { internal let previous: AnyObject? internal let base: UnsafeMutablePointer internal let set: ComputedAccessorsPtr.MutatingSetter internal let argument: UnsafeRawPointer internal let argumentSize: Int internal var value: NewValue deinit { set(value, &base.pointee, argument, argumentSize) } internal init(previous: AnyObject?, base: UnsafeMutablePointer, set: @escaping ComputedAccessorsPtr.MutatingSetter, argument: UnsafeRawPointer, argumentSize: Int, value: NewValue) { self.previous = previous self.base = base self.set = set self.argument = argument self.argumentSize = argumentSize self.value = value } } // A class that triggers writeback to a non-mutated value when destroyed. internal final class NonmutatingWritebackBuffer { internal let previous: AnyObject? internal let base: CurValue internal let set: ComputedAccessorsPtr.NonmutatingSetter internal let argument: UnsafeRawPointer internal let argumentSize: Int internal var value: NewValue deinit { set(value, base, argument, argumentSize) } internal init(previous: AnyObject?, base: CurValue, set: @escaping ComputedAccessorsPtr.NonmutatingSetter, argument: UnsafeRawPointer, argumentSize: Int, value: NewValue) { self.previous = previous self.base = base self.set = set self.argument = argument self.argumentSize = argumentSize self.value = value } } internal typealias KeyPathComputedArgumentLayoutFn = @convention(thin) (_ patternArguments: UnsafeRawPointer?) -> (size: Int, alignmentMask: Int) internal typealias KeyPathComputedArgumentInitializerFn = @convention(thin) (_ patternArguments: UnsafeRawPointer?, _ instanceArguments: UnsafeMutableRawPointer) -> () internal enum KeyPathComputedIDKind { case pointer case storedPropertyIndex case vtableOffset } internal enum KeyPathComputedIDResolution { case resolved case indirectPointer case functionCall } internal struct RawKeyPathComponent { internal var header: Header internal var body: UnsafeRawBufferPointer internal init(header: Header, body: UnsafeRawBufferPointer) { self.header = header self.body = body } @_transparent static var metadataAccessorPtrAuthKey: UInt64 { return UInt64(_SwiftKeyPath_ptrauth_MetadataAccessor) } internal struct Header { internal var _value: UInt32 init(discriminator: UInt32, payload: UInt32) { _value = 0 self.discriminator = discriminator self.payload = payload } internal var discriminator: UInt32 { get { return (_value & Header.discriminatorMask) >> Header.discriminatorShift } set { let shifted = newValue << Header.discriminatorShift _internalInvariant(shifted & Header.discriminatorMask == shifted, "discriminator doesn't fit") _value = _value & ~Header.discriminatorMask | shifted } } internal var payload: UInt32 { get { return _value & Header.payloadMask } set { _internalInvariant(newValue & Header.payloadMask == newValue, "payload too big") _value = _value & ~Header.payloadMask | newValue } } internal var storedOffsetPayload: UInt32 { get { _internalInvariant(kind == .struct || kind == .class, "not a stored component") return _value & Header.storedOffsetPayloadMask } set { _internalInvariant(kind == .struct || kind == .class, "not a stored component") _internalInvariant(newValue & Header.storedOffsetPayloadMask == newValue, "payload too big") _value = _value & ~Header.storedOffsetPayloadMask | newValue } } internal var endOfReferencePrefix: Bool { get { return _value & Header.endOfReferencePrefixFlag != 0 } set { if newValue { _value |= Header.endOfReferencePrefixFlag } else { _value &= ~Header.endOfReferencePrefixFlag } } } internal var kind: KeyPathComponentKind { switch (discriminator, payload) { case (Header.externalTag, _): return .external case (Header.structTag, _): return .struct case (Header.classTag, _): return .class case (Header.computedTag, _): return .computed case (Header.optionalTag, Header.optionalChainPayload): return .optionalChain case (Header.optionalTag, Header.optionalWrapPayload): return .optionalWrap case (Header.optionalTag, Header.optionalForcePayload): return .optionalForce default: _internalInvariantFailure("invalid header") } } internal static var payloadMask: UInt32 { return _SwiftKeyPathComponentHeader_PayloadMask } internal static var discriminatorMask: UInt32 { return _SwiftKeyPathComponentHeader_DiscriminatorMask } internal static var discriminatorShift: UInt32 { return _SwiftKeyPathComponentHeader_DiscriminatorShift } internal static var externalTag: UInt32 { return _SwiftKeyPathComponentHeader_ExternalTag } internal static var structTag: UInt32 { return _SwiftKeyPathComponentHeader_StructTag } internal static var computedTag: UInt32 { return _SwiftKeyPathComponentHeader_ComputedTag } internal static var classTag: UInt32 { return _SwiftKeyPathComponentHeader_ClassTag } internal static var optionalTag: UInt32 { return _SwiftKeyPathComponentHeader_OptionalTag } internal static var optionalChainPayload: UInt32 { return _SwiftKeyPathComponentHeader_OptionalChainPayload } internal static var optionalWrapPayload: UInt32 { return _SwiftKeyPathComponentHeader_OptionalWrapPayload } internal static var optionalForcePayload: UInt32 { return _SwiftKeyPathComponentHeader_OptionalForcePayload } internal static var endOfReferencePrefixFlag: UInt32 { return _SwiftKeyPathComponentHeader_EndOfReferencePrefixFlag } internal static var storedMutableFlag: UInt32 { return _SwiftKeyPathComponentHeader_StoredMutableFlag } internal static var storedOffsetPayloadMask: UInt32 { return _SwiftKeyPathComponentHeader_StoredOffsetPayloadMask } internal static var outOfLineOffsetPayload: UInt32 { return _SwiftKeyPathComponentHeader_OutOfLineOffsetPayload } internal static var unresolvedFieldOffsetPayload: UInt32 { return _SwiftKeyPathComponentHeader_UnresolvedFieldOffsetPayload } internal static var unresolvedIndirectOffsetPayload: UInt32 { return _SwiftKeyPathComponentHeader_UnresolvedIndirectOffsetPayload } internal static var maximumOffsetPayload: UInt32 { return _SwiftKeyPathComponentHeader_MaximumOffsetPayload } internal var isStoredMutable: Bool { _internalInvariant(kind == .struct || kind == .class) return _value & Header.storedMutableFlag != 0 } internal static var computedMutatingFlag: UInt32 { return _SwiftKeyPathComponentHeader_ComputedMutatingFlag } internal var isComputedMutating: Bool { _internalInvariant(kind == .computed) return _value & Header.computedMutatingFlag != 0 } internal static var computedSettableFlag: UInt32 { return _SwiftKeyPathComponentHeader_ComputedSettableFlag } internal var isComputedSettable: Bool { _internalInvariant(kind == .computed) return _value & Header.computedSettableFlag != 0 } internal static var computedIDByStoredPropertyFlag: UInt32 { return _SwiftKeyPathComponentHeader_ComputedIDByStoredPropertyFlag } internal static var computedIDByVTableOffsetFlag: UInt32 { return _SwiftKeyPathComponentHeader_ComputedIDByVTableOffsetFlag } internal var computedIDKind: KeyPathComputedIDKind { let storedProperty = _value & Header.computedIDByStoredPropertyFlag != 0 let vtableOffset = _value & Header.computedIDByVTableOffsetFlag != 0 switch (storedProperty, vtableOffset) { case (true, true): _internalInvariantFailure("not allowed") case (true, false): return .storedPropertyIndex case (false, true): return .vtableOffset case (false, false): return .pointer } } internal static var computedHasArgumentsFlag: UInt32 { return _SwiftKeyPathComponentHeader_ComputedHasArgumentsFlag } internal var hasComputedArguments: Bool { _internalInvariant(kind == .computed) return _value & Header.computedHasArgumentsFlag != 0 } // If a computed component is instantiated from an external property // descriptor, and both components carry arguments, we need to carry some // extra matter to be able to map between the client and external generic // contexts. internal static var computedInstantiatedFromExternalWithArgumentsFlag: UInt32 { return _SwiftKeyPathComponentHeader_ComputedInstantiatedFromExternalWithArgumentsFlag } internal var isComputedInstantiatedFromExternalWithArguments: Bool { get { _internalInvariant(kind == .computed) return _value & Header.computedInstantiatedFromExternalWithArgumentsFlag != 0 } set { _internalInvariant(kind == .computed) _value = _value & ~Header.computedInstantiatedFromExternalWithArgumentsFlag | (newValue ? Header.computedInstantiatedFromExternalWithArgumentsFlag : 0) } } internal static var externalWithArgumentsExtraSize: Int { return MemoryLayout.size } internal static var computedIDResolutionMask: UInt32 { return _SwiftKeyPathComponentHeader_ComputedIDResolutionMask } internal static var computedIDResolved: UInt32 { return _SwiftKeyPathComponentHeader_ComputedIDResolved } internal static var computedIDUnresolvedIndirectPointer: UInt32 { return _SwiftKeyPathComponentHeader_ComputedIDUnresolvedIndirectPointer } internal static var computedIDUnresolvedFunctionCall: UInt32 { return _SwiftKeyPathComponentHeader_ComputedIDUnresolvedFunctionCall } internal var computedIDResolution: KeyPathComputedIDResolution { switch payload & Header.computedIDResolutionMask { case Header.computedIDResolved: return .resolved case Header.computedIDUnresolvedIndirectPointer: return .indirectPointer case Header.computedIDUnresolvedFunctionCall: return .functionCall default: _internalInvariantFailure("invalid key path resolution") } } // The component header is 4 bytes, but may be followed by an aligned // pointer field for some kinds of component, forcing padding. internal static var pointerAlignmentSkew: Int { return MemoryLayout.size - MemoryLayout.size } internal var isTrivialPropertyDescriptor: Bool { return _value == _SwiftKeyPathComponentHeader_TrivialPropertyDescriptorMarker } /// If this is the header for a component in a key path pattern, return /// the size of the body of the component. internal var patternComponentBodySize: Int { return _componentBodySize(forPropertyDescriptor: false) } /// If this is the header for a property descriptor, return /// the size of the body of the component. internal var propertyDescriptorBodySize: Int { if isTrivialPropertyDescriptor { return 0 } return _componentBodySize(forPropertyDescriptor: true) } internal func _componentBodySize(forPropertyDescriptor: Bool) -> Int { switch kind { case .struct, .class: if storedOffsetPayload == Header.unresolvedFieldOffsetPayload || storedOffsetPayload == Header.outOfLineOffsetPayload || storedOffsetPayload == Header.unresolvedIndirectOffsetPayload { // A 32-bit offset is stored in the body. return MemoryLayout.size } // Otherwise, there's no body. return 0 case .external: // The body holds a pointer to the external property descriptor, // and some number of substitution arguments, the count of which is // in the payload. return 4 * (1 + Int(payload)) case .computed: // The body holds at minimum the id and getter. var size = 8 // If settable, it also holds the setter. if isComputedSettable { size += 4 } // If there are arguments, there's also a layout function, // witness table, and initializer function. // Property descriptors never carry argument information, though. if !forPropertyDescriptor && hasComputedArguments { size += 12 } return size case .optionalForce, .optionalChain, .optionalWrap: // Otherwise, there's no body. return 0 } } init(optionalForce: ()) { self.init(discriminator: Header.optionalTag, payload: Header.optionalForcePayload) } init(optionalWrap: ()) { self.init(discriminator: Header.optionalTag, payload: Header.optionalWrapPayload) } init(optionalChain: ()) { self.init(discriminator: Header.optionalTag, payload: Header.optionalChainPayload) } init(stored kind: KeyPathStructOrClass, mutable: Bool, inlineOffset: UInt32) { let discriminator: UInt32 switch kind { case .struct: discriminator = Header.structTag case .class: discriminator = Header.classTag } _internalInvariant(inlineOffset <= Header.maximumOffsetPayload) let payload = inlineOffset | (mutable ? Header.storedMutableFlag : 0) self.init(discriminator: discriminator, payload: payload) } init(storedWithOutOfLineOffset kind: KeyPathStructOrClass, mutable: Bool) { let discriminator: UInt32 switch kind { case .struct: discriminator = Header.structTag case .class: discriminator = Header.classTag } let payload = Header.outOfLineOffsetPayload | (mutable ? Header.storedMutableFlag : 0) self.init(discriminator: discriminator, payload: payload) } init(computedWithIDKind kind: KeyPathComputedIDKind, mutating: Bool, settable: Bool, hasArguments: Bool, instantiatedFromExternalWithArguments: Bool) { let discriminator = Header.computedTag var payload = (mutating ? Header.computedMutatingFlag : 0) | (settable ? Header.computedSettableFlag : 0) | (hasArguments ? Header.computedHasArgumentsFlag : 0) | (instantiatedFromExternalWithArguments ? Header.computedInstantiatedFromExternalWithArgumentsFlag : 0) switch kind { case .pointer: break case .storedPropertyIndex: payload |= Header.computedIDByStoredPropertyFlag case .vtableOffset: payload |= Header.computedIDByVTableOffsetFlag } self.init(discriminator: discriminator, payload: payload) } } internal var bodySize: Int { let ptrSize = MemoryLayout.size switch header.kind { case .struct, .class: if header.storedOffsetPayload == Header.outOfLineOffsetPayload { return 4 // overflowed } return 0 case .external: _internalInvariantFailure("should be instantiated away") case .optionalChain, .optionalForce, .optionalWrap: return 0 case .computed: // align to pointer, minimum two pointers for id and get var total = Header.pointerAlignmentSkew + ptrSize * 2 // additional word for a setter if header.isComputedSettable { total += ptrSize } // include the argument size if header.hasComputedArguments { // two words for argument header: size, witnesses total += ptrSize * 2 // size of argument area total += _computedArgumentSize if header.isComputedInstantiatedFromExternalWithArguments { total += Header.externalWithArgumentsExtraSize } } return total } } internal var _structOrClassOffset: Int { _internalInvariant(header.kind == .struct || header.kind == .class, "no offset for this kind") // An offset too large to fit inline is represented by a signal and stored // in the body. if header.storedOffsetPayload == Header.outOfLineOffsetPayload { // Offset overflowed into body _internalInvariant(body.count >= MemoryLayout.size, "component not big enough") return Int(body.load(as: UInt32.self)) } return Int(header.storedOffsetPayload) } internal var _computedIDValue: Int { _internalInvariant(header.kind == .computed, "not a computed property") return body.load(fromByteOffset: Header.pointerAlignmentSkew, as: Int.self) } internal var _computedID: ComputedPropertyID { _internalInvariant(header.kind == .computed, "not a computed property") return ComputedPropertyID( value: _computedIDValue, kind: header.computedIDKind) } internal var _computedAccessors: ComputedAccessorsPtr { _internalInvariant(header.kind == .computed, "not a computed property") return ComputedAccessorsPtr( header: header, value: body.baseAddress.unsafelyUnwrapped + Header.pointerAlignmentSkew + MemoryLayout.size) } internal var _computedArgumentHeaderPointer: UnsafeRawPointer { _internalInvariant(header.hasComputedArguments, "no arguments") return body.baseAddress.unsafelyUnwrapped + Header.pointerAlignmentSkew + MemoryLayout.size * (header.isComputedSettable ? 3 : 2) } internal var _computedArgumentSize: Int { return _computedArgumentHeaderPointer.load(as: Int.self) } internal var _computedArgumentWitnesses: ComputedArgumentWitnessesPtr { return _computedArgumentHeaderPointer.load( fromByteOffset: MemoryLayout.size, as: ComputedArgumentWitnessesPtr.self) } internal var _computedArguments: UnsafeRawPointer { var base = _computedArgumentHeaderPointer + MemoryLayout.size * 2 // If the component was instantiated from an external property descriptor // with its own arguments, we include some additional capture info to // be able to map to the original argument context by adjusting the size // passed to the witness operations. if header.isComputedInstantiatedFromExternalWithArguments { base += Header.externalWithArgumentsExtraSize } return base } internal var _computedMutableArguments: UnsafeMutableRawPointer { return UnsafeMutableRawPointer(mutating: _computedArguments) } internal var _computedArgumentWitnessSizeAdjustment: Int { if header.isComputedInstantiatedFromExternalWithArguments { return _computedArguments.load( fromByteOffset: -Header.externalWithArgumentsExtraSize, as: Int.self) } return 0 } internal var value: KeyPathComponent { switch header.kind { case .struct: return .struct(offset: _structOrClassOffset) case .class: return .class(offset: _structOrClassOffset) case .optionalChain: return .optionalChain case .optionalForce: return .optionalForce case .optionalWrap: return .optionalWrap case .computed: let isSettable = header.isComputedSettable let isMutating = header.isComputedMutating let id = _computedID let accessors = _computedAccessors // Argument value is unused if there are no arguments. let argument: KeyPathComponent.ArgumentRef? if header.hasComputedArguments { argument = KeyPathComponent.ArgumentRef( data: UnsafeRawBufferPointer(start: _computedArguments, count: _computedArgumentSize), witnesses: _computedArgumentWitnesses, witnessSizeAdjustment: _computedArgumentWitnessSizeAdjustment) } else { argument = nil } switch (isSettable, isMutating) { case (false, false): return .get(id: id, accessors: accessors, argument: argument) case (true, false): return .nonmutatingGetSet(id: id, accessors: accessors, argument: argument) case (true, true): return .mutatingGetSet(id: id, accessors: accessors, argument: argument) case (false, true): _internalInvariantFailure("impossible") } case .external: _internalInvariantFailure("should have been instantiated away") } } internal func destroy() { switch header.kind { case .struct, .class, .optionalChain, .optionalForce, .optionalWrap: // trivial break case .computed: // Run destructor, if any if header.hasComputedArguments, let destructor = _computedArgumentWitnesses.destroy { destructor(_computedMutableArguments, _computedArgumentSize - _computedArgumentWitnessSizeAdjustment) } case .external: _internalInvariantFailure("should have been instantiated away") } } internal func clone(into buffer: inout UnsafeMutableRawBufferPointer, endOfReferencePrefix: Bool) { var newHeader = header newHeader.endOfReferencePrefix = endOfReferencePrefix var componentSize = MemoryLayout
.size buffer.storeBytes(of: newHeader, as: Header.self) switch header.kind { case .struct, .class: if header.storedOffsetPayload == Header.outOfLineOffsetPayload { let overflowOffset = body.load(as: UInt32.self) buffer.storeBytes(of: overflowOffset, toByteOffset: 4, as: UInt32.self) componentSize += 4 } case .optionalChain, .optionalForce, .optionalWrap: break case .computed: // Fields are pointer-aligned after the header componentSize += Header.pointerAlignmentSkew buffer.storeBytes(of: _computedIDValue, toByteOffset: componentSize, as: Int.self) componentSize += MemoryLayout.size let accessors = _computedAccessors (buffer.baseAddress.unsafelyUnwrapped + MemoryLayout.size * 2) ._copyAddressDiscriminatedFunctionPointer( from: accessors.getterPtr, discriminator: ComputedAccessorsPtr.getterPtrAuthKey) componentSize += MemoryLayout.size if header.isComputedSettable { (buffer.baseAddress.unsafelyUnwrapped + MemoryLayout.size * 3) ._copyAddressDiscriminatedFunctionPointer( from: accessors.setterPtr, discriminator: header.isComputedMutating ? ComputedAccessorsPtr.mutatingSetterPtrAuthKey : ComputedAccessorsPtr.nonmutatingSetterPtrAuthKey) componentSize += MemoryLayout.size } if header.hasComputedArguments { let arguments = _computedArguments let argumentSize = _computedArgumentSize buffer.storeBytes(of: argumentSize, toByteOffset: componentSize, as: Int.self) componentSize += MemoryLayout.size buffer.storeBytes(of: _computedArgumentWitnesses, toByteOffset: componentSize, as: ComputedArgumentWitnessesPtr.self) componentSize += MemoryLayout.size if header.isComputedInstantiatedFromExternalWithArguments { // Include the extra matter for components instantiated from // external property descriptors with arguments. buffer.storeBytes(of: _computedArgumentWitnessSizeAdjustment, toByteOffset: componentSize, as: Int.self) componentSize += MemoryLayout.size } let adjustedSize = argumentSize - _computedArgumentWitnessSizeAdjustment let argumentDest = buffer.baseAddress.unsafelyUnwrapped + componentSize _computedArgumentWitnesses.copy( arguments, argumentDest, adjustedSize) if header.isComputedInstantiatedFromExternalWithArguments { // The extra information for external property descriptor arguments // can always be memcpy'd. _memcpy(dest: argumentDest + adjustedSize, src: arguments + adjustedSize, size: UInt(_computedArgumentWitnessSizeAdjustment)) } componentSize += argumentSize } case .external: _internalInvariantFailure("should have been instantiated away") } buffer = UnsafeMutableRawBufferPointer( start: buffer.baseAddress.unsafelyUnwrapped + componentSize, count: buffer.count - componentSize) } internal enum ProjectionResult { /// Continue projecting the key path with the given new value. case `continue`(NewValue) /// Stop projecting the key path and use the given value as the final /// result of the projection. case `break`(LeafValue) internal var assumingContinue: NewValue { switch self { case .continue(let x): return x case .break: _internalInvariantFailure("should not have stopped key path projection") } } } internal func _projectReadOnly( _ base: CurValue, to: NewValue.Type, endingWith: LeafValue.Type ) -> ProjectionResult { switch value { case .struct(let offset): var base2 = base return .continue(withUnsafeBytes(of: &base2) { let p = $0.baseAddress.unsafelyUnwrapped.advanced(by: offset) // The contents of the struct should be well-typed, so we can assume // typed memory here. return p.assumingMemoryBound(to: NewValue.self).pointee }) case .class(let offset): _internalInvariant(CurValue.self is AnyObject.Type, "base is not a class") let baseObj = unsafeBitCast(base, to: AnyObject.self) let basePtr = UnsafeRawPointer(Builtin.bridgeToRawPointer(baseObj)) defer { _fixLifetime(baseObj) } let offsetAddress = basePtr.advanced(by: offset) // Perform an instantaneous record access on the address in order to // ensure that the read will not conflict with an already in-progress // 'modify' access. Builtin.performInstantaneousReadAccess(offsetAddress._rawValue, NewValue.self) return .continue(offsetAddress .assumingMemoryBound(to: NewValue.self) .pointee) case .get(id: _, accessors: let accessors, argument: let argument), .mutatingGetSet(id: _, accessors: let accessors, argument: let argument), .nonmutatingGetSet(id: _, accessors: let accessors, argument: let argument): return .continue(accessors.getter()(base, argument?.data.baseAddress ?? accessors._value, argument?.data.count ?? 0)) case .optionalChain: _internalInvariant(CurValue.self == Optional.self, "should be unwrapping optional value") _internalInvariant(_isOptional(LeafValue.self), "leaf result should be optional") if let baseValue = unsafeBitCast(base, to: Optional.self) { return .continue(baseValue) } else { // TODO: A more efficient way of getting the `none` representation // of a dynamically-optional type... return .break((Optional<()>.none as Any) as! LeafValue) } case .optionalForce: _internalInvariant(CurValue.self == Optional.self, "should be unwrapping optional value") return .continue(unsafeBitCast(base, to: Optional.self)!) case .optionalWrap: _internalInvariant(NewValue.self == Optional.self, "should be wrapping optional value") return .continue( unsafeBitCast(base as Optional, to: NewValue.self)) } } internal func _projectMutableAddress( _ base: UnsafeRawPointer, from _: CurValue.Type, to _: NewValue.Type, isRoot: Bool, keepAlive: inout AnyObject? ) -> UnsafeRawPointer { switch value { case .struct(let offset): return base.advanced(by: offset) case .class(let offset): // A class dereference should only occur at the root of a mutation, // since otherwise it would be part of the reference prefix. _internalInvariant(isRoot, "class component should not appear in the middle of mutation") // AnyObject memory can alias any class reference memory, so we can // assume type here let object = base.assumingMemoryBound(to: AnyObject.self).pointee let offsetAddress = UnsafeRawPointer(Builtin.bridgeToRawPointer(object)) .advanced(by: offset) // Keep the base alive for the duration of the derived access and also // enforce exclusive access to the address. keepAlive = ClassHolder._create(previous: keepAlive, instance: object, accessingAddress: offsetAddress, type: NewValue.self) return offsetAddress case .mutatingGetSet(id: _, accessors: let accessors, argument: let argument): let baseTyped = UnsafeMutablePointer( mutating: base.assumingMemoryBound(to: CurValue.self)) let argValue = argument?.data.baseAddress ?? accessors._value let argSize = argument?.data.count ?? 0 let writeback = MutatingWritebackBuffer( previous: keepAlive, base: baseTyped, set: accessors.mutatingSetter(), argument: argValue, argumentSize: argSize, value: accessors.getter()(baseTyped.pointee, argValue, argSize)) keepAlive = writeback // A maximally-abstracted, final, stored class property should have // a stable address. return UnsafeRawPointer(Builtin.addressof(&writeback.value)) case .nonmutatingGetSet(id: _, accessors: let accessors, argument: let argument): // A nonmutating property should only occur at the root of a mutation, // since otherwise it would be part of the reference prefix. _internalInvariant(isRoot, "nonmutating component should not appear in the middle of mutation") let baseValue = base.assumingMemoryBound(to: CurValue.self).pointee let argValue = argument?.data.baseAddress ?? accessors._value let argSize = argument?.data.count ?? 0 let writeback = NonmutatingWritebackBuffer( previous: keepAlive, base: baseValue, set: accessors.nonmutatingSetter(), argument: argValue, argumentSize: argSize, value: accessors.getter()(baseValue, argValue, argSize)) keepAlive = writeback // A maximally-abstracted, final, stored class property should have // a stable address. return UnsafeRawPointer(Builtin.addressof(&writeback.value)) case .optionalForce: _internalInvariant(CurValue.self == Optional.self, "should be unwrapping an optional value") // Optional's layout happens to always put the payload at the start // address of the Optional value itself, if a value is present at all. let baseOptionalPointer = base.assumingMemoryBound(to: Optional.self) // Assert that a value exists _ = baseOptionalPointer.pointee! return base case .optionalChain, .optionalWrap, .get: _internalInvariantFailure("not a mutable key path component") } } } internal func _pop(from: inout UnsafeRawBufferPointer, as type: T.Type) -> T { let buffer = _pop(from: &from, as: type, count: 1) return buffer.baseAddress.unsafelyUnwrapped.pointee } internal func _pop(from: inout UnsafeRawBufferPointer, as: T.Type, count: Int) -> UnsafeBufferPointer { _internalInvariant(_isPOD(T.self), "should be POD") from = MemoryLayout._roundingUpBaseToAlignment(from) let byteCount = MemoryLayout.stride * count let result = UnsafeBufferPointer( start: from.baseAddress.unsafelyUnwrapped.assumingMemoryBound(to: T.self), count: count) from = UnsafeRawBufferPointer( start: from.baseAddress.unsafelyUnwrapped + byteCount, count: from.count - byteCount) return result } internal struct KeyPathBuffer { internal var data: UnsafeRawBufferPointer internal var trivial: Bool internal var hasReferencePrefix: Bool internal init(base: UnsafeRawPointer) { let header = base.load(as: Header.self) data = UnsafeRawBufferPointer( start: base + MemoryLayout.size, count: header.size) trivial = header.trivial hasReferencePrefix = header.hasReferencePrefix } internal init(partialData: UnsafeRawBufferPointer, trivial: Bool = false, hasReferencePrefix: Bool = false) { self.data = partialData self.trivial = trivial self.hasReferencePrefix = hasReferencePrefix } internal var mutableData: UnsafeMutableRawBufferPointer { return UnsafeMutableRawBufferPointer(mutating: data) } internal struct Builder { internal var buffer: UnsafeMutableRawBufferPointer internal init(_ buffer: UnsafeMutableRawBufferPointer) { self.buffer = buffer } internal mutating func pushRaw(size: Int, alignment: Int) -> UnsafeMutableRawBufferPointer { var baseAddress = buffer.baseAddress.unsafelyUnwrapped var misalign = Int(bitPattern: baseAddress) % alignment if misalign != 0 { misalign = alignment - misalign baseAddress = baseAddress.advanced(by: misalign) } let result = UnsafeMutableRawBufferPointer( start: baseAddress, count: size) buffer = UnsafeMutableRawBufferPointer( start: baseAddress + size, count: buffer.count - size - misalign) return result } internal mutating func push(_ value: T) { let buf = pushRaw(size: MemoryLayout.size, alignment: MemoryLayout.alignment) buf.storeBytes(of: value, as: T.self) } internal mutating func pushHeader(_ header: Header) { push(header) // Start the components at pointer alignment _ = pushRaw(size: RawKeyPathComponent.Header.pointerAlignmentSkew, alignment: 4) } } internal struct Header { internal var _value: UInt32 internal init(size: Int, trivial: Bool, hasReferencePrefix: Bool) { _internalInvariant(size <= Int(Header.sizeMask), "key path too big") _value = UInt32(size) | (trivial ? Header.trivialFlag : 0) | (hasReferencePrefix ? Header.hasReferencePrefixFlag : 0) } internal static var sizeMask: UInt32 { return _SwiftKeyPathBufferHeader_SizeMask } internal static var reservedMask: UInt32 { return _SwiftKeyPathBufferHeader_ReservedMask } internal static var trivialFlag: UInt32 { return _SwiftKeyPathBufferHeader_TrivialFlag } internal static var hasReferencePrefixFlag: UInt32 { return _SwiftKeyPathBufferHeader_HasReferencePrefixFlag } internal var size: Int { return Int(_value & Header.sizeMask) } internal var trivial: Bool { return _value & Header.trivialFlag != 0 } internal var hasReferencePrefix: Bool { get { return _value & Header.hasReferencePrefixFlag != 0 } set { if newValue { _value |= Header.hasReferencePrefixFlag } else { _value &= ~Header.hasReferencePrefixFlag } } } // In a key path pattern, the "trivial" flag is used to indicate // "instantiable in-line" internal var instantiableInLine: Bool { return trivial } internal func validateReservedBits() { _precondition(_value & Header.reservedMask == 0, "Reserved bits set to an unexpected bit pattern") } } internal func destroy() { // Short-circuit if nothing in the object requires destruction. if trivial { return } var bufferToDestroy = self while true { let (component, type) = bufferToDestroy.next() component.destroy() guard let _ = type else { break } } } internal mutating func next() -> (RawKeyPathComponent, Any.Type?) { let header = _pop(from: &data, as: RawKeyPathComponent.Header.self) // Track if this is the last component of the reference prefix. if header.endOfReferencePrefix { _internalInvariant(self.hasReferencePrefix, "beginMutation marker in non-reference-writable key path?") self.hasReferencePrefix = false } var component = RawKeyPathComponent(header: header, body: data) // Shrinkwrap the component buffer size. let size = component.bodySize component.body = UnsafeRawBufferPointer(start: component.body.baseAddress, count: size) _ = _pop(from: &data, as: Int8.self, count: size) // fetch type, which is in the buffer unless it's the final component let nextType: Any.Type? if data.isEmpty { nextType = nil } else { nextType = _pop(from: &data, as: Any.Type.self) } return (component, nextType) } } // MARK: Library intrinsics for projecting key paths. @_silgen_name("swift_getAtPartialKeyPath") public // COMPILER_INTRINSIC func _getAtPartialKeyPath( root: Root, keyPath: PartialKeyPath ) -> Any { func open(_: Value.Type) -> Any { return _getAtKeyPath(root: root, keyPath: unsafeDowncast(keyPath, to: KeyPath.self)) } return _openExistential(type(of: keyPath).valueType, do: open) } @_silgen_name("swift_getAtAnyKeyPath") public // COMPILER_INTRINSIC func _getAtAnyKeyPath( root: RootValue, keyPath: AnyKeyPath ) -> Any? { let (keyPathRoot, keyPathValue) = type(of: keyPath)._rootAndValueType func openRoot(_: KeyPathRoot.Type) -> Any? { guard let rootForKeyPath = root as? KeyPathRoot else { return nil } func openValue(_: Value.Type) -> Any { return _getAtKeyPath(root: rootForKeyPath, keyPath: unsafeDowncast(keyPath, to: KeyPath.self)) } return _openExistential(keyPathValue, do: openValue) } return _openExistential(keyPathRoot, do: openRoot) } @_silgen_name("swift_getAtKeyPath") public // COMPILER_INTRINSIC func _getAtKeyPath( root: Root, keyPath: KeyPath ) -> Value { return keyPath._projectReadOnly(from: root) } // The release that ends the access scope is guaranteed to happen // immediately at the end_apply call because the continuation is a // runtime call with a manual release (access scopes cannot be extended). @_silgen_name("_swift_modifyAtWritableKeyPath_impl") public // runtime entrypoint func _modifyAtWritableKeyPath_impl( root: inout Root, keyPath: WritableKeyPath ) -> (UnsafeMutablePointer, AnyObject?) { if type(of: keyPath).kind == .reference { return _modifyAtReferenceWritableKeyPath_impl(root: root, keyPath: _unsafeUncheckedDowncast(keyPath, to: ReferenceWritableKeyPath.self)) } return keyPath._projectMutableAddress(from: &root) } // The release that ends the access scope is guaranteed to happen // immediately at the end_apply call because the continuation is a // runtime call with a manual release (access scopes cannot be extended). @_silgen_name("_swift_modifyAtReferenceWritableKeyPath_impl") public // runtime entrypoint func _modifyAtReferenceWritableKeyPath_impl( root: Root, keyPath: ReferenceWritableKeyPath ) -> (UnsafeMutablePointer, AnyObject?) { return keyPath._projectMutableAddress(from: root) } @_silgen_name("swift_setAtWritableKeyPath") public // COMPILER_INTRINSIC func _setAtWritableKeyPath( root: inout Root, keyPath: WritableKeyPath, value: __owned Value ) { if type(of: keyPath).kind == .reference { return _setAtReferenceWritableKeyPath(root: root, keyPath: _unsafeUncheckedDowncast(keyPath, to: ReferenceWritableKeyPath.self), value: value) } // TODO: we should be able to do this more efficiently than projecting. let (addr, owner) = keyPath._projectMutableAddress(from: &root) addr.pointee = value _fixLifetime(owner) // FIXME: this needs a deallocation barrier to ensure that the // release isn't extended, along with the access scope. } @_silgen_name("swift_setAtReferenceWritableKeyPath") public // COMPILER_INTRINSIC func _setAtReferenceWritableKeyPath( root: Root, keyPath: ReferenceWritableKeyPath, value: __owned Value ) { // TODO: we should be able to do this more efficiently than projecting. let (addr, owner) = keyPath._projectMutableAddress(from: root) addr.pointee = value _fixLifetime(owner) // FIXME: this needs a deallocation barrier to ensure that the // release isn't extended, along with the access scope. } // MARK: Appending type system // FIXME(ABI): The type relationships between KeyPath append operands are tricky // and don't interact well with our overriding rules. Hack things by injecting // a bunch of `appending` overloads as protocol extensions so they aren't // constrained by being overrides, and so that we can use exact-type constraints // on `Self` to prevent dynamically-typed methods from being inherited by // statically-typed key paths. /// An implementation detail of key path expressions; do not use this protocol /// directly. @_show_in_interface public protocol _AppendKeyPath {} extension _AppendKeyPath where Self == AnyKeyPath { /// Returns a new key path created by appending the given key path to this /// one. /// /// Use this method to extend this key path to the value type of another key /// path. Appending the key path passed as `path` is successful only if the /// root type for `path` matches this key path's value type. This example /// creates key paths from `Array` to `String` and from `String` to /// `Int`, and then tries appending each to the other: /// /// let arrayDescription: AnyKeyPath = \Array.description /// let stringLength: AnyKeyPath = \String.count /// /// // Creates a key path from `Array` to `Int` /// let arrayDescriptionLength = arrayDescription.appending(path: stringLength) /// /// let invalidKeyPath = stringLength.appending(path: arrayDescription) /// // invalidKeyPath == nil /// /// The second call to `appending(path:)` returns `nil` /// because the root type of `arrayDescription`, `Array`, does not /// match the value type of `stringLength`, `Int`. /// /// - Parameter path: The key path to append. /// - Returns: A key path from the root of this key path and the value type /// of `path`, if `path` can be appended. If `path` can't be appended, /// returns `nil`. @inlinable public func appending(path: AnyKeyPath) -> AnyKeyPath? { return _tryToAppendKeyPaths(root: self, leaf: path) } } extension _AppendKeyPath /* where Self == PartialKeyPath */ { /// Returns a new key path created by appending the given key path to this /// one. /// /// Use this method to extend this key path to the value type of another key /// path. Appending the key path passed as `path` is successful only if the /// root type for `path` matches this key path's value type. This example /// creates key paths from `Array` to `String` and from `String` to /// `Int`, and then tries appending each to the other: /// /// let arrayDescription: PartialKeyPath> = \.description /// let stringLength: PartialKeyPath = \.count /// /// // Creates a key path from `Array` to `Int` /// let arrayDescriptionLength = arrayDescription.appending(path: stringLength) /// /// let invalidKeyPath = stringLength.appending(path: arrayDescription) /// // invalidKeyPath == nil /// /// The second call to `appending(path:)` returns `nil` /// because the root type of `arrayDescription`, `Array`, does not /// match the value type of `stringLength`, `Int`. /// /// - Parameter path: The key path to append. /// - Returns: A key path from the root of this key path and the value type /// of `path`, if `path` can be appended. If `path` can't be appended, /// returns `nil`. @inlinable public func appending(path: AnyKeyPath) -> PartialKeyPath? where Self == PartialKeyPath { return _tryToAppendKeyPaths(root: self, leaf: path) } /// Returns a new key path created by appending the given key path to this /// one. /// /// Use this method to extend this key path to the value type of another key /// path. Appending the key path passed as `path` is successful only if the /// root type for `path` matches this key path's value type. This example /// creates a key path from `Array` to `String`, and then tries /// appending compatible and incompatible key paths: /// /// let arrayDescription: PartialKeyPath> = \.description /// /// // Creates a key path from `Array` to `Int` /// let arrayDescriptionLength = arrayDescription.appending(path: \String.count) /// /// let invalidKeyPath = arrayDescription.appending(path: \Double.isZero) /// // invalidKeyPath == nil /// /// The second call to `appending(path:)` returns `nil` because the root type /// of the `path` parameter, `Double`, does not match the value type of /// `arrayDescription`, `String`. /// /// - Parameter path: The key path to append. /// - Returns: A key path from the root of this key path to the value type /// of `path`, if `path` can be appended. If `path` can't be appended, /// returns `nil`. @inlinable public func appending( path: KeyPath ) -> KeyPath? where Self == PartialKeyPath { return _tryToAppendKeyPaths(root: self, leaf: path) } /// Returns a new key path created by appending the given key path to this /// one. /// /// Use this method to extend this key path to the value type of another key /// path. Appending the key path passed as `path` is successful only if the /// root type for `path` matches this key path's value type. /// /// - Parameter path: The reference writeable key path to append. /// - Returns: A key path from the root of this key path to the value type /// of `path`, if `path` can be appended. If `path` can't be appended, /// returns `nil`. @inlinable public func appending( path: ReferenceWritableKeyPath ) -> ReferenceWritableKeyPath? where Self == PartialKeyPath { return _tryToAppendKeyPaths(root: self, leaf: path) } } extension _AppendKeyPath /* where Self == KeyPath */ { /// Returns a new key path created by appending the given key path to this /// one. /// /// Use this method to extend this key path to the value type of another key /// path. Calling `appending(path:)` results in the same key path as if the /// given key path had been specified using dot notation. In the following /// example, `keyPath1` and `keyPath2` are equivalent: /// /// let arrayDescription = \Array.description /// let keyPath1 = arrayDescription.appending(path: \String.count) /// /// let keyPath2 = \Array.description.count /// /// - Parameter path: The key path to append. /// - Returns: A key path from the root of this key path to the value type of /// `path`. @inlinable public func appending( path: KeyPath ) -> KeyPath where Self: KeyPath { return _appendingKeyPaths(root: self, leaf: path) } /* TODO public func appending( path: Leaf, // FIXME: Satisfy "Value generic param not used in signature" constraint _: Value.Type = Value.self ) -> PartialKeyPath? where Self: KeyPath, Leaf == AnyKeyPath { return _tryToAppendKeyPaths(root: self, leaf: path) } */ /// Returns a new key path created by appending the given key path to this /// one. /// /// Use this method to extend this key path to the value type of another key /// path. Calling `appending(path:)` results in the same key path as if the /// given key path had been specified using dot notation. /// /// - Parameter path: The key path to append. /// - Returns: A key path from the root of this key path to the value type of /// `path`. @inlinable public func appending( path: ReferenceWritableKeyPath ) -> ReferenceWritableKeyPath where Self == KeyPath { return _appendingKeyPaths(root: self, leaf: path) } } extension _AppendKeyPath /* where Self == WritableKeyPath */ { /// Returns a new key path created by appending the given key path to this /// one. /// /// Use this method to extend this key path to the value type of another key /// path. Calling `appending(path:)` results in the same key path as if the /// given key path had been specified using dot notation. /// /// - Parameter path: The key path to append. /// - Returns: A key path from the root of this key path to the value type of /// `path`. @inlinable public func appending( path: WritableKeyPath ) -> WritableKeyPath where Self == WritableKeyPath { return _appendingKeyPaths(root: self, leaf: path) } /// Returns a new key path created by appending the given key path to this /// one. /// /// Use this method to extend this key path to the value type of another key /// path. Calling `appending(path:)` results in the same key path as if the /// given key path had been specified using dot notation. /// /// - Parameter path: The key path to append. /// - Returns: A key path from the root of this key path to the value type of /// `path`. @inlinable public func appending( path: ReferenceWritableKeyPath ) -> ReferenceWritableKeyPath where Self == WritableKeyPath { return _appendingKeyPaths(root: self, leaf: path) } } extension _AppendKeyPath /* where Self == ReferenceWritableKeyPath */ { /// Returns a new key path created by appending the given key path to this /// one. /// /// Use this method to extend this key path to the value type of another key /// path. Calling `appending(path:)` results in the same key path as if the /// given key path had been specified using dot notation. /// /// - Parameter path: The key path to append. /// - Returns: A key path from the root of this key path to the value type of /// `path`. @inlinable public func appending( path: WritableKeyPath ) -> ReferenceWritableKeyPath where Self == ReferenceWritableKeyPath { return _appendingKeyPaths(root: self, leaf: path) } } @usableFromInline internal func _tryToAppendKeyPaths( root: AnyKeyPath, leaf: AnyKeyPath ) -> Result? { let (rootRoot, rootValue) = type(of: root)._rootAndValueType let (leafRoot, leafValue) = type(of: leaf)._rootAndValueType if rootValue != leafRoot { return nil } func open(_: Root.Type) -> Result { func open2(_: Value.Type) -> Result { func open3(_: AppendedValue.Type) -> Result { let typedRoot = unsafeDowncast(root, to: KeyPath.self) let typedLeaf = unsafeDowncast(leaf, to: KeyPath.self) let result = _appendingKeyPaths(root: typedRoot, leaf: typedLeaf) return unsafeDowncast(result, to: Result.self) } return _openExistential(leafValue, do: open3) } return _openExistential(rootValue, do: open2) } return _openExistential(rootRoot, do: open) } @usableFromInline internal func _appendingKeyPaths< Root, Value, AppendedValue, Result: KeyPath >( root: KeyPath, leaf: KeyPath ) -> Result { let resultTy = type(of: root).appendedType(with: type(of: leaf)) return root.withBuffer { var rootBuffer = $0 return leaf.withBuffer { var leafBuffer = $0 // If either operand is the identity key path, then we should return // the other operand back untouched. if leafBuffer.data.isEmpty { return unsafeDowncast(root, to: Result.self) } if rootBuffer.data.isEmpty { return unsafeDowncast(leaf, to: Result.self) } // Reserve room for the appended KVC string, if both key paths are // KVC-compatible. let appendedKVCLength: Int, rootKVCLength: Int, leafKVCLength: Int if let rootPtr = root._kvcKeyPathStringPtr, let leafPtr = leaf._kvcKeyPathStringPtr { rootKVCLength = Int(_swift_stdlib_strlen(rootPtr)) leafKVCLength = Int(_swift_stdlib_strlen(leafPtr)) // root + "." + leaf appendedKVCLength = rootKVCLength + 1 + leafKVCLength + 1 } else { rootKVCLength = 0 leafKVCLength = 0 appendedKVCLength = 0 } // Result buffer has room for both key paths' components, plus the // header, plus space for the middle type. // Align up the root so that we can put the component type after it. let rootSize = MemoryLayout._roundingUpToAlignment(rootBuffer.data.count) let resultSize = rootSize + leafBuffer.data.count + 2 * MemoryLayout.size // Tail-allocate space for the KVC string. let totalResultSize = MemoryLayout ._roundingUpToAlignment(resultSize + appendedKVCLength) var kvcStringBuffer: UnsafeMutableRawPointer? = nil let result = resultTy._create(capacityInBytes: totalResultSize) { var destBuffer = $0 // Remember where the tail-allocated KVC string buffer begins. if appendedKVCLength > 0 { kvcStringBuffer = destBuffer.baseAddress.unsafelyUnwrapped .advanced(by: resultSize) destBuffer = .init(start: destBuffer.baseAddress, count: resultSize) } var destBuilder = KeyPathBuffer.Builder(destBuffer) // Save space for the header. let leafIsReferenceWritable = type(of: leaf).kind == .reference destBuilder.pushHeader(KeyPathBuffer.Header( size: resultSize - MemoryLayout.size, trivial: rootBuffer.trivial && leafBuffer.trivial, hasReferencePrefix: rootBuffer.hasReferencePrefix || leafIsReferenceWritable )) let leafHasReferencePrefix = leafBuffer.hasReferencePrefix // Clone the root components into the buffer. while true { let (component, type) = rootBuffer.next() let isLast = type == nil // If the leaf appended path has a reference prefix, then the // entire root is part of the reference prefix. let endOfReferencePrefix: Bool if leafHasReferencePrefix { endOfReferencePrefix = false } else if isLast && leafIsReferenceWritable { endOfReferencePrefix = true } else { endOfReferencePrefix = component.header.endOfReferencePrefix } component.clone( into: &destBuilder.buffer, endOfReferencePrefix: endOfReferencePrefix) // Insert our endpoint type between the root and leaf components. if let type = type { destBuilder.push(type) } else { destBuilder.push(Value.self as Any.Type) break } } // Clone the leaf components into the buffer. while true { let (component, type) = leafBuffer.next() component.clone( into: &destBuilder.buffer, endOfReferencePrefix: component.header.endOfReferencePrefix) if let type = type { destBuilder.push(type) } else { break } } _internalInvariant(destBuilder.buffer.isEmpty, "did not fill entire result buffer") } // Build the KVC string if there is one. if let kvcStringBuffer = kvcStringBuffer { let rootPtr = root._kvcKeyPathStringPtr.unsafelyUnwrapped let leafPtr = leaf._kvcKeyPathStringPtr.unsafelyUnwrapped _memcpy(dest: kvcStringBuffer, src: rootPtr, size: UInt(rootKVCLength)) kvcStringBuffer.advanced(by: rootKVCLength) .storeBytes(of: 0x2E /* '.' */, as: CChar.self) _memcpy(dest: kvcStringBuffer.advanced(by: rootKVCLength + 1), src: leafPtr, size: UInt(leafKVCLength)) result._kvcKeyPathStringPtr = UnsafePointer(kvcStringBuffer.assumingMemoryBound(to: CChar.self)) kvcStringBuffer.advanced(by: rootKVCLength + leafKVCLength + 1) .storeBytes(of: 0 /* '\0' */, as: CChar.self) } return unsafeDowncast(result, to: Result.self) } } } // The distance in bytes from the address point of a KeyPath object to its // buffer header. Includes the size of the Swift heap object header and the // pointer to the KVC string. internal var keyPathObjectHeaderSize: Int { return MemoryLayout.size + MemoryLayout.size } internal var keyPathPatternHeaderSize: Int { return 16 } // Runtime entry point to instantiate a key path object. // Note that this has a compatibility override shim in the runtime so that // future compilers can backward-deploy support for instantiating new key path // pattern features. @_cdecl("swift_getKeyPathImpl") public func _swift_getKeyPath(pattern: UnsafeMutableRawPointer, arguments: UnsafeRawPointer) -> UnsafeRawPointer { // The key path pattern is laid out like a key path object, with a few // modifications: // - Pointers in the instantiated object are compressed into 32-bit // relative offsets in the pattern. // - The pattern begins with a field that's either zero, for a pattern that // depends on instantiation arguments, or that's a relative reference to // a global mutable pointer variable, which can be initialized to a single // shared instantiation of this pattern. // - Instead of the two-word object header with isa and refcount, two // pointers to metadata accessors are provided for the root and leaf // value types of the key path. // - Components may have unresolved forms that require instantiation. // - Type metadata and protocol conformance pointers are replaced with // relative-referenced accessor functions that instantiate the // needed generic argument when called. // // The pattern never precomputes the capabilities of the key path (readonly/ // writable/reference-writable), nor does it encode the reference prefix. // These are resolved dynamically, so that they always reflect the dynamic // capability of the properties involved. let oncePtrPtr = pattern let patternPtr = pattern.advanced(by: 4) let bufferHeader = patternPtr.load(fromByteOffset: keyPathPatternHeaderSize, as: KeyPathBuffer.Header.self) bufferHeader.validateReservedBits() // If the first word is nonzero, it relative-references a cache variable // we can use to reference a single shared instantiation of this key path. let oncePtrOffset = oncePtrPtr.load(as: Int32.self) let oncePtr: UnsafeRawPointer? if oncePtrOffset != 0 { let theOncePtr = _resolveRelativeAddress(oncePtrPtr, oncePtrOffset) oncePtr = theOncePtr // See whether we already instantiated this key path. // This is a non-atomic load because the instantiated pointer will be // written with a release barrier, and loads of the instantiated key path // ought to carry a dependency through this loaded pointer. let existingInstance = theOncePtr.load(as: UnsafeRawPointer?.self) if let existingInstance = existingInstance { // Return the instantiated object at +1. let object = Unmanaged.fromOpaque(existingInstance) // TODO: This retain will be unnecessary once we support global objects // with inert refcounting. _ = object.retain() return existingInstance } } else { oncePtr = nil } // Instantiate a new key path object modeled on the pattern. // Do a pass to determine the class of the key path we'll be instantiating // and how much space we'll need for it. let (keyPathClass, rootType, size, _) = _getKeyPathClassAndInstanceSizeFromPattern(patternPtr, arguments) // Allocate the instance. let instance = keyPathClass._create(capacityInBytes: size) { instanceData in // Instantiate the pattern into the instance. _instantiateKeyPathBuffer(patternPtr, instanceData, rootType, arguments) } // Adopt the KVC string from the pattern. let kvcStringBase = patternPtr.advanced(by: 12) let kvcStringOffset = kvcStringBase.load(as: Int32.self) if kvcStringOffset == 0 { // Null pointer. instance._kvcKeyPathStringPtr = nil } else { let kvcStringPtr = _resolveRelativeAddress(kvcStringBase, kvcStringOffset) instance._kvcKeyPathStringPtr = kvcStringPtr.assumingMemoryBound(to: CChar.self) } // If we can cache this instance as a shared instance, do so. if let oncePtr = oncePtr { // Try to replace a null pointer in the cache variable with the instance // pointer. let instancePtr = Unmanaged.passRetained(instance) while true { let (oldValue, won) = Builtin.cmpxchg_seqcst_seqcst_Word( oncePtr._rawValue, 0._builtinWordValue, UInt(bitPattern: instancePtr.toOpaque())._builtinWordValue) // If the exchange succeeds, then the instance we formed is the canonical // one. if Bool(won) { break } // Otherwise, someone raced with us to instantiate the key path pattern // and won. Their instance should be just as good as ours, so we can take // that one and let ours get deallocated. if let existingInstance = UnsafeRawPointer(bitPattern: Int(oldValue)) { // Return the instantiated object at +1. let object = Unmanaged.fromOpaque(existingInstance) // TODO: This retain will be unnecessary once we support global objects // with inert refcounting. _ = object.retain() // Release the instance we created. instancePtr.release() return existingInstance } else { // Try the cmpxchg again if it spuriously failed. continue } } } return UnsafeRawPointer(Unmanaged.passRetained(instance).toOpaque()) } // A reference to metadata, which is a pointer to a mangled name. internal typealias MetadataReference = UnsafeRawPointer // Determine the length of the given mangled name. internal 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 } // Resolve a mangled name in a generic environment, described by either a // flat GenericEnvironment * (if the bottom tag bit is 0) or possibly-nested // ContextDescriptor * (if the bottom tag bit is 1) internal func _getTypeByMangledNameInEnvironmentOrContext( _ name: UnsafePointer, _ nameLength: UInt, genericEnvironmentOrContext: UnsafeRawPointer?, genericArguments: UnsafeRawPointer?) -> Any.Type? { let taggedPointer = UInt(bitPattern: genericEnvironmentOrContext) if taggedPointer & 1 == 0 { return _getTypeByMangledNameInEnvironment(name, nameLength, genericEnvironment: genericEnvironmentOrContext, genericArguments: genericArguments) } else { let context = UnsafeRawPointer(bitPattern: taggedPointer & ~1) return _getTypeByMangledNameInContext(name, nameLength, genericContext: context, genericArguments: genericArguments) } } // Resolve the given generic argument reference to a generic argument. internal func _resolveKeyPathGenericArgReference( _ reference: UnsafeRawPointer, genericEnvironment: UnsafeRawPointer?, arguments: UnsafeRawPointer?) -> UnsafeRawPointer { // If the low bit is clear, it's a direct reference to the argument. if (UInt(bitPattern: reference) & 0x01 == 0) { return reference } // Adjust the reference. let referenceStart = reference - 1 // If we have a symbolic reference to an accessor, call it. let first = referenceStart.load(as: UInt8.self) if first == 255 && reference.load(as: UInt8.self) == 9 { typealias MetadataAccessor = @convention(c) (UnsafeRawPointer?) -> UnsafeRawPointer // Unaligned load of the offset. let pointerReference = reference + 1 var offset: Int32 = 0 _memcpy(dest: &offset, src: pointerReference, size: 4) let accessorPtrRaw = _resolveRelativeAddress(pointerReference, offset) let accessorPtrSigned = _PtrAuth.sign(pointer: accessorPtrRaw, key: .processIndependentCode, discriminator: _PtrAuth.discriminator(for: MetadataAccessor.self)) let accessor = unsafeBitCast(accessorPtrSigned, to: MetadataAccessor.self) return accessor(arguments) } let nameLength = _getSymbolicMangledNameLength(referenceStart) let namePtr = referenceStart.bindMemory(to: UInt8.self, capacity: nameLength + 1) // FIXME: Could extract this information from the mangled name. guard let result = _getTypeByMangledNameInEnvironmentOrContext(namePtr, UInt(nameLength), genericEnvironmentOrContext: genericEnvironment, genericArguments: arguments) else { let nameStr = String._fromUTF8Repairing( UnsafeBufferPointer(start: namePtr, count: nameLength) ).0 fatalError("could not demangle keypath type from '\(nameStr)'") } return unsafeBitCast(result, to: UnsafeRawPointer.self) } // Resolve the given metadata reference to (type) metadata. internal func _resolveKeyPathMetadataReference( _ reference: UnsafeRawPointer, genericEnvironment: UnsafeRawPointer?, arguments: UnsafeRawPointer?) -> Any.Type { return unsafeBitCast( _resolveKeyPathGenericArgReference( reference, genericEnvironment: genericEnvironment, arguments: arguments), to: Any.Type.self) } internal enum KeyPathStructOrClass { case `struct`, `class` } internal enum KeyPathPatternStoredOffset { case inline(UInt32) case outOfLine(UInt32) case unresolvedFieldOffset(UInt32) case unresolvedIndirectOffset(UnsafePointer) } internal struct KeyPathPatternComputedArguments { var getLayout: KeyPathComputedArgumentLayoutFn var witnesses: ComputedArgumentWitnessesPtr var initializer: KeyPathComputedArgumentInitializerFn } internal protocol KeyPathPatternVisitor { mutating func visitHeader(genericEnvironment: UnsafeRawPointer?, rootMetadataRef: MetadataReference, leafMetadataRef: MetadataReference, kvcCompatibilityString: UnsafeRawPointer?) mutating func visitStoredComponent(kind: KeyPathStructOrClass, mutable: Bool, offset: KeyPathPatternStoredOffset) mutating func visitComputedComponent(mutating: Bool, idKind: KeyPathComputedIDKind, idResolution: KeyPathComputedIDResolution, idValueBase: UnsafeRawPointer, idValue: Int32, getter: UnsafeRawPointer, setter: UnsafeRawPointer?, arguments: KeyPathPatternComputedArguments?, externalArgs: UnsafeBufferPointer?) mutating func visitOptionalChainComponent() mutating func visitOptionalForceComponent() mutating func visitOptionalWrapComponent() mutating func visitIntermediateComponentType(metadataRef: MetadataReference) mutating func finish() } internal func _resolveRelativeAddress(_ base: UnsafeRawPointer, _ offset: Int32) -> UnsafeRawPointer { // Sign-extend the offset to pointer width and add with wrap on overflow. return UnsafeRawPointer(bitPattern: Int(bitPattern: base) &+ Int(offset)) .unsafelyUnwrapped } internal func _resolveRelativeIndirectableAddress(_ base: UnsafeRawPointer, _ offset: Int32) -> UnsafeRawPointer { // Low bit indicates whether the reference is indirected or not. if offset & 1 != 0 { let ptrToPtr = _resolveRelativeAddress(base, offset - 1) return ptrToPtr.load(as: UnsafeRawPointer.self) } return _resolveRelativeAddress(base, offset) } internal func _loadRelativeAddress(at: UnsafeRawPointer, fromByteOffset: Int = 0, as: T.Type) -> T { let offset = at.load(fromByteOffset: fromByteOffset, as: Int32.self) return unsafeBitCast(_resolveRelativeAddress(at + fromByteOffset, offset), to: T.self) } internal func _walkKeyPathPattern( _ pattern: UnsafeRawPointer, walker: inout W) { // Visit the header. let genericEnvironment = _loadRelativeAddress(at: pattern, as: UnsafeRawPointer.self) let rootMetadataRef = _loadRelativeAddress(at: pattern, fromByteOffset: 4, as: MetadataReference.self) let leafMetadataRef = _loadRelativeAddress(at: pattern, fromByteOffset: 8, as: MetadataReference.self) let kvcString = _loadRelativeAddress(at: pattern, fromByteOffset: 12, as: UnsafeRawPointer.self) walker.visitHeader(genericEnvironment: genericEnvironment, rootMetadataRef: rootMetadataRef, leafMetadataRef: leafMetadataRef, kvcCompatibilityString: kvcString) func visitStored(header: RawKeyPathComponent.Header, componentBuffer: inout UnsafeRawBufferPointer) { // Decode a stored property. A small offset may be stored inline in the // header word, or else be stored out-of-line, or need instantiation of some // kind. let offset: KeyPathPatternStoredOffset switch header.storedOffsetPayload { case RawKeyPathComponent.Header.outOfLineOffsetPayload: offset = .outOfLine(_pop(from: &componentBuffer, as: UInt32.self)) case RawKeyPathComponent.Header.unresolvedFieldOffsetPayload: offset = .unresolvedFieldOffset(_pop(from: &componentBuffer, as: UInt32.self)) case RawKeyPathComponent.Header.unresolvedIndirectOffsetPayload: let base = componentBuffer.baseAddress.unsafelyUnwrapped let relativeOffset = _pop(from: &componentBuffer, as: Int32.self) let ptr = _resolveRelativeIndirectableAddress(base, relativeOffset) offset = .unresolvedIndirectOffset( ptr.assumingMemoryBound(to: UInt.self)) default: offset = .inline(header.storedOffsetPayload) } let kind: KeyPathStructOrClass = header.kind == .struct ? .struct : .class walker.visitStoredComponent(kind: kind, mutable: header.isStoredMutable, offset: offset) } func popComputedAccessors(header: RawKeyPathComponent.Header, componentBuffer: inout UnsafeRawBufferPointer) -> (idValueBase: UnsafeRawPointer, idValue: Int32, getter: UnsafeRawPointer, setter: UnsafeRawPointer?) { let idValueBase = componentBuffer.baseAddress.unsafelyUnwrapped let idValue = _pop(from: &componentBuffer, as: Int32.self) let getterBase = componentBuffer.baseAddress.unsafelyUnwrapped let getterRef = _pop(from: &componentBuffer, as: Int32.self) let getter = _resolveRelativeAddress(getterBase, getterRef) let setter: UnsafeRawPointer? if header.isComputedSettable { let setterBase = componentBuffer.baseAddress.unsafelyUnwrapped let setterRef = _pop(from: &componentBuffer, as: Int32.self) setter = _resolveRelativeAddress(setterBase, setterRef) } else { setter = nil } return (idValueBase: idValueBase, idValue: idValue, getter: getter, setter: setter) } func popComputedArguments(header: RawKeyPathComponent.Header, componentBuffer: inout UnsafeRawBufferPointer) -> KeyPathPatternComputedArguments? { if header.hasComputedArguments { let getLayoutBase = componentBuffer.baseAddress.unsafelyUnwrapped let getLayoutRef = _pop(from: &componentBuffer, as: Int32.self) let getLayoutRaw = _resolveRelativeAddress(getLayoutBase, getLayoutRef) let getLayoutSigned = _PtrAuth.sign(pointer: getLayoutRaw, key: .processIndependentCode, discriminator: _PtrAuth.discriminator(for: KeyPathComputedArgumentLayoutFn.self)) let getLayout = unsafeBitCast(getLayoutSigned, to: KeyPathComputedArgumentLayoutFn.self) let witnessesBase = componentBuffer.baseAddress.unsafelyUnwrapped let witnessesRef = _pop(from: &componentBuffer, as: Int32.self) let witnesses: UnsafeRawPointer if witnessesRef == 0 { witnesses = __swift_keyPathGenericWitnessTable_addr() } else { witnesses = _resolveRelativeAddress(witnessesBase, witnessesRef) } let initializerBase = componentBuffer.baseAddress.unsafelyUnwrapped let initializerRef = _pop(from: &componentBuffer, as: Int32.self) let initializerRaw = _resolveRelativeAddress(initializerBase, initializerRef) let initializerSigned = _PtrAuth.sign(pointer: initializerRaw, key: .processIndependentCode, discriminator: _PtrAuth.discriminator(for: KeyPathComputedArgumentInitializerFn.self)) let initializer = unsafeBitCast(initializerSigned, to: KeyPathComputedArgumentInitializerFn.self) return KeyPathPatternComputedArguments(getLayout: getLayout, witnesses: ComputedArgumentWitnessesPtr(witnesses), initializer: initializer) } else { return nil } } // We declare this down here to avoid the temptation to use it within // the functions above. let bufferPtr = pattern.advanced(by: keyPathPatternHeaderSize) let bufferHeader = bufferPtr.load(as: KeyPathBuffer.Header.self) var buffer = UnsafeRawBufferPointer(start: bufferPtr + 4, count: bufferHeader.size) while !buffer.isEmpty { let header = _pop(from: &buffer, as: RawKeyPathComponent.Header.self) // Ensure that we pop an amount of data consistent with what // RawKeyPathComponent.Header.patternComponentBodySize computes. var bufferSizeBefore = 0 var expectedPop = 0 _internalInvariant({ bufferSizeBefore = buffer.count expectedPop = header.patternComponentBodySize return true }()) switch header.kind { case .class, .struct: visitStored(header: header, componentBuffer: &buffer) case .computed: let (idValueBase, idValue, getter, setter) = popComputedAccessors(header: header, componentBuffer: &buffer) // If there are arguments, gather those too. let arguments = popComputedArguments(header: header, componentBuffer: &buffer) walker.visitComputedComponent(mutating: header.isComputedMutating, idKind: header.computedIDKind, idResolution: header.computedIDResolution, idValueBase: idValueBase, idValue: idValue, getter: getter, setter: setter, arguments: arguments, externalArgs: nil) case .optionalChain: walker.visitOptionalChainComponent() case .optionalWrap: walker.visitOptionalWrapComponent() case .optionalForce: walker.visitOptionalForceComponent() case .external: // Look at the external property descriptor to see if we should take it // over the component given in the pattern. let genericParamCount = Int(header.payload) let descriptorBase = buffer.baseAddress.unsafelyUnwrapped let descriptorOffset = _pop(from: &buffer, as: Int32.self) let descriptor = _resolveRelativeIndirectableAddress(descriptorBase, descriptorOffset) let descriptorHeader = descriptor.load(as: RawKeyPathComponent.Header.self) if descriptorHeader.isTrivialPropertyDescriptor { // If the descriptor is trivial, then use the local candidate. // Skip the external generic parameter accessors to get to it. _ = _pop(from: &buffer, as: Int32.self, count: genericParamCount) continue } // Grab the generic parameter accessors to pass to the external component. let externalArgs = _pop(from: &buffer, as: Int32.self, count: genericParamCount) // Grab the header for the local candidate in case we need it for // a computed property. let localCandidateHeader = _pop(from: &buffer, as: RawKeyPathComponent.Header.self) let localCandidateSize = localCandidateHeader.patternComponentBodySize _internalInvariant({ expectedPop += localCandidateSize + 4 return true }()) let descriptorSize = descriptorHeader.propertyDescriptorBodySize var descriptorBuffer = UnsafeRawBufferPointer(start: descriptor + 4, count: descriptorSize) // Look at what kind of component the external property has. switch descriptorHeader.kind { case .struct, .class: // A stored component. We can instantiate it // without help from the local candidate. _ = _pop(from: &buffer, as: UInt8.self, count: localCandidateSize) visitStored(header: descriptorHeader, componentBuffer: &descriptorBuffer) case .computed: // A computed component. The accessors come from the descriptor. let (idValueBase, idValue, getter, setter) = popComputedAccessors(header: descriptorHeader, componentBuffer: &descriptorBuffer) // Get the arguments from the external descriptor and/or local candidate // component. let arguments: KeyPathPatternComputedArguments? if localCandidateHeader.kind == .computed && localCandidateHeader.hasComputedArguments { // If both have arguments, then we have to build a bit of a chimera. // The canonical identity and accessors come from the descriptor, // but the argument equality/hash handling is still as described // in the local candidate. // We don't need the local candidate's accessors. _ = popComputedAccessors(header: localCandidateHeader, componentBuffer: &buffer) // We do need the local arguments. arguments = popComputedArguments(header: localCandidateHeader, componentBuffer: &buffer) } else { // If the local candidate doesn't have arguments, we don't need // anything from it at all. _ = _pop(from: &buffer, as: UInt8.self, count: localCandidateSize) arguments = nil } walker.visitComputedComponent( mutating: descriptorHeader.isComputedMutating, idKind: descriptorHeader.computedIDKind, idResolution: descriptorHeader.computedIDResolution, idValueBase: idValueBase, idValue: idValue, getter: getter, setter: setter, arguments: arguments, externalArgs: genericParamCount > 0 ? externalArgs : nil) case .optionalChain, .optionalWrap, .optionalForce, .external: _internalInvariantFailure("not possible for property descriptor") } } // Check that we consumed the expected amount of data from the pattern. _internalInvariant( { // Round the amount of data we read up to alignment. let popped = MemoryLayout._roundingUpToAlignment( bufferSizeBefore - buffer.count) return expectedPop == popped }(), """ component size consumed during pattern walk does not match \ component size returned by patternComponentBodySize """) // Break if this is the last component. if buffer.isEmpty { break } // Otherwise, pop the intermediate component type accessor and // go around again. let componentTypeBase = buffer.baseAddress.unsafelyUnwrapped let componentTypeOffset = _pop(from: &buffer, as: Int32.self) let componentTypeRef = _resolveRelativeAddress(componentTypeBase, componentTypeOffset) walker.visitIntermediateComponentType(metadataRef: componentTypeRef) _internalInvariant(!buffer.isEmpty) } // We should have walked the entire pattern. _internalInvariant(buffer.isEmpty, "did not walk entire pattern buffer") walker.finish() } internal struct GetKeyPathClassAndInstanceSizeFromPattern : KeyPathPatternVisitor { var size: Int = MemoryLayout.size // start with one word for the header var capability: KeyPathKind = .value var didChain: Bool = false var root: Any.Type! var leaf: Any.Type! var genericEnvironment: UnsafeRawPointer? let patternArgs: UnsafeRawPointer? init(patternArgs: UnsafeRawPointer?) { self.patternArgs = patternArgs } mutating func roundUpToPointerAlignment() { size = MemoryLayout._roundingUpToAlignment(size) } mutating func visitHeader(genericEnvironment: UnsafeRawPointer?, rootMetadataRef: MetadataReference, leafMetadataRef: MetadataReference, kvcCompatibilityString: UnsafeRawPointer?) { self.genericEnvironment = genericEnvironment // Get the root and leaf type metadata so we can form the class type // for the entire key path. root = _resolveKeyPathMetadataReference( rootMetadataRef, genericEnvironment: genericEnvironment, arguments: patternArgs) leaf = _resolveKeyPathMetadataReference( leafMetadataRef, genericEnvironment: genericEnvironment, arguments: patternArgs) } mutating func visitStoredComponent(kind: KeyPathStructOrClass, mutable: Bool, offset: KeyPathPatternStoredOffset) { // Mutable class properties can be the root of a reference mutation. // Mutable struct properties pass through the existing capability. if mutable { switch kind { case .class: capability = .reference case .struct: break } } else { // Immutable properties can only be read. capability = .readOnly } // The size of the instantiated component depends on whether we can fit // the offset inline. switch offset { case .inline: size += 4 case .outOfLine, .unresolvedFieldOffset, .unresolvedIndirectOffset: size += 8 } } mutating func visitComputedComponent(mutating: Bool, idKind: KeyPathComputedIDKind, idResolution: KeyPathComputedIDResolution, idValueBase: UnsafeRawPointer, idValue: Int32, getter: UnsafeRawPointer, setter: UnsafeRawPointer?, arguments: KeyPathPatternComputedArguments?, externalArgs: UnsafeBufferPointer?) { let settable = setter != nil switch (settable, mutating) { case (false, false): // If the property is get-only, the capability becomes read-only, unless // we get another reference-writable component. capability = .readOnly case (true, false): capability = .reference case (true, true): // Writable if the base is. No effect. break case (false, true): _internalInvariantFailure("unpossible") } // Save space for the header... size += 4 roundUpToPointerAlignment() // ...id, getter, and maybe setter... size += MemoryLayout.size * 2 if settable { size += MemoryLayout.size } // ...and the arguments, if any. let argumentHeaderSize = MemoryLayout.size * 2 switch (arguments, externalArgs) { case (nil, nil): break case (let arguments?, nil): size += argumentHeaderSize // If we have arguments, calculate how much space they need by invoking // the layout function. let (addedSize, addedAlignmentMask) = arguments.getLayout(patternArgs) // TODO: Handle over-aligned values _internalInvariant(addedAlignmentMask < MemoryLayout.alignment, "overaligned computed property element not supported") size += addedSize case (let arguments?, let externalArgs?): // If we're referencing an external declaration, and it takes captured // arguments, then we have to build a bit of a chimera. The canonical // identity and accessors come from the descriptor, but the argument // handling is still as described in the local candidate. size += argumentHeaderSize let (addedSize, addedAlignmentMask) = arguments.getLayout(patternArgs) // TODO: Handle over-aligned values _internalInvariant(addedAlignmentMask < MemoryLayout.alignment, "overaligned computed property element not supported") size += addedSize // We also need to store the size of the local arguments so we can // find the external component arguments. roundUpToPointerAlignment() size += RawKeyPathComponent.Header.externalWithArgumentsExtraSize size += MemoryLayout.size * externalArgs.count case (nil, let externalArgs?): // If we're instantiating an external property with a local // candidate that has no arguments, then things are a little // easier. We only need to instantiate the generic // arguments for the external component's accessors. size += argumentHeaderSize size += MemoryLayout.size * externalArgs.count } } mutating func visitOptionalChainComponent() { // Optional chaining forces the entire keypath to be read-only, even if // there are further reference-writable components. didChain = true capability = .readOnly size += 4 } mutating func visitOptionalWrapComponent() { // Optional chaining forces the entire keypath to be read-only, even if // there are further reference-writable components. didChain = true capability = .readOnly size += 4 } mutating func visitOptionalForceComponent() { // Force-unwrapping passes through the mutability of the preceding keypath. size += 4 } mutating func visitIntermediateComponentType(metadataRef _: MetadataReference) { // The instantiated component type will be stored in the instantiated // object. roundUpToPointerAlignment() size += MemoryLayout.size } mutating func finish() { } } internal func _getKeyPathClassAndInstanceSizeFromPattern( _ pattern: UnsafeRawPointer, _ arguments: UnsafeRawPointer ) -> ( keyPathClass: AnyKeyPath.Type, rootType: Any.Type, size: Int, alignmentMask: Int ) { var walker = GetKeyPathClassAndInstanceSizeFromPattern(patternArgs: arguments) _walkKeyPathPattern(pattern, walker: &walker) // Chaining always renders the whole key path read-only. if walker.didChain { walker.capability = .readOnly } // Grab the class object for the key path type we'll end up with. func openRoot(_: Root.Type) -> AnyKeyPath.Type { func openLeaf(_: Leaf.Type) -> AnyKeyPath.Type { switch walker.capability { case .readOnly: return KeyPath.self case .value: return WritableKeyPath.self case .reference: return ReferenceWritableKeyPath.self } } return _openExistential(walker.leaf!, do: openLeaf) } let classTy = _openExistential(walker.root!, do: openRoot) return (keyPathClass: classTy, rootType: walker.root!, size: walker.size, // FIXME: Handle overalignment alignmentMask: MemoryLayout._alignmentMask) } internal struct InstantiateKeyPathBuffer: KeyPathPatternVisitor { var destData: UnsafeMutableRawBufferPointer var genericEnvironment: UnsafeRawPointer? let patternArgs: UnsafeRawPointer? var base: Any.Type init(destData: UnsafeMutableRawBufferPointer, patternArgs: UnsafeRawPointer?, root: Any.Type) { self.destData = destData self.patternArgs = patternArgs self.base = root } // Track the triviality of the resulting object data. var isTrivial: Bool = true // Track where the reference prefix begins. var endOfReferencePrefixComponent: UnsafeMutableRawPointer? = nil var previousComponentAddr: UnsafeMutableRawPointer? = nil mutating func adjustDestForAlignment(of: T.Type) -> ( baseAddress: UnsafeMutableRawPointer, misalign: Int ) { let alignment = MemoryLayout.alignment var baseAddress = destData.baseAddress.unsafelyUnwrapped var misalign = Int(bitPattern: baseAddress) % alignment if misalign != 0 { misalign = alignment - misalign baseAddress = baseAddress.advanced(by: misalign) } return (baseAddress, misalign) } mutating func pushDest(_ value: T) { _internalInvariant(_isPOD(T.self)) let size = MemoryLayout.size let (baseAddress, misalign) = adjustDestForAlignment(of: T.self) withUnsafeBytes(of: value) { _memcpy(dest: baseAddress, src: $0.baseAddress.unsafelyUnwrapped, size: UInt(size)) } destData = UnsafeMutableRawBufferPointer( start: baseAddress + size, count: destData.count - size - misalign) } mutating func pushAddressDiscriminatedFunctionPointer( _ unsignedPointer: UnsafeRawPointer, discriminator: UInt64 ) { let size = MemoryLayout.size let (baseAddress, misalign) = adjustDestForAlignment(of: UnsafeRawPointer.self) baseAddress._storeFunctionPointerWithAddressDiscrimination( unsignedPointer, discriminator: discriminator) destData = UnsafeMutableRawBufferPointer( start: baseAddress + size, count: destData.count - size - misalign) } mutating func updatePreviousComponentAddr() -> UnsafeMutableRawPointer? { let oldValue = previousComponentAddr previousComponentAddr = destData.baseAddress.unsafelyUnwrapped return oldValue } mutating func visitHeader(genericEnvironment: UnsafeRawPointer?, rootMetadataRef: MetadataReference, leafMetadataRef: MetadataReference, kvcCompatibilityString: UnsafeRawPointer?) { self.genericEnvironment = genericEnvironment } mutating func visitStoredComponent(kind: KeyPathStructOrClass, mutable: Bool, offset: KeyPathPatternStoredOffset) { let previous = updatePreviousComponentAddr() switch kind { case .class: // A mutable class property can end the reference prefix. if mutable { endOfReferencePrefixComponent = previous } fallthrough case .struct: // Resolve the offset. switch offset { case .inline(let value): let header = RawKeyPathComponent.Header(stored: kind, mutable: mutable, inlineOffset: value) pushDest(header) case .outOfLine(let offset): let header = RawKeyPathComponent.Header(storedWithOutOfLineOffset: kind, mutable: mutable) pushDest(header) pushDest(offset) case .unresolvedFieldOffset(let offsetOfOffset): // Look up offset in the type metadata. The value in the pattern is // the offset within the metadata object. let metadataPtr = unsafeBitCast(base, to: UnsafeRawPointer.self) let offset: UInt32 switch kind { case .class: offset = UInt32(metadataPtr.load(fromByteOffset: Int(offsetOfOffset), as: UInt.self)) case .struct: offset = UInt32(metadataPtr.load(fromByteOffset: Int(offsetOfOffset), as: UInt32.self)) } let header = RawKeyPathComponent.Header(storedWithOutOfLineOffset: kind, mutable: mutable) pushDest(header) pushDest(offset) case .unresolvedIndirectOffset(let pointerToOffset): // Look up offset in the indirectly-referenced variable we have a // pointer. _internalInvariant(pointerToOffset.pointee <= UInt32.max) let offset = UInt32(truncatingIfNeeded: pointerToOffset.pointee) let header = RawKeyPathComponent.Header(storedWithOutOfLineOffset: kind, mutable: mutable) pushDest(header) pushDest(offset) } } } mutating func visitComputedComponent(mutating: Bool, idKind: KeyPathComputedIDKind, idResolution: KeyPathComputedIDResolution, idValueBase: UnsafeRawPointer, idValue: Int32, getter: UnsafeRawPointer, setter: UnsafeRawPointer?, arguments: KeyPathPatternComputedArguments?, externalArgs: UnsafeBufferPointer?) { let previous = updatePreviousComponentAddr() let settable = setter != nil // A nonmutating settable property can end the reference prefix. if settable && !mutating { endOfReferencePrefixComponent = previous } // Resolve the ID. let resolvedID: UnsafeRawPointer? switch idKind { case .storedPropertyIndex, .vtableOffset: _internalInvariant(idResolution == .resolved) // Zero-extend the integer value to get the instantiated id. let value = UInt(UInt32(bitPattern: idValue)) resolvedID = UnsafeRawPointer(bitPattern: value) case .pointer: // Resolve the sign-extended relative reference. var absoluteID: UnsafeRawPointer? = _resolveRelativeAddress(idValueBase, idValue) // If the pointer ID is unresolved, then it needs work to get to // the final value. switch idResolution { case .resolved: break case .indirectPointer: // The pointer in the pattern is an indirect pointer to the real // identifier pointer. absoluteID = absoluteID.unsafelyUnwrapped .load(as: UnsafeRawPointer?.self) case .functionCall: // The pointer in the pattern is to a function that generates the // identifier pointer. typealias Resolver = @convention(c) (UnsafeRawPointer?) -> UnsafeRawPointer? let resolverSigned = _PtrAuth.sign( pointer: absoluteID.unsafelyUnwrapped, key: .processIndependentCode, discriminator: _PtrAuth.discriminator(for: Resolver.self)) let resolverFn = unsafeBitCast(resolverSigned, to: Resolver.self) absoluteID = resolverFn(patternArgs) } resolvedID = absoluteID } // Bring over the header, getter, and setter. let header = RawKeyPathComponent.Header(computedWithIDKind: idKind, mutating: mutating, settable: settable, hasArguments: arguments != nil || externalArgs != nil, instantiatedFromExternalWithArguments: arguments != nil && externalArgs != nil) pushDest(header) pushDest(resolvedID) pushAddressDiscriminatedFunctionPointer(getter, discriminator: ComputedAccessorsPtr.getterPtrAuthKey) if let setter = setter { pushAddressDiscriminatedFunctionPointer(setter, discriminator: mutating ? ComputedAccessorsPtr.mutatingSetterPtrAuthKey : ComputedAccessorsPtr.nonmutatingSetterPtrAuthKey) } if let arguments = arguments { // Instantiate the arguments. let (baseSize, alignmentMask) = arguments.getLayout(patternArgs) _internalInvariant(alignmentMask < MemoryLayout.alignment, "overaligned computed arguments not implemented yet") // The real buffer stride will be rounded up to alignment. var totalSize = (baseSize + alignmentMask) & ~alignmentMask // If an external property descriptor also has arguments, they'll be // added to the end with pointer alignment. if let externalArgs = externalArgs { totalSize = MemoryLayout._roundingUpToAlignment(totalSize) totalSize += MemoryLayout.size * externalArgs.count } pushDest(totalSize) pushDest(arguments.witnesses) // A nonnull destructor in the witnesses file indicates the instantiated // payload is nontrivial. if let _ = arguments.witnesses.destroy { isTrivial = false } // If the descriptor has arguments, store the size of its specific // arguments here, so we can drop them when trying to invoke // the component's witnesses. if let externalArgs = externalArgs { pushDest(externalArgs.count * MemoryLayout.size) } // Initialize the local candidate arguments here. _internalInvariant(Int(bitPattern: destData.baseAddress) & alignmentMask == 0, "argument destination not aligned") arguments.initializer(patternArgs, destData.baseAddress.unsafelyUnwrapped) destData = UnsafeMutableRawBufferPointer( start: destData.baseAddress.unsafelyUnwrapped + baseSize, count: destData.count - baseSize) } if let externalArgs = externalArgs { if arguments == nil { // If we're instantiating an external property without any local // arguments, then we only need to instantiate the arguments to the // property descriptor. let stride = MemoryLayout.size * externalArgs.count pushDest(stride) pushDest(__swift_keyPathGenericWitnessTable_addr()) } // Write the descriptor's generic arguments, which should all be relative // references to metadata accessor functions. for i in externalArgs.indices { let base = externalArgs.baseAddress.unsafelyUnwrapped + i let offset = base.pointee let metadataRef = _resolveRelativeAddress(UnsafeRawPointer(base), offset) let result = _resolveKeyPathGenericArgReference( metadataRef, genericEnvironment: genericEnvironment, arguments: patternArgs) pushDest(result) } } } mutating func visitOptionalChainComponent() { let _ = updatePreviousComponentAddr() let header = RawKeyPathComponent.Header(optionalChain: ()) pushDest(header) } mutating func visitOptionalWrapComponent() { let _ = updatePreviousComponentAddr() let header = RawKeyPathComponent.Header(optionalWrap: ()) pushDest(header) } mutating func visitOptionalForceComponent() { let _ = updatePreviousComponentAddr() let header = RawKeyPathComponent.Header(optionalForce: ()) pushDest(header) } mutating func visitIntermediateComponentType(metadataRef: MetadataReference) { // Get the metadata for the intermediate type. let metadata = _resolveKeyPathMetadataReference( metadataRef, genericEnvironment: genericEnvironment, arguments: patternArgs) pushDest(metadata) base = metadata } mutating func finish() { // Should have filled the entire buffer by the time we reach the end of the // pattern. _internalInvariant(destData.isEmpty, "should have filled entire destination buffer") } } #if INTERNAL_CHECKS_ENABLED // In debug builds of the standard library, check that instantiation produces // components whose sizes are consistent with the sizing visitor pass. internal struct ValidatingInstantiateKeyPathBuffer: KeyPathPatternVisitor { var sizeVisitor: GetKeyPathClassAndInstanceSizeFromPattern var instantiateVisitor: InstantiateKeyPathBuffer let origDest: UnsafeMutableRawPointer init(sizeVisitor: GetKeyPathClassAndInstanceSizeFromPattern, instantiateVisitor: InstantiateKeyPathBuffer) { self.sizeVisitor = sizeVisitor self.instantiateVisitor = instantiateVisitor origDest = self.instantiateVisitor.destData.baseAddress.unsafelyUnwrapped } mutating func visitHeader(genericEnvironment: UnsafeRawPointer?, rootMetadataRef: MetadataReference, leafMetadataRef: MetadataReference, kvcCompatibilityString: UnsafeRawPointer?) { sizeVisitor.visitHeader(genericEnvironment: genericEnvironment, rootMetadataRef: rootMetadataRef, leafMetadataRef: leafMetadataRef, kvcCompatibilityString: kvcCompatibilityString) instantiateVisitor.visitHeader(genericEnvironment: genericEnvironment, rootMetadataRef: rootMetadataRef, leafMetadataRef: leafMetadataRef, kvcCompatibilityString: kvcCompatibilityString) } mutating func visitStoredComponent(kind: KeyPathStructOrClass, mutable: Bool, offset: KeyPathPatternStoredOffset) { sizeVisitor.visitStoredComponent(kind: kind, mutable: mutable, offset: offset) instantiateVisitor.visitStoredComponent(kind: kind, mutable: mutable, offset: offset) checkSizeConsistency() } mutating func visitComputedComponent(mutating: Bool, idKind: KeyPathComputedIDKind, idResolution: KeyPathComputedIDResolution, idValueBase: UnsafeRawPointer, idValue: Int32, getter: UnsafeRawPointer, setter: UnsafeRawPointer?, arguments: KeyPathPatternComputedArguments?, externalArgs: UnsafeBufferPointer?) { sizeVisitor.visitComputedComponent(mutating: mutating, idKind: idKind, idResolution: idResolution, idValueBase: idValueBase, idValue: idValue, getter: getter, setter: setter, arguments: arguments, externalArgs: externalArgs) instantiateVisitor.visitComputedComponent(mutating: mutating, idKind: idKind, idResolution: idResolution, idValueBase: idValueBase, idValue: idValue, getter: getter, setter: setter, arguments: arguments, externalArgs: externalArgs) checkSizeConsistency() } mutating func visitOptionalChainComponent() { sizeVisitor.visitOptionalChainComponent() instantiateVisitor.visitOptionalChainComponent() checkSizeConsistency() } mutating func visitOptionalWrapComponent() { sizeVisitor.visitOptionalWrapComponent() instantiateVisitor.visitOptionalWrapComponent() checkSizeConsistency() } mutating func visitOptionalForceComponent() { sizeVisitor.visitOptionalForceComponent() instantiateVisitor.visitOptionalForceComponent() checkSizeConsistency() } mutating func visitIntermediateComponentType(metadataRef: MetadataReference) { sizeVisitor.visitIntermediateComponentType(metadataRef: metadataRef) instantiateVisitor.visitIntermediateComponentType(metadataRef: metadataRef) checkSizeConsistency() } mutating func finish() { sizeVisitor.finish() instantiateVisitor.finish() checkSizeConsistency() } func checkSizeConsistency() { let nextDest = instantiateVisitor.destData.baseAddress.unsafelyUnwrapped let curSize = nextDest - origDest + MemoryLayout.size _internalInvariant(curSize == sizeVisitor.size, "size and instantiation visitors out of sync") } } #endif // INTERNAL_CHECKS_ENABLED internal func _instantiateKeyPathBuffer( _ pattern: UnsafeRawPointer, _ origDestData: UnsafeMutableRawBufferPointer, _ rootType: Any.Type, _ arguments: UnsafeRawPointer ) { let destHeaderPtr = origDestData.baseAddress.unsafelyUnwrapped var destData = UnsafeMutableRawBufferPointer( start: destHeaderPtr.advanced(by: MemoryLayout.size), count: origDestData.count - MemoryLayout.size) #if INTERNAL_CHECKS_ENABLED // If checks are enabled, use a validating walker that ensures that the // size pre-walk and instantiation walk are in sync. let sizeWalker = GetKeyPathClassAndInstanceSizeFromPattern( patternArgs: arguments) let instantiateWalker = InstantiateKeyPathBuffer( destData: destData, patternArgs: arguments, root: rootType) var walker = ValidatingInstantiateKeyPathBuffer(sizeVisitor: sizeWalker, instantiateVisitor: instantiateWalker) #else var walker = InstantiateKeyPathBuffer( destData: destData, patternArgs: arguments, root: rootType) #endif _walkKeyPathPattern(pattern, walker: &walker) #if INTERNAL_CHECKS_ENABLED let isTrivial = walker.instantiateVisitor.isTrivial let endOfReferencePrefixComponent = walker.instantiateVisitor.endOfReferencePrefixComponent #else let isTrivial = walker.isTrivial let endOfReferencePrefixComponent = walker.endOfReferencePrefixComponent #endif // Write out the header. let destHeader = KeyPathBuffer.Header( size: origDestData.count - MemoryLayout.size, trivial: isTrivial, hasReferencePrefix: endOfReferencePrefixComponent != nil) destHeaderPtr.storeBytes(of: destHeader, as: KeyPathBuffer.Header.self) // Mark the reference prefix if there is one. if let endOfReferencePrefixComponent = endOfReferencePrefixComponent { var componentHeader = endOfReferencePrefixComponent .load(as: RawKeyPathComponent.Header.self) componentHeader.endOfReferencePrefix = true endOfReferencePrefixComponent.storeBytes(of: componentHeader, as: RawKeyPathComponent.Header.self) } }