forked from pointfreeco/swift-custom-dump
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathKeyPath.swift
859 lines (713 loc) · 27.8 KB
/
KeyPath.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
import Foundation
extension AnyKeyPath: CustomDumpStringConvertible {
public var customDumpDescription: String {
// NB: We gate this to 5.9+ due to this crasher: https://github.com/apple/swift/issues/64865
#if swift(>=5.9)
if #available(macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4, *) {
return self.debugDescription
}
#endif
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
keyPathToNameLock.lock()
defer { keyPathToNameLock.unlock() }
guard let name = keyPathToName[self] else {
func reflectName() -> String {
var namedKeyPaths = Reflection.allNamedKeyPaths(forUnderlyingTypeOf: Self.rootType)
while !namedKeyPaths.isEmpty {
let (name, keyPath) = namedKeyPaths.removeFirst()
if keyPath == self {
return #"\\#(typeName(Self.rootType)).\#(name)"#
}
let valueType = type(of: keyPath).valueType
let valueNamedKeyPaths = Reflection.allNamedKeyPaths(forUnderlyingTypeOf: valueType)
for (valueName, valueKeyPath) in valueNamedKeyPaths {
if let appendedKeyPath = keyPath.appending(path: valueKeyPath) {
namedKeyPaths.append(("\(name).\(valueName)", appendedKeyPath))
}
}
}
return """
\(typeName(Self.self))<\
\(typeName(Self.rootType, genericsAbbreviated: false)), \
\(typeName(Self.valueType, genericsAbbreviated: false))>
"""
}
let name = reflectName()
keyPathToName[self] = name
return name
}
return name
#else
return """
\(typeName(Self.self))<\
\(typeName(Self.rootType, genericsAbbreviated: false)), \
\(typeName(Self.valueType, genericsAbbreviated: false))>
"""
#endif
}
}
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
private var keyPathToNameLock = NSRecursiveLock()
private var keyPathToName: [AnyKeyPath: String] = [:]
// The source code below was extracted from the "KeyPath Reflection" branch of Apple's
// "Swift Evolution Staging" package:
//
// https://github.com/apple/swift-evolution-staging/tree/reflection
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
private protocol RelativePointer {
associatedtype Pointee
var offset: Int32 { get }
func address(from ptr: UnsafeRawPointer) -> UnsafePointer<Pointee>
func pointee(from ptr: UnsafeRawPointer) -> Pointee?
}
extension RelativePointer {
fileprivate func address(from ptr: UnsafeRawPointer) -> UnsafePointer<Pointee> {
let newPtr = UnsafeRawPointer(
bitPattern: UInt(bitPattern: ptr) &+ UInt(bitPattern: Int(offset)))!
return newPtr.assumingMemoryBound(to: Pointee.self)
}
}
private struct RelativeDirectPointer<Pointee>: RelativePointer {
let offset: Int32
func pointee(from ptr: UnsafeRawPointer) -> Pointee? {
guard offset != 0 else {
return nil
}
return address(from: ptr).pointee
}
}
extension UnsafeRawPointer {
fileprivate func relativeDirect<T>(as type: T.Type) -> UnsafePointer<T> {
let relativePointer = RelativeDirectPointer<T>(
offset: load(as: Int32.self)
)
return relativePointer.address(from: self)
}
}
private struct RelativeIndirectPointer<T>: RelativePointer {
typealias Pointee = UnsafePointer<T>
let offset: Int32
func pointee(from ptr: UnsafeRawPointer) -> Pointee? {
guard offset != 0 else {
return nil
}
return address(from: ptr).pointee
}
}
private struct RelativeIndirectablePointer<Pointee>: RelativePointer {
let offset: Int32
func address(from ptr: UnsafeRawPointer) -> UnsafePointer<Pointee> {
UnsafePointer<Pointee>((ptr + Int(offset & ~1))._rawValue)
}
func pointee(from ptr: UnsafeRawPointer) -> Pointee? {
guard offset != 0 else {
return nil
}
if offset & 1 == 1 {
let pointer = UnsafeRawPointer(address(from: ptr))
.load(as: UnsafePointer<Pointee>.self)
return pointer.pointee
} else {
return address(from: ptr).pointee
}
}
}
//===----------------------------------------------------------------------===//
// Metadata Structures
//===----------------------------------------------------------------------===//
// MetadataKind is the discriminator value found at the start of all metadata
// records to determine what kind is a metadata.
private enum MetadataKind: Int {
case `class` = 0
case `struct` = 512
}
// Metadata refers to the runtime representation of a type in Swift. This
// protocol is the generic version handed out by various methods to retrieve
// metadata from types.
private protocol Metadata {
// The required backing pointer which points at the metadata record.
var pointer: UnsafeRawPointer { get }
// The discriminator which determines what kind of metadata this is.
var kind: MetadataKind { get }
}
extension Metadata {
// The type representation of the metadata.
fileprivate var type: Any.Type {
unsafeBitCast(pointer, to: Any.Type.self)
}
}
// Given an arbitrary type of anything, produce the metadata that represents
// said type.
// FIXME: Right now this only supports structs and class types, but in the
// future if we ever want to produce keypaths for tuples, enums, etc. handle
// that here.
private func getMetadata(for type: Any.Type) -> Metadata? {
let pointer = unsafeBitCast(type, to: UnsafeRawPointer.self)
let int = pointer.load(as: Int.self)
guard let kind = MetadataKind(rawValue: int) else {
// If the metadata kind is greater than 2047, then it's an ISA pointer
// meaning we have some class metadata.
guard int > 2047 else {
return nil
}
return ClassMetadata(pointer: pointer)
}
switch kind {
case .class:
return ClassMetadata(pointer: pointer)
case .struct:
return StructMetadata(pointer: pointer)
}
}
// Type Metadata
// Type Metadata is a more specialized metadata in that only struct, class, and
// enum types conform to. There's more detail about the type and its properties
// in the context descriptors, the generic types that make up said type, etc.
private protocol TypeMetadata: Metadata {}
extension TypeMetadata {
// The context descriptors describes more in detail about the type. Some of
// this information includes number of properties, the property names, the
// name of this type, generic requirements, etc.
fileprivate var contextDescriptor: TypeContextDescriptor {
switch self {
case let structMetadata as StructMetadata:
return structMetadata.descriptor
case let classMetadata as ClassMetadata:
return classMetadata.descriptor
default:
fatalError("TypeMetadata conformance we don't know about?")
}
}
// An array of integers that represent the offset to a certain field. This
// corresponds to the index of fields in the field descriptor.
fileprivate var fieldOffsets: [Int] {
switch self {
case let structMetadata as StructMetadata:
return structMetadata.fieldOffsets
case let classMetadata as ClassMetadata:
return classMetadata.fieldOffsets
default:
fatalError("TypeMetadata conformance we don't know about?")
}
}
// The pointer to the beginning of this type's generic arguments.
fileprivate var genericArgumentPointer: UnsafeRawPointer {
switch self {
case is StructMetadata:
return pointer + MemoryLayout<_StructMetadata>.size
case let classMetadata as ClassMetadata:
let descriptor = classMetadata.descriptor
guard !descriptor.typeFlags.classHasResilientSuperclass else {
let memberOffset = descriptor.resilientBounds._immediateMembersOffset
return pointer + memberOffset
}
let negativeSize = descriptor.negativeSize
let positiveSize = descriptor.positiveSize
let numImmediateMembers = descriptor.numImmediateMembers
if descriptor.typeFlags.classAreImmediateMembersNegative {
return pointer + MemoryLayout<Int>.size * -negativeSize
} else {
return pointer + MemoryLayout<Int>.size * (positiveSize - numImmediateMembers)
}
default:
fatalError("TypeMetadata conformance we don't know about?")
}
}
// Given a mangled name (preferrably one of the property type name's), return
// the type as represented by the mangled name within this type's context.
fileprivate func type(of mangledName: UnsafePointer<CChar>) -> Any.Type? {
let type = _getTypeByMangledNameInContext(
UnsafePointer<UInt8>(mangledName._rawValue),
UInt(getSymbolicMangledNameLength(UnsafeRawPointer(mangledName))),
genericContext: contextDescriptor.pointer,
genericArguments: genericArgumentPointer
)
return type
}
}
// Struct Metadata
// Struct Metadata refers to types whom are implemented via a struct. Consider
// the standard library type 'Int', it's implemented using a struct, so getting
// the type metadata for that type will return an instance of struct metadata.
private struct StructMetadata: TypeMetadata, LayoutWrapper {
typealias Layout = _StructMetadata
// The backing metadata pointer.
let pointer: UnsafeRawPointer
// The metadata discriminator.
var kind: MetadataKind {
.struct
}
// The context descriptor of this struct.
var descriptor: StructDescriptor {
layout.descriptor
}
// An array of integers with the offsets for each stored field in this struct.
var fieldOffsets: [Int] {
let fieldOffsetVectorOffset = descriptor.fieldOffsetVectorOffset
let start = pointer + MemoryLayout<Int>.size * fieldOffsetVectorOffset
let buffer = UnsafeBufferPointer<UInt32>(
start: UnsafePointer<UInt32>(start._rawValue),
count: descriptor.numFields
)
return Array(buffer).map { Int($0) }
}
}
private struct _StructMetadata {
let kind: Int
let descriptor: StructDescriptor
}
// Class Metadata
// Class Metadata refers to types whom are implemented via a class. Consider
// the standard library type 'KeyPath', it's implemented using a class, so
// getting the type metadata for that type will return an instance of class
// metadata.
private struct ClassMetadata: TypeMetadata, LayoutWrapper {
typealias Layout = _ClassMetadata
// The backing metadata pointer.
let pointer: UnsafeRawPointer
// The metadata discriminator.
var kind: MetadataKind {
.class
}
// The context descriptor of this class.
var descriptor: ClassDescriptor {
layout._descriptor
}
// An array of integers with the offsets for each stored field in this class.
var fieldOffsets: [Int] {
let fieldOffsetVectorOffset = descriptor.fieldOffsetVectorOffset
let start = pointer + MemoryLayout<Int>.size * fieldOffsetVectorOffset
let buffer = UnsafeBufferPointer<Int>(
start: UnsafePointer<Int>(start._rawValue),
count: descriptor.numFields
)
return Array(buffer)
}
// The required size of instances of this type.
var instanceSize: Int {
Int(layout._instanceSize)
}
// The alignment mask of the address point for instances of this type.
var instanceAlignMask: Int {
Int(layout._instanceAlignMask)
}
}
private struct _ClassMetadata {
let _kind: Int
let _superclass: Any.Type?
let _reserved: (Int, Int)
let _rodata: Int
let _flags: UInt32
let _instanceAddressPoint: UInt32
let _instanceSize: UInt32
let _instanceAlignMask: UInt16
let _runtimeReserved: UInt16
let _classSize: UInt32
let _classAddressPoint: UInt32
let _descriptor: ClassDescriptor
}
//===----------------------------------------------------------------------===//
// Context Descriptor Structures
//===----------------------------------------------------------------------===//
// A context descriptor describes in entity in Swift who declares some context
// which other declarations can be declared within.
private protocol ContextDescriptor {
// The backing context descriptor pointer.
var pointer: UnsafeRawPointer { get }
}
extension ContextDescriptor {
// The base structural representation of a context descriptor.
fileprivate var _contextDescriptor: _ContextDescriptor {
pointer.load(as: _ContextDescriptor.self)
}
// Flags that describe this context which include what kind it is, whether
// or not it's a generic context, whether or not it's unique, etc.
fileprivate var flags: ContextDescriptorFlags {
_contextDescriptor._flags
}
}
private struct _ContextDescriptor {
let _flags: ContextDescriptorFlags
let _parent: RelativeIndirectablePointer<_ContextDescriptor>
}
// Flags that describe this context which include what kind it is, whether
// or not it's a generic context, whether or not it's unique, etc.
private struct ContextDescriptorFlags {
// The backing integer representation of these flags.
let bits: UInt32
// The reserved bits for other flags that are interpretted differently by
// conforming context descriptor types.
var kindSpecificFlags: UInt16 {
UInt16((bits >> 0x10) & 0xFFFF)
}
}
// Type Context Descriptor
// A type context descriptor is a refined context descriptor who describes a
// type in Swift. This includes structs, classes, and enums. Protocols also
// define a new type in Swift, but aren't considered type contexts.
private protocol TypeContextDescriptor: ContextDescriptor {
// The field descriptor that describes the stored representation of this type.
var fields: FieldDescriptor { get }
}
extension TypeContextDescriptor {
// The base structural representation of a type context descriptor.
fileprivate var _typeDescriptor: _TypeContextDescriptor {
pointer.load(as: _TypeContextDescriptor.self)
}
// The field descriptor that describes the stored representation of this type.
fileprivate var fields: FieldDescriptor {
let offset = pointer.advanced(by: MemoryLayout<Int32>.size * 4)
let address = UnsafeRawPointer(_typeDescriptor._fields.address(from: offset))
return FieldDescriptor(signedPointer: address)
}
// Certain flags specific to types in Swift, such as whether or not a class
// has a resilient superclass.
fileprivate var typeFlags: TypeContextDescriptorFlags {
TypeContextDescriptorFlags(bits: flags.kindSpecificFlags)
}
}
private struct _TypeContextDescriptor {
let _base: _ContextDescriptor
let _name: RelativeDirectPointer<CChar>
let _accessor: RelativeDirectPointer<UnsafeRawPointer>
let _fields: RelativeDirectPointer<_FieldDescriptor>
}
// Certain flags specific to types in Swift, such as whether or not a class
// has a resilient superclass.
private struct TypeContextDescriptorFlags {
// The backing integer representation of these flags.
let bits: UInt16
// Whether or not the class's members are negative.
var classAreImmediateMembersNegative: Bool {
bits & 0x1000 != 0
}
// Whether or not the class has a resilient superclass.
var classHasResilientSuperclass: Bool {
bits & 0x2000 != 0
}
}
// Struct Descriptor
// A struct descriptor that describes some structure context.
private struct StructDescriptor: TypeContextDescriptor, PointerAuthenticatedLayoutWrapper {
typealias Layout = _StructDescriptor
// The backing context descriptor pointer.
let signedPointer: UnsafeRawPointer
// The offset to the field offset vector found in the metadata.
var fieldOffsetVectorOffset: Int {
Int(layout._fieldOffsetVectorOffset)
}
// The number of stored properties this struct has.
var numFields: Int {
Int(layout._numFields)
}
}
private struct _StructDescriptor {
let _base: _TypeContextDescriptor
let _numFields: UInt32
let _fieldOffsetVectorOffset: UInt32
}
// Class Descriptor
// A class descriptor that descibes some class context.
private struct ClassDescriptor: TypeContextDescriptor, PointerAuthenticatedLayoutWrapper {
typealias Layout = _ClassDescriptor
// The backing context descriptor pointer.
let signedPointer: UnsafeRawPointer
// The offset to the field offset vector found in the metadata.
var fieldOffsetVectorOffset: Int {
Int(layout._fieldOffsetVectorOffset)
}
// The negative size of the metadata objects in this class.
var negativeSize: Int {
assert(!typeFlags.classHasResilientSuperclass)
return Int(layout._negativeSizeOrResilientBounds)
}
// The number of stored properties this class defines.
var numFields: Int {
Int(layout._numFields)
}
// The total number of members this class defines (not including it's
// superclass, if it has one).
var numImmediateMembers: Int {
Int(layout._numImmediateMembers)
}
// The positive size of the metadata objects in this class.
var positiveSize: Int {
assert(!typeFlags.classHasResilientSuperclass)
return Int(layout._positiveSizeOrExtraFlags)
}
// The resilient bounds for this class.
var resilientBounds: _StoredClassMetadataBounds {
let addr = address(for: \._negativeSizeOrResilientBounds)
let pointer = UnsafeRawPointer(addr)
return pointer.relativeDirect(as: _StoredClassMetadataBounds.self).pointee
}
}
private struct _ClassDescriptor {
let _base: _TypeContextDescriptor
let _superclassMangledName: RelativeDirectPointer<CChar>
let _negativeSizeOrResilientBounds: Int32
let _positiveSizeOrExtraFlags: Int32
let _numImmediateMembers: UInt32
let _numFields: UInt32
let _fieldOffsetVectorOffset: UInt32
}
private struct _StoredClassMetadataBounds {
let _immediateMembersOffset: Int
}
// Field Descriptor
// A special descriptor that describes a type's fields.
private struct FieldDescriptor: PointerAuthenticatedLayoutWrapper {
typealias Layout = _FieldDescriptor
// The backing field descriptor pointer.
let signedPointer: UnsafeRawPointer
// The number of fields this type has. This could mean different things
// depending on what kind of type this is found under. For example, this is
// the number of stored properties found within a struct, but for enums this
// is the number of cases.
var numFields: Int {
Int(layout._numFields)
}
// An array of the field record information. Field record information contains
// things like it's mangled type, whether or not its a var, indirect, etc.
var records: [FieldRecord] {
var result = [FieldRecord]()
result.reserveCapacity(numFields)
for i in 0..<numFields {
let address = trailing + MemoryLayout<_FieldRecord>.size * i
result.append(FieldRecord(signedPointer: address))
}
return result
}
}
// A record that describes a single stored property or an enum case.
private struct FieldRecord: PointerAuthenticatedLayoutWrapper {
typealias Layout = _FieldRecord
// The backing field record pointer.
let signedPointer: UnsafeRawPointer
// The flags that describe this field record.
var flags: FieldRecordFlags {
layout._flags
}
// The mangled type name that demangles to the field's type.
var mangledTypeName: UnsafePointer<CChar> {
address(for: \._mangledTypeName)
}
// The name of the stored property/enum case.
var name: String {
String(cString: address(for: \._fieldName))
}
}
private struct _FieldDescriptor {
let _mangledTypeName: RelativeDirectPointer<CChar>
let _superclassMangledTypeName: RelativeDirectPointer<CChar>
let _kind: UInt16
let _recordSize: UInt16
let _numFields: UInt32
}
private struct _FieldRecord {
let _flags: FieldRecordFlags
let _mangledTypeName: RelativeDirectPointer<CChar>
let _fieldName: RelativeDirectPointer<CChar>
}
// The flags which describe a field record.
private struct FieldRecordFlags {
// The backing integer representation of these flags.
let bits: UInt32
// Whether or not this stored property is a var.
var isVar: Bool {
bits & 0x2 != 0
}
}
//===----------------------------------------------------------------------===//
// Misc. Utilities
//===----------------------------------------------------------------------===//
private protocol LayoutWrapper {
associatedtype Layout
var pointer: UnsafeRawPointer { get }
}
private protocol PointerAuthenticatedLayoutWrapper: LayoutWrapper {
var signedPointer: UnsafeRawPointer { get }
}
extension PointerAuthenticatedLayoutWrapper {
fileprivate var pointer: UnsafeRawPointer {
signedPointer
}
}
extension LayoutWrapper {
fileprivate var layout: Layout {
pointer.load(as: Layout.self)
}
fileprivate var trailing: UnsafeRawPointer {
pointer + MemoryLayout<Layout>.size
}
fileprivate func address<T>(for field: KeyPath<Layout, T>) -> UnsafePointer<T> {
let offset = MemoryLayout<Layout>.offset(of: field)!
return UnsafePointer<T>((pointer + offset)._rawValue)
}
fileprivate func address<T: RelativePointer, U>(
for field: KeyPath<Layout, T>
) -> UnsafePointer<U> where T.Pointee == U {
let offset = MemoryLayout<Layout>.offset(of: field)!
return layout[keyPath: field].address(from: pointer + offset)
}
}
// This is a utility within KeyPath.swift in the standard library. If this
// gets moved into there, then this goes away, but will have to rethink if this
// goes into a different module.
private func getSymbolicMangledNameLength(_ base: UnsafeRawPointer) -> Int {
var end = base
while let current = Optional(end.load(as: UInt8.self)), current != 0 {
// Skip the current character
end = end + 1
// Skip over a symbolic reference
if current >= 0x1 && current <= 0x17 {
end += 4
} else if current >= 0x18 && current <= 0x1F {
end += MemoryLayout<Int>.size
}
}
return end - base
}
@_silgen_name("swift_allocObject")
internal func _allocObject(_: UnsafeMutableRawPointer, _: Int, _: Int) -> AnyObject?
// This is a utility within KeyPath.swift in the standard library. If this
// gets moved into there, then this goes away, but will have to rethink if this
// goes into a different module.
extension AnyKeyPath {
fileprivate static func _create(
capacityInBytes bytes: Int,
initializedBy body: (UnsafeMutableRawBufferPointer) -> Void
) -> Self {
assert(
bytes > 0 && bytes % 4 == 0,
"capacity must be multiple of 4 bytes")
let metadata = getMetadata(for: self) as! ClassMetadata
var size = metadata.instanceSize
let tailStride = MemoryLayout<Int32>.stride
let tailAlignMask = MemoryLayout<Int32>.alignment - 1
size += tailAlignMask
size &= ~tailAlignMask
size += tailStride * (bytes / 4)
let alignment = metadata.instanceAlignMask | tailAlignMask
let object = _allocObject(
UnsafeMutableRawPointer(mutating: metadata.pointer),
size,
alignment
)
guard object != nil else {
fatalError("Allocating \(self) instance failed for keypath reflection")
}
// This memory layout of Int by 2 is the size of a heap object which object
// points to. Tail members appear immediately afterwards.
let base =
unsafeBitCast(object, to: UnsafeMutableRawPointer.self) + MemoryLayout<Int>.size * 2
// The first word is the kvc string pointer. Set it to 0 (nil).
base.storeBytes(of: 0, as: Int.self)
// Return an offseted base after the kvc string pointer.
let newBase = base + MemoryLayout<Int>.size
let newBytes = bytes - MemoryLayout<Int>.size
body(UnsafeMutableRawBufferPointer(start: newBase, count: newBytes))
return unsafeBitCast(object, to: self)
}
}
// Helper struct to represent the keypath buffer header. This structure is also
// found within KeyPath.swift, so if this gets moved there this goes away.
private struct KeyPathBufferHeader {
let bits: UInt32
init(hasReferencePrefix: Bool, isTrivial: Bool, size: UInt32) {
var bits = size
if hasReferencePrefix {
bits |= 0x4000_0000
}
if isTrivial {
bits |= 0x8000_0000
}
self.bits = bits
}
}
// This initializes the raw keypath buffer with the field offset information.
private func instantiateKeyPathBuffer(
_ metadata: TypeMetadata,
_ leafIndex: Int,
_ data: UnsafeMutableRawBufferPointer
) {
let header = KeyPathBufferHeader(
hasReferencePrefix: false,
isTrivial: true,
size: UInt32(MemoryLayout<UInt32>.size)
)
data.storeBytes(of: header, as: KeyPathBufferHeader.self)
var componentBits = UInt32(metadata.fieldOffsets[leafIndex])
componentBits |= metadata.kind == .struct ? 1 << 24 : 3 << 24
data.storeBytes(
of: componentBits,
toByteOffset: MemoryLayout<Int>.size,
as: UInt32.self
)
}
// Returns a concrete type for which this keypath is going to be given a root
// and leaf type.
private func getKeyPathType(
from root: TypeMetadata,
for leaf: FieldRecord
) -> AnyKeyPath.Type {
let leafType = root.type(of: leaf.mangledTypeName)!
func openRoot<Root>(_: Root.Type) -> AnyKeyPath.Type {
func openLeaf<Value>(_: Value.Type) -> AnyKeyPath.Type {
if leaf.flags.isVar {
return root.kind == .class
? ReferenceWritableKeyPath<Root, Value>.self
: WritableKeyPath<Root, Value>.self
}
return KeyPath<Root, Value>.self
}
return _openExistential(leafType, do: openLeaf)
}
return _openExistential(root.type, do: openRoot)
}
// Given a root type and a leaf index, create a concrete keypath object at
// runtime.
private func createKeyPath(root: TypeMetadata, leaf: Int) -> AnyKeyPath {
let field = root.contextDescriptor.fields.records[leaf]
let keyPathTy = getKeyPathType(from: root, for: field)
let size = MemoryLayout<Int>.size * 3
let instance = keyPathTy._create(capacityInBytes: size) {
instantiateKeyPathBuffer(root, leaf, $0)
}
let heapObj = UnsafeRawPointer(Unmanaged.passRetained(instance).autorelease().toOpaque())
let keyPath = unsafeBitCast(heapObj, to: AnyKeyPath.self)
return keyPath
}
private enum Reflection {
/// Returns the collection of all named key paths of this type.
///
/// - Parameter value: A value of any type to return the stored key paths of.
/// - Returns: An array of tuples with both the name and partial key path
/// for this value.
static func allNamedKeyPaths(
forUnderlyingTypeOf type: Any.Type
) -> [(name: String, keyPath: AnyKeyPath)] {
guard let metadata = getMetadata(for: type) as? TypeMetadata else {
return []
}
var result = [(name: String, keyPath: AnyKeyPath)]()
result.reserveCapacity(metadata.contextDescriptor.fields.numFields)
for i in 0..<metadata.contextDescriptor.fields.numFields {
let name = metadata.contextDescriptor.fields.records[i].name
let keyPath = createKeyPath(root: metadata, leaf: i)
result.append((name: name, keyPath: keyPath))
}
return result
}
}
#endif