Skip to content

Commit 4e39e59

Browse files
authored
Add a field reflection function that constructs keypaths. (#34815)
While the existing _forEachField in ReflectionMirror.swift already gives the offsets and types for each field, this isn't enough information to construct a keypath for that field in order to modify it. For reference, this should be sufficent to implement the features described here: (https://forums.swift.org/t/storedpropertyiterable/19218/62) purely at runtime without any derived conformances for many types. Note: Since there isn't enough reflection information for `.mutatingGetSet` fields, this means that we're not able to support reflecting certain types of fields (functions, nonfinal class fields, etc). Whether this is an error or not is controlled by the `.ignoreUnknown` option.
1 parent 89fab1b commit 4e39e59

File tree

8 files changed

+282
-48
lines changed

8 files changed

+282
-48
lines changed

include/swift/Reflection/Records.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ class FieldRecord {
9292
bool isIndirectCase() const {
9393
return Flags.isIndirectCase();
9494
}
95+
96+
bool isVar() const {
97+
return Flags.isVar();
98+
}
9599
};
96100

97101
struct FieldRecordIterator {

stdlib/public/SwiftShims/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ set(sources
1010
MetadataSections.h
1111
Random.h
1212
RefCount.h
13+
Reflection.h
1314
RuntimeShims.h
1415
RuntimeStubs.h
1516
SwiftStdbool.h

stdlib/public/SwiftShims/Reflection.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===--- Reflection.h - Types for access to reflection metadata. ----------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_STDLIB_SHIMS_REFLECTION_H
14+
#define SWIFT_STDLIB_SHIMS_REFLECTION_H
15+
16+
#include "SwiftStdbool.h"
17+
#include "SwiftStdint.h"
18+
19+
#ifdef __cplusplus
20+
extern "C" {
21+
#endif
22+
23+
typedef void (*NameFreeFunc)(const char*);
24+
25+
typedef struct _FieldReflectionMetadata {
26+
const char* name;
27+
NameFreeFunc freeFunc;
28+
__swift_bool isStrong;
29+
__swift_bool isVar;
30+
} _FieldReflectionMetadata;
31+
32+
#ifdef __cplusplus
33+
} // extern "C"
34+
#endif
35+
36+
#endif // SWIFT_STDLIB_SHIMS_REFLECTION_H

stdlib/public/SwiftShims/module.modulemap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module SwiftShims {
99
header "MetadataSections.h"
1010
header "Random.h"
1111
header "RefCount.h"
12+
header "Reflection.h"
1213
header "RuntimeShims.h"
1314
header "RuntimeStubs.h"
1415
header "SwiftStdbool.h"

stdlib/public/core/KeyPath.swift

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1739,6 +1739,40 @@ internal struct KeyPathBuffer {
17391739
return UnsafeMutableRawBufferPointer(mutating: data)
17401740
}
17411741

1742+
internal struct Builder {
1743+
internal var buffer: UnsafeMutableRawBufferPointer
1744+
internal init(_ buffer: UnsafeMutableRawBufferPointer) {
1745+
self.buffer = buffer
1746+
}
1747+
internal mutating func pushRaw(size: Int, alignment: Int)
1748+
-> UnsafeMutableRawBufferPointer {
1749+
var baseAddress = buffer.baseAddress.unsafelyUnwrapped
1750+
var misalign = Int(bitPattern: baseAddress) % alignment
1751+
if misalign != 0 {
1752+
misalign = alignment - misalign
1753+
baseAddress = baseAddress.advanced(by: misalign)
1754+
}
1755+
let result = UnsafeMutableRawBufferPointer(
1756+
start: baseAddress,
1757+
count: size)
1758+
buffer = UnsafeMutableRawBufferPointer(
1759+
start: baseAddress + size,
1760+
count: buffer.count - size - misalign)
1761+
return result
1762+
}
1763+
internal mutating func push<T>(_ value: T) {
1764+
let buf = pushRaw(size: MemoryLayout<T>.size,
1765+
alignment: MemoryLayout<T>.alignment)
1766+
buf.storeBytes(of: value, as: T.self)
1767+
}
1768+
internal mutating func pushHeader(_ header: Header) {
1769+
push(header)
1770+
// Start the components at pointer alignment
1771+
_ = pushRaw(size: RawKeyPathComponent.Header.pointerAlignmentSkew,
1772+
alignment: 4)
1773+
}
1774+
}
1775+
17421776
internal struct Header {
17431777
internal var _value: UInt32
17441778

@@ -2286,40 +2320,16 @@ internal func _appendingKeyPaths<
22862320
count: resultSize)
22872321
}
22882322

2289-
func pushRaw(size: Int, alignment: Int)
2290-
-> UnsafeMutableRawBufferPointer {
2291-
var baseAddress = destBuffer.baseAddress.unsafelyUnwrapped
2292-
var misalign = Int(bitPattern: baseAddress) % alignment
2293-
if misalign != 0 {
2294-
misalign = alignment - misalign
2295-
baseAddress = baseAddress.advanced(by: misalign)
2296-
}
2297-
let result = UnsafeMutableRawBufferPointer(
2298-
start: baseAddress,
2299-
count: size)
2300-
destBuffer = UnsafeMutableRawBufferPointer(
2301-
start: baseAddress + size,
2302-
count: destBuffer.count - size - misalign)
2303-
return result
2304-
}
2305-
func push<T>(_ value: T) {
2306-
let buf = pushRaw(size: MemoryLayout<T>.size,
2307-
alignment: MemoryLayout<T>.alignment)
2308-
buf.storeBytes(of: value, as: T.self)
2309-
}
2323+
var destBuilder = KeyPathBuffer.Builder(destBuffer)
23102324

23112325
// Save space for the header.
23122326
let leafIsReferenceWritable = type(of: leaf).kind == .reference
2313-
let header = KeyPathBuffer.Header(
2327+
destBuilder.pushHeader(KeyPathBuffer.Header(
23142328
size: resultSize - MemoryLayout<Int>.size,
23152329
trivial: rootBuffer.trivial && leafBuffer.trivial,
23162330
hasReferencePrefix: rootBuffer.hasReferencePrefix
23172331
|| leafIsReferenceWritable
2318-
)
2319-
push(header)
2320-
// Start the components at pointer alignment
2321-
_ = pushRaw(size: RawKeyPathComponent.Header.pointerAlignmentSkew,
2322-
alignment: 4)
2332+
))
23232333

23242334
let leafHasReferencePrefix = leafBuffer.hasReferencePrefix
23252335

@@ -2340,13 +2350,13 @@ internal func _appendingKeyPaths<
23402350
}
23412351

23422352
component.clone(
2343-
into: &destBuffer,
2353+
into: &destBuilder.buffer,
23442354
endOfReferencePrefix: endOfReferencePrefix)
2355+
// Insert our endpoint type between the root and leaf components.
23452356
if let type = type {
2346-
push(type)
2357+
destBuilder.push(type)
23472358
} else {
2348-
// Insert our endpoint type between the root and leaf components.
2349-
push(Value.self as Any.Type)
2359+
destBuilder.push(Value.self as Any.Type)
23502360
break
23512361
}
23522362
}
@@ -2356,17 +2366,17 @@ internal func _appendingKeyPaths<
23562366
let (component, type) = leafBuffer.next()
23572367

23582368
component.clone(
2359-
into: &destBuffer,
2369+
into: &destBuilder.buffer,
23602370
endOfReferencePrefix: component.header.endOfReferencePrefix)
23612371

23622372
if let type = type {
2363-
push(type)
2373+
destBuilder.push(type)
23642374
} else {
23652375
break
23662376
}
23672377
}
23682378

