From a60d354ef53b032b1fb1d316179f0f2956ed616c Mon Sep 17 00:00:00 2001 From: Julian Lettner Date: Mon, 20 Feb 2023 14:18:31 -0800 Subject: [PATCH] [SUA] Add test for allocation of classes without metadata (#63759) Add test for allocating classes with pruned metadata and refactor `computeMallocTypeSummary()` to make it easier to understand: * Use early returns for error (metadata absent) conditions * Remove reliance on implicit dependency---having a type descriptor currently implies that there is also class metadata---in case this ever changes Co-authored-by: Julian Lettner --- stdlib/public/runtime/HeapObject.cpp | 46 ++++----- test/stdlib/MetadataPruning.swift | 133 +++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 27 deletions(-) create mode 100644 test/stdlib/MetadataPruning.swift diff --git a/stdlib/public/runtime/HeapObject.cpp b/stdlib/public/runtime/HeapObject.cpp index a80bcd56556..9995b193f46 100644 --- a/stdlib/public/runtime/HeapObject.cpp +++ b/stdlib/public/runtime/HeapObject.cpp @@ -125,41 +125,33 @@ computeMallocTypeSummary(const HeapMetadata *heapMetadata) { auto *classMetadata = heapMetadata->getClassObject(); auto *typeDesc = heapMetadata->getTypeContextDescriptor(); - malloc_type_summary_t summary = {}; + // Pruned metadata or unclassified + if (!classMetadata || !typeDesc) + return {.type_kind = MALLOC_TYPE_KIND_SWIFT}; // Objc - if (classMetadata && classMetadata->isPureObjC()) { - summary.type_kind = MALLOC_TYPE_KIND_OBJC; - return summary; - } + if (classMetadata->isPureObjC()) + return {.type_kind = MALLOC_TYPE_KIND_OBJC}; - // Runtime internal and unclassified - if (!typeDesc) { - summary.type_kind = MALLOC_TYPE_KIND_CXX; - return summary; - } + malloc_type_summary_t summary = {.type_kind = MALLOC_TYPE_KIND_SWIFT}; + summary.layout_semantics.reference_count = + (classMetadata->getFlags() & ClassFlags::UsesSwiftRefcounting); - // Swift - summary.type_kind = MALLOC_TYPE_KIND_SWIFT; + auto *fieldDesc = typeDesc->Fields.get(); + if (!fieldDesc) + return summary; bool isGenericData = true; - if (auto *fieldDesc = typeDesc->Fields.get()) { - for (auto &field : *fieldDesc) { - if (field.isIndirectCase()) { - isGenericData = false; - if (field.isVar()) - summary.layout_semantics.data_pointer = true; - else - summary.layout_semantics.immutable_pointer = true; - } + for (auto &field : *fieldDesc) { + if (field.isIndirectCase()) { + isGenericData = false; + if (field.isVar()) + summary.layout_semantics.data_pointer = true; + else + summary.layout_semantics.immutable_pointer = true; } } - - if (classMetadata->Flags & ClassFlags::UsesSwiftRefcounting) { - summary.layout_semantics.reference_count = true; - } else { - summary.layout_semantics.generic_data = isGenericData; - } + summary.layout_semantics.generic_data = isGenericData; return summary; diff --git a/test/stdlib/MetadataPruning.swift b/test/stdlib/MetadataPruning.swift new file mode 100644 index 00000000000..e88173421f8 --- /dev/null +++ b/test/stdlib/MetadataPruning.swift @@ -0,0 +1,133 @@ +// RUN: %target-run-simple-swift +// RUN: %target-run-simple-swift(-Xfrontend -disable-reflection-metadata) +// RUN: %target-run-simple-swift(-Xfrontend -reflection-metadata-for-debugger-only) +// RUN: %target-run-simple-swift(-Xfrontend -disable-reflection-names) +// +// REQUIRES: executable_test + +import StdlibUnittest + +var tests = TestSuite("MetadataPruning") + +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 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 + + +func printAddress(_ obj: AnyObject) { + print("\(obj) address: \(Unmanaged.passUnretained(obj).toOpaque())") +} + +tests.test("Allocate types without metadata") { + let testStruct = TestStruct() + let genericStruct = GenericStruct(first: 1.3, second: 3.7) + let testEnum = TestEnum.three(testStruct) + let baseClass = BaseClass() + let testClass = TestClass() + let testSubclass = TestSubclass() + let genericClass = GenericClass(5, "bla") + let genericSubclass = GenericSubclass(1.1, testClass) + let ownershipTestClass = OwnershipTestClass(testClass) + let containsObject = ContainsObject(obj: testClass) + + print("\(testStruct)") + print("\(genericStruct)") + print("\(testEnum)") + printAddress(baseClass) + printAddress(testClass) + printAddress(testSubclass) + printAddress(genericClass) + printAddress(genericSubclass) + printAddress(ownershipTestClass) + print("\(containsObject)") + +#if _runtime(_ObjC) + let nsObjectSubclass = NSObjectSubclass(x: 1.2, y: 3.4) + let emptyNSObject = EmptyNSObject() + + printAddress(nsObjectSubclass) + printAddress(emptyNSObject) +#endif +} + +runAllTests()