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