|
|
1.1 root 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.0 (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, NeXT, Inc.
27: * Author: s. naroff
28: *
29: */
30:
31: static int _class_uncache = 1;
32: static int _class_slow_grow = 1;
33:
34: #import <mach/mach_interface.h>
35:
36: #ifdef SHLIB
37: #import "shlib.h"
38: #endif SHLIB
39:
40: #include <mach-o/ldsyms.h>
41: #import "objc-private.h"
42: #ifdef RUNTIME_DYLD
43: #include <mach-o/dyld.h>
44: #endif
45: #import "objc-runtime.h"
46: #import "objc.h"
47: #import "Object.h"
48: #import "maptable.h"
49:
50: #ifdef RUNTIME_DYLD
51: void *getsectdatafromheaderinfo(const struct header_info * info,
52: char * segname,
53: char * sectname,
54: int * size);
55: void _objc_bindModuleContainingList (struct objc_method_list*);
56: #endif
57: void *getsectdatafromheader(const struct mach_header *, char *, char *, int *);
58:
59: #ifdef KERNEL
60:
61: #undef bzero(x,y)
62: #define vm_page_size 8192
63:
64: #else /* KERNEL */
65:
66: #import <mach/mach.h>
67: #import <mach/thread_status.h>
68:
69: #endif /* KERNEL */
70:
71: /* CACHE_SIZE and META_CACHE_SIZE must be a power of two */
72: #define CACHE_SIZE 4 // let's try 4 instead (was 16)
73: #define META_CACHE_SIZE 4 // let's try 4 instead (8)
74:
75: #define ISCLASS(cls) ((cls)->info & CLS_CLASS)
76: #define ISMETA(cls) ((cls)->info & CLS_META)
77: #define GETMETA(cls) (ISMETA(cls) ? cls : cls->isa)
78:
79: #define ISINITIALIZED(cls) (GETMETA(cls)->info & CLS_INITIALIZED)
80: #define MARKINITIALIZED(cls) (GETMETA(cls)->info |= CLS_INITIALIZED)
81:
82: static void _cache_fill(Class, Method);
83: static Cache _cache_expand(Class);
84: static void _cache_flush (Class);
85:
86: #ifdef OBJC_COLLECTING_CACHE
87: static void _cache_collect_free(void *data, BOOL tryCollect);
88: #endif
89:
90: // Error Messages
91: static const char
92: _errNoMem[] = "failed -- out of memory(%s, %u)",
93: _errAllocNil[] = "allocating nil object",
94: _errFreedObject[] = "message %s sent to freed object=0x%lx",
95: #if RUNTIME_DYLD
96: _errNonExistentObject[] = "message %s sent to non-existent object=0x%lx",
97: #endif
98: _errBadSel[] = "invalid selector %s",
99: #if 0
100: _errDoesntRecognize[] = "Does not recognize selector %s",
101: #endif
102: _errNotSuper[] = "[%s poseAs:%s]: target not immediate superclass",
103: _errNewVars[] = "[%s poseAs:%s]: %s defines new instance variables";
104:
105: /* Information about multithtread support:
106:
107: Since we do not lock many operations which walk the superclass, method
108: and ivar chains, these chains must remain intact once a class is published
109: by inserting it into the class hashtable. All modifications must be
110: atomic so that someone walking these chains will always geta valid
111: result. */
112:
113: /* Lock for messaging. (Private extern) */
114:
115: #ifdef OBJC_COLLECTING_CACHE
116: static OBJC_DECLARE_LOCK (cacheUpdateLock);
117: static OBJC_DECLARE_LOCK (cacheCollectionLock);
118: #endif
119:
120: /* This is the read lock */
121: OBJC_DECLARE_LOCK (messageLock);
122:
123:
124: static void *objc_malloc (int size);
125:
126:
127: /* A static empty cache. All classes initially point at this cache.
128: When the first message is sent it misses in the cache, and when
129: the cache is grown it checks for this case and uses malloc rather
130: than realloc. This avoids the need to check for NULL caches in the
131: messenger. */
132:
133: const struct objc_cache emptyCache =
134: {
135: 0, /* mask */
136: 0, /* occupied */
137: #ifdef OBJC_COPY_CACHE
138: { 0, 0 } /* buckets */
139: #else
140: { NULL } /* buckets */
141: #endif
142: };
143:
144: /* Freed objects have their isa set to point to this dummy class.
145: This avoids the need to check for Nil classes in the messenger. */
146:
147: static const struct objc_class freedObjectClass =
148: {
149: Nil, /* isa */
150: Nil, /* super_class */
151: "FREED(id)", /* name */
152: 0, /* version */
153: 0, /* info */
154: 0, /* instance_size */
155: NULL, /* ivars */
156: NULL, /* methods */
157: (Cache) &emptyCache, /* cache */
158: NULL /* protocols */
159: };
160:
161: #ifdef RUNTIME_DYLD
162:
163: static const struct objc_class nonexistentObjectClass =
164: {
165: Nil, /* isa */
166: Nil, /* super_class */
167: "NONEXISTENT(id)", /* name */
168: 0, /* version */
169: 0, /* info */
170: 0, /* instance_size */
171: NULL, /* ivars */
172: NULL, /* methods */
173: (Cache) &emptyCache, /* cache */
174: NULL /* protocols */
175: };
176:
177: #endif
178:
179: const char *object_getClassName(id obj)
180: {
181: if (obj == nil)
182: return "nil";
183: else
184: return ((Class)obj->isa)->name;
185: }
186:
187: void *object_getIndexedIvars(id obj)
188: {
189: return ((char *)obj) + obj->isa->instance_size;
190: }
191:
192: // Allocate new instance of aClass with nBytes bytes of indexed vars
193:
194: id _internal_class_createInstanceFromZone(Class aClass, unsigned nBytes, NXZone *zone)
195: {
196: id obj;
197: register unsigned siz;
198:
199: if (aClass == Nil)
200: __objc_error((id)aClass, _errAllocNil, 0);
201:
202: siz = aClass->instance_size + nBytes;
203:
204: if ((obj = (id)NXZoneMalloc(zone, siz))) {
205: bzero((char *)obj, siz);
206: obj->isa = aClass;
207: return obj;
208: } else {
209: __objc_error((id)aClass, _errNoMem, aClass->name, nBytes);
210: return nil;
211: }
212: }
213:
214: id class_createInstanceFromZone(Class aClass, unsigned nBytes, NXZone *zone)
215: {
216: return (*_zoneAlloc)(aClass, nBytes, zone);
217: }
218:
219: id _internal_class_createInstance(Class aClass, unsigned nBytes)
220: {
221: return _internal_class_createInstanceFromZone(aClass, nBytes, NXDefaultMallocZone());
222: }
223:
224: id class_createInstance(Class aClass, unsigned nBytes)
225: {
226: return (*_alloc)(aClass, nBytes);
227: }
228:
229: void class_setVersion(Class aClass, int version)
230: {
231: aClass->version = version;
232: }
233:
234: int class_getVersion(Class aClass)
235: {
236: return aClass->version;
237: }
238:
239: static inline Method class_getMethod(Class cls, SEL sel)
240: {
241: do {
242: register int n;
243: register Method smt;
244: struct objc_method_list *mlist;
245:
246: for (mlist = cls->methods; mlist; mlist = mlist->method_next) {
247: smt = mlist->method_list;
248: n = mlist->method_count;
249:
250: while (--n >= 0) {
251: if (sel == smt->method_name)
252: #ifdef RUNTIME_DYLD
253: {
254: _objc_bindModuleContainingList (mlist);
255: return smt;
256: }
257: #else
258: return smt;
259: #endif
260: smt++;
261: }
262: }
263: } while ((cls = cls->super_class));
264:
265: return 0;
266: }
267:
268: Method class_getInstanceMethod(Class aClass, SEL aSelector)
269: {
270: if (aClass && aSelector)
271: return class_getMethod(aClass, aSelector);
272: else
273: return 0;
274: }
275:
276: Method class_getClassMethod(Class aClass, SEL aSelector)
277: {
278: if (aClass && aSelector)
279: return class_getMethod(GETMETA(aClass), aSelector);
280: else
281: return 0;
282: }
283:
284: static Ivar class_getVariable(Class cls, const char *name)
285: {
286: do {
287: if (cls->ivars) {
288: int i;
289: Ivar ivars = cls->ivars->ivar_list;
290:
291: for (i = 0; i < cls->ivars->ivar_count; i++)
292: if (strcmp(name,ivars[i].ivar_name) == 0)
293: return &ivars[i];
294: }
295: } while ((cls = cls->super_class));
296:
297: return 0;
298: }
299:
300: Ivar class_getInstanceVariable(Class aClass, const char *name)
301: {
302: if (aClass && name)
303: return class_getVariable(aClass, name);
304: else
305: return 0;
306: }
307:
308: /* Someday add class_getClassVariable(). */
309:
310:
311: /* Flush the instance and optionally class method caches of all subclasses. */
312:
313: static void flush_caches(Class cls, BOOL flush_meta)
314: {
315: NXHashTable *class_hash;
316: NXHashState state;
317: Class clsObject;
318:
319: if (cls->cache == 0)
320: return;
321:
322: OBJC_LOCK (&classLock);
323:
324: class_hash = objc_getClasses();
325: state = NXInitHashState(class_hash);
326:
327: while (NXNextHashState(class_hash, &state, (void **)&clsObject)) {
328:
329: Class clsIter = clsObject;
330:
331: while (clsIter) {
332:
333: if (clsIter == cls) {
334: // it is `aKindOf' the class that has
335: // been modified - flush!
336: _cache_flush (clsObject);
337: if (flush_meta)
338: _cache_flush (clsObject->isa);
339: clsIter = 0;
340: } else if (clsIter->isa == cls) {
341: _cache_flush (clsObject);
342: clsIter = 0;
343: } else if (CLS_GETINFO(clsIter, CLS_INITIALIZED)) {
344: clsIter = clsIter->super_class;
345: } else {
346: clsIter = 0;
347: }
348: }
349: }
350:
351: OBJC_UNLOCK (&classLock);
352: }
353:
354: /* Private extern. Flush instance and class method caches. */
355:
356: void _objc_flush_caches (Class cls)
357: {
358: flush_caches (cls, YES);
359: }
360:
361: /* Formerly class_addInstanceMethods(). */
362:
363: void class_addMethods(Class cls, struct objc_method_list *meths)
364: {
365: // Insert atomically.
366: meths->method_next = cls->methods;
367: cls->methods = meths;
368: // must flush when dynamically adding methods.
369: flush_caches (cls, NO);
370: }
371:
372: /* Obsolete (for binary compatibility only). */
373:
374: void class_addClassMethods(Class cls, struct objc_method_list *meths)
375: {
376: class_addMethods (cls->isa, meths);
377: }
378:
379: void class_removeMethods(Class cls, struct objc_method_list *meths)
380: {
381: // Remove atomically.
382: if (cls->methods == meths) {
383: /* it is at the front of the list - take it out */
384: cls->methods = meths->method_next;
385: } else {
386: struct objc_method_list *mlist, *prev;
387:
388: /* it is not at the front of the list - look for it */
389: prev = cls->methods;
390: mlist = prev->method_next;
391:
392: while (mlist) {
393: if (mlist == meths) {
394: prev->method_next = mlist->method_next;
395: mlist = 0;
396: } else {
397: prev = mlist;
398: mlist = prev->method_next;
399: }
400: }
401: }
402: // must flush when dynamically removing methods.
403: flush_caches (cls, NO);
404: }
405:
406: /* Private extern */
407: void _class_removeProtocols(Class cls, struct objc_protocol_list *protos)
408: {
409: // Remove atomically.
410: if (cls->protocols == protos) {
411: /* it is at the front of the list - take it out */
412: cls->protocols = protos->next;
413: } else {
414: struct objc_protocol_list *plist, *prev;
415:
416: /* it is not at the front of the list - look for it */
417: prev = cls->protocols;
418: plist = prev->next;
419:
420: while (plist) {
421: if (plist == protos) {
422: prev->next = plist->next;
423: plist = 0;
424: } else {
425: prev = plist;
426: plist = prev->next;
427: }
428: }
429: }
430: }
431:
432:
433: /*
434: * This is a hash table of classes involved in a posing
435: * situation. We use this when we need to get to the "original"
436: * class for some particular name though the function
437: * objc_getOrigClass. For instance, the implementation of
438: * [super ...] will use this to be sure that it gets hold of the
439: * correct super class, so that no infinite loops will occur
440: * if the class it appears in is involved in posing.
441: * We use the classLock to guard this hash table.
442: * See tracker bug #51856.
443: */
444:
445: static NXHashTable *posed_class_hash = 0;
446:
447:
448:
449: Class
450: objc_getOrigClass (const char *name)
451: {
452: Class ret = 0;
453:
454: OBJC_LOCK (&classLock);
455: if (posed_class_hash)
456: {
457: ret = (Class)NXMapGet(posed_class_hash, name);
458: }
459: OBJC_UNLOCK (&classLock);
460:
461: if (ret == nil)
462: {
463: ret = objc_getClass (name);
464: }
465:
466: return ret;
467: }
468:
469: /*
470: * This function is only used from class_poseAs. It's purpose is to
471: * register the original class names, before they get obscured by
472: * posing, so that [super ..] will work correctly from categories
473: * in posing classes and in categories in classes being posed for.
474: */
475:
476: static void
477: _objc_addOrigClass (Class origClass)
478: {
479: OBJC_LOCK (&classLock);
480:
481: if (posed_class_hash == 0)
482: {
483: posed_class_hash = NXCreateMapTableFromZone (NXStrValueMapPrototype,
484: 8,
485: _objc_create_zone ());
486: }
487:
488: if (NXMapGet(posed_class_hash, origClass->name) == 0)
489: {
490: NXMapInsert(posed_class_hash, origClass->name, origClass);
491: }
492:
493: OBJC_UNLOCK (&classLock);
494: }
495:
496: /* !!! class_poseAs() does not currently flush any caches. */
497:
498: Class class_poseAs(Class imposter, Class original)
499: {
500: Class clsObject;
501: char imposterName[256], *imposterNamePtr;
502: NXHashTable *class_hash;
503: NXHashState state;
504: Class copy;
505: unsigned int hidx, header_count = _objc_headerCount ();
506: struct header_info *header_vector = _objc_headerVector (NULL);
507:
508: if (imposter == original)
509: return imposter;
510:
511: if (imposter->super_class != original)
512: return (Class)[(id)imposter error:_errNotSuper,
513: imposter->name, original->name];
514: if (imposter->ivars)
515: return (Class)[(id)imposter error:_errNewVars, imposter->name,
516: original->name, imposter->name];
517:
518: // Build a string to use to replace the name of the original class.
519: strcpy (imposterName, "_%");
520: strcat (imposterName, original->name);
521:
522: imposterNamePtr = objc_malloc (strlen(imposterName)+1);
523: strcpy (imposterNamePtr, imposterName);
524:
525: // We lock the class hashtable, so we are thread safe with respect to
526: // calls to objc_getClass(). However, the class names are not
527: // changed atomically, nor are all of the subclasses updated
528: // atomically. I have ordered the operations so that you will
529: // never crash, but you may get inconsistent results....
530:
531: // register the original class so that [super ..] knows
532: // exactly which classes are the "original" classes.
533: _objc_addOrigClass (original);
534: _objc_addOrigClass (imposter);
535:
536: OBJC_LOCK (&classLock);
537:
538: class_hash = objc_getClasses ();
539:
540: // Remove both the imposter and the original class.
541: NXHashRemove(class_hash, imposter);
542: NXHashRemove(class_hash, original);
543:
544: // Copy the imposter, so that the imposter can continue
545: // its normal life in addition to changing the behaviour of
546: // the original. As a hack we don't bother to copy the metaclass.
547: // For some reason we modify the original rather than the copy.
548: copy = object_copy ((Object *) imposter, 0);
549: NXHashInsert (class_hash, copy);
550:
551: // Mark the imposter as such (this doesn't appear to be used).
552: CLS_SETINFO(imposter, CLS_POSING);
553: CLS_SETINFO(imposter->isa, CLS_POSING);
554:
555: // Change the name of the imposter to that of the original class.
556: imposter->name = original->name;
557: imposter->isa->name = original->isa->name;
558:
559: // Also copy the version field to avoid archiving problems.
560: imposter->version = original->version;
561:
562: state = NXInitHashState(class_hash);
563:
564: // Change all subclasses of the original to point to the imposter.
565: while (NXNextHashState(class_hash, &state, (void **)&clsObject))
566: {
567: while (clsObject && clsObject != imposter && clsObject != copy)
568: {
569: if (clsObject->super_class == original)
570: {
571: clsObject->super_class = imposter;
572: clsObject->isa->super_class = imposter->isa;
573: // We must flush caches here!
574: break;
575: }
576: clsObject = clsObject->super_class;
577: }
578: }
579:
580: #ifdef OBJC_CLASS_REFS
581:
582: for (hidx = 0; hidx < header_count; hidx++)
583: {
584: Class *cls_refs;
585: unsigned int size;
586:
587: #ifdef RUNTIME_DYLD
588: cls_refs = (Class *) getsectdatafromheaderinfo
589: (&header_vector[hidx],
590: SEG_OBJC, "__cls_refs", &size);
591: #else
592: cls_refs = (Class *) getsectdatafromheader
593: (header_vector[hidx].mhdr,
594: SEG_OBJC, "__cls_refs", &size);
595: #endif
596: if (cls_refs)
597: {
598: unsigned int i;
599:
600: for (i = 0; i < size / sizeof (Class); i++)
601: if (cls_refs[i] == original)
602: cls_refs[i] = imposter;
603: }
604: }
605: #endif /* OBJC_CLASS_REFS */
606:
607: // Change the name of the original class.
608: original->name = imposterNamePtr+1;
609: original->isa->name = imposterNamePtr;
610:
611: // Restore the imposter and the original class with their new names.
612: NXHashInsert(class_hash, imposter);
613: NXHashInsert(class_hash, original);
614:
615: OBJC_UNLOCK (&classLock);
616:
617: return imposter;
618: }
619:
620:
621: static void _freedHandler(id self, SEL sel)
622: {
623: __objc_error(self, _errFreedObject, SELNAME(sel), self);
624: }
625:
626: #ifdef RUNTIME_DYLD
627: static void _nonexistentHandler(id self, SEL sel)
628: {
629: __objc_error(self, _errNonExistentObject, SELNAME(sel), self);
630: }
631: #endif
632:
633: /*
634: * Purpose: Send the 'initialize' message on demand to any un-initialized
635: * class. Force initialization of superclasses first.
636: */
637:
638: /* Called only from _class_lookupMethodAndLoadCache (or itself).
639: #ifdef OBJC_COLLECTING_CACHE
640: The messageLock can be in either state.
641: #else
642: The messageLock is already assumed to be taken out.
643: It is temporarily released while the initialize method is sent.
644: #endif
645:
646: */
647:
648: static id class_initialize(Class clsDesc)
649: {
650: Class super = clsDesc->super_class;
651:
652: if (ISINITIALIZED(clsDesc))
653: return clsDesc;
654:
655: // force initialization of superclasses first
656: if (super != Nil && !ISINITIALIZED(super))
657: class_initialize(super);
658:
659: // Initializing the super class might have initialized us,
660: // or another thread might have intialized us during this time.
661: if (ISINITIALIZED(clsDesc))
662: return clsDesc;
663:
664: MARKINITIALIZED(clsDesc);
665:
666: #ifndef OBJC_COLLECTING_CACHE
667: OBJC_UNLOCK (&messageLock);
668: #endif
669:
670: [clsDesc initialize];
671:
672: #ifndef OBJC_COLLECTING_CACHE
673: OBJC_LOCK (&messageLock);
674: #endif
675:
676: return clsDesc;
677: }
678:
679: /* make a private extern in shlib */
680:
681: void _class_install_relationships(Class class, long version)
682: {
683: Class meta, clstmp;
684: int errflag = 0;
685:
686: meta = class->isa;
687:
688: meta->version = version;
689:
690: if (class->super_class) {
691: if ((clstmp = objc_getClass((const char *)class->super_class)))
692: class->super_class = clstmp;
693: else
694: errflag = 1;
695: }
696:
697: if ((clstmp = objc_getClass((const char *)meta->isa)))
698: meta->isa = clstmp->isa;
699: else
700: errflag = 1;
701:
702: if (meta->super_class) {
703: if ((clstmp = objc_getClass((const char *)meta->super_class)))
704: meta->super_class = clstmp->isa;
705: else
706: errflag = 1;
707: }
708: else /* `tie' the meta class down to its class */
709: meta->super_class = class;
710:
711: /* point all classes and meta-classes at static empty cache. */
712: if (class->cache == NULL)
713: class->cache = (Cache) &emptyCache;
714: if (meta->cache == NULL)
715: meta->cache = (Cache) &emptyCache;
716:
717: if (errflag)
718: _objc_fatal("please link appropriate classes in your program");
719: }
720:
721: static void *objc_malloc(int size)
722: {
723: void *space = NXZoneMalloc(_objc_create_zone(), size);
724:
725: if (space == 0 && size != 0)
726: _objc_fatal("unable to allocate space");
727:
728: return space;
729: }
730:
731:
732: /* Called from -[Object respondsTo:] and +[Object instancesRespondTo:]. */
733:
734: BOOL class_respondsToMethod(Class savCls, SEL sel)
735: {
736: Class cls = savCls;
737: #ifdef OBJC_COPY_CACHE
738: struct objc_cache_bucket *buckets;
739: #else
740: Method *buckets;
741: #endif
742: int index, mask;
743:
744: if (sel == 0)
745: return NO;
746:
747: OBJC_LOCK (&messageLock);
748:
749: mask = cls->cache->mask;
750: buckets = cls->cache->buckets;
751:
752: index = (unsigned int) sel & mask;
753:
754: for (;;) {
755: if (! CACHE_BUCKET_VALID (buckets[index]))
756: goto cacheMiss;
757: if (CACHE_BUCKET_NAME (buckets[index]) == sel)
758: {
759: if (CACHE_BUCKET_IMP (buckets[index]) == &_objc_msgForward)
760: {
761: OBJC_UNLOCK (&messageLock);
762: return NO;
763: }
764: else
765: {
766: OBJC_UNLOCK (&messageLock);
767: return YES;
768: }
769: }
770: index++;
771: index &= mask;
772: }
773:
774: cacheMiss:
775:
776: do {
777: register int n;
778: register Method smt;
779: struct objc_method_list *mlist;
780:
781: for (mlist = cls->methods; mlist; mlist = mlist->method_next) {
782: smt = mlist->method_list;
783: n = mlist->method_count;
784:
785: while (--n >= 0) {
786: if (sel == smt->method_name)
787: {
788: #ifdef RUNTIME_DYLD
789: _objc_bindModuleContainingList (mlist);
790: #endif
791: #ifdef OBJC_COLLECTING_CACHE
792: OBJC_UNLOCK (&messageLock);
793: _cache_fill(savCls, smt);
794: #else
795: _cache_fill(savCls, smt);
796: OBJC_UNLOCK (&messageLock);
797: #endif
798: return YES;
799: }
800: smt++;
801: }
802: }
803: } while ((cls = cls->super_class));
804:
805: {
806: Method smt = NXZoneMalloc (NXDefaultMallocZone(), sizeof (struct objc_method));
807:
808: smt->method_name = sel;
809: smt->method_types = "";
810: smt->method_imp = &_objc_msgForward;
811: #ifdef OBJC_COLLECTING_CACHE
812: OBJC_UNLOCK (&messageLock);
813: _cache_fill(savCls, smt);
814: #else
815: _cache_fill(savCls, smt);
816: OBJC_UNLOCK (&messageLock);
817: #endif
818: return NO;
819: }
820:
821: }
822:
823:
824: /* Called from -[Object methodFor:] and +[Object instanceMethodFor:]. */
825:
826: IMP class_lookupMethod(Class cls, SEL sel)
827: {
828: #ifdef OBJC_COPY_CACHE
829: struct objc_cache_bucket *buckets;
830: #else
831: Method *buckets;
832: #endif
833: int index, mask;
834: IMP result;
835:
836: if (sel == 0)
837: [(id)cls error:_errBadSel, sel];
838:
839: OBJC_LOCK (&messageLock);
840:
841: mask = cls->cache->mask;
842: buckets = cls->cache->buckets;
843:
844: index = (unsigned int) sel & mask;
845:
846: for (;;) {
847: if (! CACHE_BUCKET_VALID (buckets[index]))
848: goto cacheMiss;
849: if (CACHE_BUCKET_NAME (buckets[index]) == sel)
850: {
851: result = CACHE_BUCKET_IMP (buckets[index]);
852:
853: OBJC_UNLOCK (&messageLock);
854: return result;
855: }
856: index++;
857: index &= mask;
858: }
859:
860: cacheMiss:
861: result = _class_lookupMethodAndLoadCache(cls, sel);
862: OBJC_UNLOCK (&messageLock);
863: return result;
864: }
865:
866:
867: /* Private extern. Called from objc-load.m and _objc_callLoads(). */
868:
869: IMP class_lookupMethodInMethodList(struct objc_method_list *mlist, SEL sel)
870: {
871: register int n;
872: register Method smt;
873:
874: smt = mlist->method_list;
875: n = mlist->method_count;
876:
877: while (--n >= 0) {
878: if (sel == smt->method_name)
879: #ifdef RUNTIME_DYLD
880: {
881: _objc_bindModuleContainingList (mlist);
882: return smt->method_imp;
883: }
884: #else
885: return smt->method_imp;
886: #endif
887: smt++;
888: }
889: return 0;
890: }
891:
892: #ifdef OBJC_COPY_CACHE
893: #define BUCKETSIZE(size) ((size-1) * sizeof(struct objc_cache_bucket))
894: #else
895: #define BUCKETSIZE(size) ((size-1) * sizeof(Method))
896: #endif
897:
898: /* Private extern. Called from _cache_expand() and objc_addClass(). */
899:
900: Cache _cache_create(Class class)
901: {
902: Cache new_cache;
903: int size, i;
904:
905: size = (ISMETA (class)) ? META_CACHE_SIZE : CACHE_SIZE;
906:
907: // allocate and initialize table...
908: new_cache = NXZoneMalloc (NXDefaultMallocZone(),
909: sizeof(struct objc_cache) + BUCKETSIZE(size));
910:
911: for (i = 0; i < size; i++)
912: CACHE_BUCKET_VALID (new_cache->buckets[i]) = (void*)0;
913: new_cache->occupied = 0;
914: new_cache->mask = size - 1;
915:
916: // install the cache
917: class->cache = new_cache;
918:
919: // Reset the cache flush flag
920: class->info &= ~(CLS_FLUSH_CACHE);
921:
922: if (_class_slow_grow)
923: {
924: // reset the grow flag
925: class->info &= ~(CLS_GROW_CACHE);
926: }
927:
928: return new_cache;
929: }
930:
931:
932: /* Called from _cache_fill().
933: #ifdef OBJC_COLLECTING_CACHE
934: The cacheUpdateLock is assumed to be taken at this point.
935: #endif
936: */
937:
938: static Cache _cache_expand(Class class)
939: {
940: Cache old_cache, new_cache;
941: unsigned int size, i;
942:
943: old_cache = class->cache;
944:
945: if (old_cache == &emptyCache)
946: return _cache_create (class);
947:
948: if (_class_slow_grow)
949: {
950: if ((class->info & CLS_GROW_CACHE) == 0)
951: {
952: old_cache->occupied = 0;
953: for (i = 0; i < old_cache->mask + 1; i++)
954: {
955: if (CACHE_BUCKET_VALID (old_cache->buckets[i]))
956: {
957: #ifndef OBJC_COPY_CACHE
958: if (CACHE_BUCKET_IMP (old_cache->buckets[i])
959: == &_objc_msgForward) {
960: #ifdef OBJC_COLLECTING_CACHE
961: _cache_collect_free(old_cache->buckets[i], 0);
962: #else
963: NXZoneFree(NXDefaultMallocZone(), old_cache->buckets[i]);
964: #endif
965: }
966: #endif
967: CACHE_BUCKET_VALID (old_cache->buckets[i]) = (void*)0;
968: }
969: }
970: class->info |= CLS_GROW_CACHE;
971: return old_cache;
972: }
973: else
974: {
975: class->info &= ~CLS_GROW_CACHE;
976: }
977: }
978:
979: /* make sure size is a power of 2 */
980: size = (old_cache->mask + 1) << 1;
981:
982: /* allocate and initialize table... */
983: new_cache = NXZoneMalloc (NXDefaultMallocZone(),
984: sizeof(struct objc_cache) + BUCKETSIZE(size));
985:
986: // Zero out new cache
987: new_cache->mask = size - 1;
988: new_cache->occupied = 0;
989:
990: for (i = 0; i < size; i++)
991: CACHE_BUCKET_VALID (new_cache->buckets[i]) = (void*)0;
992:
993: if (_class_uncache == 0)
994: {
995: // Insert old cache entries into new cache
996: for (i = 0; i < old_cache->mask + 1; i++)
997: {
998: if (CACHE_BUCKET_VALID (old_cache->buckets[i]))
999: {
1000: int mask = new_cache->mask;
1001: int index = (unsigned int) CACHE_BUCKET_NAME (old_cache->buckets[i]) & mask;
1002:
1003: for (;;)
1004: {
1005: if (! CACHE_BUCKET_VALID (new_cache->buckets[index]))
1006: {
1007: new_cache->buckets[index] = old_cache->buckets[i];
1008: break;
1009: }
1010: index++;
1011: index &= mask;
1012: }
1013: new_cache->occupied++;
1014: }
1015: }
1016:
1017: // Set the cache flush flag so that we will flush this cache
1018: // before expanding it again.
1019: class->info |= CLS_FLUSH_CACHE;
1020: }
1021: else
1022: {
1023: // free any malloced method descriptors...class_respondsToMethod()
1024: // does this for negative caching.
1025: #ifndef OBJC_COPY_CACHE
1026: for (i = 0; i < old_cache->mask + 1; i++) {
1027: if (CACHE_BUCKET_VALID (old_cache->buckets[i]) &&
1028: CACHE_BUCKET_IMP (old_cache->buckets[i]) == &_objc_msgForward) {
1029: #ifdef OBJC_COLLECTING_CACHE
1030: _cache_collect_free(old_cache->buckets[i], 0);
1031: #else
1032: NXZoneFree(NXDefaultMallocZone(), old_cache->buckets[i]);
1033: #endif
1034: }
1035: }
1036: #endif
1037: }
1038:
1039: class->cache = new_cache;
1040:
1041: #ifdef OBJC_COLLECTING_CACHE
1042: _cache_collect_free(old_cache, 1);
1043: #else
1044: NXZoneFree(NXDefaultMallocZone(), old_cache);
1045: #endif
1046: return new_cache;
1047: }
1048:
1049: /* Called only from _class_lookupMethodAndLoadCache and class_respondsToMethod.
1050: #ifdef OBJC_COLLECTING_CACHE
1051: It doesn't matter if someone has the messageLock when we enter this function.
1052: This function will fail to do the update if someone else is already updating
1053: the cache, i.e. they have the cacheUpdateLock.
1054: #else
1055: The messageLock is already assumed to be taken out.
1056: #endif
1057: */
1058:
1059: static void _cache_fill(Class class, Method smt)
1060: {
1061: Cache cache;
1062: SEL sel = smt->method_name;
1063: #ifdef OBJC_COPY_CACHE
1064: IMP imp = smt->method_imp;
1065: struct objc_cache_bucket *buckets;
1066: #else
1067: Method *buckets;
1068: #endif
1069: int index, mask;
1070: unsigned int new_count;
1071:
1072: #ifdef OBJC_COLLECTING_CACHE
1073: if (! OBJC_TRYLOCK (&cacheUpdateLock))
1074: return;
1075:
1076: cache = class->cache;
1077: mask = cache->mask;
1078: index = (unsigned int) sel & mask;
1079: buckets = cache->buckets;
1080:
1081: for (;;) {
1082: if (! CACHE_BUCKET_VALID (buckets[index]))
1083: break;
1084: if (CACHE_BUCKET_NAME (buckets[index]) == sel)
1085: {
1086: OBJC_UNLOCK (&cacheUpdateLock);
1087: return;
1088: }
1089: index++;
1090: index &= mask;
1091: }
1092:
1093: #else
1094: cache = class->cache;
1095: mask = cache->mask;
1096: #endif
1097:
1098:
1099: /* Expand or flush the cache if more than 3/4 full. */
1100: new_count = cache->occupied + 1;
1101: if ((new_count * 4) > (mask + 1) * 3)
1102: {
1103: if (class->info & CLS_FLUSH_CACHE)
1104: _cache_flush (class); // Clears CLS_FLUSH_CACHE bit
1105: else
1106: {
1107: cache = _cache_expand (class); // Sets CLS_FLUSH_CACHE bit
1108: mask = cache->mask;
1109: }
1110: cache->occupied++;
1111: }
1112: else
1113: cache->occupied = new_count;
1114:
1115: buckets = cache->buckets;
1116:
1117: index = (unsigned int)sel & mask;
1118:
1119: #ifdef OBJC_COPY_CACHE
1120: imp = smt->method_imp;
1121: sel = smt->method_name;
1122: #endif
1123: for (;;)
1124: {
1125: #ifdef OBJC_COPY_CACHE
1126: #ifdef OBJC_COLLECTING_CACHE
1127: if (! CACHE_BUCKET_VALID (buckets[index]))
1128: {
1129: CACHE_BUCKET_IMP (buckets[index]) = imp;
1130: CACHE_BUCKET_NAME (buckets[index]) = sel;
1131:
1132: /* we can free it right away, because it was just
1133: allocated from _class_lookupMethodAndLoadCache */
1134: if (smt->method_imp == &_objc_msgForward)
1135: NXZoneFree (NXDefaultMallocZone (), smt);
1136: break;
1137: }
1138: #else
1139: struct objc_cache_bucket prev = buckets[index];
1140:
1141: CACHE_BUCKET_NAME (buckets[index]) = sel;
1142: CACHE_BUCKET_IMP (buckets[index]) = imp;
1143:
1144: if (! CACHE_BUCKET_VALID (prev))
1145: {
1146: if (smt->method_imp == &_objc_msgForward)
1147: NXZoneFree (NXDefaultMallocZone (), smt);
1148: break;
1149: }
1150:
1151: sel = CACHE_BUCKET_NAME (prev);
1152: imp = CACHE_BUCKET_IMP (prev);
1153: #endif /* OBJC_COLLECTING_CACHE */
1154: index++;
1155: index &= mask;
1156: #else
1157: Method prev = buckets[index];
1158: buckets[index] = smt;
1159:
1160: if (prev == NULL)
1161: break;
1162:
1163: smt = prev;
1164: index++;
1165: index &= mask;
1166: #endif
1167: }
1168:
1169: #ifdef OBJC_COLLECTING_CACHE
1170: OBJC_UNLOCK (&cacheUpdateLock);
1171: #endif
1172: }
1173:
1174:
1175: /* Called from flush_caches(). */
1176:
1177: static void _cache_flush (Class class)
1178: {
1179: Cache cache = class->cache;
1180: unsigned int i;
1181:
1182: if (cache == &emptyCache)
1183: return;
1184:
1185: // free any malloced method descriptors...class_respondsToMethod()
1186: // does this for negative caching.
1187: for (i = 0; i <= cache->mask; i++)
1188: {
1189: #ifndef OBJC_COPY_CACHE
1190: if (cache->buckets[i] &&
1191: cache->buckets[i]->method_imp == &_objc_msgForward)
1192: #ifdef OBJC_COLLECTING_CACHE
1193: _cache_collect_free(cache->buckets[i], 0);
1194: #else
1195: NXZoneFree(NXDefaultMallocZone(), cache->buckets[i]);
1196: #endif
1197: #endif
1198: CACHE_BUCKET_VALID (cache->buckets[i]) = (void*)0;
1199: }
1200:
1201: cache->occupied = 0;
1202:
1203: // Reset the cache flush flag
1204: class->info &= ~CLS_FLUSH_CACHE;
1205: }
1206:
1207:
1208: /* Called only from _class_lookupMethodAndLoadCache.
1209: The messageLock is already assumed to be taken out. */
1210:
1211: static inline Method _class_lookupMethod(Class cls, SEL sel)
1212: {
1213: register int n;
1214: register Method smt;
1215: struct objc_method_list *mlist;
1216:
1217: for (mlist = cls->methods; mlist; mlist = mlist->method_next) {
1218: smt = mlist->method_list;
1219: n = mlist->method_count;
1220:
1221: while (--n >= 0) {
1222: if (sel == smt->method_name) {
1223: #ifdef RUNTIME_DYLD
1224: {
1225: _objc_bindModuleContainingList (mlist);
1226: return smt;
1227: }
1228: #else
1229: return smt;
1230: #endif
1231: }
1232: smt++;
1233: }
1234: }
1235: return 0;
1236: }
1237:
1238:
1239: /* Returns a pointer to the dummy freed object class (private extern). */
1240:
1241: Class _objc_getFreedObjectClass (void) { return (Class) &freedObjectClass; }
1242:
1243: #ifdef RUNTIME_DYLD
1244: Class _objc_getNonexistentClass (void) { return (Class) &nonexistentObjectClass; }
1245: #endif
1246:
1247:
1248: /* Called only from objc_msgSend, objc_msgSendSuper and class_lookupMethod.
1249: (*NOT*) The messageLock is already assumed to be taken out. */
1250:
1251: IMP _class_lookupMethodAndLoadCache(Class savCls, SEL sel)
1252: {
1253: register Class cls = savCls;
1254: Method method;
1255:
1256: if (cls == &freedObjectClass)
1257: return (IMP)_freedHandler;
1258: #ifdef RUNTIME_DYLD
1259: if (cls == &nonexistentObjectClass)
1260: return (IMP)_nonexistentHandler;
1261: #endif
1262:
1263: // lazy initialization...
1264: if (CLS_GETINFO(cls,CLS_META) && !ISINITIALIZED(cls))
1265: class_initialize(objc_getClass (cls->name));
1266:
1267: do {
1268: method = _class_lookupMethod(cls, sel);
1269: if (method) {
1270: _cache_fill(savCls, method);
1271: return method->method_imp;
1272: }
1273: } while ((cls = cls->super_class));
1274:
1275: // class does not respond -- try forwarding
1276: {
1277: Method smt = NXZoneMalloc(NXDefaultMallocZone(), sizeof(struct objc_method));
1278: smt->method_name = sel;
1279: smt->method_types = "";
1280: smt->method_imp = &_objc_msgForward;
1281: _cache_fill(savCls, smt);
1282: }
1283: return &_objc_msgForward;
1284: }
1285:
1286: /* delegation */
1287:
1288: static int SubTypeUntil (const char *type, char end)
1289: {
1290: int level = 0;
1291: const char *head = type;
1292: while (*type) {
1293: if (!*type || (! level && (*type == end)))
1294: return (int)(type - head);
1295: switch (*type) {
1296: case ']': case '}': case ')': level--; break;
1297: case '[': case '{': case '(': level++; break;
1298: }
1299: type ++;
1300: }
1301: _NXLogError("Object: SubTypeUntil: end of type encountered prematurely\n");
1302: return 0;
1303: }
1304:
1305: static const char *SkipFirstType (const char *type)
1306: {
1307: while (1)
1308: {
1309: switch (*type++)
1310: {
1311: case 'O': /* bycopy */
1312: case 'n': /* in */
1313: case 'o': /* out */
1314: case 'N': /* inout */
1315: case 'r': /* const */
1316: case 'V': /* oneway */
1317: case '^': /* pointers */
1318: break;
1319:
1320: /* arrays */
1321: case '[':
1322: while ('0' <= *type && '9' >= *type) type++;
1323: return type + SubTypeUntil(type, ']') + 1;
1324:
1325: /* structures */
1326: case '{':
1327: return type + SubTypeUntil(type, '}') + 1;
1328:
1329: /* unions */
1330: case '(':
1331: return type + SubTypeUntil(type, ')') + 1;
1332:
1333: /* basic types */
1334: default:
1335: return type;
1336: }
1337: }
1338: }
1339:
1340: unsigned method_getNumberOfArguments(Method method)
1341: {
1342: const char *typedesc = method->method_types;
1343: unsigned nargs = 0;
1344:
1345: /* first, skip the return type */
1346: typedesc = SkipFirstType(typedesc);
1347:
1348: /* next, skip stack size */
1349: while ('0' <= *typedesc && '9' >= *typedesc)
1350: typedesc++;
1351:
1352: /* now, we have the arguments - count how many */
1353: while (*typedesc) {
1354:
1355: /* skip argument type */
1356: typedesc = SkipFirstType(typedesc);
1357:
1358: /* next is the argument offset, blow it off */
1359: if (*typedesc == '-') /* skip negative sign in offset */
1360: typedesc++;
1361: while ('0' <= *typedesc && '9' >= *typedesc)
1362: typedesc++;
1363:
1364: nargs++;
1365: }
1366: return nargs;
1367: }
1368:
1369: unsigned method_getSizeOfArguments(Method method)
1370: {
1371: const char *typedesc = method->method_types;
1372: unsigned stack_size = 0;
1373:
1374: /* first, skip the return type */
1375: typedesc = SkipFirstType(typedesc);
1376:
1377: while ('0' <= *typedesc && '9' >= *typedesc)
1378: stack_size = stack_size * 10 + (*typedesc++ - '0');
1379:
1380: return stack_size;
1381: }
1382:
1383: unsigned method_getArgumentInfo(Method method, int arg,
1384: const char **type, int *offset)
1385: {
1386: const char *typedesc = method->method_types;
1387: unsigned nargs = 0;
1388: unsigned self_offset = 0;
1389: BOOL offset_is_negative = NO;
1390:
1391: /* first, skip the return type */
1392: typedesc = SkipFirstType(typedesc);
1393:
1394: /* next, skip stack size */
1395: while ('0' <= *typedesc && '9' >= *typedesc)
1396: typedesc++;
1397:
1398: /* now, we have the arguments - position typedesc to the appropriate argument */
1399: while (*typedesc && nargs != arg) {
1400:
1401: /* skip argument type */
1402: typedesc = SkipFirstType(typedesc);
1403:
1404: if (nargs == 0) {
1405: if (*typedesc == '-') { /* skip negative sign in offset */
1406: offset_is_negative = YES;
1407: typedesc++;
1408: } else {
1409: offset_is_negative = NO;
1410: }
1411: while ('0' <= *typedesc && '9' >= *typedesc)
1412: self_offset = self_offset * 10 + (*typedesc++ - '0');
1413: if (offset_is_negative)
1414: self_offset = - self_offset;
1415: } else {
1416: /* next is the argument offset, blow it off */
1417: if (*typedesc == '-')
1418: typedesc++;
1419: while ('0' <= *typedesc && '9' >= *typedesc)
1420: typedesc++;
1421: }
1422: nargs++;
1423: }
1424: if (*typedesc) {
1425: unsigned arg_offset = 0;
1426:
1427: *type = typedesc;
1428: typedesc = SkipFirstType(typedesc);
1429:
1430: if (arg == 0) {
1431: #if hppa
1432: *offset = -sizeof(id);
1433: #else
1434: *offset = 0;
1435: #endif hppa
1436: } else {
1437: if (*typedesc == '-') { /* skip negative sign in offset */
1438: offset_is_negative = YES;
1439: typedesc++;
1440: } else {
1441: offset_is_negative = NO;
1442: }
1443: while ('0' <= *typedesc && '9' >= *typedesc)
1444: arg_offset = arg_offset * 10 + (*typedesc++ - '0');
1445: if (offset_is_negative)
1446: arg_offset = - arg_offset;
1447:
1448: #if hppa
1449: /*
1450: * For stacks which grow up, since margs points
1451: * to the top of the stack or the END of the args,
1452: * the first offset is at -sizeof(id) rather than 0.
1453: */
1454: self_offset += sizeof(id);
1455: #endif
1456: *offset = arg_offset - self_offset;
1457: }
1458: } else {
1459: *type = 0; *offset = 0;
1460: }
1461: return nargs;
1462: }
1463:
1464:
1465: /* Private extern. */
1466:
1467: NXZone *_objc_create_zone (void)
1468: {
1469: static NXZone *_objc_zone = 0;
1470:
1471: if (!_objc_zone)
1472: {
1473: _objc_zone = NXCreateZone (vm_page_size, vm_page_size, YES);
1474: NXNameZone (_objc_zone, "ObjC");
1475: }
1476:
1477: return _objc_zone;
1478: }
1479:
1480:
1481:
1482: /* cache collection */
1483: #ifdef OBJC_COLLECTING_CACHE
1484:
1485: #ifdef hppa
1486: static unsigned long
1487: _get_pc_for_thread (thread_t thread)
1488: {
1489: struct hp_pa_frame_thread_state state;
1490: unsigned int count = HPPA_FRAME_THREAD_STATE_COUNT;
1491: thread_get_state (thread, HPPA_FRAME_THREAD_STATE, (thread_state_t)&state, &count);
1492: return state.ts_pcoq_front;
1493: }
1494: #elif defined (i386)
1495: static unsigned long
1496: _get_pc_for_thread (thread_t thread)
1497: {
1498: i386_thread_state_t state;
1499: unsigned int count = i386_THREAD_STATE_COUNT;
1500: thread_get_state (thread, i386_THREAD_STATE, (thread_state_t)&state, &count);
1501: return state.eip;
1502: }
1503:
1504: #elif defined (m68k)
1505: static unsigned long
1506: _get_pc_for_thread (thread_t thread)
1507: {
1508: struct m68k_thread_state_regs state;
1509: unsigned int count = M68K_THREAD_STATE_REGS_COUNT;
1510: thread_get_state (thread, M68K_THREAD_STATE_REGS, (thread_state_t)&state, &count);
1511: return state.pc;
1512: }
1513:
1514: #else
1515: /* if OBJC_COLLECTING_CACHE is defined, you need the function above... */
1516: { you loose; }
1517: #endif
1518:
1519: extern unsigned long objc_entryPoints[];
1520: extern unsigned long objc_exitPoints[];
1521:
1522: static int
1523: _collecting_in_critical ()
1524: {
1525: thread_array_t threads;
1526: unsigned number, count;
1527: kern_return_t ret;
1528: int result = 0;
1529:
1530: ret = task_threads (task_self (), &threads, &number);
1531: if (ret != KERN_SUCCESS)
1532: {
1533: _NXLogError ("objc: task_thread failed\n");
1534: exit (1);
1535: }
1536:
1537: for (count = 0; !result && count < number; count++)
1538: {
1539: int region;
1540: unsigned long pc;
1541:
1542: if (threads[count] == thread_self ())
1543: continue;
1544:
1545: pc = _get_pc_for_thread (threads[count]);
1546:
1547: for (region = 0; objc_entryPoints[region]; region++)
1548: {
1549: if ( pc >= objc_entryPoints[region]
1550: && pc <= objc_exitPoints[region])
1551: {
1552: result = 1;
1553: }
1554: }
1555: }
1556:
1557: vm_deallocate(task_self(), (vm_address_t)threads, sizeof(threads)*count);
1558: return result;
1559: }
1560:
1561:
1562: static int garbage_byte_size = 0;
1563: static int garbage_threshold = 1024;
1564:
1565: static void **garbage_refs = 0;
1566: static int garbage_count = 0;
1567: static int garbage_max = 0;
1568:
1569: static void
1570: _garbage_make_room ()
1571: {
1572: static int first = 1;
1573:
1574: if (first)
1575: {
1576: first = 0;
1577: garbage_refs = NXZoneMalloc (NXDefaultMallocZone (), 128 * sizeof (void*));
1578: garbage_max = 128;
1579: }
1580: else if (garbage_count == garbage_max)
1581: {
1582: garbage_refs = NXZoneRealloc (NXDefaultMallocZone (), garbage_refs, garbage_max*2);
1583: garbage_max *= 2;
1584: }
1585: }
1586:
1587: static void _cache_collect_free(void *data, BOOL tryCollect)
1588: {
1589: char * report_garbage = getenv ("OBJC_REPORT_GARBAGE");
1590: OBJC_LOCK (&cacheCollectionLock);
1591:
1592: /* insert new element in garbage list */
1593: _garbage_make_room ();
1594: garbage_byte_size += malloc_size (data);
1595: garbage_refs[garbage_count++] = data;
1596:
1597: if (tryCollect && report_garbage)
1598: {
1599: _NXLogError ("objc: total of %d bytes of garbage ...", garbage_byte_size);
1600: }
1601:
1602: if (!tryCollect || garbage_byte_size < garbage_threshold)
1603: {
1604: OBJC_UNLOCK (&cacheCollectionLock);
1605: if (tryCollect && report_garbage)
1606: {
1607: _NXLogError ("below threshold\n");
1608: }
1609:
1610: return;
1611: }
1612:
1613: /* If someone has the explicit read lock, we don't
1614: try to garbage collect */
1615: if (OBJC_TRYLOCK (&messageLock))
1616: {
1617: if (! _collecting_in_critical ())
1618: {
1619: if (tryCollect && report_garbage)
1620: {
1621: _NXLogError ("collecting!\n");
1622: }
1623:
1624: while (garbage_count)
1625: free (garbage_refs[--garbage_count]);
1626:
1627: garbage_byte_size = 0;
1628: }
1629: else if (tryCollect && report_garbage)
1630: {
1631: _NXLogError ("in critical region\n");
1632: }
1633:
1634: OBJC_UNLOCK (&messageLock);
1635: }
1636: else if (tryCollect && report_garbage)
1637: {
1638: _NXLogError ("messageLock taken\n");
1639: }
1640:
1641: OBJC_UNLOCK (&cacheCollectionLock);
1642: return;
1643:
1644: }
1645:
1646: #endif /* OBJC_COLLECTING_CACHE */
1647:
1648:
1649:
1650: #ifndef KERNEL
1651:
1652: static void _cache_print (Cache cache)
1653: {
1654: unsigned int i, count = cache->mask + 1;
1655:
1656: for (i = 0; i < count; i++)
1657: if (CACHE_BUCKET_VALID (cache->buckets[i]))
1658: {
1659: if (CACHE_BUCKET_IMP (cache->buckets[i]) == &_objc_msgForward)
1660: printf ("does not recognize \n");
1661: printf ("%s\n", (const char *) CACHE_BUCKET_NAME (cache->buckets[i]));
1662: }
1663: }
1664:
1665:
1666: void _class_printMethodCaches (Class class)
1667: {
1668: if (class->cache == &emptyCache)
1669: printf ("no instance-method cache for class %s\n", class->name);
1670: else
1671: {
1672: printf ("instance-method cache for class %s:\n", class->name);
1673: _cache_print (class->cache);
1674: }
1675:
1676: if (class->isa->cache == &emptyCache)
1677: printf ("no class-method cache for class %s\n", class->name);
1678: else
1679: {
1680: printf ("class-method cache for class %s:\n", class->name);
1681: _cache_print (class->isa->cache);
1682: }
1683: }
1684:
1685:
1686: static unsigned int log2 (unsigned int x)
1687: {
1688: unsigned int log = 0;
1689:
1690: while (x >>= 1)
1691: log++;
1692:
1693: return log;
1694: }
1695:
1696:
1697: #define MAX_LOG_SIZE 32
1698: #define MAX_CHAIN_SIZE 100
1699:
1700: void _class_printMethodCacheStatistics (void)
1701: {
1702: NXHashTable *class_hash = objc_getClasses ();
1703: NXHashState state = NXInitHashState (class_hash);
1704: Class class;
1705: unsigned int classCount = 0;
1706: unsigned int cacheCountBySize[2][MAX_LOG_SIZE] = {0};
1707: unsigned int totalEntriesBySize[2][MAX_LOG_SIZE] = {0};
1708: unsigned int maxEntriesBySize[2][MAX_LOG_SIZE] = {0};
1709: unsigned int totalNegativeEntries = 0;
1710: unsigned int totalChainBySize[2][MAX_LOG_SIZE] = {0};
1711: unsigned int totalMissChainBySize[2][MAX_LOG_SIZE] = {0};
1712: unsigned int totalMaxChainBySize[2][MAX_LOG_SIZE] = {0};
1713: unsigned int totalMaxMissChainBySize[2][MAX_LOG_SIZE] = {0};
1714: unsigned int maxChainBySize[2][MAX_LOG_SIZE] = {0};
1715: unsigned int maxMissChainBySize[2][MAX_LOG_SIZE] = {0};
1716: unsigned int chainCount[MAX_CHAIN_SIZE] = {0};
1717: unsigned int missChainCount[MAX_CHAIN_SIZE] = {0};
1718:
1719: while (NXNextHashState (class_hash, &state, (void **) &class))
1720: {
1721: unsigned int isMeta;
1722:
1723: classCount++;
1724:
1725: for (isMeta = 0; isMeta <= 1; isMeta++)
1726: {
1727: Cache cache = isMeta ? class->isa->cache : class->cache;
1728:
1729: if (cache != &emptyCache)
1730: {
1731: unsigned int i;
1732: unsigned int mask = cache->mask;
1733: unsigned int count = mask + 1;
1734: unsigned int size = log2 (count);
1735: unsigned int entries = 0;
1736: unsigned int negativeEntries = 0;
1737: unsigned int totalChain = 0;
1738: unsigned int totalMissChain = 0;
1739: unsigned int maxChain = 0;
1740: unsigned int maxMissChain = 0;
1741:
1742: cacheCountBySize[isMeta][size]++;
1743:
1744: for (i = 0; i < count; i++)
1745: {
1746: #ifdef OBJC_COPY_CACHE
1747: struct objc_cache_bucket *buckets = cache->buckets;
1748: #else
1749: Method *buckets = cache->buckets;
1750: #endif
1751:
1752: if (CACHE_BUCKET_VALID (buckets[i]))
1753: {
1754: #ifdef OBJC_COPY_CACHE
1755: struct objc_cache_bucket method = buckets[i];
1756: #else
1757: Method method = buckets[i];
1758: #endif
1759: unsigned int hash = (unsigned int) CACHE_BUCKET_NAME (method);
1760: unsigned int chain = (i - hash) & mask;
1761: unsigned int missChain;
1762: unsigned int j = i;
1763:
1764: entries++;
1765:
1766: if (CACHE_BUCKET_IMP (method) == &_objc_msgForward)
1767: negativeEntries++;
1768:
1769: if (chain < MAX_CHAIN_SIZE)
1770: chainCount[chain]++;
1771: totalChain += chain;
1772: if (chain > maxChain)
1773: maxChain = chain;
1774:
1775: while (CACHE_BUCKET_VALID (buckets[j]))
1776: {
1777: j++;
1778: j &= mask;
1779: }
1780:
1781: missChain = (j - i) & mask;
1782: if (missChain < MAX_CHAIN_SIZE)
1783: missChainCount[missChain]++;
1784: totalMissChain += missChain;
1785: if (missChain > maxMissChain)
1786: maxMissChain = missChain;
1787: }
1788: else
1789: missChainCount[0]++;
1790: }
1791:
1792: totalEntriesBySize[isMeta][size] += entries;
1793: if (entries > maxEntriesBySize[isMeta][size])
1794: maxEntriesBySize[isMeta][size] = entries;
1795: totalNegativeEntries += negativeEntries;
1796: totalChainBySize[isMeta][size] += totalChain;
1797: totalMissChainBySize[isMeta][size] += totalMissChain;
1798: totalMaxChainBySize[isMeta][size] += maxChain;
1799: totalMaxMissChainBySize[isMeta][size] += maxMissChain;
1800: if (maxChain > maxChainBySize[isMeta][size])
1801: maxChainBySize[isMeta][size] = maxChain;
1802: if (maxMissChain > maxMissChainBySize[isMeta][size])
1803: maxMissChainBySize[isMeta][size] = maxMissChain;
1804: }
1805: }
1806: }
1807:
1808: {
1809: unsigned int isMeta, i;
1810: unsigned int cacheCount[2] = {0};
1811: unsigned int totalCacheCount = 0;
1812: unsigned int totalEntries = 0;
1813: unsigned int totalSize = 0;
1814: unsigned int totalChain = 0;
1815: unsigned int maxChain = 0;
1816: unsigned int totalMissChain = 0;
1817: unsigned int maxMissChain = 0;
1818:
1819: for (isMeta = 0; isMeta <= 1; isMeta++)
1820: {
1821: for (i = 0; i < MAX_LOG_SIZE; i++)
1822: {
1823: cacheCount[isMeta] += cacheCountBySize[isMeta][i];
1824: totalEntries += totalEntriesBySize[isMeta][i];
1825: totalSize += cacheCountBySize[isMeta][i] * (1 << i);
1826: totalChain += totalChainBySize[isMeta][i];
1827: if (maxChainBySize[isMeta][i] > maxChain)
1828: maxChain = maxChainBySize[isMeta][i];
1829: totalMissChain += totalMissChainBySize[isMeta][i];
1830: if (maxMissChainBySize[isMeta][i] > maxMissChain)
1831: maxMissChain = maxMissChainBySize[isMeta][i];
1832: }
1833:
1834: totalCacheCount += cacheCount[isMeta];
1835: }
1836:
1837: for (isMeta = 0; isMeta <= 1; isMeta++)
1838: {
1839: printf ("%u %s-method caches (out of %u classes):\n",
1840: cacheCount[isMeta],
1841: isMeta ? "class" : "instance",
1842: classCount);
1843:
1844: printf ("\n");
1845:
1846: printf ("%s-method cache size distribution:\n",
1847: isMeta ? "class" : "instance");
1848:
1849: for (i = 0; i < MAX_LOG_SIZE; i++)
1850: if (cacheCountBySize[isMeta][i])
1851: printf (" %d slots: %u\n", 1 << i, cacheCountBySize[isMeta][i]);
1852:
1853: printf ("\n");
1854:
1855: for (i = 0; i < MAX_LOG_SIZE; i++)
1856: if (cacheCountBySize[isMeta][i])
1857: {
1858: printf (" %u %s-method caches with %d slots (%u%% efficiency):\n",
1859: cacheCountBySize[isMeta][i],
1860: isMeta ? "class" : "instance",
1861: 1 << i,
1862: (100 * totalEntriesBySize[isMeta][i] + 50) /
1863: (cacheCountBySize[isMeta][i] * (1 << i)));
1864: printf (" %.2f average entries (%u maximum)\n",
1865: (float) totalEntriesBySize[isMeta][i] /
1866: (float) cacheCountBySize[isMeta][i],
1867: maxEntriesBySize[isMeta][i]);
1868: printf (" %.2f average lookup chain (%u maximum)\n",
1869: (float) totalChainBySize[isMeta][i] /
1870: (float) totalEntriesBySize[isMeta][i],
1871: maxChainBySize[isMeta][i]);
1872: printf (" %.2f average miss chain (%u maximum)\n",
1873: (float) totalMissChainBySize[isMeta][i] /
1874: (float) (cacheCountBySize[isMeta][i] * (1 << i)),
1875: maxMissChainBySize[isMeta][i]);
1876:
1877: printf ("\n");
1878: }
1879: }
1880:
1881: printf ("cummulative method cache statistics:\n");
1882: printf (" %u entries in %u slots (%u%% efficiency)\n",
1883: totalEntries, totalSize,
1884: (100 * totalEntries + 50) / totalSize);
1885: printf (" %u negative entries (%u%%)\n",
1886: totalNegativeEntries,
1887: (100 * totalNegativeEntries + 50) / totalEntries);
1888: printf (" %.2f average lookup chain (%u maximum)\n",
1889: (float) totalChain / (float) totalEntries,
1890: maxChain);
1891: printf (" %.2f average miss chain (%u maximum)\n",
1892: (float) totalMissChain / (float) totalSize,
1893: maxMissChain);
1894: #ifdef OBJC_COPY_CACHE
1895: printf (" %lu bytes malloced data\n",
1896: totalCacheCount * (sizeof (struct objc_cache) - sizeof (struct objc_cache_bucket))+
1897: totalSize * sizeof (struct objc_cache_bucket) +
1898: totalNegativeEntries * sizeof (struct objc_method));
1899: #else
1900: printf (" %lu bytes malloced data\n",
1901: totalCacheCount * (sizeof (struct objc_cache) - sizeof (Method))+
1902: totalSize * sizeof (Method) +
1903: totalNegativeEntries * sizeof (struct objc_method));
1904: #endif
1905:
1906: printf ("\n");
1907:
1908: printf ("lookup chain length distribution:\n");
1909:
1910: for (i = 0; i < MAX_CHAIN_SIZE; i++)
1911: if (chainCount[i])
1912: printf (" length %u: %u\n", i, chainCount[i]);
1913:
1914: printf ("\n");
1915:
1916: printf ("miss chain length distribution:\n");
1917:
1918: for (i = 0; i < MAX_CHAIN_SIZE; i++)
1919: if (missChainCount[i])
1920: printf (" length %u: %u\n", i, missChainCount[i]);
1921:
1922: printf ("\n");
1923: }
1924: }
1925:
1926: #endif /* KERNEL */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.