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

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: Created 9 years, 7 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) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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.
40 typedef void DestructFn(id obj); 41 typedef void DestructFn(id obj);
Mark Mentovai 2011/05/28 13:20:46 Technically the function pointer types are incompa
Scott Hess - ex-Googler 2011/05/31 20:22:30 Augmented the comment to that end.
41 DestructFn* g_object_cxxDestruct = NULL; 42 DestructFn* g_objectDestruct = NULL;
42 43
43 // The original implementation for |-[NSObject dealloc]|. 44 // The original implementation for |-[NSObject dealloc]|.
44 IMP g_originalDeallocIMP = NULL; 45 IMP g_originalDeallocIMP = NULL;
45 46
46 // Classes which freed objects become. |g_fatZombieSize| is the 47 // Classes which freed objects become. |g_fatZombieSize| is the
47 // minimum object size which can be made into a fat zombie (which can 48 // 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 49 // remember which class it was before free, even after falling off the
49 // treadmill). 50 // treadmill).
50 Class g_zombieClass = Nil; // cached [CrZombie class] 51 Class g_zombieClass = Nil; // cached [CrZombie class]
51 Class g_fatZombieClass = Nil; // cached [CrFatZombie class] 52 Class g_fatZombieClass = Nil; // cached [CrFatZombie class]
(...skipping 11 matching lines...) Expand all
63 size_t g_zombieCount = 0; 64 size_t g_zombieCount = 0;
64 size_t g_zombieIndex = 0; 65 size_t g_zombieIndex = 0;
65 66
66 typedef struct { 67 typedef struct {
67 id object; // The zombied object. 68 id object; // The zombied object.
68 Class wasa; // Value of |object->isa| before we replaced it. 69 Class wasa; // Value of |object->isa| before we replaced it.
69 } ZombieRecord; 70 } ZombieRecord;
70 71
71 ZombieRecord* g_zombies = NULL; 72 ZombieRecord* g_zombies = NULL;
72 73
73 // Lookup the private |object_cxxDestruct| function and return a 74 // Lookup the private object destruction function and return a pointer
74 // pointer to it. Returns |NULL| on failure. 75 // to it. Returns |NULL| on failure.
75 DestructFn* LookupObjectCxxDestruct() { 76 DestructFn* LookupObjectDestruct() {
76 #if ARCH_CPU_64_BITS 77 #if ARCH_CPU_64_BITS
Mark Mentovai 2011/05/28 13:20:46 Put this below the dlsym (but keep it before the n
Scott Hess - ex-Googler 2011/05/31 20:22:30 You sure? I think it's the right long-term decisi
Mark Mentovai 2011/05/31 21:00:57 shess wrote:
Scott Hess - ex-Googler 2011/06/07 21:24:51 Done. AFAICT, nlist() isn't present for 64-bit, a
77 // TODO(shess): Port to 64-bit. I believe using struct nlist_64 78 // TODO(shess): Port to 64-bit. I believe using struct nlist_64
78 // will suffice. http://crbug.com/44021 . 79 // will suffice. http://crbug.com/44021 .
79 NOTIMPLEMENTED(); 80 NOTIMPLEMENTED();
80 return NULL; 81 return NULL;
81 #endif 82 #endif
82 83
84 void* fn = dlsym(RTLD_DEFAULT, "objc_destructInstance");
Mark Mentovai 2011/05/29 22:02:31 RTLD_DEFAULT kind of sucks for a few reasons. It’l
Scott Hess - ex-Googler 2011/05/31 20:22:30 Done. I do not know what I'm doing in this area,
85 if (fn)
86 return (DestructFn*)fn;
Scott Hess - ex-Googler 2011/05/28 05:44:35 I need to clean up the casting in this function, a
Scott Hess - ex-Googler 2011/05/31 20:22:30 OK, casting is so annoying that I'm not sure I'm d
87
83 struct nlist nl[3]; 88 struct nlist nl[3];
Mark Mentovai 2011/05/29 22:02:31 If we can’t find objc_destructInstance with dlsym
Scott Hess - ex-Googler 2011/05/31 20:22:30 Earlier when I put in the whitelist, I left it way
Mark Mentovai 2011/05/31 21:00:57 shess wrote:
84 bzero(&nl, sizeof(nl)); 89 bzero(&nl, sizeof(nl));
85 90
86 nl[0].n_un.n_name = (char*)"_object_cxxDestruct"; 91 nl[0].n_un.n_name = (char*)"_object_cxxDestruct";
87 92
88 // My ability to calculate the base for offsets is apparently poor. 93 // My ability to calculate the base for offsets is apparently poor.
89 // Use |class_addIvar| as a known reference point. 94 // Use |class_addIvar| as a known reference point.
90 nl[1].n_un.n_name = (char*)"_class_addIvar"; 95 nl[1].n_un.n_name = (char*)"_class_addIvar";
91 96
92 if (nlist("/usr/lib/libobjc.dylib", nl) < 0 || 97 if (nlist("/usr/lib/libobjc.dylib", nl) < 0 ||
93 nl[0].n_type == N_UNDF || nl[1].n_type == N_UNDF) 98 nl[0].n_type == N_UNDF || nl[1].n_type == N_UNDF)
94 return NULL; 99 return NULL;
95 100
96 return (DestructFn*)((char*)&class_addIvar - nl[1].n_value + nl[0].n_value); 101 return (DestructFn*)((char*)&class_addIvar - nl[1].n_value + nl[0].n_value);
97 } 102 }
98 103
99 // Replacement |-dealloc| which turns objects into zombies and places 104 // Replacement |-dealloc| which turns objects into zombies and places
100 // them into |g_zombies| to be freed later. 105 // them into |g_zombies| to be freed later.
101 void ZombieDealloc(id self, SEL _cmd) { 106 void ZombieDealloc(id self, SEL _cmd) {
102 // This code should only be called when it is implementing |-dealloc|. 107 // This code should only be called when it is implementing |-dealloc|.
103 DCHECK_EQ(_cmd, @selector(dealloc)); 108 DCHECK_EQ(_cmd, @selector(dealloc));
104 109
105 // Use the original |-dealloc| if the object doesn't wish to be 110 // Use the original |-dealloc| if the object doesn't wish to be
106 // zombied. 111 // zombied.
107 if (!g_zombieAllObjects && ![self shouldBecomeCrZombie]) { 112 if (!g_zombieAllObjects && ![self shouldBecomeCrZombie]) {
108 g_originalDeallocIMP(self, _cmd); 113 g_originalDeallocIMP(self, _cmd);
109 return; 114 return;
110 } 115 }
111 116
112 // Use the original |-dealloc| if |object_cxxDestruct| was never 117 // Use the original |-dealloc| if |g_objectDestruct| was never
113 // initialized, because otherwise C++ destructors won't be called. 118 // initialized, because otherwise C++ destructors won't be called.
114 // This case should be impossible, but doing it wrong would cause 119 // This case should be impossible, but doing it wrong would cause
115 // terrible problems. 120 // terrible problems.
116 DCHECK(g_object_cxxDestruct); 121 DCHECK(g_objectDestruct);
117 if (!g_object_cxxDestruct) { 122 if (!g_objectDestruct) {
118 g_originalDeallocIMP(self, _cmd); 123 g_originalDeallocIMP(self, _cmd);
119 return; 124 return;
120 } 125 }
121 126
122 Class wasa = object_getClass(self); 127 Class wasa = object_getClass(self);
123 const size_t size = class_getInstanceSize(wasa); 128 const size_t size = class_getInstanceSize(wasa);
124 129
125 // Destroy the instance by calling C++ destructors and clearing it 130 // Destroy the instance by calling C++ destructors and clearing it
126 // to something unlikely to work well if someone references it. 131 // to something unlikely to work well if someone references it.
127 (*g_object_cxxDestruct)(self); 132 // NOTE(shess): |object_dispose()| will call this again when the
133 // zombie falls off the treadmill! But by then |isa| will be a
134 // class without C++ destructors or associative references, so it
135 // won't hurt anything.
Scott Hess - ex-Googler 2011/05/28 05:44:35 Sorry, I think this comment snuck over when I brok
Mark Mentovai 2011/05/28 13:20:46 shess wrote:
136 (*g_objectDestruct)(self);
128 memset(self, '!', size); 137 memset(self, '!', size);
129 138
130 // If the instance is big enough, make it into a fat zombie and have 139 // If the instance is big enough, make it into a fat zombie and have
131 // it remember the old |isa|. Otherwise make it a regular zombie. 140 // it remember the old |isa|. Otherwise make it a regular zombie.
132 // Setting |isa| rather than using |object_setClass()| because that 141 // Setting |isa| rather than using |object_setClass()| because that
133 // function is implemented with a memory barrier. The runtime's 142 // function is implemented with a memory barrier. The runtime's
134 // |_internal_object_dispose()| (in objc-class.m) does this, so it 143 // |_internal_object_dispose()| (in objc-class.m) does this, so it
135 // should be safe (messaging free'd objects shouldn't be expected to 144 // should be safe (messaging free'd objects shouldn't be expected to
136 // be thread-safe in the first place). 145 // be thread-safe in the first place).
137 if (size >= g_fatZombieSize) { 146 if (size >= g_fatZombieSize) {
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
212 } 221 }
213 222
214 // Initialize our globals, returning YES on success. 223 // Initialize our globals, returning YES on success.
215 BOOL ZombieInit() { 224 BOOL ZombieInit() {
216 static BOOL initialized = NO; 225 static BOOL initialized = NO;
217 if (initialized) 226 if (initialized)
218 return YES; 227 return YES;
219 228
220 Class rootClass = [NSObject class]; 229 Class rootClass = [NSObject class];
221 230
222 g_object_cxxDestruct = LookupObjectCxxDestruct(); 231 g_objectDestruct = LookupObjectDestruct();
223 g_originalDeallocIMP = 232 g_originalDeallocIMP =
224 class_getMethodImplementation(rootClass, @selector(dealloc)); 233 class_getMethodImplementation(rootClass, @selector(dealloc));
225 // objc_getClass() so CrZombie doesn't need +class. 234 // objc_getClass() so CrZombie doesn't need +class.
226 g_zombieClass = objc_getClass("CrZombie"); 235 g_zombieClass = objc_getClass("CrZombie");
227 g_fatZombieClass = objc_getClass("CrFatZombie"); 236 g_fatZombieClass = objc_getClass("CrFatZombie");
228 g_fatZombieSize = class_getInstanceSize(g_fatZombieClass); 237 g_fatZombieSize = class_getInstanceSize(g_fatZombieClass);
229 238
230 if (!g_object_cxxDestruct || !g_originalDeallocIMP || 239 if (!g_objectDestruct || !g_originalDeallocIMP ||
231 !g_zombieClass || !g_fatZombieClass) 240 !g_zombieClass || !g_fatZombieClass)
232 return NO; 241 return NO;
233 242
234 initialized = YES; 243 initialized = YES;
235 return YES; 244 return YES;
236 } 245 }
237 246
238 } // namespace 247 } // namespace
239 248
240 @implementation CrZombie 249 @implementation CrZombie
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
405 if (oldZombies) { 414 if (oldZombies) {
406 for (size_t i = 0; i < oldCount; ++i) { 415 for (size_t i = 0; i < oldCount; ++i) {
407 if (oldZombies[i].object) 416 if (oldZombies[i].object)
408 free(oldZombies[i].object); 417 free(oldZombies[i].object);
409 } 418 }
410 free(oldZombies); 419 free(oldZombies);
411 } 420 }
412 } 421 }
413 422
414 } // namespace ObjcEvilDoers 423 } // 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