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