Skip to content

Commit 3aa05d7

Browse files
natecook1000lorentey
authored andcommitted
[stdlib] Add _forEachField(of:options:body:) function (#32873)
This function walks all the fields of a struct, class, or tuple, and calls `body` with the name, offset, and type of each field. `body` can perform any required work or validation, returning `true` to continue walking fields or `false` to stop immediately. (cherry picked from commit 3af1deb)
1 parent 9e57fbf commit 3aa05d7

File tree

3 files changed

+741
-21
lines changed

3 files changed

+741
-21
lines changed

stdlib/public/core/ReflectionMirror.swift

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,43 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
@_silgen_name("swift_isClassType")
14+
internal func _isClassType(_: Any.Type) -> Bool
15+
16+
@_silgen_name("swift_getMetadataKind")
17+
internal func _metadataKind(_: Any.Type) -> UInt
18+
1319
@_silgen_name("swift_reflectionMirror_normalizedType")
1420
internal func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type
1521

1622
@_silgen_name("swift_reflectionMirror_count")
1723
internal func _getChildCount<T>(_: T, type: Any.Type) -> Int
1824

25+
@_silgen_name("swift_reflectionMirror_recursiveCount")
26+
internal func _getRecursiveChildCount(_: Any.Type) -> Int
27+
28+
@_silgen_name("swift_reflectionMirror_recursiveChildMetadata")
29+
internal func _getChildMetadata(
30+
_: Any.Type,
31+
index: Int,
32+
outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
33+
outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
34+
) -> Any.Type
35+
36+
@_silgen_name("swift_reflectionMirror_recursiveChildOffset")
37+
internal func _getChildOffset(
38+
_: Any.Type,
39+
index: Int
40+
) -> Int
41+
1942
internal typealias NameFreeFunc = @convention(c) (UnsafePointer<CChar>?) -> Void
2043

2144
@_silgen_name("swift_reflectionMirror_subscript")
@@ -160,3 +183,107 @@ extension Mirror {
160183
#endif
161184
}
162185
}
186+
187+
/// Options for calling `_forEachField(of:options:body:)`.
188+
@available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *)
189+
@_spi(Reflection)
190+
public struct _EachFieldOptions: OptionSet {
191+
public var rawValue: UInt32
192+
193+
public init(rawValue: UInt32) {
194+
self.rawValue = rawValue
195+
}
196+
197+
/// Require the top-level type to be a class.
198+
///
199+
/// If this is not set, the top-level type is required to be a struct or
200+
/// tuple.
201+
public static var classType = _EachFieldOptions(rawValue: 1 << 0)
202+
203+
/// Ignore fields that can't be introspected.
204+
///
205+
/// If not set, the presence of things that can't be introspected causes
206+
/// the function to immediately return `false`.
207+
public static var ignoreUnknown = _EachFieldOptions(rawValue: 1 << 1)
208+
}
209+
210+
/// The metadata "kind" for a type.
211+
@available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *)
212+
@_spi(Reflection)
213+
public enum _MetadataKind: UInt {
214+
// With "flags":
215+
// runtimePrivate = 0x100
216+
// nonHeap = 0x200
217+
// nonType = 0x400
218+
219+
case `class` = 0
220+
case `struct` = 0x200 // 0 | nonHeap
221+
case `enum` = 0x201 // 1 | nonHeap
222+
case optional = 0x202 // 2 | nonHeap
223+
case foreignClass = 0x203 // 3 | nonHeap
224+
case opaque = 0x300 // 0 | runtimePrivate | nonHeap
225+
case tuple = 0x301 // 1 | runtimePrivate | nonHeap
226+
case function = 0x302 // 2 | runtimePrivate | nonHeap
227+
case existential = 0x303 // 3 | runtimePrivate | nonHeap
228+
case metatype = 0x304 // 4 | runtimePrivate | nonHeap
229+
case objcClassWrapper = 0x305 // 5 | runtimePrivate | nonHeap
230+
case existentialMetatype = 0x306 // 6 | runtimePrivate | nonHeap
231+
case heapLocalVariable = 0x400 // 0 | nonType
232+
case heapGenericLocalVariable = 0x500 // 0 | nonType | runtimePrivate
233+
case errorObject = 0x501 // 1 | nonType | runtimePrivate
234+
case unknown = 0xffff
235+
236+
init(_ type: Any.Type) {
237+
let v = _metadataKind(type)
238+
if let result = _MetadataKind(rawValue: v) {
239+
self = result
240+
} else {
241+
self = .unknown
242+
}
243+
}
244+
}
245+
246+
/// Calls the given closure on every field of the specified type.
247+
///
248+
/// If `body` returns `false` for any field, no additional fields are visited.
249+
///
250+
/// - Parameters:
251+
/// - type: The type to inspect.
252+
/// - options: Options to use when reflecting over `type`.
253+
/// - body: A closure to call with information about each field in `type`.
254+
/// The parameters to `body` are a pointer to a C string holding the name
255+
/// of the field, the offset of the field in bytes, the type of the field,
256+
/// and the `_MetadataKind` of the field's type.
257+
/// - Returns: `true` if every invocation of `body` returns `true`; otherwise,
258+
/// `false`.
259+
@available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *)
260+
@discardableResult
261+
@_spi(Reflection)
262+
public func _forEachField(
263+
of type: Any.Type,
264+
options: _EachFieldOptions = [],
265+
body: (UnsafePointer<CChar>, Int, Any.Type, _MetadataKind) -> Bool
266+
) -> Bool {
267+
// Require class type iff `.classType` is included as an option
268+
if _isClassType(type) != options.contains(.classType) {
269+
return false
270+
}
271+
272+
let childCount = _getRecursiveChildCount(type)
273+
for i in 0..<childCount {
274+
let offset = _getChildOffset(type, index: i)
275+
276+
var nameC: UnsafePointer<CChar>? = nil
277+
var freeFunc: NameFreeFunc? = nil
278+
let childType = _getChildMetadata(
279+
type, index: i, outName: &nameC, outFreeFunc: &freeFunc)
280+
defer { freeFunc?(nameC) }
281+
let kind = _MetadataKind(childType)
282+
283+
if !body(nameC!, offset, childType, kind) {
284+
return false
285+
}
286+
}
287+
288+
return true
289+
}

0 commit comments

Comments
 (0)