Skip to content

Commit db79762

Browse files
committed
Reflection: Fix class layout start offset calculations
Fixes <rdar://problem/29115967>.
1 parent 83692bb commit db79762

29 files changed

+265
-107
lines changed

include/swift/Reflection/ReflectionContext.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,16 +93,13 @@ class ReflectionContext
9393
// by looking at the size of the superclass
9494
bool valid;
9595
unsigned size, align;
96-
auto super =
97-
this->readSuperClassFromClassMetadata(MetadataAddress);
98-
if (super) {
99-
std::tie(valid, size, align) =
100-
this->readInstanceSizeAndAlignmentFromClassMetadata(super);
101-
102-
// Perform layout
103-
if (valid)
104-
TI = TC.getClassInstanceTypeInfo(TR, size, align);
105-
}
96+
std::tie(valid, size, align) =
97+
this->readInstanceSizeAndAlignmentFromClassMetadata(MetadataAddress);
98+
99+
// Perform layout
100+
if (valid)
101+
TI = TC.getClassInstanceTypeInfo(TR, size, align);
102+
106103
break;
107104
}
108105
default:

include/swift/Remote/MetadataReader.h

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -564,33 +564,90 @@ class MetadataReader {
564564
/// instance size and alignment.
565565
std::tuple<bool, unsigned, unsigned>
566566
readInstanceSizeAndAlignmentFromClassMetadata(StoredPointer MetadataAddress) {
567-
auto superMeta = readMetadata(MetadataAddress);
568-
if (!superMeta || superMeta->getKind() != MetadataKind::Class)
567+
auto meta = readMetadata(MetadataAddress);
568+
if (!meta || meta->getKind() != MetadataKind::Class)
569569
return std::make_tuple(false, 0, 0);
570570

571-
auto super = cast<TargetClassMetadata<Runtime>>(superMeta);
571+
auto classMeta = cast<TargetClassMetadata<Runtime>>(meta);
572572

573573
// See swift_initClassMetadata_UniversalStrategy()
574574
uint32_t size, align;
575-
if (super->isTypeMetadata()) {
576-
size = super->getInstanceSize();
577-
align = super->getInstanceAlignMask() + 1;
578-
} else {
575+
576+
// If this class is defined in Objective-C, return the value of the
577+
// InstanceStart field from the ROData.
578+
if (!classMeta->isTypeMetadata()) {
579579
// The following algorithm only works on the non-fragile Apple runtime.
580580

581581
// Grab the RO-data pointer. This part is not ABI.
582582
StoredPointer roDataPtr = readObjCRODataPtr(MetadataAddress);
583583
if (!roDataPtr)
584584
return std::make_tuple(false, 0, 0);
585585

586-
auto address = roDataPtr + sizeof(uint32_t) * 2;
586+
// Get the address of the InstanceStart field.
587+
auto address = roDataPtr + sizeof(uint32_t) * 1;
587588

588-
align = 16; // malloc alignment guarantee
589+
align = 16;
589590

590591
if (!Reader->readInteger(RemoteAddress(address), &size))
591592
return std::make_tuple(false, 0, 0);
593+
594+
assert((size & (align - 1)) == 0);
595+
return std::make_tuple(true, size, align);
596+
}
597+
598+
// Otherwise, it is a Swift class. Get the superclass.
599+
auto superAddr = readSuperClassFromClassMetadata(MetadataAddress);
600+
if (superAddr) {
601+
auto superMeta = readMetadata(superAddr);
602+
if (!superMeta || superMeta->getKind() != MetadataKind::Class)
603+
return std::make_tuple(false, 0, 0);
604+
605+
auto superclassMeta = cast<TargetClassMetadata<Runtime>>(superMeta);
606+
607+
// If the superclass is an Objective-C class, we start layout
608+
// from the InstanceSize of the superclass, aligned up to
609+
// 16 bytes.
610+
if (superclassMeta->isTypeMetadata()) {
611+
// Superclass is a Swift class. Get the size of an instance,
612+
// and start layout from that.
613+
size = superclassMeta->getInstanceSize();
614+
align = 1;
615+
616+
return std::make_tuple(true, size, align);
617+
}
618+
619+
std::string superName;
620+
if (!readObjCClassName(superAddr, superName))
621+
return std::make_tuple(false, 0, 0);
622+
623+
if (superName != "SwiftObject") {
624+
// Grab the RO-data pointer. This part is not ABI.
625+
StoredPointer roDataPtr = readObjCRODataPtr(superAddr);
626+
if (!roDataPtr)
627+
return std::make_tuple(false, 0, 0);
628+
629+
// Get the address of the InstanceSize field.
630+
auto address = roDataPtr + sizeof(uint32_t) * 2;
631+
632+
// malloc alignment boundary.
633+
align = 16;
634+
635+
if (!Reader->readInteger(RemoteAddress(address), &size))
636+
return std::make_tuple(false, 0, 0);
637+
638+
// Round up to the alignment boundary.
639+
size = (size + (align - 1)) & ~(align - 1);
640+
641+
return std::make_tuple(true, size, align);
642+
}
643+
644+
// Fall through.
592645
}
593646

647+
// No superclass, just an object header. 12 bytes on 32-bit, 16 on 64-bit
648+
size = Reader->getPointerSize() + sizeof(uint64_t);
649+
align = 1;
650+
594651
return std::make_tuple(true, size, align);
595652
}
596653

stdlib/public/Reflection/TypeLowering.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,9 +1266,9 @@ const TypeInfo *TypeConverter::getClassInstanceTypeInfo(const TypeRef *TR,
12661266
// TypeRef to make field types concrete.
12671267
RecordTypeInfoBuilder builder(*this, RecordKind::ClassInstance);
12681268

1269-
// Start layout from the given instance start offset.
1270-
// Extra inhabitants do not matter for a class instance payload.
1271-
builder.addField(start, align, /*numExtraInhabitants=*/0);
1269+
// Start layout from the given instance start offset. This should
1270+
// be the superclass instance size.
1271+
builder.addField(start, 1, /*numExtraInhabitants=*/0);
12721272

12731273
for (auto Field : getBuilder().getFieldTypeRefs(TR, FD))
12741274
builder.addField(Field.Name, Field.TR);

validation-test/Reflection/functions.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,14 @@ class CapturingClass {
164164
// CHECK-64: (class functions.CapturingClass)
165165

166166
// CHECK-64: Type info:
167-
// CHECK-64: (class_instance size=16 alignment=16 stride=16
167+
// CHECK-64: (class_instance size=16 alignment=1 stride=16
168168

169169
// CHECK-32: Reflecting an object.
170170
// CHECK-32: Type reference:
171171
// CHECK-32: (class functions.CapturingClass)
172172

173173
// CHECK-32: Type info:
174-
// CHECK-32: (class_instance size=12 alignment=16 stride=16
174+
// CHECK-32: (class_instance size=12 alignment=1 stride=12
175175
@_semantics("optimize.sil.never")
176176
func arity0Capture1() -> () -> () {
177177
let closure = {

validation-test/Reflection/inherits_NSObject.swift

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,62 +9,80 @@ import Foundation
99

1010
import SwiftReflectionTest
1111

12-
class InheritsNSObject : NSObject {}
13-
class ContainsInheritsNSObject : NSObject {
14-
var a: InheritsNSObject
15-
var _b: InheritsNSObject
16-
17-
var b: InheritsNSObject {
18-
get { return _b }
19-
set (newVal) { _b = newVal }
20-
}
21-
22-
override init() {
23-
a = InheritsNSObject()
24-
_b = InheritsNSObject()
25-
}
12+
class BaseNSClass : NSObject {
13+
var w: Int = 0
14+
var x: Bool = false
2615
}
2716

28-
let inheritsNSObject = InheritsNSObject()
29-
reflect(object: inheritsNSObject)
17+
class DerivedNSClass : BaseNSClass {
18+
var y: Bool = false
19+
var z: Int = 0
20+
}
21+
22+
let baseClass = BaseNSClass()
23+
reflect(object: baseClass)
3024

3125
// CHECK-64: Reflecting an object.
3226
// CHECK-64: Type reference:
33-
// CHECK-64: (class inherits_NSObject.InheritsNSObject)
27+
// CHECK-64: (class inherits_NSObject.BaseNSClass)
3428

3529
// CHECK-64: Type info:
36-
// CHECK-64: (class_instance size=8 alignment=16 stride=16 num_extra_inhabitants=0)
30+
// CHECK-64-NEXT: (class_instance size=25 alignment=8 stride=32 num_extra_inhabitants=0
31+
// CHECK-64-NEXT: (field name=w offset=16
32+
// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0
33+
// CHECK-64-NEXT: (field name=_value offset=0
34+
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0))))
35+
// CHECK-64-NEXT: (field name=x offset=24
36+
// CHECK-64-NEXT: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=254
37+
// CHECK-64-NEXT: (field name=_value offset=0
38+
// CHECK-64-NEXT: (builtin size=1 alignment=1 stride=1 num_extra_inhabitants=254)))))
3739

3840
// CHECK-32: Reflecting an object.
3941
// CHECK-32: Type reference:
40-
// CHECK-32: (class inherits_NSObject.InheritsNSObject)
42+
// CHECK-32: (class inherits_NSObject.BaseNSClass)
4143

4244
// CHECK-32: Type info:
43-
// CHECK-32: (class_instance size=4 alignment=16 stride=16 num_extra_inhabitants=0)
44-
45-
let sc = ContainsInheritsNSObject()
46-
reflect(object: sc)
45+
// CHECK-32-NEXT: (class_instance size=21 alignment=4 stride=24 num_extra_inhabitants=0
46+
// CHECK-32-NEXT: (field name=w offset=16
47+
// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0
48+
// CHECK-32-NEXT: (field name=_value offset=0
49+
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0))))
50+
// CHECK-32-NEXT: (field name=x offset=20
51+
// CHECK-32-NEXT: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=254
52+
// CHECK-32-NEXT: (field name=_value offset=0
53+
// CHECK-32-NEXT: (builtin size=1 alignment=1 stride=1 num_extra_inhabitants=254)))))
54+
55+
let derivedClass = DerivedNSClass()
56+
reflect(object: derivedClass)
4757

