Skip to content

Commit 1c7a50d

Browse files
committed
Reflection: Fix class layout, again
There were a few problems here with subclasses of Objective-C classes. Use the InstanceStart field from rodata to correctly lay out instance variables, and verify the results match with dynamic and static layout. Better fix for <rdar://problem/27932061>.
1 parent 78d11de commit 1c7a50d

File tree

9 files changed

+354
-97
lines changed

9 files changed

+354
-97
lines changed

include/swift/Reflection/ReflectionContext.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,13 @@ class ReflectionContext
9292
// Figure out where the stored properties of this class begin
9393
// by looking at the size of the superclass
9494
bool valid;
95-
unsigned size, align;
96-
std::tie(valid, size, align) =
97-
this->readInstanceSizeAndAlignmentFromClassMetadata(MetadataAddress);
95+
unsigned start;
96+
std::tie(valid, start) =
97+
this->readInstanceStartAndAlignmentFromClassMetadata(MetadataAddress);
9898

9999
// Perform layout
100100
if (valid)
101-
TI = TC.getClassInstanceTypeInfo(TR, size, align);
101+
TI = TC.getClassInstanceTypeInfo(TR, start);
102102

103103
break;
104104
}

include/swift/Reflection/TypeLowering.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,7 @@ class TypeConverter {
246246
///
247247
/// Not cached.
248248
const TypeInfo *getClassInstanceTypeInfo(const TypeRef *TR,
249-
unsigned start,
250-
unsigned align);
249+
unsigned start);
251250

252251
private:
253252
friend class swift::reflection::LowerType;

include/swift/Remote/MetadataReader.h

Lines changed: 15 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -561,94 +561,28 @@ class MetadataReader {
561561
}
562562

563563
/// Given a remote pointer to class metadata, attempt to discover its class
564-
/// instance size and alignment.
565-
std::tuple<bool, unsigned, unsigned>
566-
readInstanceSizeAndAlignmentFromClassMetadata(StoredPointer MetadataAddress) {
564+
/// instance size and whether fields should use the resilient layout strategy.
565+
std::pair<bool, unsigned>
566+
readInstanceStartAndAlignmentFromClassMetadata(StoredPointer MetadataAddress) {
567567
auto meta = readMetadata(MetadataAddress);
568568
if (!meta || meta->getKind() != MetadataKind::Class)
569-
return std::make_tuple(false, 0, 0);
570-
571-
auto classMeta = cast<TargetClassMetadata<Runtime>>(meta);
572-
573-
// See swift_initClassMetadata_UniversalStrategy()
574-
uint32_t size, align;
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()) {
579-
// The following algorithm only works on the non-fragile Apple runtime.
580-
581-
// Grab the RO-data pointer. This part is not ABI.
582-
StoredPointer roDataPtr = readObjCRODataPtr(MetadataAddress);
583-
if (!roDataPtr)
584-
return std::make_tuple(false, 0, 0);
585-
586-
// Get the address of the InstanceStart field.
587-
auto address = roDataPtr + sizeof(uint32_t) * 1;
588-
589-
align = 16;
590-
591-
if (!Reader->readInteger(RemoteAddress(address), &size))
592-
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);
569+
return std::make_pair(false, 0);
637570

638-
// Round up to the alignment boundary.
639-
size = (size + (align - 1)) & ~(align - 1);
571+
// The following algorithm only works on the non-fragile Apple runtime.
640572

641-
return std::make_tuple(true, size, align);
642-
}
573+
// Grab the RO-data pointer. This part is not ABI.
574+
StoredPointer roDataPtr = readObjCRODataPtr(MetadataAddress);
575+
if (!roDataPtr)
576+
return std::make_pair(false, 0);
643577

644-
// Fall through.
645-
}
578+
// Get the address of the InstanceStart field.
579+
auto address = roDataPtr + sizeof(uint32_t) * 1;
646580

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;
581+
unsigned start;
582+
if (!Reader->readInteger(RemoteAddress(address), &start))
583+
return std::make_pair(false, 0);
650584

651-
return std::make_tuple(true, size, align);
585+
return std::make_pair(true, start);
652586
}
653587

654588
/// Given a remote pointer to metadata, attempt to turn it into a type.

