1/* 2 * Copyright (c) 2005-2009 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/*********************************************************************** 25* objc-runtime-new.m 26* Support for new-ABI classes and images. 27**********************************************************************/ 28 29#if __OBJC2__ 30 31#include "objc-private.h" 32#include "objc-runtime-new.h" 33#include "objc-file.h" 34#include "objc-cache.h" 35#include <objc/message.h> 36#include <mach/shared_region.h> 37 38#define newprotocol(p) ((protocol_t *)p) 39 40static void disableTaggedPointers(); 41static void detach_class(Class cls, BOOL isMeta); 42static void free_class(Class cls); 43static Class setSuperclass(Class cls, Class newSuper); 44static Class realizeClass(Class cls); 45static method_t *getMethodNoSuper_nolock(Class cls, SEL sel); 46static method_t *getMethod_nolock(Class cls, SEL sel); 47static IMP _method_getImplementation(method_t *m); 48static IMP addMethod(Class cls, SEL name, IMP imp, const char *types, BOOL replace); 49static NXHashTable *realizedClasses(void); 50static bool isRRSelector(SEL sel); 51static bool isAWZSelector(SEL sel); 52static void updateCustomRR_AWZ(Class cls, method_t *meth); 53static method_t *search_method_list(const method_list_t *mlist, SEL sel); 54#if SUPPORT_FIXUP 55static void fixupMessageRef(message_ref_t *msg); 56#endif 57 58id objc_noop_imp(id self, SEL _cmd __unused) { 59 return self; 60} 61 62 63/*********************************************************************** 64* Lock management 65**********************************************************************/ 66rwlock_t runtimeLock; 67rwlock_t selLock; 68mutex_t cacheUpdateLock = MUTEX_INITIALIZER; 69recursive_mutex_t loadMethodLock = RECURSIVE_MUTEX_INITIALIZER; 70 71void lock_init(void) 72{ 73 rwlock_init(&selLock); 74 rwlock_init(&runtimeLock); 75 recursive_mutex_init(&loadMethodLock); 76} 77 78 79typedef struct { 80 category_t *cat; 81 BOOL fromBundle; 82} category_pair_t; 83 84typedef struct { 85 uint32_t count; 86 category_pair_t list[0]; // variable-size 87} category_list; 88 89#define FOREACH_METHOD_LIST(_mlist, _cls, code) \ 90 do { \ 91 const method_list_t *_mlist; \ 92 if (_cls->data()->method_lists) { \ 93 if (_cls->data()->flags & RW_METHOD_ARRAY) { \ 94 method_list_t **_mlistp; \ 95 for (_mlistp=_cls->data()->method_lists; *_mlistp; _mlistp++){\ 96 _mlist = *_mlistp; \ 97 code \ 98 } \ 99 } else { \ 100 _mlist = _cls->data()->method_list; \ 101 code \ 102 } \ 103 } \ 104 } while (0) 105 106 107/* 108 Low two bits of mlist->entsize is used as the fixed-up marker. 109 PREOPTIMIZED VERSION: 110 Fixed-up method lists get entsize&3 == 3. 111 dyld shared cache sets this for method lists it preoptimizes. 112 UN-PREOPTIMIZED VERSION: 113 Fixed-up method lists get entsize&3 == 1. 114 dyld shared cache uses 3, but those aren't trusted. 115*/ 116 117static uint32_t fixed_up_method_list = 3; 118 119void 120disableSharedCacheOptimizations(void) 121{ 122 fixed_up_method_list = 1; 123} 124 125static BOOL isMethodListFixedUp(const method_list_t *mlist) 126{ 127 return (mlist->entsize_NEVER_USE & 3) == fixed_up_method_list; 128} 129 130static void setMethodListFixedUp(method_list_t *mlist) 131{ 132 rwlock_assert_writing(&runtimeLock); 133 assert(!isMethodListFixedUp(mlist)); 134 mlist->entsize_NEVER_USE = (mlist->entsize_NEVER_USE & ~3) | fixed_up_method_list; 135} 136 137/* 138static size_t chained_property_list_size(const chained_property_list *plist) 139{ 140 return sizeof(chained_property_list) + 141 plist->count * sizeof(property_t); 142} 143*/ 144 145static size_t protocol_list_size(const protocol_list_t *plist) 146{ 147 return sizeof(protocol_list_t) + plist->count * sizeof(protocol_t *); 148} 149 150 151// low bit used by dyld shared cache 152static uint32_t method_list_entsize(const method_list_t *mlist) 153{ 154 return mlist->entsize_NEVER_USE & ~(uint32_t)3; 155} 156 157static size_t method_list_size(const method_list_t *mlist) 158{ 159 return sizeof(method_list_t) + (mlist->count-1)*method_list_entsize(mlist); 160} 161 162static method_t *method_list_nth(const method_list_t *mlist, uint32_t i) 163{ 164 return &mlist->get(i); 165} 166 167static uint32_t method_list_count(const method_list_t *mlist) 168{ 169 return mlist ? mlist->count : 0; 170} 171 172static void method_list_swap(method_list_t *mlist, uint32_t i, uint32_t j) 173{ 174 size_t entsize = method_list_entsize(mlist); 175 char temp[entsize]; 176 memcpy(temp, method_list_nth(mlist, i), entsize); 177 memcpy(method_list_nth(mlist, i), method_list_nth(mlist, j), entsize); 178 memcpy(method_list_nth(mlist, j), temp, entsize); 179} 180 181static uint32_t method_list_index(const method_list_t *mlist,const method_t *m) 182{ 183 uint32_t i = (uint32_t)(((uintptr_t)m - (uintptr_t)mlist) / method_list_entsize(mlist)); 184 assert(i < mlist->count); 185 return i; 186} 187 188 189static size_t ivar_list_size(const ivar_list_t *ilist) 190{ 191 return sizeof(ivar_list_t) + (ilist->count-1) * ilist->entsize; 192} 193 194static ivar_t *ivar_list_nth(const ivar_list_t *ilist, uint32_t i) 195{ 196 return (ivar_t *)(i*ilist->entsize + (char *)&ilist->first); 197} 198 199 200static method_list_t *cat_method_list(const category_t *cat, BOOL isMeta) 201{ 202 if (!cat) return nil; 203 204 if (isMeta) return cat->classMethods; 205 else return cat->instanceMethods; 206} 207 208static uint32_t cat_method_count(const category_t *cat, BOOL isMeta) 209{ 210 method_list_t *cmlist = cat_method_list(cat, isMeta); 211 return cmlist ? cmlist->count : 0; 212} 213 214static method_t *cat_method_nth(const category_t *cat, BOOL isMeta, uint32_t i) 215{ 216 method_list_t *cmlist = cat_method_list(cat, isMeta); 217 if (!cmlist) return nil; 218 219 return method_list_nth(cmlist, i); 220} 221 222 223static property_t * 224property_list_nth(const property_list_t *plist, uint32_t i) 225{ 226 return (property_t *)(i*plist->entsize + (char *)&plist->first); 227} 228 229// fixme don't chain property lists 230typedef struct chained_property_list { 231 struct chained_property_list *next; 232 uint32_t count; 233 property_t list[0]; // variable-size 234} chained_property_list; 235 236 237static void try_free(const void *p) 238{ 239 if (p && malloc_size(p)) free((void *)p); 240} 241 242 243/*********************************************************************** 244* make_ro_writeable 245* Reallocates rw->ro if necessary to make it writeable. 246* Locking: runtimeLock must be held by the caller. 247**********************************************************************/ 248static class_ro_t *make_ro_writeable(class_rw_t *rw) 249{ 250 rwlock_assert_writing(&runtimeLock); 251 252 if (rw->flags & RW_COPIED_RO) { 253 // already writeable, do nothing 254 } else { 255 class_ro_t *ro = (class_ro_t *) 256 _memdup_internal(rw->ro, sizeof(*rw->ro)); 257 rw->ro = ro; 258 rw->flags |= RW_COPIED_RO; 259 } 260 return (class_ro_t *)rw->ro; 261} 262 263 264/*********************************************************************** 265* unattachedCategories 266* Returns the class => categories map of unattached categories. 267* Locking: runtimeLock must be held by the caller. 268**********************************************************************/ 269static NXMapTable *unattachedCategories(void) 270{ 271 rwlock_assert_writing(&runtimeLock); 272 273 static NXMapTable *category_map = nil; 274 275 if (category_map) return category_map; 276 277 // fixme initial map size 278 category_map = NXCreateMapTableFromZone(NXPtrValueMapPrototype, 16, 279 _objc_internal_zone()); 280 281 return category_map; 282} 283 284 285/*********************************************************************** 286* addUnattachedCategoryForClass 287* Records an unattached category. 288* Locking: runtimeLock must be held by the caller. 289**********************************************************************/ 290static void addUnattachedCategoryForClass(category_t *cat, Class cls, 291 header_info *catHeader) 292{ 293 rwlock_assert_writing(&runtimeLock); 294 295 BOOL catFromBundle = (catHeader->mhdr->filetype == MH_BUNDLE) ? YES: NO; 296 297 // DO NOT use cat->cls! cls may be cat->cls->isa instead 298 NXMapTable *cats = unattachedCategories(); 299 category_list *list; 300 301 list = (category_list *)NXMapGet(cats, cls); 302 if (!list) { 303 list = (category_list *) 304 _calloc_internal(sizeof(*list) + sizeof(list->list[0]), 1); 305 } else { 306 list = (category_list *) 307 _realloc_internal(list, sizeof(*list) + sizeof(list->list[0]) * (list->count + 1)); 308 } 309 list->list[list->count++] = (category_pair_t){cat, catFromBundle}; 310 NXMapInsert(cats, cls, list); 311} 312 313 314/*********************************************************************** 315* removeUnattachedCategoryForClass 316* Removes an unattached category. 317* Locking: runtimeLock must be held by the caller. 318**********************************************************************/ 319static void removeUnattachedCategoryForClass(category_t *cat, Class cls) 320{ 321 rwlock_assert_writing(&runtimeLock); 322 323 // DO NOT use cat->cls! cls may be cat->cls->isa instead 324 NXMapTable *cats = unattachedCategories(); 325 category_list *list; 326 327 list = (category_list *)NXMapGet(cats, cls); 328 if (!list) return; 329 330 uint32_t i; 331 for (i = 0; i < list->count; i++) { 332 if (list->list[i].cat == cat) { 333 // shift entries to preserve list order 334 memmove(&list->list[i], &list->list[i+1], 335 (list->count-i-1) * sizeof(list->list[i])); 336 list->count--; 337 return; 338 } 339 } 340} 341 342 343/*********************************************************************** 344* unattachedCategoriesForClass 345* Returns the list of unattached categories for a class, and 346* deletes them from the list. 347* The result must be freed by the caller. 348* Locking: runtimeLock must be held by the caller. 349**********************************************************************/ 350static category_list *unattachedCategoriesForClass(Class cls) 351{ 352 rwlock_assert_writing(&runtimeLock); 353 return (category_list *)NXMapRemove(unattachedCategories(), cls); 354} 355 356 357/*********************************************************************** 358* classNSObject 359* Returns class NSObject. 360* Locking: none 361**********************************************************************/ 362static Class classNSObject(void) 363{ 364 extern objc_class OBJC_CLASS_$_NSObject; 365 return (Class)&OBJC_CLASS_$_NSObject; 366} 367 368 369/*********************************************************************** 370* printReplacements 371* Implementation of PrintReplacedMethods / OBJC_PRINT_REPLACED_METHODS. 372* Warn about methods from cats that override other methods in cats or cls. 373* Assumes no methods from cats have been added to cls yet. 374**********************************************************************/ 375static void printReplacements(Class cls, category_list *cats) 376{ 377 uint32_t c; 378 BOOL isMeta = cls->isMetaClass(); 379 380 if (!cats) return; 381 382 // Newest categories are LAST in cats 383 // Later categories override earlier ones. 384 for (c = 0; c < cats->count; c++) { 385 category_t *cat = cats->list[c].cat; 386 uint32_t cmCount = cat_method_count(cat, isMeta); 387 uint32_t m; 388 for (m = 0; m < cmCount; m++) { 389 uint32_t c2, m2; 390 method_t *meth2 = nil; 391 method_t *meth = cat_method_nth(cat, isMeta, m); 392 SEL s = sel_registerName((const char *)meth->name); 393 394 // Don't warn about GC-ignored selectors 395 if (ignoreSelector(s)) continue; 396 397 // Look for method in earlier categories 398 for (c2 = 0; c2 < c; c2++) { 399 category_t *cat2 = cats->list[c2].cat; 400 uint32_t cm2Count = cat_method_count(cat2, isMeta); 401 for (m2 = 0; m2 < cm2Count; m2++) { 402 meth2 = cat_method_nth(cat2, isMeta, m2); 403 SEL s2 = sel_registerName((const char *)meth2->name); 404 if (s == s2) goto whine; 405 } 406 } 407 408 // Look for method in cls 409 FOREACH_METHOD_LIST(mlist, cls, { 410 for (m2 = 0; m2 < mlist->count; m2++) { 411 meth2 = method_list_nth(mlist, m2); 412 SEL s2 = sel_registerName((const char *)meth2->name); 413 if (s == s2) goto whine; 414 } 415 }); 416 417 // Didn't find any override. 418 continue; 419 420 whine: 421 // Found an override. 422 logReplacedMethod(cls->name(), s, cls->isMetaClass(), cat->name, 423 _method_getImplementation(meth2), 424 _method_getImplementation(meth)); 425 } 426 } 427} 428 429 430static BOOL isBundleClass(Class cls) 431{ 432 return (cls->data()->ro->flags & RO_FROM_BUNDLE) ? YES : NO; 433} 434 435 436static method_list_t * 437fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort) 438{ 439 rwlock_assert_writing(&runtimeLock); 440 assert(!isMethodListFixedUp(mlist)); 441 442 mlist = (method_list_t *) 443 _memdup_internal(mlist, method_list_size(mlist)); 444 445 // fixme lock less in attachMethodLists ? 446 sel_lock(); 447 448 // Unique selectors in list. 449 uint32_t m; 450 for (m = 0; m < mlist->count; m++) { 451 method_t *meth = method_list_nth(mlist, m); 452 SEL sel = sel_registerNameNoLock((const char *)meth->name, bundleCopy); 453 meth->name = sel; 454 455 if (ignoreSelector(sel)) { 456 meth->imp = (IMP)&_objc_ignored_method; 457 } 458 } 459 460 sel_unlock(); 461 462 // Sort by selector address. 463 if (sort) { 464 method_t::SortBySELAddress sorter; 465 std::stable_sort(mlist->begin(), mlist->end(), sorter); 466 } 467 468 // Mark method list as uniqued and sorted 469 setMethodListFixedUp(mlist); 470 471 return mlist; 472} 473 474 475static void 476attachMethodLists(Class cls, method_list_t **addedLists, int addedCount, 477 bool baseMethods, bool methodsFromBundle, 478 bool flushCaches) 479{ 480 rwlock_assert_writing(&runtimeLock); 481 482 // Don't scan redundantly 483 bool scanForCustomRR = !UseGC && !cls->hasCustomRR(); 484 bool scanForCustomAWZ = !UseGC && !cls->hasCustomAWZ(); 485 486 // RR special cases: 487 // GC is custom RR. 488 // NSObject's base instance methods are not custom RR. 489 // All other root classes are custom RR. 490 // updateCustomRR_AWZ also knows about these cases. 491 if (UseGC) { 492 cls->setHasCustomRR(); 493 scanForCustomRR = false; 494 } 495 if (baseMethods && scanForCustomRR && cls->isRootClass()) { 496 if (cls != classNSObject()) { 497 cls->setHasCustomRR(); 498 } 499 scanForCustomRR = false; 500 } 501 502 // AWZ special cases: 503 // NSObject's base class methods are not custom AWZ. 504 // All other root metaclasses are custom AWZ. 505 // updateCustomRR_AWZ also knows about these cases. 506 if (baseMethods && scanForCustomAWZ && cls->isRootMetaclass()) { 507 if (cls != classNSObject()->ISA()) { 508 cls->setHasCustomAWZ(); 509 } 510 scanForCustomAWZ = false; 511 } 512 513 // Method list array is nil-terminated. 514 // Some elements of lists are nil; we must filter them out. 515 516 method_list_t *oldBuf[2]; 517 method_list_t **oldLists; 518 int oldCount = 0; 519 if (cls->data()->flags & RW_METHOD_ARRAY) { 520 oldLists = cls->data()->method_lists; 521 } else { 522 oldBuf[0] = cls->data()->method_list; 523 oldBuf[1] = nil; 524 oldLists = oldBuf; 525 } 526 if (oldLists) { 527 while (oldLists[oldCount]) oldCount++; 528 } 529 530 int newCount = oldCount; 531 for (int i = 0; i < addedCount; i++) { 532 if (addedLists[i]) newCount++; // only non-nil entries get added 533 } 534 535 method_list_t *newBuf[2]; 536 method_list_t **newLists; 537 if (newCount > 1) { 538 newLists = (method_list_t **) 539 _malloc_internal((1 + newCount) * sizeof(*newLists)); 540 } else { 541 newLists = newBuf; 542 } 543 544 // Add method lists to array. 545 // Reallocate un-fixed method lists. 546 // The new methods are PREPENDED to the method list array. 547 548 newCount = 0; 549 int i; 550 for (i = 0; i < addedCount; i++) { 551 method_list_t *mlist = addedLists[i]; 552 if (!mlist) continue; 553 554 // Fixup selectors if necessary 555 if (!isMethodListFixedUp(mlist)) { 556 mlist = fixupMethodList(mlist, methodsFromBundle, true/*sort*/); 557 } 558 559 // Scan for method implementations tracked by the class's flags 560 for (uint32_t m = 0; 561 (scanForCustomRR || scanForCustomAWZ) && m < mlist->count; 562 m++) 563 { 564 SEL sel = method_list_nth(mlist, m)->name; 565 if (scanForCustomRR && isRRSelector(sel)) { 566 cls->setHasCustomRR(); 567 scanForCustomRR = false; 568 } else if (scanForCustomAWZ && isAWZSelector(sel)) { 569 cls->setHasCustomAWZ(); 570 scanForCustomAWZ = false; 571 } 572 } 573 574 // Update method caches 575 if (flushCaches) { 576 cache_eraseMethods(cls, mlist); 577 } 578 579 // Fill method list array 580 newLists[newCount++] = mlist; 581 } 582 583 // Copy old methods to the method list array 584 for (i = 0; i < oldCount; i++) { 585 newLists[newCount++] = oldLists[i]; 586 } 587 if (oldLists && oldLists != oldBuf) free(oldLists); 588 589 // nil-terminate 590 newLists[newCount] = nil; 591 592 if (newCount > 1) { 593 assert(newLists != newBuf); 594 cls->data()->method_lists = newLists; 595 cls->setInfo(RW_METHOD_ARRAY); 596 } else { 597 assert(newLists == newBuf); 598 cls->data()->method_list = newLists[0]; 599 assert(!(cls->data()->flags & RW_METHOD_ARRAY)); 600 } 601} 602 603static void 604attachCategoryMethods(Class cls, category_list *cats, bool flushCaches) 605{ 606 if (!cats) return; 607 if (PrintReplacedMethods) printReplacements(cls, cats); 608 609 bool isMeta = cls->isMetaClass(); 610 method_list_t **mlists = (method_list_t **) 611 _malloc_internal(cats->count * sizeof(*mlists)); 612 613 // Count backwards through cats to get newest categories first 614 int mcount = 0; 615 int i = cats->count; 616 BOOL fromBundle = NO; 617 while (i--) { 618 method_list_t *mlist = cat_method_list(cats->list[i].cat, isMeta); 619 if (mlist) { 620 mlists[mcount++] = mlist; 621 fromBundle |= cats->list[i].fromBundle; 622 } 623 } 624 625 attachMethodLists(cls, mlists, mcount, NO, fromBundle, flushCaches); 626 627 _free_internal(mlists); 628 629} 630 631 632static chained_property_list * 633buildPropertyList(const property_list_t *plist, category_list *cats, BOOL isMeta) 634{ 635 chained_property_list *newlist; 636 uint32_t count = 0; 637 uint32_t p, c; 638 639 // Count properties in all lists. 640 if (plist) count = plist->count; 641 if (cats) { 642 for (c = 0; c < cats->count; c++) { 643 category_t *cat = cats->list[c].cat; 644 /* 645 if (isMeta && cat->classProperties) { 646 count += cat->classProperties->count; 647 } 648 else*/ 649 if (!isMeta && cat->instanceProperties) { 650 count += cat->instanceProperties->count; 651 } 652 } 653 } 654 655 if (count == 0) return nil; 656 657 // Allocate new list. 658 newlist = (chained_property_list *) 659 _malloc_internal(sizeof(*newlist) + count * sizeof(property_t)); 660 newlist->count = 0; 661 newlist->next = nil; 662 663 // Copy properties; newest categories first, then ordinary properties 664 if (cats) { 665 c = cats->count; 666 while (c--) { 667 property_list_t *cplist; 668 category_t *cat = cats->list[c].cat; 669 /* 670 if (isMeta) { 671 cplist = cat->classProperties; 672 } else */ 673 { 674 cplist = cat->instanceProperties; 675 } 676 if (cplist) { 677 for (p = 0; p < cplist->count; p++) { 678 newlist->list[newlist->count++] = 679 *property_list_nth(cplist, p); 680 } 681 } 682 } 683 } 684 if (plist) { 685 for (p = 0; p < plist->count; p++) { 686 newlist->list[newlist->count++] = *property_list_nth(plist, p); 687 } 688 } 689 690 assert(newlist->count == count); 691 692 return newlist; 693} 694 695 696static const protocol_list_t ** 697buildProtocolList(category_list *cats, const protocol_list_t *base, 698 const protocol_list_t **protos) 699{ 700 const protocol_list_t **p, **newp; 701 const protocol_list_t **newprotos; 702 unsigned int count = 0; 703 unsigned int i; 704 705 // count protocol list in base 706 if (base) count++; 707 708 // count protocol lists in cats 709 if (cats) for (i = 0; i < cats->count; i++) { 710 category_t *cat = cats->list[i].cat; 711 if (cat->protocols) count++; 712 } 713 714 // no base or category protocols? return existing protocols unchanged 715 if (count == 0) return protos; 716 717 // count protocol lists in protos 718 for (p = protos; p && *p; p++) { 719 count++; 720 } 721 722 if (count == 0) return nil; 723 724 newprotos = (const protocol_list_t **) 725 _malloc_internal((count+1) * sizeof(protocol_list_t *)); 726 newp = newprotos; 727 728 if (base) { 729 *newp++ = base; 730 } 731 732 for (p = protos; p && *p; p++) { 733 *newp++ = *p; 734 } 735 736 if (cats) for (i = 0; i < cats->count; i++) { 737 category_t *cat = cats->list[i].cat; 738 if (cat->protocols) { 739 *newp++ = cat->protocols; 740 } 741 } 742 743 *newp = nil; 744 745 return newprotos; 746} 747 748 749/*********************************************************************** 750* methodizeClass 751* Fixes up cls's method list, protocol list, and property list. 752* Attaches any outstanding categories. 753* Locking: runtimeLock must be held by the caller 754**********************************************************************/ 755static void methodizeClass(Class cls) 756{ 757 category_list *cats; 758 BOOL isMeta; 759 760 rwlock_assert_writing(&runtimeLock); 761 762 isMeta = cls->isMetaClass(); 763 764 // Methodizing for the first time 765 if (PrintConnecting) { 766 _objc_inform("CLASS: methodizing class '%s' %s", 767 cls->name(), isMeta ? "(meta)" : ""); 768 } 769 770 // Build method and protocol and property lists. 771 // Include methods and protocols and properties from categories, if any 772 773 attachMethodLists(cls, (method_list_t **)&cls->data()->ro->baseMethods, 1, 774 YES, isBundleClass(cls), NO); 775 776 // Root classes get bonus method implementations if they don't have 777 // them already. These apply before category replacements. 778 779 if (cls->isRootMetaclass()) { 780 // root metaclass 781 addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO); 782 } 783 784 cats = unattachedCategoriesForClass(cls); 785 attachCategoryMethods(cls, cats, NO); 786 787 if (cats || cls->data()->ro->baseProperties) { 788 cls->data()->properties = 789 buildPropertyList(cls->data()->ro->baseProperties, cats, isMeta); 790 } 791 792 if (cats || cls->data()->ro->baseProtocols) { 793 cls->data()->protocols = 794 buildProtocolList(cats, cls->data()->ro->baseProtocols, nil); 795 } 796 797 if (PrintConnecting) { 798 uint32_t i; 799 if (cats) { 800 for (i = 0; i < cats->count; i++) { 801 _objc_inform("CLASS: attached category %c%s(%s)", 802 isMeta ? '+' : '-', 803 cls->name(), cats->list[i].cat->name); 804 } 805 } 806 } 807 808 if (cats) _free_internal(cats); 809 810#ifndef NDEBUG 811 // Debug: sanity-check all SELs; log method list contents 812 FOREACH_METHOD_LIST(mlist, cls, { 813 method_list_t::method_iterator iter = mlist->begin(); 814 method_list_t::method_iterator end = mlist->end(); 815 for ( ; iter != end; ++iter) { 816 if (PrintConnecting) { 817 _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-', 818 cls->name(), sel_getName(iter->name)); 819 } 820 assert(ignoreSelector(iter->name) || sel_registerName(sel_getName(iter->name))==iter->name); 821 } 822 }); 823#endif 824} 825 826 827/*********************************************************************** 828* remethodizeClass 829* Attach outstanding categories to an existing class. 830* Fixes up cls's method list, protocol list, and property list. 831* Updates method caches for cls and its subclasses. 832* Locking: runtimeLock must be held by the caller 833**********************************************************************/ 834static void remethodizeClass(Class cls) 835{ 836 category_list *cats; 837 BOOL isMeta; 838 839 rwlock_assert_writing(&runtimeLock); 840 841 isMeta = cls->isMetaClass(); 842 843 // Re-methodizing: check for more categories 844 if ((cats = unattachedCategoriesForClass(cls))) { 845 chained_property_list *newproperties; 846 const protocol_list_t **newprotos; 847 848 if (PrintConnecting) { 849 _objc_inform("CLASS: attaching categories to class '%s' %s", 850 cls->name(), isMeta ? "(meta)" : ""); 851 } 852 853 // Update methods, properties, protocols 854 855 attachCategoryMethods(cls, cats, YES); 856 857 newproperties = buildPropertyList(nil, cats, isMeta); 858 if (newproperties) { 859 newproperties->next = cls->data()->properties; 860 cls->data()->properties = newproperties; 861 } 862 863 newprotos = buildProtocolList(cats, nil, cls->data()->protocols); 864 if (cls->data()->protocols && cls->data()->protocols != newprotos) { 865 _free_internal(cls->data()->protocols); 866 } 867 cls->data()->protocols = newprotos; 868 869 _free_internal(cats); 870 } 871} 872 873 874/*********************************************************************** 875* getClass 876* Looks up a class by name. The class MIGHT NOT be realized. 877* Locking: runtimeLock must be read- or write-locked by the caller. 878**********************************************************************/ 879 880// This is a misnomer: gdb_objc_realized_classes is actually a list of 881// named classes not in the dyld shared cache, whether realized or not. 882NXMapTable *gdb_objc_realized_classes; // exported for debuggers in objc-gdb.h 883 884static Class getClass(const char *name) 885{ 886 rwlock_assert_locked(&runtimeLock); 887 888 // allocated in _read_images 889 assert(gdb_objc_realized_classes); 890 891 // Try runtime-allocated table 892 Class result = (Class)NXMapGet(gdb_objc_realized_classes, name); 893 if (result) return result; 894 895 // Try table from dyld shared cache 896 return getPreoptimizedClass(name); 897} 898 899 900/*********************************************************************** 901* addNamedClass 902* Adds name => cls to the named non-meta class map. 903* Warns about duplicate class names and keeps the old mapping. 904* Locking: runtimeLock must be held by the caller 905**********************************************************************/ 906static void addNamedClass(Class cls, const char *name) 907{ 908 rwlock_assert_writing(&runtimeLock); 909 Class old; 910 if ((old = getClass(name))) { 911 inform_duplicate(name, old, cls); 912 } else { 913 NXMapInsert(gdb_objc_realized_classes, name, cls); 914 } 915 assert(!(cls->data()->flags & RO_META)); 916 917 // wrong: constructed classes are already realized when they get here 918 // assert(!cls->isRealized()); 919} 920 921 922/*********************************************************************** 923* removeNamedClass 924* Removes cls from the name => cls map. 925* Locking: runtimeLock must be held by the caller 926**********************************************************************/ 927static void removeNamedClass(Class cls, const char *name) 928{ 929 rwlock_assert_writing(&runtimeLock); 930 assert(!(cls->data()->flags & RO_META)); 931 if (cls == NXMapGet(gdb_objc_realized_classes, name)) { 932 NXMapRemove(gdb_objc_realized_classes, name); 933 } else { 934 // cls has a name collision with another class - don't remove the other 935 } 936} 937 938 939/*********************************************************************** 940* realizedClasses 941* Returns the class list for realized non-meta classes. 942* Locking: runtimeLock must be read- or write-locked by the caller 943**********************************************************************/ 944static NXHashTable *realized_class_hash = nil; 945 946static NXHashTable *realizedClasses(void) 947{ 948 rwlock_assert_locked(&runtimeLock); 949 950 // allocated in _read_images 951 assert(realized_class_hash); 952 953 return realized_class_hash; 954} 955 956 957/*********************************************************************** 958* realizedMetaclasses 959* Returns the class list for realized metaclasses. 960* Locking: runtimeLock must be read- or write-locked by the caller 961**********************************************************************/ 962static NXHashTable *realized_metaclass_hash = nil; 963static NXHashTable *realizedMetaclasses(void) 964{ 965 rwlock_assert_locked(&runtimeLock); 966 967 // allocated in _read_images 968 assert(realized_metaclass_hash); 969 970 return realized_metaclass_hash; 971} 972 973 974/*********************************************************************** 975* addRealizedClass 976* Adds cls to the realized non-meta class hash. 977* Locking: runtimeLock must be held by the caller 978**********************************************************************/ 979static void addRealizedClass(Class cls) 980{ 981 rwlock_assert_writing(&runtimeLock); 982 void *old; 983 old = NXHashInsert(realizedClasses(), cls); 984 objc_addRegisteredClass(cls); 985 assert(!cls->isMetaClass()); 986 assert(!old); 987} 988 989 990/*********************************************************************** 991* removeRealizedClass 992* Removes cls from the realized non-meta class hash. 993* Locking: runtimeLock must be held by the caller 994**********************************************************************/ 995static void removeRealizedClass(Class cls) 996{ 997 rwlock_assert_writing(&runtimeLock); 998 if (cls->isRealized()) { 999 assert(!cls->isMetaClass()); 1000 NXHashRemove(realizedClasses(), cls); 1001 objc_removeRegisteredClass(cls); 1002 } 1003} 1004 1005 1006/*********************************************************************** 1007* addRealizedMetaclass 1008* Adds cls to the realized metaclass hash. 1009* Locking: runtimeLock must be held by the caller 1010**********************************************************************/ 1011static void addRealizedMetaclass(Class cls) 1012{ 1013 rwlock_assert_writing(&runtimeLock); 1014 void *old; 1015 old = NXHashInsert(realizedMetaclasses(), cls); 1016 assert(cls->isMetaClass()); 1017 assert(!old); 1018} 1019 1020 1021/*********************************************************************** 1022* removeRealizedMetaclass 1023* Removes cls from the realized metaclass hash. 1024* Locking: runtimeLock must be held by the caller 1025**********************************************************************/ 1026static void removeRealizedMetaclass(Class cls) 1027{ 1028 rwlock_assert_writing(&runtimeLock); 1029 if (cls->isRealized()) { 1030 assert(cls->isMetaClass()); 1031 NXHashRemove(realizedMetaclasses(), cls); 1032 } 1033} 1034 1035 1036/*********************************************************************** 1037* futureNamedClasses 1038* Returns the classname => future class map for unrealized future classes. 1039* Locking: runtimeLock must be held by the caller 1040**********************************************************************/ 1041static NXMapTable *futureNamedClasses(void) 1042{ 1043 rwlock_assert_writing(&runtimeLock); 1044 1045 static NXMapTable *future_named_class_map = nil; 1046 1047 if (future_named_class_map) return future_named_class_map; 1048 1049 // future_named_class_map is big enough for CF's classes and a few others 1050 future_named_class_map = 1051 NXCreateMapTableFromZone(NXStrValueMapPrototype, 32, 1052 _objc_internal_zone()); 1053 1054 return future_named_class_map; 1055} 1056 1057 1058/*********************************************************************** 1059* addFutureNamedClass 1060* Installs cls as the class structure to use for the named class if it appears. 1061* Locking: runtimeLock must be held by the caller 1062**********************************************************************/ 1063static void addFutureNamedClass(const char *name, Class cls) 1064{ 1065 void *old; 1066 1067 rwlock_assert_writing(&runtimeLock); 1068 1069 if (PrintFuture) { 1070 _objc_inform("FUTURE: reserving %p for %s", (void*)cls, name); 1071 } 1072 1073 class_rw_t *rw = (class_rw_t *)_calloc_internal(sizeof(class_rw_t), 1); 1074 class_ro_t *ro = (class_ro_t *)_calloc_internal(sizeof(class_ro_t), 1); 1075 ro->name = _strdup_internal(name); 1076 rw->ro = ro; 1077 cls->setData(rw); 1078 cls->data()->flags = RO_FUTURE; 1079 1080 old = NXMapKeyCopyingInsert(futureNamedClasses(), name, cls); 1081 assert(!old); 1082} 1083 1084 1085/*********************************************************************** 1086* removeFutureNamedClass 1087* Removes the named class from the unrealized future class list, 1088* because it has been realized. 1089* Locking: runtimeLock must be held by the caller 1090**********************************************************************/ 1091static void removeFutureNamedClass(const char *name) 1092{ 1093 rwlock_assert_writing(&runtimeLock); 1094 1095 NXMapKeyFreeingRemove(futureNamedClasses(), name); 1096} 1097 1098 1099/*********************************************************************** 1100* remappedClasses 1101* Returns the oldClass => newClass map for realized future classes. 1102* Returns the oldClass => nil map for ignored weak-linked classes. 1103* Locking: runtimeLock must be read- or write-locked by the caller 1104**********************************************************************/ 1105static NXMapTable *remappedClasses(BOOL create) 1106{ 1107 static NXMapTable *remapped_class_map = nil; 1108 1109 rwlock_assert_locked(&runtimeLock); 1110 1111 if (remapped_class_map) return remapped_class_map; 1112 if (!create) return nil; 1113 1114 // remapped_class_map is big enough to hold CF's classes and a few others 1115 INIT_ONCE_PTR(remapped_class_map, 1116 NXCreateMapTableFromZone(NXPtrValueMapPrototype, 32, 1117 _objc_internal_zone()), 1118 NXFreeMapTable(v)); 1119 1120 return remapped_class_map; 1121} 1122 1123 1124/*********************************************************************** 1125* noClassesRemapped 1126* Returns YES if no classes have been remapped 1127* Locking: runtimeLock must be read- or write-locked by the caller 1128**********************************************************************/ 1129static BOOL noClassesRemapped(void) 1130{ 1131 rwlock_assert_locked(&runtimeLock); 1132 1133 BOOL result = (remappedClasses(NO) == nil); 1134 return result; 1135} 1136 1137 1138/*********************************************************************** 1139* addRemappedClass 1140* newcls is a realized future class, replacing oldcls. 1141* OR newcls is nil, replacing ignored weak-linked class oldcls. 1142* Locking: runtimeLock must be write-locked by the caller 1143**********************************************************************/ 1144static void addRemappedClass(Class oldcls, Class newcls) 1145{ 1146 rwlock_assert_writing(&runtimeLock); 1147 1148 if (PrintFuture) { 1149 _objc_inform("FUTURE: using %p instead of %p for %s", 1150 (void*)oldcls, (void*)newcls, oldcls->name()); 1151 } 1152 1153 void *old; 1154 old = NXMapInsert(remappedClasses(YES), oldcls, newcls); 1155 assert(!old); 1156} 1157 1158 1159/*********************************************************************** 1160* remapClass 1161* Returns the live class pointer for cls, which may be pointing to 1162* a class struct that has been reallocated. 1163* Returns nil if cls is ignored because of weak linking. 1164* Locking: runtimeLock must be read- or write-locked by the caller 1165**********************************************************************/ 1166static Class remapClass(Class cls) 1167{ 1168 rwlock_assert_locked(&runtimeLock); 1169 1170 Class c2; 1171 1172 if (!cls) return nil; 1173 1174 if (NXMapMember(remappedClasses(YES), cls, (void**)&c2) == NX_MAPNOTAKEY) { 1175 return cls; 1176 } else { 1177 return c2; 1178 } 1179} 1180 1181static Class remapClass(classref_t cls) 1182{ 1183 return remapClass((Class)cls); 1184} 1185 1186Class _class_remap(Class cls) 1187{ 1188 rwlock_read(&runtimeLock); 1189 Class result = remapClass(cls); 1190 rwlock_unlock_read(&runtimeLock); 1191 return result; 1192} 1193 1194/*********************************************************************** 1195* remapClassRef 1196* Fix up a class ref, in case the class referenced has been reallocated 1197* or is an ignored weak-linked class. 1198* Locking: runtimeLock must be read- or write-locked by the caller 1199**********************************************************************/ 1200static void remapClassRef(Class *clsref) 1201{ 1202 rwlock_assert_locked(&runtimeLock); 1203 1204 Class newcls = remapClass(*clsref); 1205 if (*clsref != newcls) *clsref = newcls; 1206} 1207 1208 1209/*********************************************************************** 1210* nonMetaClasses 1211* Returns the memoized metaclass => class map 1212* Used for some cases of +initialize. 1213* This map does not contain all classes and metaclasses. It only 1214* contains memoized results from the slow path in getNonMetaClass(), 1215* and classes that the slow path can't find (like objc_registerClassPair). 1216* Locking: runtimeLock must be read- or write-locked by the caller 1217**********************************************************************/ 1218static NXMapTable *nonmeta_class_map = nil; 1219static NXMapTable *nonMetaClasses(void) 1220{ 1221 rwlock_assert_locked(&runtimeLock); 1222 1223 if (nonmeta_class_map) return nonmeta_class_map; 1224 1225 // nonmeta_class_map is typically small 1226 INIT_ONCE_PTR(nonmeta_class_map, 1227 NXCreateMapTableFromZone(NXPtrValueMapPrototype, 32, 1228 _objc_internal_zone()), 1229 NXFreeMapTable(v)); 1230 1231 return nonmeta_class_map; 1232} 1233 1234 1235/*********************************************************************** 1236* addNonMetaClass 1237* Adds metacls => cls to the memoized metaclass map 1238* Locking: runtimeLock must be held by the caller 1239**********************************************************************/ 1240static void addNonMetaClass(Class cls) 1241{ 1242 rwlock_assert_writing(&runtimeLock); 1243 void *old; 1244 old = NXMapInsert(nonMetaClasses(), cls->ISA(), cls); 1245 1246 assert(cls->isRealized()); 1247 assert(cls->ISA()->isRealized()); 1248 assert(!cls->isMetaClass()); 1249 assert(cls->ISA()->isMetaClass()); 1250 assert(!old); 1251} 1252 1253 1254static void removeNonMetaClass(Class cls) 1255{ 1256 rwlock_assert_writing(&runtimeLock); 1257 NXMapRemove(nonMetaClasses(), cls->ISA()); 1258} 1259 1260 1261/*********************************************************************** 1262* getNonMetaClass 1263* Return the ordinary class for this class or metaclass. 1264* `inst` is an instance of `cls` or a subclass thereof, or nil. 1265* Non-nil inst is faster. 1266* Used by +initialize. 1267* Locking: runtimeLock must be read- or write-locked by the caller 1268**********************************************************************/ 1269static Class getNonMetaClass(Class metacls, id inst) 1270{ 1271 static int total, slow, memo; 1272 rwlock_assert_locked(&runtimeLock); 1273 1274 realizeClass(metacls); 1275 1276 total++; 1277 1278 // return cls itself if it's already a non-meta class 1279 if (!metacls->isMetaClass()) return metacls; 1280 1281 // metacls really is a metaclass 1282 1283 // special case for root metaclass 1284 // where inst == inst->ISA() == metacls is possible 1285 if (metacls->ISA() == metacls) { 1286 Class cls = metacls->superclass; 1287 assert(cls->isRealized()); 1288 assert(!cls->isMetaClass()); 1289 assert(cls->ISA() == metacls); 1290 if (cls->ISA() == metacls) return cls; 1291 } 1292 1293 // use inst if available 1294 if (inst) { 1295 Class cls = (Class)inst; 1296 realizeClass(cls); 1297 // cls may be a subclass - find the real class for metacls 1298 while (cls && cls->ISA() != metacls) { 1299 cls = cls->superclass; 1300 realizeClass(cls); 1301 } 1302 if (cls) { 1303 assert(!cls->isMetaClass()); 1304 assert(cls->ISA() == metacls); 1305 return cls; 1306 } 1307#if !NDEBUG 1308 _objc_fatal("cls is not an instance of metacls"); 1309#else 1310 // release build: be forgiving and fall through to slow lookups 1311#endif 1312 } 1313 1314 // try memoized table 1315 Class cls = (Class)NXMapGet(nonMetaClasses(), metacls); 1316 if (cls) { 1317 memo++; 1318 if (PrintInitializing) { 1319 _objc_inform("INITIALIZE: %d/%d (%g%%) memoized metaclass lookups", 1320 memo, total, memo*100.0/total); 1321 } 1322 1323 assert(cls->isRealized()); 1324 assert(!cls->isMetaClass()); 1325 assert(cls->ISA() == metacls); 1326 return cls; 1327 } 1328 1329 // try slow lookup 1330 slow++; 1331 if (PrintInitializing) { 1332 _objc_inform("INITIALIZE: %d/%d (%g%%) slow metaclass lookups", 1333 slow, total, slow*100.0/total); 1334 } 1335 1336 for (header_info *hi = FirstHeader; hi; hi = hi->next) { 1337 size_t count; 1338 classref_t *classlist = _getObjc2ClassList(hi, &count); 1339 for (size_t i = 0; i < count; i++) { 1340 cls = remapClass(classlist[i]); 1341 if (cls && cls->ISA() == metacls) { 1342 // memoize result 1343 realizeClass(cls); 1344 addNonMetaClass(cls); 1345 return cls; 1346 } 1347 } 1348 } 1349 1350 _objc_fatal("no class for metaclass %p", (void*)metacls); 1351 1352 return cls; 1353} 1354 1355 1356/*********************************************************************** 1357* _class_getNonMetaClass 1358* Return the ordinary class for this class or metaclass. 1359* Used by +initialize. 1360* Locking: acquires runtimeLock 1361**********************************************************************/ 1362Class _class_getNonMetaClass(Class cls, id obj) 1363{ 1364 rwlock_write(&runtimeLock); 1365 cls = getNonMetaClass(cls, obj); 1366 assert(cls->isRealized()); 1367 rwlock_unlock_write(&runtimeLock); 1368 1369 return cls; 1370} 1371 1372 1373/*********************************************************************** 1374* addSubclass 1375* Adds subcls as a subclass of supercls. 1376* Locking: runtimeLock must be held by the caller. 1377**********************************************************************/ 1378static void addSubclass(Class supercls, Class subcls) 1379{ 1380 rwlock_assert_writing(&runtimeLock); 1381 1382 if (supercls && subcls) { 1383 assert(supercls->isRealized()); 1384 assert(subcls->isRealized()); 1385 subcls->data()->nextSiblingClass = supercls->data()->firstSubclass; 1386 supercls->data()->firstSubclass = subcls; 1387 1388 if (supercls->data()->flags & RW_HAS_CXX_CTOR) { 1389 subcls->data()->flags |= RW_HAS_CXX_CTOR; 1390 } 1391 1392 if (supercls->data()->flags & RW_HAS_CXX_DTOR) { 1393 subcls->data()->flags |= RW_HAS_CXX_DTOR; 1394 } 1395 1396 if (supercls->hasCustomRR()) { 1397 subcls->setHasCustomRR(true); 1398 } 1399 1400 if (supercls->hasCustomAWZ()) { 1401 subcls->setHasCustomAWZ(true); 1402 } 1403 } 1404} 1405 1406 1407/*********************************************************************** 1408* removeSubclass 1409* Removes subcls as a subclass of supercls. 1410* Locking: runtimeLock must be held by the caller. 1411**********************************************************************/ 1412static void removeSubclass(Class supercls, Class subcls) 1413{ 1414 rwlock_assert_writing(&runtimeLock); 1415 assert(supercls->isRealized()); 1416 assert(subcls->isRealized()); 1417 assert(subcls->superclass == supercls); 1418 1419 Class *cp; 1420 for (cp = &supercls->data()->firstSubclass; 1421 *cp && *cp != subcls; 1422 cp = &(*cp)->data()->nextSiblingClass) 1423 ; 1424 assert(*cp == subcls); 1425 *cp = subcls->data()->nextSiblingClass; 1426} 1427 1428 1429 1430/*********************************************************************** 1431* protocols 1432* Returns the protocol name => protocol map for protocols. 1433* Locking: runtimeLock must read- or write-locked by the caller 1434**********************************************************************/ 1435static NXMapTable *protocols(void) 1436{ 1437 static NXMapTable *protocol_map = nil; 1438 1439 rwlock_assert_locked(&runtimeLock); 1440 1441 INIT_ONCE_PTR(protocol_map, 1442 NXCreateMapTableFromZone(NXStrValueMapPrototype, 16, 1443 _objc_internal_zone()), 1444 NXFreeMapTable(v) ); 1445 1446 return protocol_map; 1447} 1448 1449 1450/*********************************************************************** 1451* remapProtocol 1452* Returns the live protocol pointer for proto, which may be pointing to 1453* a protocol struct that has been reallocated. 1454* Locking: runtimeLock must be read- or write-locked by the caller 1455**********************************************************************/ 1456static protocol_t *remapProtocol(protocol_ref_t proto) 1457{ 1458 rwlock_assert_locked(&runtimeLock); 1459 1460 protocol_t *newproto = (protocol_t *) 1461 NXMapGet(protocols(), ((protocol_t *)proto)->name); 1462 return newproto ? newproto : (protocol_t *)proto; 1463} 1464 1465 1466/*********************************************************************** 1467* remapProtocolRef 1468* Fix up a protocol ref, in case the protocol referenced has been reallocated. 1469* Locking: runtimeLock must be read- or write-locked by the caller 1470**********************************************************************/ 1471static void remapProtocolRef(protocol_t **protoref) 1472{ 1473 rwlock_assert_locked(&runtimeLock); 1474 1475 protocol_t *newproto = remapProtocol((protocol_ref_t)*protoref); 1476 if (*protoref != newproto) *protoref = newproto; 1477} 1478 1479 1480/*********************************************************************** 1481* moveIvars 1482* Slides a class's ivars to accommodate the given superclass size. 1483* Also slides ivar and weak GC layouts if provided. 1484* Ivars are NOT compacted to compensate for a superclass that shrunk. 1485* Locking: runtimeLock must be held by the caller. 1486**********************************************************************/ 1487static void moveIvars(class_ro_t *ro, uint32_t superSize, 1488 layout_bitmap *ivarBitmap, layout_bitmap *weakBitmap) 1489{ 1490 rwlock_assert_writing(&runtimeLock); 1491 1492 uint32_t diff; 1493 uint32_t i; 1494 1495 assert(superSize > ro->instanceStart); 1496 diff = superSize - ro->instanceStart; 1497 1498 if (ro->ivars) { 1499 // Find maximum alignment in this class's ivars 1500 uint32_t maxAlignment = 1; 1501 for (i = 0; i < ro->ivars->count; i++) { 1502 ivar_t *ivar = ivar_list_nth(ro->ivars, i); 1503 if (!ivar->offset) continue; // anonymous bitfield 1504 1505 uint32_t alignment = ivar->alignment(); 1506 if (alignment > maxAlignment) maxAlignment = alignment; 1507 } 1508 1509 // Compute a slide value that preserves that alignment 1510 uint32_t alignMask = maxAlignment - 1; 1511 if (diff & alignMask) diff = (diff + alignMask) & ~alignMask; 1512 1513 // Slide all of this class's ivars en masse 1514 for (i = 0; i < ro->ivars->count; i++) { 1515 ivar_t *ivar = ivar_list_nth(ro->ivars, i); 1516 if (!ivar->offset) continue; // anonymous bitfield 1517 1518 uint32_t oldOffset = (uint32_t)*ivar->offset; 1519 uint32_t newOffset = oldOffset + diff; 1520 *ivar->offset = newOffset; 1521 1522 if (PrintIvars) { 1523 _objc_inform("IVARS: offset %u -> %u for %s (size %u, align %u)", 1524 oldOffset, newOffset, ivar->name, 1525 ivar->size, ivar->alignment()); 1526 } 1527 } 1528 1529 // Slide GC layouts 1530 uint32_t oldOffset = ro->instanceStart; 1531 uint32_t newOffset = ro->instanceStart + diff; 1532 1533 if (ivarBitmap) { 1534 layout_bitmap_slide(ivarBitmap, 1535 oldOffset >> WORD_SHIFT, 1536 newOffset >> WORD_SHIFT); 1537 } 1538 if (weakBitmap) { 1539 layout_bitmap_slide(weakBitmap, 1540 oldOffset >> WORD_SHIFT, 1541 newOffset >> WORD_SHIFT); 1542 } 1543 } 1544 1545 *(uint32_t *)&ro->instanceStart += diff; 1546 *(uint32_t *)&ro->instanceSize += diff; 1547 1548 if (!ro->ivars) { 1549 // No ivars slid, but superclass changed size. 1550 // Expand bitmap in preparation for layout_bitmap_splat(). 1551 if (ivarBitmap) layout_bitmap_grow(ivarBitmap, ro->instanceSize >> WORD_SHIFT); 1552 if (weakBitmap) layout_bitmap_grow(weakBitmap, ro->instanceSize >> WORD_SHIFT); 1553 } 1554} 1555 1556 1557/*********************************************************************** 1558* getIvar 1559* Look up an ivar by name. 1560* Locking: runtimeLock must be read- or write-locked by the caller. 1561**********************************************************************/ 1562static ivar_t *getIvar(Class cls, const char *name) 1563{ 1564 rwlock_assert_locked(&runtimeLock); 1565 1566 const ivar_list_t *ivars; 1567 assert(cls->isRealized()); 1568 if ((ivars = cls->data()->ro->ivars)) { 1569 uint32_t i; 1570 for (i = 0; i < ivars->count; i++) { 1571 ivar_t *ivar = ivar_list_nth(ivars, i); 1572 if (!ivar->offset) continue; // anonymous bitfield 1573 1574 // ivar->name may be nil for anonymous bitfields etc. 1575 if (ivar->name && 0 == strcmp(name, ivar->name)) { 1576 return ivar; 1577 } 1578 } 1579 } 1580 1581 return nil; 1582} 1583 1584 1585static void reconcileInstanceVariables(Class cls, Class supercls) 1586{ 1587 class_rw_t *rw = cls->data(); 1588 const class_ro_t *ro = rw->ro; 1589 1590 assert(supercls); 1591 assert(!cls->isMetaClass()); 1592 1593 // Non-fragile ivars - reconcile this class with its superclass 1594 layout_bitmap ivarBitmap; 1595 layout_bitmap weakBitmap; 1596 bool layoutsChanged = NO; 1597 bool mergeLayouts = UseGC; 1598 const class_ro_t *super_ro = supercls->data()->ro; 1599 1600 if (DebugNonFragileIvars) { 1601 // Debugging: Force non-fragile ivars to slide. 1602 // Intended to find compiler, runtime, and program bugs. 1603 // If it fails with this and works without, you have a problem. 1604 1605 // Operation: Reset everything to 0 + misalignment. 1606 // Then force the normal sliding logic to push everything back. 1607 1608 // Exceptions: root classes, metaclasses, *NSCF* classes, 1609 // __CF* classes, NSConstantString, NSSimpleCString 1610 1611 // (already know it's not root because supercls != nil) 1612 if (!strstr(cls->name(), "NSCF") && 1613 0 != strncmp(cls->name(), "__CF", 4) && 1614 0 != strcmp(cls->name(), "NSConstantString") && 1615 0 != strcmp(cls->name(), "NSSimpleCString")) 1616 { 1617 uint32_t oldStart = ro->instanceStart; 1618 uint32_t oldSize = ro->instanceSize; 1619 class_ro_t *ro_w = make_ro_writeable(rw); 1620 ro = rw->ro; 1621 1622 // Find max ivar alignment in class. 1623 // default to word size to simplify ivar update 1624 uint32_t alignment = 1<<WORD_SHIFT; 1625 if (ro->ivars) { 1626 uint32_t i; 1627 for (i = 0; i < ro->ivars->count; i++) { 1628 ivar_t *ivar = ivar_list_nth(ro->ivars, i); 1629 if (ivar->alignment() > alignment) { 1630 alignment = ivar->alignment(); 1631 } 1632 } 1633 } 1634 uint32_t misalignment = ro->instanceStart % alignment; 1635 uint32_t delta = ro->instanceStart - misalignment; 1636 ro_w->instanceStart = misalignment; 1637 ro_w->instanceSize -= delta; 1638 1639 if (PrintIvars) { 1640 _objc_inform("IVARS: DEBUG: forcing ivars for class '%s' " 1641 "to slide (instanceStart %zu -> %zu)", 1642 cls->name(), (size_t)oldStart, 1643 (size_t)ro->instanceStart); 1644 } 1645 1646 if (ro->ivars) { 1647 uint32_t i; 1648 for (i = 0; i < ro->ivars->count; i++) { 1649 ivar_t *ivar = ivar_list_nth(ro->ivars, i); 1650 if (!ivar->offset) continue; // anonymous bitfield 1651 *ivar->offset -= delta; 1652 } 1653 } 1654 1655 if (mergeLayouts) { 1656 layout_bitmap layout; 1657 if (ro->ivarLayout) { 1658 layout = layout_bitmap_create(ro->ivarLayout, 1659 oldSize, oldSize, NO); 1660 layout_bitmap_slide_anywhere(&layout, 1661 delta >> WORD_SHIFT, 0); 1662 ro_w->ivarLayout = layout_string_create(layout); 1663 layout_bitmap_free(layout); 1664 } 1665 if (ro->weakIvarLayout) { 1666 layout = layout_bitmap_create(ro->weakIvarLayout, 1667 oldSize, oldSize, YES); 1668 layout_bitmap_slide_anywhere(&layout, 1669 delta >> WORD_SHIFT, 0); 1670 ro_w->weakIvarLayout = layout_string_create(layout); 1671 layout_bitmap_free(layout); 1672 } 1673 } 1674 } 1675 } 1676 1677 if (ro->instanceStart >= super_ro->instanceSize && !mergeLayouts) { 1678 // Superclass has not overgrown its space, and we don't 1679 // need to rebuild GC layouts. We're done here. 1680 return; 1681 } 1682 // fixme can optimize for "class has no new ivars", etc 1683 1684 if (mergeLayouts) { 1685 // WARNING: gcc c++ sets instanceStart/Size=0 for classes with 1686 // no local ivars, but does provide a layout bitmap. 1687 // Handle that case specially so layout_bitmap_create doesn't die 1688 // The other ivar sliding code below still works fine, and 1689 // the final result is a good class. 1690 if (ro->instanceStart == 0 && ro->instanceSize == 0) { 1691 // We can't use ro->ivarLayout because we don't know 1692 // how long it is. Force a new layout to be created. 1693 if (PrintIvars) { 1694 _objc_inform("IVARS: instanceStart/Size==0 for class %s; " 1695 "disregarding ivar layout", ro->name); 1696 } 1697 ivarBitmap = layout_bitmap_create_empty(super_ro->instanceSize, NO); 1698 weakBitmap = layout_bitmap_create_empty(super_ro->instanceSize, YES); 1699 layoutsChanged = YES; 1700 } 1701 else { 1702 ivarBitmap = 1703 layout_bitmap_create(ro->ivarLayout, 1704 ro->instanceSize, 1705 ro->instanceSize, NO); 1706 weakBitmap = 1707 layout_bitmap_create(ro->weakIvarLayout, 1708 ro->instanceSize, 1709 ro->instanceSize, YES); 1710 } 1711 } 1712 1713 if (ro->instanceStart < super_ro->instanceSize) { 1714 // Superclass has changed size. This class's ivars must move. 1715 // Also slide layout bits in parallel. 1716 // This code is incapable of compacting the subclass to 1717 // compensate for a superclass that shrunk, so don't do that. 1718 if (PrintIvars) { 1719 _objc_inform("IVARS: sliding ivars for class %s " 1720 "(superclass was %u bytes, now %u)", 1721 ro->name, ro->instanceStart, 1722 super_ro->instanceSize); 1723 } 1724 class_ro_t *ro_w = make_ro_writeable(rw); 1725 ro = rw->ro; 1726 moveIvars(ro_w, super_ro->instanceSize, 1727 mergeLayouts ? &ivarBitmap : nil, 1728 mergeLayouts ? &weakBitmap : nil); 1729 gdb_objc_class_changed(cls, OBJC_CLASS_IVARS_CHANGED, ro->name); 1730 layoutsChanged = YES; 1731 } 1732 1733 if (mergeLayouts) { 1734 // Check superclass's layout against this class's layout. 1735 // This needs to be done even if the superclass is not bigger. 1736 layout_bitmap superBitmap; 1737 1738 superBitmap = layout_bitmap_create(super_ro->ivarLayout, 1739 super_ro->instanceSize, 1740 super_ro->instanceSize, NO); 1741 layoutsChanged |= layout_bitmap_splat(ivarBitmap, superBitmap, 1742 ro->instanceStart); 1743 layout_bitmap_free(superBitmap); 1744 1745 // check the superclass' weak layout. 1746 superBitmap = layout_bitmap_create(super_ro->weakIvarLayout, 1747 super_ro->instanceSize, 1748 super_ro->instanceSize, YES); 1749 layoutsChanged |= layout_bitmap_splat(weakBitmap, superBitmap, 1750 ro->instanceStart); 1751 layout_bitmap_free(superBitmap); 1752 1753 // Rebuild layout strings if necessary. 1754 if (layoutsChanged) { 1755 if (PrintIvars) { 1756 _objc_inform("IVARS: gc layout changed for class %s",ro->name); 1757 } 1758 class_ro_t *ro_w = make_ro_writeable(rw); 1759 ro = rw->ro; 1760 if (DebugNonFragileIvars) { 1761 try_free(ro_w->ivarLayout); 1762 try_free(ro_w->weakIvarLayout); 1763 } 1764 ro_w->ivarLayout = layout_string_create(ivarBitmap); 1765 ro_w->weakIvarLayout = layout_string_create(weakBitmap); 1766 } 1767 1768 layout_bitmap_free(ivarBitmap); 1769 layout_bitmap_free(weakBitmap); 1770 } 1771} 1772 1773/*********************************************************************** 1774* realizeClass 1775* Performs first-time initialization on class cls, 1776* including allocating its read-write data. 1777* Returns the real class structure for the class. 1778* Locking: runtimeLock must be write-locked by the caller 1779**********************************************************************/ 1780static Class realizeClass(Class cls) 1781{ 1782 rwlock_assert_writing(&runtimeLock); 1783 1784 const class_ro_t *ro; 1785 class_rw_t *rw; 1786 Class supercls; 1787 Class metacls; 1788 BOOL isMeta; 1789 1790 if (!cls) return nil; 1791 if (cls->isRealized()) return cls; 1792 assert(cls == remapClass(cls)); 1793 1794 ro = (const class_ro_t *)cls->data(); 1795 if (ro->flags & RO_FUTURE) { 1796 // This was a future class. rw data is already allocated. 1797 rw = cls->data(); 1798 ro = cls->data()->ro; 1799 cls->changeInfo(RW_REALIZED, RW_FUTURE); 1800 } else { 1801 // Normal class. Allocate writeable class data. 1802 rw = (class_rw_t *)_calloc_internal(sizeof(class_rw_t), 1); 1803 rw->ro = ro; 1804 rw->flags = RW_REALIZED; 1805 cls->setData(rw); 1806 } 1807 1808 isMeta = (ro->flags & RO_META) ? YES : NO; 1809 1810 rw->version = isMeta ? 7 : 0; // old runtime went up to 6 1811 1812 if (PrintConnecting) { 1813 _objc_inform("CLASS: realizing class '%s' %s %p %p", 1814 ro->name, isMeta ? "(meta)" : "", (void*)cls, ro); 1815 } 1816 1817 // Realize superclass and metaclass, if they aren't already. 1818 // This needs to be done after RW_REALIZED is set above, for root classes. 1819 supercls = realizeClass(remapClass(cls->superclass)); 1820 metacls = realizeClass(remapClass(cls->ISA())); 1821 1822 // Check for remapped superclass and metaclass 1823 if (supercls != cls->superclass) { 1824 cls->superclass = supercls; 1825 } 1826 if (metacls != cls->ISA()) { 1827 cls->changeIsa(metacls); 1828 } 1829 1830 /* debug: print them all 1831 if (ro->ivars) { 1832 uint32_t i; 1833 for (i = 0; i < ro->ivars->count; i++) { 1834 ivar_t *ivar = ivar_list_nth(ro->ivars, i); 1835 if (!ivar->offset) continue; // anonymous bitfield 1836 1837 _objc_inform("IVARS: %s.%s (offset %u, size %u, align %u)", 1838 ro->name, ivar->name, 1839 *ivar->offset, ivar->size, ivar->alignment()); 1840 } 1841 } 1842 */ 1843 1844 // Reconcile instance variable offsets / layout. 1845 if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls); 1846 1847 // Copy some flags from ro to rw 1848 if (ro->flags & RO_HAS_CXX_STRUCTORS) { 1849 rw->flags |= RW_HAS_CXX_DTOR; 1850 if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) { 1851 rw->flags |= RW_HAS_CXX_CTOR; 1852 } 1853 } 1854 1855 // Connect this class to its superclass's subclass lists 1856 if (supercls) { 1857 addSubclass(supercls, cls); 1858 } 1859 1860 // Attach categories 1861 methodizeClass(cls); 1862 1863 if (!isMeta) { 1864 addRealizedClass(cls); 1865 } else { 1866 addRealizedMetaclass(cls); 1867 } 1868 1869 return cls; 1870} 1871 1872 1873/*********************************************************************** 1874* missingWeakSuperclass 1875* Return YES if some superclass of cls was weak-linked and is missing. 1876**********************************************************************/ 1877static BOOL 1878missingWeakSuperclass(Class cls) 1879{ 1880 assert(!cls->isRealized()); 1881 1882 if (!cls->superclass) { 1883 // superclass nil. This is normal for root classes only. 1884 return (!(cls->data()->flags & RO_ROOT)); 1885 } else { 1886 // superclass not nil. Check if a higher superclass is missing. 1887 Class supercls = remapClass(cls->superclass); 1888 assert(cls != cls->superclass); 1889 assert(cls != supercls); 1890 if (!supercls) return YES; 1891 if (supercls->isRealized()) return NO; 1892 return missingWeakSuperclass(supercls); 1893 } 1894} 1895 1896 1897/*********************************************************************** 1898* realizeAllClassesInImage 1899* Non-lazily realizes all unrealized classes in the given image. 1900* Locking: runtimeLock must be held by the caller. 1901**********************************************************************/ 1902static void realizeAllClassesInImage(header_info *hi) 1903{ 1904 rwlock_assert_writing(&runtimeLock); 1905 1906 size_t count, i; 1907 classref_t *classlist; 1908 1909 if (hi->allClassesRealized) return; 1910 1911 classlist = _getObjc2ClassList(hi, &count); 1912 1913 for (i = 0; i < count; i++) { 1914 realizeClass(remapClass(classlist[i])); 1915 } 1916 1917 hi->allClassesRealized = YES; 1918} 1919 1920 1921/*********************************************************************** 1922* realizeAllClasses 1923* Non-lazily realizes all unrealized classes in all known images. 1924* Locking: runtimeLock must be held by the caller. 1925**********************************************************************/ 1926static void realizeAllClasses(void) 1927{ 1928 rwlock_assert_writing(&runtimeLock); 1929 1930 header_info *hi; 1931 for (hi = FirstHeader; hi; hi = hi->next) { 1932 realizeAllClassesInImage(hi); 1933 } 1934} 1935 1936 1937/*********************************************************************** 1938* _objc_allocateFutureClass 1939* Allocate an unresolved future class for the given class name. 1940* Returns any existing allocation if one was already made. 1941* Assumes the named class doesn't exist yet. 1942* Locking: acquires runtimeLock 1943**********************************************************************/ 1944Class _objc_allocateFutureClass(const char *name) 1945{ 1946 rwlock_write(&runtimeLock); 1947 1948 Class cls; 1949 NXMapTable *future_named_class_map = futureNamedClasses(); 1950 1951 if ((cls = (Class)NXMapGet(future_named_class_map, name))) { 1952 // Already have a future class for this name. 1953 rwlock_unlock_write(&runtimeLock); 1954 return cls; 1955 } 1956 1957 cls = _calloc_class(sizeof(objc_class)); 1958 addFutureNamedClass(name, cls); 1959 1960 rwlock_unlock_write(&runtimeLock); 1961 return cls; 1962} 1963 1964 1965/*********************************************************************** 1966* 1967**********************************************************************/ 1968void objc_setFutureClass(Class cls, const char *name) 1969{ 1970 // fixme hack do nothing - NSCFString handled specially elsewhere 1971} 1972 1973 1974BOOL _class_isFutureClass(Class cls) 1975{ 1976 return cls && cls->isFuture(); 1977} 1978 1979 1980/*********************************************************************** 1981* _objc_flush_caches 1982* Flushes all caches. 1983* (Historical behavior: flush caches for cls, its metaclass, 1984* and subclasses thereof. Nil flushes all classes.) 1985* Locking: acquires runtimeLock 1986**********************************************************************/ 1987static void flushCaches(Class cls) 1988{ 1989 rwlock_assert_writing(&runtimeLock); 1990 1991 mutex_lock(&cacheUpdateLock); 1992 1993 if (cls) { 1994 FOREACH_REALIZED_CLASS_AND_SUBCLASS(c, cls, { 1995 cache_erase_nolock(&c->cache); 1996 }); 1997 1998 if (!cls->superclass) { 1999 // root; metaclasses are subclasses and were flushed above 2000 } else { 2001 FOREACH_REALIZED_CLASS_AND_SUBCLASS(c, cls->ISA(), { 2002 cache_erase_nolock(&c->cache); 2003 }); 2004 } 2005 } 2006 else { 2007 Class c; 2008 NXHashTable *classes = realizedClasses(); 2009 NXHashState state = NXInitHashState(classes); 2010 while (NXNextHashState(classes, &state, (void **)&c)) { 2011 cache_erase_nolock(&c->cache); 2012 } 2013 classes = realizedMetaclasses(); 2014 state = NXInitHashState(classes); 2015 while (NXNextHashState(classes, &state, (void **)&c)) { 2016 cache_erase_nolock(&c->cache); 2017 } 2018 } 2019 2020 mutex_unlock(&cacheUpdateLock); 2021} 2022 2023 2024static void flushImps(Class cls, SEL sel1, IMP imp1, SEL sel2, IMP imp2) 2025{ 2026 rwlock_assert_writing(&runtimeLock); 2027 2028 mutex_lock(&cacheUpdateLock); 2029 2030 if (cls) { 2031 FOREACH_REALIZED_CLASS_AND_SUBCLASS(c, cls, { 2032 cache_eraseImp_nolock(c, sel1, imp1); 2033 if (sel2) cache_eraseImp_nolock(c, sel2, imp2); 2034 }); 2035 2036 if (!cls->superclass) { 2037 // root; metaclasses are subclasses and were flushed above 2038 } else { 2039 FOREACH_REALIZED_CLASS_AND_SUBCLASS(c, cls->ISA(), { 2040 cache_eraseImp_nolock(c, sel1, imp1); 2041 if (sel2) cache_eraseImp_nolock(c, sel2, imp2); 2042 }); 2043 } 2044 } 2045 else { 2046 Class c; 2047 NXHashTable *classes = realizedClasses(); 2048 NXHashState state = NXInitHashState(classes); 2049 while (NXNextHashState(classes, &state, (void **)&c)) { 2050 cache_eraseImp_nolock(c, sel1, imp1); 2051 if (sel2) cache_eraseImp_nolock(c, sel2, imp2); 2052 } 2053 classes = realizedMetaclasses(); 2054 state = NXInitHashState(classes); 2055 while (NXNextHashState(classes, &state, (void **)&c)) { 2056 cache_eraseImp_nolock(c, sel1, imp1); 2057 if (sel2) cache_eraseImp_nolock(c, sel2, imp2); 2058 } 2059 } 2060 2061 mutex_unlock(&cacheUpdateLock); 2062} 2063 2064 2065void _objc_flush_caches(Class cls) 2066{ 2067 rwlock_write(&runtimeLock); 2068 flushCaches(cls); 2069 rwlock_unlock_write(&runtimeLock); 2070 2071 if (!cls) { 2072 // collectALot if cls==nil 2073 mutex_lock(&cacheUpdateLock); 2074 cache_collect(true); 2075 mutex_unlock(&cacheUpdateLock); 2076 } 2077} 2078 2079 2080/*********************************************************************** 2081* map_images 2082* Process the given images which are being mapped in by dyld. 2083* Calls ABI-agnostic code after taking ABI-specific locks. 2084* 2085* Locking: write-locks runtimeLock 2086**********************************************************************/ 2087const char * 2088map_images(enum dyld_image_states state, uint32_t infoCount, 2089 const struct dyld_image_info infoList[]) 2090{ 2091 const char *err; 2092 2093 rwlock_write(&runtimeLock); 2094 err = map_images_nolock(state, infoCount, infoList); 2095 rwlock_unlock_write(&runtimeLock); 2096 return err; 2097} 2098 2099 2100/*********************************************************************** 2101* load_images 2102* Process +load in the given images which are being mapped in by dyld. 2103* Calls ABI-agnostic code after taking ABI-specific locks. 2104* 2105* Locking: write-locks runtimeLock and loadMethodLock 2106**********************************************************************/ 2107const char * 2108load_images(enum dyld_image_states state, uint32_t infoCount, 2109 const struct dyld_image_info infoList[]) 2110{ 2111 BOOL found; 2112 2113 recursive_mutex_lock(&loadMethodLock); 2114 2115 // Discover load methods 2116 rwlock_write(&runtimeLock); 2117 found = load_images_nolock(state, infoCount, infoList); 2118 rwlock_unlock_write(&runtimeLock); 2119 2120 // Call +load methods (without runtimeLock - re-entrant) 2121 if (found) { 2122 call_load_methods(); 2123 } 2124 2125 recursive_mutex_unlock(&loadMethodLock); 2126 2127 return nil; 2128} 2129 2130 2131/*********************************************************************** 2132* unmap_image 2133* Process the given image which is about to be unmapped by dyld. 2134* mh is mach_header instead of headerType because that's what 2135* dyld_priv.h says even for 64-bit. 2136* 2137* Locking: write-locks runtimeLock and loadMethodLock 2138**********************************************************************/ 2139void 2140unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide) 2141{ 2142 recursive_mutex_lock(&loadMethodLock); 2143 rwlock_write(&runtimeLock); 2144 2145 unmap_image_nolock(mh); 2146 2147 rwlock_unlock_write(&runtimeLock); 2148 recursive_mutex_unlock(&loadMethodLock); 2149} 2150 2151 2152 2153/*********************************************************************** 2154* _read_images 2155* Perform initial processing of the headers in the linked 2156* list beginning with headerList. 2157* 2158* Called by: map_images_nolock 2159* 2160* Locking: runtimeLock acquired by map_images 2161**********************************************************************/ 2162void _read_images(header_info **hList, uint32_t hCount) 2163{ 2164 header_info *hi; 2165 uint32_t hIndex; 2166 size_t count; 2167 size_t i; 2168 Class *resolvedFutureClasses = nil; 2169 size_t resolvedFutureClassCount = 0; 2170 static unsigned int totalMethodLists; 2171 static unsigned int preoptimizedMethodLists; 2172 static unsigned int totalClasses; 2173 static unsigned int preoptimizedClasses; 2174 static BOOL doneOnce; 2175 2176 rwlock_assert_writing(&runtimeLock); 2177 2178#define EACH_HEADER \ 2179 hIndex = 0; \ 2180 crashlog_header_name(nil) && hIndex < hCount && (hi = hList[hIndex]) && crashlog_header_name(hi); \ 2181 hIndex++ 2182 2183 if (!doneOnce) { 2184 doneOnce = YES; 2185 2186 if (DisableTaggedPointers) { 2187 disableTaggedPointers(); 2188 } 2189 2190 // Count classes. Size various table based on the total. 2191 int total = 0; 2192 int unoptimizedTotal = 0; 2193 for (EACH_HEADER) { 2194 if (_getObjc2ClassList(hi, &count)) { 2195 total += (int)count; 2196 if (!hi->inSharedCache) unoptimizedTotal += count; 2197 } 2198 } 2199 2200 if (PrintConnecting) { 2201 _objc_inform("CLASS: found %d classes during launch", total); 2202 } 2203 2204 // namedClasses (NOT realizedClasses) 2205 // Preoptimized classes don't go in this table. 2206 // 4/3 is NXMapTable's load factor 2207 int namedClassesSize = 2208 (isPreoptimized() ? unoptimizedTotal : total) * 4 / 3; 2209 gdb_objc_realized_classes = 2210 NXCreateMapTableFromZone(NXStrValueMapPrototype, namedClassesSize, 2211 _objc_internal_zone()); 2212 2213 // realizedClasses and realizedMetaclasses - less than the full total 2214 realized_class_hash = 2215 NXCreateHashTableFromZone(NXPtrPrototype, total / 8, nil, 2216 _objc_internal_zone()); 2217 realized_metaclass_hash = 2218 NXCreateHashTableFromZone(NXPtrPrototype, total / 8, nil, 2219 _objc_internal_zone()); 2220 } 2221 2222 2223 // Discover classes. Fix up unresolved future classes. Mark bundle classes. 2224 NXMapTable *future_named_class_map = futureNamedClasses(); 2225 2226 for (EACH_HEADER) { 2227 bool headerIsBundle = (hi->mhdr->filetype == MH_BUNDLE); 2228 bool headerInSharedCache = hi->inSharedCache; 2229 2230 classref_t *classlist = _getObjc2ClassList(hi, &count); 2231 for (i = 0; i < count; i++) { 2232 Class cls = (Class)classlist[i]; 2233 const char *name = cls->name(); 2234 2235 if (missingWeakSuperclass(cls)) { 2236 // No superclass (probably weak-linked). 2237 // Disavow any knowledge of this subclass. 2238 if (PrintConnecting) { 2239 _objc_inform("CLASS: IGNORING class '%s' with " 2240 "missing weak-linked superclass", name); 2241 } 2242 addRemappedClass(cls, nil); 2243 cls->superclass = nil; 2244 continue; 2245 } 2246 2247 // Note: Class __ARCLite__'s hack does not go through here. 2248 // Class structure fixups that apply to it also need to be 2249 // performed in non-lazy realization below. 2250 2251 Class newCls = nil; 2252 if (NXCountMapTable(future_named_class_map) > 0) { 2253 newCls = (Class)NXMapGet(future_named_class_map, name); 2254 removeFutureNamedClass(name); 2255 } 2256 2257 if (newCls) { 2258 // Copy objc_class to future class's struct. 2259 // Preserve future's rw data block. 2260 class_rw_t *rw = newCls->data(); 2261 const class_ro_t *old_ro = rw->ro; 2262 memcpy(newCls, cls, sizeof(objc_class)); 2263 rw->ro = (class_ro_t *)newCls->data(); 2264 newCls->setData(rw); 2265 _free_internal((void *)old_ro->name); 2266 _free_internal((void *)old_ro); 2267 2268 addRemappedClass(cls, newCls); 2269 cls = newCls; 2270 2271 // Non-lazily realize the class below. 2272 resolvedFutureClasses = (Class *) 2273 _realloc_internal(resolvedFutureClasses, 2274 (resolvedFutureClassCount+1) 2275 * sizeof(Class)); 2276 resolvedFutureClasses[resolvedFutureClassCount++] = newCls; 2277 } 2278 2279 totalClasses++; 2280 if (headerInSharedCache && isPreoptimized()) { 2281 // class list built in shared cache 2282 // fixme strict assert doesn't work because of duplicates 2283 // assert(cls == getClass(name)); 2284 assert(getClass(name)); 2285 preoptimizedClasses++; 2286 } else { 2287 addNamedClass(cls, name); 2288 } 2289 2290 // for future reference: shared cache never contains MH_BUNDLEs 2291 if (headerIsBundle) { 2292 cls->data()->flags |= RO_FROM_BUNDLE; 2293 cls->ISA()->data()->flags |= RO_FROM_BUNDLE; 2294 } 2295 2296 if (PrintPreopt) { 2297 const method_list_t *mlist; 2298 if ((mlist = ((class_ro_t *)cls->data())->baseMethods)) { 2299 totalMethodLists++; 2300 if (isMethodListFixedUp(mlist)) preoptimizedMethodLists++; 2301 } 2302 if ((mlist = ((class_ro_t *)cls->ISA()->data())->baseMethods)) { 2303 totalMethodLists++; 2304 if (isMethodListFixedUp(mlist)) preoptimizedMethodLists++; 2305 } 2306 } 2307 } 2308 } 2309 2310 if (PrintPreopt && totalMethodLists) { 2311 _objc_inform("PREOPTIMIZATION: %u/%u (%.3g%%) method lists pre-sorted", 2312 preoptimizedMethodLists, totalMethodLists, 2313 100.0*preoptimizedMethodLists/totalMethodLists); 2314 } 2315 if (PrintPreopt && totalClasses) { 2316 _objc_inform("PREOPTIMIZATION: %u/%u (%.3g%%) classes pre-registered", 2317 preoptimizedClasses, totalClasses, 2318 100.0*preoptimizedClasses/totalClasses); 2319 } 2320 2321 // Fix up remapped classes 2322 // Class list and nonlazy class list remain unremapped. 2323 // Class refs and super refs are remapped for message dispatching. 2324 2325 if (!noClassesRemapped()) { 2326 for (EACH_HEADER) { 2327 Class *classrefs = _getObjc2ClassRefs(hi, &count); 2328 for (i = 0; i < count; i++) { 2329 remapClassRef(&classrefs[i]); 2330 } 2331 // fixme why doesn't test future1 catch the absence of this? 2332 classrefs = _getObjc2SuperRefs(hi, &count); 2333 for (i = 0; i < count; i++) { 2334 remapClassRef(&classrefs[i]); 2335 } 2336 } 2337 } 2338 2339 2340 // Fix up @selector references 2341 sel_lock(); 2342 for (EACH_HEADER) { 2343 if (PrintPreopt) { 2344 if (sel_preoptimizationValid(hi)) { 2345 _objc_inform("PREOPTIMIZATION: honoring preoptimized selectors in %s", 2346 hi->fname); 2347 } 2348 else if (_objcHeaderOptimizedByDyld(hi)) { 2349 _objc_inform("PREOPTIMIZATION: IGNORING preoptimized selectors in %s", 2350 hi->fname); 2351 } 2352 } 2353 2354 if (sel_preoptimizationValid(hi)) continue; 2355 2356 SEL *sels = _getObjc2SelectorRefs(hi, &count); 2357 BOOL isBundle = hi->mhdr->filetype == MH_BUNDLE; 2358 for (i = 0; i < count; i++) { 2359 sels[i] = sel_registerNameNoLock((const char *)sels[i], isBundle); 2360 } 2361 } 2362 sel_unlock(); 2363 2364#if SUPPORT_FIXUP 2365 // Fix up old objc_msgSend_fixup call sites 2366 for (EACH_HEADER) { 2367 message_ref_t *refs = _getObjc2MessageRefs(hi, &count); 2368 if (count == 0) continue; 2369 2370 if (PrintVtables) { 2371 _objc_inform("VTABLES: repairing %zu unsupported vtable dispatch " 2372 "call sites in %s", count, hi->fname); 2373 } 2374 for (i = 0; i < count; i++) { 2375 fixupMessageRef(refs+i); 2376 } 2377 } 2378#endif 2379 2380 // Discover protocols. Fix up protocol refs. 2381 NXMapTable *protocol_map = protocols(); 2382 for (EACH_HEADER) { 2383 extern objc_class OBJC_CLASS_$_Protocol; 2384 Class cls = (Class)&OBJC_CLASS_$_Protocol; 2385 assert(cls); 2386 protocol_t **protocols = _getObjc2ProtocolList(hi, &count); 2387 // fixme duplicate protocol from bundle 2388 for (i = 0; i < count; i++) { 2389 if (!NXMapGet(protocol_map, protocols[i]->name)) { 2390 protocols[i]->initIsa(cls); 2391 NXMapKeyCopyingInsert(protocol_map, 2392 protocols[i]->name, protocols[i]); 2393 if (PrintProtocols) { 2394 _objc_inform("PROTOCOLS: protocol at %p is %s", 2395 protocols[i], protocols[i]->name); 2396 } 2397 } else { 2398 if (PrintProtocols) { 2399 _objc_inform("PROTOCOLS: protocol at %p is %s (duplicate)", 2400 protocols[i], protocols[i]->name); 2401 } 2402 } 2403 } 2404 } 2405 for (EACH_HEADER) { 2406 protocol_t **protocols; 2407 protocols = _getObjc2ProtocolRefs(hi, &count); 2408 for (i = 0; i < count; i++) { 2409 remapProtocolRef(&protocols[i]); 2410 } 2411 } 2412 2413 // Realize non-lazy classes (for +load methods and static instances) 2414 for (EACH_HEADER) { 2415 classref_t *classlist = 2416 _getObjc2NonlazyClassList(hi, &count); 2417 for (i = 0; i < count; i++) { 2418 Class cls = remapClass(classlist[i]); 2419 if (!cls) continue; 2420 2421 realizeClass(cls); 2422 } 2423 } 2424 2425 // Realize newly-resolved future classes, in case CF manipulates them 2426 if (resolvedFutureClasses) { 2427 for (i = 0; i < resolvedFutureClassCount; i++) { 2428 realizeClass(resolvedFutureClasses[i]); 2429 } 2430 _free_internal(resolvedFutureClasses); 2431 } 2432 2433 // Discover categories. 2434 for (EACH_HEADER) { 2435 category_t **catlist = 2436 _getObjc2CategoryList(hi, &count); 2437 for (i = 0; i < count; i++) { 2438 category_t *cat = catlist[i]; 2439 Class cls = remapClass(cat->cls); 2440 2441 if (!cls) { 2442 // Category's target class is missing (probably weak-linked). 2443 // Disavow any knowledge of this category. 2444 catlist[i] = nil; 2445 if (PrintConnecting) { 2446 _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with " 2447 "missing weak-linked target class", 2448 cat->name, cat); 2449 } 2450 continue; 2451 } 2452 2453 // Process this category. 2454 // First, register the category with its target class. 2455 // Then, rebuild the class's method lists (etc) if 2456 // the class is realized. 2457 BOOL classExists = NO; 2458 if (cat->instanceMethods || cat->protocols 2459 || cat->instanceProperties) 2460 { 2461 addUnattachedCategoryForClass(cat, cls, hi); 2462 if (cls->isRealized()) { 2463 remethodizeClass(cls); 2464 classExists = YES; 2465 } 2466 if (PrintConnecting) { 2467 _objc_inform("CLASS: found category -%s(%s) %s", 2468 cls->name(), cat->name, 2469 classExists ? "on existing class" : ""); 2470 } 2471 } 2472 2473 if (cat->classMethods || cat->protocols 2474 /* || cat->classProperties */) 2475 { 2476 addUnattachedCategoryForClass(cat, cls->ISA(), hi); 2477 if (cls->ISA()->isRealized()) { 2478 remethodizeClass(cls->ISA()); 2479 } 2480 if (PrintConnecting) { 2481 _objc_inform("CLASS: found category +%s(%s)", 2482 cls->name(), cat->name); 2483 } 2484 } 2485 } 2486 } 2487 2488 // Category discovery MUST BE LAST to avoid potential races 2489 // when other threads call the new category code before 2490 // this thread finishes its fixups. 2491 2492 // +load handled by prepare_load_methods() 2493 2494 if (DebugNonFragileIvars) { 2495 realizeAllClasses(); 2496 } 2497 2498#undef EACH_HEADER 2499} 2500 2501 2502/*********************************************************************** 2503* prepare_load_methods 2504* Schedule +load for classes in this image, any un-+load-ed 2505* superclasses in other images, and any categories in this image. 2506**********************************************************************/ 2507// Recursively schedule +load for cls and any un-+load-ed superclasses. 2508// cls must already be connected. 2509static void schedule_class_load(Class cls) 2510{ 2511 if (!cls) return; 2512 assert(cls->isRealized()); // _read_images should realize 2513 2514 if (cls->data()->flags & RW_LOADED) return; 2515 2516 // Ensure superclass-first ordering 2517 schedule_class_load(cls->superclass); 2518 2519 add_class_to_loadable_list(cls); 2520 cls->setInfo(RW_LOADED); 2521} 2522 2523void prepare_load_methods(header_info *hi) 2524{ 2525 size_t count, i; 2526 2527 rwlock_assert_writing(&runtimeLock); 2528 2529 classref_t *classlist = 2530 _getObjc2NonlazyClassList(hi, &count); 2531 for (i = 0; i < count; i++) { 2532 schedule_class_load(remapClass(classlist[i])); 2533 } 2534 2535 category_t **categorylist = _getObjc2NonlazyCategoryList(hi, &count); 2536 for (i = 0; i < count; i++) { 2537 category_t *cat = categorylist[i]; 2538 Class cls = remapClass(cat->cls); 2539 if (!cls) continue; // category for ignored weak-linked class 2540 realizeClass(cls); 2541 assert(cls->ISA()->isRealized()); 2542 add_category_to_loadable_list(cat); 2543 } 2544} 2545 2546 2547/*********************************************************************** 2548* _unload_image 2549* Only handles MH_BUNDLE for now. 2550* Locking: write-lock and loadMethodLock acquired by unmap_image 2551**********************************************************************/ 2552void _unload_image(header_info *hi) 2553{ 2554 size_t count, i; 2555 2556 recursive_mutex_assert_locked(&loadMethodLock); 2557 rwlock_assert_writing(&runtimeLock); 2558 2559 // Unload unattached categories and categories waiting for +load. 2560 2561 category_t **catlist = _getObjc2CategoryList(hi, &count); 2562 for (i = 0; i < count; i++) { 2563 category_t *cat = catlist[i]; 2564 if (!cat) continue; // category for ignored weak-linked class 2565 Class cls = remapClass(cat->cls); 2566 assert(cls); // shouldn't have live category for dead class 2567 2568 // fixme for MH_DYLIB cat's class may have been unloaded already 2569 2570 // unattached list 2571 removeUnattachedCategoryForClass(cat, cls); 2572 2573 // +load queue 2574 remove_category_from_loadable_list(cat); 2575 } 2576 2577 // Unload classes. 2578 2579 classref_t *classlist = _getObjc2ClassList(hi, &count); 2580 2581 // First detach classes from each other. Then free each class. 2582 // This avoid bugs where this loop unloads a subclass before its superclass 2583 2584 for (i = 0; i < count; i++) { 2585 Class cls = remapClass(classlist[i]); 2586 if (cls) { 2587 remove_class_from_loadable_list(cls); 2588 detach_class(cls->ISA(), YES); 2589 detach_class(cls, NO); 2590 } 2591 } 2592 2593 for (i = 0; i < count; i++) { 2594 Class cls = remapClass(classlist[i]); 2595 if (cls) { 2596 free_class(cls->ISA()); 2597 free_class(cls); 2598 } 2599 } 2600 2601 // XXX FIXME -- Clean up protocols: 2602 // <rdar://problem/9033191> Support unloading protocols at dylib/image unload time 2603 2604 // fixme DebugUnload 2605} 2606 2607 2608/*********************************************************************** 2609* method_getDescription 2610* Returns a pointer to this method's objc_method_description. 2611* Locking: none 2612**********************************************************************/ 2613struct objc_method_description * 2614method_getDescription(Method m) 2615{ 2616 if (!m) return nil; 2617 return (struct objc_method_description *)m; 2618} 2619 2620 2621/*********************************************************************** 2622* method_getImplementation 2623* Returns this method's IMP. 2624* Locking: none 2625**********************************************************************/ 2626static IMP 2627_method_getImplementation(method_t *m) 2628{ 2629 if (!m) return nil; 2630 return m->imp; 2631} 2632 2633IMP 2634method_getImplementation(Method m) 2635{ 2636 return _method_getImplementation(m); 2637} 2638 2639 2640/*********************************************************************** 2641* method_getName 2642* Returns this method's selector. 2643* The method must not be nil. 2644* The method must already have been fixed-up. 2645* Locking: none 2646**********************************************************************/ 2647SEL 2648method_getName(Method m) 2649{ 2650 if (!m) return nil; 2651 2652 assert(m->name == sel_registerName(sel_getName(m->name))); 2653 return m->name; 2654} 2655 2656 2657/*********************************************************************** 2658* method_getTypeEncoding 2659* Returns this method's old-style type encoding string. 2660* The method must not be nil. 2661* Locking: none 2662**********************************************************************/ 2663const char * 2664method_getTypeEncoding(Method m) 2665{ 2666 if (!m) return nil; 2667 return m->types; 2668} 2669 2670 2671/*********************************************************************** 2672* method_setImplementation 2673* Sets this method's implementation to imp. 2674* The previous implementation is returned. 2675**********************************************************************/ 2676static IMP 2677_method_setImplementation(Class cls, method_t *m, IMP imp) 2678{ 2679 rwlock_assert_writing(&runtimeLock); 2680 2681 if (!m) return nil; 2682 if (!imp) return nil; 2683 2684 if (ignoreSelector(m->name)) { 2685 // Ignored methods stay ignored 2686 return m->imp; 2687 } 2688 2689 IMP old = _method_getImplementation(m); 2690 m->imp = imp; 2691 2692 // Class-side cache updates are slow if cls is nil (i.e. unknown) 2693 // RR/AWZ updates are slow if cls is nil (i.e. unknown) 2694 // fixme build list of classes whose Methods are known externally? 2695 2696 // Scrub the old IMP from the cache. 2697 // Can't simply overwrite the new IMP because the cached value could be 2698 // the same IMP from a different Method. 2699 flushImps(cls, m->name, old, nil, nil); 2700 2701 // Catch changes to retain/release and allocWithZone implementations 2702 updateCustomRR_AWZ(cls, m); 2703 2704 return old; 2705} 2706 2707IMP 2708method_setImplementation(Method m, IMP imp) 2709{ 2710 // Don't know the class - will be slow if RR/AWZ are affected 2711 // fixme build list of classes whose Methods are known externally? 2712 IMP result; 2713 rwlock_write(&runtimeLock); 2714 result = _method_setImplementation(Nil, m, imp); 2715 rwlock_unlock_write(&runtimeLock); 2716 return result; 2717} 2718 2719 2720void method_exchangeImplementations(Method m1, Method m2) 2721{ 2722 if (!m1 || !m2) return; 2723 2724 rwlock_write(&runtimeLock); 2725 2726 if (ignoreSelector(m1->name) || ignoreSelector(m2->name)) { 2727 // Ignored methods stay ignored. Now they're both ignored. 2728 m1->imp = (IMP)&_objc_ignored_method; 2729 m2->imp = (IMP)&_objc_ignored_method; 2730 rwlock_unlock_write(&runtimeLock); 2731 return; 2732 } 2733 2734 IMP m1_imp = m1->imp; 2735 m1->imp = m2->imp; 2736 m2->imp = m1_imp; 2737 2738 2739 // RR/AWZ updates are slow because class is unknown 2740 // Class-side cache updates are slow because class is unknown 2741 // fixme build list of classes whose Methods are known externally? 2742 2743 // Scrub the old IMPs from the caches. 2744 // Can't simply overwrite the new IMP because the cached value could be 2745 // the same IMP from a different Method. 2746 flushImps(nil, m1->name,m2->imp, m2->name,m1->imp); 2747 2748 updateCustomRR_AWZ(nil, m1); 2749 updateCustomRR_AWZ(nil, m2); 2750 2751 rwlock_unlock_write(&runtimeLock); 2752} 2753 2754 2755/*********************************************************************** 2756* ivar_getOffset 2757* fixme 2758* Locking: none 2759**********************************************************************/ 2760ptrdiff_t 2761ivar_getOffset(Ivar ivar) 2762{ 2763 if (!ivar) return 0; 2764 return *ivar->offset; 2765} 2766 2767 2768/*********************************************************************** 2769* ivar_getName 2770* fixme 2771* Locking: none 2772**********************************************************************/ 2773const char * 2774ivar_getName(Ivar ivar) 2775{ 2776 if (!ivar) return nil; 2777 return ivar->name; 2778} 2779 2780 2781/*********************************************************************** 2782* ivar_getTypeEncoding 2783* fixme 2784* Locking: none 2785**********************************************************************/ 2786const char * 2787ivar_getTypeEncoding(Ivar ivar) 2788{ 2789 if (!ivar) return nil; 2790 return ivar->type; 2791} 2792 2793 2794 2795const char *property_getName(objc_property_t prop) 2796{ 2797 return prop->name; 2798} 2799 2800const char *property_getAttributes(objc_property_t prop) 2801{ 2802 return prop->attributes; 2803} 2804 2805objc_property_attribute_t *property_copyAttributeList(objc_property_t prop, 2806 unsigned int *outCount) 2807{ 2808 if (!prop) { 2809 if (outCount) *outCount = 0; 2810 return nil; 2811 } 2812 2813 objc_property_attribute_t *result; 2814 rwlock_read(&runtimeLock); 2815 result = copyPropertyAttributeList(prop->attributes,outCount); 2816 rwlock_unlock_read(&runtimeLock); 2817 return result; 2818} 2819 2820char * property_copyAttributeValue(objc_property_t prop, const char *name) 2821{ 2822 if (!prop || !name || *name == '\0') return nil; 2823 2824 char *result; 2825 rwlock_read(&runtimeLock); 2826 result = copyPropertyAttributeValue(prop->attributes, name); 2827 rwlock_unlock_read(&runtimeLock); 2828 return result; 2829} 2830 2831 2832/*********************************************************************** 2833* getExtendedTypesIndexesForMethod 2834* Returns: 2835* a is the count of methods in all method lists before m's method list 2836* b is the index of m in m's method list 2837* a+b is the index of m's extended types in the extended types array 2838**********************************************************************/ 2839static void getExtendedTypesIndexesForMethod(protocol_t *proto, const method_t *m, bool isRequiredMethod, bool isInstanceMethod, uint32_t& a, uint32_t &b) 2840{ 2841 a = 0; 2842 2843 if (isRequiredMethod && isInstanceMethod) { 2844 b = method_list_index(proto->instanceMethods, m); 2845 return; 2846 } 2847 a += method_list_count(proto->instanceMethods); 2848 2849 if (isRequiredMethod && !isInstanceMethod) { 2850 b = method_list_index(proto->classMethods, m); 2851 return; 2852 } 2853 a += method_list_count(proto->classMethods); 2854 2855 if (!isRequiredMethod && isInstanceMethod) { 2856 b = method_list_index(proto->optionalInstanceMethods, m); 2857 return; 2858 } 2859 a += method_list_count(proto->optionalInstanceMethods); 2860 2861 if (!isRequiredMethod && !isInstanceMethod) { 2862 b = method_list_index(proto->optionalClassMethods, m); 2863 return; 2864 } 2865 a += method_list_count(proto->optionalClassMethods); 2866} 2867 2868 2869/*********************************************************************** 2870* getExtendedTypesIndexForMethod 2871* Returns the index of m's extended types in proto's extended types array. 2872**********************************************************************/ 2873static uint32_t getExtendedTypesIndexForMethod(protocol_t *proto, const method_t *m, bool isRequiredMethod, bool isInstanceMethod) 2874{ 2875 uint32_t a; 2876 uint32_t b; 2877 getExtendedTypesIndexesForMethod(proto, m, isRequiredMethod, 2878 isInstanceMethod, a, b); 2879 return a + b; 2880} 2881 2882 2883/*********************************************************************** 2884* fixupProtocolMethodList 2885* Fixes up a single method list in a protocol. 2886**********************************************************************/ 2887static void 2888fixupProtocolMethodList(protocol_t *proto, method_list_t **mlistp, 2889 bool required, bool instance) 2890{ 2891 rwlock_assert_writing(&runtimeLock); 2892 2893 if (!*mlistp) return; 2894 if (isMethodListFixedUp(*mlistp)) return; 2895 2896 bool hasExtendedMethodTypes = proto->hasExtendedMethodTypes(); 2897 *mlistp = fixupMethodList(*mlistp, true/*always copy for simplicity*/, 2898 !hasExtendedMethodTypes/*sort if no ext*/); 2899 2900 method_list_t *mlist = *mlistp; 2901 2902 if (hasExtendedMethodTypes) { 2903 // Sort method list and extended method types together. 2904 // fixupMethodList() can't do this. 2905 // fixme COW stomp 2906 uint32_t count = method_list_count(mlist); 2907 uint32_t prefix; 2908 uint32_t junk; 2909 getExtendedTypesIndexesForMethod(proto, method_list_nth(mlist, 0), 2910 required, instance, prefix, junk); 2911 const char **types = proto->extendedMethodTypes; 2912 for (uint32_t i = 0; i < count; i++) { 2913 for (uint32_t j = i+1; j < count; j++) { 2914 method_t *mi = method_list_nth(mlist, i); 2915 method_t *mj = method_list_nth(mlist, j); 2916 if (mi->name > mj->name) { 2917 method_list_swap(mlist, i, j); 2918 std::swap(types[prefix+i], types[prefix+j]); 2919 } 2920 } 2921 } 2922 } 2923} 2924 2925 2926/*********************************************************************** 2927* fixupProtocol 2928* Fixes up all of a protocol's method lists. 2929**********************************************************************/ 2930static void 2931fixupProtocol(protocol_t *proto) 2932{ 2933 rwlock_assert_writing(&runtimeLock); 2934 2935 if (proto->protocols) { 2936 for (uintptr_t i = 0; i < proto->protocols->count; i++) { 2937 protocol_t *sub = remapProtocol(proto->protocols->list[i]); 2938 if (!sub->isFixedUp()) fixupProtocol(sub); 2939 } 2940 } 2941 2942 fixupProtocolMethodList(proto, &proto->instanceMethods, YES, YES); 2943 fixupProtocolMethodList(proto, &proto->classMethods, YES, NO); 2944 fixupProtocolMethodList(proto, &proto->optionalInstanceMethods, NO, YES); 2945 fixupProtocolMethodList(proto, &proto->optionalClassMethods, NO, NO); 2946 2947 // fixme memory barrier so we can check this with no lock 2948 proto->flags |= PROTOCOL_FIXED_UP; 2949} 2950 2951 2952/*********************************************************************** 2953* fixupProtocolIfNeeded 2954* Fixes up all of a protocol's method lists if they aren't fixed up already. 2955* Locking: write-locks runtimeLock. 2956**********************************************************************/ 2957static void 2958fixupProtocolIfNeeded(protocol_t *proto) 2959{ 2960 rwlock_assert_unlocked(&runtimeLock); 2961 assert(proto); 2962 2963 if (!proto->isFixedUp()) { 2964 rwlock_write(&runtimeLock); 2965 fixupProtocol(proto); 2966 rwlock_unlock_write(&runtimeLock); 2967 } 2968} 2969 2970 2971static method_list_t * 2972getProtocolMethodList(protocol_t *proto, bool required, bool instance) 2973{ 2974 method_list_t **mlistp = nil; 2975 if (required) { 2976 if (instance) { 2977 mlistp = &proto->instanceMethods; 2978 } else { 2979 mlistp = &proto->classMethods; 2980 } 2981 } else { 2982 if (instance) { 2983 mlistp = &proto->optionalInstanceMethods; 2984 } else { 2985 mlistp = &proto->optionalClassMethods; 2986 } 2987 } 2988 2989 return *mlistp; 2990} 2991 2992 2993/*********************************************************************** 2994* protocol_getMethod_nolock 2995* Locking: runtimeLock must be held by the caller 2996**********************************************************************/ 2997static method_t * 2998protocol_getMethod_nolock(protocol_t *proto, SEL sel, 2999 bool isRequiredMethod, bool isInstanceMethod, 3000 bool recursive) 3001{ 3002 rwlock_assert_locked(&runtimeLock); 3003 3004 if (!proto || !sel) return nil; 3005 3006 assert(proto->isFixedUp()); 3007 3008 method_list_t *mlist = 3009 getProtocolMethodList(proto, isRequiredMethod, isInstanceMethod); 3010 if (mlist) { 3011 method_t *m = search_method_list(mlist, sel); 3012 if (m) return m; 3013 } 3014 3015 if (recursive && proto->protocols) { 3016 method_t *m; 3017 for (uint32_t i = 0; i < proto->protocols->count; i++) { 3018 protocol_t *realProto = remapProtocol(proto->protocols->list[i]); 3019 m = protocol_getMethod_nolock(realProto, sel, 3020 isRequiredMethod, isInstanceMethod, 3021 true); 3022 if (m) return m; 3023 } 3024 } 3025 3026 return nil; 3027} 3028 3029 3030/*********************************************************************** 3031* protocol_getMethod 3032* fixme 3033* Locking: acquires runtimeLock 3034**********************************************************************/ 3035Method 3036protocol_getMethod(protocol_t *proto, SEL sel, bool isRequiredMethod, bool isInstanceMethod, bool recursive) 3037{ 3038 if (!proto) return nil; 3039 fixupProtocolIfNeeded(proto); 3040 3041 rwlock_read(&runtimeLock); 3042 method_t *result = protocol_getMethod_nolock(proto, sel, 3043 isRequiredMethod, 3044 isInstanceMethod, 3045 recursive); 3046 rwlock_unlock_read(&runtimeLock); 3047 return result; 3048} 3049 3050 3051/*********************************************************************** 3052* protocol_getMethodTypeEncoding_nolock 3053* Return the @encode string for the requested protocol method. 3054* Returns nil if the compiler did not emit any extended @encode data. 3055* Locking: runtimeLock must be held for writing by the caller 3056**********************************************************************/ 3057const char * 3058protocol_getMethodTypeEncoding_nolock(protocol_t *proto, SEL sel, 3059 bool isRequiredMethod, 3060 bool isInstanceMethod) 3061{ 3062 rwlock_assert_locked(&runtimeLock); 3063 3064 if (!proto) return nil; 3065 if (!proto->hasExtendedMethodTypes()) return nil; 3066 3067 assert(proto->isFixedUp()); 3068 3069 method_t *m = 3070 protocol_getMethod_nolock(proto, sel, 3071 isRequiredMethod, isInstanceMethod, false); 3072 if (m) { 3073 uint32_t i = getExtendedTypesIndexForMethod(proto, m, 3074 isRequiredMethod, 3075 isInstanceMethod); 3076 return proto->extendedMethodTypes[i]; 3077 } 3078 3079 // No method with that name. Search incorporated protocols. 3080 if (proto->protocols) { 3081 for (uintptr_t i = 0; i < proto->protocols->count; i++) { 3082 const char *enc = 3083 protocol_getMethodTypeEncoding_nolock(remapProtocol(proto->protocols->list[i]), sel, isRequiredMethod, isInstanceMethod); 3084 if (enc) return enc; 3085 } 3086 } 3087 3088 return nil; 3089} 3090 3091/*********************************************************************** 3092* _protocol_getMethodTypeEncoding 3093* Return the @encode string for the requested protocol method. 3094* Returns nil if the compiler did not emit any extended @encode data. 3095* Locking: acquires runtimeLock 3096**********************************************************************/ 3097const char * 3098_protocol_getMethodTypeEncoding(Protocol *proto_gen, SEL sel, 3099 BOOL isRequiredMethod, BOOL isInstanceMethod) 3100{ 3101 protocol_t *proto = newprotocol(proto_gen); 3102 3103 if (!proto) return nil; 3104 fixupProtocolIfNeeded(proto); 3105 3106 const char *enc; 3107 rwlock_read(&runtimeLock); 3108 enc = protocol_getMethodTypeEncoding_nolock(proto, sel, 3109 isRequiredMethod, 3110 isInstanceMethod); 3111 rwlock_unlock_read(&runtimeLock); 3112 return enc; 3113} 3114 3115/*********************************************************************** 3116* protocol_getName 3117* Returns the name of the given protocol. 3118* Locking: runtimeLock must not be held by the caller 3119**********************************************************************/ 3120const char * 3121protocol_getName(Protocol *proto) 3122{ 3123 return newprotocol(proto)->name; 3124} 3125 3126 3127/*********************************************************************** 3128* protocol_getInstanceMethodDescription 3129* Returns the description of a named instance method. 3130* Locking: runtimeLock must not be held by the caller 3131**********************************************************************/ 3132struct objc_method_description 3133protocol_getMethodDescription(Protocol *p, SEL aSel, 3134 BOOL isRequiredMethod, BOOL isInstanceMethod) 3135{ 3136 Method m = 3137 protocol_getMethod(newprotocol(p), aSel, 3138 isRequiredMethod, isInstanceMethod, true); 3139 if (m) return *method_getDescription(m); 3140 else return (struct objc_method_description){nil, nil}; 3141} 3142 3143 3144/*********************************************************************** 3145* protocol_conformsToProtocol_nolock 3146* Returns YES if self conforms to other. 3147* Locking: runtimeLock must be held by the caller. 3148**********************************************************************/ 3149static bool 3150protocol_conformsToProtocol_nolock(protocol_t *self, protocol_t *other) 3151{ 3152 rwlock_assert_locked(&runtimeLock); 3153 3154 if (!self || !other) { 3155 return NO; 3156 } 3157 3158 // protocols need not be fixed up 3159 3160 if (0 == strcmp(self->name, other->name)) { 3161 return YES; 3162 } 3163 3164 if (self->protocols) { 3165 uintptr_t i; 3166 for (i = 0; i < self->protocols->count; i++) { 3167 protocol_t *proto = remapProtocol(self->protocols->list[i]); 3168 if (0 == strcmp(other->name, proto->name)) { 3169 return YES; 3170 } 3171 if (protocol_conformsToProtocol_nolock(proto, other)) { 3172 return YES; 3173 } 3174 } 3175 } 3176 3177 return NO; 3178} 3179 3180 3181/*********************************************************************** 3182* protocol_conformsToProtocol 3183* Returns YES if self conforms to other. 3184* Locking: acquires runtimeLock 3185**********************************************************************/ 3186BOOL protocol_conformsToProtocol(Protocol *self, Protocol *other) 3187{ 3188 BOOL result; 3189 rwlock_read(&runtimeLock); 3190 result = protocol_conformsToProtocol_nolock(newprotocol(self), 3191 newprotocol(other)); 3192 rwlock_unlock_read(&runtimeLock); 3193 return result; 3194} 3195 3196 3197/*********************************************************************** 3198* protocol_isEqual 3199* Return YES if two protocols are equal (i.e. conform to each other) 3200* Locking: acquires runtimeLock 3201**********************************************************************/ 3202BOOL protocol_isEqual(Protocol *self, Protocol *other) 3203{ 3204 if (self == other) return YES; 3205 if (!self || !other) return NO; 3206 3207 if (!protocol_conformsToProtocol(self, other)) return NO; 3208 if (!protocol_conformsToProtocol(other, self)) return NO; 3209 3210 return YES; 3211} 3212 3213 3214/*********************************************************************** 3215* protocol_copyMethodDescriptionList 3216* Returns descriptions of a protocol's methods. 3217* Locking: acquires runtimeLock 3218**********************************************************************/ 3219struct objc_method_description * 3220protocol_copyMethodDescriptionList(Protocol *p, 3221 BOOL isRequiredMethod,BOOL isInstanceMethod, 3222 unsigned int *outCount) 3223{ 3224 protocol_t *proto = newprotocol(p); 3225 struct objc_method_description *result = nil; 3226 unsigned int count = 0; 3227 3228 if (!proto) { 3229 if (outCount) *outCount = 0; 3230 return nil; 3231 } 3232 3233 fixupProtocolIfNeeded(proto); 3234 3235 rwlock_read(&runtimeLock); 3236 3237 method_list_t *mlist = 3238 getProtocolMethodList(proto, isRequiredMethod, isInstanceMethod); 3239 3240 if (mlist) { 3241 unsigned int i; 3242 count = mlist->count; 3243 result = (struct objc_method_description *) 3244 calloc(count + 1, sizeof(struct objc_method_description)); 3245 for (i = 0; i < count; i++) { 3246 method_t *m = method_list_nth(mlist, i); 3247 result[i].name = m->name; 3248 result[i].types = (char *)m->types; 3249 } 3250 } 3251 3252 rwlock_unlock_read(&runtimeLock); 3253 3254 if (outCount) *outCount = count; 3255 return result; 3256} 3257 3258 3259/*********************************************************************** 3260* protocol_getProperty 3261* fixme 3262* Locking: runtimeLock must be held by the caller 3263**********************************************************************/ 3264static property_t * 3265protocol_getProperty_nolock(protocol_t *proto, const char *name, 3266 bool isRequiredProperty, bool isInstanceProperty) 3267{ 3268 rwlock_assert_locked(&runtimeLock); 3269 3270 if (!isRequiredProperty || !isInstanceProperty) { 3271 // Only required instance properties are currently supported 3272 return nil; 3273 } 3274 3275 property_list_t *plist; 3276 if ((plist = proto->instanceProperties)) { 3277 uint32_t i; 3278 for (i = 0; i < plist->count; i++) { 3279 property_t *prop = property_list_nth(plist, i); 3280 if (0 == strcmp(name, prop->name)) { 3281 return prop; 3282 } 3283 } 3284 } 3285 3286 if (proto->protocols) { 3287 uintptr_t i; 3288 for (i = 0; i < proto->protocols->count; i++) { 3289 protocol_t *p = remapProtocol(proto->protocols->list[i]); 3290 property_t *prop = 3291 protocol_getProperty_nolock(p, name, 3292 isRequiredProperty, 3293 isInstanceProperty); 3294 if (prop) return prop; 3295 } 3296 } 3297 3298 return nil; 3299} 3300 3301objc_property_t protocol_getProperty(Protocol *p, const char *name, 3302 BOOL isRequiredProperty, BOOL isInstanceProperty) 3303{ 3304 property_t *result; 3305 3306 if (!p || !name) return nil; 3307 3308 rwlock_read(&runtimeLock); 3309 result = protocol_getProperty_nolock(newprotocol(p), name, 3310 isRequiredProperty, 3311 isInstanceProperty); 3312 rwlock_unlock_read(&runtimeLock); 3313 3314 return (objc_property_t)result; 3315} 3316 3317 3318/*********************************************************************** 3319* protocol_copyPropertyList 3320* fixme 3321* Locking: acquires runtimeLock 3322**********************************************************************/ 3323static property_t ** 3324copyPropertyList(property_list_t *plist, unsigned int *outCount) 3325{ 3326 property_t **result = nil; 3327 unsigned int count = 0; 3328 3329 if (plist) { 3330 count = plist->count; 3331 } 3332 3333 if (count > 0) { 3334 unsigned int i; 3335 result = (property_t **)malloc((count+1) * sizeof(property_t *)); 3336 3337 for (i = 0; i < count; i++) { 3338 result[i] = property_list_nth(plist, i); 3339 } 3340 result[i] = nil; 3341 } 3342 3343 if (outCount) *outCount = count; 3344 return result; 3345} 3346 3347objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount) 3348{ 3349 property_t **result = nil; 3350 3351 if (!proto) { 3352 if (outCount) *outCount = 0; 3353 return nil; 3354 } 3355 3356 rwlock_read(&runtimeLock); 3357 3358 property_list_t *plist = newprotocol(proto)->instanceProperties; 3359 result = copyPropertyList(plist, outCount); 3360 3361 rwlock_unlock_read(&runtimeLock); 3362 3363 return (objc_property_t *)result; 3364} 3365 3366 3367/*********************************************************************** 3368* protocol_copyProtocolList 3369* Copies this protocol's incorporated protocols. 3370* Does not copy those protocol's incorporated protocols in turn. 3371* Locking: acquires runtimeLock 3372**********************************************************************/ 3373Protocol * __unsafe_unretained * 3374protocol_copyProtocolList(Protocol *p, unsigned int *outCount) 3375{ 3376 unsigned int count = 0; 3377 Protocol **result = nil; 3378 protocol_t *proto = newprotocol(p); 3379 3380 if (!proto) { 3381 if (outCount) *outCount = 0; 3382 return nil; 3383 } 3384 3385 rwlock_read(&runtimeLock); 3386 3387 if (proto->protocols) { 3388 count = (unsigned int)proto->protocols->count; 3389 } 3390 if (count > 0) { 3391 result = (Protocol **)malloc((count+1) * sizeof(Protocol *)); 3392 3393 unsigned int i; 3394 for (i = 0; i < count; i++) { 3395 result[i] = (Protocol *)remapProtocol(proto->protocols->list[i]); 3396 } 3397 result[i] = nil; 3398 } 3399 3400 rwlock_unlock_read(&runtimeLock); 3401 3402 if (outCount) *outCount = count; 3403 return result; 3404} 3405 3406 3407/*********************************************************************** 3408* objc_allocateProtocol 3409* Creates a new protocol. The protocol may not be used until 3410* objc_registerProtocol() is called. 3411* Returns nil if a protocol with the same name already exists. 3412* Locking: acquires runtimeLock 3413**********************************************************************/ 3414Protocol * 3415objc_allocateProtocol(const char *name) 3416{ 3417 rwlock_write(&runtimeLock); 3418 3419 if (NXMapGet(protocols(), name)) { 3420 rwlock_unlock_write(&runtimeLock); 3421 return nil; 3422 } 3423 3424 protocol_t *result = (protocol_t *)_calloc_internal(sizeof(protocol_t), 1); 3425 3426 extern objc_class OBJC_CLASS_$___IncompleteProtocol; 3427 Class cls = (Class)&OBJC_CLASS_$___IncompleteProtocol; 3428 result->initIsa(cls); 3429 result->name = _strdup_internal(name); 3430 3431 // fixme reserve name without installing 3432 3433 rwlock_unlock_write(&runtimeLock); 3434 3435 return (Protocol *)result; 3436} 3437 3438 3439/*********************************************************************** 3440* objc_registerProtocol 3441* Registers a newly-constructed protocol. The protocol is now 3442* ready for use and immutable. 3443* Locking: acquires runtimeLock 3444**********************************************************************/ 3445void objc_registerProtocol(Protocol *proto_gen) 3446{ 3447 protocol_t *proto = newprotocol(proto_gen); 3448 3449 rwlock_write(&runtimeLock); 3450 3451 extern objc_class OBJC_CLASS_$___IncompleteProtocol; 3452 Class oldcls = (Class)&OBJC_CLASS_$___IncompleteProtocol; 3453 extern objc_class OBJC_CLASS_$_Protocol; 3454 Class cls = (Class)&OBJC_CLASS_$_Protocol; 3455 3456 if (proto->ISA() == cls) { 3457 _objc_inform("objc_registerProtocol: protocol '%s' was already " 3458 "registered!", proto->name); 3459 rwlock_unlock_write(&runtimeLock); 3460 return; 3461 } 3462 if (proto->ISA() != oldcls) { 3463 _objc_inform("objc_registerProtocol: protocol '%s' was not allocated " 3464 "with objc_allocateProtocol!", proto->name); 3465 rwlock_unlock_write(&runtimeLock); 3466 return; 3467 } 3468 3469 proto->initIsa(cls); 3470 3471 NXMapKeyCopyingInsert(protocols(), proto->name, proto); 3472 3473 rwlock_unlock_write(&runtimeLock); 3474} 3475 3476 3477/*********************************************************************** 3478* protocol_addProtocol 3479* Adds an incorporated protocol to another protocol. 3480* No method enforcement is performed. 3481* `proto` must be under construction. `addition` must not. 3482* Locking: acquires runtimeLock 3483**********************************************************************/ 3484void 3485protocol_addProtocol(Protocol *proto_gen, Protocol *addition_gen) 3486{ 3487 protocol_t *proto = newprotocol(proto_gen); 3488 protocol_t *addition = newprotocol(addition_gen); 3489 3490 extern objc_class OBJC_CLASS_$___IncompleteProtocol; 3491 Class cls = (Class)&OBJC_CLASS_$___IncompleteProtocol; 3492 3493 if (!proto_gen) return; 3494 if (!addition_gen) return; 3495 3496 rwlock_write(&runtimeLock); 3497 3498 if (proto->ISA() != cls) { 3499 _objc_inform("protocol_addProtocol: modified protocol '%s' is not " 3500 "under construction!", proto->name); 3501 rwlock_unlock_write(&runtimeLock); 3502 return; 3503 } 3504 if (addition->ISA() == cls) { 3505 _objc_inform("protocol_addProtocol: added protocol '%s' is still " 3506 "under construction!", addition->name); 3507 rwlock_unlock_write(&runtimeLock); 3508 return; 3509 } 3510 3511 protocol_list_t *protolist = proto->protocols; 3512 if (!protolist) { 3513 protolist = (protocol_list_t *) 3514 _calloc_internal(1, sizeof(protocol_list_t) 3515 + sizeof(protolist->list[0])); 3516 } else { 3517 protolist = (protocol_list_t *) 3518 _realloc_internal(protolist, protocol_list_size(protolist) 3519 + sizeof(protolist->list[0])); 3520 } 3521 3522 protolist->list[protolist->count++] = (protocol_ref_t)addition; 3523 proto->protocols = protolist; 3524 3525 rwlock_unlock_write(&runtimeLock); 3526} 3527 3528 3529/*********************************************************************** 3530* protocol_addMethodDescription 3531* Adds a method to a protocol. The protocol must be under construction. 3532* Locking: acquires runtimeLock 3533**********************************************************************/ 3534static void 3535protocol_addMethod_nolock(method_list_t **list, SEL name, const char *types) 3536{ 3537 if (!*list) { 3538 *list = (method_list_t *) 3539 _calloc_internal(sizeof(method_list_t), 1); 3540 (*list)->entsize_NEVER_USE = sizeof((*list)->first); 3541 setMethodListFixedUp(*list); 3542 } else { 3543 size_t size = method_list_size(*list) + method_list_entsize(*list); 3544 *list = (method_list_t *) 3545 _realloc_internal(*list, size); 3546 } 3547 3548 method_t *meth = method_list_nth(*list, (*list)->count++); 3549 meth->name = name; 3550 meth->types = _strdup_internal(types ? types : ""); 3551 meth->imp = nil; 3552} 3553 3554void 3555protocol_addMethodDescription(Protocol *proto_gen, SEL name, const char *types, 3556 BOOL isRequiredMethod, BOOL isInstanceMethod) 3557{ 3558 protocol_t *proto = newprotocol(proto_gen); 3559 3560 extern objc_class OBJC_CLASS_$___IncompleteProtocol; 3561 Class cls = (Class)&OBJC_CLASS_$___IncompleteProtocol; 3562 3563 if (!proto_gen) return; 3564 3565 rwlock_write(&runtimeLock); 3566 3567 if (proto->ISA() != cls) { 3568 _objc_inform("protocol_addMethodDescription: protocol '%s' is not " 3569 "under construction!", proto->name); 3570 rwlock_unlock_write(&runtimeLock); 3571 return; 3572 } 3573 3574 if (isRequiredMethod && isInstanceMethod) { 3575 protocol_addMethod_nolock(&proto->instanceMethods, name, types); 3576 } else if (isRequiredMethod && !isInstanceMethod) { 3577 protocol_addMethod_nolock(&proto->classMethods, name, types); 3578 } else if (!isRequiredMethod && isInstanceMethod) { 3579 protocol_addMethod_nolock(&proto->optionalInstanceMethods, name,types); 3580 } else /* !isRequiredMethod && !isInstanceMethod) */ { 3581 protocol_addMethod_nolock(&proto->optionalClassMethods, name, types); 3582 } 3583 3584 rwlock_unlock_write(&runtimeLock); 3585} 3586 3587 3588/*********************************************************************** 3589* protocol_addProperty 3590* Adds a property to a protocol. The protocol must be under construction. 3591* Locking: acquires runtimeLock 3592**********************************************************************/ 3593static void 3594protocol_addProperty_nolock(property_list_t **plist, const char *name, 3595 const objc_property_attribute_t *attrs, 3596 unsigned int count) 3597{ 3598 if (!*plist) { 3599 *plist = (property_list_t *) 3600 _calloc_internal(sizeof(property_list_t), 1); 3601 (*plist)->entsize = sizeof(property_t); 3602 } else { 3603 *plist = (property_list_t *) 3604 _realloc_internal(*plist, sizeof(property_list_t) 3605 + (*plist)->count * (*plist)->entsize); 3606 } 3607 3608 property_t *prop = property_list_nth(*plist, (*plist)->count++); 3609 prop->name = _strdup_internal(name); 3610 prop->attributes = copyPropertyAttributeString(attrs, count); 3611} 3612 3613void 3614protocol_addProperty(Protocol *proto_gen, const char *name, 3615 const objc_property_attribute_t *attrs, 3616 unsigned int count, 3617 BOOL isRequiredProperty, BOOL isInstanceProperty) 3618{ 3619 protocol_t *proto = newprotocol(proto_gen); 3620 3621 extern objc_class OBJC_CLASS_$___IncompleteProtocol; 3622 Class cls = (Class)&OBJC_CLASS_$___IncompleteProtocol; 3623 3624 if (!proto) return; 3625 if (!name) return; 3626 3627 rwlock_write(&runtimeLock); 3628 3629 if (proto->ISA() != cls) { 3630 _objc_inform("protocol_addProperty: protocol '%s' is not " 3631 "under construction!", proto->name); 3632 rwlock_unlock_write(&runtimeLock); 3633 return; 3634 } 3635 3636 if (isRequiredProperty && isInstanceProperty) { 3637 protocol_addProperty_nolock(&proto->instanceProperties, name, attrs, count); 3638 } 3639 //else if (isRequiredProperty && !isInstanceProperty) { 3640 // protocol_addProperty_nolock(&proto->classProperties, name, attrs, count); 3641 //} else if (!isRequiredProperty && isInstanceProperty) { 3642 // protocol_addProperty_nolock(&proto->optionalInstanceProperties, name, attrs, count); 3643 //} else /* !isRequiredProperty && !isInstanceProperty) */ { 3644 // protocol_addProperty_nolock(&proto->optionalClassProperties, name, attrs, count); 3645 //} 3646 3647 rwlock_unlock_write(&runtimeLock); 3648} 3649 3650 3651/*********************************************************************** 3652* objc_getClassList 3653* Returns pointers to all classes. 3654* This requires all classes be realized, which is regretfully non-lazy. 3655* Locking: acquires runtimeLock 3656**********************************************************************/ 3657int 3658objc_getClassList(Class *buffer, int bufferLen) 3659{ 3660 rwlock_write(&runtimeLock); 3661 3662 realizeAllClasses(); 3663 3664 int count; 3665 Class cls; 3666 NXHashState state; 3667 NXHashTable *classes = realizedClasses(); 3668 int allCount = NXCountHashTable(classes); 3669 3670 if (!buffer) { 3671 rwlock_unlock_write(&runtimeLock); 3672 return allCount; 3673 } 3674 3675 count = 0; 3676 state = NXInitHashState(classes); 3677 while (count < bufferLen && 3678 NXNextHashState(classes, &state, (void **)&cls)) 3679 { 3680 buffer[count++] = cls; 3681 } 3682 3683 rwlock_unlock_write(&runtimeLock); 3684 3685 return allCount; 3686} 3687 3688 3689/*********************************************************************** 3690* objc_copyClassList 3691* Returns pointers to all classes. 3692* This requires all classes be realized, which is regretfully non-lazy. 3693* 3694* outCount may be nil. *outCount is the number of classes returned. 3695* If the returned array is not nil, it is nil-terminated and must be 3696* freed with free(). 3697* Locking: write-locks runtimeLock 3698**********************************************************************/ 3699Class * 3700objc_copyClassList(unsigned int *outCount) 3701{ 3702 rwlock_write(&runtimeLock); 3703 3704 realizeAllClasses(); 3705 3706 Class *result = nil; 3707 NXHashTable *classes = realizedClasses(); 3708 unsigned int count = NXCountHashTable(classes); 3709 3710 if (count > 0) { 3711 Class cls; 3712 NXHashState state = NXInitHashState(classes); 3713 result = (Class *)malloc((1+count) * sizeof(Class)); 3714 count = 0; 3715 while (NXNextHashState(classes, &state, (void **)&cls)) { 3716 result[count++] = cls; 3717 } 3718 result[count] = nil; 3719 } 3720 3721 rwlock_unlock_write(&runtimeLock); 3722 3723 if (outCount) *outCount = count; 3724 return result; 3725} 3726 3727 3728/*********************************************************************** 3729* objc_copyProtocolList 3730* Returns pointers to all protocols. 3731* Locking: read-locks runtimeLock 3732**********************************************************************/ 3733Protocol * __unsafe_unretained * 3734objc_copyProtocolList(unsigned int *outCount) 3735{ 3736 rwlock_read(&runtimeLock); 3737 3738 unsigned int count, i; 3739 Protocol *proto; 3740 const char *name; 3741 NXMapState state; 3742 NXMapTable *protocol_map = protocols(); 3743 Protocol **result; 3744 3745 count = NXCountMapTable(protocol_map); 3746 if (count == 0) { 3747 rwlock_unlock_read(&runtimeLock); 3748 if (outCount) *outCount = 0; 3749 return nil; 3750 } 3751 3752 result = (Protocol **)calloc(1 + count, sizeof(Protocol *)); 3753 3754 i = 0; 3755 state = NXInitMapState(protocol_map); 3756 while (NXNextMapState(protocol_map, &state, 3757 (const void **)&name, (const void **)&proto)) 3758 { 3759 result[i++] = proto; 3760 } 3761 3762 result[i++] = nil; 3763 assert(i == count+1); 3764 3765 rwlock_unlock_read(&runtimeLock); 3766 3767 if (outCount) *outCount = count; 3768 return result; 3769} 3770 3771 3772/*********************************************************************** 3773* objc_getProtocol 3774* Get a protocol by name, or return nil 3775* Locking: read-locks runtimeLock 3776**********************************************************************/ 3777Protocol *objc_getProtocol(const char *name) 3778{ 3779 rwlock_read(&runtimeLock); 3780 Protocol *result = (Protocol *)NXMapGet(protocols(), name); 3781 rwlock_unlock_read(&runtimeLock); 3782 return result; 3783} 3784 3785 3786/*********************************************************************** 3787* class_copyMethodList 3788* fixme 3789* Locking: read-locks runtimeLock 3790**********************************************************************/ 3791Method * 3792class_copyMethodList(Class cls, unsigned int *outCount) 3793{ 3794 unsigned int count = 0; 3795 Method *result = nil; 3796 3797 if (!cls) { 3798 if (outCount) *outCount = 0; 3799 return nil; 3800 } 3801 3802 rwlock_read(&runtimeLock); 3803 3804 assert(cls->isRealized()); 3805 3806 FOREACH_METHOD_LIST(mlist, cls, { 3807 count += mlist->count; 3808 }); 3809 3810 if (count > 0) { 3811 unsigned int m; 3812 result = (Method *)malloc((count + 1) * sizeof(Method)); 3813 3814 m = 0; 3815 FOREACH_METHOD_LIST(mlist, cls, { 3816 unsigned int i; 3817 for (i = 0; i < mlist->count; i++) { 3818 method_t *aMethod = method_list_nth(mlist, i); 3819 if (ignoreSelector(method_getName(aMethod))) { 3820 count--; 3821 continue; 3822 } 3823 result[m++] = aMethod; 3824 } 3825 }); 3826 result[m] = nil; 3827 } 3828 3829 rwlock_unlock_read(&runtimeLock); 3830 3831 if (outCount) *outCount = count; 3832 return result; 3833} 3834 3835 3836/*********************************************************************** 3837* class_copyIvarList 3838* fixme 3839* Locking: read-locks runtimeLock 3840**********************************************************************/ 3841Ivar * 3842class_copyIvarList(Class cls, unsigned int *outCount) 3843{ 3844 const ivar_list_t *ivars; 3845 Ivar *result = nil; 3846 unsigned int count = 0; 3847 unsigned int i; 3848 3849 if (!cls) { 3850 if (outCount) *outCount = 0; 3851 return nil; 3852 } 3853 3854 rwlock_read(&runtimeLock); 3855 3856 assert(cls->isRealized()); 3857 3858 if ((ivars = cls->data()->ro->ivars) && ivars->count) { 3859 result = (Ivar *)malloc((ivars->count+1) * sizeof(Ivar)); 3860 3861 for (i = 0; i < ivars->count; i++) { 3862 ivar_t *ivar = ivar_list_nth(ivars, i); 3863 if (!ivar->offset) continue; // anonymous bitfield 3864 result[count++] = ivar; 3865 } 3866 result[count] = nil; 3867 } 3868 3869 rwlock_unlock_read(&runtimeLock); 3870 3871 if (outCount) *outCount = count; 3872 return result; 3873} 3874 3875 3876/*********************************************************************** 3877* class_copyPropertyList. Returns a heap block containing the 3878* properties declared in the class, or nil if the class 3879* declares no properties. Caller must free the block. 3880* Does not copy any superclass's properties. 3881* Locking: read-locks runtimeLock 3882**********************************************************************/ 3883objc_property_t * 3884class_copyPropertyList(Class cls, unsigned int *outCount) 3885{ 3886 chained_property_list *plist; 3887 unsigned int count = 0; 3888 property_t **result = nil; 3889 3890 if (!cls) { 3891 if (outCount) *outCount = 0; 3892 return nil; 3893 } 3894 3895 rwlock_read(&runtimeLock); 3896 3897 assert(cls->isRealized()); 3898 3899 for (plist = cls->data()->properties; plist; plist = plist->next) { 3900 count += plist->count; 3901 } 3902 3903 if (count > 0) { 3904 unsigned int p; 3905 result = (property_t **)malloc((count + 1) * sizeof(property_t *)); 3906 3907 p = 0; 3908 for (plist = cls->data()->properties; plist; plist = plist->next) { 3909 unsigned int i; 3910 for (i = 0; i < plist->count; i++) { 3911 result[p++] = &plist->list[i]; 3912 } 3913 } 3914 result[p] = nil; 3915 } 3916 3917 rwlock_unlock_read(&runtimeLock); 3918 3919 if (outCount) *outCount = count; 3920 return (objc_property_t *)result; 3921} 3922 3923 3924/*********************************************************************** 3925* objc_class::getLoadMethod 3926* fixme 3927* Called only from add_class_to_loadable_list. 3928* Locking: runtimeLock must be read- or write-locked by the caller. 3929**********************************************************************/ 3930IMP 3931objc_class::getLoadMethod() 3932{ 3933 rwlock_assert_locked(&runtimeLock); 3934 3935 const method_list_t *mlist; 3936 uint32_t i; 3937 3938 assert(isRealized()); 3939 assert(ISA()->isRealized()); 3940 assert(!isMetaClass()); 3941 assert(ISA()->isMetaClass()); 3942 3943 mlist = ISA()->data()->ro->baseMethods; 3944 if (mlist) for (i = 0; i < mlist->count; i++) { 3945 method_t *m = method_list_nth(mlist, i); 3946 if (0 == strcmp((const char *)m->name, "load")) { 3947 return m->imp; 3948 } 3949 } 3950 3951 return nil; 3952} 3953 3954 3955/*********************************************************************** 3956* _category_getName 3957* Returns a category's name. 3958* Locking: none 3959**********************************************************************/ 3960const char * 3961_category_getName(Category cat) 3962{ 3963 return cat->name; 3964} 3965 3966 3967/*********************************************************************** 3968* _category_getClassName 3969* Returns a category's class's name 3970* Called only from add_category_to_loadable_list and 3971* remove_category_from_loadable_list. 3972* Locking: runtimeLock must be read- or write-locked by the caller 3973**********************************************************************/ 3974const char * 3975_category_getClassName(Category cat) 3976{ 3977 rwlock_assert_locked(&runtimeLock); 3978 return remapClass(cat->cls)->name(); 3979} 3980 3981 3982/*********************************************************************** 3983* _category_getClass 3984* Returns a category's class 3985* Called only by call_category_loads. 3986* Locking: read-locks runtimeLock 3987**********************************************************************/ 3988Class 3989_category_getClass(Category cat) 3990{ 3991 rwlock_read(&runtimeLock); 3992 Class result = remapClass(cat->cls); 3993 assert(result->isRealized()); // ok for call_category_loads' usage 3994 rwlock_unlock_read(&runtimeLock); 3995 return result; 3996} 3997 3998 3999/*********************************************************************** 4000* _category_getLoadMethod 4001* fixme 4002* Called only from add_category_to_loadable_list 4003* Locking: runtimeLock must be read- or write-locked by the caller 4004**********************************************************************/ 4005IMP 4006_category_getLoadMethod(Category cat) 4007{ 4008 rwlock_assert_locked(&runtimeLock); 4009 4010 const method_list_t *mlist; 4011 uint32_t i; 4012 4013 mlist = cat->classMethods; 4014 if (mlist) for (i = 0; i < mlist->count; i++) { 4015 method_t *m = method_list_nth(mlist, i); 4016 if (0 == strcmp((const char *)m->name, "load")) { 4017 return m->imp; 4018 } 4019 } 4020 4021 return nil; 4022} 4023 4024 4025/*********************************************************************** 4026* class_copyProtocolList 4027* fixme 4028* Locking: read-locks runtimeLock 4029**********************************************************************/ 4030Protocol * __unsafe_unretained * 4031class_copyProtocolList(Class cls, unsigned int *outCount) 4032{ 4033 Protocol **r; 4034 const protocol_list_t **p; 4035 unsigned int count = 0; 4036 unsigned int i; 4037 Protocol **result = nil; 4038 4039 if (!cls) { 4040 if (outCount) *outCount = 0; 4041 return nil; 4042 } 4043 4044 rwlock_read(&runtimeLock); 4045 4046 assert(cls->isRealized()); 4047 4048 for (p = cls->data()->protocols; p && *p; p++) { 4049 count += (uint32_t)(*p)->count; 4050 } 4051 4052 if (count) { 4053 result = (Protocol **)malloc((count+1) * sizeof(Protocol *)); 4054 r = result; 4055 for (p = cls->data()->protocols; p && *p; p++) { 4056 for (i = 0; i < (*p)->count; i++) { 4057 *r++ = (Protocol *)remapProtocol((*p)->list[i]); 4058 } 4059 } 4060 *r++ = nil; 4061 } 4062 4063 rwlock_unlock_read(&runtimeLock); 4064 4065 if (outCount) *outCount = count; 4066 return result; 4067} 4068 4069 4070/*********************************************************************** 4071* _objc_copyClassNamesForImage 4072* fixme 4073* Locking: read-locks runtimeLock 4074**********************************************************************/ 4075const char ** 4076_objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount) 4077{ 4078 size_t count, i, shift; 4079 classref_t *classlist; 4080 const char **names; 4081 4082 rwlock_read(&runtimeLock); 4083 4084 classlist = _getObjc2ClassList(hi, &count); 4085 names = (const char **)malloc((count+1) * sizeof(const char *)); 4086 4087 shift = 0; 4088 for (i = 0; i < count; i++) { 4089 Class cls = remapClass(classlist[i]); 4090 if (cls) { 4091 names[i-shift] = cls->name(); 4092 } else { 4093 shift++; // ignored weak-linked class 4094 } 4095 } 4096 count -= shift; 4097 names[count] = nil; 4098 4099 rwlock_unlock_read(&runtimeLock); 4100 4101 if (outCount) *outCount = (unsigned int)count; 4102 return names; 4103} 4104 4105 4106/*********************************************************************** 4107 * _class_getInstanceStart 4108 * Uses alignedInstanceStart() to ensure that ARR layout strings are 4109 * interpreted relative to the first word aligned ivar of an object. 4110 * Locking: none 4111 **********************************************************************/ 4112 4113static uint32_t 4114alignedInstanceStart(Class cls) 4115{ 4116 assert(cls); 4117 assert(cls->isRealized()); 4118 return (uint32_t)((cls->data()->ro->instanceStart + WORD_MASK) & ~WORD_MASK); 4119} 4120 4121uint32_t _class_getInstanceStart(Class cls) { 4122 return alignedInstanceStart(cls); 4123} 4124 4125 4126/*********************************************************************** 4127* class_getVersion 4128* fixme 4129* Locking: none 4130**********************************************************************/ 4131int 4132class_getVersion(Class cls) 4133{ 4134 if (!cls) return 0; 4135 assert(cls->isRealized()); 4136 return cls->data()->version; 4137} 4138 4139 4140/*********************************************************************** 4141* class_setVersion 4142* fixme 4143* Locking: none 4144**********************************************************************/ 4145void 4146class_setVersion(Class cls, int version) 4147{ 4148 if (!cls) return; 4149 assert(cls->isRealized()); 4150 cls->data()->version = version; 4151} 4152 4153 4154static method_t *findMethodInSortedMethodList(SEL key, const method_list_t *list) 4155{ 4156 const method_t * const first = &list->first; 4157 const method_t *base = first; 4158 const method_t *probe; 4159 uintptr_t keyValue = (uintptr_t)key; 4160 uint32_t count; 4161 4162 for (count = list->count; count != 0; count >>= 1) { 4163 probe = base + (count >> 1); 4164 4165 uintptr_t probeValue = (uintptr_t)probe->name; 4166 4167 if (keyValue == probeValue) { 4168 // `probe` is a match. 4169 // Rewind looking for the *first* occurrence of this value. 4170 // This is required for correct category overrides. 4171 while (probe > first && keyValue == (uintptr_t)probe[-1].name) { 4172 probe--; 4173 } 4174 return (method_t *)probe; 4175 } 4176 4177 if (keyValue > probeValue) { 4178 base = probe + 1; 4179 count--; 4180 } 4181 } 4182 4183 return nil; 4184} 4185 4186/*********************************************************************** 4187* getMethodNoSuper_nolock 4188* fixme 4189* Locking: runtimeLock must be read- or write-locked by the caller 4190**********************************************************************/ 4191static method_t *search_method_list(const method_list_t *mlist, SEL sel) 4192{ 4193 int methodListIsFixedUp = isMethodListFixedUp(mlist); 4194 int methodListHasExpectedSize = mlist->getEntsize() == sizeof(method_t); 4195 4196 if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) { 4197 return findMethodInSortedMethodList(sel, mlist); 4198 } else { 4199 // Linear search of unsorted method list 4200 method_list_t::method_iterator iter = mlist->begin(); 4201 method_list_t::method_iterator end = mlist->end(); 4202 for ( ; iter != end; ++iter) { 4203 if (iter->name == sel) return &*iter; 4204 } 4205 } 4206 4207#ifndef NDEBUG 4208 // sanity-check negative results 4209 if (isMethodListFixedUp(mlist)) { 4210 method_list_t::method_iterator iter = mlist->begin(); 4211 method_list_t::method_iterator end = mlist->end(); 4212 for ( ; iter != end; ++iter) { 4213 if (iter->name == sel) { 4214 _objc_fatal("linear search worked when binary search did not"); 4215 } 4216 } 4217 } 4218#endif 4219 4220 return nil; 4221} 4222 4223static method_t * 4224getMethodNoSuper_nolock(Class cls, SEL sel) 4225{ 4226 rwlock_assert_locked(&runtimeLock); 4227 4228 assert(cls->isRealized()); 4229 // fixme nil cls? 4230 // fixme nil sel? 4231 4232 FOREACH_METHOD_LIST(mlist, cls, { 4233 method_t *m = search_method_list(mlist, sel); 4234 if (m) return m; 4235 }); 4236 4237 return nil; 4238} 4239 4240 4241/*********************************************************************** 4242* getMethod_nolock 4243* fixme 4244* Locking: runtimeLock must be read- or write-locked by the caller 4245**********************************************************************/ 4246static method_t * 4247getMethod_nolock(Class cls, SEL sel) 4248{ 4249 method_t *m = nil; 4250 4251 rwlock_assert_locked(&runtimeLock); 4252 4253 // fixme nil cls? 4254 // fixme nil sel? 4255 4256 assert(cls->isRealized()); 4257 4258 while (cls && ((m = getMethodNoSuper_nolock(cls, sel))) == nil) { 4259 cls = cls->superclass; 4260 } 4261 4262 return m; 4263} 4264 4265 4266/*********************************************************************** 4267* _class_getMethod 4268* fixme 4269* Locking: read-locks runtimeLock 4270**********************************************************************/ 4271static Method _class_getMethod(Class cls, SEL sel) 4272{ 4273 method_t *m; 4274 rwlock_read(&runtimeLock); 4275 m = getMethod_nolock(cls, sel); 4276 rwlock_unlock_read(&runtimeLock); 4277 return m; 4278} 4279 4280 4281/*********************************************************************** 4282* class_getInstanceMethod. Return the instance method for the 4283* specified class and selector. 4284**********************************************************************/ 4285Method class_getInstanceMethod(Class cls, SEL sel) 4286{ 4287 if (!cls || !sel) return nil; 4288 4289 // This deliberately avoids +initialize because it historically did so. 4290 4291 // This implementation is a bit weird because it's the only place that 4292 // wants a Method instead of an IMP. 4293 4294#warning fixme build and search caches 4295 4296 // Search method lists, try method resolver, etc. 4297 lookUpImpOrNil(cls, sel, nil, 4298 NO/*initialize*/, NO/*cache*/, YES/*resolver*/); 4299 4300#warning fixme build and search caches 4301 4302 return _class_getMethod(cls, sel); 4303} 4304 4305 4306/*********************************************************************** 4307* log_and_fill_cache 4308* Log this method call. If the logger permits it, fill the method cache. 4309* cls is the method whose cache should be filled. 4310* implementer is the class that owns the implementation in question. 4311**********************************************************************/ 4312static void 4313log_and_fill_cache(Class cls, Class implementer, IMP imp, SEL sel) 4314{ 4315#if SUPPORT_MESSAGE_LOGGING 4316 if (objcMsgLogEnabled) { 4317 bool cacheIt = logMessageSend(implementer->isMetaClass(), 4318 cls->getName(), 4319 implementer->getName(), 4320 sel); 4321 if (!cacheIt) return; 4322 } 4323#endif 4324 cache_fill (cls, sel, imp); 4325} 4326 4327 4328/*********************************************************************** 4329* _class_lookupMethodAndLoadCache. 4330* Method lookup for dispatchers ONLY. OTHER CODE SHOULD USE lookUpImp(). 4331* This lookup avoids optimistic cache scan because the dispatcher 4332* already tried that. 4333**********************************************************************/ 4334IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls) 4335{ 4336 return lookUpImpOrForward(cls, sel, obj, 4337 YES/*initialize*/, NO/*cache*/, YES/*resolver*/); 4338} 4339 4340 4341/*********************************************************************** 4342* lookUpImpOrForward. 4343* The standard IMP lookup. 4344* initialize==NO tries to avoid +initialize (but sometimes fails) 4345* cache==NO skips optimistic unlocked lookup (but uses cache elsewhere) 4346* Most callers should use initialize==YES and cache==YES. 4347* inst is an instance of cls or a subclass thereof, or nil if none is known. 4348* If cls is an un-initialized metaclass then a non-nil inst is faster. 4349* May return _objc_msgForward_impcache. IMPs destined for external use 4350* must be converted to _objc_msgForward or _objc_msgForward_stret. 4351* If you don't want forwarding at all, use lookUpImpOrNil() instead. 4352**********************************************************************/ 4353IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 4354 bool initialize, bool cache, bool resolver) 4355{ 4356 Class curClass; 4357 IMP imp = nil; 4358 Method meth; 4359 bool triedResolver = NO; 4360 4361 rwlock_assert_unlocked(&runtimeLock); 4362 4363 // Optimistic cache lookup 4364 if (cache) { 4365 imp = cache_getImp(cls, sel); 4366 if (imp) return imp; 4367 } 4368 4369 if (!cls->isRealized()) { 4370 rwlock_write(&runtimeLock); 4371 realizeClass(cls); 4372 rwlock_unlock_write(&runtimeLock); 4373 } 4374 4375 if (initialize && !cls->isInitialized()) { 4376 _class_initialize (_class_getNonMetaClass(cls, inst)); 4377 // If sel == initialize, _class_initialize will send +initialize and 4378 // then the messenger will send +initialize again after this 4379 // procedure finishes. Of course, if this is not being called 4380 // from the messenger then it won't happen. 2778172 4381 } 4382 4383 // The lock is held to make method-lookup + cache-fill atomic 4384 // with respect to method addition. Otherwise, a category could 4385 // be added but ignored indefinitely because the cache was re-filled 4386 // with the old value after the cache flush on behalf of the category. 4387 retry: 4388 rwlock_read(&runtimeLock); 4389 4390 // Ignore GC selectors 4391 if (ignoreSelector(sel)) { 4392 imp = _objc_ignored_method; 4393 cache_fill(cls, sel, imp); 4394 goto done; 4395 } 4396 4397 // Try this class's cache. 4398 4399 imp = cache_getImp(cls, sel); 4400 if (imp) goto done; 4401 4402 // Try this class's method lists. 4403 4404 meth = getMethodNoSuper_nolock(cls, sel); 4405 if (meth) { 4406 log_and_fill_cache(cls, cls, meth->imp, sel); 4407 imp = meth->imp; 4408 goto done; 4409 } 4410 4411 // Try superclass caches and method lists. 4412 4413 curClass = cls; 4414 while ((curClass = curClass->superclass)) { 4415 // Superclass cache. 4416 imp = cache_getImp(curClass, sel); 4417 if (imp) { 4418 if (imp != (IMP)_objc_msgForward_impcache) { 4419 // Found the method in a superclass. Cache it in this class. 4420 log_and_fill_cache(cls, curClass, imp, sel); 4421 goto done; 4422 } 4423 else { 4424 // Found a forward:: entry in a superclass. 4425 // Stop searching, but don't cache yet; call method 4426 // resolver for this class first. 4427 break; 4428 } 4429 } 4430 4431 // Superclass method list. 4432 meth = getMethodNoSuper_nolock(curClass, sel); 4433 if (meth) { 4434 log_and_fill_cache(cls, curClass, meth->imp, sel); 4435 imp = meth->imp; 4436 goto done; 4437 } 4438 } 4439 4440 // No implementation found. Try method resolver once. 4441 4442 if (resolver && !triedResolver) { 4443 rwlock_unlock_read(&runtimeLock); 4444 _class_resolveMethod(cls, sel, inst); 4445 // Don't cache the result; we don't hold the lock so it may have 4446 // changed already. Re-do the search from scratch instead. 4447 triedResolver = YES; 4448 goto retry; 4449 } 4450 4451 // No implementation found, and method resolver didn't help. 4452 // Use forwarding. 4453 4454 imp = (IMP)_objc_msgForward_impcache; 4455 cache_fill(cls, sel, imp); 4456 4457 done: 4458 rwlock_unlock_read(&runtimeLock); 4459 4460 // paranoia: look for ignored selectors with non-ignored implementations 4461 assert(!(ignoreSelector(sel) && imp != (IMP)&_objc_ignored_method)); 4462 4463 // paranoia: never let uncached leak out 4464 assert(imp != _objc_msgSend_uncached_impcache); 4465 4466 return imp; 4467} 4468 4469 4470/*********************************************************************** 4471* lookUpImpOrNil. 4472* Like lookUpImpOrForward, but returns nil instead of _objc_msgForward_impcache 4473**********************************************************************/ 4474IMP lookUpImpOrNil(Class cls, SEL sel, id inst, 4475 bool initialize, bool cache, bool resolver) 4476{ 4477 IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver); 4478 if (imp == _objc_msgForward_impcache) return nil; 4479 else return imp; 4480} 4481 4482 4483/*********************************************************************** 4484* lookupMethodInClassAndLoadCache. 4485* Like _class_lookupMethodAndLoadCache, but does not search superclasses. 4486* Caches and returns objc_msgForward if the method is not found in the class. 4487**********************************************************************/ 4488IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel) 4489{ 4490 Method meth; 4491 IMP imp; 4492 4493 // fixme this is incomplete - no resolver, +initialize, GC - 4494 // but it's only used for .cxx_construct/destruct so we don't care 4495 assert(sel == SEL_cxx_construct || sel == SEL_cxx_destruct); 4496 4497 // Search cache first. 4498 imp = cache_getImp(cls, sel); 4499 if (imp) return imp; 4500 4501 // Cache miss. Search method list. 4502 4503 rwlock_read(&runtimeLock); 4504 4505 meth = getMethodNoSuper_nolock(cls, sel); 4506 4507 if (meth) { 4508 // Hit in method list. Cache it. 4509 cache_fill(cls, sel, meth->imp); 4510 rwlock_unlock_read(&runtimeLock); 4511 return meth->imp; 4512 } else { 4513 // Miss in method list. Cache objc_msgForward. 4514 cache_fill(cls, sel, _objc_msgForward_impcache); 4515 rwlock_unlock_read(&runtimeLock); 4516 return _objc_msgForward_impcache; 4517 } 4518} 4519 4520 4521/*********************************************************************** 4522* class_getProperty 4523* fixme 4524* Locking: read-locks runtimeLock 4525**********************************************************************/ 4526objc_property_t class_getProperty(Class cls, const char *name) 4527{ 4528 property_t *result = nil; 4529 chained_property_list *plist; 4530 4531 if (!cls || !name) return nil; 4532 4533 rwlock_read(&runtimeLock); 4534 4535 assert(cls->isRealized()); 4536 4537 for ( ; cls; cls = cls->superclass) { 4538 for (plist = cls->data()->properties; plist; plist = plist->next) { 4539 uint32_t i; 4540 for (i = 0; i < plist->count; i++) { 4541 if (0 == strcmp(name, plist->list[i].name)) { 4542 result = &plist->list[i]; 4543 goto done; 4544 } 4545 } 4546 } 4547 } 4548 4549 done: 4550 rwlock_unlock_read(&runtimeLock); 4551 4552 return (objc_property_t)result; 4553} 4554 4555 4556/*********************************************************************** 4557* Locking: fixme 4558**********************************************************************/ 4559 4560Class gdb_class_getClass(Class cls) 4561{ 4562 const char *className = cls->name(); 4563 if(!className || !strlen(className)) return Nil; 4564 Class rCls = look_up_class(className, NO, NO); 4565 return rCls; 4566} 4567 4568Class gdb_object_getClass(id obj) 4569{ 4570 if (!obj) return nil; 4571 return gdb_class_getClass(obj->getIsa()); 4572} 4573 4574 4575/*********************************************************************** 4576* Locking: write-locks runtimeLock 4577**********************************************************************/ 4578void 4579objc_class::setInitialized() 4580{ 4581 Class metacls; 4582 4583 assert(!isMetaClass()); 4584 4585 metacls = this->ISA(); 4586 4587 metacls->changeInfo(RW_INITIALIZED, RW_INITIALIZING); 4588} 4589 4590 4591/*********************************************************************** 4592 * _class_usesAutomaticRetainRelease 4593 * Returns YES if class was compiled with -fobjc-arc 4594 **********************************************************************/ 4595BOOL _class_usesAutomaticRetainRelease(Class cls) 4596{ 4597 return (cls->data()->ro->flags & RO_IS_ARR) ? YES : NO; 4598} 4599 4600 4601/*********************************************************************** 4602* Return YES if sel is used by retain/release implementors 4603**********************************************************************/ 4604static bool isRRSelector(SEL sel) 4605{ 4606 return (sel == SEL_retain || sel == SEL_release || 4607 sel == SEL_autorelease || sel == SEL_retainCount); 4608} 4609 4610 4611/*********************************************************************** 4612* Return YES if sel is used by alloc or allocWithZone implementors 4613**********************************************************************/ 4614static bool isAWZSelector(SEL sel) 4615{ 4616 return (sel == SEL_allocWithZone || sel == SEL_alloc); 4617} 4618 4619 4620/*********************************************************************** 4621* Mark this class and all of its subclasses as implementors or 4622* inheritors of custom RR (retain/release/autorelease/retainCount) 4623**********************************************************************/ 4624void objc_class::setHasCustomRR(bool inherited) 4625{ 4626 rwlock_assert_writing(&runtimeLock); 4627 4628 if (hasCustomRR()) return; 4629 4630 FOREACH_REALIZED_CLASS_AND_SUBCLASS(c, (Class)this, { 4631 if (PrintCustomRR && !c->hasCustomRR()) { 4632 _objc_inform("CUSTOM RR: %s%s%s", c->name(), 4633 c->isMetaClass() ? " (meta)" : "", 4634 (inherited || c != (Class)this) ? " (inherited)" : ""); 4635 } 4636#if CLASS_FAST_FLAGS_VIA_RW_DATA 4637 c->data_NEVER_USE |= CLASS_FAST_FLAG_HAS_CUSTOM_RR; 4638#else 4639 c->data()->flags |= RW_HAS_CUSTOM_RR; 4640#endif 4641 }); 4642} 4643 4644 4645/*********************************************************************** 4646* Mark this class and all of its subclasses as implementors or 4647* inheritors of custom alloc/allocWithZone: 4648**********************************************************************/ 4649void objc_class::setHasCustomAWZ(bool inherited ) 4650{ 4651 rwlock_assert_writing(&runtimeLock); 4652 4653 if (hasCustomAWZ()) return; 4654 4655 FOREACH_REALIZED_CLASS_AND_SUBCLASS(c, (Class)this, { 4656 if (PrintCustomAWZ && !c->hasCustomAWZ()) { 4657 _objc_inform("CUSTOM AWZ: %s%s%s", c->name(), 4658 c->isMetaClass() ? " (meta)" : "", 4659 (inherited || c != (Class)this) ? " (inherited)" : ""); 4660 } 4661 c->data()->flags |= RW_HAS_CUSTOM_AWZ; 4662 }); 4663} 4664 4665 4666/*********************************************************************** 4667* Update custom RR and AWZ when a method changes its IMP 4668**********************************************************************/ 4669static void 4670updateCustomRR_AWZ(Class cls, method_t *meth) 4671{ 4672 // In almost all cases, IMP swizzling does not affect custom RR/AWZ bits. 4673 // The class is already marked for custom RR/AWZ, so changing the IMP 4674 // does not transition from non-custom to custom. 4675 // 4676 // The only cases where IMP swizzling can affect the RR/AWZ bits is 4677 // if the swizzled method is one of the methods that is assumed to be 4678 // non-custom. These special cases come from attachMethodLists(). 4679 // We look for such cases here if we do not know the affected class. 4680 4681 if (isRRSelector(meth->name)) { 4682 if (cls) { 4683 cls->setHasCustomRR(); 4684 } else { 4685 // Don't know the class. 4686 // The only special case is class NSObject. 4687 FOREACH_METHOD_LIST(mlist, classNSObject(), { 4688 for (uint32_t i = 0; i < mlist->count; i++) { 4689 if (meth == method_list_nth(mlist, i)) { 4690 // Yep, they're swizzling NSObject. 4691 classNSObject()->setHasCustomRR(); 4692 return; 4693 } 4694 } 4695 }); 4696 } 4697 } 4698 else if (isAWZSelector(meth->name)) { 4699 if (cls) { 4700 cls->setHasCustomAWZ(); 4701 } else { 4702 // Don't know the class. 4703 // The only special case is metaclass NSObject. 4704 FOREACH_METHOD_LIST(mlist, classNSObject()->ISA(), { 4705 for (uint32_t i = 0; i < mlist->count; i++) { 4706 if (meth == method_list_nth(mlist, i)) { 4707 // Yep, they're swizzling metaclass NSObject. 4708 classNSObject()->ISA()->setHasCustomAWZ(); 4709 return; 4710 } 4711 } 4712 }); 4713 } 4714 } 4715} 4716 4717 4718/*********************************************************************** 4719* class_getIvarLayout 4720* Called by the garbage collector. 4721* The class must be nil or already realized. 4722* Locking: none 4723**********************************************************************/ 4724const uint8_t * 4725class_getIvarLayout(Class cls) 4726{ 4727 if (cls) return cls->data()->ro->ivarLayout; 4728 else return nil; 4729} 4730 4731 4732/*********************************************************************** 4733* class_getWeakIvarLayout 4734* Called by the garbage collector. 4735* The class must be nil or already realized. 4736* Locking: none 4737**********************************************************************/ 4738const uint8_t * 4739class_getWeakIvarLayout(Class cls) 4740{ 4741 if (cls) return cls->data()->ro->weakIvarLayout; 4742 else return nil; 4743} 4744 4745 4746/*********************************************************************** 4747* class_setIvarLayout 4748* Changes the class's GC scan layout. 4749* nil layout means no unscanned ivars 4750* The class must be under construction. 4751* fixme: sanity-check layout vs instance size? 4752* fixme: sanity-check layout vs superclass? 4753* Locking: acquires runtimeLock 4754**********************************************************************/ 4755void 4756class_setIvarLayout(Class cls, const uint8_t *layout) 4757{ 4758 if (!cls) return; 4759 4760 rwlock_write(&runtimeLock); 4761 4762 // Can only change layout of in-construction classes. 4763 // note: if modifications to post-construction classes were 4764 // allowed, there would be a race below (us vs. concurrent GC scan) 4765 if (!(cls->data()->flags & RW_CONSTRUCTING)) { 4766 _objc_inform("*** Can't set ivar layout for already-registered " 4767 "class '%s'", cls->name()); 4768 rwlock_unlock_write(&runtimeLock); 4769 return; 4770 } 4771 4772 class_ro_t *ro_w = make_ro_writeable(cls->data()); 4773 4774 try_free(ro_w->ivarLayout); 4775 ro_w->ivarLayout = _ustrdup_internal(layout); 4776 4777 rwlock_unlock_write(&runtimeLock); 4778} 4779 4780// SPI: Instance-specific object layout. 4781 4782void 4783_class_setIvarLayoutAccessor(Class cls, const uint8_t* (*accessor) (id object)) { 4784 if (!cls) return; 4785 4786 rwlock_write(&runtimeLock); 4787 4788 class_ro_t *ro_w = make_ro_writeable(cls->data()); 4789 4790 // FIXME: this really isn't safe to free if there are instances of this class already. 4791 if (!(cls->data()->flags & RW_HAS_INSTANCE_SPECIFIC_LAYOUT)) try_free(ro_w->ivarLayout); 4792 ro_w->ivarLayout = (uint8_t *)accessor; 4793 cls->setInfo(RW_HAS_INSTANCE_SPECIFIC_LAYOUT); 4794 4795 rwlock_unlock_write(&runtimeLock); 4796} 4797 4798const uint8_t * 4799_object_getIvarLayout(Class cls, id object) 4800{ 4801 if (cls) { 4802 const uint8_t* layout = cls->data()->ro->ivarLayout; 4803 if (cls->data()->flags & RW_HAS_INSTANCE_SPECIFIC_LAYOUT) { 4804 const uint8_t* (*accessor) (id object) = (const uint8_t* (*)(id))layout; 4805 layout = accessor(object); 4806 } 4807 return layout; 4808 } 4809 return nil; 4810} 4811 4812/*********************************************************************** 4813* class_setWeakIvarLayout 4814* Changes the class's GC weak layout. 4815* nil layout means no weak ivars 4816* The class must be under construction. 4817* fixme: sanity-check layout vs instance size? 4818* fixme: sanity-check layout vs superclass? 4819* Locking: acquires runtimeLock 4820**********************************************************************/ 4821void 4822class_setWeakIvarLayout(Class cls, const uint8_t *layout) 4823{ 4824 if (!cls) return; 4825 4826 rwlock_write(&runtimeLock); 4827 4828 // Can only change layout of in-construction classes. 4829 // note: if modifications to post-construction classes were 4830 // allowed, there would be a race below (us vs. concurrent GC scan) 4831 if (!(cls->data()->flags & RW_CONSTRUCTING)) { 4832 _objc_inform("*** Can't set weak ivar layout for already-registered " 4833 "class '%s'", cls->name()); 4834 rwlock_unlock_write(&runtimeLock); 4835 return; 4836 } 4837 4838 class_ro_t *ro_w = make_ro_writeable(cls->data()); 4839 4840 try_free(ro_w->weakIvarLayout); 4841 ro_w->weakIvarLayout = _ustrdup_internal(layout); 4842 4843 rwlock_unlock_write(&runtimeLock); 4844} 4845 4846 4847/*********************************************************************** 4848* _class_getVariable 4849* fixme 4850* Locking: read-locks runtimeLock 4851**********************************************************************/ 4852Ivar 4853_class_getVariable(Class cls, const char *name, Class *memberOf) 4854{ 4855 rwlock_read(&runtimeLock); 4856 4857 for ( ; cls; cls = cls->superclass) { 4858 ivar_t *ivar = getIvar(cls, name); 4859 if (ivar) { 4860 rwlock_unlock_read(&runtimeLock); 4861 if (memberOf) *memberOf = cls; 4862 return ivar; 4863 } 4864 } 4865 4866 rwlock_unlock_read(&runtimeLock); 4867 4868 return nil; 4869} 4870 4871 4872/*********************************************************************** 4873* class_conformsToProtocol 4874* fixme 4875* Locking: read-locks runtimeLock 4876**********************************************************************/ 4877BOOL class_conformsToProtocol(Class cls, Protocol *proto_gen) 4878{ 4879 protocol_t *proto = newprotocol(proto_gen); 4880 const protocol_list_t **plist; 4881 unsigned int i; 4882 BOOL result = NO; 4883 4884 if (!cls) return NO; 4885 if (!proto_gen) return NO; 4886 4887 rwlock_read(&runtimeLock); 4888 4889 assert(cls->isRealized()); 4890 4891 for (plist = cls->data()->protocols; plist && *plist; plist++) { 4892 for (i = 0; i < (*plist)->count; i++) { 4893 protocol_t *p = remapProtocol((*plist)->list[i]); 4894 if (p == proto || protocol_conformsToProtocol_nolock(p, proto)) { 4895 result = YES; 4896 goto done; 4897 } 4898 } 4899 } 4900 4901 done: 4902 rwlock_unlock_read(&runtimeLock); 4903 4904 return result; 4905} 4906 4907 4908/********************************************************************** 4909* addMethod 4910* fixme 4911* Locking: runtimeLock must be held by the caller 4912**********************************************************************/ 4913static IMP 4914addMethod(Class cls, SEL name, IMP imp, const char *types, BOOL replace) 4915{ 4916 IMP result = nil; 4917 4918 rwlock_assert_writing(&runtimeLock); 4919 4920 assert(types); 4921 assert(cls->isRealized()); 4922 4923 method_t *m; 4924 if ((m = getMethodNoSuper_nolock(cls, name))) { 4925 // already exists 4926 if (!replace) { 4927 result = _method_getImplementation(m); 4928 } else { 4929 result = _method_setImplementation(cls, m, imp); 4930 } 4931 } else { 4932 // fixme optimize 4933 method_list_t *newlist; 4934 newlist = (method_list_t *)_calloc_internal(sizeof(*newlist), 1); 4935 newlist->entsize_NEVER_USE = (uint32_t)sizeof(method_t) | fixed_up_method_list; 4936 newlist->count = 1; 4937 newlist->first.name = name; 4938 newlist->first.types = strdup(types); 4939 if (!ignoreSelector(name)) { 4940 newlist->first.imp = imp; 4941 } else { 4942 newlist->first.imp = (IMP)&_objc_ignored_method; 4943 } 4944 4945 attachMethodLists(cls, &newlist, 1, NO, NO, YES); 4946 4947 result = nil; 4948 } 4949 4950 return result; 4951} 4952 4953 4954BOOL 4955class_addMethod(Class cls, SEL name, IMP imp, const char *types) 4956{ 4957 if (!cls) return NO; 4958 4959 rwlock_write(&runtimeLock); 4960 IMP old = addMethod(cls, name, imp, types ?: "", NO); 4961 rwlock_unlock_write(&runtimeLock); 4962 return old ? NO : YES; 4963} 4964 4965 4966IMP 4967class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) 4968{ 4969 if (!cls) return nil; 4970 4971 rwlock_write(&runtimeLock); 4972 IMP old = addMethod(cls, name, imp, types ?: "", YES); 4973 rwlock_unlock_write(&runtimeLock); 4974 return old; 4975} 4976 4977 4978/*********************************************************************** 4979* class_addIvar 4980* Adds an ivar to a class. 4981* Locking: acquires runtimeLock 4982**********************************************************************/ 4983BOOL 4984class_addIvar(Class cls, const char *name, size_t size, 4985 uint8_t alignment, const char *type) 4986{ 4987 if (!cls) return NO; 4988 4989 if (!type) type = ""; 4990 if (name && 0 == strcmp(name, "")) name = nil; 4991 4992 rwlock_write(&runtimeLock); 4993 4994 assert(cls->isRealized()); 4995 4996 // No class variables 4997 if (cls->isMetaClass()) { 4998 rwlock_unlock_write(&runtimeLock); 4999 return NO; 5000 } 5001 5002 // Can only add ivars to in-construction classes. 5003 if (!(cls->data()->flags & RW_CONSTRUCTING)) { 5004 rwlock_unlock_write(&runtimeLock); 5005 return NO; 5006 } 5007 5008 // Check for existing ivar with this name, unless it's anonymous. 5009 // Check for too-big ivar. 5010 // fixme check for superclass ivar too? 5011 if ((name && getIvar(cls, name)) || size > UINT32_MAX) { 5012 rwlock_unlock_write(&runtimeLock); 5013 return NO; 5014 } 5015 5016 class_ro_t *ro_w = make_ro_writeable(cls->data()); 5017 5018 // fixme allocate less memory here 5019 5020 ivar_list_t *oldlist, *newlist; 5021 if ((oldlist = (ivar_list_t *)cls->data()->ro->ivars)) { 5022 size_t oldsize = ivar_list_size(oldlist); 5023 newlist = (ivar_list_t *) 5024 _calloc_internal(oldsize + oldlist->entsize, 1); 5025 memcpy(newlist, oldlist, oldsize); 5026 _free_internal(oldlist); 5027 } else { 5028 newlist = (ivar_list_t *) 5029 _calloc_internal(sizeof(ivar_list_t), 1); 5030 newlist->entsize = (uint32_t)sizeof(ivar_t); 5031 } 5032 5033 uint32_t offset = cls->unalignedInstanceSize(); 5034 uint32_t alignMask = (1<<alignment)-1; 5035 offset = (offset + alignMask) & ~alignMask; 5036 5037 ivar_t *ivar = ivar_list_nth(newlist, newlist->count++); 5038#if __x86_64__ 5039 // Deliberately over-allocate the ivar offset variable. 5040 // Use calloc() to clear all 64 bits. See the note in struct ivar_t. 5041 ivar->offset = (int32_t *)(int64_t *)_calloc_internal(sizeof(int64_t), 1); 5042#else 5043 ivar->offset = (int32_t *)_malloc_internal(sizeof(int32_t)); 5044#endif 5045 *ivar->offset = offset; 5046 ivar->name = name ? _strdup_internal(name) : nil; 5047 ivar->type = _strdup_internal(type); 5048 ivar->alignment_raw = alignment; 5049 ivar->size = (uint32_t)size; 5050 5051 ro_w->ivars = newlist; 5052 ro_w->instanceSize = (uint32_t)(offset + size); 5053 5054 // Ivar layout updated in registerClass. 5055 5056 rwlock_unlock_write(&runtimeLock); 5057 5058 return YES; 5059} 5060 5061 5062/*********************************************************************** 5063* class_addProtocol 5064* Adds a protocol to a class. 5065* Locking: acquires runtimeLock 5066**********************************************************************/ 5067BOOL class_addProtocol(Class cls, Protocol *protocol_gen) 5068{ 5069 protocol_t *protocol = newprotocol(protocol_gen); 5070 protocol_list_t *plist; 5071 const protocol_list_t **plistp; 5072 5073 if (!cls) return NO; 5074 if (class_conformsToProtocol(cls, protocol_gen)) return NO; 5075 5076 rwlock_write(&runtimeLock); 5077 5078 assert(cls->isRealized()); 5079 5080 // fixme optimize 5081 plist = (protocol_list_t *) 5082 _malloc_internal(sizeof(protocol_list_t) + sizeof(protocol_t *)); 5083 plist->count = 1; 5084 plist->list[0] = (protocol_ref_t)protocol; 5085 5086 unsigned int count = 0; 5087 for (plistp = cls->data()->protocols; plistp && *plistp; plistp++) { 5088 count++; 5089 } 5090 5091 cls->data()->protocols = (const protocol_list_t **) 5092 _realloc_internal(cls->data()->protocols, 5093 (count+2) * sizeof(protocol_list_t *)); 5094 cls->data()->protocols[count] = plist; 5095 cls->data()->protocols[count+1] = nil; 5096 5097 // fixme metaclass? 5098 5099 rwlock_unlock_write(&runtimeLock); 5100 5101 return YES; 5102} 5103 5104 5105/*********************************************************************** 5106* class_addProperty 5107* Adds a property to a class. 5108* Locking: acquires runtimeLock 5109**********************************************************************/ 5110static BOOL 5111_class_addProperty(Class cls, const char *name, 5112 const objc_property_attribute_t *attrs, unsigned int count, 5113 BOOL replace) 5114{ 5115 chained_property_list *plist; 5116 5117 if (!cls) return NO; 5118 if (!name) return NO; 5119 5120 property_t *prop = class_getProperty(cls, name); 5121 if (prop && !replace) { 5122 // already exists, refuse to replace 5123 return NO; 5124 } 5125 else if (prop) { 5126 // replace existing 5127 rwlock_write(&runtimeLock); 5128 try_free(prop->attributes); 5129 prop->attributes = copyPropertyAttributeString(attrs, count); 5130 rwlock_unlock_write(&runtimeLock); 5131 return YES; 5132 } 5133 else { 5134 rwlock_write(&runtimeLock); 5135 5136 assert(cls->isRealized()); 5137 5138 plist = (chained_property_list *) 5139 _malloc_internal(sizeof(*plist) + sizeof(plist->list[0])); 5140 plist->count = 1; 5141 plist->list[0].name = _strdup_internal(name); 5142 plist->list[0].attributes = copyPropertyAttributeString(attrs, count); 5143 5144 plist->next = cls->data()->properties; 5145 cls->data()->properties = plist; 5146 5147 rwlock_unlock_write(&runtimeLock); 5148 5149 return YES; 5150 } 5151} 5152 5153BOOL 5154class_addProperty(Class cls, const char *name, 5155 const objc_property_attribute_t *attrs, unsigned int n) 5156{ 5157 return _class_addProperty(cls, name, attrs, n, NO); 5158} 5159 5160void 5161class_replaceProperty(Class cls, const char *name, 5162 const objc_property_attribute_t *attrs, unsigned int n) 5163{ 5164 _class_addProperty(cls, name, attrs, n, YES); 5165} 5166 5167 5168/*********************************************************************** 5169* look_up_class 5170* Look up a class by name, and realize it. 5171* Locking: acquires runtimeLock 5172**********************************************************************/ 5173Class 5174look_up_class(const char *name, 5175 BOOL includeUnconnected __attribute__((unused)), 5176 BOOL includeClassHandler __attribute__((unused))) 5177{ 5178 if (!name) return nil; 5179 5180 rwlock_read(&runtimeLock); 5181 Class result = getClass(name); 5182 BOOL unrealized = result && !result->isRealized(); 5183 rwlock_unlock_read(&runtimeLock); 5184 if (unrealized) { 5185 rwlock_write(&runtimeLock); 5186 realizeClass(result); 5187 rwlock_unlock_write(&runtimeLock); 5188 } 5189 return result; 5190} 5191 5192 5193/*********************************************************************** 5194* objc_duplicateClass 5195* fixme 5196* Locking: acquires runtimeLock 5197**********************************************************************/ 5198Class 5199objc_duplicateClass(Class original, const char *name, 5200 size_t extraBytes) 5201{ 5202 Class duplicate; 5203 5204 rwlock_write(&runtimeLock); 5205 5206 assert(original->isRealized()); 5207 assert(!original->isMetaClass()); 5208 5209 duplicate = _calloc_class(original->ISA()->alignedInstanceSize()+extraBytes); 5210 if (original->ISA()->unalignedInstanceSize() < sizeof(objc_class)) { 5211 _objc_inform("busted! %s\n", original->data()->ro->name); 5212 } 5213 5214 5215 duplicate->initIsa(original->ISA()); 5216 duplicate->superclass = original->superclass; 5217 5218 duplicate->cache.buckets = (bucket_t *)&_objc_empty_cache; 5219 // cache.shiftmask and cache.occupied are already zero 5220 5221 duplicate->setData((class_rw_t *)_calloc_internal(sizeof(*original->data()), 1)); 5222 duplicate->data()->flags = (original->data()->flags | RW_COPIED_RO); 5223 duplicate->data()->version = original->data()->version; 5224 duplicate->data()->firstSubclass = nil; 5225 duplicate->data()->nextSiblingClass = nil; 5226 5227 duplicate->data()->ro = (class_ro_t *) 5228 _memdup_internal(original->data()->ro, sizeof(*original->data()->ro)); 5229 *(char **)&duplicate->data()->ro->name = _strdup_internal(name); 5230 5231 if (original->data()->flags & RW_METHOD_ARRAY) { 5232 duplicate->data()->method_lists = (method_list_t **) 5233 _memdup_internal(original->data()->method_lists, 5234 malloc_size(original->data()->method_lists)); 5235 method_list_t **mlistp; 5236 for (mlistp = duplicate->data()->method_lists; *mlistp; mlistp++) { 5237 *mlistp = (method_list_t *) 5238 _memdup_internal(*mlistp, method_list_size(*mlistp)); 5239 } 5240 } else { 5241 if (original->data()->method_list) { 5242 duplicate->data()->method_list = (method_list_t *) 5243 _memdup_internal(original->data()->method_list, 5244 method_list_size(original->data()->method_list)); 5245 } 5246 } 5247 5248 // fixme dies when categories are added to the base 5249 duplicate->data()->properties = original->data()->properties; 5250 duplicate->data()->protocols = original->data()->protocols; 5251 5252 if (duplicate->superclass) { 5253 addSubclass(duplicate->superclass, duplicate); 5254 } 5255 5256 // Don't methodize class - construction above is correct 5257 5258 addNamedClass(duplicate, duplicate->data()->ro->name); 5259 addRealizedClass(duplicate); 5260 // no: duplicate->ISA == original->ISA 5261 // addRealizedMetaclass(duplicate->ISA); 5262 5263 if (PrintConnecting) { 5264 _objc_inform("CLASS: realizing class '%s' (duplicate of %s) %p %p", 5265 name, original->data()->ro->name, 5266 (void*)duplicate, duplicate->data()->ro); 5267 } 5268 5269 rwlock_unlock_write(&runtimeLock); 5270 5271 return duplicate; 5272} 5273 5274/*********************************************************************** 5275* objc_initializeClassPair 5276* Locking: runtimeLock must be write-locked by the caller 5277**********************************************************************/ 5278 5279// &UnsetLayout is the default ivar layout during class construction 5280static const uint8_t UnsetLayout = 0; 5281 5282static void objc_initializeClassPair_internal(Class superclass, const char *name, Class cls, Class meta) 5283{ 5284 rwlock_assert_writing(&runtimeLock); 5285 5286 class_ro_t *cls_ro_w, *meta_ro_w; 5287 5288 cls->cache.buckets = (bucket_t *)&_objc_empty_cache; 5289 meta->cache.buckets = (bucket_t *)&_objc_empty_cache; 5290 // cache.shiftmask and cache.occupied are already zero 5291 5292 cls->setData((class_rw_t *)_calloc_internal(sizeof(class_rw_t), 1)); 5293 meta->setData((class_rw_t *)_calloc_internal(sizeof(class_rw_t), 1)); 5294 cls_ro_w = (class_ro_t *)_calloc_internal(sizeof(class_ro_t), 1); 5295 meta_ro_w = (class_ro_t *)_calloc_internal(sizeof(class_ro_t), 1); 5296 cls->data()->ro = cls_ro_w; 5297 meta->data()->ro = meta_ro_w; 5298 5299 // Set basic info 5300 5301 cls->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED; 5302 meta->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED; 5303 cls->data()->version = 0; 5304 meta->data()->version = 7; 5305 5306 cls_ro_w->flags = 0; 5307 meta_ro_w->flags = RO_META; 5308 if (!superclass) { 5309 cls_ro_w->flags |= RO_ROOT; 5310 meta_ro_w->flags |= RO_ROOT; 5311 } 5312 if (superclass) { 5313 cls_ro_w->instanceStart = superclass->unalignedInstanceSize(); 5314 meta_ro_w->instanceStart = superclass->ISA()->unalignedInstanceSize(); 5315 cls_ro_w->instanceSize = cls_ro_w->instanceStart; 5316 meta_ro_w->instanceSize = meta_ro_w->instanceStart; 5317 } else { 5318 cls_ro_w->instanceStart = 0; 5319 meta_ro_w->instanceStart = (uint32_t)sizeof(objc_class); 5320 cls_ro_w->instanceSize = (uint32_t)sizeof(id); // just an isa 5321 meta_ro_w->instanceSize = meta_ro_w->instanceStart; 5322 } 5323 5324 cls_ro_w->name = _strdup_internal(name); 5325 meta_ro_w->name = _strdup_internal(name); 5326 5327 cls_ro_w->ivarLayout = &UnsetLayout; 5328 cls_ro_w->weakIvarLayout = &UnsetLayout; 5329 5330 // Connect to superclasses and metaclasses 5331 cls->initIsa(meta); 5332 if (superclass) { 5333 meta->initIsa(superclass->ISA()->ISA()); 5334 cls->superclass = superclass; 5335 meta->superclass = superclass->ISA(); 5336 addSubclass(superclass, cls); 5337 addSubclass(superclass->ISA(), meta); 5338 } else { 5339 meta->initIsa(meta); 5340 cls->superclass = Nil; 5341 meta->superclass = cls; 5342 addSubclass(cls, meta); 5343 } 5344} 5345 5346/*********************************************************************** 5347* objc_initializeClassPair 5348**********************************************************************/ 5349Class objc_initializeClassPair(Class superclass, const char *name, Class cls, Class meta) 5350{ 5351 rwlock_write(&runtimeLock); 5352 5353 // 5354 // Common superclass integrity checks with objc_allocateClassPair 5355 // 5356 if (getClass(name)) { 5357 rwlock_unlock_write(&runtimeLock); 5358 return Nil; 5359 } 5360 // fixme reserve class against simultaneous allocation 5361 5362 if (superclass) assert(superclass->isRealized()); 5363 5364 if (superclass && superclass->data()->flags & RW_CONSTRUCTING) { 5365 // Can't make subclass of an in-construction class 5366 rwlock_unlock_write(&runtimeLock); 5367 return Nil; 5368 } 5369 5370 5371 // just initialize what was supplied 5372 objc_initializeClassPair_internal(superclass, name, cls, meta); 5373 5374 rwlock_unlock_write(&runtimeLock); 5375 return cls; 5376} 5377 5378/*********************************************************************** 5379* objc_allocateClassPair 5380* fixme 5381* Locking: acquires runtimeLock 5382**********************************************************************/ 5383Class objc_allocateClassPair(Class superclass, const char *name, 5384 size_t extraBytes) 5385{ 5386 Class cls, meta; 5387 5388 rwlock_write(&runtimeLock); 5389 5390 // 5391 // Common superclass integrity checks with objc_initializeClassPair 5392 // 5393 if (getClass(name)) { 5394 rwlock_unlock_write(&runtimeLock); 5395 return Nil; 5396 } 5397 // fixme reserve class against simmultaneous allocation 5398 5399 if (superclass) assert(superclass->isRealized()); 5400 5401 if (superclass && superclass->data()->flags & RW_CONSTRUCTING) { 5402 // Can't make subclass of an in-construction class 5403 rwlock_unlock_write(&runtimeLock); 5404 return Nil; 5405 } 5406 5407 5408 5409 // Allocate new classes. 5410 size_t size = sizeof(objc_class); 5411 size_t metasize = sizeof(objc_class); 5412 if (superclass) { 5413 size = superclass->ISA()->alignedInstanceSize(); 5414 metasize = superclass->ISA()->ISA()->alignedInstanceSize(); 5415 } 5416 cls = _calloc_class(size + extraBytes); 5417 meta = _calloc_class(metasize + extraBytes); 5418 5419 objc_initializeClassPair_internal(superclass, name, cls, meta); 5420 5421 rwlock_unlock_write(&runtimeLock); 5422 5423 return cls; 5424} 5425 5426 5427/*********************************************************************** 5428* objc_registerClassPair 5429* fixme 5430* Locking: acquires runtimeLock 5431**********************************************************************/ 5432void objc_registerClassPair(Class cls) 5433{ 5434 rwlock_write(&runtimeLock); 5435 5436 if ((cls->data()->flags & RW_CONSTRUCTED) || 5437 (cls->ISA()->data()->flags & RW_CONSTRUCTED)) 5438 { 5439 _objc_inform("objc_registerClassPair: class '%s' was already " 5440 "registered!", cls->data()->ro->name); 5441 rwlock_unlock_write(&runtimeLock); 5442 return; 5443 } 5444 5445 if (!(cls->data()->flags & RW_CONSTRUCTING) || 5446 !(cls->ISA()->data()->flags & RW_CONSTRUCTING)) 5447 { 5448 _objc_inform("objc_registerClassPair: class '%s' was not " 5449 "allocated with objc_allocateClassPair!", 5450 cls->data()->ro->name); 5451 rwlock_unlock_write(&runtimeLock); 5452 return; 5453 } 5454 5455 // Build ivar layouts 5456 if (UseGC) { 5457 Class supercls = cls->superclass; 5458 class_ro_t *ro_w = (class_ro_t *)cls->data()->ro; 5459 5460 if (ro_w->ivarLayout != &UnsetLayout) { 5461 // Class builder already called class_setIvarLayout. 5462 } 5463 else if (!supercls) { 5464 // Root class. Scan conservatively (should be isa ivar only). 5465 ro_w->ivarLayout = nil; 5466 } 5467 else if (ro_w->ivars == nil) { 5468 // No local ivars. Use superclass's layouts. 5469 ro_w->ivarLayout = 5470 _ustrdup_internal(supercls->data()->ro->ivarLayout); 5471 } 5472 else { 5473 // Has local ivars. Build layouts based on superclass. 5474 layout_bitmap bitmap = 5475 layout_bitmap_create(supercls->data()->ro->ivarLayout, 5476 supercls->unalignedInstanceSize(), 5477 cls->unalignedInstanceSize(), NO); 5478 uint32_t i; 5479 for (i = 0; i < ro_w->ivars->count; i++) { 5480 ivar_t *ivar = ivar_list_nth(ro_w->ivars, i); 5481 if (!ivar->offset) continue; // anonymous bitfield 5482 5483 layout_bitmap_set_ivar(bitmap, ivar->type, *ivar->offset); 5484 } 5485 ro_w->ivarLayout = layout_string_create(bitmap); 5486 layout_bitmap_free(bitmap); 5487 } 5488 5489 if (ro_w->weakIvarLayout != &UnsetLayout) { 5490 // Class builder already called class_setWeakIvarLayout. 5491 } 5492 else if (!supercls) { 5493 // Root class. No weak ivars (should be isa ivar only). 5494 ro_w->weakIvarLayout = nil; 5495 } 5496 else if (ro_w->ivars == nil) { 5497 // No local ivars. Use superclass's layout. 5498 ro_w->weakIvarLayout = 5499 _ustrdup_internal(supercls->data()->ro->weakIvarLayout); 5500 } 5501 else { 5502 // Has local ivars. Build layout based on superclass. 5503 // No way to add weak ivars yet. 5504 ro_w->weakIvarLayout = 5505 _ustrdup_internal(supercls->data()->ro->weakIvarLayout); 5506 } 5507 } 5508 5509 // Clear "under construction" bit, set "done constructing" bit 5510 cls->data()->flags &= ~RW_CONSTRUCTING; 5511 cls->ISA()->data()->flags &= ~RW_CONSTRUCTING; 5512 cls->data()->flags |= RW_CONSTRUCTED; 5513 cls->ISA()->data()->flags |= RW_CONSTRUCTED; 5514 5515 // Add to named and realized classes 5516 addNamedClass(cls, cls->data()->ro->name); 5517 addRealizedClass(cls); 5518 addRealizedMetaclass(cls->ISA()); 5519 addNonMetaClass(cls); 5520 5521 rwlock_unlock_write(&runtimeLock); 5522} 5523 5524 5525/*********************************************************************** 5526* detach_class 5527* Disconnect a class from other data structures. 5528* Exception: does not remove the class from the +load list 5529* Call this before free_class. 5530* Locking: runtimeLock must be held by the caller. 5531**********************************************************************/ 5532static void detach_class(Class cls, BOOL isMeta) 5533{ 5534 rwlock_assert_writing(&runtimeLock); 5535 5536 // categories not yet attached to this class 5537 category_list *cats; 5538 cats = unattachedCategoriesForClass(cls); 5539 if (cats) free(cats); 5540 5541 // superclass's subclass list 5542 if (cls->isRealized()) { 5543 Class supercls = cls->superclass; 5544 if (supercls) { 5545 removeSubclass(supercls, cls); 5546 } 5547 } 5548 5549 // class tables and +load queue 5550 if (!isMeta) { 5551 removeNamedClass(cls, cls->name()); 5552 removeRealizedClass(cls); 5553 removeNonMetaClass(cls); 5554 } else { 5555 removeRealizedMetaclass(cls); 5556 } 5557} 5558 5559 5560/*********************************************************************** 5561* free_class 5562* Frees a class's data structures. 5563* Call this after detach_class. 5564* Locking: runtimeLock must be held by the caller 5565**********************************************************************/ 5566static void free_class(Class cls) 5567{ 5568 rwlock_assert_writing(&runtimeLock); 5569 5570 if (! cls->isRealized()) return; 5571 5572 uint32_t i; 5573 5574 if (cls->cache.buckets != (bucket_t *)&_objc_empty_cache) { 5575 free(cls->cache.buckets); 5576 } 5577 5578 FOREACH_METHOD_LIST(mlist, cls, { 5579 for (i = 0; i < mlist->count; i++) { 5580 method_t *m = method_list_nth(mlist, i); 5581 try_free(m->types); 5582 } 5583 try_free(mlist); 5584 }); 5585 if (cls->data()->flags & RW_METHOD_ARRAY) { 5586 try_free(cls->data()->method_lists); 5587 } 5588 5589 const ivar_list_t *ilist = cls->data()->ro->ivars; 5590 if (ilist) { 5591 for (i = 0; i < ilist->count; i++) { 5592 const ivar_t *ivar = ivar_list_nth(ilist, i); 5593 try_free(ivar->offset); 5594 try_free(ivar->name); 5595 try_free(ivar->type); 5596 } 5597 try_free(ilist); 5598 } 5599 5600 const protocol_list_t **plistp; 5601 for (plistp = cls->data()->protocols; plistp && *plistp; plistp++) { 5602 try_free(*plistp); 5603 } 5604 try_free(cls->data()->protocols); 5605 5606 const chained_property_list *proplist = cls->data()->properties; 5607 while (proplist) { 5608 for (i = 0; i < proplist->count; i++) { 5609 const property_t *prop = proplist->list+i; 5610 try_free(prop->name); 5611 try_free(prop->attributes); 5612 } 5613 { 5614 const chained_property_list *temp = proplist; 5615 proplist = proplist->next; 5616 try_free(temp); 5617 } 5618 } 5619 5620 try_free(cls->data()->ro->ivarLayout); 5621 try_free(cls->data()->ro->weakIvarLayout); 5622 try_free(cls->data()->ro->name); 5623 try_free(cls->data()->ro); 5624 try_free(cls->data()); 5625 try_free(cls); 5626} 5627 5628 5629void objc_disposeClassPair(Class cls) 5630{ 5631 rwlock_write(&runtimeLock); 5632 5633 if (!(cls->data()->flags & (RW_CONSTRUCTED|RW_CONSTRUCTING)) || 5634 !(cls->ISA()->data()->flags & (RW_CONSTRUCTED|RW_CONSTRUCTING))) 5635 { 5636 // class not allocated with objc_allocateClassPair 5637 // disposing still-unregistered class is OK! 5638 _objc_inform("objc_disposeClassPair: class '%s' was not " 5639 "allocated with objc_allocateClassPair!", 5640 cls->data()->ro->name); 5641 rwlock_unlock_write(&runtimeLock); 5642 return; 5643 } 5644 5645 if (cls->isMetaClass()) { 5646 _objc_inform("objc_disposeClassPair: class '%s' is a metaclass, " 5647 "not a class!", cls->data()->ro->name); 5648 rwlock_unlock_write(&runtimeLock); 5649 return; 5650 } 5651 5652 // Shouldn't have any live subclasses. 5653 if (cls->data()->firstSubclass) { 5654 _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, " 5655 "including '%s'!", cls->data()->ro->name, 5656 cls->data()->firstSubclass->name()); 5657 } 5658 if (cls->ISA()->data()->firstSubclass) { 5659 _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, " 5660 "including '%s'!", cls->data()->ro->name, 5661 cls->ISA()->data()->firstSubclass->name()); 5662 } 5663 5664 // don't remove_class_from_loadable_list() 5665 // - it's not there and we don't have the lock 5666 detach_class(cls->ISA(), YES); 5667 detach_class(cls, NO); 5668 free_class(cls->ISA()); 5669 free_class(cls); 5670 5671 rwlock_unlock_write(&runtimeLock); 5672} 5673 5674 5675/*********************************************************************** 5676* class_createInstance 5677* fixme 5678* Locking: none 5679**********************************************************************/ 5680static id 5681_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone) 5682 __attribute__((always_inline)); 5683 5684static id 5685_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone) 5686{ 5687 if (!cls) return nil; 5688 5689 assert(cls->isRealized()); 5690 5691 size_t size = cls->alignedInstanceSize() + extraBytes; 5692 5693 // CF requires all object be at least 16 bytes. 5694 if (size < 16) size = 16; 5695 5696 id obj; 5697#if SUPPORT_GC 5698 if (UseGC) { 5699 obj = (id)auto_zone_allocate_object(gc_zone, size, 5700 AUTO_OBJECT_SCANNED, 0, 1); 5701 } else 5702#endif 5703 if (zone) { 5704 obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size); 5705 } else { 5706 obj = (id)calloc(1, size); 5707 } 5708 if (!obj) return nil; 5709 5710 obj->initIsa(cls); 5711 5712 if (cls->hasCxxCtor()) { 5713 obj = _objc_constructOrFree(cls, obj); 5714 } 5715 5716 return obj; 5717} 5718 5719 5720id 5721class_createInstance(Class cls, size_t extraBytes) 5722{ 5723 return _class_createInstanceFromZone(cls, extraBytes, nil); 5724} 5725 5726/*********************************************************************** 5727* class_createInstances 5728* fixme 5729* Locking: none 5730**********************************************************************/ 5731unsigned 5732class_createInstances(Class cls, size_t extraBytes, 5733 id *results, unsigned num_requested) 5734{ 5735 return _class_createInstancesFromZone(cls, extraBytes, nil, 5736 results, num_requested); 5737} 5738 5739static BOOL classOrSuperClassesUseARR(Class cls) { 5740 while (cls) { 5741 if (_class_usesAutomaticRetainRelease(cls)) return true; 5742 cls = cls->superclass; 5743 } 5744 return false; 5745} 5746 5747static void arr_fixup_copied_references(id newObject, id oldObject) 5748{ 5749 // use ARR layouts to correctly copy the references from old object to new, both strong and weak. 5750 Class cls = oldObject->ISA(); 5751 for ( ; cls; cls = cls->superclass) { 5752 if (_class_usesAutomaticRetainRelease(cls)) { 5753 // FIXME: align the instance start to nearest id boundary. This currently handles the case where 5754 // the the compiler folds a leading BOOL (char, short, etc.) into the alignment slop of a superclass. 5755 size_t instanceStart = _class_getInstanceStart(cls); 5756 const uint8_t *strongLayout = class_getIvarLayout(cls); 5757 if (strongLayout) { 5758 id *newPtr = (id *)((char*)newObject + instanceStart); 5759 unsigned char byte; 5760 while ((byte = *strongLayout++)) { 5761 unsigned skips = (byte >> 4); 5762 unsigned scans = (byte & 0x0F); 5763 newPtr += skips; 5764 while (scans--) { 5765 // ensure strong references are properly retained. 5766 id value = *newPtr++; 5767 if (value) objc_retain(value); 5768 } 5769 } 5770 } 5771 const uint8_t *weakLayout = class_getWeakIvarLayout(cls); 5772 // fix up weak references if any. 5773 if (weakLayout) { 5774 id *newPtr = (id *)((char*)newObject + instanceStart), *oldPtr = (id *)((char*)oldObject + instanceStart); 5775 unsigned char byte; 5776 while ((byte = *weakLayout++)) { 5777 unsigned skips = (byte >> 4); 5778 unsigned weaks = (byte & 0x0F); 5779 newPtr += skips, oldPtr += skips; 5780 while (weaks--) { 5781 *newPtr = nil; 5782 objc_storeWeak(newPtr, objc_loadWeak(oldPtr)); 5783 ++newPtr, ++oldPtr; 5784 } 5785 } 5786 } 5787 } 5788 } 5789} 5790 5791/*********************************************************************** 5792* object_copyFromZone 5793* fixme 5794* Locking: none 5795**********************************************************************/ 5796static id 5797_object_copyFromZone(id oldObj, size_t extraBytes, void *zone) 5798{ 5799 id obj; 5800 size_t size; 5801 5802 if (!oldObj) return nil; 5803 if (oldObj->isTaggedPointer()) return oldObj; 5804 5805 size = oldObj->ISA()->alignedInstanceSize() + extraBytes; 5806#if SUPPORT_GC 5807 if (UseGC) { 5808 obj = (id) auto_zone_allocate_object(gc_zone, size, 5809 AUTO_OBJECT_SCANNED, 0, 1); 5810 } else 5811#endif 5812 if (zone) { 5813 obj = (id) malloc_zone_calloc((malloc_zone_t *)zone, size, 1); 5814 } else { 5815 obj = (id) calloc(1, size); 5816 } 5817 if (!obj) return nil; 5818 5819 // fixme this doesn't handle C++ ivars correctly (#4619414) 5820 objc_memmove_collectable(obj, oldObj, size); 5821 5822#if SUPPORT_GC 5823 if (UseGC) 5824 gc_fixup_weakreferences(obj, oldObj); 5825 else if (classOrSuperClassesUseARR(obj->ISA())) 5826 arr_fixup_copied_references(obj, oldObj); 5827#else 5828 if (classOrSuperClassesUseARR(obj->ISA())) 5829 arr_fixup_copied_references(obj, oldObj); 5830#endif 5831 5832 return obj; 5833} 5834 5835 5836/*********************************************************************** 5837* object_copy 5838* fixme 5839* Locking: none 5840**********************************************************************/ 5841id 5842object_copy(id oldObj, size_t extraBytes) 5843{ 5844 return _object_copyFromZone(oldObj, extraBytes, malloc_default_zone()); 5845} 5846 5847 5848#if !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE) 5849 5850/*********************************************************************** 5851* class_createInstanceFromZone 5852* fixme 5853* Locking: none 5854**********************************************************************/ 5855id 5856class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone) 5857{ 5858 return _class_createInstanceFromZone(cls, extraBytes, zone); 5859} 5860 5861/*********************************************************************** 5862* object_copyFromZone 5863* fixme 5864* Locking: none 5865**********************************************************************/ 5866id 5867object_copyFromZone(id oldObj, size_t extraBytes, void *zone) 5868{ 5869 return _object_copyFromZone(oldObj, extraBytes, zone); 5870} 5871 5872#endif 5873 5874 5875/*********************************************************************** 5876* objc_destructInstance 5877* Destroys an instance without freeing memory. 5878* Calls C++ destructors. 5879* Calls ARR ivar cleanup. 5880* Removes associative references. 5881* Returns `obj`. Does nothing if `obj` is nil. 5882* Be warned that GC DOES NOT CALL THIS. If you edit this, also edit finalize. 5883* CoreFoundation and other clients do call this under GC. 5884**********************************************************************/ 5885void *objc_destructInstance(id obj) 5886{ 5887 if (obj) { 5888 Class cls = obj->getIsa(); 5889 5890 // Read all of the flags at once for performance. 5891 bool cxx = cls->hasCxxDtor(); 5892 bool assoc = !UseGC && cls->instancesHaveAssociatedObjects(); 5893 5894 // This order is important. 5895 if (cxx) object_cxxDestruct(obj); 5896 if (assoc) _object_remove_assocations(obj); 5897 5898 if (!UseGC) objc_clear_deallocating(obj); 5899 } 5900 5901 return obj; 5902} 5903 5904 5905/*********************************************************************** 5906* object_dispose 5907* fixme 5908* Locking: none 5909**********************************************************************/ 5910id 5911object_dispose(id obj) 5912{ 5913 if (!obj) return nil; 5914 5915 objc_destructInstance(obj); 5916 5917#if SUPPORT_GC 5918 if (UseGC) { 5919 auto_zone_retain(gc_zone, obj); // gc free expects rc==1 5920 } 5921#endif 5922 5923 free(obj); 5924 5925 return nil; 5926} 5927 5928 5929/*********************************************************************** 5930* _objc_getFreedObjectClass 5931* fixme 5932* Locking: none 5933**********************************************************************/ 5934Class _objc_getFreedObjectClass (void) 5935{ 5936 return nil; 5937} 5938 5939 5940 5941/*********************************************************************** 5942* Tagged pointer objects. 5943* 5944* Tagged pointer objects store the class and the object value in the 5945* object pointer; the "pointer" does not actually point to anything. 5946* 5947* Tagged pointer objects currently use this representation: 5948* (LSB) 5949* 1 bit set if tagged, clear if ordinary object pointer 5950* 3 bits tag index 5951* 60 bits payload 5952* (MSB) 5953* The tag index defines the object's class. 5954* The payload format is defined by the object's class. 5955* 5956* This representation is subject to change. Representation-agnostic SPI is: 5957* objc-internal.h for class implementers. 5958* objc-gdb.h for debuggers. 5959**********************************************************************/ 5960#if !SUPPORT_TAGGED_POINTERS 5961 5962// These variables are always provided for debuggers. 5963uintptr_t objc_debug_taggedpointer_mask = 0; 5964unsigned objc_debug_taggedpointer_slot_shift = 0; 5965uintptr_t objc_debug_taggedpointer_slot_mask = 0; 5966unsigned objc_debug_taggedpointer_payload_lshift = 0; 5967unsigned objc_debug_taggedpointer_payload_rshift = 0; 5968Class objc_debug_taggedpointer_classes[1] = { nil }; 5969 5970static void 5971disableTaggedPointers() { } 5972 5973#else 5974 5975// The "slot" used in the class table and given to the debugger 5976// includes the is-tagged bit. This makes objc_msgSend faster. 5977 5978uintptr_t objc_debug_taggedpointer_mask = TAG_MASK; 5979unsigned objc_debug_taggedpointer_slot_shift = TAG_SLOT_SHIFT; 5980uintptr_t objc_debug_taggedpointer_slot_mask = TAG_SLOT_MASK; 5981unsigned objc_debug_taggedpointer_payload_lshift = TAG_PAYLOAD_LSHIFT; 5982unsigned objc_debug_taggedpointer_payload_rshift = TAG_PAYLOAD_RSHIFT; 5983// objc_debug_taggedpointer_classes is defined in objc-msg-*.s 5984 5985static void 5986disableTaggedPointers() 5987{ 5988 objc_debug_taggedpointer_mask = 0; 5989 objc_debug_taggedpointer_slot_shift = 0; 5990 objc_debug_taggedpointer_slot_mask = 0; 5991 objc_debug_taggedpointer_payload_lshift = 0; 5992 objc_debug_taggedpointer_payload_rshift = 0; 5993} 5994 5995static int 5996tagSlotForTagIndex(objc_tag_index_t tag) 5997{ 5998#if TAG_MASK == 1 5999 return (tag << 1) | 1; 6000#else 6001# error unimplemented 6002#endif 6003} 6004 6005 6006/*********************************************************************** 6007* _objc_registerTaggedPointerClass 6008* Set the class to use for the given tagged pointer index. 6009* Aborts if the tag is out of range, or if the tag is already 6010* used by some other class. 6011**********************************************************************/ 6012void 6013_objc_registerTaggedPointerClass(objc_tag_index_t tag, Class cls) 6014{ 6015 if (objc_debug_taggedpointer_mask == 0) { 6016 _objc_fatal("tagged pointers are disabled"); 6017 } 6018 6019 if ((unsigned int)tag >= TAG_COUNT) { 6020 _objc_fatal("tag index %u is too large.", tag); 6021 } 6022 6023 int slot = tagSlotForTagIndex(tag); 6024 Class oldCls = objc_tag_classes[slot]; 6025 6026 if (cls && oldCls && cls != oldCls) { 6027 _objc_fatal("tag index %u used for two different classes " 6028 "(was %p %s, now %p %s)", tag, 6029 oldCls, class_getName(oldCls), cls, class_getName(cls)); 6030 } 6031 6032 objc_tag_classes[slot] = cls; 6033} 6034 6035 6036// Deprecated name. 6037void _objc_insert_tagged_isa(unsigned char slotNumber, Class isa) 6038{ 6039 return _objc_registerTaggedPointerClass((objc_tag_index_t)slotNumber, isa); 6040} 6041 6042 6043/*********************************************************************** 6044* _objc_getClassForTag 6045* Returns the class that is using the given tagged pointer tag. 6046* Returns nil if no class is using that tag or the tag is out of range. 6047**********************************************************************/ 6048Class 6049_objc_getClassForTag(objc_tag_index_t tag) 6050{ 6051 if ((unsigned int)tag >= TAG_COUNT) return nil; 6052 return objc_tag_classes[tagSlotForTagIndex(tag)]; 6053} 6054 6055#endif 6056 6057 6058#if SUPPORT_FIXUP 6059 6060OBJC_EXTERN void objc_msgSend_fixedup(void); 6061OBJC_EXTERN void objc_msgSendSuper2_fixedup(void); 6062OBJC_EXTERN void objc_msgSend_stret_fixedup(void); 6063OBJC_EXTERN void objc_msgSendSuper2_stret_fixedup(void); 6064#if defined(__i386__) || defined(__x86_64__) 6065OBJC_EXTERN void objc_msgSend_fpret_fixedup(void); 6066#endif 6067#if defined(__x86_64__) 6068OBJC_EXTERN void objc_msgSend_fp2ret_fixedup(void); 6069#endif 6070 6071/*********************************************************************** 6072* fixupMessageRef 6073* Repairs an old vtable dispatch call site. 6074* vtable dispatch itself is not supported. 6075**********************************************************************/ 6076static void 6077fixupMessageRef(message_ref_t *msg) 6078{ 6079 msg->sel = sel_registerName((const char *)msg->sel); 6080 6081 if (ignoreSelector(msg->sel)) { 6082 // ignored selector - bypass dispatcher 6083 msg->imp = (IMP)&_objc_ignored_method; 6084 } 6085 else if (msg->imp == &objc_msgSend_fixup) { 6086 msg->imp = &objc_msgSend_fixedup; 6087 } 6088 else if (msg->imp == &objc_msgSendSuper2_fixup) { 6089 msg->imp = &objc_msgSendSuper2_fixedup; 6090 } 6091 else if (msg->imp == &objc_msgSend_stret_fixup) { 6092 msg->imp = &objc_msgSend_stret_fixedup; 6093 } 6094 else if (msg->imp == &objc_msgSendSuper2_stret_fixup) { 6095 msg->imp = &objc_msgSendSuper2_stret_fixedup; 6096 } 6097#if defined(__i386__) || defined(__x86_64__) 6098 else if (msg->imp == &objc_msgSend_fpret_fixup) { 6099 msg->imp = &objc_msgSend_fpret_fixedup; 6100 } 6101#endif 6102#if defined(__x86_64__) 6103 else if (msg->imp == &objc_msgSend_fp2ret_fixup) { 6104 msg->imp = &objc_msgSend_fp2ret_fixedup; 6105 } 6106#endif 6107} 6108 6109// SUPPORT_FIXUP 6110#endif 6111 6112 6113// ProKit SPI 6114static Class setSuperclass(Class cls, Class newSuper) 6115{ 6116 Class oldSuper; 6117 6118 rwlock_assert_writing(&runtimeLock); 6119 6120 assert(cls->isRealized()); 6121 assert(newSuper->isRealized()); 6122 6123 oldSuper = cls->superclass; 6124 removeSubclass(oldSuper, cls); 6125 removeSubclass(oldSuper->ISA(), cls->ISA()); 6126 6127 cls->superclass = newSuper; 6128 cls->ISA()->superclass = newSuper->ISA(); 6129 addSubclass(newSuper, cls); 6130 addSubclass(newSuper->ISA(), cls->ISA()); 6131 6132 // Flush subclass's method caches. 6133 // If subclass is not yet +initialized then its cache will be empty. 6134 // Otherwise this is very slow for sel-side caches. 6135 if (cls->isInitialized() || cls->ISA()->isInitialized()) { 6136 flushCaches(cls); 6137 } 6138 6139 return oldSuper; 6140} 6141 6142 6143Class class_setSuperclass(Class cls, Class newSuper) 6144{ 6145 Class oldSuper; 6146 6147 rwlock_write(&runtimeLock); 6148 oldSuper = setSuperclass(cls, newSuper); 6149 rwlock_unlock_write(&runtimeLock); 6150 6151 return oldSuper; 6152} 6153 6154#endif 6155