4858
// CHECK-64: Reflecting an object.
4959
// CHECK-64: Type reference:
50-
// CHECK-64: (class inherits_NSObject.ContainsInheritsNSObject)
60+
// CHECK-64: (class inherits_NSObject.DerivedNSClass)
5161

52-
// CHECK-64: Type info:
53-
// CHECK-64: (class_instance size=24 alignment=16 stride=32 num_extra_inhabitants=0
54-
// CHECK-64-NEXT: (field name=a offset=8
55-
// CHECK-64-NEXT: (reference kind=strong refcounting=unknown))
56-
// CHECK-64-NEXT: (field name=_b offset=16
57-
// CHECK-64-NEXT: (reference kind=strong refcounting=unknown)))
62+
// CHECK-64: Type info:
63+
// CHECK-64-NEXT: (class_instance size=40 alignment=8 stride=40 num_extra_inhabitants=0
64+
// CHECK-64-NEXT: (field name=y offset=25
65+
// CHECK-64-NEXT: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=254
66+
// CHECK-64-NEXT: (field name=_value offset=0
67+
// CHECK-64-NEXT: (builtin size=1 alignment=1 stride=1 num_extra_inhabitants=254))))
68+
// CHECK-64-NEXT: (field name=z offset=32
69+
// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0
70+
// CHECK-64-NEXT: (field name=_value offset=0
71+
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0)))))
5872

