From 3aa05d7286f7e7f42dba1e4f226e72a60a0c7814 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Tue, 14 Jul 2020 16:31:32 -0500 Subject: [PATCH 1/2] [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 3af1deb447d1d809a1c95a064d7273e9aaa250cf) --- stdlib/public/core/ReflectionMirror.swift | 129 +++++++- stdlib/public/runtime/ReflectionMirror.mm | 253 ++++++++++++-- test/stdlib/ForEachField.swift | 380 ++++++++++++++++++++++ 3 files changed, 741 insertions(+), 21 deletions(-) create mode 100644 test/stdlib/ForEachField.swift diff --git a/stdlib/public/core/ReflectionMirror.swift b/stdlib/public/core/ReflectionMirror.swift index ead2c32caacbc..ab764df9148fd 100644 --- a/stdlib/public/core/ReflectionMirror.swift +++ b/stdlib/public/core/ReflectionMirror.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// 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 @@ -10,12 +10,35 @@ // //===----------------------------------------------------------------------===// +@_silgen_name("swift_isClassType") +internal func _isClassType(_: Any.Type) -> Bool + +@_silgen_name("swift_getMetadataKind") +internal func _metadataKind(_: Any.Type) -> UInt + @_silgen_name("swift_reflectionMirror_normalizedType") internal func _getNormalizedType(_: T, type: Any.Type) -> Any.Type @_silgen_name("swift_reflectionMirror_count") internal func _getChildCount(_: T, type: Any.Type) -> Int +@_silgen_name("swift_reflectionMirror_recursiveCount") +internal func _getRecursiveChildCount(_: Any.Type) -> Int + +@_silgen_name("swift_reflectionMirror_recursiveChildMetadata") +internal func _getChildMetadata( + _: Any.Type, + index: Int, + outName: UnsafeMutablePointer?>, + outFreeFunc: UnsafeMutablePointer +) -> Any.Type + +@_silgen_name("swift_reflectionMirror_recursiveChildOffset") +internal func _getChildOffset( + _: Any.Type, + index: Int +) -> Int + internal typealias NameFreeFunc = @convention(c) (UnsafePointer?) -> Void @_silgen_name("swift_reflectionMirror_subscript") @@ -160,3 +183,107 @@ extension Mirror { #endif } } + +/// Options for calling `_forEachField(of:options:body:)`. +@available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *) +@_spi(Reflection) +public struct _EachFieldOptions: OptionSet { + public var rawValue: UInt32 + + public init(rawValue: UInt32) { + self.rawValue = rawValue + } + + /// Require the top-level type to be a class. + /// + /// If this is not set, the top-level type is required to be a struct or + /// tuple. + public static var classType = _EachFieldOptions(rawValue: 1 << 0) + + /// Ignore fields that can't be introspected. + /// + /// If not set, the presence of things that can't be introspected causes + /// the function to immediately return `false`. + public static var ignoreUnknown = _EachFieldOptions(rawValue: 1 << 1) +} + +/// The metadata "kind" for a type. +@available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *) +@_spi(Reflection) +public enum _MetadataKind: UInt { + // With "flags": + // runtimePrivate = 0x100 + // nonHeap = 0x200 + // nonType = 0x400 + + case `class` = 0 + case `struct` = 0x200 // 0 | nonHeap + case `enum` = 0x201 // 1 | nonHeap + case optional = 0x202 // 2 | nonHeap + case foreignClass = 0x203 // 3 | nonHeap + case opaque = 0x300 // 0 | runtimePrivate | nonHeap + case tuple = 0x301 // 1 | runtimePrivate | nonHeap + case function = 0x302 // 2 | runtimePrivate | nonHeap + case existential = 0x303 // 3 | runtimePrivate | nonHeap + case metatype = 0x304 // 4 | runtimePrivate | nonHeap + case objcClassWrapper = 0x305 // 5 | runtimePrivate | nonHeap + case existentialMetatype = 0x306 // 6 | runtimePrivate | nonHeap + case heapLocalVariable = 0x400 // 0 | nonType + case heapGenericLocalVariable = 0x500 // 0 | nonType | runtimePrivate + case errorObject = 0x501 // 1 | nonType | runtimePrivate + case unknown = 0xffff + + init(_ type: Any.Type) { + let v = _metadataKind(type) + if let result = _MetadataKind(rawValue: v) { + self = result + } else { + self = .unknown + } + } +} + +/// 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 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *) +@discardableResult +@_spi(Reflection) +public func _forEachField( + of type: Any.Type, + options: _EachFieldOptions = [], + body: (UnsafePointer, Int, Any.Type, _MetadataKind) -> Bool +) -> Bool { + // Require class type iff `.classType` is included as an option + if _isClassType(type) != options.contains(.classType) { + return false + } + + let childCount = _getRecursiveChildCount(type) + for i in 0..? = nil + var freeFunc: NameFreeFunc? = nil + let childType = _getChildMetadata( + type, index: i, outName: &nameC, outFreeFunc: &freeFunc) + defer { freeFunc?(nameC) } + let kind = _MetadataKind(childType) + + if !body(nameC!, offset, childType, kind) { + return false + } + } + + return true +} diff --git a/stdlib/public/runtime/ReflectionMirror.mm b/stdlib/public/runtime/ReflectionMirror.mm index f6f8f5833abdb..7bca2d635f2fb 100644 --- a/stdlib/public/runtime/ReflectionMirror.mm +++ b/stdlib/public/runtime/ReflectionMirror.mm @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// 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 @@ -213,6 +213,10 @@ static AnyReturn copyFieldContents(OpaqueValue *fieldData, virtual char displayStyle() = 0; virtual intptr_t count() = 0; + virtual intptr_t childOffset(intptr_t index) = 0; + virtual const FieldType childMetadata(intptr_t index, + const char **outName, + void (**outFreeFunc)(const char *)) = 0; virtual AnyReturn subscript(intptr_t index, const char **outName, void (**outFreeFunc)(const char *)) = 0; virtual const char *enumCaseName() { return nullptr; } @@ -221,6 +225,22 @@ virtual AnyReturn subscript(intptr_t index, const char **outName, virtual id quickLookObject() { return nil; } #endif + // For class types, traverse through superclasses when providing field + // information. The base implementations call through to their local-only + // counterparts. + virtual intptr_t recursiveCount() { + return count(); + } + virtual intptr_t recursiveChildOffset(intptr_t index) { + return childOffset(index); + } + virtual const FieldType recursiveChildMetadata(intptr_t index, + const char **outName, + void (**outFreeFunc)(const char *)) + { + return childMetadata(index, outName, outFreeFunc); + } + virtual ~ReflectionMirrorImpl() {} }; @@ -236,8 +256,19 @@ intptr_t count() { return Tuple->NumElements; } - AnyReturn subscript(intptr_t i, const char **outName, - void (**outFreeFunc)(const char *)) { + intptr_t childOffset(intptr_t i) { + auto *Tuple = static_cast(type); + + if (i < 0 || (size_t)i > Tuple->NumElements) + swift::crash("Swift mirror subscript bounds check failure"); + + // Get the nth element. + auto &elt = Tuple->getElement(i); + return elt.Offset; + } + + const FieldType childMetadata(intptr_t i, const char **outName, + void (**outFreeFunc)(const char *)) { auto *Tuple = static_cast(type); if (i < 0 || (size_t)i > Tuple->NumElements) @@ -270,12 +301,21 @@ AnyReturn subscript(intptr_t i, const char **outName, // Get the nth element. auto &elt = Tuple->getElement(i); + + return FieldType(elt.Type); + } + + AnyReturn subscript(intptr_t i, const char **outName, + void (**outFreeFunc)(const char *)) { + auto eltOffset = childOffset(i); + auto fieldType = childMetadata(i, outName, outFreeFunc); + auto *bytes = reinterpret_cast(value); - auto *eltData = reinterpret_cast(bytes + elt.Offset); + auto *eltData = reinterpret_cast(bytes + eltOffset); Any result; - result.Type = elt.Type; + result.Type = fieldType.getType(); auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer); result.Type->vw_initializeWithCopy(opaqueValueAddr, const_cast(eltData)); @@ -409,17 +449,19 @@ intptr_t count() { auto *Struct = static_cast(type); return Struct->getDescription()->NumFields; } - - AnyReturn subscript(intptr_t i, const char **outName, - void (**outFreeFunc)(const char *)) { + + intptr_t childOffset(intptr_t i) { auto *Struct = static_cast(type); if (i < 0 || (size_t)i > Struct->getDescription()->NumFields) swift::crash("Swift mirror subscript bounds check failure"); // Load the offset from its respective vector. - auto fieldOffset = Struct->getFieldOffsets()[i]; + return Struct->getFieldOffsets()[i]; + } + const FieldType childMetadata(intptr_t i, const char **outName, + void (**outFreeFunc)(const char *)) { StringRef name; FieldType fieldInfo; std::tie(name, fieldInfo) = getFieldAt(type, i); @@ -428,9 +470,17 @@ AnyReturn subscript(intptr_t i, const char **outName, *outName = name.data(); *outFreeFunc = nullptr; + return fieldInfo; + } + + AnyReturn subscript(intptr_t i, const char **outName, + void (**outFreeFunc)(const char *)) { + auto fieldInfo = childMetadata(i, outName, outFreeFunc); + auto *bytes = reinterpret_cast(value); + auto fieldOffset = childOffset(i); auto *fieldData = reinterpret_cast(bytes + fieldOffset); - + return copyFieldContents(fieldData, fieldInfo); } }; @@ -475,11 +525,25 @@ intptr_t count() { return 0; } + // No fields if reflecting the enumeration type instead of a case + if (!value) { + return 0; + } + const Metadata *payloadType; getInfo(nullptr, &payloadType, nullptr); return (payloadType != nullptr) ? 1 : 0; } + intptr_t childOffset(intptr_t i) { + return 0; + } + + const FieldType childMetadata(intptr_t i, const char **outName, + void (**outFreeFunc)(const char *)) { + return FieldType(); + } + AnyReturn subscript(intptr_t i, const char **outName, void (**outFreeFunc)(const char *)) { unsigned tag; @@ -547,21 +611,54 @@ char displayStyle() { return 'c'; } + bool hasSuperclassMirror() { + auto *Clas = static_cast(type); + auto description = Clas->getDescription(); + + return ((description->SuperclassType) + && (Clas->Superclass) + && (Clas->Superclass->isTypeMetadata())); + } + + ClassImpl superclassMirror() { + auto *Clas = static_cast(type); + auto description = Clas->getDescription(); + + if (description->SuperclassType) { + if (auto theSuperclass = Clas->Superclass) { + auto impl = ClassImpl(); + impl.type = (Metadata *)theSuperclass; + impl.value = nullptr; + return impl; + } + } + swift::crash("No superclass mirror found"); + } + intptr_t count() { if (!isReflectable()) return 0; auto *Clas = static_cast(type); - auto count = Clas->getDescription()->NumFields; + auto description = Clas->getDescription(); + auto count = description->NumFields; return count; } - - AnyReturn subscript(intptr_t i, const char **outName, - void (**outFreeFunc)(const char *)) { + + intptr_t recursiveCount() { + if (hasSuperclassMirror()) { + return superclassMirror().recursiveCount() + count(); + } + + return count(); + } + + intptr_t childOffset(intptr_t i) { auto *Clas = static_cast(type); + auto description = Clas->getDescription(); - if (i < 0 || (size_t)i > Clas->getDescription()->NumFields) + if (i < 0 || (size_t)i > description->NumFields) swift::crash("Swift mirror subscript bounds check failure"); // FIXME: If the class has ObjC heritage, get the field offset using the ObjC @@ -579,18 +676,63 @@ AnyReturn subscript(intptr_t i, const char **outName, swift::crash("Object appears to be Objective-C, but no runtime."); #endif } + return (intptr_t)fieldOffset; + } + intptr_t recursiveChildOffset(intptr_t i) { + if (hasSuperclassMirror()) { + auto superMirror = superclassMirror(); + auto superclassFieldCount = superMirror.recursiveCount(); + + if (i < superclassFieldCount) { + return superMirror.recursiveChildOffset(i); + } else { + i -= superclassFieldCount; + } + } + + return childOffset(i); + } + + const FieldType childMetadata(intptr_t i, const char **outName, + void (**outFreeFunc)(const char *)) { StringRef name; FieldType fieldInfo; std::tie(name, fieldInfo) = getFieldAt(type, i); assert(!fieldInfo.isIndirect() && "class indirect properties not implemented"); - - auto *bytes = *reinterpret_cast(value); - auto *fieldData = reinterpret_cast(bytes + fieldOffset); *outName = name.data(); *outFreeFunc = nullptr; - + + return fieldInfo; + } + + const FieldType recursiveChildMetadata(intptr_t i, + const char **outName, + void (**outFreeFunc)(const char *)) + { + if (hasSuperclassMirror()) { + auto superMirror = superclassMirror(); + auto superclassFieldCount = superMirror.recursiveCount(); + + if (i < superclassFieldCount) { + return superMirror.recursiveChildMetadata(i, outName, outFreeFunc); + } else { + i -= superclassFieldCount; + } + } + + return childMetadata(i, outName, outFreeFunc); + } + + AnyReturn subscript(intptr_t i, const char **outName, + void (**outFreeFunc)(const char *)) { + auto fieldInfo = childMetadata(i, outName, outFreeFunc); + + auto *bytes = *reinterpret_cast(value); + auto fieldOffset = childOffset(i); + auto *fieldData = reinterpret_cast(bytes + fieldOffset); + return copyFieldContents(fieldData, fieldInfo); } @@ -619,6 +761,15 @@ intptr_t count() { return 0; } + intptr_t childOffset(intptr_t i) { + swift::crash("Cannot get children of Objective-C objects."); + } + + const FieldType childMetadata(intptr_t i, const char **outName, + void (**outFreeFunc)(const char *)) { + swift::crash("Cannot get children of Objective-C objects."); + } + AnyReturn subscript(intptr_t i, const char **outName, void (**outFreeFunc)(const char *)) { swift::crash("Cannot get children of Objective-C objects."); @@ -636,7 +787,16 @@ char displayStyle() { intptr_t count() { return 0; } - + + intptr_t childOffset(intptr_t i) { + swift::crash("Metatypes have no children."); + } + + const FieldType childMetadata(intptr_t i, const char **outName, + void (**outFreeFunc)(const char *)) { + swift::crash("Metatypes have no children."); + } + AnyReturn subscript(intptr_t i, const char **outName, void (**outFreeFunc)(const char *)) { swift::crash("Metatypes have no children."); @@ -654,6 +814,15 @@ intptr_t count() { return 0; } + intptr_t childOffset(intptr_t i) { + swift::crash("Opaque types have no children."); + } + + const FieldType childMetadata(intptr_t i, const char **outName, + void (**outFreeFunc)(const char *)) { + swift::crash("Opaque types have no children."); + } + AnyReturn subscript(intptr_t i, const char **outName, void (**outFreeFunc)(const char *)) { swift::crash("Opaque types have no children."); @@ -786,6 +955,12 @@ auto call(OpaqueValue *passedValue, const Metadata *T, const Metadata *passedTyp return call(value, T, type, [](ReflectionMirrorImpl *impl) { return impl->type; }); } +// func _getMetadataKind(_ type: Any.Type) -> UInt +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API +uintptr_t swift_getMetadataKind(const Metadata *type) { + return static_cast(type->getKind()); +} + // func _getChildCount(_: T, type: Any.Type) -> Int SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API intptr_t swift_reflectionMirror_count(OpaqueValue *value, @@ -796,6 +971,44 @@ intptr_t swift_reflectionMirror_count(OpaqueValue *value, }); } +// func _getChildCount(_ type: Any.Type) -> Int +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API +intptr_t swift_reflectionMirror_recursiveCount(const Metadata *type) { + return call(nullptr, type, type, [](ReflectionMirrorImpl *impl) { + return impl->recursiveCount(); + }); +} + +// func _getChildMetadata( +// type: Any.Type, +// index: Int, +// outName: UnsafeMutablePointer?>, +// outFreeFunc: UnsafeMutablePointer +// ) -> 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 *)) { + return call(nullptr, type, type, [&](ReflectionMirrorImpl *impl) { + return impl->recursiveChildMetadata(index, outName, outFreeFunc).getType(); + }); +} + +// internal func _getChildOffset( +// type: Any.Type, +// index: Int +// ) -> Int +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API +intptr_t swift_reflectionMirror_recursiveChildOffset( + const Metadata *type, + intptr_t index) { + return call(nullptr, type, type, [&](ReflectionMirrorImpl *impl) { + return impl->recursiveChildOffset(index); + }); +} + // We intentionally use a non-POD return type with this entry point to give // it an indirect return ABI for compatibility with Swift. #pragma clang diagnostic push diff --git a/test/stdlib/ForEachField.swift b/test/stdlib/ForEachField.swift new file mode 100644 index 0000000000000..5078278840dd4 --- /dev/null +++ b/test/stdlib/ForEachField.swift @@ -0,0 +1,380 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// RUN: %target-run-simple-swift +// REQUIRES: executable_test + +@_spi(Reflection) import Swift +import StdlibUnittest + +struct TestStruct { + var int = 0 + var double = 0.0 + var bool = false +} + +struct GenericStruct { + var int = 0 + var first: T + var second: T +} + +enum TestEnum { + case one + case two + case three(TestStruct) +} + +class BaseClass { + var superInt = 0 + init() {} +} + +class TestClass: BaseClass { + var int = 0 + var double = 0.0 + var bool = false + override init() {} +} + +class TestSubclass: TestClass { + var strings: [String] = [] + override init() {} +} + +class GenericClass: BaseClass { + var first: T + var second: U + + init(_ t: T, _ u: U) { + self.first = t + self.second = u + } +} + +class GenericSubclass: GenericClass { + var third: W + + init(_ v: V, _ w: W) { + self.third = w + super.init(v, false) + } +} + +class OwnershipTestClass: BaseClass { + weak var test1: TestClass? + unowned var test2: TestClass + unowned(unsafe) var test3: TestClass + + init(_ t: TestClass) { + self.test1 = t + self.test2 = t + self.test3 = t + } +} + +struct SimilarToNSPoint { + var x: Double + var y: Double +} + +struct SimilarToNSSize { + var width: Double + var height: Double +} + +struct SimilarToNSRect { + var origin: SimilarToNSPoint + var size: SimilarToNSSize +} + +struct ContainsObject { + var obj: TestClass +} + +#if _runtime(_ObjC) +import Foundation + +class NSObjectSubclass: NSObject { + var point: (Double, Double) + + init(x: Double, y: Double) { + self.point = (x, y) + } +} + +class EmptyNSObject: NSObject {} +#endif + +@available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *) +func checkFields( + of type: T.Type, + options: _EachFieldOptions = [], + fields: [String: (Int, Any.Type)] +) { + var count = 0 + + _forEachField(of: T.self, options: options) { + charPtr, offset, type, kind in + count += 1 + + let fieldName = String(cString: charPtr) + guard let (checkOffset, checkType) = fields[fieldName] else { + expectTrue(false, "Unexpected field '\(fieldName)'") + return true + } + + expectEqual(checkOffset, offset) + expectEqual(checkType, type) + return true + } + + expectEqual(fields.count, count) +} + +protocol ExistentialProtocol {} + +extension TestStruct: ExistentialProtocol {} +extension GenericStruct: ExistentialProtocol {} +extension GenericSubclass: ExistentialProtocol {} + +@available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *) +extension ExistentialProtocol { + static func doCheckFields( + options: _EachFieldOptions = [], + fields: [String: (Int, Any.Type)] + ) { + checkFields(of: Self.self, options: options, fields: fields) + } +} + +@available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *) +func checkFieldsAsExistential( + of type: ExistentialProtocol.Type, + options: _EachFieldOptions = [], + fields: [String: (Int, Any.Type)] +) { + type.doCheckFields(options: options, fields: fields) +} + +@available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *) +func _withTypeEncodingCallback(encoding: inout String, name: UnsafePointer, offset: Int, type: Any.Type, kind: _MetadataKind) -> Bool { + if type == Bool.self { + encoding += "B" + return true + } else if type == Int.self { + if MemoryLayout.size == MemoryLayout.size { + encoding += "q" + } else if MemoryLayout.size == MemoryLayout.size { + encoding += "l" + } else { + return false + } + return true + } else if type == Double.self { + encoding += "d" + return true + } + + switch kind { + case .struct: + encoding += "{" + defer { encoding += "}" } + _forEachField(of: type) { name, offset, type, kind in + _withTypeEncodingCallback(encoding: &encoding, name: name, offset: offset, type: type, kind: kind) + } + case .class: + encoding += "@" + default: + break + } + return true +} + +@available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *) +func getTypeEncoding(_ type: T.Type) -> String? { + var encoding = "" + _ = _forEachField(of: type) { name, offset, type, kind in + _withTypeEncodingCallback(encoding: &encoding, name: name, offset: offset, type: type, kind: kind) + } + return "{\(encoding)}" +} + +//===----------------------------------------------------------------------===// + +var tests = TestSuite("ForEachField") + +if #available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *) { + + tests.test("TestTuple") { + checkFields( + of: (Int, Bool).self, + fields: [".0": (0, Int.self), ".1": (MemoryLayout.stride, Bool.self)]) + + checkFields( + of: (a: Int, b: Bool).self, + fields: ["a": (0, Int.self), "b": (MemoryLayout.stride, Bool.self)]) + } + + tests.test("TestEnum") { + checkFields(of: TestEnum.self, fields: [:]) + } + + tests.test("TestStruct") { + checkFields( + of: TestStruct.self, + fields: [ + "int": (0, Int.self), + "double": (MemoryLayout.stride, Double.self), + "bool": (MemoryLayout.stride * 2, Bool.self), + ]) + + checkFieldsAsExistential( + of: TestStruct.self, + fields: [ + "int": (0, Int.self), + "double": (MemoryLayout.stride, Double.self), + "bool": (MemoryLayout.stride * 2, Bool.self), + ]) + + // Applying to struct type with .classType option fails + expectFalse(_forEachField(of: TestStruct.self, options: .classType) { + _, _, _, _ in true + }) + } + + func checkGenericStruct(_: T.Type) { + let firstOffset = max(MemoryLayout.stride, MemoryLayout.alignment) + + checkFields( + of: GenericStruct.self, + fields: [ + "int": (0, Int.self), + "first": (firstOffset, T.self), + "second": (firstOffset + MemoryLayout.stride, T.self), + ]) + + checkFieldsAsExistential( + of: GenericStruct.self, + fields: [ + "int": (0, Int.self), + "first": (firstOffset, T.self), + "second": (firstOffset + MemoryLayout.stride, T.self), + ]) + } + + tests.test("GenericStruct") { + checkGenericStruct(Bool.self) + checkGenericStruct(TestStruct.self) + checkGenericStruct((TestStruct, TestClass, Int, Int).self) + } + + tests.test("TestClass") { + let classOffset = MemoryLayout.stride * 2 + let doubleOffset = classOffset + + max(MemoryLayout.stride * 2, MemoryLayout.stride) + + checkFields( + of: TestClass.self, options: .classType, + fields: [ + "superInt": (classOffset, Int.self), + "int": (classOffset + MemoryLayout.stride, Int.self), + "double": (doubleOffset, Double.self), + "bool": (doubleOffset + MemoryLayout.stride, Bool.self), + ]) + + checkFields( + of: TestSubclass.self, options: .classType, + fields: [ + "superInt": (classOffset, Int.self), + "int": (classOffset + MemoryLayout.stride, Int.self), + "double": (doubleOffset, Double.self), + "bool": (doubleOffset + MemoryLayout.stride, Bool.self), + "strings": (doubleOffset + MemoryLayout.stride + MemoryLayout>.stride, Array.self), + ]) + + let firstOffset = classOffset + + max(MemoryLayout.stride, MemoryLayout.alignment) + checkFields( + of: GenericSubclass.self, options: .classType, + fields: [ + "superInt": (classOffset, Int.self), + "first": (firstOffset, TestStruct.self), + "second": (firstOffset + MemoryLayout.size, Bool.self), + "third": (firstOffset + MemoryLayout.stride, TestStruct.self), + ]) + + checkFields( + of: GenericSubclass.self, options: .classType, + fields: [ + "superInt": (classOffset, Int.self), + "first": (classOffset + MemoryLayout.stride, Int.self), + "second": (classOffset + MemoryLayout.stride * 2, Bool.self), + "third": (0, Never.self), + ]) + + checkFieldsAsExistential( + of: GenericSubclass.self, options: .classType, + fields: [ + "superInt": (classOffset, Int.self), + "first": (firstOffset, TestStruct.self), + "second": (firstOffset + MemoryLayout.size, Bool.self), + "third": (firstOffset + MemoryLayout.stride, TestStruct.self), + ]) + + // Applying to class type without .classType option fails + expectFalse(_forEachField(of: TestClass.self) { + _, _, _, _ in true + }) + } + + tests.test("OwnershipTestClass") { + let classOffset = MemoryLayout.stride * 2 + + checkFields( + of: OwnershipTestClass.self, options: .classType, + fields: [ + "superInt": (classOffset, Int.self), + "test1": (classOffset + MemoryLayout.stride, Optional.self), + "test2": (classOffset + MemoryLayout.stride * 2, TestClass.self), + "test3": (classOffset + MemoryLayout.stride * 3, TestClass.self), + ]) + } + + #if _runtime(_ObjC) + tests.test("NSObjectSubclass") { + expectTrue(_forEachField(of: NSObjectSubclass.self, options: .classType) { + charPtr, _, type, _ in + + let fieldName = String(cString: charPtr) + return type == (Double, Double).self + && fieldName == "point" + }) + + expectTrue(_forEachField(of: EmptyNSObject.self, options: .classType) { + _, _, _, _ in true + }) + } + #endif + + tests.test("withTypeEncoding") { + expectEqual("{@}", getTypeEncoding(ContainsObject.self)) + expectEqual("{{dd}{dd}}", getTypeEncoding(SimilarToNSRect.self)) + + let testEncoding = getTypeEncoding(TestStruct.self) + expectTrue("{qdB}" == testEncoding || "{ldB}" == testEncoding) + } + + runAllTests() +} else { + runNoTests() +} From fbf9ddaa341f9f989250819a013e8e980db78539 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 14 Jul 2020 20:01:12 -0700 Subject: [PATCH 2/2] runtime: correct the `asprintf` shim for Windows Ensure that the string that is formed from the `asprintf` call is null-terminated. The `_vsnprintf` call will not null-terminate the string if the length is not sufficient. (cherry picked from commit 37ee73cda9c99cf069f6de7a9ef1b850e204c9c7) --- stdlib/public/runtime/ReflectionMirror.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/public/runtime/ReflectionMirror.mm b/stdlib/public/runtime/ReflectionMirror.mm index 7bca2d635f2fb..28fcce3dbeeae 100644 --- a/stdlib/public/runtime/ReflectionMirror.mm +++ b/stdlib/public/runtime/ReflectionMirror.mm @@ -57,6 +57,7 @@ int asprintf(char **strp, const char *fmt, ...) { return -1; length = _vsnprintf(*strp, length, fmt, argp1); + (*strp)[length] = '\0'; va_end(argp0); va_end(argp1);