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