5973
// CHECK-32: Reflecting an object.
6074
// CHECK-32: Type reference:
61-
// CHECK-32: (class inherits_NSObject.ContainsInheritsNSObject)
62-
63-
// CHECK-32: Type info:
64-
// CHECK-32: (class_instance size=12 alignment=16 stride=16 num_extra_inhabitants=0
65-
// CHECK-32-NEXT: (field name=a offset=4
66-
// CHECK-32-NEXT: (reference kind=strong refcounting=unknown))
67-
// CHECK-32-NEXT: (field name=_b offset=8
68-
// CHECK-32-NEXT: (reference kind=strong refcounting=unknown)))
75+
// CHECK-32: (class inherits_NSObject.DerivedNSClass)
76+
77+
// CHECK-32: Type info:
78+
// CHECK-32-NEXT: (class_instance size=24 alignment=4 stride=24 num_extra_inhabitants=0
79+
// CHECK-32-NEXT: (field name=y offset=17
80+
// CHECK-32-NEXT: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=254
81+
// CHECK-32-NEXT: (field name=_value offset=0
82+
// CHECK-32-NEXT: (builtin size=1 alignment=1 stride=1 num_extra_inhabitants=254))))
83+
// CHECK-32-NEXT: (field name=z offset=20
84+
// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0
85+
// CHECK-32-NEXT: (field name=_value offset=0
86+
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0)))))
6987

