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