Skip to content

Add a field reflection function that constructs keypaths. #34815

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/Reflection/Records.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ class FieldRecord {
bool isIndirectCase() const {
return Flags.isIndirectCase();
}

bool isVar() const {
return Flags.isVar();
}
};

struct FieldRecordIterator {
Expand Down
1 change: 1 addition & 0 deletions stdlib/public/SwiftShims/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ set(sources
MetadataSections.h
Random.h
RefCount.h
Reflection.h
RuntimeShims.h
RuntimeStubs.h
SwiftStdbool.h
Expand Down
36 changes: 36 additions & 0 deletions stdlib/public/SwiftShims/Reflection.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//===--- Reflection.h - Types for access to reflection metadata. ----------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 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
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_STDLIB_SHIMS_REFLECTION_H
#define SWIFT_STDLIB_SHIMS_REFLECTION_H

#include "SwiftStdbool.h"
#include "SwiftStdint.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef void (*NameFreeFunc)(const char*);

typedef struct _FieldReflectionMetadata {
const char* name;
NameFreeFunc freeFunc;
__swift_bool isStrong;
__swift_bool isVar;
} _FieldReflectionMetadata;

#ifdef __cplusplus
} // extern "C"
#endif

#endif // SWIFT_STDLIB_SHIMS_REFLECTION_H
1 change: 1 addition & 0 deletions stdlib/public/SwiftShims/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module SwiftShims {
header "MetadataSections.h"
header "Random.h"
header "RefCount.h"
header "Reflection.h"
header "RuntimeShims.h"
header "RuntimeStubs.h"
header "SwiftStdbool.h"
Expand Down
78 changes: 44 additions & 34 deletions stdlib/public/core/KeyPath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1739,6 +1739,40 @@ internal struct KeyPathBuffer {
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<T>(_ value: T) {
let buf = pushRaw(size: MemoryLayout<T>.size,
alignment: MemoryLayout<T>.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

Expand Down Expand Up @@ -2280,40 +2314,16 @@ internal func _appendingKeyPaths<
count: resultSize)
}

func pushRaw(size: Int, alignment: Int)
-> UnsafeMutableRawBufferPointer {
var baseAddress = destBuffer.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)
destBuffer = UnsafeMutableRawBufferPointer(
start: baseAddress + size,
count: destBuffer.count - size - misalign)
return result
}
func push<T>(_ value: T) {
let buf = pushRaw(size: MemoryLayout<T>.size,
alignment: MemoryLayout<T>.alignment)
buf.storeBytes(of: value, as: T.self)
}
var destBuilder = KeyPathBuffer.Builder(destBuffer)

// Save space for the header.
let leafIsReferenceWritable = type(of: leaf).kind == .reference
let header = KeyPathBuffer.Header(
destBuilder.pushHeader(KeyPathBuffer.Header(
size: resultSize - MemoryLayout<Int>.size,
trivial: rootBuffer.trivial && leafBuffer.trivial,
hasReferencePrefix: rootBuffer.hasReferencePrefix
|| leafIsReferenceWritable
)
push(header)
// Start the components at pointer alignment
_ = pushRaw(size: RawKeyPathComponent.Header.pointerAlignmentSkew,
alignment: 4)
))

let leafHasReferencePrefix = leafBuffer.hasReferencePrefix

Expand All @@ -2334,13 +2344,13 @@ internal func _appendingKeyPaths<
}

component.clone(
into: &destBuffer,
into: &destBuilder.buffer,
endOfReferencePrefix: endOfReferencePrefix)
// Insert our endpoint type between the root and leaf components.
if let type = type {
push(type)
destBuilder.push(type)
} else {
// Insert our endpoint type between the root and leaf components.
push(Value.self as Any.Type)
destBuilder.push(Value.self as Any.Type)
break
}
}
Expand All @@ -2350,17 +2360,17 @@ internal func _appendingKeyPaths<
let (component, type) = leafBuffer.next()

component.clone(
into: &destBuffer,
into: &destBuilder.buffer,
endOfReferencePrefix: component.header.endOfReferencePrefix)

if let type = type {
push(type)
destBuilder.push(type)
} else {
break
}
}

_internalInvariant(destBuffer.isEmpty,
_internalInvariant(destBuilder.buffer.isEmpty,
"did not fill entire result buffer")
}

Expand Down
94 changes: 86 additions & 8 deletions stdlib/public/core/ReflectionMirror.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
//
//===----------------------------------------------------------------------===//

import SwiftShims

@_silgen_name("swift_isClassType")
internal func _isClassType(_: Any.Type) -> Bool

Expand All @@ -29,8 +31,7 @@ internal func _getRecursiveChildCount(_: Any.Type) -> Int
internal func _getChildMetadata(
_: Any.Type,
index: Int,
outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
fieldMetadata: UnsafeMutablePointer<_FieldReflectionMetadata>
) -> Any.Type

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

var nameC: UnsafePointer<CChar>? = nil
var freeFunc: NameFreeFunc? = nil
let childType = _getChildMetadata(
type, index: i, outName: &nameC, outFreeFunc: &freeFunc)
defer { freeFunc?(nameC) }
var field = _FieldReflectionMetadata()
let childType = _getChildMetadata(type, index: i, fieldMetadata: &field)
defer { field.freeFunc?(field.name) }
let kind = _MetadataKind(childType)

if !body(field.name!, offset, childType, kind) {
return false
}
}

return true
}

