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