Skip to content

Commit a4c70eb

Browse files
committed
Merge pull request #1798 from DougGregor/sr-704-backport
[Swift 2.2.1][Swift runtime] Deallocation of partial class instances for Objective-C-derived classes
2 parents 9e755c2 + f5f4baf commit a4c70eb

File tree

3 files changed

+128
-6
lines changed

3 files changed

+128
-6
lines changed

include/swift/Runtime/ObjCBridge.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ extern "C" id objc_initWeak(id*, id);
4545
extern "C" id objc_storeWeak(id*, id);
4646
extern "C" void objc_destroyWeak(id*);
4747
extern "C" id objc_loadWeakRetained(id*);
48+
extern "C" Class object_setClass(id, Class);
4849

4950
// Description of an Objective-C image.
5051
// __DATA,__objc_imageinfo stores one of these.

stdlib/public/runtime/HeapObject.cpp

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -453,16 +453,48 @@ extern "C" void swift_deallocPartialClassInstance(HeapObject *object,
453453
return;
454454

455455
// Destroy ivars
456-
auto *objectMetadata = _swift_getClassOfAllocated(object);
457-
while (objectMetadata != metadata) {
458-
auto classMetadata = objectMetadata->getClassObject();
459-
assert(classMetadata && "Not a class?");
456+
auto *classMetadata = _swift_getClassOfAllocated(object)->getClassObject();
457+
assert(classMetadata && "Not a class?");
458+
while (classMetadata != metadata) {
459+
#if SWIFT_OBJC_INTEROP
460+
// If we have hit a pure Objective-C class, we won't see another ivar
461+
// destroyer.
462+
if (classMetadata->isPureObjC()) {
463+
// Set the class to the pure Objective-C superclass, so that when dealloc
464+
// runs, it starts at that superclass.
465+
object_setClass((id)object, (Class)classMetadata);
466+
467+
// Release the object.
468+
objc_release((id)object);
469+
return;
470+
}
471+
#endif
472+
460473
if (auto fn = classMetadata->getIVarDestroyer())
461474
fn(object);
462-
objectMetadata = classMetadata->SuperClass;
463-
assert(objectMetadata && "Given metatype not a superclass of object type?");
475+
476+
classMetadata = classMetadata->SuperClass->getClassObject();
477+
assert(classMetadata && "Given metatype not a superclass of object type?");
464478
}
465479

480+
#if SWIFT_OBJC_INTEROP
481+
// If this class doesn't use Swift-native reference counting, use
482+
// objc_release instead.
483+
if (!usesNativeSwiftReferenceCounting(classMetadata)) {
484+
// Find the pure Objective-C superclass.
485+
while (!classMetadata->isPureObjC())
486+
classMetadata = classMetadata->SuperClass->getClassObject();
487+
488+
// Set the class to the pure Objective-C superclass, so that when dealloc
489+
// runs, it starts at that superclass.
490+
object_setClass((id)object, (Class)classMetadata);
491+
492+
// Release the object.
493+
objc_release((id)object);
494+
return;
495+
}
496+
#endif
497+
466498
// The strong reference count should be +1 -- tear down the object
467499
bool shouldDeallocate = object->refCount.decrementShouldDeallocate();
468500
assert(shouldDeallocate);
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// RUN: %target-run-simple-swift
2+
// REQUIRES: executable_test
3+
// REQUIRES: objc_interop
4+
5+
import StdlibUnittest
6+
import Foundation
7+
8+
var ObjCFailableInitTestSuite = TestSuite("ObjCFailableInit")
9+
10+
class Canary {
11+
static var count: Int = 0
12+
13+
init() {
14+
Canary.count += 1
15+
}
16+
17+
deinit {
18+
Canary.count -= 1
19+
}
20+
}
21+
22+
extension NSDate {
23+
convenience init?(b: Bool) {
24+
guard b else { return nil }
25+
self.init()
26+
}
27+
}
28+
29+
class MyDate : NSDate {
30+
var derivedCanary = Canary()
31+
32+
static var count = 0
33+
34+
override init() {
35+
MyDate.count += 1
36+
super.init()
37+
}
38+
39+
required convenience init(coder: NSCoder) {
40+
fatalError("not implemented")
41+
}
42+
43+
deinit {
44+
MyDate.count -= 1
45+
}
46+
47+
@objc convenience init?(b: Bool) {
48+
guard b else { return nil }
49+
self.init()
50+
}
51+
}
52+
53+
class MyDerivedDate : MyDate {
54+
var canary = Canary()
55+
56+
static var derivedCount = 0
57+
58+
override init() {
59+
MyDerivedDate.count += 1
60+
}
61+
62+
deinit {
63+
MyDerivedDate.count -= 1
64+
}
65+
66+
}
67+
68+
func mustFail<T>(f: () -> T?) {
69+
if f() != nil {
70+
preconditionFailure("Didn't fail")
71+
}
72+
}
73+
74+
ObjCFailableInitTestSuite.test("InitFailure_Before") {
75+
mustFail { NSDate(b: false) }
76+
expectEqual(0, Canary.count)
77+
78+
mustFail { MyDate(b: false) }
79+
expectEqual(0, Canary.count)
80+
expectEqual(0, MyDate.count)
81+
82+
mustFail { MyDerivedDate(b: false) }
83+
expectEqual(0, Canary.count)
84+
expectEqual(0, MyDate.count)
85+
expectEqual(0, MyDerivedDate.derivedCount)
86+
}
87+
88+
runAllTests()
89+

0 commit comments

Comments
 (0)