|
|
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.