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