1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <Cocoa/Cocoa.h>
#include <dlfcn.h>
#include "base/logging.h"
#import "base/memory/scoped_nsobject.h"
#import "chrome/browser/ui/cocoa/objc_zombie.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
namespace {
// Dynamically look up |objc_setAssociatedObject()|, which isn't
// available until the 10.6 SDK.
typedef void objc_setAssociatedObjectFn(id object, void *key, id value,
int policy);
objc_setAssociatedObjectFn* LookupSetAssociatedObjectFn() {
return reinterpret_cast<objc_setAssociatedObjectFn*>(
dlsym(RTLD_DEFAULT, "objc_setAssociatedObject"));
}
} // namespace
@interface ZombieCxxDestructTest : NSObject
{
scoped_nsobject<id> aRef_;
}
- (id)initWith:(id)anObject;
@end
@implementation ZombieCxxDestructTest
- (id)initWith:(id)anObject {
self = [super init];
if (self) {
aRef_.reset([anObject retain]);
}
return self;
}
@end
@interface ZombieAssociatedObjectTest : NSObject
+ (BOOL)supportsAssociatedObjects;
- (id)initWithAssociatedObject:(id)anObject;
@end
@implementation ZombieAssociatedObjectTest
+ (BOOL)supportsAssociatedObjects {
if (LookupSetAssociatedObjectFn())
return YES;
return NO;
}
- (id)initWithAssociatedObject:(id)anObject {
self = [super init];
if (self) {
objc_setAssociatedObjectFn* fn = LookupSetAssociatedObjectFn();
if (fn) {
// Cribbed from 10.6 <objc/runtime.h>.
static const int kObjcAssociationRetain = 01401;
// The address of the variable itself is the unique key, the
// contents don't matter.
static char kAssociatedObjectKey = 'x';
(*fn)(self, &kAssociatedObjectKey, anObject, kObjcAssociationRetain);
}
}
return self;
}
@end
namespace {
// Verify that the C++ destructors run when the last reference to the
// object is released.
// NOTE(shess): To test the negative, comment out the |g_objectDestruct()|
// call in |ZombieDealloc()|.
TEST(ObjcZombieTest, CxxDestructors) {
scoped_nsobject<id> anObject([[NSObject alloc] init]);
EXPECT_EQ(1u, [anObject retainCount]);
ASSERT_TRUE(ObjcEvilDoers::ZombieEnable(YES, 100));
scoped_nsobject<ZombieCxxDestructTest> soonInfected(
[[ZombieCxxDestructTest alloc] initWith:anObject]);
EXPECT_EQ(2u, [anObject retainCount]);
// When |soonInfected| becomes a zombie, the C++ destructors should
// run and release a reference to |anObject|.
soonInfected.reset();
EXPECT_EQ(1u, [anObject retainCount]);
// The local reference should remain (C++ destructors aren't re-run).
ObjcEvilDoers::ZombieDisable();
EXPECT_EQ(1u, [anObject retainCount]);
}
// Verify that the associated objects are released when the object is
// released.
// NOTE(shess): To test the negative, hardcode |g_objectDestruct| to
// the 10.5 version in |ZombieInit()|, and run this test on 10.6.
TEST(ObjcZombieTest, AssociatedObjectsReleased) {
if (![ZombieAssociatedObjectTest supportsAssociatedObjects]) {
LOG(ERROR)
<< "ObjcZombieTest.AssociatedObjectsReleased not supported on 10.5";
return;
}
scoped_nsobject<id> anObject([[NSObject alloc] init]);
EXPECT_EQ(1u, [anObject retainCount]);
ASSERT_TRUE(ObjcEvilDoers::ZombieEnable(YES, 100));
scoped_nsobject<ZombieAssociatedObjectTest> soonInfected(
[[ZombieAssociatedObjectTest alloc] initWithAssociatedObject:anObject]);
EXPECT_EQ(2u, [anObject retainCount]);
// When |soonInfected| becomes a zombie, the associated object
// should be released.
soonInfected.reset();
EXPECT_EQ(1u, [anObject retainCount]);
// The local reference should remain (associated objects not re-released).
ObjcEvilDoers::ZombieDisable();
EXPECT_EQ(1u, [anObject retainCount]);
}
} // namespace
|