]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-runtime.mm
b7a25a551c678b4c55d0de53a0b536063779091c
[apple/objc4.git] / runtime / objc-runtime.mm
1 /*
2 * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /***********************************************************************
24 * objc-runtime.m
25 * Copyright 1988-1996, NeXT Software, Inc.
26 * Author: s. naroff
27 *
28 **********************************************************************/
29
30
31
32 /***********************************************************************
33 * Imports.
34 **********************************************************************/
35
36 #include "objc-private.h"
37 #include "objc-loadmethod.h"
38 #include "message.h"
39
40 OBJC_EXPORT Class getOriginalClassForPosingClass(Class);
41
42
43 /***********************************************************************
44 * Exports.
45 **********************************************************************/
46
47 // Settings from environment variables
48 #define OPTION(var, env, help) bool var = false;
49 #include "objc-env.h"
50 #undef OPTION
51
52 struct option_t {
53 bool* var;
54 const char *env;
55 const char *help;
56 size_t envlen;
57 };
58
59 const option_t Settings[] = {
60 #define OPTION(var, env, help) option_t{&var, #env, help, strlen(#env)},
61 #include "objc-env.h"
62 #undef OPTION
63 };
64
65
66 // objc's key for pthread_getspecific
67 static tls_key_t _objc_pthread_key;
68
69 // Selectors
70 SEL SEL_load = NULL;
71 SEL SEL_initialize = NULL;
72 SEL SEL_resolveInstanceMethod = NULL;
73 SEL SEL_resolveClassMethod = NULL;
74 SEL SEL_cxx_construct = NULL;
75 SEL SEL_cxx_destruct = NULL;
76 SEL SEL_retain = NULL;
77 SEL SEL_release = NULL;
78 SEL SEL_autorelease = NULL;
79 SEL SEL_retainCount = NULL;
80 SEL SEL_alloc = NULL;
81 SEL SEL_allocWithZone = NULL;
82 SEL SEL_dealloc = NULL;
83 SEL SEL_copy = NULL;
84 SEL SEL_new = NULL;
85 SEL SEL_finalize = NULL;
86 SEL SEL_forwardInvocation = NULL;
87 SEL SEL_tryRetain = NULL;
88 SEL SEL_isDeallocating = NULL;
89 SEL SEL_retainWeakReference = NULL;
90 SEL SEL_allowsWeakReference = NULL;
91
92
93 header_info *FirstHeader = 0; // NULL means empty list
94 header_info *LastHeader = 0; // NULL means invalid; recompute it
95 int HeaderCount = 0;
96
97
98 /***********************************************************************
99 * objc_getClass. Return the id of the named class. If the class does
100 * not exist, call _objc_classLoader and then objc_classHandler, either of
101 * which may create a new class.
102 * Warning: doesn't work if aClassName is the name of a posed-for class's isa!
103 **********************************************************************/
104 Class objc_getClass(const char *aClassName)
105 {
106 if (!aClassName) return Nil;
107
108 // NO unconnected, YES class handler
109 return look_up_class(aClassName, NO, YES);
110 }
111
112
113 /***********************************************************************
114 * objc_getRequiredClass.
115 * Same as objc_getClass, but kills the process if the class is not found.
116 * This is used by ZeroLink, where failing to find a class would be a
117 * compile-time link error without ZeroLink.
118 **********************************************************************/
119 Class objc_getRequiredClass(const char *aClassName)
120 {
121 Class cls = objc_getClass(aClassName);
122 if (!cls) _objc_fatal("link error: class '%s' not found.", aClassName);
123 return cls;
124 }
125
126
127 /***********************************************************************
128 * objc_lookUpClass. Return the id of the named class.
129 * If the class does not exist, call _objc_classLoader, which may create
130 * a new class.
131 *
132 * Formerly objc_getClassWithoutWarning ()
133 **********************************************************************/
134 Class objc_lookUpClass(const char *aClassName)
135 {
136 if (!aClassName) return Nil;
137
138 // NO unconnected, NO class handler
139 return look_up_class(aClassName, NO, NO);
140 }
141
142
143 /***********************************************************************
144 * objc_getMetaClass. Return the id of the meta class the named class.
145 * Warning: doesn't work if aClassName is the name of a posed-for class's isa!
146 **********************************************************************/
147 Class objc_getMetaClass(const char *aClassName)
148 {
149 Class cls;
150
151 if (!aClassName) return Nil;
152
153 cls = objc_getClass (aClassName);
154 if (!cls)
155 {
156 _objc_inform ("class `%s' not linked into application", aClassName);
157 return Nil;
158 }
159
160 return cls->ISA();
161 }
162
163
164 /***********************************************************************
165 * appendHeader. Add a newly-constructed header_info to the list.
166 **********************************************************************/
167 void appendHeader(header_info *hi)
168 {
169 // Add the header to the header list.
170 // The header is appended to the list, to preserve the bottom-up order.
171 HeaderCount++;
172 hi->next = NULL;
173 if (!FirstHeader) {
174 // list is empty
175 FirstHeader = LastHeader = hi;
176 } else {
177 if (!LastHeader) {
178 // list is not empty, but LastHeader is invalid - recompute it
179 LastHeader = FirstHeader;
180 while (LastHeader->next) LastHeader = LastHeader->next;
181 }
182 // LastHeader is now valid
183 LastHeader->next = hi;
184 LastHeader = hi;
185 }
186 }
187
188
189 /***********************************************************************
190 * removeHeader
191 * Remove the given header from the header list.
192 * FirstHeader is updated.
193 * LastHeader is set to NULL. Any code that uses LastHeader must
194 * detect this NULL and recompute LastHeader by traversing the list.
195 **********************************************************************/
196 void removeHeader(header_info *hi)
197 {
198 header_info **hiP;
199
200 for (hiP = &FirstHeader; *hiP != NULL; hiP = &(**hiP).next) {
201 if (*hiP == hi) {
202 header_info *deadHead = *hiP;
203
204 // Remove from the linked list (updating FirstHeader if necessary).
205 *hiP = (**hiP).next;
206
207 // Update LastHeader if necessary.
208 if (LastHeader == deadHead) {
209 LastHeader = NULL; // will be recomputed next time it's used
210 }
211
212 HeaderCount--;
213 break;
214 }
215 }
216 }
217
218
219 /***********************************************************************
220 * environ_init
221 * Read environment variables that affect the runtime.
222 * Also print environment variable help, if requested.
223 **********************************************************************/
224 void environ_init(void)
225 {
226 if (issetugid()) {
227 // All environment variables are silently ignored when setuid or setgid
228 // This includes OBJC_HELP and OBJC_PRINT_OPTIONS themselves.
229 return;
230 }
231
232 bool PrintHelp = false;
233 bool PrintOptions = false;
234 bool maybeMallocDebugging = false;
235
236 // Scan environ[] directly instead of calling getenv() a lot.
237 // This optimizes the case where none are set.
238 for (char **p = *_NSGetEnviron(); *p != nil; p++) {
239 if (0 == strncmp(*p, "Malloc", 6) || 0 == strncmp(*p, "DYLD", 4) ||
240 0 == strncmp(*p, "NSZombiesEnabled", 16))
241 {
242 maybeMallocDebugging = true;
243 }
244
245 if (0 != strncmp(*p, "OBJC_", 5)) continue;
246
247 if (0 == strncmp(*p, "OBJC_HELP=", 10)) {
248 PrintHelp = true;
249 continue;
250 }
251 if (0 == strncmp(*p, "OBJC_PRINT_OPTIONS=", 19)) {
252 PrintOptions = true;
253 continue;
254 }
255
256 const char *value = strchr(*p, '=');
257 if (!*value) continue;
258 value++;
259
260 for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
261 const option_t *opt = &Settings[i];
262 if ((size_t)(value - *p) == 1+opt->envlen &&
263 0 == strncmp(*p, opt->env, opt->envlen))
264 {
265 *opt->var = (0 == strcmp(value, "YES"));
266 break;
267 }
268 }
269 }
270
271 // Special case: enable some autorelease pool debugging
272 // when some malloc debugging is enabled
273 // and OBJC_DEBUG_POOL_ALLOCATION is not set to something other than NO.
274 if (maybeMallocDebugging) {
275 const char *insert = getenv("DYLD_INSERT_LIBRARIES");
276 const char *zombie = getenv("NSZombiesEnabled");
277 const char *pooldebug = getenv("OBJC_DEBUG_POOL_ALLOCATION");
278 if ((getenv("MallocStackLogging")
279 || getenv("MallocStackLoggingNoCompact")
280 || (zombie && (*zombie == 'Y' || *zombie == 'y'))
281 || (insert && strstr(insert, "libgmalloc")))
282 &&
283 (!pooldebug || 0 == strcmp(pooldebug, "YES")))
284 {
285 DebugPoolAllocation = true;
286 }
287 }
288
289 // Print OBJC_HELP and OBJC_PRINT_OPTIONS output.
290 if (PrintHelp || PrintOptions) {
291 if (PrintHelp) {
292 _objc_inform("Objective-C runtime debugging. Set variable=YES to enable.");
293 _objc_inform("OBJC_HELP: describe available environment variables");
294 if (PrintOptions) {
295 _objc_inform("OBJC_HELP is set");
296 }
297 _objc_inform("OBJC_PRINT_OPTIONS: list which options are set");
298 }
299 if (PrintOptions) {
300 _objc_inform("OBJC_PRINT_OPTIONS is set");
301 }
302
303 for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
304 const option_t *opt = &Settings[i];
305 if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
306 if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
307 }
308 }
309 }
310
311
312 /***********************************************************************
313 * logReplacedMethod
314 * OBJC_PRINT_REPLACED_METHODS implementation
315 **********************************************************************/
316 void
317 logReplacedMethod(const char *className, SEL s,
318 bool isMeta, const char *catName,
319 IMP oldImp, IMP newImp)
320 {
321 const char *oldImage = "??";
322 const char *newImage = "??";
323
324 // Silently ignore +load replacement because category +load is special
325 if (s == SEL_load) return;
326
327 #if TARGET_OS_WIN32
328 // don't know dladdr()/dli_fname equivalent
329 #else
330 Dl_info dl;
331
332 if (dladdr((void*)oldImp, &dl) && dl.dli_fname) oldImage = dl.dli_fname;
333 if (dladdr((void*)newImp, &dl) && dl.dli_fname) newImage = dl.dli_fname;
334 #endif
335
336 _objc_inform("REPLACED: %c[%s %s] %s%s (IMP was %p (%s), now %p (%s))",
337 isMeta ? '+' : '-', className, sel_getName(s),
338 catName ? "by category " : "", catName ? catName : "",
339 oldImp, oldImage, newImp, newImage);
340 }
341
342
343
344 /***********************************************************************
345 * objc_setMultithreaded.
346 **********************************************************************/
347 void objc_setMultithreaded (BOOL flag)
348 {
349 OBJC_WARN_DEPRECATED;
350
351 // Nothing here. Thread synchronization in the runtime is always active.
352 }
353
354
355 /***********************************************************************
356 * _objc_fetch_pthread_data
357 * Fetch objc's pthread data for this thread.
358 * If the data doesn't exist yet and create is NO, return NULL.
359 * If the data doesn't exist yet and create is YES, allocate and return it.
360 **********************************************************************/
361 _objc_pthread_data *_objc_fetch_pthread_data(bool create)
362 {
363 _objc_pthread_data *data;
364
365 data = (_objc_pthread_data *)tls_get(_objc_pthread_key);
366 if (!data && create) {
367 data = (_objc_pthread_data *)
368 calloc(1, sizeof(_objc_pthread_data));
369 tls_set(_objc_pthread_key, data);
370 }
371
372 return data;
373 }
374
375
376 /***********************************************************************
377 * _objc_pthread_destroyspecific
378 * Destructor for objc's per-thread data.
379 * arg shouldn't be NULL, but we check anyway.
380 **********************************************************************/
381 extern void _destroyInitializingClassList(struct _objc_initializing_classes *list);
382 void _objc_pthread_destroyspecific(void *arg)
383 {
384 _objc_pthread_data *data = (_objc_pthread_data *)arg;
385 if (data != NULL) {
386 _destroyInitializingClassList(data->initializingClasses);
387 _destroySyncCache(data->syncCache);
388 _destroyAltHandlerList(data->handlerList);
389 for (int i = 0; i < (int)countof(data->printableNames); i++) {
390 if (data->printableNames[i]) {
391 free(data->printableNames[i]);
392 }
393 }
394
395 // add further cleanup here...
396
397 free(data);
398 }
399 }
400
401
402 void tls_init(void)
403 {
404 #if SUPPORT_DIRECT_THREAD_KEYS
405 _objc_pthread_key = TLS_DIRECT_KEY;
406 pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
407 #else
408 _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
409 #endif
410 }
411
412
413 /***********************************************************************
414 * _objcInit
415 * Former library initializer. This function is now merely a placeholder
416 * for external callers. All runtime initialization has now been moved
417 * to map_images() and _objc_init.
418 **********************************************************************/
419 void _objcInit(void)
420 {
421 // do nothing
422 }
423
424
425 /***********************************************************************
426 * objc_setForwardHandler
427 **********************************************************************/
428
429 #if !__OBJC2__
430
431 // Default forward handler (nil) goes to forward:: dispatch.
432 void *_objc_forward_handler = nil;
433 void *_objc_forward_stret_handler = nil;
434
435 #else
436
437 // Default forward handler halts the process.
438 __attribute__((noreturn)) void
439 objc_defaultForwardHandler(id self, SEL sel)
440 {
441 _objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
442 "(no message forward handler is installed)",
443 class_isMetaClass(object_getClass(self)) ? '+' : '-',
444 object_getClassName(self), sel_getName(sel), self);
445 }
446 void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
447
448 #if SUPPORT_STRET
449 struct stret { int i[100]; };
450 __attribute__((noreturn)) struct stret
451 objc_defaultForwardStretHandler(id self, SEL sel)
452 {
453 objc_defaultForwardHandler(self, sel);
454 }
455 void *_objc_forward_stret_handler = (void*)objc_defaultForwardStretHandler;
456 #endif
457
458 #endif
459
460 void objc_setForwardHandler(void *fwd, void *fwd_stret)
461 {
462 _objc_forward_handler = fwd;
463 #if SUPPORT_STRET
464 _objc_forward_stret_handler = fwd_stret;
465 #endif
466 }
467
468
469 #if !__OBJC2__
470 // GrP fixme
471 extern "C" Class _objc_getOrigClass(const char *name);
472 #endif
473 const char *class_getImageName(Class cls)
474 {
475 #if TARGET_OS_WIN32
476 TCHAR *szFileName;
477 DWORD charactersCopied;
478 Class origCls;
479 HMODULE classModule;
480 bool res;
481 #endif
482 if (!cls) return NULL;
483
484 #if !__OBJC2__
485 cls = _objc_getOrigClass(cls->demangledName());
486 #endif
487 #if TARGET_OS_WIN32
488 charactersCopied = 0;
489 szFileName = malloc(MAX_PATH * sizeof(TCHAR));
490
491 origCls = objc_getOrigClass(cls->demangledName());
492 classModule = NULL;
493 res = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)origCls, &classModule);
494 if (res && classModule) {
495 charactersCopied = GetModuleFileName(classModule, szFileName, MAX_PATH * sizeof(TCHAR));
496 }
497 if (classModule) FreeLibrary(classModule);
498 if (charactersCopied) {
499 return (const char *)szFileName;
500 } else {
501 free(szFileName);
502 }
503 return NULL;
504 #else
505 return dyld_image_path_containing_address(cls);
506 #endif
507 }
508
509
510 const char **objc_copyImageNames(unsigned int *outCount)
511 {
512 header_info *hi;
513 int count = 0;
514 int max = HeaderCount;
515 #if TARGET_OS_WIN32
516 const TCHAR **names = (const TCHAR **)calloc(max+1, sizeof(TCHAR *));
517 #else
518 const char **names = (const char **)calloc(max+1, sizeof(char *));
519 #endif
520
521 for (hi = FirstHeader; hi != NULL && count < max; hi = hi->next) {
522 #if TARGET_OS_WIN32
523 if (hi->moduleName) {
524 names[count++] = hi->moduleName;
525 }
526 #else
527 if (hi->fname) {
528 names[count++] = hi->fname;
529 }
530 #endif
531 }
532 names[count] = NULL;
533
534 if (count == 0) {
535 // Return NULL instead of empty list if there are no images
536 free((void *)names);
537 names = NULL;
538 }
539
540 if (outCount) *outCount = count;
541 return names;
542 }
543
544
545 /**********************************************************************
546 *
547 **********************************************************************/
548 const char **
549 objc_copyClassNamesForImage(const char *image, unsigned int *outCount)
550 {
551 header_info *hi;
552
553 if (!image) {
554 if (outCount) *outCount = 0;
555 return NULL;
556 }
557
558 // Find the image.
559 for (hi = FirstHeader; hi != NULL; hi = hi->next) {
560 #if TARGET_OS_WIN32
561 if (0 == wcscmp((TCHAR *)image, hi->moduleName)) break;
562 #else
563 if (0 == strcmp(image, hi->fname)) break;
564 #endif
565 }
566
567 if (!hi) {
568 if (outCount) *outCount = 0;
569 return NULL;
570 }
571
572 return _objc_copyClassNamesForImage(hi, outCount);
573 }
574
575
576 /**********************************************************************
577 * Fast Enumeration Support
578 **********************************************************************/
579
580 static void (*enumerationMutationHandler)(id);
581
582 /**********************************************************************
583 * objc_enumerationMutation
584 * called by compiler when a mutation is detected during foreach iteration
585 **********************************************************************/
586 void objc_enumerationMutation(id object) {
587 if (enumerationMutationHandler == nil) {
588 _objc_fatal("mutation detected during 'for(... in ...)' enumeration of object %p.", (void*)object);
589 }
590 (*enumerationMutationHandler)(object);
591 }
592
593
594 /**********************************************************************
595 * objc_setEnumerationMutationHandler
596 * an entry point to customize mutation error handing
597 **********************************************************************/
598 void objc_setEnumerationMutationHandler(void (*handler)(id)) {
599 enumerationMutationHandler = handler;
600 }
601
602
603 /**********************************************************************
604 * Associative Reference Support
605 **********************************************************************/
606
607 id objc_getAssociatedObject_non_gc(id object, const void *key) {
608 return _object_get_associative_reference(object, (void *)key);
609 }
610
611
612 void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy) {
613 _object_set_associative_reference(object, (void *)key, value, policy);
614 }
615
616
617 #if SUPPORT_GC
618
619 id objc_getAssociatedObject_gc(id object, const void *key) {
620 // auto_zone doesn't handle tagged pointer objects. Track it ourselves.
621 if (object->isTaggedPointer()) return objc_getAssociatedObject_non_gc(object, key);
622
623 return (id)auto_zone_get_associative_ref(gc_zone, object, (void *)key);
624 }
625
626 void objc_setAssociatedObject_gc(id object, const void *key, id value, objc_AssociationPolicy policy) {
627 // auto_zone doesn't handle tagged pointer objects. Track it ourselves.
628 if (object->isTaggedPointer()) return objc_setAssociatedObject_non_gc(object, key, value, policy);
629
630 if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) {
631 value = ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
632 }
633 auto_zone_set_associative_ref(gc_zone, object, (void *)key, value);
634 }
635
636 // objc_setAssociatedObject and objc_getAssociatedObject are
637 // resolver functions in objc-auto.mm.
638
639 #else
640
641 id
642 objc_getAssociatedObject(id object, const void *key)
643 {
644 return objc_getAssociatedObject_non_gc(object, key);
645 }
646
647 void
648 objc_setAssociatedObject(id object, const void *key, id value,
649 objc_AssociationPolicy policy)
650 {
651 objc_setAssociatedObject_non_gc(object, key, value, policy);
652 }
653
654 #endif
655
656
657 void objc_removeAssociatedObjects(id object)
658 {
659 #if SUPPORT_GC
660 if (UseGC) {
661 auto_zone_erase_associative_refs(gc_zone, object);
662 } else
663 #endif
664 {
665 if (object && object->hasAssociatedObjects()) {
666 _object_remove_assocations(object);
667 }
668 }
669 }
670