/// Calls the given closure on every field of the specified type.
///
/// If `body` returns `false` for any field, no additional fields are visited.
///
/// - Parameters:
/// - type: The type to inspect.
/// - options: Options to use when reflecting over `type`.
/// - body: A closure to call with information about each field in `type`.
/// The parameters to `body` are a pointer to a C string holding the name
/// of the field, the offset of the field in bytes, the type of the field,
/// and the `_MetadataKind` of the field's type.
/// - Returns: `true` if every invocation of `body` returns `true`; otherwise,
/// `false`.
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
@discardableResult
@_spi(Reflection)
public func _forEachFieldWithKeyPath<Root>(
of type: Root.Type,
options: _EachFieldOptions = [],
body: (UnsafePointer<CChar>, PartialKeyPath<Root>) -> Bool
) -> Bool {
// Class types not supported because the metadata does not have
// enough information to construct computed properties.
if _isClassType(type) || options.contains(.classType) {
return false
}
let ignoreUnknown = options.contains(.ignoreUnknown)

let childCount = _getRecursiveChildCount(type)
for i in 0..<childCount {
let offset = _getChildOffset(type, index: i)

var field = _FieldReflectionMetadata()
let childType = _getChildMetadata(type, index: i, fieldMetadata: &field)
defer { field.freeFunc?(field.name) }
let kind = _MetadataKind(childType)
let supportedType: Bool
switch kind {
case .struct, .class, .optional, .existential,
.existentialMetatype, .tuple, .enum:
supportedType = true
default:
supportedType = false
}
if !supportedType || !field.isStrong {
if !ignoreUnknown { return false }
continue;
}
func keyPathType<Leaf>(for: Leaf.Type) -> PartialKeyPath<Root>.Type {
if field.isVar { return WritableKeyPath<Root, Leaf>.self }
return KeyPath<Root, Leaf>.self
}
let resultSize = MemoryLayout<Int32>.size + MemoryLayout<Int>.size
let partialKeyPath = _openExistential(childType, do: keyPathType)
._create(capacityInBytes: resultSize) {
var destBuilder = KeyPathBuffer.Builder($0)
destBuilder.pushHeader(KeyPathBuffer.Header(
size: resultSize - MemoryLayout<Int>.size,
trivial: true,
hasReferencePrefix: false
))
let component = RawKeyPathComponent(
header: RawKeyPathComponent.Header(stored: .struct,
mutable: field.isVar,
inlineOffset: UInt32(offset)),
body: UnsafeRawBufferPointer(start: nil, count: 0))
component.clone(
into: &destBuilder.buffer,
endOfReferencePrefix: false)
}

if !body(nameC!, offset, childType, kind) {
if !body(field.name!, partialKeyPath) {
return false
}
}
Expand Down
23 changes: 17 additions & 6 deletions stdlib/public/runtime/ReflectionMirror.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "swift/Runtime/Portability.h"
#include "Private.h"
#include "WeakReference.h"
#include "../SwiftShims/Reflection.h"
#include <cassert>
#include <cinttypes>
#include <cstdio>
Expand Down Expand Up @@ -82,6 +83,7 @@ namespace {
class FieldType {
const Metadata *type;
bool indirect;
bool var = false;
TypeReferenceOwnership referenceOwnership;
public:

Expand All @@ -97,6 +99,8 @@ class FieldType {
const TypeReferenceOwnership getReferenceOwnership() const { return referenceOwnership; }
bool isIndirect() const { return indirect; }
void setIndirect(bool value) { indirect = value; }
bool isVar() const { return var; }
void setIsVar(bool value) { var = value; }
void setReferenceOwnership(TypeReferenceOwnership newOwnership) {
referenceOwnership = newOwnership;
}
Expand Down Expand Up @@ -292,7 +296,10 @@ struct TupleImpl : ReflectionMirrorImpl {
// Get the nth element.
auto &elt = Tuple->getElement(i);

return FieldType(elt.Type);
FieldType result(elt.Type);
// All tuples are mutable.
result.setIsVar(true);
return result;
}

AnyReturn subscript(intptr_t i, const char **outName,
Expand Down Expand Up @@ -431,6 +438,7 @@ getFieldAt(const Metadata *base, unsigned index) {
auto fieldType = FieldType(typeInfo.getMetadata());
fieldType.setIndirect(field.isIndirectCase());
fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership());
fieldType.setIsVar(field.isVar());
return {name, fieldType};
}

Expand Down Expand Up @@ -993,17 +1001,20 @@ intptr_t swift_reflectionMirror_recursiveCount(const Metadata *type) {
// func _getChildMetadata(
// type: Any.Type,
// index: Int,
// outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
// outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
// fieldMetadata: UnsafeMutablePointer<_FieldReflectionMetadata>
// ) -> Any.Type
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
const Metadata *swift_reflectionMirror_recursiveChildMetadata(
const Metadata *type,
intptr_t index,
const char **outName,
void (**outFreeFunc)(const char *)) {
_FieldReflectionMetadata* field) {
return call(nullptr, type, type, [&](ReflectionMirrorImpl *impl) {
return impl->recursiveChildMetadata(index, outName, outFreeFunc).getType();
FieldType fieldInfo = impl->recursiveChildMetadata(index, &field->name,
&field->freeFunc);

field->isStrong = fieldInfo.getReferenceOwnership().isStrong();
field->isVar = fieldInfo.isVar();
return fieldInfo.getType();
});
}

Expand Down
Loading