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