Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(945)

Side by Side Diff: chrome/browser/ui/cocoa/objc_zombie.mm

Issue 7084017: [Mac] Use object_destructInstance() if available. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Mark's changes. Created 9 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "chrome/browser/ui/cocoa/objc_zombie.h" 5 #import "chrome/browser/ui/cocoa/objc_zombie.h"
6 6
7 #include <dlfcn.h> 7 #include <dlfcn.h>
8 #include <mach-o/dyld.h> 8 #include <mach-o/dyld.h>
9 #include <mach-o/nlist.h> 9 #include <mach-o/nlist.h>
10 10
(...skipping 15 matching lines...) Expand all
26 // Objects with enough space are made into "fat" zombies, which 26 // Objects with enough space are made into "fat" zombies, which
27 // directly remember which class they were until reallocated. 27 // directly remember which class they were until reallocated.
28 @interface CrFatZombie : CrZombie { 28 @interface CrFatZombie : CrZombie {
29 @public 29 @public
30 Class wasa; 30 Class wasa;
31 } 31 }
32 @end 32 @end
33 33
34 namespace { 34 namespace {
35 35
36 // |object_cxxDestruct()| is an Objective-C runtime function which 36 // Function which destroys the contents of an object without freeing
37 // traverses the object's class tree for ".cxxdestruct" methods which 37 // the object. On 10.5 this is |object_cxxDestruct()|, which
38 // are run to call C++ destructors as part of |-dealloc|. The 38 // traverses the class hierarchy to run the C++ destructors. On 10.6
39 // function is not public, so must be looked up using nlist. 39 // this is |objc_destructInstance()| which calls
40 // |object_cxxDestruct()| and removes associative references.
41 // |objc_destructInstance()| returns |void*|, pretending it has no
42 // return value makes the code simpler.
40 typedef void DestructFn(id obj); 43 typedef void DestructFn(id obj);
41 DestructFn* g_object_cxxDestruct = NULL; 44 DestructFn* g_objectDestruct = NULL;
42 45
43 // The original implementation for |-[NSObject dealloc]|. 46 // The original implementation for |-[NSObject dealloc]|.
44 IMP g_originalDeallocIMP = NULL; 47 IMP g_originalDeallocIMP = NULL;
45 48
46 // Classes which freed objects become. |g_fatZombieSize| is the 49 // Classes which freed objects become. |g_fatZombieSize| is the
47 // minimum object size which can be made into a fat zombie (which can 50 // minimum object size which can be made into a fat zombie (which can
48 // remember which class it was before free, even after falling off the 51 // remember which class it was before free, even after falling off the
49 // treadmill). 52 // treadmill).
50 Class g_zombieClass = Nil; // cached [CrZombie class] 53 Class g_zombieClass = Nil; // cached [CrZombie class]
51 Class g_fatZombieClass = Nil; // cached [CrFatZombie class] 54 Class g_fatZombieClass = Nil; // cached [CrFatZombie class]
(...skipping 11 matching lines...) Expand all
63 size_t g_zombieCount = 0; 66 size_t g_zombieCount = 0;
64 size_t g_zombieIndex = 0; 67 size_t g_zombieIndex = 0;
65 68
66 typedef struct { 69 typedef struct {
67 id object; // The zombied object. 70 id object; // The zombied object.
68 Class wasa; // Value of |object->isa| before we replaced it. 71 Class wasa; // Value of |object->isa| before we replaced it.
69 } ZombieRecord; 72 } ZombieRecord;
70 73
71 ZombieRecord* g_zombies = NULL; 74 ZombieRecord* g_zombies = NULL;
72 75
73 // Lookup the private |object_cxxDestruct| function and return a 76 // Lookup the private object destruction function and return a pointer
74 // pointer to it. Returns |NULL| on failure. 77 // to it. Returns |NULL| on failure.
75 DestructFn* LookupObjectCxxDestruct() { 78 DestructFn* LookupObjectDestruct() {
76 #if ARCH_CPU_64_BITS 79 #if ARCH_CPU_64_BITS
77 // TODO(shess): Port to 64-bit. I believe using struct nlist_64 80 // TODO(shess): Port to 64-bit. I believe using struct nlist_64
78 // will suffice. http://crbug.com/44021 . 81 // will suffice. http://crbug.com/44021 .
79 NOTIMPLEMENTED(); 82 NOTIMPLEMENTED();
80 return NULL; 83 return NULL;
81 #endif 84 #endif
82 85
86 // Lookup |objc_destructInstance()| dynamically because it isn't
87 // available on 10.5.
88 const void* addr = reinterpret_cast<void*>(&object_getClass);
89 Dl_info info;
90 if (dladdr(addr, &info)) {
91 void* handle = dlopen(info.dli_fname, RTLD_LAZY|RTLD_LOCAL|RTLD_FIRST);
Mark Mentovai 2011/05/31 21:00:57 Not RTLD_FIRST. RTLD_LAZY | RTLD_LOCAL is fine. No
Scott Hess - ex-Googler 2011/06/07 21:24:51 Done.
92 if (handle) {
93 void* fn = dlsym(handle, "objc_destructInstance");
94 dlclose(handle);
Mark Mentovai 2011/05/31 21:00:57 This is normally dangerous. If you get a pointer f
Scott Hess - ex-Googler 2011/06/07 21:24:51 Done. You're making me think fondly about dlsym(R
95 if (fn)
96 return reinterpret_cast<DestructFn*>(fn);
Mark Mentovai 2011/05/31 21:00:57 This cast is fine.
Scott Hess - ex-Googler 2011/06/07 21:24:51 OK.
97 }
98 }
99
100 // |object_cxxDestruct()| is |__private_extern__| in the runtime, so
101 // there isn't a straight-forward way to find it.
83 struct nlist nl[3]; 102 struct nlist nl[3];
84 bzero(&nl, sizeof(nl)); 103 bzero(&nl, sizeof(nl));
85 104
86 nl[0].n_un.n_name = (char*)"_object_cxxDestruct"; 105 nl[0].n_un.n_name = const_cast<char*>("_object_cxxDestruct");
87 106
88 // My ability to calculate the base for offsets is apparently poor. 107 // My ability to calculate the base for offsets is apparently poor.
89 // Use |class_addIvar| as a known reference point. 108 // Use |class_addIvar| as a known reference point.
90 nl[1].n_un.n_name = (char*)"_class_addIvar"; 109 nl[1].n_un.n_name = const_cast<char*>("_class_addIvar");
91 110
92 if (nlist("/usr/lib/libobjc.dylib", nl) < 0 || 111 if (nlist("/usr/lib/libobjc.dylib", nl) < 0 ||
Mark Mentovai 2011/05/31 21:00:57 You could maybe use info.dli_fname here too, since
Scott Hess - ex-Googler 2011/06/07 21:24:51 I did break the function across the two code paths
93 nl[0].n_type == N_UNDF || nl[1].n_type == N_UNDF) 112 nl[0].n_type == N_UNDF || nl[1].n_type == N_UNDF)
94 return NULL; 113 return NULL;
95 114
96 return (DestructFn*)((char*)&class_addIvar - nl[1].n_value + nl[0].n_value); 115 return reinterpret_cast<DestructFn*>(
116 reinterpret_cast<char*>(&class_addIvar) - nl[1].n_value + nl[0].n_value);
Mark Mentovai 2011/05/31 21:05:05 Maybe intptr_t is a little more meaningful than ch
Scott Hess - ex-Googler 2011/06/07 21:24:51 Done.
97 } 117 }
98 118
99 // Replacement |-dealloc| which turns objects into zombies and places 119 // Replacement |-dealloc| which turns objects into zombies and places
100 // them into |g_zombies| to be freed later. 120 // them into |g_zombies| to be freed later.
101 void ZombieDealloc(id self, SEL _cmd) { 121 void ZombieDealloc(id self, SEL _cmd) {
102 // This code should only be called when it is implementing |-dealloc|. 122 // This code should only be called when it is implementing |-dealloc|.
103 DCHECK_EQ(_cmd, @selector(dealloc)); 123 DCHECK_EQ(_cmd, @selector(dealloc));
104 124
105 // Use the original |-dealloc| if the object doesn't wish to be 125 // Use the original |-dealloc| if the object doesn't wish to be
106 // zombied. 126 // zombied.
107 if (!g_zombieAllObjects && ![self shouldBecomeCrZombie]) { 127 if (!g_zombieAllObjects && ![self shouldBecomeCrZombie]) {
108 g_originalDeallocIMP(self, _cmd); 128 g_originalDeallocIMP(self, _cmd);
109 return; 129 return;
110 } 130 }
111 131
112 // Use the original |-dealloc| if |object_cxxDestruct| was never 132 // Use the original |-dealloc| if |g_objectDestruct| was never
113 // initialized, because otherwise C++ destructors won't be called. 133 // initialized, because otherwise C++ destructors won't be called.
114 // This case should be impossible, but doing it wrong would cause 134 // This case should be impossible, but doing it wrong would cause
115 // terrible problems. 135 // terrible problems.
116 DCHECK(g_object_cxxDestruct); 136 DCHECK(g_objectDestruct);
117 if (!g_object_cxxDestruct) { 137 if (!g_objectDestruct) {
118 g_originalDeallocIMP(self, _cmd); 138 g_originalDeallocIMP(self, _cmd);
119 return; 139 return;
120 } 140 }
121 141
122 Class wasa = object_getClass(self); 142 Class wasa = object_getClass(self);
123 const size_t size = class_getInstanceSize(wasa); 143 const size_t size = class_getInstanceSize(wasa);
124 144
125 // Destroy the instance by calling C++ destructors and clearing it 145 // Destroy the instance by calling C++ destructors and clearing it
126 // to something unlikely to work well if someone references it. 146 // to something unlikely to work well if someone references it.
127 // NOTE(shess): |object_dispose()| will call |object_cxxDestruct()| 147 // NOTE(shess): |object_dispose()| will call this again when the
128 // again when the zombie falls off the treadmill! But by then |isa| 148 // zombie falls off the treadmill! But by then |isa| will be a
129 // will be something without destructors, so it won't hurt anything. 149 // class without C++ destructors or associative references, so it
130 (*g_object_cxxDestruct)(self); 150 // won't hurt anything.
151 (*g_objectDestruct)(self);
131 memset(self, '!', size); 152 memset(self, '!', size);
132 153
133 // If the instance is big enough, make it into a fat zombie and have 154 // If the instance is big enough, make it into a fat zombie and have
134 // it remember the old |isa|. Otherwise make it a regular zombie. 155 // it remember the old |isa|. Otherwise make it a regular zombie.
135 // Setting |isa| rather than using |object_setClass()| because that 156 // Setting |isa| rather than using |object_setClass()| because that
136 // function is implemented with a memory barrier. The runtime's 157 // function is implemented with a memory barrier. The runtime's
137 // |_internal_object_dispose()| (in objc-class.m) does this, so it 158 // |_internal_object_dispose()| (in objc-class.m) does this, so it
138 // should be safe (messaging free'd objects shouldn't be expected to 159 // should be safe (messaging free'd objects shouldn't be expected to
139 // be thread-safe in the first place). 160 // be thread-safe in the first place).
140 if (size >= g_fatZombieSize) { 161 if (size >= g_fatZombieSize) {
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
215 } 236 }
216 237
217 // Initialize our globals, returning YES on success. 238 // Initialize our globals, returning YES on success.
218 BOOL ZombieInit() { 239 BOOL ZombieInit() {
219 static BOOL initialized = NO; 240 static BOOL initialized = NO;
220 if (initialized) 241 if (initialized)
221 return YES; 242 return YES;
222 243
223 Class rootClass = [NSObject class]; 244 Class rootClass = [NSObject class];
224 245
225 g_object_cxxDestruct = LookupObjectCxxDestruct(); 246 g_objectDestruct = LookupObjectDestruct();
226 g_originalDeallocIMP = 247 g_originalDeallocIMP =
227 class_getMethodImplementation(rootClass, @selector(dealloc)); 248 class_getMethodImplementation(rootClass, @selector(dealloc));
228 // objc_getClass() so CrZombie doesn't need +class. 249 // objc_getClass() so CrZombie doesn't need +class.
229 g_zombieClass = objc_getClass("CrZombie"); 250 g_zombieClass = objc_getClass("CrZombie");
230 g_fatZombieClass = objc_getClass("CrFatZombie"); 251 g_fatZombieClass = objc_getClass("CrFatZombie");
231 g_fatZombieSize = class_getInstanceSize(g_fatZombieClass); 252 g_fatZombieSize = class_getInstanceSize(g_fatZombieClass);
232 253
233 if (!g_object_cxxDestruct || !g_originalDeallocIMP || 254 if (!g_objectDestruct || !g_originalDeallocIMP ||
234 !g_zombieClass || !g_fatZombieClass) 255 !g_zombieClass || !g_fatZombieClass)
235 return NO; 256 return NO;
236 257
237 initialized = YES; 258 initialized = YES;
238 return YES; 259 return YES;
239 } 260 }
240 261
241 } // namespace 262 } // namespace
242 263
243 @implementation CrZombie 264 @implementation CrZombie
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
408 if (oldZombies) { 429 if (oldZombies) {
409 for (size_t i = 0; i < oldCount; ++i) { 430 for (size_t i = 0; i < oldCount; ++i) {
410 if (oldZombies[i].object) 431 if (oldZombies[i].object)
411 object_dispose(oldZombies[i].object); 432 object_dispose(oldZombies[i].object);
412 } 433 }
413 free(oldZombies); 434 free(oldZombies);
414 } 435 }
415 } 436 }
416 437
417 } // namespace ObjcEvilDoers 438 } // namespace ObjcEvilDoers
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698