From 3522a338a28ca0ef501a7e0ed1f1f8250d33b599 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 4 Apr 2023 09:44:18 -0700 Subject: [PATCH 1/2] Create function to create an offset based keypath at runtime --- stdlib/public/core/KeyPath.swift | 45 ++++++++++++++++++++++++++++++++ test/stdlib/KeyPath.swift | 21 +++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift index 05102ed10389e..d2e27a520c46d 100644 --- a/stdlib/public/core/KeyPath.swift +++ b/stdlib/public/core/KeyPath.swift @@ -3893,6 +3893,51 @@ internal func _instantiateKeyPathBuffer( return offset } +@available(SwiftStdlib 5.9, *) +public func _createOffsetBasedKeyPath( + root: Any.Type, + value: Any.Type, + offset: Int, + isClass: Bool = false +) -> AnyKeyPath { + func openRoot(_: Root.Type) -> AnyKeyPath.Type { + func openValue(_: Value.Type) -> AnyKeyPath.Type { + KeyPath.self + } + + return _openExistential(value, do: openValue(_:)) + } + + let kpTy = _openExistential(root, do: openRoot(_:)) + + // The buffer header is 32 bits, but components must start on a word + // boundary. + let kpBufferSize = MemoryLayout.size + MemoryLayout.size + return kpTy._create(capacityInBytes: kpBufferSize) { + var builder = KeyPathBuffer.Builder($0) + let header = KeyPathBuffer.Header( + size: kpBufferSize - MemoryLayout.size, + trivial: true, + hasReferencePrefix: false + ) + + builder.pushHeader(header) + + let componentHeader = RawKeyPathComponent.Header( + stored: isClass ? .class : .struct, + mutable: false, + inlineOffset: UInt32(offset) + ) + + let component = RawKeyPathComponent( + header: componentHeader, + body: UnsafeRawBufferPointer(start: nil, count: 0) + ) + + component.clone(into: &builder.buffer, endOfReferencePrefix: false) + } +} + #if SWIFT_ENABLE_REFLECTION @_silgen_name("swift_keyPath_copySymbolName") diff --git a/test/stdlib/KeyPath.swift b/test/stdlib/KeyPath.swift index 734de6fc9d064..68ea4257c34c6 100644 --- a/test/stdlib/KeyPath.swift +++ b/test/stdlib/KeyPath.swift @@ -1065,5 +1065,26 @@ keyPath.test("ReferenceWritableKeyPath statically typed as WritableKeyPath") { expectEqual(outer[keyPath: upcastKeyPath], 46) } +struct Dog { + var name: String + var age: Int +} + +if #available(SwiftStdlib 5.9, *) { + keyPath.test("_createOffsetBasedKeyPath") { + let dogAgeKp = _createOffsetBasedKeyPath( + root: Dog.self, + value: Int.self, + offset: 16 + ) as? KeyPath + + expectNotNil(dogAgeKp) + + let sparky = Dog(name: "Sparky", age: 7) + + expectEqual(sparky[keyPath: dogAgeKp!], 7) + } +} + runAllTests() From e9c0b32497a623d647d2ae0362355c1352e99803 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 4 Apr 2023 09:54:09 -0700 Subject: [PATCH 2/2] Remove class support --- stdlib/public/core/KeyPath.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift index d2e27a520c46d..d7914b979afe5 100644 --- a/stdlib/public/core/KeyPath.swift +++ b/stdlib/public/core/KeyPath.swift @@ -3897,8 +3897,7 @@ internal func _instantiateKeyPathBuffer( public func _createOffsetBasedKeyPath( root: Any.Type, value: Any.Type, - offset: Int, - isClass: Bool = false + offset: Int ) -> AnyKeyPath { func openRoot(_: Root.Type) -> AnyKeyPath.Type { func openValue(_: Value.Type) -> AnyKeyPath.Type { @@ -3924,7 +3923,7 @@ public func _createOffsetBasedKeyPath( builder.pushHeader(header) let componentHeader = RawKeyPathComponent.Header( - stored: isClass ? .class : .struct, + stored: .struct, mutable: false, inlineOffset: UInt32(offset) )