stdlib/public/Reflection/TypeLowering.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,8 +1251,7 @@ const TypeInfo *TypeConverter::getTypeInfo(const TypeRef *TR) {
12511251
}
12521252

12531253
const TypeInfo *TypeConverter::getClassInstanceTypeInfo(const TypeRef *TR,
1254-
unsigned start,
1255-
unsigned align) {
1254+
unsigned start) {
12561255
const FieldDescriptor *FD = getBuilder().getFieldTypeInfo(TR);
12571256
if (FD == nullptr) {
12581257
DEBUG(std::cerr << "No field descriptor: "; TR->dump());
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#ifndef SWIFT_TEST_OBJC_CLASSES_H
2+
#define SWIFT_TEST_OBJC_CLASSES_H
3+
4+
#import <Foundation/Foundation.h>
5+
6+
@interface FirstClass : NSObject
7+
@property void *x;
8+
@end
9+
10+
@interface SecondClass : NSObject
11+
@property void *x;
12+
@property void *y;
13+
@end
14+
15+
@interface ThirdClass : NSObject
16+
@property void *x;
17+
@property void *y;
18+
@property void *z;
19+
@end
20+
21+
#endif
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#import "ObjCClasses.h"
2+
3+
@implementation FirstClass : NSObject
4+
@synthesize x;
5+
@end
6+
7+
@implementation SecondClass : NSObject
8+
@synthesize x;
9+
@synthesize y;
10+
@end
11+
12+
@implementation ThirdClass : NSObject
13+
@synthesize x;
14+
@synthesize y;
15+
@synthesize z;
16+
@end
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module ObjCClasses {
2+
header "ObjCClasses.h"
3+
}

validation-test/Reflection/inherits_NSObject.swift

Lines changed: 111 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// REQUIRES: executable_test
77

88
import Foundation
9+
import simd
910

1011
import SwiftReflectionTest
1112

@@ -14,11 +15,6 @@ class BaseNSClass : NSObject {
1415
var x: Bool = false
1516
}
1617

17-
class DerivedNSClass : BaseNSClass {
18-
var y: Bool = false
19-
var z: Int = 0
20-
}
21-
2218
let baseClass = BaseNSClass()
2319
reflect(object: baseClass)
2420

@@ -42,16 +38,21 @@ reflect(object: baseClass)
4238
// CHECK-32: (class inherits_NSObject.BaseNSClass)
4339

4440
// CHECK-32: Type info:
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
41+
// CHECK-32-NEXT: (class_instance size=17 alignment=4 stride=20 num_extra_inhabitants=0
42+
// CHECK-32-NEXT: (field name=w offset=12
4743
// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0
4844
// CHECK-32-NEXT: (field name=_value offset=0
4945
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0))))
50-
// CHECK-32-NEXT: (field name=x offset=20
46+
// CHECK-32-NEXT: (field name=x offset=16
5147
// CHECK-32-NEXT: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=254
5248
// CHECK-32-NEXT: (field name=_value offset=0
5349
// CHECK-32-NEXT: (builtin size=1 alignment=1 stride=1 num_extra_inhabitants=254)))))
5450

51+
class DerivedNSClass : BaseNSClass {
52+
var y: Bool = false
53+
var z: Int = 0
54+
}
55+
5556
let derivedClass = DerivedNSClass()
5657
reflect(object: derivedClass)
5758

@@ -85,4 +86,106 @@ reflect(object: derivedClass)
8586
// CHECK-32-NEXT: (field name=_value offset=0
8687
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0)))))
8788