7088
doneReflecting()
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// RUN: rm -rf %t && mkdir -p %t
2+
// RUN: %target-build-swift -lswiftSwiftReflectionTest %s -o %t/inherits_Swift
3+
// RUN: %target-run %target-swift-reflection-test %t/inherits_Swift | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize
4+
5+
// REQUIRES: objc_interop
6+
// REQUIRES: executable_test
7+
8+
import SwiftReflectionTest
9+
10+
class BaseClass {
11+
var w: Int = 0
12+
var x: Bool = false
13+
}
14+
15+
class DerivedClass : BaseClass {
16+
var y: Bool = false
17+
var z: Int = 0
18+
}
19+
20+
// CHECK-64: Reflecting an object.
21+
// CHECK-64: Type reference:
22+
// CHECK-64: (class inherits_Swift.BaseClass)
23+
24+
// CHECK-64: Type info:
25+
// CHECK-64-NEXT: (class_instance size=25 alignment=8 stride=32 num_extra_inhabitants=0
26+
// CHECK-64-NEXT: (field name=w offset=16
27+
// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0
28+
// CHECK-64-NEXT: (field name=_value offset=0
29+
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0))))
30+
// CHECK-64-NEXT: (field name=x offset=24
31+
// CHECK-64-NEXT: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=254
32+
// CHECK-64-NEXT: (field name=_value offset=0
33+
// CHECK-64-NEXT: (builtin size=1 alignment=1 stride=1 num_extra_inhabitants=254)))))
34+
35+
// CHECK-32: Reflecting an object.
36+
// CHECK-32: Type reference:
37+
// CHECK-32: (class inherits_Swift.BaseClass)
38+
39+
// CHECK-32: Type info:
40+
// CHECK-32-NEXT: (class_instance size=17 alignment=4 stride=20 num_extra_inhabitants=0
41+
// CHECK-32-NEXT: (field name=w offset=12
42+
// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0
43+
// CHECK-32-NEXT: (field name=_value offset=0
44+
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0))))
45+
// CHECK-32-NEXT: (field name=x offset=16
46+
// CHECK-32-NEXT: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=254
47+
// CHECK-32-NEXT: (field name=_value offset=0
48+
// CHECK-32-NEXT: (builtin size=1 alignment=1 stride=1 num_extra_inhabitants=254)))))
49+
50+
let baseObject = BaseClass()
51+
reflect(object: baseObject)
52+
53+
let derivedObject = DerivedClass()
54+
reflect(object: derivedObject)
55+
56+
// CHECK-64: Reflecting an object.
57+
// CHECK-64: Type reference:
58+
// CHECK-64: (class inherits_Swift.DerivedClass)
59+
60+
// CHECK-64: Type info:
61+
// CHECK-64-NEXT: (class_instance size=40 alignment=8 stride=40 num_extra_inhabitants=0
62+
// CHECK-64-NEXT: (field name=y offset=25
63+
// CHECK-64-NEXT: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=254
64+
// CHECK-64-NEXT: (field name=_value offset=0
65+
// CHECK-64-NEXT: (builtin size=1 alignment=1 stride=1 num_extra_inhabitants=254))))
66+
// CHECK-64-NEXT: (field name=z offset=32
67+
// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0
68+
// CHECK-64-NEXT: (field name=_value offset=0
69+
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0)))))
70+
71+
// CHECK-32: Reflecting an object.
72+
// CHECK-32: Type reference:
73+
// CHECK-32: (class inherits_Swift.DerivedClass)
74+
75+
// CHECK-32: Type info:
76+
// CHECK-32-NEXT: (class_instance size=24 alignment=4 stride=24 num_extra_inhabitants=0
77+
// CHECK-32-NEXT: (field name=y offset=17
78+
// CHECK-32-NEXT: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=254
79+
// CHECK-32-NEXT: (field name=_value offset=0
80+
// CHECK-32-NEXT: (builtin size=1 alignment=1 stride=1 num_extra_inhabitants=254))))
81+
// CHECK-32-NEXT: (field name=z offset=20
82+
// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0
83+
// CHECK-32-NEXT: (field name=_value offset=0
84+
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0)))))
85+
86+
doneReflecting()

