]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-class.m
0b4dc990037b886f6e9ebc04482f6432fd805480
[apple/objc4.git] / runtime / objc-class.m
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /***********************************************************************
26 * objc-class.m
27 * Copyright 1988-1997, Apple Computer, Inc.
28 * Author: s. naroff
29 **********************************************************************/
30
31
32 /***********************************************************************
33 * Method cache locking (GrP 2001-1-14)
34 *
35 * For speed, objc_msgSend does not acquire any locks when it reads
36 * method caches. Instead, all cache changes are performed so that any
37 * objc_msgSend running concurrently with the cache mutator will not
38 * crash or hang or get an incorrect result from the cache.
39 *
40 * When cache memory becomes unused (e.g. the old cache after cache
41 * expansion), it is not immediately freed, because a concurrent
42 * objc_msgSend could still be using it. Instead, the memory is
43 * disconnected from the data structures and placed on a garbage list.
44 * The memory is now only accessible to instances of objc_msgSend that
45 * were running when the memory was disconnected; any further calls to
46 * objc_msgSend will not see the garbage memory because the other data
47 * structures don't point to it anymore. The collecting_in_critical
48 * function checks the PC of all threads and returns FALSE when all threads
49 * are found to be outside objc_msgSend. This means any call to objc_msgSend
50 * that could have had access to the garbage has finished or moved past the
51 * cache lookup stage, so it is safe to free the memory.
52 *
53 * All functions that modify cache data or structures must acquire the
54 * cacheUpdateLock to prevent interference from concurrent modifications.
55 * The function that frees cache garbage must acquire the cacheUpdateLock
56 * and use collecting_in_critical() to flush out cache readers.
57 *
58 * Cache readers (PC-checked by collecting_in_critical())
59 * objc_msgSend*
60 * _cache_getImp
61 * _cache_getMethod
62 *
63 * Cache writers (hold cacheUpdateLock while reading or writing; not PC-checked)
64 * _cache_fill (acquires lock)
65 * _cache_expand (only called from cache_fill)
66 * _cache_create (only called from cache_expand)
67 * bcopy (only called from instrumented cache_expand)
68 * flush_caches (acquires lock)
69 * _cache_flush (only called from cache_fill and flush_caches)
70 * _cache_collect_free (only called from cache_expand and cache_flush)
71 *
72 * UNPROTECTED cache readers (NOT thread-safe; used for debug info only)
73 * _cache_print
74 * _class_printMethodCaches
75 * _class_printDuplicateCacheEntries
76 * _class_printMethodCacheStatistics
77 *
78 * _class_lookupMethodAndLoadCache is a special case. It may read a
79 * method triplet out of one cache and store it in another cache. This
80 * is unsafe if the method triplet is a forward:: entry, because the
81 * triplet itself could be freed unless _class_lookupMethodAndLoadCache
82 * were PC-checked or used a lock. Additionally, storing the method
83 * triplet in both caches would result in double-freeing if both caches
84 * were flushed or expanded. The solution is for _cache_getMethod to
85 * ignore all entries whose implementation is _objc_msgForward, so
86 * _class_lookupMethodAndLoadCache cannot look at a forward:: entry
87 * unsafely or place it in multiple caches.
88 ***********************************************************************/
89
90 /***********************************************************************
91 * Thread-safety during class initialization (GrP 2001-9-24)
92 *
93 * Initial state: CLS_INITIALIZING and CLS_INITIALIZED both clear.
94 * During initialization: CLS_INITIALIZING is set
95 * After initialization: CLS_INITIALIZING clear and CLS_INITIALIZED set.
96 * CLS_INITIALIZING and CLS_INITIALIZED are never set at the same time.
97 * CLS_INITIALIZED is never cleared once set.
98 *
99 * Only one thread is allowed to actually initialize a class and send
100 * +initialize. Enforced by allowing only one thread to set CLS_INITIALIZING.
101 *
102 * Additionally, threads trying to send messages to a class must wait for
103 * +initialize to finish. During initialization of a class, that class's
104 * method cache is kept empty. objc_msgSend will revert to
105 * class_lookupMethodAndLoadCache, which checks CLS_INITIALIZED before
106 * messaging. If CLS_INITIALIZED is clear but CLS_INITIALIZING is set,
107 * the thread must block, unless it is the thread that started
108 * initializing the class in the first place.
109 *
110 * Each thread keeps a list of classes it's initializing.
111 * The global classInitLock is used to synchronize changes to CLS_INITIALIZED
112 * and CLS_INITIALIZING: the transition to CLS_INITIALIZING must be
113 * an atomic test-and-set with respect to itself and the transition
114 * to CLS_INITIALIZED.
115 * The global classInitWaitCond is used to block threads waiting for an
116 * initialization to complete. The classInitLock synchronizes
117 * condition checking and the condition variable.
118 **********************************************************************/
119
120 /***********************************************************************
121 * +initialize deadlock case when a class is marked initializing while
122 * its superclass is initialized. Solved by completely initializing
123 * superclasses before beginning to initialize a class.
124 *
125 * OmniWeb class hierarchy:
126 * OBObject
127 * | ` OBPostLoader
128 * OFObject
129 * / \
130 * OWAddressEntry OWController
131 * |
132 * OWConsoleController
133 *
134 * Thread 1 (evil testing thread):
135 * initialize OWAddressEntry
136 * super init OFObject
137 * super init OBObject
138 * [OBObject initialize] runs OBPostLoader, which inits lots of classes...
139 * initialize OWConsoleController
140 * super init OWController - wait for Thread 2 to finish OWController init
141 *
142 * Thread 2 (normal OmniWeb thread):
143 * initialize OWController
144 * super init OFObject - wait for Thread 1 to finish OFObject init
145 *
146 * deadlock!
147 *
148 * Solution: fully initialize super classes before beginning to initialize
149 * a subclass. Then the initializing+initialized part of the class hierarchy
150 * will be a contiguous subtree starting at the root, so other threads
151 * can't jump into the middle between two initializing classes, and we won't
152 * get stuck while a superclass waits for its subclass which waits for the
153 * superclass.
154 **********************************************************************/
155
156
157
158 /***********************************************************************
159 * Imports.
160 **********************************************************************/
161
162 #import <mach/mach_interface.h>
163 #include <mach-o/ldsyms.h>
164 #include <mach-o/dyld.h>
165
166 #include <sys/types.h>
167 #include <unistd.h>
168 #include <stdlib.h>
169 #include <sys/uio.h>
170 #include <sys/fcntl.h>
171
172 #import "objc-class.h"
173
174 #import <objc/Object.h>
175 #import <objc/objc-runtime.h>
176 #import "objc-private.h"
177 #import "hashtable2.h"
178 #import "maptable.h"
179
180 #include <sys/types.h>
181
182 #include <CoreFoundation/CFDictionary.h>
183
184 // Needed functions not in any header file
185 size_t malloc_size (const void * ptr);
186
187 // Needed kernel interface
188 #import <mach/mach.h>
189 #import <mach/thread_status.h>
190
191
192 /***********************************************************************
193 * Conditionals.
194 **********************************************************************/
195
196 // Define PRELOAD_SUPERCLASS_CACHES to cause method lookups to add the
197 // method the appropriate superclass caches, in addition to the normal
198 // encaching in the subclass where the method was messaged. Doing so
199 // will speed up messaging the same method from instances of the
200 // superclasses, but also uses up valuable cache space for a speculative
201 // purpose
202 // See radar 2364264 about incorrectly propogating _objc_forward entries
203 // and double freeing them, first, before turning this on!
204 // (Radar 2364264 is now "inactive".)
205 // Double-freeing is also a potential problem when this is off. See
206 // note about _class_lookupMethodAndLoadCache in "Method cache locking".
207 //#define PRELOAD_SUPERCLASS_CACHES
208
209 /***********************************************************************
210 * Exports.
211 **********************************************************************/
212
213 #ifdef OBJC_INSTRUMENTED
214 enum {
215 CACHE_HISTOGRAM_SIZE = 512
216 };
217
218 unsigned int CacheHitHistogram [CACHE_HISTOGRAM_SIZE];
219 unsigned int CacheMissHistogram [CACHE_HISTOGRAM_SIZE];
220 #endif
221
222 /***********************************************************************
223 * Constants and macros internal to this module.
224 **********************************************************************/
225
226 // INIT_CACHE_SIZE and INIT_META_CACHE_SIZE must be a power of two
227 enum {
228 INIT_CACHE_SIZE_LOG2 = 2,
229 INIT_META_CACHE_SIZE_LOG2 = 2,
230 INIT_CACHE_SIZE = (1 << INIT_CACHE_SIZE_LOG2),
231 INIT_META_CACHE_SIZE = (1 << INIT_META_CACHE_SIZE_LOG2)
232 };
233
234 // Amount of space required for count hash table buckets, knowing that
235 // one entry is embedded in the cache structure itself
236 #define TABLE_SIZE(count) ((count - 1) * sizeof(Method))
237
238 // A sentinal (magic value) to report bad thread_get_state status
239 #define PC_SENTINAL 0
240
241
242 /***********************************************************************
243 * Types internal to this module.
244 **********************************************************************/
245
246 #ifdef OBJC_INSTRUMENTED
247 struct CacheInstrumentation
248 {
249 unsigned int hitCount; // cache lookup success tally
250 unsigned int hitProbes; // sum entries checked to hit
251 unsigned int maxHitProbes; // max entries checked to hit
252 unsigned int missCount; // cache lookup no-find tally
253 unsigned int missProbes; // sum entries checked to miss
254 unsigned int maxMissProbes; // max entries checked to miss
255 unsigned int flushCount; // cache flush tally
256 unsigned int flushedEntries; // sum cache entries flushed
257 unsigned int maxFlushedEntries; // max cache entries flushed
258 };
259 typedef struct CacheInstrumentation CacheInstrumentation;
260
261 // Cache instrumentation data follows table, so it is most compatible
262 #define CACHE_INSTRUMENTATION(cache) (CacheInstrumentation *) &cache->buckets[cache->mask + 1];
263 #endif
264
265 /***********************************************************************
266 * Function prototypes internal to this module.
267 **********************************************************************/
268
269 static Ivar class_getVariable (Class cls, const char * name);
270 static void flush_caches (Class cls, BOOL flush_meta);
271 static void addClassToOriginalClass (Class posingClass, Class originalClass);
272 static void _objc_addOrigClass (Class origClass);
273 static void _freedHandler (id self, SEL sel);
274 static void _nonexistentHandler (id self, SEL sel);
275 static void class_initialize (Class cls);
276 static Cache _cache_expand (Class cls);
277 static int LogObjCMessageSend (BOOL isClassMethod, const char * objectsClass, const char * implementingClass, SEL selector);
278 static BOOL _cache_fill (Class cls, Method smt, SEL sel);
279 static void _cache_addForwardEntry(Class cls, SEL sel);
280 static void _cache_flush (Class cls);
281 static int SubtypeUntil (const char * type, char end);
282 static const char * SkipFirstType (const char * type);
283
284 static unsigned long _get_pc_for_thread (mach_port_t thread);
285 static int _collecting_in_critical (void);
286 static void _garbage_make_room (void);
287 static void _cache_collect_free (void * data, BOOL tryCollect);
288
289 static void _cache_print (Cache cache);
290 static unsigned int log2 (unsigned int x);
291 static void PrintCacheHeader (void);
292 #ifdef OBJC_INSTRUMENTED
293 static void PrintCacheHistogram (char * title, unsigned int * firstEntry, unsigned int entryCount);
294 #endif
295
296 /***********************************************************************
297 * Static data internal to this module.
298 **********************************************************************/
299
300 // When _class_uncache is non-zero, cache growth copies the existing
301 // entries into the new (larger) cache. When this flag is zero, new
302 // (larger) caches start out empty.
303 static int _class_uncache = 1;
304
305 // When _class_slow_grow is non-zero, any given cache is actually grown
306 // only on the odd-numbered times it becomes full; on the even-numbered
307 // times, it is simply emptied and re-used. When this flag is zero,
308 // caches are grown every time.
309 static int _class_slow_grow = 1;
310
311 // Lock for cache access.
312 // Held when modifying a cache in place.
313 // Held when installing a new cache on a class.
314 // Held when adding to the cache garbage list.
315 // Held when disposing cache garbage.
316 // See "Method cache locking" above for notes about cache locking.
317 static OBJC_DECLARE_LOCK(cacheUpdateLock);
318
319 // classInitLock protects classInitWaitCond and examination and modification
320 // of CLS_INITIALIZED and CLS_INITIALIZING.
321 OBJC_DECLARE_LOCK(classInitLock);
322 // classInitWaitCond is signalled when any class is done initializing.
323 // Threads that are waiting for a class to finish initializing wait on this.
324 pthread_cond_t classInitWaitCond = PTHREAD_COND_INITIALIZER;
325
326 CFMutableDictionaryRef _classIMPTables = NULL;
327
328 // When traceDuplicates is non-zero, _cacheFill checks whether the method
329 // being encached is already there. The number of times it finds a match
330 // is tallied in cacheFillDuplicates. When traceDuplicatesVerbose is
331 // non-zero, each duplication is logged when found in this way.
332 static int traceDuplicates = 0;
333 static int traceDuplicatesVerbose = 0;
334 static int cacheFillDuplicates = 0;
335
336 #ifdef OBJC_INSTRUMENTED
337 // Instrumentation
338 static unsigned int LinearFlushCachesCount = 0;
339 static unsigned int LinearFlushCachesVisitedCount = 0;
340 static unsigned int MaxLinearFlushCachesVisitedCount = 0;
341 static unsigned int NonlinearFlushCachesCount = 0;
342 static unsigned int NonlinearFlushCachesClassCount = 0;
343 static unsigned int NonlinearFlushCachesVisitedCount = 0;
344 static unsigned int MaxNonlinearFlushCachesVisitedCount = 0;
345 static unsigned int IdealFlushCachesCount = 0;
346 static unsigned int MaxIdealFlushCachesCount = 0;
347 #endif
348
349 // Method call logging
350 typedef int (*ObjCLogProc)(BOOL, const char *, const char *, SEL);
351
352 static int totalCacheFills = 0;
353 static int objcMsgLogFD = (-1);
354 static ObjCLogProc objcMsgLogProc = &LogObjCMessageSend;
355 static int objcMsgLogEnabled = 0;
356
357 // Error Messages
358 static const char
359 _errNoMem[] = "failed -- out of memory(%s, %u)",
360 _errAllocNil[] = "allocating nil object",
361 _errFreedObject[] = "message %s sent to freed object=0x%lx",
362 _errNonExistentObject[] = "message %s sent to non-existent object=0x%lx",
363 _errBadSel[] = "invalid selector %s",
364 _errNotSuper[] = "[%s poseAs:%s]: target not immediate superclass",
365 _errNewVars[] = "[%s poseAs:%s]: %s defines new instance variables";
366
367 /***********************************************************************
368 * Information about multi-thread support:
369 *
370 * Since we do not lock many operations which walk the superclass, method
371 * and ivar chains, these chains must remain intact once a class is published
372 * by inserting it into the class hashtable. All modifications must be
373 * atomic so that someone walking these chains will always geta valid
374 * result.
375 ***********************************************************************/
376 /***********************************************************************
377 * A static empty cache. All classes initially point at this cache.
378 * When the first message is sent it misses in the cache, and when
379 * the cache is grown it checks for this case and uses malloc rather
380 * than realloc. This avoids the need to check for NULL caches in the
381 * messenger.
382 ***********************************************************************/
383
384 const struct objc_cache emptyCache =
385 {
386 0, // mask
387 0, // occupied
388 { NULL } // buckets
389 };
390
391 // Freed objects have their isa set to point to this dummy class.
392 // This avoids the need to check for Nil classes in the messenger.
393 static const struct objc_class freedObjectClass =
394 {
395 Nil, // isa
396 Nil, // super_class
397 "FREED(id)", // name
398 0, // version
399 0, // info
400 0, // instance_size
401 NULL, // ivars
402 NULL, // methodLists
403 (Cache) &emptyCache, // cache
404 NULL // protocols
405 };
406
407 static const struct objc_class nonexistentObjectClass =
408 {
409 Nil, // isa
410 Nil, // super_class
411 "NONEXISTENT(id)", // name
412 0, // version
413 0, // info
414 0, // instance_size
415 NULL, // ivars
416 NULL, // methodLists
417 (Cache) &emptyCache, // cache
418 NULL // protocols
419 };
420
421 /***********************************************************************
422 * object_getClassName.
423 **********************************************************************/
424 const char * object_getClassName (id obj)
425 {
426 // Even nil objects have a class name, sort of
427 if (obj == nil)
428 return "nil";
429
430 // Retrieve name from object's class
431 return ((struct objc_class *) obj->isa)->name;
432 }
433
434 /***********************************************************************
435 * object_getIndexedIvars.
436 **********************************************************************/
437 void * object_getIndexedIvars (id obj)
438 {
439 // ivars are tacked onto the end of the object
440 return ((char *) obj) + ((struct objc_class *) obj->isa)->instance_size;
441 }
442
443
444 /***********************************************************************
445 * _internal_class_createInstanceFromZone. Allocate an instance of the
446 * specified class with the specified number of bytes for indexed
447 * variables, in the specified zone. The isa field is set to the
448 * class, all other fields are zeroed.
449 **********************************************************************/
450 static id _internal_class_createInstanceFromZone (Class aClass,
451 unsigned nIvarBytes,
452 void * z)
453 {
454 id obj;
455 register unsigned byteCount;
456
457 // Can't create something for nothing
458 if (aClass == Nil)
459 {
460 __objc_error ((id) aClass, _errAllocNil, 0);
461 return nil;
462 }
463
464 // Allocate and initialize
465 byteCount = ((struct objc_class *) aClass)->instance_size + nIvarBytes;
466 obj = (id) malloc_zone_calloc (z, 1, byteCount);
467 if (!obj)
468 {
469 __objc_error ((id) aClass, _errNoMem, ((struct objc_class *) aClass)->name, nIvarBytes);
470 return nil;
471 }
472
473 // Set the isa pointer
474 obj->isa = aClass;
475 return obj;
476 }
477
478 /***********************************************************************
479 * _internal_class_createInstance. Allocate an instance of the specified
480 * class with the specified number of bytes for indexed variables, in
481 * the default zone, using _internal_class_createInstanceFromZone.
482 **********************************************************************/
483 static id _internal_class_createInstance (Class aClass,
484 unsigned nIvarBytes)
485 {
486 return _internal_class_createInstanceFromZone (aClass,
487 nIvarBytes,
488 malloc_default_zone ());
489 }
490
491 id (*_poseAs)() = (id (*)())class_poseAs;
492 id (*_alloc)(Class, unsigned) = _internal_class_createInstance;
493 id (*_zoneAlloc)(Class, unsigned, void *) = _internal_class_createInstanceFromZone;
494
495 /***********************************************************************
496 * class_createInstanceFromZone. Allocate an instance of the specified
497 * class with the specified number of bytes for indexed variables, in
498 * the specified zone, using _zoneAlloc.
499 **********************************************************************/
500 id class_createInstanceFromZone (Class aClass,
501 unsigned nIvarBytes,
502 void * z)
503 {
504 // _zoneAlloc can be overridden, but is initially set to
505 // _internal_class_createInstanceFromZone
506 return (*_zoneAlloc) (aClass, nIvarBytes, z);
507 }
508
509 /***********************************************************************
510 * class_createInstance. Allocate an instance of the specified class with
511 * the specified number of bytes for indexed variables, using _alloc.
512 **********************************************************************/
513 id class_createInstance (Class aClass,
514 unsigned nIvarBytes)
515 {
516 // _alloc can be overridden, but is initially set to
517 // _internal_class_createInstance
518 return (*_alloc) (aClass, nIvarBytes);
519 }
520
521 /***********************************************************************
522 * class_setVersion. Record the specified version with the class.
523 **********************************************************************/
524 void class_setVersion (Class aClass,
525 int version)
526 {
527 ((struct objc_class *) aClass)->version = version;
528 }
529
530 /***********************************************************************
531 * class_getVersion. Return the version recorded with the class.
532 **********************************************************************/
533 int class_getVersion (Class aClass)
534 {
535 return ((struct objc_class *) aClass)->version;
536 }
537
538 static void _addListIMPsToTable(CFMutableDictionaryRef table, struct objc_method_list *mlist, Class cls, void **iterator) {
539 int i;
540 struct objc_method_list *new_mlist;
541 if (!mlist) return;
542 /* Work from end of list so that categories override */
543 new_mlist = _class_inlinedNextMethodList(cls, iterator);
544 _addListIMPsToTable(table, new_mlist, cls, iterator);
545 for (i = 0; i < mlist->method_count; i++) {
546 CFDictionarySetValue(table, mlist->method_list[i].method_name, mlist->method_list[i].method_imp);
547 }
548 }
549
550 static void _addClassIMPsToTable(CFMutableDictionaryRef table, Class cls) {
551 struct objc_method_list *mlist;
552 void *iterator = 0;
553 #ifdef INCLUDE_SUPER_IMPS_IN_IMP_TABLE
554 if (cls->super_class) { /* Do superclass first so subclass overrides */
555 CFMutableDictionaryRef super_table = CFDictionaryGetValue(_classIMPTables, cls->super_class);
556 if (super_table) {
557 CFIndex cnt;
558 const void **keys, **values, *buffer1[128], *buffer2[128];
559 cnt = CFDictionaryGetCount(super_table);
560 keys = (cnt <= 128) ? buffer1 : CFAllocatorAllocate(NULL, cnt * sizeof(void *), 0);
561 values = (cnt <= 128) ? buffer2 : CFAllocatorAllocate(NULL, cnt * sizeof(void *), 0);
562 CFDictionaryGetKeysAndValues(super_table, keys, values);
563 while (cnt--) {
564 CFDictionarySetValue(table, keys[cnt], values[cnt]);
565 }
566 if (keys != buffer1) CFAllocatorDeallocate(NULL, keys);
567 if (values != buffer2) CFAllocatorDeallocate(NULL, values);
568 } else {
569 _addClassIMPsToTable(table, cls->super_class);
570 }
571 }
572 #endif
573 mlist = _class_inlinedNextMethodList(cls, &iterator);
574 _addListIMPsToTable(table, mlist, cls, &iterator);
575 }
576
577 CFMutableDictionaryRef _getClassIMPTable(Class cls) {
578 CFMutableDictionaryRef table;
579 if (NULL == _classIMPTables) {
580 // maps Classes to mutable dictionaries
581 _classIMPTables = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
582 }
583 table = (CFMutableDictionaryRef)CFDictionaryGetValue(_classIMPTables, cls);
584 // IMP table maps SELs to IMPS
585 if (NULL == table) {
586 table = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
587 _addClassIMPsToTable(table, cls);
588 CFDictionaryAddValue(_classIMPTables, cls, table);
589 }
590 return table;
591 }
592
593 static inline Method _findNamedMethodInList(struct objc_method_list * mlist, const char *meth_name) {
594 int i;
595 if (!mlist) return NULL;
596 for (i = 0; i < mlist->method_count; i++) {
597 Method m = &mlist->method_list[i];
598 if (*((const char *)m->method_name) == *meth_name && 0 == strcmp((const char *)(m->method_name), meth_name)) {
599 return m;
600 }
601 }
602 return NULL;
603 }
604
605 /* These next three functions are the heart of ObjC method lookup. */
606 static inline Method _findMethodInList(struct objc_method_list * mlist, SEL sel) {
607 int i;
608 if (!mlist) return NULL;
609 for (i = 0; i < mlist->method_count; i++) {
610 Method m = &mlist->method_list[i];
611 if (m->method_name == sel) {
612 return m;
613 }
614 }
615 return NULL;
616 }
617
618 static inline Method _findMethodInClass(Class cls, SEL sel) {
619 struct objc_method_list *mlist;
620 void *iterator = 0;
621 while ((mlist = _class_inlinedNextMethodList(cls, &iterator))) {
622 Method m = _findMethodInList(mlist, sel);
623 if (m) return m;
624 }
625 return NULL;
626 }
627
628 static inline Method _getMethod(Class cls, SEL sel) {
629 for (; cls; cls = cls->super_class) {
630 Method m = _findMethodInClass(cls, sel);
631 if (m) return m;
632 }
633 return NULL;
634 }
635
636
637 /***********************************************************************
638 * class_getInstanceMethod. Return the instance method for the
639 * specified class and selector.
640 **********************************************************************/
641 Method class_getInstanceMethod (Class aClass,
642 SEL aSelector)
643 {
644 // Need both a class and a selector
645 if (!aClass || !aSelector)
646 return NULL;
647
648 // Go to the class
649 return _getMethod (aClass, aSelector);
650 }
651
652 /***********************************************************************
653 * class_getClassMethod. Return the class method for the specified
654 * class and selector.
655 **********************************************************************/
656 Method class_getClassMethod (Class aClass,
657 SEL aSelector)
658 {
659 // Need both a class and a selector
660 if (!aClass || !aSelector)
661 return NULL;
662
663 // Go to the class or isa
664 return _getMethod (GETMETA(aClass), aSelector);
665 }
666
667 /***********************************************************************
668 * class_getVariable. Return the named instance variable.
669 **********************************************************************/
670 static Ivar class_getVariable (Class cls,
671 const char * name)
672 {
673 struct objc_class * thisCls;
674
675 // Outer loop - search the class and its superclasses
676 for (thisCls = cls; thisCls != Nil; thisCls = ((struct objc_class *) thisCls)->super_class)
677 {
678 int index;
679 Ivar thisIvar;
680
681 // Skip class having no ivars
682 if (!thisCls->ivars)
683 continue;
684
685 // Inner loop - search the given class
686 thisIvar = &thisCls->ivars->ivar_list[0];
687 for (index = 0; index < thisCls->ivars->ivar_count; index += 1)
688 {
689 // Check this ivar's name. Be careful because the
690 // compiler generates ivar entries with NULL ivar_name
691 // (e.g. for anonymous bit fields).
692 if ((thisIvar->ivar_name) &&
693 (strcmp (name, thisIvar->ivar_name) == 0))
694 return thisIvar;
695
696 // Move to next ivar
697 thisIvar += 1;
698 }
699 }
700
701 // Not found
702 return NULL;
703 }
704
705 /***********************************************************************
706 * class_getInstanceVariable. Return the named instance variable.
707 *
708 * Someday add class_getClassVariable ().
709 **********************************************************************/
710 Ivar class_getInstanceVariable (Class aClass,
711 const char * name)
712 {
713 // Must have a class and a name
714 if (!aClass || !name)
715 return NULL;
716
717 // Look it up
718 return class_getVariable (aClass, name);
719 }
720
721 /***********************************************************************
722 * flush_caches. Flush the instance and optionally class method caches
723 * of cls and all its subclasses.
724 *
725 * Specifying Nil for the class "all classes."
726 **********************************************************************/
727 static void flush_caches(Class cls, BOOL flush_meta)
728 {
729 int numClasses = 0, newNumClasses;
730 struct objc_class * * classes = NULL;
731 int i;
732 struct objc_class * clsObject;
733 #ifdef OBJC_INSTRUMENTED
734 unsigned int classesVisited;
735 unsigned int subclassCount;
736 #endif
737
738 // Do nothing if class has no cache
739 // This check is safe to do without any cache locks.
740 if (cls && !((struct objc_class *) cls)->cache)
741 return;
742
743 newNumClasses = objc_getClassList((Class *)NULL, 0);
744 while (numClasses < newNumClasses) {
745 numClasses = newNumClasses;
746 classes = realloc(classes, sizeof(Class) * numClasses);
747 newNumClasses = objc_getClassList((Class *)classes, numClasses);
748 }
749 numClasses = newNumClasses;
750
751 OBJC_LOCK(&cacheUpdateLock);
752
753 // Handle nil and root instance class specially: flush all
754 // instance and class method caches. Nice that this
755 // loop is linear vs the N-squared loop just below.
756 if (!cls || !((struct objc_class *) cls)->super_class)
757 {
758 #ifdef OBJC_INSTRUMENTED
759 LinearFlushCachesCount += 1;
760 classesVisited = 0;
761 subclassCount = 0;
762 #endif
763 // Traverse all classes in the hash table
764 for (i = 0; i < numClasses; i++)
765 {
766 struct objc_class * metaClsObject;
767 #ifdef OBJC_INSTRUMENTED
768 classesVisited += 1;
769 #endif
770 clsObject = classes[i];
771
772 // Skip class that is known not to be a subclass of this root
773 // (the isa pointer of any meta class points to the meta class
774 // of the root).
775 // NOTE: When is an isa pointer of a hash tabled class ever nil?
776 metaClsObject = clsObject->isa;
777 if (cls && metaClsObject && cls->isa != metaClsObject->isa)
778 {
779 continue;
780 }
781
782 #ifdef OBJC_INSTRUMENTED
783 subclassCount += 1;
784 #endif
785
786 _cache_flush (clsObject);
787 if (flush_meta && metaClsObject != NULL) {
788 _cache_flush (metaClsObject);
789 }
790 }
791 #ifdef OBJC_INSTRUMENTED
792 LinearFlushCachesVisitedCount += classesVisited;
793 if (classesVisited > MaxLinearFlushCachesVisitedCount)
794 MaxLinearFlushCachesVisitedCount = classesVisited;
795 IdealFlushCachesCount += subclassCount;
796 if (subclassCount > MaxIdealFlushCachesCount)
797 MaxIdealFlushCachesCount = subclassCount;
798 #endif
799
800 OBJC_UNLOCK(&cacheUpdateLock);
801 free(classes);
802 return;
803 }
804
805 // Outer loop - flush any cache that could now get a method from
806 // cls (i.e. the cache associated with cls and any of its subclasses).
807 #ifdef OBJC_INSTRUMENTED
808 NonlinearFlushCachesCount += 1;
809 classesVisited = 0;
810 subclassCount = 0;
811 #endif
812 for (i = 0; i < numClasses; i++)
813 {
814 struct objc_class * clsIter;
815
816 #ifdef OBJC_INSTRUMENTED
817 NonlinearFlushCachesClassCount += 1;
818 #endif
819 clsObject = classes[i];
820
821 // Inner loop - Process a given class
822 clsIter = clsObject;
823 while (clsIter)
824 {
825
826 #ifdef OBJC_INSTRUMENTED
827 classesVisited += 1;
828 #endif
829 // Flush clsObject instance method cache if
830 // clsObject is a subclass of cls, or is cls itself
831 // Flush the class method cache if that was asked for
832 if (clsIter == cls)
833 {
834 #ifdef OBJC_INSTRUMENTED
835 subclassCount += 1;
836 #endif
837 _cache_flush (clsObject);
838 if (flush_meta)
839 _cache_flush (clsObject->isa);
840
841 break;
842
843 }
844
845 // Flush clsObject class method cache if cls is
846 // the meta class of clsObject or of one
847 // of clsObject's superclasses
848 else if (clsIter->isa == cls)
849 {
850 #ifdef OBJC_INSTRUMENTED
851 subclassCount += 1;
852 #endif
853 _cache_flush (clsObject->isa);
854 break;
855 }
856
857 // Move up superclass chain
858 else if (ISINITIALIZED(clsIter))
859 clsIter = clsIter->super_class;
860
861 // clsIter is not initialized, so its cache
862 // must be empty. This happens only when
863 // clsIter == clsObject, because
864 // superclasses are initialized before
865 // subclasses, and this loop traverses
866 // from sub- to super- classes.
867 else
868 break;
869 }
870 }
871 #ifdef OBJC_INSTRUMENTED
872 NonlinearFlushCachesVisitedCount += classesVisited;
873 if (classesVisited > MaxNonlinearFlushCachesVisitedCount)
874 MaxNonlinearFlushCachesVisitedCount = classesVisited;
875 IdealFlushCachesCount += subclassCount;
876 if (subclassCount > MaxIdealFlushCachesCount)
877 MaxIdealFlushCachesCount = subclassCount;
878 #endif
879
880 OBJC_UNLOCK(&cacheUpdateLock);
881 free(classes);
882 }
883
884 /***********************************************************************
885 * _objc_flush_caches. Flush the caches of the specified class and any
886 * of its subclasses. If cls is a meta-class, only meta-class (i.e.
887 * class method) caches are flushed. If cls is an instance-class, both
888 * instance-class and meta-class caches are flushed.
889 **********************************************************************/
890 void _objc_flush_caches (Class cls)
891 {
892 flush_caches (cls, YES);
893 }
894
895 /***********************************************************************
896 * do_not_remove_this_dummy_function.
897 **********************************************************************/
898 void do_not_remove_this_dummy_function (void)
899 {
900 (void) class_nextMethodList (NULL, NULL);
901 }
902
903 /***********************************************************************
904 * class_nextMethodList.
905 *
906 * usage:
907 * void * iterator = 0;
908 * while (class_nextMethodList (cls, &iterator)) {...}
909 **********************************************************************/
910 OBJC_EXPORT struct objc_method_list * class_nextMethodList (Class cls,
911 void ** it)
912 {
913 return _class_inlinedNextMethodList(cls, it);
914 }
915
916 /***********************************************************************
917 * _dummy.
918 **********************************************************************/
919 void _dummy (void)
920 {
921 (void) class_nextMethodList (Nil, NULL);
922 }
923
924 /***********************************************************************
925 * class_addMethods.
926 *
927 * Formerly class_addInstanceMethods ()
928 **********************************************************************/
929 void class_addMethods (Class cls,
930 struct objc_method_list * meths)
931 {
932 // Insert atomically.
933 _objc_insertMethods (meths, &((struct objc_class *) cls)->methodLists);
934
935 // Must flush when dynamically adding methods. No need to flush
936 // all the class method caches. If cls is a meta class, though,
937 // this will still flush it and any of its sub-meta classes.
938 flush_caches (cls, NO);
939 }
940
941 /***********************************************************************
942 * class_addClassMethods.
943 *
944 * Obsolete (for binary compatibility only).
945 **********************************************************************/
946 void class_addClassMethods (Class cls,
947 struct objc_method_list * meths)
948 {
949 class_addMethods (((struct objc_class *) cls)->isa, meths);
950 }
951
952 /***********************************************************************
953 * class_removeMethods.
954 **********************************************************************/
955 void class_removeMethods (Class cls,
956 struct objc_method_list * meths)
957 {
958 // Remove atomically.
959 _objc_removeMethods (meths, &((struct objc_class *) cls)->methodLists);
960
961 // Must flush when dynamically removing methods. No need to flush
962 // all the class method caches. If cls is a meta class, though,
963 // this will still flush it and any of its sub-meta classes.
964 flush_caches (cls, NO);
965 }
966
967 /***********************************************************************
968 * addClassToOriginalClass. Add to a hash table of classes involved in
969 * a posing situation. We use this when we need to get to the "original"
970 * class for some particular name through the function objc_getOrigClass.
971 * For instance, the implementation of [super ...] will use this to be
972 * sure that it gets hold of the correct super class, so that no infinite
973 * loops will occur if the class it appears in is involved in posing.
974 *
975 * We use the classLock to guard the hash table.
976 *
977 * See tracker bug #51856.
978 **********************************************************************/
979
980 static NXMapTable * posed_class_hash = NULL;
981 static NXMapTable * posed_class_to_original_class_hash = NULL;
982
983 static void addClassToOriginalClass (Class posingClass,
984 Class originalClass)
985 {
986 // Install hash table when it is first needed
987 if (!posed_class_to_original_class_hash)
988 {
989 posed_class_to_original_class_hash =
990 NXCreateMapTableFromZone (NXPtrValueMapPrototype,
991 8,
992 _objc_create_zone ());
993 }
994
995 // Add pose to hash table
996 NXMapInsert (posed_class_to_original_class_hash,
997 posingClass,
998 originalClass);
999 }
1000
1001 /***********************************************************************
1002 * getOriginalClassForPosingClass.
1003 **********************************************************************/
1004 Class getOriginalClassForPosingClass (Class posingClass)
1005 {
1006 return NXMapGet (posed_class_to_original_class_hash, posingClass);
1007 }
1008
1009 /***********************************************************************
1010 * objc_getOrigClass.
1011 **********************************************************************/
1012 Class objc_getOrigClass (const char * name)
1013 {
1014 struct objc_class * ret;
1015
1016 // Look for class among the posers
1017 ret = Nil;
1018 OBJC_LOCK(&classLock);
1019 if (posed_class_hash)
1020 ret = (Class) NXMapGet (posed_class_hash, name);
1021 OBJC_UNLOCK(&classLock);
1022 if (ret)
1023 return ret;
1024
1025 // Not a poser. Do a normal lookup.
1026 ret = objc_getClass (name);
1027 if (!ret)
1028 _objc_inform ("class `%s' not linked into application", name);
1029
1030 return ret;
1031 }
1032
1033 /***********************************************************************
1034 * _objc_addOrigClass. This function is only used from class_poseAs.
1035 * Registers the original class names, before they get obscured by
1036 * posing, so that [super ..] will work correctly from categories
1037 * in posing classes and in categories in classes being posed for.
1038 **********************************************************************/
1039 static void _objc_addOrigClass (Class origClass)
1040 {
1041 OBJC_LOCK(&classLock);
1042
1043 // Create the poser's hash table on first use
1044 if (!posed_class_hash)
1045 {
1046 posed_class_hash = NXCreateMapTableFromZone (NXStrValueMapPrototype,
1047 8,
1048 _objc_create_zone ());
1049 }
1050
1051 // Add the named class iff it is not already there (or collides?)
1052 if (NXMapGet (posed_class_hash, ((struct objc_class *)origClass)->name) == 0)
1053 NXMapInsert (posed_class_hash, ((struct objc_class *)origClass)->name, origClass);
1054
1055 OBJC_UNLOCK(&classLock);
1056 }
1057
1058 /***********************************************************************
1059 * class_poseAs.
1060 *
1061 * !!! class_poseAs () does not currently flush any caches.
1062 **********************************************************************/
1063 Class class_poseAs (Class imposter,
1064 Class original)
1065 {
1066 struct objc_class * clsObject;
1067 char * imposterNamePtr;
1068 NXHashTable * class_hash;
1069 NXHashState state;
1070 struct objc_class * copy;
1071 #ifdef OBJC_CLASS_REFS
1072 header_info * hInfo;
1073 #endif
1074
1075 // Trivial case is easy
1076 if (imposter == original)
1077 return imposter;
1078
1079 // Imposter must be an immediate subclass of the original
1080 if (((struct objc_class *)imposter)->super_class != original) {
1081 __objc_error(imposter, _errNotSuper, ((struct objc_class *)imposter)->name, ((struct objc_class *)original)->name);
1082 }
1083
1084 // Can't pose when you have instance variables (how could it work?)
1085 if (((struct objc_class *)imposter)->ivars) {
1086 __objc_error(imposter, _errNewVars, ((struct objc_class *)imposter)->name, ((struct objc_class *)original)->name, ((struct objc_class *)imposter)->name);
1087 }
1088
1089 // Build a string to use to replace the name of the original class.
1090 #define imposterNamePrefix "_%"
1091 imposterNamePtr = malloc_zone_malloc(_objc_create_zone(), strlen(((struct objc_class *)original)->name) + strlen(imposterNamePrefix) + 1);
1092 strcpy(imposterNamePtr, imposterNamePrefix);
1093 strcat(imposterNamePtr, ((struct objc_class *)original)->name);
1094 #undef imposterNamePrefix
1095
1096 // We lock the class hashtable, so we are thread safe with respect to
1097 // calls to objc_getClass (). However, the class names are not
1098 // changed atomically, nor are all of the subclasses updated
1099 // atomically. I have ordered the operations so that you will
1100 // never crash, but you may get inconsistent results....
1101
1102 // Register the original class so that [super ..] knows
1103 // exactly which classes are the "original" classes.
1104 _objc_addOrigClass (original);
1105 _objc_addOrigClass (imposter);
1106
1107 OBJC_LOCK(&classLock);
1108
1109 class_hash = objc_getClasses ();
1110
1111 // Remove both the imposter and the original class.
1112 NXHashRemove (class_hash, imposter);
1113 NXHashRemove (class_hash, original);
1114
1115 // Copy the imposter, so that the imposter can continue
1116 // its normal life in addition to changing the behavior of
1117 // the original. As a hack we don't bother to copy the metaclass.
1118 // For some reason we modify the original rather than the copy.
1119 copy = (*_zoneAlloc)(imposter->isa, sizeof(struct objc_class), _objc_create_zone());
1120 memmove(copy, imposter, sizeof(struct objc_class));
1121
1122 NXHashInsert (class_hash, copy);
1123 addClassToOriginalClass (imposter, copy);
1124
1125 // Mark the imposter as such
1126 CLS_SETINFO(((struct objc_class *)imposter), CLS_POSING);
1127 CLS_SETINFO(((struct objc_class *)imposter)->isa, CLS_POSING);
1128
1129 // Change the name of the imposter to that of the original class.
1130 ((struct objc_class *)imposter)->name = ((struct objc_class *)original)->name;
1131 ((struct objc_class *)imposter)->isa->name = ((struct objc_class *)original)->isa->name;
1132
1133 // Also copy the version field to avoid archiving problems.
1134 ((struct objc_class *)imposter)->version = ((struct objc_class *)original)->version;
1135
1136 // Change all subclasses of the original to point to the imposter.
1137 state = NXInitHashState (class_hash);
1138 while (NXNextHashState (class_hash, &state, (void **) &clsObject))
1139 {
1140 while ((clsObject) && (clsObject != imposter) &&
1141 (clsObject != copy))
1142 {
1143 if (clsObject->super_class == original)
1144 {
1145 clsObject->super_class = imposter;
1146 clsObject->isa->super_class = ((struct objc_class *)imposter)->isa;
1147 // We must flush caches here!
1148 break;
1149 }
1150
1151 clsObject = clsObject->super_class;
1152 }
1153 }
1154
1155 #ifdef OBJC_CLASS_REFS
1156 // Replace the original with the imposter in all class refs
1157 // Major loop - process all headers
1158 for (hInfo = _objc_headerStart(); hInfo != NULL; hInfo = hInfo->next)
1159 {
1160 Class * cls_refs;
1161 unsigned int refCount;
1162 unsigned int index;
1163
1164 // Get refs associated with this header
1165 cls_refs = (Class *) _getObjcClassRefs ((headerType *) hInfo->mhdr, &refCount);
1166 if (!cls_refs || !refCount)
1167 continue;
1168
1169 // Minor loop - process this header's refs
1170 cls_refs = (Class *) ((unsigned long) cls_refs + hInfo->image_slide);
1171 for (index = 0; index < refCount; index += 1)
1172 {
1173 if (cls_refs[index] == original)
1174 cls_refs[index] = imposter;
1175 }
1176 }
1177 #endif // OBJC_CLASS_REFS
1178
1179 // Change the name of the original class.
1180 ((struct objc_class *)original)->name = imposterNamePtr + 1;
1181 ((struct objc_class *)original)->isa->name = imposterNamePtr;
1182
1183 // Restore the imposter and the original class with their new names.
1184 NXHashInsert (class_hash, imposter);
1185 NXHashInsert (class_hash, original);
1186
1187 OBJC_UNLOCK(&classLock);
1188
1189 return imposter;
1190 }
1191
1192 /***********************************************************************
1193 * _freedHandler.
1194 **********************************************************************/
1195 static void _freedHandler (id self,
1196 SEL sel)
1197 {
1198 __objc_error (self, _errFreedObject, SELNAME(sel), self);
1199 }
1200
1201 /***********************************************************************
1202 * _nonexistentHandler.
1203 **********************************************************************/
1204 static void _nonexistentHandler (id self,
1205 SEL sel)
1206 {
1207 __objc_error (self, _errNonExistentObject, SELNAME(sel), self);
1208 }
1209
1210 /***********************************************************************
1211 * _class_install_relationships. Fill in the class pointers of a class
1212 * that was loaded before some or all of the classes it needs to point to.
1213 * The deal here is that the class pointer fields have been usurped to
1214 * hold the string name of the pertinent class. Our job is to look up
1215 * the real thing based on those stored names.
1216 **********************************************************************/
1217 void _class_install_relationships (Class cls,
1218 long version)
1219 {
1220 struct objc_class * meta;
1221 struct objc_class * clstmp;
1222
1223 // Get easy access to meta class structure
1224 meta = ((struct objc_class *)cls)->isa;
1225
1226 // Set version in meta class strucure
1227 meta->version = version;
1228
1229 // Install superclass based on stored name. No name iff
1230 // cls is a root class.
1231 if (((struct objc_class *)cls)->super_class)
1232 {
1233 clstmp = objc_getClass ((const char *) ((struct objc_class *)cls)->super_class);
1234 if (!clstmp)
1235 {
1236 _objc_inform("failed objc_getClass(%s) for %s->super_class", (const char *)((struct objc_class *)cls)->super_class, ((struct objc_class *)cls)->name);
1237 goto Error;
1238 }
1239
1240 ((struct objc_class *)cls)->super_class = clstmp;
1241 }
1242
1243 // Install meta's isa based on stored name. Meta class isa
1244 // pointers always point to the meta class of the root class
1245 // (root meta class, too, it points to itself!).
1246 clstmp = objc_getClass ((const char *) meta->isa);
1247 if (!clstmp)
1248 {
1249 _objc_inform("failed objc_getClass(%s) for %s->isa->isa", (const char *) meta->isa, ((struct objc_class *)cls)->name);
1250 goto Error;
1251 }
1252
1253 meta->isa = clstmp->isa;
1254
1255 // Install meta's superclass based on stored name. No name iff
1256 // cls is a root class.
1257 if (meta->super_class)
1258 {
1259 // Locate instance class of super class
1260 clstmp = objc_getClass ((const char *) meta->super_class);
1261 if (!clstmp)
1262 {
1263 _objc_inform("failed objc_getClass(%s) for %s->isa->super_class", (const char *)meta->super_class, ((struct objc_class *)cls)->name);
1264 goto Error;
1265 }
1266
1267 // Store meta class of super class
1268 meta->super_class = clstmp->isa;
1269 }
1270
1271 // cls is root, so `tie' the (root) meta class down to its
1272 // instance class. This way, class methods can come from
1273 // the root instance class.
1274 else
1275 ((struct objc_class *)meta)->super_class = cls;
1276
1277 // Use common static empty cache instead of NULL
1278 if (((struct objc_class *)cls)->cache == NULL)
1279 ((struct objc_class *)cls)->cache = (Cache) &emptyCache;
1280 if (((struct objc_class *)meta)->cache == NULL)
1281 ((struct objc_class *)meta)->cache = (Cache) &emptyCache;
1282
1283 return;
1284
1285 Error:
1286 _objc_fatal ("please link appropriate classes in your program");
1287 }
1288
1289 /***********************************************************************
1290 * class_respondsToMethod.
1291 *
1292 * Called from -[Object respondsTo:] and +[Object instancesRespondTo:]
1293 **********************************************************************/
1294 BOOL class_respondsToMethod (Class cls,
1295 SEL sel)
1296 {
1297 Method meth;
1298 IMP imp;
1299
1300 // No one responds to zero!
1301 if (!sel)
1302 return NO;
1303
1304 imp = _cache_getImp(cls, sel);
1305 if (imp) {
1306 // Found method in cache.
1307 // If the cache entry is forward::, the class does not respond to sel.
1308 return (imp != &_objc_msgForward);
1309 }
1310
1311 // Handle cache miss
1312 meth = _getMethod(cls, sel);
1313 if (meth) {
1314 _cache_fill(cls, meth, sel);
1315 return YES;
1316 }
1317
1318 // Not implemented. Use _objc_msgForward.
1319 _cache_addForwardEntry(cls, sel);
1320
1321 return NO;
1322 }
1323
1324
1325 /***********************************************************************
1326 * class_lookupMethod.
1327 *
1328 * Called from -[Object methodFor:] and +[Object instanceMethodFor:]
1329 **********************************************************************/
1330 // GrP is this used anymore?
1331 IMP class_lookupMethod (Class cls,
1332 SEL sel)
1333 {
1334 IMP imp;
1335
1336 // No one responds to zero!
1337 if (!sel) {
1338 __objc_error(cls, _errBadSel, sel);
1339 }
1340
1341 imp = _cache_getImp(cls, sel);
1342 if (imp) return imp;
1343
1344 // Handle cache miss
1345 return _class_lookupMethodAndLoadCache (cls, sel);
1346 }
1347
1348 /***********************************************************************
1349 * class_lookupMethodInMethodList.
1350 *
1351 * Called from objc-load.m and _objc_callLoads ()
1352 **********************************************************************/
1353 IMP class_lookupMethodInMethodList (struct objc_method_list * mlist,
1354 SEL sel)
1355 {
1356 Method m = _findMethodInList(mlist, sel);
1357 return (m ? m->method_imp : NULL);
1358 }
1359
1360 IMP class_lookupNamedMethodInMethodList(struct objc_method_list *mlist,
1361 const char *meth_name)
1362 {
1363 Method m = meth_name ? _findNamedMethodInList(mlist, meth_name) : NULL;
1364 return (m ? m->method_imp : NULL);
1365 }
1366
1367
1368 /***********************************************************************
1369 * _cache_malloc.
1370 *
1371 * Called from _cache_create() and cache_expand()
1372 **********************************************************************/
1373 static Cache _cache_malloc(int slotCount)
1374 {
1375 Cache new_cache;
1376 size_t size;
1377
1378 // Allocate table (why not check for failure?)
1379 size = sizeof(struct objc_cache) + TABLE_SIZE(slotCount);
1380 #ifdef OBJC_INSTRUMENTED
1381 size += sizeof(CacheInstrumentation);
1382 #endif
1383 new_cache = malloc_zone_calloc (_objc_create_zone(), size, 1);
1384
1385 // [c|v]allocated memory is zeroed, so all buckets are invalidated
1386 // and occupied == 0 and all instrumentation is zero.
1387
1388 new_cache->mask = slotCount - 1;
1389
1390 return new_cache;
1391 }
1392
1393
1394 /***********************************************************************
1395 * _cache_create.
1396 *
1397 * Called from _cache_expand().
1398 * Cache locks: cacheUpdateLock must be held by the caller.
1399 **********************************************************************/
1400 Cache _cache_create (Class cls)
1401 {
1402 Cache new_cache;
1403 int slotCount;
1404
1405 // Select appropriate size
1406 slotCount = (ISMETA(cls)) ? INIT_META_CACHE_SIZE : INIT_CACHE_SIZE;
1407
1408 new_cache = _cache_malloc(slotCount);
1409
1410 // Install the cache
1411 ((struct objc_class *)cls)->cache = new_cache;
1412
1413 // Clear the cache flush flag so that we will not flush this cache
1414 // before expanding it for the first time.
1415 ((struct objc_class * )cls)->info &= ~(CLS_FLUSH_CACHE);
1416
1417 // Clear the grow flag so that we will re-use the current storage,
1418 // rather than actually grow the cache, when expanding the cache
1419 // for the first time
1420 if (_class_slow_grow)
1421 ((struct objc_class * )cls)->info &= ~(CLS_GROW_CACHE);
1422
1423 // Return our creation
1424 return new_cache;
1425 }
1426
1427 /***********************************************************************
1428 * _cache_expand.
1429 *
1430 * Called from _cache_fill ()
1431 * Cache locks: cacheUpdateLock must be held by the caller.
1432 **********************************************************************/
1433 static Cache _cache_expand (Class cls)
1434 {
1435 Cache old_cache;
1436 Cache new_cache;
1437 unsigned int slotCount;
1438 unsigned int index;
1439
1440 // First growth goes from emptyCache to a real one
1441 old_cache = ((struct objc_class *)cls)->cache;
1442 if (old_cache == &emptyCache)
1443 return _cache_create (cls);
1444
1445 // iff _class_slow_grow, trade off actual cache growth with re-using
1446 // the current one, so that growth only happens every odd time
1447 if (_class_slow_grow)
1448 {
1449 // CLS_GROW_CACHE controls every-other-time behavior. If it
1450 // is non-zero, let the cache grow this time, but clear the
1451 // flag so the cache is reused next time
1452 if ((((struct objc_class * )cls)->info & CLS_GROW_CACHE) != 0)
1453 ((struct objc_class * )cls)->info &= ~CLS_GROW_CACHE;
1454
1455 // Reuse the current cache storage this time
1456 else
1457 {
1458 // Clear the valid-entry counter
1459 old_cache->occupied = 0;
1460
1461 // Invalidate all the cache entries
1462 for (index = 0; index < old_cache->mask + 1; index += 1)
1463 {
1464 // Remember what this entry was, so we can possibly
1465 // deallocate it after the bucket has been invalidated
1466 Method oldEntry = old_cache->buckets[index];
1467 // Skip invalid entry
1468 if (!CACHE_BUCKET_VALID(old_cache->buckets[index]))
1469 continue;
1470
1471 // Invalidate this entry
1472 CACHE_BUCKET_VALID(old_cache->buckets[index]) = NULL;
1473
1474 // Deallocate "forward::" entry
1475 if (CACHE_BUCKET_IMP(oldEntry) == &_objc_msgForward)
1476 {
1477 _cache_collect_free (oldEntry, NO);
1478 }
1479 }
1480
1481 // Set the slow growth flag so the cache is next grown
1482 ((struct objc_class * )cls)->info |= CLS_GROW_CACHE;
1483
1484 // Return the same old cache, freshly emptied
1485 return old_cache;
1486 }
1487
1488 }
1489
1490 // Double the cache size
1491 slotCount = (old_cache->mask + 1) << 1;
1492
1493 new_cache = _cache_malloc(slotCount);
1494
1495 #ifdef OBJC_INSTRUMENTED
1496 // Propagate the instrumentation data
1497 {
1498 CacheInstrumentation * oldCacheData;
1499 CacheInstrumentation * newCacheData;
1500
1501 oldCacheData = CACHE_INSTRUMENTATION(old_cache);
1502 newCacheData = CACHE_INSTRUMENTATION(new_cache);
1503 bcopy ((const char *)oldCacheData, (char *)newCacheData, sizeof(CacheInstrumentation));
1504 }
1505 #endif
1506
1507 // iff _class_uncache, copy old cache entries into the new cache
1508 if (_class_uncache == 0)
1509 {
1510 int newMask;
1511
1512 newMask = new_cache->mask;
1513
1514 // Look at all entries in the old cache
1515 for (index = 0; index < old_cache->mask + 1; index += 1)
1516 {
1517 int index2;
1518
1519 // Skip invalid entry
1520 if (!CACHE_BUCKET_VALID(old_cache->buckets[index]))
1521 continue;
1522
1523 // Hash the old entry into the new table
1524 index2 = CACHE_HASH(CACHE_BUCKET_NAME(old_cache->buckets[index]),
1525 newMask);
1526
1527 // Find an available spot, at or following the hashed spot;
1528 // Guaranteed to not infinite loop, because table has grown
1529 for (;;)
1530 {
1531 if (!CACHE_BUCKET_VALID(new_cache->buckets[index2]))
1532 {
1533 new_cache->buckets[index2] = old_cache->buckets[index];
1534 break;
1535 }
1536
1537 index2 += 1;
1538 index2 &= newMask;
1539 }
1540
1541 // Account for the addition
1542 new_cache->occupied += 1;
1543 }
1544
1545 // Set the cache flush flag so that we will flush this cache
1546 // before expanding it again.
1547 ((struct objc_class * )cls)->info |= CLS_FLUSH_CACHE;
1548 }
1549
1550 // Deallocate "forward::" entries from the old cache
1551 else
1552 {
1553 for (index = 0; index < old_cache->mask + 1; index += 1)
1554 {
1555 if (CACHE_BUCKET_VALID(old_cache->buckets[index]) &&
1556 CACHE_BUCKET_IMP(old_cache->buckets[index]) == &_objc_msgForward)
1557 {
1558 _cache_collect_free (old_cache->buckets[index], NO);
1559 }
1560 }
1561 }
1562
1563 // Install new cache
1564 ((struct objc_class *)cls)->cache = new_cache;
1565
1566 // Deallocate old cache, try freeing all the garbage
1567 _cache_collect_free (old_cache, YES);
1568 return new_cache;
1569 }
1570
1571 /***********************************************************************
1572 * instrumentObjcMessageSends/logObjcMessageSends.
1573 **********************************************************************/
1574 static int LogObjCMessageSend (BOOL isClassMethod,
1575 const char * objectsClass,
1576 const char * implementingClass,
1577 SEL selector)
1578 {
1579 char buf[ 1024 ];
1580
1581 // Create/open the log file
1582 if (objcMsgLogFD == (-1))
1583 {
1584 snprintf (buf, sizeof(buf), "/tmp/msgSends-%d", (int) getpid ());
1585 objcMsgLogFD = open (buf, O_WRONLY | O_CREAT, 0666);
1586 }
1587
1588 // Make the log entry
1589 snprintf(buf, sizeof(buf), "%c %s %s %s\n",
1590 isClassMethod ? '+' : '-',
1591 objectsClass,
1592 implementingClass,
1593 (char *) selector);
1594
1595 write (objcMsgLogFD, buf, strlen(buf));
1596
1597 // Tell caller to not cache the method
1598 return 0;
1599 }
1600
1601 void instrumentObjcMessageSends (BOOL flag)
1602 {
1603 int enabledValue = (flag) ? 1 : 0;
1604
1605 // Shortcut NOP
1606 if (objcMsgLogEnabled == enabledValue)
1607 return;
1608
1609 // If enabling, flush all method caches so we get some traces
1610 if (flag)
1611 flush_caches (Nil, YES);
1612
1613 // Sync our log file
1614 if (objcMsgLogFD != (-1))
1615 fsync (objcMsgLogFD);
1616
1617 objcMsgLogEnabled = enabledValue;
1618 }
1619
1620 void logObjcMessageSends (ObjCLogProc logProc)
1621 {
1622 if (logProc)
1623 {
1624 objcMsgLogProc = logProc;
1625 objcMsgLogEnabled = 1;
1626 }
1627 else
1628 {
1629 objcMsgLogProc = logProc;
1630 objcMsgLogEnabled = 0;
1631 }
1632
1633 if (objcMsgLogFD != (-1))
1634 fsync (objcMsgLogFD);
1635 }
1636
1637
1638 /***********************************************************************
1639 * _cache_fill. Add the specified method to the specified class' cache.
1640 * Returns NO if the cache entry wasn't added: cache was busy,
1641 * class is still being initialized, new entry is a duplicate.
1642 *
1643 * Called only from _class_lookupMethodAndLoadCache and
1644 * class_respondsToMethod and _cache_addForwardEntry.
1645 *
1646 * Cache locks: cacheUpdateLock must not be held.
1647 **********************************************************************/
1648 static BOOL _cache_fill(Class cls, Method smt, SEL sel)
1649 {
1650 unsigned int newOccupied;
1651 arith_t index;
1652 Method *buckets;
1653 Cache cache;
1654
1655 // Never cache before +initialize is done
1656 if (!ISINITIALIZED(cls)) {
1657 return NO;
1658 }
1659
1660 // Keep tally of cache additions
1661 totalCacheFills += 1;
1662
1663 OBJC_LOCK(&cacheUpdateLock);
1664
1665 cache = ((struct objc_class *)cls)->cache;
1666
1667 // Check for duplicate entries, if we're in the mode
1668 if (traceDuplicates)
1669 {
1670 int index2;
1671 arith_t mask = cache->mask;
1672 buckets = cache->buckets;
1673
1674 // Scan the cache
1675 for (index2 = 0; index2 < mask + 1; index2 += 1)
1676 {
1677 // Skip invalid or non-duplicate entry
1678 if ((!CACHE_BUCKET_VALID(buckets[index2])) ||
1679 (strcmp ((char *) CACHE_BUCKET_NAME(buckets[index2]), (char *) smt->method_name) != 0))
1680 continue;
1681
1682 // Tally duplication, but report iff wanted
1683 cacheFillDuplicates += 1;
1684 if (traceDuplicatesVerbose)
1685 {
1686 _objc_inform ("Cache fill duplicate #%d: found %x adding %x: %s\n",
1687 cacheFillDuplicates,
1688 (unsigned int) CACHE_BUCKET_NAME(buckets[index2]),
1689 (unsigned int) smt->method_name,
1690 (char *) smt->method_name);
1691 }
1692 }
1693 }
1694
1695 // Make sure the entry wasn't added to the cache by some other thread
1696 // before we grabbed the cacheUpdateLock.
1697 // Don't use _cache_getMethod() because _cache_getMethod() doesn't
1698 // return forward:: entries.
1699 if (_cache_getImp(cls, sel)) {
1700 OBJC_UNLOCK(&cacheUpdateLock);
1701 return NO; // entry is already cached, didn't add new one
1702 }
1703
1704 // Use the cache as-is if it is less than 3/4 full
1705 newOccupied = cache->occupied + 1;
1706 if ((newOccupied * 4) <= (cache->mask + 1) * 3) {
1707 // Cache is less than 3/4 full.
1708 cache->occupied = newOccupied;
1709 } else {
1710 // Cache is too full. Flush it or expand it.
1711 if ((((struct objc_class * )cls)->info & CLS_FLUSH_CACHE) != 0) {
1712 _cache_flush (cls);
1713 } else {
1714 cache = _cache_expand (cls);
1715 }
1716
1717 // Account for the addition
1718 cache->occupied += 1;
1719 }
1720
1721 // Insert the new entry. This can be done by either:
1722 // (a) Scanning for the first unused spot. Easy!
1723 // (b) Opening up an unused spot by sliding existing
1724 // entries down by one. The benefit of this
1725 // extra work is that it puts the most recently
1726 // loaded entries closest to where the selector
1727 // hash starts the search.
1728 //
1729 // The loop is a little more complicated because there
1730 // are two kinds of entries, so there have to be two ways
1731 // to slide them.
1732 buckets = cache->buckets;
1733 index = CACHE_HASH(sel, cache->mask);
1734 for (;;)
1735 {
1736 // Slide existing entries down by one
1737 Method saveMethod;
1738
1739 // Copy current entry to a local
1740 saveMethod = buckets[index];
1741
1742 // Copy previous entry (or new entry) to current slot
1743 buckets[index] = smt;
1744
1745 // Done if current slot had been invalid
1746 if (saveMethod == NULL)
1747 break;
1748
1749 // Prepare to copy saved value into next slot
1750 smt = saveMethod;
1751
1752 // Move on to next slot
1753 index += 1;
1754 index &= cache->mask;
1755 }
1756
1757 OBJC_UNLOCK(&cacheUpdateLock);
1758
1759 return YES; // successfully added new cache entry
1760 }
1761
1762
1763 /***********************************************************************
1764 * _cache_addForwardEntry
1765 * Add a forward:: entry for the given selector to cls's method cache.
1766 * Does nothing if the cache addition fails for any reason.
1767 * Called from class_respondsToMethod and _class_lookupMethodAndLoadCache.
1768 * Cache locks: cacheUpdateLock must not be held.
1769 **********************************************************************/
1770 static void _cache_addForwardEntry(Class cls, SEL sel)
1771 {
1772 Method smt;
1773
1774 smt = malloc_zone_malloc(_objc_create_zone(), sizeof(struct objc_method));
1775 smt->method_name = sel;
1776 smt->method_types = "";
1777 smt->method_imp = &_objc_msgForward;
1778 if (! _cache_fill(cls, smt, sel)) {
1779 // Entry not added to cache. Don't leak the method struct.
1780 malloc_zone_free(_objc_create_zone(), smt);
1781 }
1782 }
1783
1784
1785 /***********************************************************************
1786 * _cache_flush. Invalidate all valid entries in the given class' cache,
1787 * and clear the CLS_FLUSH_CACHE in the cls->info.
1788 *
1789 * Called from flush_caches() and _cache_fill()
1790 * Cache locks: cacheUpdateLock must be held by the caller.
1791 **********************************************************************/
1792 static void _cache_flush (Class cls)
1793 {
1794 Cache cache;
1795 unsigned int index;
1796
1797 // Locate cache. Ignore unused cache.
1798 cache = ((struct objc_class *)cls)->cache;
1799 if (cache == NULL || cache == &emptyCache)
1800 return;
1801
1802 #ifdef OBJC_INSTRUMENTED
1803 {
1804 CacheInstrumentation * cacheData;
1805
1806 // Tally this flush
1807 cacheData = CACHE_INSTRUMENTATION(cache);
1808 cacheData->flushCount += 1;
1809 cacheData->flushedEntries += cache->occupied;
1810 if (cache->occupied > cacheData->maxFlushedEntries)
1811 cacheData->maxFlushedEntries = cache->occupied;
1812 }
1813 #endif
1814
1815 // Traverse the cache
1816 for (index = 0; index <= cache->mask; index += 1)
1817 {
1818 // Remember what this entry was, so we can possibly
1819 // deallocate it after the bucket has been invalidated
1820 Method oldEntry = cache->buckets[index];
1821
1822 // Invalidate this entry
1823 CACHE_BUCKET_VALID(cache->buckets[index]) = NULL;
1824
1825 // Deallocate "forward::" entry
1826 if (oldEntry && oldEntry->method_imp == &_objc_msgForward)
1827 _cache_collect_free (oldEntry, NO);
1828 }
1829
1830 // Clear the valid-entry counter
1831 cache->occupied = 0;
1832
1833 // Clear the cache flush flag so that we will not flush this cache
1834 // before expanding it again.
1835 ((struct objc_class * )cls)->info &= ~CLS_FLUSH_CACHE;
1836 }
1837
1838 /***********************************************************************
1839 * _objc_getFreedObjectClass. Return a pointer to the dummy freed
1840 * object class. Freed objects get their isa pointers replaced with
1841 * a pointer to the freedObjectClass, so that we can catch usages of
1842 * the freed object.
1843 **********************************************************************/
1844 Class _objc_getFreedObjectClass (void)
1845 {
1846 return (Class) &freedObjectClass;
1847 }
1848
1849 /***********************************************************************
1850 * _objc_getNonexistentClass. Return a pointer to the dummy nonexistent
1851 * object class. This is used when, for example, mapping the class
1852 * refs for an image, and the class can not be found, so that we can
1853 * catch later uses of the non-existent class.
1854 **********************************************************************/
1855 Class _objc_getNonexistentClass (void)
1856 {
1857 return (Class) &nonexistentObjectClass;
1858 }
1859
1860
1861 /***********************************************************************
1862 * struct _objc_initializing_classes
1863 * Per-thread list of classes currently being initialized by that thread.
1864 * During initialization, that thread is allowed to send messages to that
1865 * class, but other threads have to wait.
1866 * The list is a simple array of metaclasses (the metaclass stores
1867 * the initialization state).
1868 **********************************************************************/
1869 typedef struct _objc_initializing_classes {
1870 int classesAllocated;
1871 struct objc_class** metaclasses;
1872 } _objc_initializing_classes;
1873
1874
1875 /***********************************************************************
1876 * _fetchInitializingClassList
1877 * Return the list of classes being initialized by this thread.
1878 * If create == YES, create the list when no classes are being initialized by this thread.
1879 * If create == NO, return NULL when no classes are being initialized by this thread.
1880 **********************************************************************/
1881 static _objc_initializing_classes *_fetchInitializingClassList(BOOL create)
1882 {
1883 _objc_pthread_data *data;
1884 _objc_initializing_classes *list;
1885 struct objc_class **classes;
1886
1887 data = pthread_getspecific(_objc_pthread_key);
1888 if (data == NULL) {
1889 if (!create) {
1890 return NULL;
1891 } else {
1892 data = calloc(1, sizeof(_objc_pthread_data));
1893 pthread_setspecific(_objc_pthread_key, data);
1894 }
1895 }
1896
1897 list = data->initializingClasses;
1898 if (list == NULL) {
1899 if (!create) {
1900 return NULL;
1901 } else {
1902 list = calloc(1, sizeof(_objc_initializing_classes));
1903 data->initializingClasses = list;
1904 }
1905 }
1906
1907 classes = list->metaclasses;
1908 if (classes == NULL) {
1909 // If _objc_initializing_classes exists, allocate metaclass array,
1910 // even if create == NO.
1911 // Allow 4 simultaneous class inits on this thread before realloc.
1912 list->classesAllocated = 4;
1913 classes = calloc(list->classesAllocated, sizeof(struct objc_class *));
1914 list->metaclasses = classes;
1915 }
1916 return list;
1917 }
1918
1919
1920 /***********************************************************************
1921 * _destroyInitializingClassList
1922 * Deallocate memory used by the given initialization list.
1923 * Any part of the list may be NULL.
1924 * Called from _objc_pthread_destroyspecific().
1925 **********************************************************************/
1926 void _destroyInitializingClassList(_objc_initializing_classes *list)
1927 {
1928 if (list != NULL) {
1929 if (list->metaclasses != NULL) {
1930 free(list->metaclasses);
1931 }
1932 free(list);
1933 }
1934 }
1935
1936
1937 /***********************************************************************
1938 * _thisThreadIsInitializingClass
1939 * Return TRUE if this thread is currently initializing the given class.
1940 **********************************************************************/
1941 static BOOL _thisThreadIsInitializingClass(struct objc_class *cls)
1942 {
1943 int i;
1944
1945 _objc_initializing_classes *list = _fetchInitializingClassList(NO);
1946 if (list) {
1947 cls = GETMETA(cls);
1948 for (i = 0; i < list->classesAllocated; i++) {
1949 if (cls == list->metaclasses[i]) return YES;
1950 }
1951 }
1952
1953 // no list or not found in list
1954 return NO;
1955 }
1956
1957
1958 /***********************************************************************
1959 * _setThisThreadIsInitializingClass
1960 * Record that this thread is currently initializing the given class.
1961 * This thread will be allowed to send messages to the class, but
1962 * other threads will have to wait.
1963 **********************************************************************/
1964 static void _setThisThreadIsInitializingClass(struct objc_class *cls)
1965 {
1966 int i;
1967 _objc_initializing_classes *list = _fetchInitializingClassList(YES);
1968 cls = GETMETA(cls);
1969
1970 // paranoia: explicitly disallow duplicates
1971 for (i = 0; i < list->classesAllocated; i++) {
1972 if (cls == list->metaclasses[i]) {
1973 _objc_fatal("thread is already initializing this class!");
1974 return; // already the initializer
1975 }
1976 }
1977
1978 for (i = 0; i < list->classesAllocated; i++) {
1979 if (0 == list->metaclasses[i]) {
1980 list->metaclasses[i] = cls;
1981 return;
1982 }
1983 }
1984
1985 // class list is full - reallocate
1986 list->classesAllocated = list->classesAllocated * 2 + 1;
1987 list->metaclasses = realloc(list->metaclasses, list->classesAllocated * sizeof(struct objc_class *));
1988 // zero out the new entries
1989 list->metaclasses[i++] = cls;
1990 for ( ; i < list->classesAllocated; i++) {
1991 list->metaclasses[i] = NULL;
1992 }
1993 }
1994
1995
1996 /***********************************************************************
1997 * _setThisThreadIsNotInitializingClass
1998 * Record that this thread is no longer initializing the given class.
1999 **********************************************************************/
2000 static void _setThisThreadIsNotInitializingClass(struct objc_class *cls)
2001 {
2002 int i;
2003
2004 _objc_initializing_classes *list = _fetchInitializingClassList(NO);
2005 if (list) {
2006 cls = GETMETA(cls);
2007 for (i = 0; i < list->classesAllocated; i++) {
2008 if (cls == list->metaclasses[i]) {
2009 list->metaclasses[i] = NULL;
2010 return;
2011 }
2012 }
2013 }
2014
2015 // no list or not found in list
2016 _objc_fatal("thread is not initializing this class!");
2017 }
2018
2019
2020 /***********************************************************************
2021 * class_initialize. Send the '+initialize' message on demand to any
2022 * uninitialized class. Force initialization of superclasses first.
2023 *
2024 * Called only from _class_lookupMethodAndLoadCache (or itself).
2025 **********************************************************************/
2026 static void class_initialize(struct objc_class *cls)
2027 {
2028 long *infoP = &GETMETA(cls)->info;
2029 BOOL reallyInitialize = NO;
2030
2031 // Get the real class from the metaclass. The superclass chain
2032 // hangs off the real class only.
2033 if (ISMETA(cls)) {
2034 if (strncmp(cls->name, "_%", 2) == 0) {
2035 // Posee's meta's name is smashed and isn't in the class_hash,
2036 // so objc_getClass doesn't work.
2037 char *baseName = strchr(cls->name, '%'); // get posee's real name
2038 cls = objc_getClass(baseName);
2039 } else {
2040 cls = objc_getClass(cls->name);
2041 }
2042 }
2043
2044 // Make sure super is done initializing BEFORE beginning to initialize cls.
2045 // See note about deadlock above.
2046 if (cls->super_class && !ISINITIALIZED(cls->super_class)) {
2047 class_initialize(cls->super_class);
2048 }
2049
2050 // Try to atomically set CLS_INITIALIZING.
2051 pthread_mutex_lock(&classInitLock);
2052 if (!ISINITIALIZED(cls) && !ISINITIALIZING(cls)) {
2053 *infoP |= CLS_INITIALIZING;
2054 reallyInitialize = YES;
2055 }
2056 pthread_mutex_unlock(&classInitLock);
2057
2058 if (reallyInitialize) {
2059 // We successfully set the CLS_INITIALIZING bit. Initialize the class.
2060
2061 // Record that we're initializing this class so we can message it.
2062 _setThisThreadIsInitializingClass(cls);
2063
2064 // bind the module in - if it came from a bundle or dynamic library
2065 _objc_bindClassIfNeeded(cls);
2066
2067 // chain on the categories and bind them if necessary
2068 _objc_resolve_categories_for_class(cls);
2069
2070 // Send the +initialize message.
2071 // Note that +initialize is sent to the superclass (again) if
2072 // this class doesn't implement +initialize. 2157218
2073 [(id)cls initialize];
2074
2075 // Done initializing. Update the info bits and notify waiting threads.
2076 pthread_mutex_lock(&classInitLock);
2077 *infoP = (*infoP | CLS_INITIALIZED) & ~CLS_INITIALIZING;
2078 pthread_cond_broadcast(&classInitWaitCond);
2079 pthread_mutex_unlock(&classInitLock);
2080 _setThisThreadIsNotInitializingClass(cls);
2081 return;
2082 }
2083
2084 else if (ISINITIALIZING(cls)) {
2085 // We couldn't set INITIALIZING because INITIALIZING was already set.
2086 // If this thread set it earlier, continue normally.
2087 // If some other thread set it, block until initialize is done.
2088 // It's ok if INITIALIZING changes to INITIALIZED while we're here,
2089 // because we safely check for INITIALIZED inside the lock
2090 // before blocking.
2091 if (_thisThreadIsInitializingClass(cls)) {
2092 return;
2093 } else {
2094 pthread_mutex_lock(&classInitLock);
2095 while (!ISINITIALIZED(cls)) {
2096 pthread_cond_wait(&classInitWaitCond, &classInitLock);
2097 }
2098 pthread_mutex_unlock(&classInitLock);
2099 return;
2100 }
2101 }
2102
2103 else if (ISINITIALIZED(cls)) {
2104 // Set CLS_INITIALIZING failed because someone else already
2105 // initialized the class. Continue normally.
2106 // NOTE this check must come AFTER the ISINITIALIZING case.
2107 // Otherwise: Another thread is initializing this class. ISINITIALIZED
2108 // is false. Skip this clause. Then the other thread finishes
2109 // initialization and sets INITIALIZING=no and INITIALIZED=yes.
2110 // Skip the ISINITIALIZING clause. Die horribly.
2111 return;
2112 }
2113
2114 else {
2115 // We shouldn't be here.
2116 _objc_fatal("thread-safe class init in objc runtime is buggy!");
2117 }
2118 }
2119
2120
2121 /***********************************************************************
2122 * _class_lookupMethodAndLoadCache.
2123 *
2124 * Called only from objc_msgSend, objc_msgSendSuper and class_lookupMethod.
2125 **********************************************************************/
2126 IMP _class_lookupMethodAndLoadCache (Class cls,
2127 SEL sel)
2128 {
2129 struct objc_class * curClass;
2130 Method meth;
2131 IMP methodPC = NULL;
2132
2133 trace(0xb300, 0, 0, 0);
2134
2135 // Check for freed class
2136 if (cls == &freedObjectClass)
2137 return (IMP) _freedHandler;
2138
2139 // Check for nonexistent class
2140 if (cls == &nonexistentObjectClass)
2141 return (IMP) _nonexistentHandler;
2142
2143 trace(0xb301, 0, 0, 0);
2144
2145 if (!ISINITIALIZED(cls)) {
2146 class_initialize ((struct objc_class *)cls);
2147 // If sel == initialize, class_initialize will send +initialize and
2148 // then the messenger will send +initialize again after this
2149 // procedure finishes. Of course, if this is not being called
2150 // from the messenger then it won't happen. 2778172
2151 }
2152
2153 trace(0xb302, 0, 0, 0);
2154
2155 // Outer loop - search the caches and method lists of the
2156 // class and its super-classes
2157 for (curClass = cls; curClass; curClass = ((struct objc_class * )curClass)->super_class)
2158 {
2159 #ifdef PRELOAD_SUPERCLASS_CACHES
2160 struct objc_class *curClass2;
2161 #endif
2162
2163 trace(0xb303, 0, 0, 0);
2164
2165 // Beware of thread-unsafety and double-freeing of forward::
2166 // entries here! See note in "Method cache locking" above.
2167 // The upshot is that _cache_getMethod() will return NULL
2168 // instead of returning a forward:: entry.
2169 meth = _cache_getMethod(curClass, sel, &_objc_msgForward);
2170 if (meth) {
2171 // Found the method in this class or a superclass.
2172 // Cache the method in this class, unless we just found it in
2173 // this class's cache.
2174 if (curClass != cls) {
2175 #ifdef PRELOAD_SUPERCLASS_CACHES
2176 for (curClass2 = cls; curClass2 != curClass; curClass2 = curClass2->super_class)
2177 _cache_fill (curClass2, meth, sel);
2178 _cache_fill (curClass, meth, sel);
2179 #else
2180 _cache_fill (cls, meth, sel);
2181 #endif
2182 }
2183
2184 methodPC = meth->method_imp;
2185 break;
2186 }
2187
2188 trace(0xb304, (int)methodPC, 0, 0);
2189
2190 // Cache scan failed. Search method list.
2191
2192 meth = _findMethodInClass(curClass, sel);
2193 if (meth) {
2194 // If logging is enabled, log the message send and let
2195 // the logger decide whether to encache the method.
2196 if ((objcMsgLogEnabled == 0) ||
2197 (objcMsgLogProc (CLS_GETINFO(((struct objc_class * )curClass),
2198 CLS_META) ? YES : NO,
2199 ((struct objc_class *)cls)->name,
2200 curClass->name, sel)))
2201 {
2202 // Cache the method implementation
2203 #ifdef PRELOAD_SUPERCLASS_CACHES
2204 for (curClass2 = cls; curClass2 != curClass; curClass2 = curClass2->super_class)
2205 _cache_fill (curClass2, meth, sel);
2206 _cache_fill (curClass, meth, sel);
2207 #else
2208 _cache_fill (cls, meth, sel);
2209 #endif
2210 }
2211
2212 methodPC = meth->method_imp;
2213 break;
2214 }
2215
2216 trace(0xb305, (int)methodPC, 0, 0);
2217 }
2218
2219 trace(0xb306, (int)methodPC, 0, 0);
2220
2221 if (methodPC == NULL)
2222 {
2223 // Class and superclasses do not respond -- use forwarding
2224 _cache_addForwardEntry(cls, sel);
2225 methodPC = &_objc_msgForward;
2226 }
2227
2228 trace(0xb30f, (int)methodPC, 0, 0);
2229
2230 return methodPC;
2231 }
2232
2233
2234 /***********************************************************************
2235 * SubtypeUntil.
2236 *
2237 * Delegation.
2238 **********************************************************************/
2239 static int SubtypeUntil (const char * type,
2240 char end)
2241 {
2242 int level = 0;
2243 const char * head = type;
2244
2245 //
2246 while (*type)
2247 {
2248 if (!*type || (!level && (*type == end)))
2249 return (int)(type - head);
2250
2251 switch (*type)
2252 {
2253 case ']': case '}': case ')': level--; break;
2254 case '[': case '{': case '(': level += 1; break;
2255 }
2256
2257 type += 1;
2258 }
2259
2260 _objc_fatal ("Object: SubtypeUntil: end of type encountered prematurely\n");
2261 return 0;
2262 }
2263
2264 /***********************************************************************
2265 * SkipFirstType.
2266 **********************************************************************/
2267 static const char * SkipFirstType (const char * type)
2268 {
2269 while (1)
2270 {
2271 switch (*type++)
2272 {
2273 case 'O': /* bycopy */
2274 case 'n': /* in */
2275 case 'o': /* out */
2276 case 'N': /* inout */
2277 case 'r': /* const */
2278 case 'V': /* oneway */
2279 case '^': /* pointers */
2280 break;
2281
2282 /* arrays */
2283 case '[':
2284 while ((*type >= '0') && (*type <= '9'))
2285 type += 1;
2286 return type + SubtypeUntil (type, ']') + 1;
2287
2288 /* structures */
2289 case '{':
2290 return type + SubtypeUntil (type, '}') + 1;
2291
2292 /* unions */
2293 case '(':
2294 return type + SubtypeUntil (type, ')') + 1;
2295
2296 /* basic types */
2297 default:
2298 return type;
2299 }
2300 }
2301 }
2302
2303 /***********************************************************************
2304 * method_getNumberOfArguments.
2305 **********************************************************************/
2306 unsigned method_getNumberOfArguments (Method method)
2307 {
2308 const char * typedesc;
2309 unsigned nargs;
2310
2311 // First, skip the return type
2312 typedesc = method->method_types;
2313 typedesc = SkipFirstType (typedesc);
2314
2315 // Next, skip stack size
2316 while ((*typedesc >= '0') && (*typedesc <= '9'))
2317 typedesc += 1;
2318
2319 // Now, we have the arguments - count how many
2320 nargs = 0;
2321 while (*typedesc)
2322 {
2323 // Traverse argument type
2324 typedesc = SkipFirstType (typedesc);
2325
2326 // Skip GNU runtime's register parameter hint
2327 if (*typedesc == '+') typedesc++;
2328
2329 // Traverse (possibly negative) argument offset
2330 if (*typedesc == '-')
2331 typedesc += 1;
2332 while ((*typedesc >= '0') && (*typedesc <= '9'))
2333 typedesc += 1;
2334
2335 // Made it past an argument
2336 nargs += 1;
2337 }
2338
2339 return nargs;
2340 }
2341
2342 /***********************************************************************
2343 * method_getSizeOfArguments.
2344 **********************************************************************/
2345 #ifndef __alpha__
2346 unsigned method_getSizeOfArguments (Method method)
2347 {
2348 const char * typedesc;
2349 unsigned stack_size;
2350 #if defined(__ppc__) || defined(ppc)
2351 unsigned trueBaseOffset;
2352 unsigned foundBaseOffset;
2353 #endif
2354
2355 // Get our starting points
2356 stack_size = 0;
2357 typedesc = method->method_types;
2358
2359 // Skip the return type
2360 #if defined (__ppc__) || defined(ppc)
2361 // Struct returns cause the parameters to be bumped
2362 // by a register, so the offset to the receiver is
2363 // 4 instead of the normal 0.
2364 trueBaseOffset = (*typedesc == '{') ? 4 : 0;
2365 #endif
2366 typedesc = SkipFirstType (typedesc);
2367
2368 // Convert ASCII number string to integer
2369 while ((*typedesc >= '0') && (*typedesc <= '9'))
2370 stack_size = (stack_size * 10) + (*typedesc++ - '0');
2371 #if defined (__ppc__) || defined(ppc)
2372 // NOTE: This is a temporary measure pending a compiler fix.
2373 // Work around PowerPC compiler bug wherein the method argument
2374 // string contains an incorrect value for the "stack size."
2375 // Generally, the size is reported 4 bytes too small, so we apply
2376 // that fudge factor. Unfortunately, there is at least one case
2377 // where the error is something other than -4: when the last
2378 // parameter is a double, the reported stack is much too high
2379 // (about 32 bytes). We do not attempt to detect that case.
2380 // The result of returning a too-high value is that objc_msgSendv
2381 // can bus error if the destination of the marg_list copying
2382 // butts up against excluded memory.
2383 // This fix disables itself when it sees a correctly built
2384 // type string (i.e. the offset for the Id is correct). This
2385 // keeps us out of lockstep with the compiler.
2386
2387 // skip the '@' marking the Id field
2388 typedesc = SkipFirstType (typedesc);
2389
2390 // Skip GNU runtime's register parameter hint
2391 if (*typedesc == '+') typedesc++;
2392
2393 // pick up the offset for the Id field
2394 foundBaseOffset = 0;
2395 while ((*typedesc >= '0') && (*typedesc <= '9'))
2396 foundBaseOffset = (foundBaseOffset * 10) + (*typedesc++ - '0');
2397
2398 // add fudge factor iff the Id field offset was wrong
2399 if (foundBaseOffset != trueBaseOffset)
2400 stack_size += 4;
2401 #endif
2402
2403 return stack_size;
2404 }
2405
2406 #else // __alpha__
2407 // XXX Getting the size of a type is done all over the place
2408 // (Here, Foundation, remote project)! - Should unify
2409
2410 unsigned int getSizeOfType (const char * type, unsigned int * alignPtr);
2411
2412 unsigned method_getSizeOfArguments (Method method)
2413 {
2414 const char * type;
2415 int size;
2416 int index;
2417 int align;
2418 int offset;
2419 unsigned stack_size;
2420 int nargs;
2421
2422 nargs = method_getNumberOfArguments (method);
2423 stack_size = (*method->method_types == '{') ? sizeof(void *) : 0;
2424
2425 for (index = 0; index < nargs; index += 1)
2426 {
2427 (void) method_getArgumentInfo (method, index, &type, &offset);
2428 size = getSizeOfType (type, &align);
2429 stack_size += ((size + 7) & ~7);
2430 }
2431
2432 return stack_size;
2433 }
2434 #endif // __alpha__
2435
2436 /***********************************************************************
2437 * method_getArgumentInfo.
2438 **********************************************************************/
2439 unsigned method_getArgumentInfo (Method method,
2440 int arg,
2441 const char ** type,
2442 int * offset)
2443 {
2444 const char * typedesc = method->method_types;
2445 unsigned nargs = 0;
2446 unsigned self_offset = 0;
2447 BOOL offset_is_negative = NO;
2448
2449 // First, skip the return type
2450 typedesc = SkipFirstType (typedesc);
2451
2452 // Next, skip stack size
2453 while ((*typedesc >= '0') && (*typedesc <= '9'))
2454 typedesc += 1;
2455
2456 // Now, we have the arguments - position typedesc to the appropriate argument
2457 while (*typedesc && nargs != arg)
2458 {
2459
2460 // Skip argument type
2461 typedesc = SkipFirstType (typedesc);
2462
2463 if (nargs == 0)
2464 {
2465 // Skip GNU runtime's register parameter hint
2466 if (*typedesc == '+') typedesc++;
2467
2468 // Skip negative sign in offset
2469 if (*typedesc == '-')
2470 {
2471 offset_is_negative = YES;
2472 typedesc += 1;
2473 }
2474 else
2475 offset_is_negative = NO;
2476
2477 while ((*typedesc >= '0') && (*typedesc <= '9'))
2478 self_offset = self_offset * 10 + (*typedesc++ - '0');
2479 if (offset_is_negative)
2480 self_offset = -(self_offset);
2481
2482 }
2483
2484 else
2485 {
2486 // Skip GNU runtime's register parameter hint
2487 if (*typedesc == '+') typedesc++;
2488
2489 // Skip (possibly negative) argument offset
2490 if (*typedesc == '-')
2491 typedesc += 1;
2492 while ((*typedesc >= '0') && (*typedesc <= '9'))
2493 typedesc += 1;
2494 }
2495
2496 nargs += 1;
2497 }
2498
2499 if (*typedesc)
2500 {
2501 unsigned arg_offset = 0;
2502
2503 *type = typedesc;
2504 typedesc = SkipFirstType (typedesc);
2505
2506 if (arg == 0)
2507 {
2508 #ifdef hppa
2509 *offset = -sizeof(id);
2510 #else
2511 *offset = 0;
2512 #endif // hppa
2513 }
2514
2515 else
2516 {
2517 // Skip GNU register parameter hint
2518 if (*typedesc == '+') typedesc++;
2519
2520 // Pick up (possibly negative) argument offset
2521 if (*typedesc == '-')
2522 {
2523 offset_is_negative = YES;
2524 typedesc += 1;
2525 }
2526 else
2527 offset_is_negative = NO;
2528
2529 while ((*typedesc >= '0') && (*typedesc <= '9'))
2530 arg_offset = arg_offset * 10 + (*typedesc++ - '0');
2531 if (offset_is_negative)
2532 arg_offset = - arg_offset;
2533
2534 #ifdef hppa
2535 // For stacks which grow up, since margs points
2536 // to the top of the stack or the END of the args,
2537 // the first offset is at -sizeof(id) rather than 0.
2538 self_offset += sizeof(id);
2539 #endif
2540 *offset = arg_offset - self_offset;
2541 }
2542
2543 }
2544
2545 else
2546 {
2547 *type = 0;
2548 *offset = 0;
2549 }
2550
2551 return nargs;
2552 }
2553
2554 /***********************************************************************
2555 * _objc_create_zone.
2556 **********************************************************************/
2557
2558 void * _objc_create_zone (void)
2559 {
2560 static void *_objc_z = (void *)0xffffffff;
2561 if ( _objc_z == (void *)0xffffffff ) {
2562 char *s = getenv("OBJC_USE_OBJC_ZONE");
2563 if ( s ) {
2564 if ( (*s == '1') || (*s == 'y') || (*s == 'Y') ) {
2565 _objc_z = malloc_create_zone(vm_page_size, 0);
2566 malloc_set_zone_name(_objc_z, "ObjC");
2567 }
2568 }
2569 if ( _objc_z == (void *)0xffffffff ) {
2570 _objc_z = malloc_default_zone();
2571 }
2572 }
2573 return _objc_z;
2574 }
2575
2576 /***********************************************************************
2577 * cache collection.
2578 **********************************************************************/
2579
2580 static unsigned long _get_pc_for_thread (mach_port_t thread)
2581 #ifdef hppa
2582 {
2583 struct hp_pa_frame_thread_state state;
2584 unsigned int count = HPPA_FRAME_THREAD_STATE_COUNT;
2585 kern_return_t okay = thread_get_state (thread, HPPA_FRAME_THREAD_STATE, (thread_state_t)&state, &count);
2586 return (okay == KERN_SUCCESS) ? state.ts_pcoq_front : PC_SENTINAL;
2587 }
2588 #elif defined(sparc)
2589 {
2590 struct sparc_thread_state_regs state;
2591 unsigned int count = SPARC_THREAD_STATE_REGS_COUNT;
2592 kern_return_t okay = thread_get_state (thread, SPARC_THREAD_STATE_REGS, (thread_state_t)&state, &count);
2593 return (okay == KERN_SUCCESS) ? state.regs.r_pc : PC_SENTINAL;
2594 }
2595 #elif defined(__i386__) || defined(i386)
2596 {
2597 i386_thread_state_t state;
2598 unsigned int count = i386_THREAD_STATE_COUNT;
2599 kern_return_t okay = thread_get_state (thread, i386_THREAD_STATE, (thread_state_t)&state, &count);
2600 return (okay == KERN_SUCCESS) ? state.eip : PC_SENTINAL;
2601 }
2602 #elif defined(m68k)
2603 {
2604 struct m68k_thread_state_regs state;
2605 unsigned int count = M68K_THREAD_STATE_REGS_COUNT;
2606 kern_return_t okay = thread_get_state (thread, M68K_THREAD_STATE_REGS, (thread_state_t)&state, &count);
2607 return (okay == KERN_SUCCESS) ? state.pc : PC_SENTINAL;
2608 }
2609 #elif defined(__ppc__) || defined(ppc)
2610 {
2611 struct ppc_thread_state state;
2612 unsigned int count = PPC_THREAD_STATE_COUNT;
2613 kern_return_t okay = thread_get_state (thread, PPC_THREAD_STATE, (thread_state_t)&state, &count);
2614 return (okay == KERN_SUCCESS) ? state.srr0 : PC_SENTINAL;
2615 }
2616 #else
2617 {
2618 #error _get_pc_for_thread () not implemented for this architecture
2619 }
2620 #endif
2621
2622 /***********************************************************************
2623 * _collecting_in_critical.
2624 * Returns TRUE if some thread is currently executing a cache-reading
2625 * function. Collection of cache garbage is not allowed when a cache-
2626 * reading function is in progress because it might still be using
2627 * the garbage memory.
2628 **********************************************************************/
2629 OBJC_EXPORT unsigned long objc_entryPoints[];
2630 OBJC_EXPORT unsigned long objc_exitPoints[];
2631
2632 static int _collecting_in_critical (void)
2633 {
2634 thread_act_port_array_t threads;
2635 unsigned number;
2636 unsigned count;
2637 kern_return_t ret;
2638 int result;
2639
2640 mach_port_t mythread = pthread_mach_thread_np(pthread_self());
2641
2642 // Get a list of all the threads in the current task
2643 ret = task_threads (mach_task_self (), &threads, &number);
2644 if (ret != KERN_SUCCESS)
2645 {
2646 _objc_fatal("task_thread failed (result %d)\n", ret);
2647 }
2648
2649 // Check whether any thread is in the cache lookup code
2650 result = FALSE;
2651 for (count = 0; count < number; count++)
2652 {
2653 int region;
2654 unsigned long pc;
2655
2656 // Don't bother checking ourselves
2657 if (threads[count] == mythread)
2658 continue;
2659
2660 // Find out where thread is executing
2661 pc = _get_pc_for_thread (threads[count]);
2662
2663 // Check for bad status, and if so, assume the worse (can't collect)
2664 if (pc == PC_SENTINAL)
2665 {
2666 result = TRUE;
2667 goto done;
2668 }
2669
2670 // Check whether it is in the cache lookup code
2671 for (region = 0; objc_entryPoints[region] != 0; region++)
2672 {
2673 if ((pc >= objc_entryPoints[region]) &&
2674 (pc <= objc_exitPoints[region]))
2675 {
2676 result = TRUE;
2677 goto done;
2678 }
2679 }
2680 }
2681
2682 done:
2683 // Deallocate the port rights for the threads
2684 for (count = 0; count < number; count++) {
2685 mach_port_deallocate(mach_task_self (), threads[count]);
2686 }
2687
2688 // Deallocate the thread list
2689 vm_deallocate (mach_task_self (), (vm_address_t) threads, sizeof(threads) * number);
2690
2691 // Return our finding
2692 return result;
2693 }
2694
2695 /***********************************************************************
2696 * _garbage_make_room. Ensure that there is enough room for at least
2697 * one more ref in the garbage.
2698 **********************************************************************/
2699
2700 // amount of memory represented by all refs in the garbage
2701 static int garbage_byte_size = 0;
2702
2703 // do not empty the garbage until garbage_byte_size gets at least this big
2704 static int garbage_threshold = 1024;
2705
2706 // table of refs to free
2707 static void **garbage_refs = 0;
2708
2709 // current number of refs in garbage_refs
2710 static int garbage_count = 0;
2711
2712 // capacity of current garbage_refs
2713 static int garbage_max = 0;
2714
2715 // capacity of initial garbage_refs
2716 enum {
2717 INIT_GARBAGE_COUNT = 128
2718 };
2719
2720 static void _garbage_make_room (void)
2721 {
2722 static int first = 1;
2723 volatile void * tempGarbage;
2724
2725 // Create the collection table the first time it is needed
2726 if (first)
2727 {
2728 first = 0;
2729 garbage_refs = malloc_zone_malloc (_objc_create_zone(),
2730 INIT_GARBAGE_COUNT * sizeof(void *));
2731 garbage_max = INIT_GARBAGE_COUNT;
2732 }
2733
2734 // Double the table if it is full
2735 else if (garbage_count == garbage_max)
2736 {
2737 tempGarbage = malloc_zone_realloc ((void *) _objc_create_zone(),
2738 (void *) garbage_refs,
2739 (size_t) garbage_max * 2 * sizeof(void *));
2740 garbage_refs = (void **) tempGarbage;
2741 garbage_max *= 2;
2742 }
2743 }
2744
2745 /***********************************************************************
2746 * _cache_collect_free. Add the specified malloc'd memory to the list
2747 * of them to free at some later point.
2748 * Cache locks: cacheUpdateLock must be held by the caller.
2749 **********************************************************************/
2750 static void _cache_collect_free (void * data,
2751 BOOL tryCollect)
2752 {
2753 static char *report_garbage = (char *)0xffffffff;
2754
2755 if ((char *)0xffffffff == report_garbage) {
2756 // Check whether to log our activity
2757 report_garbage = getenv ("OBJC_REPORT_GARBAGE");
2758 }
2759
2760 // Insert new element in garbage list
2761 // Note that we do this even if we end up free'ing everything
2762 _garbage_make_room ();
2763 garbage_byte_size += malloc_size (data);
2764 garbage_refs[garbage_count++] = data;
2765
2766 // Log our progress
2767 if (tryCollect && report_garbage)
2768 _objc_inform ("total of %d bytes of garbage ...", garbage_byte_size);
2769
2770 // Done if caller says not to empty or the garbage is not full
2771 if (!tryCollect || (garbage_byte_size < garbage_threshold))
2772 {
2773 if (tryCollect && report_garbage)
2774 _objc_inform ("couldn't collect cache garbage: below threshold\n");
2775
2776 return;
2777 }
2778
2779 // tryCollect is guaranteed to be true after this point
2780
2781 // Synchronize garbage collection with objc_msgSend and other cache readers
2782 if (!_collecting_in_critical ()) {
2783 // No cache readers in progress - garbage is now deletable
2784
2785 // Log our progress
2786 if (report_garbage)
2787 _objc_inform ("collecting!\n");
2788
2789 // Dispose all refs now in the garbage
2790 while (garbage_count)
2791 free (garbage_refs[--garbage_count]);
2792
2793 // Clear the total size indicator
2794 garbage_byte_size = 0;
2795 }
2796 else {
2797 // objc_msgSend (or other cache reader) is currently looking in the
2798 // cache and might still be using some garbage.
2799 if (report_garbage) {
2800 _objc_inform ("couldn't collect cache garbage: objc_msgSend in progress\n");
2801 }
2802 }
2803 }
2804
2805
2806 /***********************************************************************
2807 * _cache_print.
2808 **********************************************************************/
2809 static void _cache_print (Cache cache)
2810 {
2811 unsigned int index;
2812 unsigned int count;
2813
2814 count = cache->mask + 1;
2815 for (index = 0; index < count; index += 1)
2816 if (CACHE_BUCKET_VALID(cache->buckets[index]))
2817 {
2818 if (CACHE_BUCKET_IMP(cache->buckets[index]) == &_objc_msgForward)
2819 printf ("does not recognize: \n");
2820 printf ("%s\n", (const char *) CACHE_BUCKET_NAME(cache->buckets[index]));
2821 }
2822 }
2823
2824 /***********************************************************************
2825 * _class_printMethodCaches.
2826 **********************************************************************/
2827 void _class_printMethodCaches (Class cls)
2828 {
2829 if (((struct objc_class *)cls)->cache == &emptyCache)
2830 printf ("no instance-method cache for class %s\n", ((struct objc_class *)cls)->name);
2831
2832 else
2833 {
2834 printf ("instance-method cache for class %s:\n", ((struct objc_class *)cls)->name);
2835 _cache_print (((struct objc_class *)cls)->cache);
2836 }
2837
2838 if (((struct objc_class * )((struct objc_class * )cls)->isa)->cache == &emptyCache)
2839 printf ("no class-method cache for class %s\n", ((struct objc_class *)cls)->name);
2840
2841 else
2842 {
2843 printf ("class-method cache for class %s:\n", ((struct objc_class *)cls)->name);
2844 _cache_print (((struct objc_class * )((struct objc_class * )cls)->isa)->cache);
2845 }
2846 }
2847
2848 /***********************************************************************
2849 * log2.
2850 **********************************************************************/
2851 static unsigned int log2 (unsigned int x)
2852 {
2853 unsigned int log;
2854
2855 log = 0;
2856 while (x >>= 1)
2857 log += 1;
2858
2859 return log;
2860 }
2861
2862 /***********************************************************************
2863 * _class_printDuplicateCacheEntries.
2864 **********************************************************************/
2865 void _class_printDuplicateCacheEntries (BOOL detail)
2866 {
2867 NXHashTable * class_hash;
2868 NXHashState state;
2869 struct objc_class * cls;
2870 unsigned int duplicates;
2871 unsigned int index1;
2872 unsigned int index2;
2873 unsigned int mask;
2874 unsigned int count;
2875 unsigned int isMeta;
2876 Cache cache;
2877
2878
2879 printf ("Checking for duplicate cache entries \n");
2880
2881 // Outermost loop - iterate over all classes
2882 class_hash = objc_getClasses ();
2883 state = NXInitHashState (class_hash);
2884 duplicates = 0;
2885 while (NXNextHashState (class_hash, &state, (void **) &cls))
2886 {
2887 // Control loop - do given class' cache, then its isa's cache
2888 for (isMeta = 0; isMeta <= 1; isMeta += 1)
2889 {
2890 // Select cache of interest and make sure it exists
2891 cache = isMeta ? cls->isa->cache : ((struct objc_class *)cls)->cache;
2892 if (cache == &emptyCache)
2893 continue;
2894
2895 // Middle loop - check each entry in the given cache
2896 mask = cache->mask;
2897 count = mask + 1;
2898 for (index1 = 0; index1 < count; index1 += 1)
2899 {
2900 // Skip invalid entry
2901 if (!CACHE_BUCKET_VALID(cache->buckets[index1]))
2902 continue;
2903
2904 // Inner loop - check that given entry matches no later entry
2905 for (index2 = index1 + 1; index2 < count; index2 += 1)
2906 {
2907 // Skip invalid entry
2908 if (!CACHE_BUCKET_VALID(cache->buckets[index2]))
2909 continue;
2910
2911 // Check for duplication by method name comparison
2912 if (strcmp ((char *) CACHE_BUCKET_NAME(cache->buckets[index1]),
2913 (char *) CACHE_BUCKET_NAME(cache->buckets[index2])) == 0)
2914 {
2915 if (detail)
2916 printf ("%s %s\n", ((struct objc_class *)cls)->name, (char *) CACHE_BUCKET_NAME(cache->buckets[index1]));
2917 duplicates += 1;
2918 break;
2919 }
2920 }
2921 }
2922 }
2923 }
2924
2925 // Log the findings
2926 printf ("duplicates = %d\n", duplicates);
2927 printf ("total cache fills = %d\n", totalCacheFills);
2928 }
2929
2930 /***********************************************************************
2931 * PrintCacheHeader.
2932 **********************************************************************/
2933 static void PrintCacheHeader (void)
2934 {
2935 #ifdef OBJC_INSTRUMENTED
2936 printf ("Cache Cache Slots Avg Max AvgS MaxS AvgS MaxS TotalD AvgD MaxD TotalD AvgD MaxD TotD AvgD MaxD\n");
2937 printf ("Size Count Used Used Used Hit Hit Miss Miss Hits Prbs Prbs Misses Prbs Prbs Flsh Flsh Flsh\n");
2938 printf ("----- ----- ----- ----- ---- ---- ---- ---- ---- ------- ---- ---- ------- ---- ---- ---- ---- ----\n");
2939 #else
2940 printf ("Cache Cache Slots Avg Max AvgS MaxS AvgS MaxS\n");
2941 printf ("Size Count Used Used Used Hit Hit Miss Miss\n");
2942 printf ("----- ----- ----- ----- ---- ---- ---- ---- ----\n");
2943 #endif
2944 }
2945
2946 /***********************************************************************
2947 * PrintCacheInfo.
2948 **********************************************************************/
2949 static void PrintCacheInfo (unsigned int cacheSize,
2950 unsigned int cacheCount,
2951 unsigned int slotsUsed,
2952 float avgUsed,
2953 unsigned int maxUsed,
2954 float avgSHit,
2955 unsigned int maxSHit,
2956 float avgSMiss,
2957 unsigned int maxSMiss
2958 #ifdef OBJC_INSTRUMENTED
2959 , unsigned int totDHits,
2960 float avgDHit,
2961 unsigned int maxDHit,
2962 unsigned int totDMisses,
2963 float avgDMiss,
2964 unsigned int maxDMiss,
2965 unsigned int totDFlsh,
2966 float avgDFlsh,
2967 unsigned int maxDFlsh
2968 #endif
2969 )
2970 {
2971 #ifdef OBJC_INSTRUMENTED
2972 printf ("%5u %5u %5u %5.1f %4u %4.1f %4u %4.1f %4u %7u %4.1f %4u %7u %4.1f %4u %4u %4.1f %4u\n",
2973 #else
2974 printf ("%5u %5u %5u %5.1f %4u %4.1f %4u %4.1f %4u\n",
2975 #endif
2976 cacheSize, cacheCount, slotsUsed, avgUsed, maxUsed, avgSHit, maxSHit, avgSMiss, maxSMiss
2977 #ifdef OBJC_INSTRUMENTED
2978 , totDHits, avgDHit, maxDHit, totDMisses, avgDMiss, maxDMiss, totDFlsh, avgDFlsh, maxDFlsh
2979 #endif
2980 );
2981
2982 }
2983
2984 #ifdef OBJC_INSTRUMENTED
2985 /***********************************************************************
2986 * PrintCacheHistogram. Show the non-zero entries from the specified
2987 * cache histogram.
2988 **********************************************************************/
2989 static void PrintCacheHistogram (char * title,
2990 unsigned int * firstEntry,
2991 unsigned int entryCount)
2992 {
2993 unsigned int index;
2994 unsigned int * thisEntry;
2995
2996 printf ("%s\n", title);
2997 printf (" Probes Tally\n");
2998 printf (" ------ -----\n");
2999 for (index = 0, thisEntry = firstEntry;
3000 index < entryCount;
3001 index += 1, thisEntry += 1)
3002 {
3003 if (*thisEntry == 0)
3004 continue;
3005
3006 printf (" %6d %5d\n", index, *thisEntry);
3007 }
3008 }
3009 #endif
3010
3011 /***********************************************************************
3012 * _class_printMethodCacheStatistics.
3013 **********************************************************************/
3014
3015 #define MAX_LOG2_SIZE 32
3016 #define MAX_CHAIN_SIZE 100
3017
3018 void _class_printMethodCacheStatistics (void)
3019 {
3020 unsigned int isMeta;
3021 unsigned int index;
3022 NXHashTable * class_hash;
3023 NXHashState state;
3024 struct objc_class * cls;
3025 unsigned int totalChain;
3026 unsigned int totalMissChain;
3027 unsigned int maxChain;
3028 unsigned int maxMissChain;
3029 unsigned int classCount;
3030 unsigned int negativeEntryCount;
3031 unsigned int cacheExpandCount;
3032 unsigned int cacheCountBySize[2][MAX_LOG2_SIZE] = {{0}};
3033 unsigned int totalEntriesBySize[2][MAX_LOG2_SIZE] = {{0}};
3034 unsigned int maxEntriesBySize[2][MAX_LOG2_SIZE] = {{0}};
3035 unsigned int totalChainBySize[2][MAX_LOG2_SIZE] = {{0}};
3036 unsigned int totalMissChainBySize[2][MAX_LOG2_SIZE] = {{0}};
3037 unsigned int totalMaxChainBySize[2][MAX_LOG2_SIZE] = {{0}};
3038 unsigned int totalMaxMissChainBySize[2][MAX_LOG2_SIZE] = {{0}};
3039 unsigned int maxChainBySize[2][MAX_LOG2_SIZE] = {{0}};
3040 unsigned int maxMissChainBySize[2][MAX_LOG2_SIZE] = {{0}};
3041 unsigned int chainCount[MAX_CHAIN_SIZE] = {0};
3042 unsigned int missChainCount[MAX_CHAIN_SIZE] = {0};
3043 #ifdef OBJC_INSTRUMENTED
3044 unsigned int hitCountBySize[2][MAX_LOG2_SIZE] = {{0}};
3045 unsigned int hitProbesBySize[2][MAX_LOG2_SIZE] = {{0}};
3046 unsigned int maxHitProbesBySize[2][MAX_LOG2_SIZE] = {{0}};
3047 unsigned int missCountBySize[2][MAX_LOG2_SIZE] = {{0}};
3048 unsigned int missProbesBySize[2][MAX_LOG2_SIZE] = {{0}};
3049 unsigned int maxMissProbesBySize[2][MAX_LOG2_SIZE] = {{0}};
3050 unsigned int flushCountBySize[2][MAX_LOG2_SIZE] = {{0}};
3051 unsigned int flushedEntriesBySize[2][MAX_LOG2_SIZE] = {{0}};
3052 unsigned int maxFlushedEntriesBySize[2][MAX_LOG2_SIZE] = {{0}};
3053 #endif
3054
3055 printf ("Printing cache statistics\n");
3056
3057 // Outermost loop - iterate over all classes
3058 class_hash = objc_getClasses ();
3059 state = NXInitHashState (class_hash);
3060 classCount = 0;
3061 negativeEntryCount = 0;
3062 cacheExpandCount = 0;
3063 while (NXNextHashState (class_hash, &state, (void **) &cls))
3064 {
3065 // Tally classes
3066 classCount += 1;
3067
3068 // Control loop - do given class' cache, then its isa's cache
3069 for (isMeta = 0; isMeta <= 1; isMeta += 1)
3070 {
3071 Cache cache;
3072 unsigned int mask;
3073 unsigned int log2Size;
3074 unsigned int entryCount;
3075
3076 // Select cache of interest
3077 cache = isMeta ? cls->isa->cache : ((struct objc_class *)cls)->cache;
3078
3079 // Ignore empty cache... should we?
3080 if (cache == &emptyCache)
3081 continue;
3082
3083 // Middle loop - do each entry in the given cache
3084 mask = cache->mask;
3085 entryCount = 0;
3086 totalChain = 0;
3087 totalMissChain = 0;
3088 maxChain = 0;
3089 maxMissChain = 0;
3090 for (index = 0; index < mask + 1; index += 1)
3091 {
3092 Method * buckets;
3093 Method method;
3094 uarith_t hash;
3095 uarith_t methodChain;
3096 uarith_t methodMissChain;
3097 uarith_t index2;
3098
3099 // If entry is invalid, the only item of
3100 // interest is that future insert hashes
3101 // to this entry can use it directly.
3102 buckets = cache->buckets;
3103 if (!CACHE_BUCKET_VALID(buckets[index]))
3104 {
3105 missChainCount[0] += 1;
3106 continue;
3107 }
3108
3109 method = buckets[index];
3110
3111 // Tally valid entries
3112 entryCount += 1;
3113
3114 // Tally "forward::" entries
3115 if (CACHE_BUCKET_IMP(method) == &_objc_msgForward)
3116 negativeEntryCount += 1;
3117
3118 // Calculate search distance (chain length) for this method
3119 // The chain may wrap around to the beginning of the table.
3120 hash = CACHE_HASH(CACHE_BUCKET_NAME(method), mask);
3121 if (index >= hash) methodChain = index - hash;
3122 else methodChain = (mask+1) + index - hash;
3123
3124 // Tally chains of this length
3125 if (methodChain < MAX_CHAIN_SIZE)
3126 chainCount[methodChain] += 1;
3127
3128 // Keep sum of all chain lengths
3129 totalChain += methodChain;
3130
3131 // Record greatest chain length
3132 if (methodChain > maxChain)
3133 maxChain = methodChain;
3134
3135 // Calculate search distance for miss that hashes here
3136 index2 = index;
3137 while (CACHE_BUCKET_VALID(buckets[index2]))
3138 {
3139 index2 += 1;
3140 index2 &= mask;
3141 }
3142 methodMissChain = ((index2 - index) & mask);
3143
3144 // Tally miss chains of this length
3145 if (methodMissChain < MAX_CHAIN_SIZE)
3146 missChainCount[methodMissChain] += 1;
3147
3148 // Keep sum of all miss chain lengths in this class
3149 totalMissChain += methodMissChain;
3150
3151 // Record greatest miss chain length
3152 if (methodMissChain > maxMissChain)
3153 maxMissChain = methodMissChain;
3154 }
3155
3156 // Factor this cache into statistics about caches of the same
3157 // type and size (all caches are a power of two in size)
3158 log2Size = log2 (mask + 1);
3159 cacheCountBySize[isMeta][log2Size] += 1;
3160 totalEntriesBySize[isMeta][log2Size] += entryCount;
3161 if (entryCount > maxEntriesBySize[isMeta][log2Size])
3162 maxEntriesBySize[isMeta][log2Size] = entryCount;
3163 totalChainBySize[isMeta][log2Size] += totalChain;
3164 totalMissChainBySize[isMeta][log2Size] += totalMissChain;
3165 totalMaxChainBySize[isMeta][log2Size] += maxChain;
3166 totalMaxMissChainBySize[isMeta][log2Size] += maxMissChain;
3167 if (maxChain > maxChainBySize[isMeta][log2Size])
3168 maxChainBySize[isMeta][log2Size] = maxChain;
3169 if (maxMissChain > maxMissChainBySize[isMeta][log2Size])
3170 maxMissChainBySize[isMeta][log2Size] = maxMissChain;
3171 #ifdef OBJC_INSTRUMENTED
3172 {
3173 CacheInstrumentation * cacheData;
3174
3175 cacheData = CACHE_INSTRUMENTATION(cache);
3176 hitCountBySize[isMeta][log2Size] += cacheData->hitCount;
3177 hitProbesBySize[isMeta][log2Size] += cacheData->hitProbes;
3178 if (cacheData->maxHitProbes > maxHitProbesBySize[isMeta][log2Size])
3179 maxHitProbesBySize[isMeta][log2Size] = cacheData->maxHitProbes;
3180 missCountBySize[isMeta][log2Size] += cacheData->missCount;
3181 missProbesBySize[isMeta][log2Size] += cacheData->missProbes;
3182 if (cacheData->maxMissProbes > maxMissProbesBySize[isMeta][log2Size])
3183 maxMissProbesBySize[isMeta][log2Size] = cacheData->maxMissProbes;
3184 flushCountBySize[isMeta][log2Size] += cacheData->flushCount;
3185 flushedEntriesBySize[isMeta][log2Size] += cacheData->flushedEntries;
3186 if (cacheData->maxFlushedEntries > maxFlushedEntriesBySize[isMeta][log2Size])
3187 maxFlushedEntriesBySize[isMeta][log2Size] = cacheData->maxFlushedEntries;
3188 }
3189 #endif
3190 // Caches start with a power of two number of entries, and grow by doubling, so
3191 // we can calculate the number of times this cache has expanded
3192 if (isMeta)
3193 cacheExpandCount += log2Size - INIT_META_CACHE_SIZE_LOG2;
3194 else
3195 cacheExpandCount += log2Size - INIT_CACHE_SIZE_LOG2;
3196
3197 }
3198 }
3199
3200 {
3201 unsigned int cacheCountByType[2] = {0};
3202 unsigned int totalCacheCount = 0;
3203 unsigned int totalEntries = 0;
3204 unsigned int maxEntries = 0;
3205 unsigned int totalSlots = 0;
3206 #ifdef OBJC_INSTRUMENTED
3207 unsigned int totalHitCount = 0;
3208 unsigned int totalHitProbes = 0;
3209 unsigned int maxHitProbes = 0;
3210 unsigned int totalMissCount = 0;
3211 unsigned int totalMissProbes = 0;
3212 unsigned int maxMissProbes = 0;
3213 unsigned int totalFlushCount = 0;
3214 unsigned int totalFlushedEntries = 0;
3215 unsigned int maxFlushedEntries = 0;
3216 #endif
3217
3218 totalChain = 0;
3219 maxChain = 0;
3220 totalMissChain = 0;
3221 maxMissChain = 0;
3222
3223 // Sum information over all caches
3224 for (isMeta = 0; isMeta <= 1; isMeta += 1)
3225 {
3226 for (index = 0; index < MAX_LOG2_SIZE; index += 1)
3227 {
3228 cacheCountByType[isMeta] += cacheCountBySize[isMeta][index];
3229 totalEntries += totalEntriesBySize[isMeta][index];
3230 totalSlots += cacheCountBySize[isMeta][index] * (1 << index);
3231 totalChain += totalChainBySize[isMeta][index];
3232 if (maxEntriesBySize[isMeta][index] > maxEntries)
3233 maxEntries = maxEntriesBySize[isMeta][index];
3234 if (maxChainBySize[isMeta][index] > maxChain)
3235 maxChain = maxChainBySize[isMeta][index];
3236 totalMissChain += totalMissChainBySize[isMeta][index];
3237 if (maxMissChainBySize[isMeta][index] > maxMissChain)
3238 maxMissChain = maxMissChainBySize[isMeta][index];
3239 #ifdef OBJC_INSTRUMENTED
3240 totalHitCount += hitCountBySize[isMeta][index];
3241 totalHitProbes += hitProbesBySize[isMeta][index];
3242 if (maxHitProbesBySize[isMeta][index] > maxHitProbes)
3243 maxHitProbes = maxHitProbesBySize[isMeta][index];
3244 totalMissCount += missCountBySize[isMeta][index];
3245 totalMissProbes += missProbesBySize[isMeta][index];
3246 if (maxMissProbesBySize[isMeta][index] > maxMissProbes)
3247 maxMissProbes = maxMissProbesBySize[isMeta][index];
3248 totalFlushCount += flushCountBySize[isMeta][index];
3249 totalFlushedEntries += flushedEntriesBySize[isMeta][index];
3250 if (maxFlushedEntriesBySize[isMeta][index] > maxFlushedEntries)
3251 maxFlushedEntries = maxFlushedEntriesBySize[isMeta][index];
3252 #endif
3253 }
3254
3255 totalCacheCount += cacheCountByType[isMeta];
3256 }
3257
3258 // Log our findings
3259 printf ("There are %u classes\n", classCount);
3260
3261 for (isMeta = 0; isMeta <= 1; isMeta += 1)
3262 {
3263 // Number of this type of class
3264 printf ("\nThere are %u %s-method caches, broken down by size (slot count):\n",
3265 cacheCountByType[isMeta],
3266 isMeta ? "class" : "instance");
3267
3268 // Print header
3269 PrintCacheHeader ();
3270
3271 // Keep format consistent even if there are caches of this kind
3272 if (cacheCountByType[isMeta] == 0)
3273 {
3274 printf ("(none)\n");
3275 continue;
3276 }
3277
3278 // Usage information by cache size
3279 for (index = 0; index < MAX_LOG2_SIZE; index += 1)
3280 {
3281 unsigned int cacheCount;
3282 unsigned int cacheSlotCount;
3283 unsigned int cacheEntryCount;
3284
3285 // Get number of caches of this type and size
3286 cacheCount = cacheCountBySize[isMeta][index];
3287 if (cacheCount == 0)
3288 continue;
3289
3290 // Get the cache slot count and the total number of valid entries
3291 cacheSlotCount = (1 << index);
3292 cacheEntryCount = totalEntriesBySize[isMeta][index];
3293
3294 // Give the analysis
3295 PrintCacheInfo (cacheSlotCount,
3296 cacheCount,
3297 cacheEntryCount,
3298 (float) cacheEntryCount / (float) cacheCount,
3299 maxEntriesBySize[isMeta][index],
3300 (float) totalChainBySize[isMeta][index] / (float) cacheEntryCount,
3301 maxChainBySize[isMeta][index],
3302 (float) totalMissChainBySize[isMeta][index] / (float) (cacheCount * cacheSlotCount),
3303 maxMissChainBySize[isMeta][index]
3304 #ifdef OBJC_INSTRUMENTED
3305 , hitCountBySize[isMeta][index],
3306 hitCountBySize[isMeta][index] ?
3307 (float) hitProbesBySize[isMeta][index] / (float) hitCountBySize[isMeta][index] : 0.0,
3308 maxHitProbesBySize[isMeta][index],
3309 missCountBySize[isMeta][index],
3310 missCountBySize[isMeta][index] ?
3311 (float) missProbesBySize[isMeta][index] / (float) missCountBySize[isMeta][index] : 0.0,
3312 maxMissProbesBySize[isMeta][index],
3313 flushCountBySize[isMeta][index],
3314 flushCountBySize[isMeta][index] ?
3315 (float) flushedEntriesBySize[isMeta][index] / (float) flushCountBySize[isMeta][index] : 0.0,
3316 maxFlushedEntriesBySize[isMeta][index]
3317 #endif
3318 );
3319 }
3320 }
3321
3322 // Give overall numbers
3323 printf ("\nCumulative:\n");
3324 PrintCacheHeader ();
3325 PrintCacheInfo (totalSlots,
3326 totalCacheCount,
3327 totalEntries,
3328 (float) totalEntries / (float) totalCacheCount,
3329 maxEntries,
3330 (float) totalChain / (float) totalEntries,
3331 maxChain,
3332 (float) totalMissChain / (float) totalSlots,
3333 maxMissChain
3334 #ifdef OBJC_INSTRUMENTED
3335 , totalHitCount,
3336 totalHitCount ?
3337 (float) totalHitProbes / (float) totalHitCount : 0.0,
3338 maxHitProbes,
3339 totalMissCount,
3340 totalMissCount ?
3341 (float) totalMissProbes / (float) totalMissCount : 0.0,
3342 maxMissProbes,
3343 totalFlushCount,
3344 totalFlushCount ?
3345 (float) totalFlushedEntries / (float) totalFlushCount : 0.0,
3346 maxFlushedEntries
3347 #endif
3348 );
3349
3350 printf ("\nNumber of \"forward::\" entries: %d\n", negativeEntryCount);
3351 printf ("Number of cache expansions: %d\n", cacheExpandCount);
3352 #ifdef OBJC_INSTRUMENTED
3353 printf ("flush_caches: total calls total visits average visits max visits total classes visits/class\n");
3354 printf (" ----------- ------------ -------------- ---------- ------------- -------------\n");
3355 printf (" linear %11u %12u %14.1f %10u %13u %12.2f\n",
3356 LinearFlushCachesCount,
3357 LinearFlushCachesVisitedCount,
3358 LinearFlushCachesCount ?
3359 (float) LinearFlushCachesVisitedCount / (float) LinearFlushCachesCount : 0.0,
3360 MaxLinearFlushCachesVisitedCount,
3361 LinearFlushCachesVisitedCount,
3362 1.0);
3363 printf (" nonlinear %11u %12u %14.1f %10u %13u %12.2f\n",
3364 NonlinearFlushCachesCount,
3365 NonlinearFlushCachesVisitedCount,
3366 NonlinearFlushCachesCount ?
3367 (float) NonlinearFlushCachesVisitedCount / (float) NonlinearFlushCachesCount : 0.0,
3368 MaxNonlinearFlushCachesVisitedCount,
3369 NonlinearFlushCachesClassCount,
3370 NonlinearFlushCachesClassCount ?
3371 (float) NonlinearFlushCachesVisitedCount / (float) NonlinearFlushCachesClassCount : 0.0);
3372 printf (" ideal %11u %12u %14.1f %10u %13u %12.2f\n",
3373 LinearFlushCachesCount + NonlinearFlushCachesCount,
3374 IdealFlushCachesCount,
3375 LinearFlushCachesCount + NonlinearFlushCachesCount ?
3376 (float) IdealFlushCachesCount / (float) (LinearFlushCachesCount + NonlinearFlushCachesCount) : 0.0,
3377 MaxIdealFlushCachesCount,
3378 LinearFlushCachesVisitedCount + NonlinearFlushCachesClassCount,
3379 LinearFlushCachesVisitedCount + NonlinearFlushCachesClassCount ?
3380 (float) IdealFlushCachesCount / (float) (LinearFlushCachesVisitedCount + NonlinearFlushCachesClassCount) : 0.0);
3381
3382 PrintCacheHistogram ("\nCache hit histogram:", &CacheHitHistogram[0], CACHE_HISTOGRAM_SIZE);
3383 PrintCacheHistogram ("\nCache miss histogram:", &CacheMissHistogram[0], CACHE_HISTOGRAM_SIZE);
3384 #endif
3385
3386 #if 0
3387 printf ("\nLookup chains:");
3388 for (index = 0; index < MAX_CHAIN_SIZE; index += 1)
3389 {
3390 if (chainCount[index] != 0)
3391 printf (" %u:%u", index, chainCount[index]);
3392 }
3393
3394 printf ("\nMiss chains:");
3395 for (index = 0; index < MAX_CHAIN_SIZE; index += 1)
3396 {
3397 if (missChainCount[index] != 0)
3398 printf (" %u:%u", index, missChainCount[index]);
3399 }
3400
3401 printf ("\nTotal memory usage for cache data structures: %lu bytes\n",
3402 totalCacheCount * (sizeof(struct objc_cache) - sizeof(Method)) +
3403 totalSlots * sizeof(Method) +
3404 negativeEntryCount * sizeof(struct objc_method));
3405 #endif
3406 }
3407 }
3408
3409 /***********************************************************************
3410 * checkUniqueness.
3411 **********************************************************************/
3412 void checkUniqueness (SEL s1,
3413 SEL s2)
3414 {
3415 if (s1 == s2)
3416 return;
3417
3418 if (s1 && s2 && (strcmp ((const char *) s1, (const char *) s2) == 0))
3419 _objc_inform ("%p != %p but !strcmp (%s, %s)\n", s1, s2, (char *) s1, (char *) s2);
3420 }
3421