89+
// Note: dynamic layout starts at offset 8, not 16
90+
class GenericBaseNSClass<T> : NSObject {
91+
var w: T = 0 as! T
92+
}
93+
94+
let genericBaseClass = GenericBaseNSClass<Int>()
95+
reflect(object: genericBaseClass)
96+
97+
// CHECK-64: Reflecting an object.
98+
// CHECK-64: Type reference:
99+
// CHECK-64: (bound_generic_class inherits_NSObject.GenericBaseNSClass
100+
// CHECK-64: (struct Swift.Int))
101+
102+
// CHECK-64: Type info:
103+
// CHECK-64-NEXT: (class_instance size=16 alignment=8 stride=16 num_extra_inhabitants=0
104+
// CHECK-64-NEXT: (field name=w offset=8
105+
// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0
106+
// CHECK-64-NEXT: (field name=_value offset=0
107+
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0)))))
108+
109+
// CHECK-32: Reflecting an object.
110+
// CHECK-32: Type reference:
111+
// CHECK-32: (bound_generic_class inherits_NSObject.GenericBaseNSClass
112+
// CHECK-32: (struct Swift.Int))
113+
114+
// CHECK-32: Type info:
115+
// CHECK-32-NEXT: (class_instance size=8 alignment=4 stride=8 num_extra_inhabitants=0
116+
// CHECK-32-NEXT: (field name=w offset=4
117+
// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0
118+
// CHECK-32-NEXT: (field name=_value offset=0
119+
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0)))))
120+
121+
class AlignedNSClass : NSObject {
122+
var w: Int = 0
123+
var x: int4 = [1,2,3,4]
124+
}
125+
126+
let alignedClass = AlignedNSClass()
127+
reflect(object: alignedClass)
128+
129+
// CHECK-64: Reflecting an object.
130+
// CHECK-64: Type reference:
131+
// CHECK-64: (class inherits_NSObject.AlignedNSClass)
132+
133+
// CHECK-64: Type info:
134+
// CHECK-64-NEXT: (class_instance size=48 alignment=16 stride=48 num_extra_inhabitants=0
135+
// CHECK-64-NEXT: (field name=w offset=16
136+
// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0
137+
// CHECK-64-NEXT: (field name=_value offset=0
138+
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0))))
139+
// CHECK-64-NEXT: (field name=x offset=32
140+
// CHECK-64-NEXT: (builtin size=16 alignment=16 stride=16 num_extra_inhabitants=0)))
141+
142+
// CHECK-32: Reflecting an object.
143+
// CHECK-32: Type reference:
144+
// CHECK-32: (class inherits_NSObject.AlignedNSClass)
145+
146+
// CHECK-32: Type info:
147+
// CHECK-32-NEXT: (class_instance size=32 alignment=16 stride=32 num_extra_inhabitants=0
148+
// CHECK-32-NEXT: (field name=w offset=12
149+
// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0
150+
// CHECK-32-NEXT: (field name=_value offset=0
151+
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0))))
152+
// CHECK-32-NEXT: (field name=x offset=16
153+
// CHECK-32-NEXT: (builtin size=16 alignment=16 stride=16 num_extra_inhabitants=0)))
154+
155+
class GenericAlignedNSClass<T> : NSObject {
156+
var w: T = 0 as! T
157+
var x: int4 = [1,2,3,4]
158+
}
159+
160+
let genericAlignedClass = GenericAlignedNSClass<Int>()
161+
reflect(object: genericAlignedClass)
162+
163+
// CHECK-64: Reflecting an object.
164+
// CHECK-64: Type reference:
165+
// CHECK-64: (bound_generic_class inherits_NSObject.GenericAlignedNSClass
166+
// CHECK-64: (struct Swift.Int))
167+
168+
// CHECK-64: Type info:
169+
// CHECK-64-NEXT: (class_instance size=48 alignment=16 stride=48 num_extra_inhabitants=0
170+
// CHECK-64-NEXT: (field name=w offset=16
171+
// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0
172+
// CHECK-64-NEXT: (field name=_value offset=0
173+
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0))))
174+
// CHECK-64-NEXT: (field name=x offset=32
175+
// CHECK-64-NEXT: (builtin size=16 alignment=16 stride=16 num_extra_inhabitants=0)))
176+
177+
// CHECK-32: Reflecting an object.
178+
// CHECK-32: Type reference:
179+
// CHECK-32: (bound_generic_class inherits_NSObject.GenericAlignedNSClass
180+
// CHECK-32: (struct Swift.Int))
181+
182+
// CHECK-32: Type info:
183+
// CHECK-32-NEXT: (class_instance size=48 alignment=16 stride=48 num_extra_inhabitants=0
184+
// CHECK-32-NEXT: (field name=w offset=16
185+
// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0
186+
// CHECK-32-NEXT: (field name=_value offset=0
187+
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0))))
188+
// CHECK-32-NEXT: (field name=x offset=32
189+
// CHECK-32-NEXT: (builtin size=16 alignment=16 stride=16 num_extra_inhabitants=0)))
190+
88191
doneReflecting()

0 commit comments

Comments
 (0)