[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 <julian.lettner@apple.com>
This commit is contained in:
Julian Lettner
2023-02-20 14:18:31 -08:00
committed by GitHub
parent 24d8ec529e
commit a60d354ef5
2 changed files with 152 additions and 27 deletions

View File

@@ -125,41 +125,33 @@ computeMallocTypeSummary(const HeapMetadata *heapMetadata) {
auto *classMetadata = heapMetadata->getClassObject(); auto *classMetadata = heapMetadata->getClassObject();
auto *typeDesc = heapMetadata->getTypeContextDescriptor(); auto *typeDesc = heapMetadata->getTypeContextDescriptor();
malloc_type_summary_t summary = {}; // Pruned metadata or unclassified
if (!classMetadata || !typeDesc)
return {.type_kind = MALLOC_TYPE_KIND_SWIFT};
// Objc // Objc
if (classMetadata && classMetadata->isPureObjC()) { if (classMetadata->isPureObjC())
summary.type_kind = MALLOC_TYPE_KIND_OBJC; return {.type_kind = MALLOC_TYPE_KIND_OBJC};
return summary;
}
// Runtime internal and unclassified malloc_type_summary_t summary = {.type_kind = MALLOC_TYPE_KIND_SWIFT};
if (!typeDesc) { summary.layout_semantics.reference_count =
summary.type_kind = MALLOC_TYPE_KIND_CXX; (classMetadata->getFlags() & ClassFlags::UsesSwiftRefcounting);
return summary;
}
// Swift auto *fieldDesc = typeDesc->Fields.get();
summary.type_kind = MALLOC_TYPE_KIND_SWIFT; if (!fieldDesc)
return summary;
bool isGenericData = true; bool isGenericData = true;
if (auto *fieldDesc = typeDesc->Fields.get()) { for (auto &field : *fieldDesc) {
for (auto &field : *fieldDesc) { if (field.isIndirectCase()) {
if (field.isIndirectCase()) { isGenericData = false;
isGenericData = false; if (field.isVar())
if (field.isVar()) summary.layout_semantics.data_pointer = true;
summary.layout_semantics.data_pointer = true; else
else summary.layout_semantics.immutable_pointer = true;
summary.layout_semantics.immutable_pointer = true;
}
} }
} }
summary.layout_semantics.generic_data = isGenericData;
if (classMetadata->Flags & ClassFlags::UsesSwiftRefcounting) {
summary.layout_semantics.reference_count = true;
} else {
summary.layout_semantics.generic_data = isGenericData;
}
return summary; return summary;

View File

@@ -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<T> {
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<T, U>: BaseClass {
var first: T
var second: U
init(_ t: T, _ u: U) {
self.first = t
self.second = u
}
}
class GenericSubclass<V, W>: GenericClass<V, Bool> {
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<Double>(first: 1.3, second: 3.7)
let testEnum = TestEnum.three(testStruct)
let baseClass = BaseClass()
let testClass = TestClass()
let testSubclass = TestSubclass()
let genericClass = GenericClass<Int, String>(5, "bla")
let genericSubclass = GenericSubclass<Double, TestClass>(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()