validation-test/Reflection/reflect_Array.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ reflect(object: obj)
2626
// CHECK-64: (class reflect_Array.TestClass)
2727

2828
// CHECK-64: Type info:
29-
// CHECK-64: (class_instance size=24 alignment=16 stride=32 num_extra_inhabitants=0
29+
// CHECK-64: (class_instance size=24 alignment=8 stride=32 num_extra_inhabitants=0
3030
// CHECK-64: (field name=t offset=16
3131
// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=1
3232
// CHECK-64: (field name=_buffer offset=0
@@ -42,7 +42,7 @@ reflect(object: obj)
4242
// CHECK-32: (class reflect_Array.TestClass)
4343

4444
// CHECK-32: Type info:
45-
// CHECK-32: (class_instance size=16 alignment=16 stride=16 num_extra_inhabitants=0
45+
// CHECK-32: (class_instance size=16 alignment=4 stride=16 num_extra_inhabitants=0
4646
// CHECK-32: (field name=t offset=12
4747
// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=1
4848
// CHECK-32: (field name=_buffer offset=0

validation-test/Reflection/reflect_Bool.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ reflect(object: obj)
2323
// CHECK-64: (class reflect_Bool.TestClass)
2424

2525
// CHECK-64: Type info:
26-
// CHECK-64: (class_instance size=17 alignment=16 stride=32 num_extra_inhabitants=0
26+
// CHECK-64: (class_instance size=17 alignment=1 stride=17 num_extra_inhabitants=0
2727
// CHECK-64: (field name=t offset=16
2828
// CHECK-64: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=254
2929
// CHECK-64: (field name=_value offset=0
@@ -35,7 +35,7 @@ reflect(object: obj)
3535
// CHECK-32: (class reflect_Bool.TestClass)
3636

3737
// CHECK-32: Type info:
38-
// CHECK-32: (class_instance size=13 alignment=16 stride=16 num_extra_inhabitants=0
38+
// CHECK-32: (class_instance size=13 alignment=1 stride=13 num_extra_inhabitants=0
3939
// CHECK-32: (field name=t offset=12
4040
// CHECK-32: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=254
4141
// CHECK-32: (field name=_value offset=0

0 commit comments

Comments
 (0)