diff --git a/include/swift/Reflection/Records.h b/include/swift/Reflection/Records.h index c43ade467c6b0..80478ccd3aea2 100644 --- a/include/swift/Reflection/Records.h +++ b/include/swift/Reflection/Records.h @@ -92,6 +92,10 @@ class FieldRecord { bool isIndirectCase() const { return Flags.isIndirectCase(); } + + bool isVar() const { + return Flags.isVar(); + } }; struct FieldRecordIterator { diff --git a/stdlib/public/SwiftShims/CMakeLists.txt b/stdlib/public/SwiftShims/CMakeLists.txt index 33c49a3f8861e..926020372fb2e 100644 --- a/stdlib/public/SwiftShims/CMakeLists.txt +++ b/stdlib/public/SwiftShims/CMakeLists.txt @@ -10,6 +10,7 @@ set(sources MetadataSections.h Random.h RefCount.h + Reflection.h RuntimeShims.h RuntimeStubs.h SwiftStdbool.h diff --git a/stdlib/public/SwiftShims/Reflection.h b/stdlib/public/SwiftShims/Reflection.h new file mode 100644 index 0000000000000..143a630412f6a --- /dev/null +++ b/stdlib/public/SwiftShims/Reflection.h @@ -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 diff --git a/stdlib/public/SwiftShims/module.modulemap b/stdlib/public/SwiftShims/module.modulemap index 8ba9b93a8f9af..de62f9c36e91c 100644 --- a/stdlib/public/SwiftShims/module.modulemap +++ b/stdlib/public/SwiftShims/module.modulemap @@ -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" diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift index 19aed078f5217..adaef849dfacd 100644 --- a/stdlib/public/core/KeyPath.swift +++ b/stdlib/public/core/KeyPath.swift @@ -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(_ value: T) { + let buf = pushRaw(size: MemoryLayout.size, + alignment: MemoryLayout.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 @@ -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(_ value: T) { - let buf = pushRaw(size: MemoryLayout.size, - alignment: MemoryLayout.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.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 @@ -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 } } @@ -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") } diff --git a/stdlib/public/core/ReflectionMirror.swift b/stdlib/public/core/ReflectionMirror.swift index 8ed481582570d..84261c76d8065 100644 --- a/stdlib/public/core/ReflectionMirror.swift +++ b/stdlib/public/core/ReflectionMirror.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import SwiftShims + @_silgen_name("swift_isClassType") internal func _isClassType(_: Any.Type) -> Bool @@ -29,8 +31,7 @@ internal func _getRecursiveChildCount(_: Any.Type) -> Int internal func _getChildMetadata( _: Any.Type, index: Int, - outName: UnsafeMutablePointer?>, - outFreeFunc: UnsafeMutablePointer + fieldMetadata: UnsafeMutablePointer<_FieldReflectionMetadata> ) -> Any.Type @_silgen_name("swift_reflectionMirror_recursiveChildOffset") @@ -281,14 +282,91 @@ public func _forEachField( for i in 0..? = 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( + of type: Root.Type, + options: _EachFieldOptions = [], + body: (UnsafePointer, PartialKeyPath) -> 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..(for: Leaf.Type) -> PartialKeyPath.Type { + if field.isVar { return WritableKeyPath.self } + return KeyPath.self + } + let resultSize = MemoryLayout.size + MemoryLayout.size + let partialKeyPath = _openExistential(childType, do: keyPathType) + ._create(capacityInBytes: resultSize) { + var destBuilder = KeyPathBuffer.Builder($0) + destBuilder.pushHeader(KeyPathBuffer.Header( + size: resultSize - MemoryLayout.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 } } diff --git a/stdlib/public/runtime/ReflectionMirror.cpp b/stdlib/public/runtime/ReflectionMirror.cpp index ef6e746064ec2..d8dc1e1292166 100644 --- a/stdlib/public/runtime/ReflectionMirror.cpp +++ b/stdlib/public/runtime/ReflectionMirror.cpp @@ -23,6 +23,7 @@ #include "swift/Runtime/Portability.h" #include "Private.h" #include "WeakReference.h" +#include "../SwiftShims/Reflection.h" #include #include #include @@ -82,6 +83,7 @@ namespace { class FieldType { const Metadata *type; bool indirect; + bool var = false; TypeReferenceOwnership referenceOwnership; public: @@ -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; } @@ -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, @@ -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}; } @@ -993,17 +1001,20 @@ intptr_t swift_reflectionMirror_recursiveCount(const Metadata *type) { // func _getChildMetadata( // type: Any.Type, // index: Int, -// outName: UnsafeMutablePointer?>, -// outFreeFunc: UnsafeMutablePointer +// 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(); }); } diff --git a/test/stdlib/ForEachField.swift b/test/stdlib/ForEachField.swift index 5078278840dd4..e80375fafbd8b 100644 --- a/test/stdlib/ForEachField.swift +++ b/test/stdlib/ForEachField.swift @@ -101,6 +101,27 @@ struct ContainsObject { var obj: TestClass } +struct LetKeyPaths { + let int : Int + let double: Double +} + +protocol TestExisential {} + +struct KeyPathTypes { + weak var weakObj: TestClass? + unowned var unownedObj: TestClass + var obj: TestClass + var tuple: (Int, Int, Int) + var structField: Int + var function: (Int) -> (Int) + var optionalFunction: (Int) -> (Int)? + var enumField: TestEnum + var existential: TestExisential + var existentialMetatype: Any.Type + var metatype: Int.Type +} + #if _runtime(_ObjC) import Foundation @@ -141,6 +162,31 @@ func checkFields( expectEqual(fields.count, count) } +@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) +func checkFieldsWithKeyPath( + of type: T.Type, + options: _EachFieldOptions = [], + fields: [String: PartialKeyPath] +) { + var count = 0 + + _forEachFieldWithKeyPath(of: T.self, options: options) { + charPtr, keyPath in + count += 1 + + let fieldName = String(cString: charPtr) + guard let checkKeyPath = fields[fieldName] else { + expectTrue(false, "Unexpected field '\(fieldName)'") + return true + } + + expectTrue(checkKeyPath == keyPath) + return true + } + + expectEqual(fields.count, count) +} + protocol ExistentialProtocol {} extension TestStruct: ExistentialProtocol {} @@ -252,6 +298,53 @@ if #available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *) { }) } + if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { + tests.test("StructKeyPath") { + checkFieldsWithKeyPath( + of: TestStruct.self, + fields: [ + "int": \TestStruct.int, + "double": \TestStruct.double, + "bool": \TestStruct.bool, + ]) + } + + tests.test("LetKeyPaths") { + checkFieldsWithKeyPath( + of: LetKeyPaths.self, + fields: [ + "int": \LetKeyPaths.int, + "double": \LetKeyPaths.double, + ]) + } + + tests.test("KeyPathTypes") { + checkFieldsWithKeyPath( + of: KeyPathTypes.self, + options: .ignoreUnknown, + fields: [ + "obj": \KeyPathTypes.obj, + "tuple": \KeyPathTypes.tuple, + "structField": \KeyPathTypes.structField, + "enumField": \KeyPathTypes.enumField, + "existential": \KeyPathTypes.existential, + "existentialMetatype": \KeyPathTypes.existentialMetatype, + ]) + } + + tests.test("TupleKeyPath") { + typealias TestTuple = (Int, Int, TestClass, TestStruct) + checkFieldsWithKeyPath( + of: TestTuple.self, + fields: [ + ".0": \TestTuple.0, + ".1": \TestTuple.1, + ".2": \TestTuple.2, + ".3": \TestTuple.3, + ]) + } + } + func checkGenericStruct(_: T.Type) { let firstOffset = max(MemoryLayout.stride, MemoryLayout.alignment)