2369-
_internalInvariant(destBuffer.isEmpty,
2379+
_internalInvariant(destBuilder.buffer.isEmpty,
23702380
"did not fill entire result buffer")
23712381
}
23722382

stdlib/public/core/ReflectionMirror.swift

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import SwiftShims
14+
1315
@_silgen_name("swift_isClassType")
1416
internal func _isClassType(_: Any.Type) -> Bool
1517

@@ -29,8 +31,7 @@ internal func _getRecursiveChildCount(_: Any.Type) -> Int
2931
internal func _getChildMetadata(
3032
_: Any.Type,
3133
index: Int,
32-
outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
33-
outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
34+
fieldMetadata: UnsafeMutablePointer<_FieldReflectionMetadata>
3435
) -> Any.Type
3536

3637
@_silgen_name("swift_reflectionMirror_recursiveChildOffset")
@@ -281,14 +282,91 @@ public func _forEachField(
281282
for i in 0..<childCount {
282283
let offset = _getChildOffset(type, index: i)
283284

284-
var nameC: UnsafePointer<CChar>? = nil
285-
var freeFunc: NameFreeFunc? = nil
286-
let childType = _getChildMetadata(
287-
type, index: i, outName: &nameC, outFreeFunc: &freeFunc)
288-
defer { freeFunc?(nameC) }
285+
var field = _FieldReflectionMetadata()
286+
let childType = _getChildMetadata(type, index: i, fieldMetadata: &field)
287+
defer { field.freeFunc?(field.name) }
288+
let kind = _MetadataKind(childType)
289+
290+
if !body(field.name!, offset, childType, kind) {
291+
return false
292+
}
293+
}
294+
295+
return true
296+
}
297+
298+
/// Calls the given closure on every field of the specified type.
299+
///
300+
/// If `body` returns `false` for any field, no additional fields are visited.
301+
///
302+
/// - Parameters:
303+
/// - type: The type to inspect.
304+
/// - options: Options to use when reflecting over `type`.
305+
/// - body: A closure to call with information about each field in `type`.
306+
/// The parameters to `body` are a pointer to a C string holding the name
307+
/// of the field, the offset of the field in bytes, the type of the field,
308+
/// and the `_MetadataKind` of the field's type.
309+
/// - Returns: `true` if every invocation of `body` returns `true`; otherwise,
310+
/// `false`.
311+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
312+
@discardableResult
313+
@_spi(Reflection)
314+
public func _forEachFieldWithKeyPath<Root>(
315+
of type: Root.Type,
316+
options: _EachFieldOptions = [],
317+
body: (UnsafePointer<CChar>, PartialKeyPath<Root>) -> Bool
318+
) -> Bool {
319+
// Class types not supported because the metadata does not have
320+
// enough information to construct computed properties.
321+
if _isClassType(type) || options.contains(.classType) {
322+
return false
323+
}
324+
let ignoreUnknown = options.contains(.ignoreUnknown)
325+
326+
let childCount = _getRecursiveChildCount(type)
327+
for i in 0..<childCount {
328+
let offset = _getChildOffset(type, index: i)
329+
330+
var field = _FieldReflectionMetadata()
331+
let childType = _getChildMetadata(type, index: i, fieldMetadata: &field)
332+
defer { field.freeFunc?(field.name) }
289333
let kind = _MetadataKind(childType)
334+
let supportedType: Bool
335+
switch kind {
336+
case .struct, .class, .optional, .existential,
337+
.existentialMetatype, .tuple, .enum:
338+
supportedType = true
339+
default:
340+
supportedType = false
341+
}
342+
if !supportedType || !field.isStrong {
343+
if !ignoreUnknown { return false }
344+
continue;
345+
}
346+
func keyPathType<Leaf>(for: Leaf.Type) -> PartialKeyPath<Root>.Type {
347+
if field.isVar { return WritableKeyPath<Root, Leaf>.self }
348+
return KeyPath<Root, Leaf>.self
349+
}
350+
let resultSize = MemoryLayout<Int32>.size + MemoryLayout<Int>.size
351+
let partialKeyPath = _openExistential(childType, do: keyPathType)
352+
._create(capacityInBytes: resultSize) {
353+
var destBuilder = KeyPathBuffer.Builder($0)
354+
destBuilder.pushHeader(KeyPathBuffer.Header(
355+
size: resultSize - MemoryLayout<Int>.size,
356+
trivial: true,
357+
hasReferencePrefix: false
358+
))
359+
let component = RawKeyPathComponent(
360+
header: RawKeyPathComponent.Header(stored: .struct,
361+
mutable: field.isVar,
362+
inlineOffset: UInt32(offset)),
363+
body: UnsafeRawBufferPointer(start: nil, count: 0))
364+
component.clone(
365+
into: &destBuilder.buffer,
366+
endOfReferencePrefix: false)
367+
}
290368

291-
if !body(nameC!, offset, childType, kind) {
369+
if !body(field.name!, partialKeyPath) {
292370
return false
293371
}
294372
}

stdlib/public/runtime/ReflectionMirror.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "swift/Runtime/Portability.h"
2424
#include "Private.h"
2525
#include "WeakReference.h"
26+
#include "../SwiftShims/Reflection.h"
2627
#include <cassert>
2728
#include <cinttypes>
2829
#include <cstdio>
@@ -82,6 +83,7 @@ namespace {
8283
class FieldType {
8384
const Metadata *type;
8485
bool indirect;
86+
bool var = false;
8587
TypeReferenceOwnership referenceOwnership;
8688
public:
8789

@@ -97,6 +99,8 @@ class FieldType {
9799
const TypeReferenceOwnership getReferenceOwnership() const { return referenceOwnership; }
98100
bool isIndirect() const { return indirect; }
99101
void setIndirect(bool value) { indirect = value; }
102+
bool isVar() const { return var; }
103+
void setIsVar(bool value) { var = value; }
100104
void setReferenceOwnership(TypeReferenceOwnership newOwnership) {
101105
referenceOwnership = newOwnership;
102106
}
@@ -292,7 +296,10 @@ struct TupleImpl : ReflectionMirrorImpl {
292296
// Get the nth element.
293297
auto &elt = Tuple->getElement(i);
294298

295-
return FieldType(elt.Type);
299+
FieldType result(elt.Type);
300+
// All tuples are mutable.
301+
result.setIsVar(true);
302+
return result;
296303
}
297304

298305
AnyReturn subscript(intptr_t i, const char **outName,
@@ -431,6 +438,7 @@ getFieldAt(const Metadata *base, unsigned index) {
431438
auto fieldType = FieldType(typeInfo.getMetadata());
432439
fieldType.setIndirect(field.isIndirectCase());
433440
fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership());
441+
fieldType.setIsVar(field.isVar());
434442
return {name, fieldType};
435443
}
436444

@@ -993,17 +1001,20 @@ intptr_t swift_reflectionMirror_recursiveCount(const Metadata *type) {
9931001
// func _getChildMetadata(
9941002
// type: Any.Type,
9951003
// index: Int,
996-
// outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
997-
// outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
1004+
// fieldMetadata: UnsafeMutablePointer<_FieldReflectionMetadata>
9981005
// ) -> Any.Type
9991006
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
10001007
const Metadata *swift_reflectionMirror_recursiveChildMetadata(
10011008
const Metadata *type,
10021009
intptr_t index,
1003-
const char **outName,
1004-
void (**outFreeFunc)(const char *)) {
1010+
_FieldReflectionMetadata* field) {
10051011
return call(nullptr, type, type, [&](ReflectionMirrorImpl *impl) {
1006-
return impl->recursiveChildMetadata(index, outName, outFreeFunc).getType();
1012+
FieldType fieldInfo = impl->recursiveChildMetadata(index, &field->name,
1013+
&field->freeFunc);
1014+
1015+
field->isStrong = fieldInfo.getReferenceOwnership().isStrong();
1016+
field->isVar = fieldInfo.isVar();
1017+
return fieldInfo.getType();
10071018
});
10081019
}
10091020

0 commit comments

Comments
 (0)