2 * Copyright (c) 2005-2008 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 /***********************************************************************
26 * Support for new-ABI classes and images.
27 **********************************************************************/
31 #include "objc-private.h"
32 #include "objc-runtime-new.h"
33 #include <objc/message.h>
35 #define newcls(cls) ((struct class_t *)cls)
36 #define newcat(cat) ((struct category_t *)cat)
37 #define newmethod(meth) ((struct method_t *)meth)
38 #define newivar(ivar) ((struct ivar_t *)ivar)
39 #define newcategory(cat) ((struct category_t *)cat)
40 #define newprotocol(p) ((struct protocol_t *)p)
43 #define WORD_SHIFT 3UL
46 #define WORD_SHIFT 2UL
50 static const char *getName(struct class_t *cls);
51 static uint32_t instanceSize(struct class_t *cls);
52 static BOOL isMetaClass(struct class_t *cls);
53 static struct class_t *getSuperclass(struct class_t *cls);
54 static void unload_class(class_t *cls, BOOL isMeta);
55 static class_t *setSuperclass(class_t *cls, class_t *newSuper);
56 static class_t *realizeClass(class_t *cls);
57 static void flushCaches(class_t *cls);
58 static void flushVtables(class_t *cls);
59 static method_t *getMethodNoSuper_nolock(struct class_t *cls, SEL sel);
60 static method_t *getMethod_nolock(class_t *cls, SEL sel);
61 static void changeInfo(class_t *cls, unsigned int set, unsigned int clear);
62 static IMP _method_getImplementation(method_t *m);
65 /***********************************************************************
67 * Every lock used anywhere must be managed here.
68 * Locks not managed here may cause gdb deadlocks.
69 **********************************************************************/
70 __private_extern__ rwlock_t runtimeLock = {0};
71 __private_extern__ rwlock_t selLock = {0};
72 __private_extern__ mutex_t cacheUpdateLock = MUTEX_INITIALIZER;
73 __private_extern__ recursive_mutex_t loadMethodLock = RECURSIVE_MUTEX_INITIALIZER;
74 static int debugger_runtimeLock;
75 static int debugger_selLock;
76 static int debugger_cacheUpdateLock;
77 static int debugger_loadMethodLock;
81 __private_extern__ void lock_init(void)
83 rwlock_init(&selLock);
84 rwlock_init(&runtimeLock);
85 recursive_mutex_init(&loadMethodLock);
89 /***********************************************************************
91 * Attempt to acquire some locks for debugger mode.
92 * Returns 0 if debugger mode failed because too many locks are unavailable.
94 * Locks successfully acquired are held until endDebuggerMode().
95 * Locks not acquired are off-limits until endDebuggerMode(); any
96 * attempt to manipulate them will cause a trap.
97 * Locks not handled here may cause deadlocks in gdb.
98 **********************************************************************/
99 __private_extern__ int startDebuggerMode(void)
101 int result = DEBUGGER_FULL;
103 // runtimeLock is required (can't do much without it)
104 if (rwlock_try_write(&runtimeLock)) {
105 debugger_runtimeLock = RDWR;
106 } else if (rwlock_try_read(&runtimeLock)) {
107 debugger_runtimeLock = RDONLY;
108 result = DEBUGGER_PARTIAL;
113 // cacheUpdateLock is required (must not fail a necessary cache flush)
114 // must be AFTER runtimeLock to avoid lock inversion
115 if (mutex_try_lock(&cacheUpdateLock)) {
116 debugger_cacheUpdateLock = RDWR;
118 rwlock_unlock(&runtimeLock, debugger_runtimeLock);
119 debugger_runtimeLock = 0;
123 // selLock is optional
124 if (rwlock_try_write(&selLock)) {
125 debugger_selLock = RDWR;
126 } else if (rwlock_try_read(&selLock)) {
127 debugger_selLock = RDONLY;
128 result = DEBUGGER_PARTIAL;
130 debugger_selLock = 0;
131 result = DEBUGGER_PARTIAL;
134 // loadMethodLock is optional
135 if (recursive_mutex_try_lock(&loadMethodLock)) {
136 debugger_loadMethodLock = RDWR;
138 debugger_loadMethodLock = 0;
139 result = DEBUGGER_PARTIAL;
145 /***********************************************************************
147 * Relinquish locks acquired in startDebuggerMode().
148 **********************************************************************/
149 __private_extern__ void endDebuggerMode(void)
151 assert(debugger_runtimeLock != 0);
153 rwlock_unlock(&runtimeLock, debugger_runtimeLock);
154 debugger_runtimeLock = 0;
156 rwlock_unlock(&selLock, debugger_selLock);
157 debugger_selLock = 0;
159 assert(debugger_cacheUpdateLock == RDWR);
160 mutex_unlock(&cacheUpdateLock);
161 debugger_cacheUpdateLock = 0;
163 if (debugger_loadMethodLock) {
164 recursive_mutex_unlock(&loadMethodLock);
165 debugger_loadMethodLock = 0;
169 /***********************************************************************
170 * isManagedDuringDebugger
171 * Returns YES if the given lock is handled specially during debugger
172 * mode (i.e. debugger mode tries to acquire it).
173 **********************************************************************/
174 __private_extern__ BOOL isManagedDuringDebugger(void *lock)
176 if (lock == &selLock) return YES;
177 if (lock == &cacheUpdateLock) return YES;
178 if (lock == &runtimeLock) return YES;
179 if (lock == &loadMethodLock) return YES;
183 /***********************************************************************
184 * isLockedDuringDebugger
185 * Returns YES if the given mutex was acquired by debugger mode.
186 * Locking a managed mutex during debugger mode causes a trap unless
188 **********************************************************************/
189 __private_extern__ BOOL isLockedDuringDebugger(mutex_t *lock)
191 assert(DebuggerMode);
193 if (lock == &cacheUpdateLock) return YES;
194 if (lock == (mutex_t *)&loadMethodLock) return YES;
199 /***********************************************************************
200 * isReadingDuringDebugger
201 * Returns YES if the given rwlock was read-locked by debugger mode.
202 * Read-locking a managed rwlock during debugger mode causes a trap unless
204 **********************************************************************/
205 __private_extern__ BOOL isReadingDuringDebugger(rwlock_t *lock)
207 assert(DebuggerMode);
209 // read-lock is allowed even if debugger mode actually write-locked it
210 if (debugger_runtimeLock && lock == &runtimeLock) return YES;
211 if (debugger_selLock && lock == &selLock) return YES;
216 /***********************************************************************
217 * isWritingDuringDebugger
218 * Returns YES if the given rwlock was write-locked by debugger mode.
219 * Write-locking a managed rwlock during debugger mode causes a trap unless
221 **********************************************************************/
222 __private_extern__ BOOL isWritingDuringDebugger(rwlock_t *lock)
224 assert(DebuggerMode);
226 if (debugger_runtimeLock == RDWR && lock == &runtimeLock) return YES;
227 if (debugger_selLock == RDWR && lock == &selLock) return YES;
233 /***********************************************************************
236 * Every class gets a vtable pointer. The vtable is an array of IMPs.
237 * The selectors represented in the vtable are the same for all classes
238 * (i.e. no class has a bigger or smaller vtable).
239 * Each vtable index has an associated trampoline which dispatches to
240 * the IMP at that index for the receiver class's vtable (after
241 * checking for NULL). Dispatch fixup uses these trampolines instead
243 * Fragility: The vtable size and list of selectors is chosen at launch
244 * time. No compiler-generated code depends on any particular vtable
245 * configuration, or even the use of vtable dispatch at all.
246 * Memory size: If a class's vtable is identical to its superclass's
247 * (i.e. the class overrides none of the vtable selectors), then
248 * the class points directly to its superclass's vtable. This means
249 * selectors to be included in the vtable should be chosen so they are
250 * (1) frequently called, but (2) not too frequently overridden. In
251 * particular, -dealloc is a bad choice.
252 * Forwarding: If a class doesn't implement some vtable selector, that
253 * selector's IMP is set to objc_msgSend in that class's vtable.
254 * +initialize: Each class keeps the default vtable (which always
255 * redirects to objc_msgSend) until its +initialize is completed.
256 * Otherwise, the first message to a class could be a vtable dispatch,
257 * and the vtable trampoline doesn't include +initialize checking.
258 * Changes: Categories, addMethod, and setImplementation all force vtable
259 * reconstruction for the class and all of its subclasses, if the
260 * vtable selectors are affected.
261 **********************************************************************/
264 x, x, x, x, x, x, x, x
266 X8(x), X8(x), X8(x), X8(x), X8(x), X8(x), X8(x), X8(x)
270 #define vtableMax 128
272 IMP _objc_empty_vtable[vtableMax] = {
278 // Trampoline descriptors for gdb.
280 objc_trampoline_header *gdb_objc_trampolines = NULL;
282 void gdb_objc_trampolines_changed(objc_trampoline_header *thdr) __attribute__((noinline));
283 void gdb_objc_trampolines_changed(objc_trampoline_header *thdr)
285 rwlock_assert_writing(&runtimeLock);
286 assert(thdr == gdb_objc_trampolines);
289 _objc_inform("VTABLES: gdb_objc_trampolines_changed(%p)", thdr);
293 // fixme workaround for rdar://6667753
294 static void appendTrampolines(objc_trampoline_header *thdr) __attribute__((noinline));
296 static void appendTrampolines(objc_trampoline_header *thdr)
298 rwlock_assert_writing(&runtimeLock);
299 assert(thdr->next == NULL);
301 if (gdb_objc_trampolines != thdr->next) {
302 thdr->next = gdb_objc_trampolines;
304 gdb_objc_trampolines = thdr;
306 gdb_objc_trampolines_changed(thdr);
309 // Vtable management.
311 static size_t vtableStrlen;
312 static size_t vtableCount;
313 static SEL *vtableSelectors;
314 static IMP *vtableTrampolines;
315 static const char * const defaultVtable[] = {
321 "respondsToSelector:",
333 static const char * const defaultVtableGC[] = {
339 "respondsToSelector:",
349 "countByEnumeratingWithState:objects:count:",
352 extern id objc_msgSend_vtable0(id, SEL, ...);
353 extern id objc_msgSend_vtable1(id, SEL, ...);
354 extern id objc_msgSend_vtable2(id, SEL, ...);
355 extern id objc_msgSend_vtable3(id, SEL, ...);
356 extern id objc_msgSend_vtable4(id, SEL, ...);
357 extern id objc_msgSend_vtable5(id, SEL, ...);
358 extern id objc_msgSend_vtable6(id, SEL, ...);
359 extern id objc_msgSend_vtable7(id, SEL, ...);
360 extern id objc_msgSend_vtable8(id, SEL, ...);
361 extern id objc_msgSend_vtable9(id, SEL, ...);
362 extern id objc_msgSend_vtable10(id, SEL, ...);
363 extern id objc_msgSend_vtable11(id, SEL, ...);
364 extern id objc_msgSend_vtable12(id, SEL, ...);
365 extern id objc_msgSend_vtable13(id, SEL, ...);
366 extern id objc_msgSend_vtable14(id, SEL, ...);
367 extern id objc_msgSend_vtable15(id, SEL, ...);
369 static IMP const defaultVtableTrampolines[] = {
370 objc_msgSend_vtable0,
371 objc_msgSend_vtable1,
372 objc_msgSend_vtable2,
373 objc_msgSend_vtable3,
374 objc_msgSend_vtable4,
375 objc_msgSend_vtable5,
376 objc_msgSend_vtable6,
377 objc_msgSend_vtable7,
378 objc_msgSend_vtable8,
379 objc_msgSend_vtable9,
380 objc_msgSend_vtable10,
381 objc_msgSend_vtable11,
382 objc_msgSend_vtable12,
383 objc_msgSend_vtable13,
384 objc_msgSend_vtable14,
385 objc_msgSend_vtable15,
387 extern objc_trampoline_header defaultVtableTrampolineDescriptors;
389 static void check_vtable_size(void) __unused;
390 static void check_vtable_size(void)
392 // Fail to compile if vtable sizes don't match.
393 int c1[sizeof(defaultVtableTrampolines)-sizeof(defaultVtable)] __unused;
394 int c2[sizeof(defaultVtable)-sizeof(defaultVtableTrampolines)] __unused;
395 int c3[sizeof(defaultVtableTrampolines)-sizeof(defaultVtableGC)] __unused;
396 int c4[sizeof(defaultVtableGC)-sizeof(defaultVtableTrampolines)] __unused;
398 // Fail to compile if vtableMax is too small
399 int c5[vtableMax - sizeof(defaultVtable)] __unused;
400 int c6[vtableMax - sizeof(defaultVtableGC)] __unused;
406 monomorphic (self rdi, sel* rsi, temp r10 and r11) {
408 jeq returnZero // nil check
409 movq 8(%rsi), %rsi // load _cmd (fixme schedule)
411 cmp 0(%rdi), %r10 // isa check
412 jeq imp // fixme long branches
414 cmp 0(%rdi), %r10 // fixme load rdi once for multiple isas
415 jeq imp2 // fixme long branches
416 jmp objc_msgSend // fixme long branches
420 extern uint8_t vtable_prototype;
421 extern uint8_t vtable_ignored;
422 extern int vtable_prototype_size;
423 extern int vtable_prototype_index_offset;
424 static size_t makeVtableTrampoline(uint8_t *dst, size_t index)
427 memcpy(dst, &vtable_prototype, vtable_prototype_size);
430 #if defined(__x86_64__)
431 uint16_t *p = (uint16_t *)(dst + vtable_prototype_index_offset + 3);
432 if (*p != 0x7fff) _objc_fatal("vtable_prototype busted");
435 # warning unknown architecture
438 return vtable_prototype_size;
442 static void initVtables(void)
444 if (DisableVtables) {
446 _objc_inform("VTABLES: vtable dispatch disabled by OBJC_DISABLE_VTABLES");
449 vtableSelectors = NULL;
450 vtableTrampolines = NULL;
454 const char * const *names;
458 names = defaultVtableGC;
459 vtableCount = sizeof(defaultVtableGC) / sizeof(defaultVtableGC[0]);
461 names = defaultVtable;
462 vtableCount = sizeof(defaultVtable) / sizeof(defaultVtable[0]);
464 if (vtableCount > vtableMax) vtableCount = vtableMax;
466 vtableSelectors = _malloc_internal(vtableCount * sizeof(SEL));
467 vtableTrampolines = _malloc_internal(vtableCount * sizeof(IMP));
469 // Built-in trampolines and their descriptors
471 size_t defaultVtableTrampolineCount =
472 sizeof(defaultVtableTrampolines) / sizeof(defaultVtableTrampolines[0]);
474 // debug: use generated code for 3/4 of the table
475 defaultVtableTrampolineCount /= 4;
478 for (i = 0; i < defaultVtableTrampolineCount && i < vtableCount; i++) {
479 vtableSelectors[i] = sel_registerName(names[i]);
480 vtableTrampolines[i] = defaultVtableTrampolines[i];
482 appendTrampolines(&defaultVtableTrampolineDescriptors);
485 // Generated trampolines and their descriptors
487 if (vtableCount > defaultVtableTrampolineCount) {
488 // Memory for trampoline code
489 size_t generatedCount =
490 vtableCount - defaultVtableTrampolineCount;
492 const int align = 16;
494 round_page(sizeof(objc_trampoline_header) + align +
495 generatedCount * (sizeof(objc_trampoline_descriptor)
496 + vtable_prototype_size + align));
497 void *codeAddr = mmap(0, codeSize, PROT_READ|PROT_WRITE,
498 MAP_PRIVATE|MAP_ANON,
499 VM_MAKE_TAG(VM_MEMORY_OBJC_DISPATCHERS), 0);
500 uint8_t *t = (uint8_t *)codeAddr;
503 objc_trampoline_header *thdr = (objc_trampoline_header *)t;
504 thdr->headerSize = sizeof(objc_trampoline_header);
505 thdr->descSize = sizeof(objc_trampoline_descriptor);
506 thdr->descCount = (uint32_t)generatedCount;
509 // Trampoline descriptors
510 objc_trampoline_descriptor *tdesc = (objc_trampoline_descriptor *)(thdr+1);
511 t = (uint8_t *)&tdesc[generatedCount];
512 t += align - ((uintptr_t)t % align);
516 for (i = defaultVtableTrampolineCount, tdi = 0;
520 vtableSelectors[i] = sel_registerName(names[i]);
521 if (vtableSelectors[i] == (SEL)kIgnore) {
522 vtableTrampolines[i] = (IMP)&vtable_ignored;
523 tdesc[tdi].offset = 0;
524 tdesc[tdi].flags = 0;
526 vtableTrampolines[i] = (IMP)t;
528 (uint32_t)((uintptr_t)t - (uintptr_t)&tdesc[tdi]);
530 OBJC_TRAMPOLINE_MESSAGE|OBJC_TRAMPOLINE_VTABLE;
532 t += makeVtableTrampoline(t, i);
533 t += align - ((uintptr_t)t % align);
537 appendTrampolines(thdr);
538 sys_icache_invalidate(codeAddr, codeSize);
539 mprotect(codeAddr, codeSize, PROT_READ|PROT_EXEC);
544 for (i = 0; i < vtableCount; i++) {
545 _objc_inform("VTABLES: vtable[%zu] %p %s",
546 i, vtableTrampolines[i],
547 sel_getName(vtableSelectors[i]));
551 if (PrintVtableImages) {
552 _objc_inform("VTABLE IMAGES: '#' implemented by class");
553 _objc_inform("VTABLE IMAGES: '-' inherited from superclass");
554 _objc_inform("VTABLE IMAGES: ' ' not implemented");
555 for (i = 0; i <= vtableCount; i++) {
556 char spaces[vtableCount+1+1];
558 for (j = 0; j < i; j++) {
562 _objc_inform("VTABLE IMAGES: %s%s", spaces,
563 i<vtableCount ? sel_getName(vtableSelectors[i]) : "");
567 if (PrintVtables || PrintVtableImages) {
569 for (i = 0; i < vtableCount; i++) {
570 vtableStrlen += strlen(sel_getName(vtableSelectors[i]));
576 static int vtable_getIndex(SEL sel)
579 for (i = 0; i < vtableCount; i++) {
580 if (vtableSelectors[i] == sel) return i;
585 static BOOL vtable_containsSelector(SEL sel)
587 return (vtable_getIndex(sel) < 0) ? NO : YES;
590 static void printVtableOverrides(class_t *cls, class_t *supercls)
592 char overrideMap[vtableCount+1];
596 size_t overridesBufferSize = vtableStrlen + 2*vtableCount + 1;
598 _calloc_internal(overridesBufferSize, 1);
599 for (i = 0; i < vtableCount; i++) {
600 if (vtableSelectors[i] == (SEL)kIgnore) {
601 overrideMap[i] = '-';
604 if (getMethodNoSuper_nolock(cls, vtableSelectors[i])) {
605 strlcat(overrides, sel_getName(vtableSelectors[i]), overridesBufferSize);
606 strlcat(overrides, ", ", overridesBufferSize);
607 overrideMap[i] = '#';
608 } else if (getMethod_nolock(cls, vtableSelectors[i])) {
609 overrideMap[i] = '-';
611 overrideMap[i] = ' ';
615 _objc_inform("VTABLES: %s%s implements %s",
616 getName(cls), isMetaClass(cls) ? "(meta)" : "",
619 _free_internal(overrides);
622 for (i = 0; i < vtableCount; i++) {
623 overrideMap[i] = '#';
627 if (PrintVtableImages) {
628 overrideMap[vtableCount] = '\0';
629 _objc_inform("VTABLE IMAGES: %s %s%s", overrideMap,
630 getName(cls), isMetaClass(cls) ? "(meta)" : "");
634 /***********************************************************************
636 * Rebuilds vtable for cls, using superclass's vtable if appropriate.
637 * Assumes superclass's vtable is up to date.
638 * Does nothing to subclass vtables.
639 * Locking: runtimeLock must be held by the caller.
640 **********************************************************************/
641 static void updateVtable(class_t *cls, BOOL force)
643 rwlock_assert_writing(&runtimeLock);
645 // Keep default vtable until +initialize is complete.
646 // Default vtable redirects to objc_msgSend, which
647 // enforces +initialize locking.
648 if (!force && !_class_isInitialized((Class)cls)) {
651 _objc_inform("VTABLES: KEEPING DEFAULT vtable for "
652 "uninitialized class %s%s",
653 getName(cls), isMetaClass(cls) ? "(meta)" : "");
659 // Decide whether this class can share its superclass's vtable.
661 struct class_t *supercls = getSuperclass(cls);
662 BOOL needVtable = NO;
665 // Root classes always need a vtable
668 else if (cls->data->flags & RW_SPECIALIZED_VTABLE) {
669 // Once you have your own vtable, you never go back
673 for (i = 0; i < vtableCount; i++) {
674 if (vtableSelectors[i] == (SEL)kIgnore) continue;
675 method_t *m = getMethodNoSuper_nolock(cls, vtableSelectors[i]);
676 // assume any local implementation differs from super's
684 // Build a vtable for this class, or not.
688 _objc_inform("VTABLES: USING SUPERCLASS vtable for class %s%s",
689 getName(cls), isMetaClass(cls) ? "(meta)" : "");
691 cls->vtable = supercls->vtable;
695 _objc_inform("VTABLES: %s vtable for class %s%s",
696 (cls->data->flags & RW_SPECIALIZED_VTABLE) ?
697 "UPDATING SPECIALIZED" : "CREATING SPECIALIZED",
698 getName(cls), isMetaClass(cls) ? "(meta)" : "");
700 if (PrintVtables || PrintVtableImages) {
701 printVtableOverrides(cls, supercls);
704 IMP *new_vtable = cls->vtable;
705 IMP *super_vtable = supercls ? supercls->vtable : _objc_empty_vtable;
706 // fixme use msgForward (instead of msgSend from empty vtable) ?
708 if (cls->data->flags & RW_SPECIALIZED_VTABLE) {
709 // update cls->vtable in place
710 new_vtable = cls->vtable;
711 assert(new_vtable != _objc_empty_vtable);
714 new_vtable = malloc(vtableCount * sizeof(IMP));
715 changeInfo(cls, RW_SPECIALIZED_VTABLE, 0);
718 for (i = 0; i < vtableCount; i++) {
719 if (vtableSelectors[i] == (SEL)kIgnore) {
720 new_vtable[i] = (IMP)&vtable_ignored;
722 method_t *m = getMethodNoSuper_nolock(cls, vtableSelectors[i]);
723 if (m) new_vtable[i] = _method_getImplementation(m);
724 else new_vtable[i] = super_vtable[i];
728 if (cls->vtable != new_vtable) {
729 // don't let other threads see uninitialized parts of new_vtable
731 cls->vtable = new_vtable;
740 static void initVtables(void)
743 _objc_inform("VTABLES: no vtables on this architecture");
747 static BOOL vtable_containsSelector(SEL sel)
752 static void updateVtable(class_t *cls, BOOL force)
766 category_pair_t list[0]; // variable-size
769 #define FOREACH_METHOD_LIST(_mlist, _cls, code) \
771 const method_list_t *_mlist; \
772 if (_cls->data->methods) { \
773 method_list_t **_mlistp; \
774 for (_mlistp = _cls->data->methods; *_mlistp; _mlistp++) { \
782 // fixme don't chain property lists
783 typedef struct chained_property_list {
784 struct chained_property_list *next;
786 struct objc_property list[0]; // variable-size
787 } chained_property_list;
790 Low two bits of mlist->entsize is used as the fixed-up marker.
791 PREOPTIMIZED VERSION:
792 Fixed-up method lists get entsize&3 == 3.
793 dyld shared cache sets this for method lists it preoptimizes.
794 UN-PREOPTIMIZED VERSION:
795 Fixed-up method lists get entsize&3 == 1.
796 dyld shared cache uses 3, but those aren't trusted.
799 static uint32_t fixed_up_method_list = 3;
801 __private_extern__ void
802 disableSelectorPreoptimization(void)
804 fixed_up_method_list = 1;
807 static BOOL isMethodListFixedUp(const method_list_t *mlist)
809 return (mlist->entsize_NEVER_USE & 3) == fixed_up_method_list;
812 static void setMethodListFixedUp(method_list_t *mlist)
814 rwlock_assert_writing(&runtimeLock);
815 assert(!isMethodListFixedUp(mlist));
816 mlist->entsize_NEVER_USE = (mlist->entsize_NEVER_USE & ~3) | fixed_up_method_list;
820 static size_t chained_property_list_size(const chained_property_list *plist)
822 return sizeof(chained_property_list) +
823 plist->count * sizeof(struct objc_property);
826 static size_t protocol_list_size(const protocol_list_t *plist)
828 return sizeof(protocol_list_t) + plist->count * sizeof(protocol_t *);
832 // low bit used by dyld shared cache
833 static uint32_t method_list_entsize(const method_list_t *mlist)
835 return mlist->entsize_NEVER_USE & ~(uint32_t)3;
838 static size_t method_list_size(const method_list_t *mlist)
840 return sizeof(method_list_t) + (mlist->count-1)*method_list_entsize(mlist);
843 static method_t *method_list_nth(const method_list_t *mlist, uint32_t i)
845 return (method_t *)(i*method_list_entsize(mlist) + (char *)&mlist->first);
849 static size_t ivar_list_size(const ivar_list_t *ilist)
851 return sizeof(ivar_list_t) + (ilist->count-1) * ilist->entsize;
854 static ivar_t *ivar_list_nth(const ivar_list_t *ilist, uint32_t i)
856 return (ivar_t *)(i*ilist->entsize + (char *)&ilist->first);
860 static method_list_t *cat_method_list(const category_t *cat, BOOL isMeta)
862 if (!cat) return NULL;
864 if (isMeta) return cat->classMethods;
865 else return cat->instanceMethods;
868 static uint32_t cat_method_count(const category_t *cat, BOOL isMeta)
870 method_list_t *cmlist = cat_method_list(cat, isMeta);
871 return cmlist ? cmlist->count : 0;
874 static method_t *cat_method_nth(const category_t *cat, BOOL isMeta, uint32_t i)
876 method_list_t *cmlist = cat_method_list(cat, isMeta);
877 if (!cmlist) return NULL;
879 return method_list_nth(cmlist, i);
883 // part of ivar_t, with non-deprecated alignment
891 static uint32_t ivar_alignment(const ivar_t *ivar)
893 uint32_t alignment = ((ivar_alignment_t *)ivar)->alignment;
894 if (alignment == (uint32_t)-1) alignment = (uint32_t)WORD_SHIFT;
899 static void try_free(const void *p)
901 if (p && malloc_size(p)) free((void *)p);
905 /***********************************************************************
907 * Reallocates rw->ro if necessary to make it writeable.
908 * Locking: runtimeLock must be held by the caller.
909 **********************************************************************/
910 static class_ro_t *make_ro_writeable(class_rw_t *rw)
912 rwlock_assert_writing(&runtimeLock);
914 if (rw->flags & RW_COPIED_RO) {
915 // already writeable, do nothing
917 class_ro_t *ro = _memdup_internal(rw->ro, sizeof(*rw->ro));
919 rw->flags |= RW_COPIED_RO;
921 return (class_ro_t *)rw->ro;
925 /***********************************************************************
926 * unattachedCategories
927 * Returns the class => categories map of unattached categories.
928 * Locking: runtimeLock must be held by the caller.
929 **********************************************************************/
930 static NXMapTable *unattachedCategories(void)
932 rwlock_assert_writing(&runtimeLock);
934 static NXMapTable *category_map = NULL;
936 if (category_map) return category_map;
938 // fixme initial map size
939 category_map = NXCreateMapTableFromZone(NXPtrValueMapPrototype, 16,
940 _objc_internal_zone());
946 /***********************************************************************
947 * addUnattachedCategoryForClass
948 * Records an unattached category.
949 * Locking: runtimeLock must be held by the caller.
950 **********************************************************************/
951 static void addUnattachedCategoryForClass(category_t *cat, class_t *cls,
952 header_info *catHeader)
954 rwlock_assert_writing(&runtimeLock);
956 BOOL catFromBundle = (catHeader->mhdr->filetype == MH_BUNDLE) ? YES: NO;
958 // DO NOT use cat->cls!
959 // cls may be cat->cls->isa, or cat->cls may have been remapped.
960 NXMapTable *cats = unattachedCategories();
963 list = NXMapGet(cats, cls);
965 list = _calloc_internal(sizeof(*list) + sizeof(list->list[0]), 1);
967 list = _realloc_internal(list, sizeof(*list) + sizeof(list->list[0]) * (list->count + 1));
969 list->list[list->count++] = (category_pair_t){cat, catFromBundle};
970 NXMapInsert(cats, cls, list);
974 /***********************************************************************
975 * removeUnattachedCategoryForClass
976 * Removes an unattached category.
977 * Locking: runtimeLock must be held by the caller.
978 **********************************************************************/
979 static void removeUnattachedCategoryForClass(category_t *cat, class_t *cls)
981 rwlock_assert_writing(&runtimeLock);
983 // DO NOT use cat->cls!
984 // cls may be cat->cls->isa, or cat->cls may have been remapped.
985 NXMapTable *cats = unattachedCategories();
988 list = NXMapGet(cats, cls);
992 for (i = 0; i < list->count; i++) {
993 if (list->list[i].cat == cat) {
994 // shift entries to preserve list order
995 memmove(&list->list[i], &list->list[i+1],
996 (list->count-i-1) * sizeof(list->list[i]));
1004 /***********************************************************************
1005 * unattachedCategoriesForClass
1006 * Returns the list of unattached categories for a class, and
1007 * deletes them from the list.
1008 * The result must be freed by the caller.
1009 * Locking: runtimeLock must be held by the caller.
1010 **********************************************************************/
1011 static category_list *unattachedCategoriesForClass(class_t *cls)
1013 rwlock_assert_writing(&runtimeLock);
1014 return NXMapRemove(unattachedCategories(), cls);
1018 /***********************************************************************
1020 * Returns YES if class cls has been realized.
1021 * Locking: To prevent concurrent realization, hold runtimeLock.
1022 **********************************************************************/
1023 static BOOL isRealized(class_t *cls)
1025 return (cls->data->flags & RW_REALIZED) ? YES : NO;
1029 /***********************************************************************
1031 * Returns YES if class cls is an unrealized future class.
1032 * Locking: To prevent concurrent realization, hold runtimeLock.
1033 **********************************************************************/
1034 static BOOL isFuture(class_t *cls)
1036 return (cls->data->flags & RW_FUTURE) ? YES : NO;
1040 /***********************************************************************
1042 * Implementation of PrintReplacedMethods / OBJC_PRINT_REPLACED_METHODS.
1043 * Warn about methods from cats that override other methods in cats or cls.
1044 * Assumes no methods from cats have been added to cls yet.
1045 **********************************************************************/
1046 static void printReplacements(class_t *cls, category_list *cats)
1049 BOOL isMeta = isMetaClass(cls);
1053 // Newest categories are LAST in cats
1054 // Later categories override earlier ones.
1055 for (c = 0; c < cats->count; c++) {
1056 category_t *cat = cats->list[c].cat;
1057 uint32_t cmCount = cat_method_count(cat, isMeta);
1059 for (m = 0; m < cmCount; m++) {
1061 method_t *meth2 = NULL;
1062 method_t *meth = cat_method_nth(cat, isMeta, m);
1063 SEL s = sel_registerName((const char *)meth->name);
1065 // Don't warn about GC-ignored selectors
1066 if (s == (SEL)kIgnore) continue;
1068 // Look for method in earlier categories
1069 for (c2 = 0; c2 < c; c2++) {
1070 category_t *cat2 = cats->list[c2].cat;
1071 uint32_t cm2Count = cat_method_count(cat2, isMeta);
1072 for (m2 = 0; m2 < cm2Count; m2++) {
1073 meth2 = cat_method_nth(cat2, isMeta, m2);
1074 SEL s2 = sel_registerName((const char *)meth2->name);
1075 if (s == s2) goto whine;
1079 // Look for method in cls
1080 FOREACH_METHOD_LIST(mlist, cls, {
1081 for (m2 = 0; m2 < mlist->count; m2++) {
1082 meth2 = method_list_nth(mlist, m2);
1083 SEL s2 = sel_registerName((const char *)meth2->name);
1084 if (s == s2) goto whine;
1088 // Didn't find any override.
1092 // Found an override.
1093 logReplacedMethod(getName(cls), s, isMetaClass(cls), cat->name,
1094 _method_getImplementation(meth2),
1095 _method_getImplementation(meth));
1101 static BOOL isBundleClass(class_t *cls)
1103 return (cls->data->ro->flags & RO_FROM_BUNDLE) ? YES : NO;
1108 fixupMethodList(method_list_t *mlist, BOOL bundleCopy)
1110 assert(!isMethodListFixedUp(mlist));
1112 // fixme lock less in attachMethodLists ?
1116 for (m = 0; m < mlist->count; m++) {
1117 method_t *meth = method_list_nth(mlist, m);
1118 SEL sel = sel_registerNameNoLock((const char *)meth->name, bundleCopy);
1121 if (sel == (SEL)kIgnore) {
1122 meth->imp = (IMP)&_objc_ignored_method;
1128 setMethodListFixedUp(mlist);
1132 attachMethodLists(class_t *cls, method_list_t **lists, int count,
1133 BOOL methodsFromBundle, BOOL *outVtablesAffected)
1135 rwlock_assert_writing(&runtimeLock);
1137 BOOL vtablesAffected = NO;
1138 size_t listsSize = count * sizeof(*lists);
1140 // Create or extend method list array
1141 // Leave `count` empty slots at the start of the array to be filled below.
1143 if (!cls->data->methods) {
1144 // no bonus method lists yet
1145 cls->data->methods = _calloc_internal(1 + count, sizeof(*lists));
1147 size_t oldSize = malloc_size(cls->data->methods);
1148 cls->data->methods =
1149 _realloc_internal(cls->data->methods, oldSize + listsSize);
1150 memmove(cls->data->methods + count, cls->data->methods, oldSize);
1153 // Add method lists to array.
1154 // Reallocate un-fixed method lists.
1157 for (i = 0; i < count; i++) {
1158 method_list_t *mlist = lists[i];
1159 if (!mlist) continue;
1161 // Fixup selectors if necessary
1162 if (!isMethodListFixedUp(mlist)) {
1163 mlist = _memdup_internal(mlist, method_list_size(mlist));
1164 fixupMethodList(mlist, methodsFromBundle);
1167 // Scan for vtable updates
1168 if (outVtablesAffected && !vtablesAffected) {
1170 for (m = 0; m < mlist->count; m++) {
1171 SEL sel = method_list_nth(mlist, m)->name;
1172 if (vtable_containsSelector(sel)) vtablesAffected = YES;
1176 // Fill method list array
1177 cls->data->methods[i] = mlist;
1180 if (outVtablesAffected) *outVtablesAffected = vtablesAffected;
1184 attachCategoryMethods(class_t *cls, category_list *cats,
1185 BOOL *outVtablesAffected)
1188 if (PrintReplacedMethods) printReplacements(cls, cats);
1190 BOOL isMeta = isMetaClass(cls);
1191 method_list_t **mlists = _malloc_internal(cats->count * sizeof(*mlists));
1193 // Count backwards through cats to get newest categories first
1195 int i = cats->count;
1196 BOOL fromBundle = NO;
1198 method_list_t *mlist = cat_method_list(cats->list[i].cat, isMeta);
1200 mlists[mcount++] = mlist;
1201 fromBundle |= cats->list[i].fromBundle;
1205 attachMethodLists(cls, mlists, mcount, fromBundle, outVtablesAffected);
1207 _free_internal(mlists);
1212 static chained_property_list *
1213 buildPropertyList(const struct objc_property_list *plist, category_list *cats, BOOL isMeta)
1215 // Do NOT use cat->cls! It may have been remapped.
1216 chained_property_list *newlist;
1220 // Count properties in all lists.
1221 if (plist) count = plist->count;
1223 for (c = 0; c < cats->count; c++) {
1224 category_t *cat = cats->list[c].cat;
1226 if (isMeta && cat->classProperties) {
1227 count += cat->classProperties->count;
1230 if (!isMeta && cat->instanceProperties) {
1231 count += cat->instanceProperties->count;
1236 if (count == 0) return NULL;
1238 // Allocate new list.
1239 newlist = _malloc_internal(sizeof(*newlist) + count * sizeof(struct objc_property));
1241 newlist->next = NULL;
1243 // Copy properties; newest categories first, then ordinary properties
1247 struct objc_property_list *cplist;
1248 category_t *cat = cats->list[c].cat;
1251 cplist = cat->classProperties;
1254 cplist = cat->instanceProperties;
1257 for (p = 0; p < cplist->count; p++) {
1258 newlist->list[newlist->count++] =
1259 *property_list_nth(cplist, p);
1265 for (p = 0; p < plist->count; p++) {
1266 newlist->list[newlist->count++] = *property_list_nth(plist, p);
1270 assert(newlist->count == count);
1276 static protocol_list_t **
1277 buildProtocolList(category_list *cats, struct protocol_list_t *base,
1278 struct protocol_list_t **protos)
1280 // Do NOT use cat->cls! It may have been remapped.
1281 struct protocol_list_t **p, **newp;
1282 struct protocol_list_t **newprotos;
1286 // count protocol list in base
1289 // count protocol lists in cats
1290 if (cats) for (i = 0; i < cats->count; i++) {
1291 category_t *cat = cats->list[i].cat;
1292 if (cat->protocols) count++;
1295 // no base or category protocols? return existing protocols unchanged
1296 if (count == 0) return protos;
1298 // count protocol lists in protos
1299 for (p = protos; p && *p; p++) {
1303 if (count == 0) return NULL;
1305 newprotos = (struct protocol_list_t **)
1306 _malloc_internal((count+1) * sizeof(struct protocol_list_t *));
1313 for (p = protos; p && *p; p++) {
1317 if (cats) for (i = 0; i < cats->count; i++) {
1318 category_t *cat = cats->list[i].cat;
1319 if (cat->protocols) {
1320 *newp++ = cat->protocols;
1330 /***********************************************************************
1332 * Fixes up cls's method list, protocol list, and property list.
1333 * Attaches any outstanding categories.
1335 * Locking: runtimeLock must be held by the caller
1336 **********************************************************************/
1337 static void methodizeClass(struct class_t *cls)
1339 category_list *cats;
1342 rwlock_assert_writing(&runtimeLock);
1344 isMeta = isMetaClass(cls);
1346 // Methodizing for the first time
1347 if (PrintConnecting) {
1348 _objc_inform("CLASS: methodizing class '%s' %s",
1349 getName(cls), isMeta ? "(meta)" : "");
1352 // Build method and protocol and property lists.
1353 // Include methods and protocols and properties from categories, if any
1354 // Do NOT use cat->cls! It may have been remapped.
1356 attachMethodLists(cls, (method_list_t **)&cls->data->ro->baseMethods, 1,
1357 isBundleClass(cls), NULL);
1359 cats = unattachedCategoriesForClass(cls);
1360 attachCategoryMethods(cls, cats, NULL);
1362 if (cats || cls->data->ro->baseProperties) {
1363 cls->data->properties =
1364 buildPropertyList(cls->data->ro->baseProperties, cats, isMeta);
1367 if (cats || cls->data->ro->baseProtocols) {
1368 cls->data->protocols =
1369 buildProtocolList(cats, cls->data->ro->baseProtocols, NULL);
1372 if (PrintConnecting) {
1375 for (i = 0; i < cats->count; i++) {
1376 _objc_inform("CLASS: attached category %c%s(%s)",
1378 getName(cls), cats->list[i].cat->name);
1383 if (cats) _free_internal(cats);
1385 // No vtable until +initialize completes
1386 assert(cls->vtable == _objc_empty_vtable);
1390 /***********************************************************************
1392 * Attach outstanding categories to an existing class.
1393 * Fixes up cls's method list, protocol list, and property list.
1394 * Updates method caches and vtables for cls and its subclasses.
1395 * Locking: runtimeLock must be held by the caller
1396 **********************************************************************/
1397 static void remethodizeClass(struct class_t *cls)
1399 category_list *cats;
1402 rwlock_assert_writing(&runtimeLock);
1404 isMeta = isMetaClass(cls);
1406 // Re-methodizing: check for more categories
1407 if ((cats = unattachedCategoriesForClass(cls))) {
1408 chained_property_list *newproperties;
1409 struct protocol_list_t **newprotos;
1410 BOOL vtableAffected = NO;
1412 if (PrintConnecting) {
1413 _objc_inform("CLASS: attaching categories to class '%s' %s",
1414 getName(cls), isMeta ? "(meta)" : "");
1417 // Update methods, properties, protocols
1419 attachCategoryMethods(cls, cats, &vtableAffected);
1421 newproperties = buildPropertyList(NULL, cats, isMeta);
1422 if (newproperties) {
1423 newproperties->next = cls->data->properties;
1424 cls->data->properties = newproperties;
1427 newprotos = buildProtocolList(cats, NULL, cls->data->protocols);
1428 if (cls->data->protocols && cls->data->protocols != newprotos) {
1429 _free_internal(cls->data->protocols);
1431 cls->data->protocols = newprotos;
1433 _free_internal(cats);
1435 // Update method caches and vtables
1437 if (vtableAffected) flushVtables(cls);
1442 /***********************************************************************
1444 * Atomically sets and clears some bits in cls's info field.
1445 * set and clear must not overlap.
1446 **********************************************************************/
1447 static void changeInfo(class_t *cls, unsigned int set, unsigned int clear)
1449 uint32_t oldf, newf;
1451 assert(isFuture(cls) || isRealized(cls));
1454 oldf = cls->data->flags;
1455 newf = (oldf | set) & ~clear;
1456 } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&cls->data->flags));
1460 /***********************************************************************
1462 * Returns the classname => class map of all non-meta classes.
1463 * Locking: runtimeLock must be read- or write-locked by the caller
1464 **********************************************************************/
1466 NXMapTable *gdb_objc_realized_classes; // exported for debuggers in objc-gdb.h
1468 static NXMapTable *namedClasses(void)
1470 rwlock_assert_locked(&runtimeLock);
1472 INIT_ONCE_PTR(gdb_objc_realized_classes,
1473 NXCreateMapTableFromZone(NXStrValueMapPrototype, 1024,
1474 _objc_internal_zone()),
1475 NXFreeMapTable(v) );
1477 return gdb_objc_realized_classes;
1481 /***********************************************************************
1483 * Adds name => cls to the named non-meta class map.
1484 * Warns about duplicate class names and keeps the old mapping.
1485 * Locking: runtimeLock must be held by the caller
1486 **********************************************************************/
1487 static void addNamedClass(class_t *cls, const char *name)
1489 rwlock_assert_writing(&runtimeLock);
1491 if ((old = NXMapGet(namedClasses(), name))) {
1492 inform_duplicate(name, (Class)old, (Class)cls);
1494 NXMapInsert(namedClasses(), name, cls);
1496 assert(!(cls->data->flags & RO_META));
1498 // wrong: constructed classes are already realized when they get here
1499 // assert(!isRealized(cls));
1503 /***********************************************************************
1505 * Removes cls from the name => cls map.
1506 * Locking: runtimeLock must be held by the caller
1507 **********************************************************************/
1508 static void removeNamedClass(class_t *cls, const char *name)
1510 rwlock_assert_writing(&runtimeLock);
1511 assert(!(cls->data->flags & RO_META));
1512 if (cls == NXMapGet(namedClasses(), name)) {
1513 NXMapRemove(namedClasses(), name);
1515 // cls has a name collision with another class - don't remove the other
1520 /***********************************************************************
1522 * Returns the class list for realized non-meta classes.
1523 * Locking: runtimeLock must be read- or write-locked by the caller
1524 **********************************************************************/
1525 static NXHashTable *realizedClasses(void)
1527 static NXHashTable *class_hash = NULL;
1529 rwlock_assert_locked(&runtimeLock);
1531 INIT_ONCE_PTR(class_hash,
1532 NXCreateHashTableFromZone(NXPtrPrototype, 1024, NULL,
1533 _objc_internal_zone()),
1534 NXFreeHashTable(v));
1540 /***********************************************************************
1541 * realizedMetaclasses
1542 * Returns the class list for realized metaclasses.
1543 * Locking: runtimeLock must be read- or write-locked by the caller
1544 **********************************************************************/
1545 static NXHashTable *realizedMetaclasses(void)
1547 static NXHashTable *class_hash = NULL;
1549 rwlock_assert_locked(&runtimeLock);
1551 INIT_ONCE_PTR(class_hash,
1552 NXCreateHashTableFromZone(NXPtrPrototype, 1024, NULL,
1553 _objc_internal_zone()),
1554 NXFreeHashTable(v));
1560 /***********************************************************************
1562 * Adds cls to the realized non-meta class hash.
1563 * Locking: runtimeLock must be held by the caller
1564 **********************************************************************/
1565 static void addRealizedClass(class_t *cls)
1567 rwlock_assert_writing(&runtimeLock);
1569 old = NXHashInsert(realizedClasses(), cls);
1570 objc_addRegisteredClass((Class)cls);
1571 assert(!isMetaClass(cls));
1576 /***********************************************************************
1577 * removeRealizedClass
1578 * Removes cls from the realized non-meta class hash.
1579 * Locking: runtimeLock must be held by the caller
1580 **********************************************************************/
1581 static void removeRealizedClass(class_t *cls)
1583 rwlock_assert_writing(&runtimeLock);
1584 if (isRealized(cls)) {
1585 assert(!isMetaClass(cls));
1586 NXHashRemove(realizedClasses(), cls);
1587 objc_removeRegisteredClass((Class)cls);
1592 /***********************************************************************
1593 * addRealizedMetaclass
1594 * Adds cls to the realized metaclass hash.
1595 * Locking: runtimeLock must be held by the caller
1596 **********************************************************************/
1597 static void addRealizedMetaclass(class_t *cls)
1599 rwlock_assert_writing(&runtimeLock);
1601 old = NXHashInsert(realizedMetaclasses(), cls);
1602 assert(isMetaClass(cls));
1607 /***********************************************************************
1608 * removeRealizedMetaclass
1609 * Removes cls from the realized metaclass hash.
1610 * Locking: runtimeLock must be held by the caller
1611 **********************************************************************/
1612 static void removeRealizedMetaclass(class_t *cls)
1614 rwlock_assert_writing(&runtimeLock);
1615 if (isRealized(cls)) {
1616 assert(isMetaClass(cls));
1617 NXHashRemove(realizedMetaclasses(), cls);
1622 /***********************************************************************
1623 * uninitializedClasses
1624 * Returns the metaclass => class map for un-+initialized classes
1625 * Replaces the 32-bit cls = objc_getName(metacls) during +initialize.
1626 * Locking: runtimeLock must be read- or write-locked by the caller
1627 **********************************************************************/
1628 static NXMapTable *uninitializedClasses(void)
1630 static NXMapTable *class_map = NULL;
1632 rwlock_assert_locked(&runtimeLock);
1634 INIT_ONCE_PTR(class_map,
1635 NXCreateMapTableFromZone(NXPtrValueMapPrototype, 1024,
1636 _objc_internal_zone()),
1637 NXFreeMapTable(v) );
1643 /***********************************************************************
1644 * addUninitializedClass
1645 * Adds metacls => cls to the un-+initialized class map
1646 * Locking: runtimeLock must be held by the caller
1647 **********************************************************************/
1648 static void addUninitializedClass(class_t *cls, class_t *metacls)
1650 rwlock_assert_writing(&runtimeLock);
1652 old = NXMapInsert(uninitializedClasses(), metacls, cls);
1653 assert(isRealized(metacls) ? isMetaClass(metacls) : metacls->data->flags & RO_META);
1654 assert(! (isRealized(cls) ? isMetaClass(cls) : cls->data->flags & RO_META));
1659 static void removeUninitializedClass(class_t *cls)
1661 rwlock_assert_writing(&runtimeLock);
1662 NXMapRemove(uninitializedClasses(), cls->isa);
1666 /***********************************************************************
1668 * Return the ordinary class for this class or metaclass.
1669 * Used by +initialize.
1670 * Locking: runtimeLock must be read- or write-locked by the caller
1671 **********************************************************************/
1672 static class_t *getNonMetaClass(class_t *cls)
1674 rwlock_assert_locked(&runtimeLock);
1675 if (isMetaClass(cls)) {
1676 cls = NXMapGet(uninitializedClasses(), cls);
1682 /***********************************************************************
1683 * _class_getNonMetaClass
1684 * Return the ordinary class for this class or metaclass.
1685 * Used by +initialize.
1686 * Locking: acquires runtimeLock
1687 **********************************************************************/
1688 __private_extern__ Class _class_getNonMetaClass(Class cls_gen)
1690 class_t *cls = newcls(cls_gen);
1691 rwlock_write(&runtimeLock);
1692 cls = getNonMetaClass(cls);
1694 rwlock_unlock_write(&runtimeLock);
1701 /***********************************************************************
1703 * Returns the classname => future class map for unrealized future classes.
1704 * Locking: runtimeLock must be held by the caller
1705 **********************************************************************/
1706 static NXMapTable *futureClasses(void)
1708 rwlock_assert_writing(&runtimeLock);
1710 static NXMapTable *future_class_map = NULL;
1712 if (future_class_map) return future_class_map;
1714 // future_class_map is big enough to hold CF's classes and a few others
1715 future_class_map = NXCreateMapTableFromZone(NXStrValueMapPrototype, 32,
1716 _objc_internal_zone());
1718 return future_class_map;
1722 /***********************************************************************
1724 * Installs cls as the class structure to use for the named class if it appears.
1725 * Locking: runtimeLock must be held by the caller
1726 **********************************************************************/
1727 static void addFutureClass(const char *name, class_t *cls)
1731 rwlock_assert_writing(&runtimeLock);
1734 _objc_inform("FUTURE: reserving %p for %s", cls, name);
1737 cls->data = _calloc_internal(sizeof(*cls->data), 1);
1738 cls->data->flags = RO_FUTURE;
1740 old = NXMapKeyCopyingInsert(futureClasses(), name, cls);
1745 /***********************************************************************
1747 * Removes the named class from the unrealized future class list,
1748 * because it has been realized.
1749 * Locking: runtimeLock must be held by the caller
1750 **********************************************************************/
1751 static void removeFutureClass(const char *name)
1753 rwlock_assert_writing(&runtimeLock);
1755 NXMapKeyFreeingRemove(futureClasses(), name);
1759 /***********************************************************************
1761 * Returns the oldClass => newClass map for realized future classes.
1762 * Returns the oldClass => NULL map for ignored weak-linked classes.
1763 * Locking: runtimeLock must be read- or write-locked by the caller
1764 **********************************************************************/
1765 static NXMapTable *remappedClasses(BOOL create)
1767 static NXMapTable *remapped_class_map = NULL;
1769 rwlock_assert_locked(&runtimeLock);
1771 if (remapped_class_map) return remapped_class_map;
1772 if (!create) return NULL;
1774 // remapped_class_map is big enough to hold CF's classes and a few others
1775 INIT_ONCE_PTR(remapped_class_map,
1776 NXCreateMapTableFromZone(NXPtrValueMapPrototype, 32,
1777 _objc_internal_zone()),
1780 return remapped_class_map;
1784 /***********************************************************************
1786 * Returns YES if no classes have been remapped
1787 * Locking: runtimeLock must be read- or write-locked by the caller
1788 **********************************************************************/
1789 static BOOL noClassesRemapped(void)
1791 rwlock_assert_locked(&runtimeLock);
1793 BOOL result = (remappedClasses(NO) == NULL);
1798 /***********************************************************************
1800 * newcls is a realized future class, replacing oldcls.
1801 * OR newcls is NULL, replacing ignored weak-linked class oldcls.
1802 * Locking: runtimeLock must be write-locked by the caller
1803 **********************************************************************/
1804 static void addRemappedClass(class_t *oldcls, class_t *newcls)
1806 rwlock_assert_writing(&runtimeLock);
1809 _objc_inform("FUTURE: using %p instead of %p for %s",
1810 oldcls, newcls, getName(newcls));
1814 old = NXMapInsert(remappedClasses(YES), oldcls, newcls);
1819 /***********************************************************************
1821 * Returns the live class pointer for cls, which may be pointing to
1822 * a class struct that has been reallocated.
1823 * Returns NULL if cls is ignored because of weak linking.
1824 * Locking: runtimeLock must be read- or write-locked by the caller
1825 **********************************************************************/
1826 static class_t *remapClass(class_t *cls)
1828 rwlock_assert_locked(&runtimeLock);
1832 if (!cls) return NULL;
1834 if (NXMapMember(remappedClasses(YES), cls, (void**)&c2) == NX_MAPNOTAKEY) {
1842 /***********************************************************************
1844 * Fix up a class ref, in case the class referenced has been reallocated
1845 * or is an ignored weak-linked class.
1846 * Locking: runtimeLock must be read- or write-locked by the caller
1847 **********************************************************************/
1848 static void remapClassRef(class_t **clsref)
1850 rwlock_assert_locked(&runtimeLock);
1852 class_t *newcls = remapClass(*clsref);
1853 if (*clsref != newcls) *clsref = newcls;
1857 /***********************************************************************
1859 * Adds subcls as a subclass of supercls.
1860 * Locking: runtimeLock must be held by the caller.
1861 **********************************************************************/
1862 static void addSubclass(class_t *supercls, class_t *subcls)
1864 rwlock_assert_writing(&runtimeLock);
1866 if (supercls && subcls) {
1867 assert(isRealized(supercls));
1868 assert(isRealized(subcls));
1869 subcls->data->nextSiblingClass = supercls->data->firstSubclass;
1870 supercls->data->firstSubclass = subcls;
1875 /***********************************************************************
1877 * Removes subcls as a subclass of supercls.
1878 * Locking: runtimeLock must be held by the caller.
1879 **********************************************************************/
1880 static void removeSubclass(class_t *supercls, class_t *subcls)
1882 rwlock_assert_writing(&runtimeLock);
1883 assert(getSuperclass(subcls) == supercls);
1886 for (cp = &supercls->data->firstSubclass;
1887 *cp && *cp != subcls;
1888 cp = &(*cp)->data->nextSiblingClass)
1890 assert(*cp == subcls);
1891 *cp = subcls->data->nextSiblingClass;
1896 /***********************************************************************
1898 * Returns the protocol name => protocol map for protocols.
1899 * Locking: runtimeLock must read- or write-locked by the caller
1900 **********************************************************************/
1901 static NXMapTable *protocols(void)
1903 static NXMapTable *protocol_map = NULL;
1905 rwlock_assert_locked(&runtimeLock);
1907 INIT_ONCE_PTR(protocol_map,
1908 NXCreateMapTableFromZone(NXStrValueMapPrototype, 16,
1909 _objc_internal_zone()),
1910 NXFreeMapTable(v) );
1912 return protocol_map;
1916 /***********************************************************************
1918 * Returns the live protocol pointer for proto, which may be pointing to
1919 * a protocol struct that has been reallocated.
1920 * Locking: runtimeLock must be read- or write-locked by the caller
1921 **********************************************************************/
1922 static protocol_t *remapProtocol(protocol_ref_t proto)
1924 rwlock_assert_locked(&runtimeLock);
1926 protocol_t *newproto = NXMapGet(protocols(), ((protocol_t *)proto)->name);
1927 return newproto ? newproto : (protocol_t *)proto;
1931 /***********************************************************************
1933 * Fix up a protocol ref, in case the protocol referenced has been reallocated.
1934 * Locking: runtimeLock must be read- or write-locked by the caller
1935 **********************************************************************/
1936 static void remapProtocolRef(protocol_t **protoref)
1938 rwlock_assert_locked(&runtimeLock);
1940 protocol_t *newproto = remapProtocol((protocol_ref_t)*protoref);
1941 if (*protoref != newproto) *protoref = newproto;
1945 /***********************************************************************
1947 * Slides a class's ivars to accommodate the given superclass size.
1948 * Also slides ivar and weak GC layouts if provided.
1949 * Ivars are NOT compacted to compensate for a superclass that shrunk.
1950 * Locking: runtimeLock must be held by the caller.
1951 **********************************************************************/
1952 static void moveIvars(class_ro_t *ro, uint32_t superSize,
1953 layout_bitmap *ivarBitmap, layout_bitmap *weakBitmap)
1955 rwlock_assert_writing(&runtimeLock);
1960 assert(superSize > ro->instanceStart);
1961 diff = superSize - ro->instanceStart;
1964 // Find maximum alignment in this class's ivars
1965 uint32_t maxAlignment = 1;
1966 for (i = 0; i < ro->ivars->count; i++) {
1967 ivar_t *ivar = ivar_list_nth(ro->ivars, i);
1968 if (!ivar->offset) continue; // anonymous bitfield
1970 uint32_t alignment = ivar_alignment(ivar);
1971 if (alignment > maxAlignment) maxAlignment = alignment;
1974 // Compute a slide value that preserves that alignment
1975 uint32_t alignMask = maxAlignment - 1;
1976 if (diff & alignMask) diff = (diff + alignMask) & ~alignMask;
1978 // Slide all of this class's ivars en masse
1979 for (i = 0; i < ro->ivars->count; i++) {
1980 ivar_t *ivar = ivar_list_nth(ro->ivars, i);
1981 if (!ivar->offset) continue; // anonymous bitfield
1983 uint32_t oldOffset = (uint32_t)*ivar->offset;
1984 uint32_t newOffset = oldOffset + diff;
1985 *ivar->offset = newOffset;
1988 _objc_inform("IVARS: offset %u -> %u for %s (size %u, align %u)",
1989 oldOffset, newOffset, ivar->name,
1990 ivar->size, ivar_alignment(ivar));
1995 uint32_t oldOffset = ro->instanceStart;
1996 uint32_t newOffset = ro->instanceStart + diff;
1999 layout_bitmap_slide(ivarBitmap,
2000 oldOffset >> WORD_SHIFT,
2001 newOffset >> WORD_SHIFT);
2004 layout_bitmap_slide(weakBitmap,
2005 oldOffset >> WORD_SHIFT,
2006 newOffset >> WORD_SHIFT);
2010 *(uint32_t *)&ro->instanceStart += diff;
2011 *(uint32_t *)&ro->instanceSize += diff;
2014 // No ivars slid, but superclass changed size.
2015 // Expand bitmap in preparation for layout_bitmap_splat().
2016 if (ivarBitmap) layout_bitmap_grow(ivarBitmap, ro->instanceSize >> WORD_SHIFT);
2017 if (weakBitmap) layout_bitmap_grow(weakBitmap, ro->instanceSize >> WORD_SHIFT);
2022 /***********************************************************************
2024 * Look up an ivar by name.
2025 * Locking: runtimeLock must be read- or write-locked by the caller.
2026 **********************************************************************/
2027 static ivar_t *getIvar(class_t *cls, const char *name)
2029 rwlock_assert_locked(&runtimeLock);
2031 const ivar_list_t *ivars;
2032 assert(isRealized(cls));
2033 if ((ivars = cls->data->ro->ivars)) {
2035 for (i = 0; i < ivars->count; i++) {
2036 struct ivar_t *ivar = ivar_list_nth(ivars, i);
2037 if (!ivar->offset) continue; // anonymous bitfield
2039 // ivar->name may be NULL for anonymous bitfields etc.
2040 if (ivar->name && 0 == strcmp(name, ivar->name)) {
2050 /***********************************************************************
2052 * Performs first-time initialization on class cls,
2053 * including allocating its read-write data.
2054 * Returns the real class structure for the class.
2055 * Locking: runtimeLock must be write-locked by the caller
2056 **********************************************************************/
2057 static class_t *realizeClass(class_t *cls)
2059 rwlock_assert_writing(&runtimeLock);
2061 const class_ro_t *ro;
2067 if (!cls) return NULL;
2068 if (isRealized(cls)) return cls;
2069 assert(cls == remapClass(cls));
2071 ro = (const class_ro_t *)cls->data;
2072 if (ro->flags & RO_FUTURE) {
2073 // This was a future class. rw data is already allocated.
2076 changeInfo(cls, RW_REALIZED, RW_FUTURE);
2078 // Normal class. Allocate writeable class data.
2079 rw = _calloc_internal(sizeof(class_rw_t), 1);
2081 rw->flags = RW_REALIZED;
2085 isMeta = (ro->flags & RO_META) ? YES : NO;
2087 rw->version = isMeta ? 7 : 0; // old runtime went up to 6
2089 if (PrintConnecting) {
2090 _objc_inform("CLASS: realizing class '%s' %s %p %p",
2091 ro->name, isMeta ? "(meta)" : "", cls, ro);
2094 // Realize superclass and metaclass, if they aren't already.
2095 // This needs to be done after RW_REALIZED is set above, for root classes.
2096 supercls = realizeClass(remapClass(cls->superclass));
2097 metacls = realizeClass(remapClass(cls->isa));
2099 // Check for remapped superclass
2100 // fixme doesn't handle remapped metaclass
2101 assert(metacls == cls->isa);
2102 if (supercls != cls->superclass) {
2103 cls->superclass = supercls;
2106 /* debug: print them all
2109 for (i = 0; i < ro->ivars->count; i++) {
2110 ivar_t *ivar = ivar_list_nth(ro->ivars, i);
2111 if (!ivar->offset) continue; // anonymous bitfield
2113 _objc_inform("IVARS: %s.%s (offset %u, size %u, align %u)",
2114 ro->name, ivar->name,
2115 *ivar->offset, ivar->size, ivar_alignment(ivar));
2122 // Non-fragile ivars - reconcile this class with its superclass
2123 layout_bitmap ivarBitmap;
2124 layout_bitmap weakBitmap;
2125 BOOL layoutsChanged = NO;
2128 // fixme can optimize for "class has no new ivars", etc
2129 // WARNING: gcc c++ sets instanceStart/Size=0 for classes with
2130 // no local ivars, but does provide a layout bitmap.
2131 // Handle that case specially so layout_bitmap_create doesn't die
2132 // The other ivar sliding code below still works fine, and
2133 // the final result is a good class.
2134 if (ro->instanceStart == 0 && ro->instanceSize == 0) {
2135 // We can't use ro->ivarLayout because we don't know
2136 // how long it is. Force a new layout to be created.
2138 _objc_inform("IVARS: instanceStart/Size==0 for class %s; "
2139 "disregarding ivar layout", ro->name);
2142 layout_bitmap_create(NULL,
2143 supercls->data->ro->instanceSize,
2144 supercls->data->ro->instanceSize, NO);
2146 layout_bitmap_create(NULL,
2147 supercls->data->ro->instanceSize,
2148 supercls->data->ro->instanceSize, YES);
2149 layoutsChanged = YES;
2152 layout_bitmap_create(ro->ivarLayout,
2154 ro->instanceSize, NO);
2156 layout_bitmap_create(ro->weakIvarLayout,
2158 ro->instanceSize, YES);
2162 if (ro->instanceStart < supercls->data->ro->instanceSize) {
2163 // Superclass has changed size. This class's ivars must move.
2164 // Also slide layout bits in parallel.
2165 // This code is incapable of compacting the subclass to
2166 // compensate for a superclass that shrunk, so don't do that.
2168 _objc_inform("IVARS: sliding ivars for class %s "
2169 "(superclass was %u bytes, now %u)",
2170 ro->name, ro->instanceStart,
2171 supercls->data->ro->instanceSize);
2173 class_ro_t *ro_w = make_ro_writeable(rw);
2175 moveIvars(ro_w, supercls->data->ro->instanceSize,
2176 UseGC ? &ivarBitmap : NULL, UseGC ? &weakBitmap : NULL);
2177 gdb_objc_class_changed((Class)cls, OBJC_CLASS_IVARS_CHANGED, ro->name);
2178 layoutsChanged = YES;
2182 // Check superclass's layout against this class's layout.
2183 // This needs to be done even if the superclass is not bigger.
2184 layout_bitmap superBitmap =
2185 layout_bitmap_create(supercls->data->ro->ivarLayout,
2186 supercls->data->ro->instanceSize,
2187 supercls->data->ro->instanceSize, NO);
2188 layoutsChanged |= layout_bitmap_splat(ivarBitmap, superBitmap,
2190 layout_bitmap_free(superBitmap);
2193 layout_bitmap_create(supercls->data->ro->weakIvarLayout,
2194 supercls->data->ro->instanceSize,
2195 supercls->data->ro->instanceSize, YES);
2196 layoutsChanged |= layout_bitmap_splat(weakBitmap, superBitmap,
2198 layout_bitmap_free(superBitmap);
2200 if (layoutsChanged) {
2201 // Rebuild layout strings.
2203 _objc_inform("IVARS: gc layout changed for class %s",
2206 class_ro_t *ro_w = make_ro_writeable(rw);
2208 ro_w->ivarLayout = layout_string_create(ivarBitmap);
2209 ro_w->weakIvarLayout = layout_string_create(weakBitmap);
2212 layout_bitmap_free(ivarBitmap);
2213 layout_bitmap_free(weakBitmap);
2217 // Connect this class to its superclass's subclass lists
2219 addSubclass(supercls, cls);
2222 // Attach categories
2223 methodizeClass(cls);
2226 addRealizedClass(cls);
2228 addRealizedMetaclass(cls);
2235 /***********************************************************************
2237 * Looks up a class by name. The class MIGHT NOT be realized.
2238 * Locking: runtimeLock must be read- or write-locked by the caller.
2239 **********************************************************************/
2240 static class_t *getClass(const char *name)
2242 rwlock_assert_locked(&runtimeLock);
2244 return (class_t *)NXMapGet(namedClasses(), name);
2248 /***********************************************************************
2249 * missingWeakSuperclass
2250 * Return YES if some superclass of cls was weak-linked and is missing.
2251 **********************************************************************/
2253 missingWeakSuperclass(class_t *cls)
2255 assert(!isRealized(cls));
2257 if (!cls->superclass) {
2258 // superclass NULL. This is normal for root classes only.
2259 return (!(cls->data->flags & RO_ROOT));
2261 // superclass not NULL. Check if a higher superclass is missing.
2262 class_t *supercls = remapClass(cls->superclass);
2263 if (!supercls) return YES;
2264 if (isRealized(supercls)) return NO;
2265 return missingWeakSuperclass(supercls);
2270 /***********************************************************************
2271 * realizeAllClassesInImage
2272 * Non-lazily realizes all unrealized classes in the given image.
2273 * Locking: runtimeLock must be held by the caller.
2274 **********************************************************************/
2275 static void realizeAllClassesInImage(header_info *hi)
2277 rwlock_assert_writing(&runtimeLock);
2280 class_t **classlist;
2282 if (hi->allClassesRealized) return;
2284 classlist = _getObjc2ClassList(hi, &count);
2286 for (i = 0; i < count; i++) {
2287 realizeClass(remapClass(classlist[i]));
2290 hi->allClassesRealized = YES;
2294 /***********************************************************************
2296 * Non-lazily realizes all unrealized classes in all known images.
2297 * Locking: runtimeLock must be held by the caller.
2298 **********************************************************************/
2299 static void realizeAllClasses(void)
2301 rwlock_assert_writing(&runtimeLock);
2304 for (hi = FirstHeader; hi; hi = hi->next) {
2305 realizeAllClassesInImage(hi);
2310 /***********************************************************************
2311 * _objc_allocateFutureClass
2312 * Allocate an unresolved future class for the given class name.
2313 * Returns any existing allocation if one was already made.
2314 * Assumes the named class doesn't exist yet.
2315 * Locking: acquires runtimeLock
2316 **********************************************************************/
2317 __private_extern__ Class _objc_allocateFutureClass(const char *name)
2319 rwlock_write(&runtimeLock);
2321 struct class_t *cls;
2322 NXMapTable *future_class_map = futureClasses();
2324 if ((cls = NXMapGet(future_class_map, name))) {
2325 // Already have a future class for this name.
2326 rwlock_unlock_write(&runtimeLock);
2330 cls = (class_t *)_calloc_class(sizeof(*cls));
2331 addFutureClass(name, cls);
2333 rwlock_unlock_write(&runtimeLock);
2338 /***********************************************************************
2340 **********************************************************************/
2341 void objc_setFutureClass(Class cls, const char *name)
2343 // fixme hack do nothing - NSCFString handled specially elsewhere
2347 #define FOREACH_REALIZED_SUBCLASS(_c, _cls, code) \
2349 rwlock_assert_writing(&runtimeLock); \
2350 class_t *_top = _cls; \
2351 class_t *_c = _top; \
2355 if (_c->data->firstSubclass) { \
2356 _c = _c->data->firstSubclass; \
2358 while (!_c->data->nextSiblingClass && _c != _top) { \
2359 _c = getSuperclass(_c); \
2361 if (_c == _top) break; \
2362 _c = _c->data->nextSiblingClass; \
2366 /* nil means all realized classes */ \
2367 NXHashTable *_classes = realizedClasses(); \
2368 NXHashTable *_metaclasses = realizedMetaclasses(); \
2369 NXHashState _state; \
2370 _state = NXInitHashState(_classes); \
2371 while (NXNextHashState(_classes, &_state, (void**)&_c)) \
2375 _state = NXInitHashState(_metaclasses); \
2376 while (NXNextHashState(_metaclasses, &_state, (void**)&_c)) \
2384 /***********************************************************************
2386 * Rebuilds vtables for cls and its realized subclasses.
2387 * If cls is Nil, all realized classes and metaclasses are touched.
2388 * Locking: runtimeLock must be held by the caller.
2389 **********************************************************************/
2390 static void flushVtables(class_t *cls)
2392 rwlock_assert_writing(&runtimeLock);
2394 if (PrintVtables && !cls) {
2395 _objc_inform("VTABLES: ### EXPENSIVE ### global vtable flush!");
2398 FOREACH_REALIZED_SUBCLASS(c, cls, {
2399 updateVtable(c, NO);
2404 /***********************************************************************
2406 * Flushes caches for cls and its realized subclasses.
2407 * Does not update vtables.
2408 * If cls is Nil, all realized and metaclasses classes are touched.
2409 * Locking: runtimeLock must be held by the caller.
2410 **********************************************************************/
2411 static void flushCaches(class_t *cls)
2413 rwlock_assert_writing(&runtimeLock);
2415 FOREACH_REALIZED_SUBCLASS(c, cls, {
2416 flush_cache((Class)c);
2421 /***********************************************************************
2423 * Flushes caches and rebuilds vtables for cls, its subclasses,
2424 * and optionally its metaclass.
2425 * Locking: acquires runtimeLock
2426 **********************************************************************/
2427 __private_extern__ void flush_caches(Class cls_gen, BOOL flush_meta)
2429 class_t *cls = newcls(cls_gen);
2430 rwlock_write(&runtimeLock);
2431 // fixme optimize vtable flushing? (only needed for vtable'd selectors)
2434 // don't flush root class's metaclass twice (it's a subclass of the root)
2435 if (flush_meta && getSuperclass(cls)) {
2436 flushCaches(cls->isa);
2437 flushVtables(cls->isa);
2439 rwlock_unlock_write(&runtimeLock);
2443 /***********************************************************************
2445 * Process the given images which are being mapped in by dyld.
2446 * Calls ABI-agnostic code after taking ABI-specific locks.
2448 * Locking: write-locks runtimeLock
2449 **********************************************************************/
2450 __private_extern__ const char *
2451 map_images(enum dyld_image_states state, uint32_t infoCount,
2452 const struct dyld_image_info infoList[])
2456 rwlock_write(&runtimeLock);
2457 err = map_images_nolock(state, infoCount, infoList);
2458 rwlock_unlock_write(&runtimeLock);
2463 /***********************************************************************
2465 * Process +load in the given images which are being mapped in by dyld.
2466 * Calls ABI-agnostic code after taking ABI-specific locks.
2468 * Locking: write-locks runtimeLock and loadMethodLock
2469 **********************************************************************/
2470 __private_extern__ const char *
2471 load_images(enum dyld_image_states state, uint32_t infoCount,
2472 const struct dyld_image_info infoList[])
2476 recursive_mutex_lock(&loadMethodLock);
2478 // Discover load methods
2479 rwlock_write(&runtimeLock);
2480 found = load_images_nolock(state, infoCount, infoList);
2481 rwlock_unlock_write(&runtimeLock);
2483 // Call +load methods (without runtimeLock - re-entrant)
2485 call_load_methods();
2488 recursive_mutex_unlock(&loadMethodLock);
2494 /***********************************************************************
2496 * Process the given image which is about to be unmapped by dyld.
2497 * mh is mach_header instead of headerType because that's what
2498 * dyld_priv.h says even for 64-bit.
2500 * Locking: write-locks runtimeLock and loadMethodLock
2501 **********************************************************************/
2502 __private_extern__ void
2503 unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide)
2505 recursive_mutex_lock(&loadMethodLock);
2506 rwlock_write(&runtimeLock);
2508 unmap_image_nolock(mh, vmaddr_slide);
2510 rwlock_unlock_write(&runtimeLock);
2511 recursive_mutex_unlock(&loadMethodLock);
2515 /***********************************************************************
2517 * Perform initial processing of the headers in the linked
2518 * list beginning with headerList.
2520 * Called by: map_images_nolock
2522 * Locking: runtimeLock acquired by map_images
2523 **********************************************************************/
2524 __private_extern__ void _read_images(header_info **hList, uint32_t hCount)
2530 class_t **resolvedFutureClasses = NULL;
2531 size_t resolvedFutureClassCount = 0;
2532 static BOOL doneOnce;
2534 rwlock_assert_writing(&runtimeLock);
2541 #define EACH_HEADER \
2543 hIndex < hCount && (hi = hList[hIndex]); \
2546 // Complain about images that contain old-ABI data
2547 // fixme new-ABI compiler still emits some bits into __OBJC segment
2550 if (_getObjcSelectorRefs(hi, &count) || _getObjcModules(hi, &count)) {
2551 _objc_inform("found old-ABI metadata in image %s !",
2552 hi->os.dl_info.dli_fname);
2557 static BOOL hackedNSCFString = NO;
2558 if (!hackedNSCFString) {
2559 // Insert future class __CFConstantStringClassReference == NSCFString
2560 void *dlh = dlopen("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", RTLD_LAZY | RTLD_NOLOAD | RTLD_FIRST);
2562 void *addr = dlsym(dlh, "__CFConstantStringClassReference");
2564 addFutureClass("NSCFString", (class_t *)addr);
2565 hackedNSCFString = YES;
2571 // Discover classes. Fix up unresolved future classes. Mark bundle classes.
2572 NXMapTable *future_class_map = futureClasses();
2574 class_t **classlist = _getObjc2ClassList(hi, &count);
2575 for (i = 0; i < count; i++) {
2576 const char *name = getName(classlist[i]);
2578 if (missingWeakSuperclass(classlist[i])) {
2579 // No superclass (probably weak-linked).
2580 // Disavow any knowledge of this subclass.
2581 if (PrintConnecting) {
2582 _objc_inform("CLASS: IGNORING class '%s' with "
2583 "missing weak-linked superclass", name);
2585 addRemappedClass(classlist[i], NULL);
2586 classlist[i]->superclass = NULL;
2587 classlist[i] = NULL;
2591 if (NXCountMapTable(future_class_map) > 0) {
2592 class_t *newCls = NXMapGet(future_class_map, name);
2594 // Copy class_t to future class's struct.
2595 // Preserve future's rw data block.
2596 class_rw_t *rw = newCls->data;
2597 memcpy(newCls, classlist[i], sizeof(class_t));
2598 rw->ro = (class_ro_t *)newCls->data;
2601 removeFutureClass(name);
2602 addRemappedClass(classlist[i], newCls);
2603 classlist[i] = newCls;
2604 // Non-lazily realize the class below.
2605 resolvedFutureClasses = (class_t **)
2606 _realloc_internal(resolvedFutureClasses,
2607 (resolvedFutureClassCount+1)
2608 * sizeof(class_t *));
2609 resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
2612 addNamedClass(classlist[i], name);
2613 addUninitializedClass(classlist[i], classlist[i]->isa);
2614 if (hi->mhdr->filetype == MH_BUNDLE) {
2615 classlist[i]->data->flags |= RO_FROM_BUNDLE;
2616 classlist[i]->isa->data->flags |= RO_FROM_BUNDLE;
2621 // Fix up remapped classes
2622 // classlist is up to date, but classrefs may not be
2624 if (!noClassesRemapped()) {
2626 class_t **classrefs = _getObjc2ClassRefs(hi, &count);
2627 for (i = 0; i < count; i++) {
2628 remapClassRef(&classrefs[i]);
2630 // fixme why doesn't test future1 catch the absence of this?
2631 classrefs = _getObjc2SuperRefs(hi, &count);
2632 for (i = 0; i < count; i++) {
2633 remapClassRef(&classrefs[i]);
2639 // Fix up @selector references
2643 if (sel_preoptimizationValid(hi)) {
2644 _objc_inform("PREOPTIMIZATION: honoring preoptimized selectors in %s",
2645 _nameForHeader(hi->mhdr));
2647 else if (_objcHeaderOptimizedByDyld(hi)) {
2648 _objc_inform("PREOPTIMIZATION: IGNORING preoptimized selectors in %s",
2649 _nameForHeader(hi->mhdr));
2653 if (sel_preoptimizationValid(hi)) continue;
2655 SEL *sels = _getObjc2SelectorRefs(hi, &count);
2656 BOOL isBundle = hi->mhdr->filetype == MH_BUNDLE;
2657 for (i = 0; i < count; i++) {
2658 sels[i] = sel_registerNameNoLock((const char *)sels[i], isBundle);
2663 // Discover protocols. Fix up protocol refs.
2664 NXMapTable *protocol_map = protocols();
2666 extern struct class_t OBJC_CLASS_$_Protocol;
2667 Class cls = (Class)&OBJC_CLASS_$_Protocol;
2669 protocol_t **protocols = _getObjc2ProtocolList(hi, &count);
2670 // fixme duplicate protocol from bundle
2671 for (i = 0; i < count; i++) {
2672 if (!NXMapGet(protocol_map, protocols[i]->name)) {
2673 protocols[i]->isa = cls;
2674 NXMapKeyCopyingInsert(protocol_map,
2675 protocols[i]->name, protocols[i]);
2676 if (PrintProtocols) {
2677 _objc_inform("PROTOCOLS: protocol at %p is %s",
2678 protocols[i], protocols[i]->name);
2681 if (PrintProtocols) {
2682 _objc_inform("PROTOCOLS: protocol at %p is %s (duplicate)",
2683 protocols[i], protocols[i]->name);
2689 protocol_t **protocols;
2690 protocols = _getObjc2ProtocolRefs(hi, &count);
2691 for (i = 0; i < count; i++) {
2692 remapProtocolRef(&protocols[i]);
2696 // Realize non-lazy classes (for +load methods and static instances)
2698 class_t **classlist =
2699 _getObjc2NonlazyClassList(hi, &count);
2700 for (i = 0; i < count; i++) {
2701 realizeClass(remapClass(classlist[i]));
2705 // Realize newly-resolved future classes, in case CF manipulates them
2706 if (resolvedFutureClasses) {
2707 for (i = 0; i < resolvedFutureClassCount; i++) {
2708 realizeClass(resolvedFutureClasses[i]);
2710 _free_internal(resolvedFutureClasses);
2713 // Discover categories.
2715 category_t **catlist =
2716 _getObjc2CategoryList(hi, &count);
2717 for (i = 0; i < count; i++) {
2718 category_t *cat = catlist[i];
2719 // Do NOT use cat->cls! It may have been remapped.
2720 class_t *cls = remapClass(cat->cls);
2723 // Category's target class is missing (probably weak-linked).
2724 // Disavow any knowledge of this category.
2726 if (PrintConnecting) {
2727 _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
2728 "missing weak-linked target class",
2734 // Process this category.
2735 // First, register the category with its target class.
2736 // Then, rebuild the class's method lists (etc) if
2737 // the class is realized.
2738 BOOL classExists = NO;
2739 if (cat->instanceMethods || cat->protocols
2740 || cat->instanceProperties)
2742 addUnattachedCategoryForClass(cat, cls, hi);
2743 if (isRealized(cls)) {
2744 remethodizeClass(cls);
2747 if (PrintConnecting) {
2748 _objc_inform("CLASS: found category -%s(%s) %s",
2749 getName(cls), cat->name,
2750 classExists ? "on existing class" : "");
2754 if (cat->classMethods || cat->protocols
2755 /* || cat->classProperties */)
2757 addUnattachedCategoryForClass(cat, cls->isa, hi);
2758 if (isRealized(cls->isa)) {
2759 remethodizeClass(cls->isa);
2761 if (PrintConnecting) {
2762 _objc_inform("CLASS: found category +%s(%s)",
2763 getName(cls), cat->name);
2769 // Category discovery MUST BE LAST to avoid potential races
2770 // when other threads call the new category code before
2771 // this thread finishes its fixups.
2773 // +load handled by prepare_load_methods()
2779 /***********************************************************************
2780 * prepare_load_methods
2781 * Schedule +load for classes in this image, any un-+load-ed
2782 * superclasses in other images, and any categories in this image.
2783 **********************************************************************/
2784 // Recursively schedule +load for cls and any un-+load-ed superclasses.
2785 // cls must already be connected.
2786 static void schedule_class_load(class_t *cls)
2789 assert(isRealized(cls)); // _read_images should realize
2791 if (cls->data->flags & RW_LOADED) return;
2793 // Ensure superclass-first ordering
2794 schedule_class_load(getSuperclass(cls));
2796 add_class_to_loadable_list((Class)cls);
2797 changeInfo(cls, RW_LOADED, 0);
2800 __private_extern__ void prepare_load_methods(header_info *hi)
2804 rwlock_assert_writing(&runtimeLock);
2806 class_t **classlist =
2807 _getObjc2NonlazyClassList(hi, &count);
2808 for (i = 0; i < count; i++) {
2809 schedule_class_load(remapClass(classlist[i]));
2812 category_t **categorylist = _getObjc2NonlazyCategoryList(hi, &count);
2813 for (i = 0; i < count; i++) {
2814 category_t *cat = categorylist[i];
2815 // Do NOT use cat->cls! It may have been remapped.
2816 class_t *cls = remapClass(cat->cls);
2817 if (!cls) continue; // category for ignored weak-linked class
2819 assert(isRealized(cls->isa));
2820 add_category_to_loadable_list((Category)cat);
2825 /***********************************************************************
2827 * Only handles MH_BUNDLE for now.
2828 * Locking: write-lock and loadMethodLock acquired by unmap_image
2829 **********************************************************************/
2830 __private_extern__ void _unload_image(header_info *hi)
2834 recursive_mutex_assert_locked(&loadMethodLock);
2835 rwlock_assert_writing(&runtimeLock);
2837 // Unload unattached categories and categories waiting for +load.
2839 category_t **catlist = _getObjc2CategoryList(hi, &count);
2840 for (i = 0; i < count; i++) {
2841 category_t *cat = catlist[i];
2842 if (!cat) continue; // category for ignored weak-linked class
2843 class_t *cls = remapClass(cat->cls);
2844 assert(cls); // shouldn't have live category for dead class
2846 // fixme for MH_DYLIB cat's class may have been unloaded already
2849 removeUnattachedCategoryForClass(cat, cls);
2852 remove_category_from_loadable_list((Category)cat);
2857 class_t **classlist = _getObjc2ClassList(hi, &count);
2858 for (i = 0; i < count; i++) {
2859 class_t *cls = classlist[i];
2860 // fixme remapped classes?
2861 // fixme ignored weak-linked classes
2863 remove_class_from_loadable_list((Class)cls);
2864 unload_class(cls->isa, YES);
2865 unload_class(cls, NO);
2869 // Clean up protocols.
2870 #warning fixme protocol unload
2872 // fixme DebugUnload
2876 /***********************************************************************
2877 * method_getDescription
2878 * Returns a pointer to this method's objc_method_description.
2880 **********************************************************************/
2881 struct objc_method_description *
2882 method_getDescription(Method m)
2884 if (!m) return NULL;
2885 return (struct objc_method_description *)newmethod(m);
2889 /***********************************************************************
2890 * method_getImplementation
2891 * Returns this method's IMP.
2893 **********************************************************************/
2895 _method_getImplementation(method_t *m)
2897 if (!m) return NULL;
2902 method_getImplementation(Method m)
2904 return _method_getImplementation(newmethod(m));
2908 /***********************************************************************
2910 * Returns this method's selector.
2911 * The method must not be NULL.
2912 * The method must already have been fixed-up.
2914 **********************************************************************/
2916 method_getName(Method m_gen)
2918 struct method_t *m = newmethod(m_gen);
2919 if (!m) return NULL;
2921 assert((SEL)m->name == sel_registerName((char *)m->name));
2922 return (SEL)m->name;
2926 /***********************************************************************
2927 * method_getTypeEncoding
2928 * Returns this method's old-style type encoding string.
2929 * The method must not be NULL.
2931 **********************************************************************/
2933 method_getTypeEncoding(Method m)
2935 if (!m) return NULL;
2936 return newmethod(m)->types;
2940 /***********************************************************************
2941 * method_setImplementation
2942 * Sets this method's implementation to imp.
2943 * The previous implementation is returned.
2944 **********************************************************************/
2946 _method_setImplementation(class_t *cls, method_t *m, IMP imp)
2948 rwlock_assert_writing(&runtimeLock);
2950 if (!m) return NULL;
2951 if (!imp) return NULL;
2953 if (m->name == (SEL)kIgnore) {
2954 // Ignored methods stay ignored
2958 IMP old = _method_getImplementation(m);
2961 // No cache flushing needed - cache contains Methods not IMPs.
2963 if (vtable_containsSelector(newmethod(m)->name)) {
2964 // Will be slow if cls is NULL (i.e. unknown)
2965 // fixme build list of classes whose Methods are known externally?
2969 // fixme update monomorphism if necessary
2975 method_setImplementation(Method m, IMP imp)
2977 // Don't know the class - will be slow if vtables are affected
2978 // fixme build list of classes whose Methods are known externally?
2980 rwlock_write(&runtimeLock);
2981 result = _method_setImplementation(Nil, newmethod(m), imp);
2982 rwlock_unlock_write(&runtimeLock);
2987 void method_exchangeImplementations(Method m1_gen, Method m2_gen)
2989 method_t *m1 = newmethod(m1_gen);
2990 method_t *m2 = newmethod(m2_gen);
2991 if (!m1 || !m2) return;
2993 rwlock_write(&runtimeLock);
2995 if (m1->name == (SEL)kIgnore || m2->name == (SEL)kIgnore) {
2996 // Ignored methods stay ignored. Now they're both ignored.
2997 m1->imp = (IMP)&_objc_ignored_method;
2998 m2->imp = (IMP)&_objc_ignored_method;
2999 rwlock_unlock_write(&runtimeLock);
3003 IMP m1_imp = m1->imp;
3007 if (vtable_containsSelector(m1->name) ||
3008 vtable_containsSelector(m2->name))
3010 // Don't know the class - will be slow if vtables are affected
3011 // fixme build list of classes whose Methods are known externally?
3015 // fixme update monomorphism if necessary
3017 rwlock_unlock_write(&runtimeLock);
3021 /***********************************************************************
3025 **********************************************************************/
3027 ivar_getOffset(Ivar ivar)
3029 if (!ivar) return 0;
3030 return *newivar(ivar)->offset;
3034 /***********************************************************************
3038 **********************************************************************/
3040 ivar_getName(Ivar ivar)
3042 if (!ivar) return NULL;
3043 return newivar(ivar)->name;
3047 /***********************************************************************
3048 * ivar_getTypeEncoding
3051 **********************************************************************/
3053 ivar_getTypeEncoding(Ivar ivar)
3055 if (!ivar) return NULL;
3056 return newivar(ivar)->type;
3060 /***********************************************************************
3061 * _protocol_getMethod_nolock
3062 * Locking: runtimeLock must be write-locked by the caller
3063 **********************************************************************/
3065 _protocol_getMethod_nolock(protocol_t *proto, SEL sel,
3066 BOOL isRequiredMethod, BOOL isInstanceMethod)
3068 rwlock_assert_writing(&runtimeLock);
3071 if (!proto || !sel) return NULL;
3073 method_list_t **mlistp = NULL;
3075 if (isRequiredMethod) {
3076 if (isInstanceMethod) {
3077 mlistp = &proto->instanceMethods;
3079 mlistp = &proto->classMethods;
3082 if (isInstanceMethod) {
3083 mlistp = &proto->optionalInstanceMethods;
3085 mlistp = &proto->optionalClassMethods;
3090 method_list_t *mlist = *mlistp;
3091 if (!isMethodListFixedUp(mlist)) {
3092 mlist = _memdup_internal(mlist, method_list_size(mlist));
3093 fixupMethodList(mlist, YES/*always copy for simplicity*/);
3096 for (i = 0; i < mlist->count; i++) {
3097 method_t *m = method_list_nth(mlist, i);
3098 if (sel == m->name) return (Method)m;
3102 if (proto->protocols) {
3104 for (i = 0; i < proto->protocols->count; i++) {
3105 protocol_t *realProto = remapProtocol(proto->protocols->list[i]);
3106 m = _protocol_getMethod_nolock(realProto, sel,
3107 isRequiredMethod, isInstanceMethod);
3116 /***********************************************************************
3117 * _protocol_getMethod
3119 * Locking: write-locks runtimeLock
3120 **********************************************************************/
3121 __private_extern__ Method
3122 _protocol_getMethod(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod)
3124 rwlock_write(&runtimeLock);
3125 Method result = _protocol_getMethod_nolock(newprotocol(p), sel,
3128 rwlock_unlock_write(&runtimeLock);
3133 /***********************************************************************
3135 * Returns the name of the given protocol.
3136 * Locking: runtimeLock must not be held by the caller
3137 **********************************************************************/
3139 protocol_getName(Protocol *proto)
3141 return newprotocol(proto)->name;
3145 /***********************************************************************
3146 * protocol_getInstanceMethodDescription
3147 * Returns the description of a named instance method.
3148 * Locking: runtimeLock must not be held by the caller
3149 **********************************************************************/
3150 struct objc_method_description
3151 protocol_getMethodDescription(Protocol *p, SEL aSel,
3152 BOOL isRequiredMethod, BOOL isInstanceMethod)
3155 _protocol_getMethod(p, aSel, isRequiredMethod, isInstanceMethod);
3156 if (m) return *method_getDescription(m);
3157 else return (struct objc_method_description){NULL, NULL};
3161 /***********************************************************************
3162 * _protocol_conformsToProtocol_nolock
3163 * Returns YES if self conforms to other.
3164 * Locking: runtimeLock must be held by the caller.
3165 **********************************************************************/
3166 static BOOL _protocol_conformsToProtocol_nolock(protocol_t *self, protocol_t *other)
3168 if (!self || !other) {
3172 if (0 == strcmp(self->name, other->name)) {
3176 if (self->protocols) {
3178 for (i = 0; i < self->protocols->count; i++) {
3179 protocol_t *proto = remapProtocol(self->protocols->list[i]);
3180 if (0 == strcmp(other->name, proto->name)) {
3183 if (_protocol_conformsToProtocol_nolock(proto, other)) {
3193 /***********************************************************************
3194 * protocol_conformsToProtocol
3195 * Returns YES if self conforms to other.
3196 * Locking: acquires runtimeLock
3197 **********************************************************************/
3198 BOOL protocol_conformsToProtocol(Protocol *self, Protocol *other)
3201 rwlock_read(&runtimeLock);
3202 result = _protocol_conformsToProtocol_nolock(newprotocol(self),
3203 newprotocol(other));
3204 rwlock_unlock_read(&runtimeLock);
3209 /***********************************************************************
3211 * Return YES if two protocols are equal (i.e. conform to each other)
3212 * Locking: acquires runtimeLock
3213 **********************************************************************/
3214 BOOL protocol_isEqual(Protocol *self, Protocol *other)
3216 if (self == other) return YES;
3217 if (!self || !other) return NO;
3219 if (!protocol_conformsToProtocol(self, other)) return NO;
3220 if (!protocol_conformsToProtocol(other, self)) return NO;
3226 /***********************************************************************
3227 * protocol_copyMethodDescriptionList
3228 * Returns descriptions of a protocol's methods.
3229 * Locking: acquires runtimeLock
3230 **********************************************************************/
3231 struct objc_method_description *
3232 protocol_copyMethodDescriptionList(Protocol *p,
3233 BOOL isRequiredMethod,BOOL isInstanceMethod,
3234 unsigned int *outCount)
3236 struct protocol_t *proto = newprotocol(p);
3237 struct objc_method_description *result = NULL;
3238 unsigned int count = 0;
3241 if (outCount) *outCount = 0;
3245 rwlock_read(&runtimeLock);
3247 method_list_t *mlist = NULL;
3249 if (isRequiredMethod) {
3250 if (isInstanceMethod) {
3251 mlist = proto->instanceMethods;
3253 mlist = proto->classMethods;
3256 if (isInstanceMethod) {
3257 mlist = proto->optionalInstanceMethods;
3259 mlist = proto->optionalClassMethods;
3265 count = mlist->count;
3266 result = calloc(count + 1, sizeof(struct objc_method_description));
3267 for (i = 0; i < count; i++) {
3268 method_t *m = method_list_nth(mlist, i);
3269 result[i].name = sel_registerName((const char *)m->name);
3270 result[i].types = (char *)m->types;
3274 rwlock_unlock_read(&runtimeLock);
3276 if (outCount) *outCount = count;
3281 /***********************************************************************
3282 * protocol_getProperty
3284 * Locking: acquires runtimeLock
3285 **********************************************************************/
3287 _protocol_getProperty_nolock(protocol_t *proto, const char *name,
3288 BOOL isRequiredProperty, BOOL isInstanceProperty)
3290 if (!isRequiredProperty || !isInstanceProperty) {
3291 // Only required instance properties are currently supported
3295 struct objc_property_list *plist;
3296 if ((plist = proto->instanceProperties)) {
3298 for (i = 0; i < plist->count; i++) {
3299 Property prop = property_list_nth(plist, i);
3300 if (0 == strcmp(name, prop->name)) {
3306 if (proto->protocols) {
3308 for (i = 0; i < proto->protocols->count; i++) {
3309 protocol_t *p = remapProtocol(proto->protocols->list[i]);
3311 _protocol_getProperty_nolock(p, name,
3313 isInstanceProperty);
3314 if (prop) return prop;
3321 Property protocol_getProperty(Protocol *p, const char *name,
3322 BOOL isRequiredProperty, BOOL isInstanceProperty)
3326 if (!p || !name) return NULL;
3328 rwlock_read(&runtimeLock);
3329 result = _protocol_getProperty_nolock(newprotocol(p), name,
3331 isInstanceProperty);
3332 rwlock_unlock_read(&runtimeLock);
3338 /***********************************************************************
3339 * protocol_copyPropertyList
3341 * Locking: acquires runtimeLock
3342 **********************************************************************/
3343 Property *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
3345 Property *result = NULL;
3348 if (outCount) *outCount = 0;
3352 rwlock_read(&runtimeLock);
3354 struct objc_property_list *plist = newprotocol(proto)->instanceProperties;
3355 result = copyPropertyList(plist, outCount);
3357 rwlock_unlock_read(&runtimeLock);
3363 /***********************************************************************
3364 * protocol_copyProtocolList
3365 * Copies this protocol's incorporated protocols.
3366 * Does not copy those protocol's incorporated protocols in turn.
3367 * Locking: acquires runtimeLock
3368 **********************************************************************/
3369 Protocol **protocol_copyProtocolList(Protocol *p, unsigned int *outCount)
3371 unsigned int count = 0;
3372 Protocol **result = NULL;
3373 protocol_t *proto = newprotocol(p);
3376 if (outCount) *outCount = 0;
3380 rwlock_read(&runtimeLock);
3382 if (proto->protocols) {
3383 count = (unsigned int)proto->protocols->count;
3386 result = malloc((count+1) * sizeof(Protocol *));
3389 for (i = 0; i < count; i++) {
3390 result[i] = (Protocol *)remapProtocol(proto->protocols->list[i]);
3395 rwlock_unlock_read(&runtimeLock);
3397 if (outCount) *outCount = count;
3402 /***********************************************************************
3404 * Returns pointers to all classes.
3405 * This requires all classes be realized, which is regretfully non-lazy.
3406 * Locking: acquires runtimeLock
3407 **********************************************************************/
3409 objc_getClassList(Class *buffer, int bufferLen)
3411 rwlock_write(&runtimeLock);
3413 realizeAllClasses();
3418 NXHashTable *classes = realizedClasses();
3419 int allCount = NXCountHashTable(classes);
3422 rwlock_unlock_write(&runtimeLock);
3427 state = NXInitHashState(classes);
3428 while (count < bufferLen &&
3429 NXNextHashState(classes, &state, (void **)&cls))
3431 buffer[count++] = (Class)cls;
3434 rwlock_unlock_write(&runtimeLock);
3440 /***********************************************************************
3441 * objc_copyProtocolList
3442 * Returns pointers to all protocols.
3443 * Locking: read-locks runtimeLock
3444 **********************************************************************/
3446 objc_copyProtocolList(unsigned int *outCount)
3448 rwlock_read(&runtimeLock);
3454 NXMapTable *protocol_map = protocols();
3457 count = NXCountMapTable(protocol_map);
3459 rwlock_unlock_read(&runtimeLock);
3460 if (outCount) *outCount = 0;
3464 result = calloc(1 + count, sizeof(Protocol *));
3467 state = NXInitMapState(protocol_map);
3468 while (NXNextMapState(protocol_map, &state,
3469 (const void **)&name, (const void **)&proto))
3471 result[i++] = proto;
3475 assert(i == count+1);
3477 rwlock_unlock_read(&runtimeLock);
3479 if (outCount) *outCount = count;
3484 /***********************************************************************
3486 * Get a protocol by name, or return NULL
3487 * Locking: read-locks runtimeLock
3488 **********************************************************************/
3489 Protocol *objc_getProtocol(const char *name)
3491 rwlock_read(&runtimeLock);
3492 Protocol *result = (Protocol *)NXMapGet(protocols(), name);
3493 rwlock_unlock_read(&runtimeLock);
3498 /***********************************************************************
3499 * class_copyMethodList
3501 * Locking: read-locks runtimeLock
3502 **********************************************************************/
3504 class_copyMethodList(Class cls_gen, unsigned int *outCount)
3506 struct class_t *cls = newcls(cls_gen);
3507 unsigned int count = 0;
3508 Method *result = NULL;
3511 if (outCount) *outCount = 0;
3515 rwlock_read(&runtimeLock);
3517 assert(isRealized(cls));
3519 FOREACH_METHOD_LIST(mlist, cls, {
3520 count += mlist->count;
3525 result = malloc((count + 1) * sizeof(Method));
3528 FOREACH_METHOD_LIST(mlist, cls, {
3530 for (i = 0; i < mlist->count; i++) {
3531 Method aMethod = (Method)method_list_nth(mlist, i);
3532 if (method_getName(aMethod) == (SEL)kIgnore) {
3536 result[m++] = aMethod;
3542 rwlock_unlock_read(&runtimeLock);
3544 if (outCount) *outCount = count;
3549 /***********************************************************************
3550 * class_copyIvarList
3552 * Locking: read-locks runtimeLock
3553 **********************************************************************/
3555 class_copyIvarList(Class cls_gen, unsigned int *outCount)
3557 struct class_t *cls = newcls(cls_gen);
3558 const ivar_list_t *ivars;
3559 Ivar *result = NULL;
3560 unsigned int count = 0;
3564 if (outCount) *outCount = 0;
3568 rwlock_read(&runtimeLock);
3570 assert(isRealized(cls));
3572 if ((ivars = cls->data->ro->ivars) && ivars->count) {
3573 result = malloc((ivars->count+1) * sizeof(Ivar));
3575 for (i = 0; i < ivars->count; i++) {
3576 ivar_t *ivar = ivar_list_nth(ivars, i);
3577 if (!ivar->offset) continue; // anonymous bitfield
3578 result[count++] = (Ivar)ivar;
3580 result[count] = NULL;
3583 rwlock_unlock_read(&runtimeLock);
3585 if (outCount) *outCount = count;
3590 /***********************************************************************
3591 * class_copyPropertyList. Returns a heap block containing the
3592 * properties declared in the class, or NULL if the class
3593 * declares no properties. Caller must free the block.
3594 * Does not copy any superclass's properties.
3595 * Locking: read-locks runtimeLock
3596 **********************************************************************/
3598 class_copyPropertyList(Class cls_gen, unsigned int *outCount)
3600 struct class_t *cls = newcls(cls_gen);
3601 chained_property_list *plist;
3602 unsigned int count = 0;
3603 Property *result = NULL;
3606 if (outCount) *outCount = 0;
3610 rwlock_read(&runtimeLock);
3612 assert(isRealized(cls));
3614 for (plist = cls->data->properties; plist; plist = plist->next) {
3615 count += plist->count;
3620 result = malloc((count + 1) * sizeof(Property));
3623 for (plist = cls->data->properties; plist; plist = plist->next) {
3625 for (i = 0; i < plist->count; i++) {
3626 result[p++] = (Property)&plist->list[i];
3632 rwlock_unlock_read(&runtimeLock);
3634 if (outCount) *outCount = count;
3639 /***********************************************************************
3640 * _class_getLoadMethod
3642 * Called only from add_class_to_loadable_list.
3643 * Locking: runtimeLock must be read- or write-locked by the caller.
3644 **********************************************************************/
3645 __private_extern__ IMP
3646 _class_getLoadMethod(Class cls_gen)
3648 rwlock_assert_locked(&runtimeLock);
3650 struct class_t *cls = newcls(cls_gen);
3651 const method_list_t *mlist;
3654 assert(isRealized(cls));
3655 assert(isRealized(cls->isa));
3656 assert(!isMetaClass(cls));
3657 assert(isMetaClass(cls->isa));
3659 mlist = cls->isa->data->ro->baseMethods;
3660 if (mlist) for (i = 0; i < mlist->count; i++) {
3661 method_t *m = method_list_nth(mlist, i);
3662 if (0 == strcmp((const char *)m->name, "load")) {
3671 /***********************************************************************
3673 * Returns a category's name.
3675 **********************************************************************/
3676 __private_extern__ const char *
3677 _category_getName(Category cat)
3679 return newcategory(cat)->name;
3683 /***********************************************************************
3684 * _category_getClassName
3685 * Returns a category's class's name
3686 * Called only from add_category_to_loadable_list and
3687 * remove_category_from_loadable_list.
3688 * Locking: runtimeLock must be read- or write-locked by the caller
3689 **********************************************************************/
3690 __private_extern__ const char *
3691 _category_getClassName(Category cat)
3693 rwlock_assert_locked(&runtimeLock);
3694 // cat->cls may have been remapped
3695 return getName(remapClass(newcategory(cat)->cls));
3699 /***********************************************************************
3700 * _category_getClass
3701 * Returns a category's class
3702 * Called only by call_category_loads.
3703 * Locking: read-locks runtimeLock
3704 **********************************************************************/
3705 __private_extern__ Class
3706 _category_getClass(Category cat)
3708 rwlock_read(&runtimeLock);
3709 // cat->cls may have been remapped
3710 struct class_t *result = remapClass(newcategory(cat)->cls);
3711 assert(isRealized(result)); // ok for call_category_loads' usage
3712 rwlock_unlock_read(&runtimeLock);
3713 return (Class)result;
3717 /***********************************************************************
3718 * _category_getLoadMethod
3720 * Called only from add_category_to_loadable_list
3721 * Locking: runtimeLock must be read- or write-locked by the caller
3722 **********************************************************************/
3723 __private_extern__ IMP
3724 _category_getLoadMethod(Category cat)
3726 rwlock_assert_locked(&runtimeLock);
3728 const method_list_t *mlist;
3731 mlist = newcategory(cat)->classMethods;
3732 if (mlist) for (i = 0; i < mlist->count; i++) {
3733 method_t *m = method_list_nth(mlist, i);
3734 if (0 == strcmp((const char *)m->name, "load")) {
3743 /***********************************************************************
3744 * class_copyProtocolList
3746 * Locking: read-locks runtimeLock
3747 **********************************************************************/
3749 class_copyProtocolList(Class cls_gen, unsigned int *outCount)
3751 struct class_t *cls = newcls(cls_gen);
3753 struct protocol_list_t **p;
3754 unsigned int count = 0;
3756 Protocol **result = NULL;
3759 if (outCount) *outCount = 0;
3763 rwlock_read(&runtimeLock);
3765 assert(isRealized(cls));
3767 for (p = cls->data->protocols; p && *p; p++) {
3768 count += (uint32_t)(*p)->count;
3772 result = malloc((count+1) * sizeof(Protocol *));
3774 for (p = cls->data->protocols; p && *p; p++) {
3775 for (i = 0; i < (*p)->count; i++) {
3776 *r++ = (Protocol *)remapProtocol((*p)->list[i]);
3782 rwlock_unlock_read(&runtimeLock);
3784 if (outCount) *outCount = count;
3789 /***********************************************************************
3790 * _objc_copyClassNamesForImage
3792 * Locking: read-locks runtimeLock
3793 **********************************************************************/
3794 __private_extern__ const char **
3795 _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount)
3797 size_t count, i, shift;
3798 class_t **classlist;
3801 rwlock_read(&runtimeLock);
3803 classlist = _getObjc2ClassList(hi, &count);
3804 names = malloc((count+1) * sizeof(const char *));
3807 for (i = 0; i < count; i++) {
3808 class_t *cls = remapClass(classlist[i]);
3810 names[i-shift] = getName(classlist[i]);
3812 shift++; // ignored weak-linked class
3816 names[count] = NULL;
3818 rwlock_unlock_read(&runtimeLock);
3820 if (outCount) *outCount = (unsigned int)count;
3825 /***********************************************************************
3829 **********************************************************************/
3830 __private_extern__ Cache
3831 _class_getCache(Class cls)
3833 return newcls(cls)->cache;
3837 /***********************************************************************
3838 * _class_getInstanceSize
3841 **********************************************************************/
3842 __private_extern__ size_t
3843 _class_getInstanceSize(Class cls)
3846 return instanceSize(newcls(cls));
3850 instanceSize(struct class_t *cls)
3853 assert(isRealized(cls));
3854 // fixme rdar://5244378
3855 return (uint32_t)((cls->data->ro->instanceSize + WORD_MASK) & ~WORD_MASK);
3859 /***********************************************************************
3863 **********************************************************************/
3865 class_getVersion(Class cls)
3868 assert(isRealized(newcls(cls)));
3869 return newcls(cls)->data->version;
3873 /***********************************************************************
3877 **********************************************************************/
3878 __private_extern__ void
3879 _class_setCache(Class cls, Cache cache)
3881 newcls(cls)->cache = cache;
3885 /***********************************************************************
3889 **********************************************************************/
3891 class_setVersion(Class cls, int version)
3894 assert(isRealized(newcls(cls)));
3895 newcls(cls)->data->version = version;
3899 /***********************************************************************
3902 * Locking: acquires runtimeLock
3903 **********************************************************************/
3904 __private_extern__ const char *_class_getName(Class cls)
3906 if (!cls) return "nil";
3907 // fixme hack rwlock_write(&runtimeLock);
3908 const char *name = getName(newcls(cls));
3909 // rwlock_unlock_write(&runtimeLock);
3914 /***********************************************************************
3917 * Locking: runtimeLock must be held by the caller
3918 **********************************************************************/
3920 getName(struct class_t *cls)
3922 // fixme hack rwlock_assert_writing(&runtimeLock);
3925 if (isRealized(cls)) {
3926 return cls->data->ro->name;
3928 return ((const struct class_ro_t *)cls->data)->name;
3933 /***********************************************************************
3934 * getMethodNoSuper_nolock
3936 * Locking: runtimeLock must be read- or write-locked by the caller
3937 **********************************************************************/
3939 getMethodNoSuper_nolock(struct class_t *cls, SEL sel)
3941 rwlock_assert_locked(&runtimeLock);
3945 assert(isRealized(cls));
3949 FOREACH_METHOD_LIST(mlist, cls, {
3950 for (i = 0; i < mlist->count; i++) {
3951 method_t *m = method_list_nth(mlist, i);
3952 if (m->name == sel) return m;
3960 /***********************************************************************
3961 * _class_getMethodNoSuper
3963 * Locking: read-locks runtimeLock
3964 **********************************************************************/
3965 __private_extern__ Method
3966 _class_getMethodNoSuper(Class cls, SEL sel)
3968 rwlock_read(&runtimeLock);
3969 Method result = (Method)getMethodNoSuper_nolock(newcls(cls), sel);
3970 rwlock_unlock_read(&runtimeLock);
3974 /***********************************************************************
3975 * _class_getMethodNoSuper
3976 * For use inside lockForMethodLookup() only.
3977 * Locking: read-locks runtimeLock
3978 **********************************************************************/
3979 __private_extern__ Method
3980 _class_getMethodNoSuper_nolock(Class cls, SEL sel)
3982 return (Method)getMethodNoSuper_nolock(newcls(cls), sel);
3986 /***********************************************************************
3989 * Locking: runtimeLock must be read- or write-locked by the caller
3990 **********************************************************************/
3992 getMethod_nolock(class_t *cls, SEL sel)
3996 rwlock_assert_locked(&runtimeLock);
4001 assert(isRealized(cls));
4003 while (cls && ((m = getMethodNoSuper_nolock(cls, sel))) == NULL) {
4004 cls = getSuperclass(cls);
4011 /***********************************************************************
4014 * Locking: read-locks runtimeLock
4015 **********************************************************************/
4016 __private_extern__ Method _class_getMethod(Class cls, SEL sel)
4019 rwlock_read(&runtimeLock);
4020 m = (Method)getMethod_nolock(newcls(cls), sel);
4021 rwlock_unlock_read(&runtimeLock);
4026 /***********************************************************************
4027 * ABI-specific lookUpMethod helpers.
4028 * Locking: read- and write-locks runtimeLock.
4029 **********************************************************************/
4030 __private_extern__ void lockForMethodLookup(void)
4032 rwlock_read(&runtimeLock);
4034 __private_extern__ void unlockForMethodLookup(void)
4036 rwlock_unlock_read(&runtimeLock);
4039 __private_extern__ IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init)
4041 rwlock_assert_unlocked(&runtimeLock);
4043 if (!isRealized(newcls(cls))) {
4044 rwlock_write(&runtimeLock);
4045 realizeClass(newcls(cls));
4046 rwlock_unlock_write(&runtimeLock);
4049 if (init && !_class_isInitialized(cls)) {
4050 _class_initialize (cls);
4051 // If sel == initialize, _class_initialize will send +initialize and
4052 // then the messenger will send +initialize again after this
4053 // procedure finishes. Of course, if this is not being called
4054 // from the messenger then it won't happen. 2778172
4061 /***********************************************************************
4064 * Locking: read-locks runtimeLock
4065 **********************************************************************/
4066 Property class_getProperty(Class cls_gen, const char *name)
4068 Property result = NULL;
4069 chained_property_list *plist;
4070 struct class_t *cls = newcls(cls_gen);
4072 if (!cls || !name) return NULL;
4074 rwlock_read(&runtimeLock);
4076 assert(isRealized(cls));
4078 for ( ; cls; cls = getSuperclass(cls)) {
4079 for (plist = cls->data->properties; plist; plist = plist->next) {
4081 for (i = 0; i < plist->count; i++) {
4082 if (0 == strcmp(name, plist->list[i].name)) {
4083 result = &plist->list[i];
4091 rwlock_unlock_read(&runtimeLock);
4097 /***********************************************************************
4099 **********************************************************************/
4100 __private_extern__ BOOL _class_isMetaClass(Class cls)
4102 if (!cls) return NO;
4103 return isMetaClass(newcls(cls));
4107 isMetaClass(struct class_t *cls)
4110 assert(isRealized(cls));
4111 return (cls->data->ro->flags & RO_META) ? YES : NO;
4115 __private_extern__ Class _class_getMeta(Class cls)
4118 if (isMetaClass(newcls(cls))) return cls;
4119 else return ((id)cls)->isa;
4122 Class gdb_class_getClass(Class cls)
4124 const char *className = strdup(getName(newcls(cls)));
4125 if(!className) return Nil;
4126 Class rCls = look_up_class(className, NO, NO);
4127 free((char*)className);
4131 BOOL gdb_objc_isRuntimeLocked()
4133 if (rwlock_try_write(&runtimeLock)) {
4134 rwlock_unlock_write(&runtimeLock);
4138 if (mutex_try_lock(&cacheUpdateLock)) {
4139 mutex_unlock(&cacheUpdateLock);
4146 /***********************************************************************
4148 **********************************************************************/
4149 __private_extern__ BOOL
4150 _class_isInitializing(Class cls_gen)
4152 struct class_t *cls = newcls(_class_getMeta(cls_gen));
4153 return (cls->data->flags & RW_INITIALIZING) ? YES : NO;
4157 /***********************************************************************
4159 **********************************************************************/
4160 __private_extern__ BOOL
4161 _class_isInitialized(Class cls_gen)
4163 struct class_t *cls = newcls(_class_getMeta(cls_gen));
4164 return (cls->data->flags & RW_INITIALIZED) ? YES : NO;
4168 /***********************************************************************
4170 **********************************************************************/
4171 __private_extern__ void
4172 _class_setInitializing(Class cls_gen)
4174 struct class_t *cls = newcls(_class_getMeta(cls_gen));
4175 changeInfo(cls, RW_INITIALIZING, 0);
4179 /***********************************************************************
4180 * Locking: write-locks runtimeLock
4181 **********************************************************************/
4182 __private_extern__ void
4183 _class_setInitialized(Class cls_gen)
4186 struct class_t *metacls;
4187 struct class_t *cls;
4189 rwlock_write(&runtimeLock);
4190 metacls = newcls(_class_getMeta(cls_gen));
4191 cls = getNonMetaClass(metacls);
4193 // Update vtables (initially postponed pending +initialize completion)
4194 // Do cls first because root metacls is a subclass of root cls
4195 updateVtable(cls, YES);
4196 updateVtable(metacls, YES);
4198 rwlock_unlock_write(&runtimeLock);
4200 changeInfo(metacls, RW_INITIALIZED, RW_INITIALIZING);
4204 /***********************************************************************
4206 **********************************************************************/
4207 __private_extern__ BOOL
4208 _class_shouldGrowCache(Class cls)
4210 return YES; // fixme good or bad for memory use?
4214 /***********************************************************************
4216 **********************************************************************/
4217 __private_extern__ void
4218 _class_setGrowCache(Class cls, BOOL grow)
4220 // fixme good or bad for memory use?
4224 /***********************************************************************
4228 **********************************************************************/
4229 __private_extern__ BOOL
4230 _class_isLoadable(Class cls)
4232 assert(isRealized(newcls(cls)));
4233 return YES; // any class registered for +load is definitely loadable
4237 /***********************************************************************
4239 **********************************************************************/
4240 __private_extern__ BOOL
4241 _class_hasCxxStructorsNoSuper(Class cls)
4243 assert(isRealized(newcls(cls)));
4244 return (newcls(cls)->data->ro->flags & RO_HAS_CXX_STRUCTORS) ? YES : NO;
4248 /***********************************************************************
4250 **********************************************************************/
4251 __private_extern__ BOOL
4252 _class_shouldFinalizeOnMainThread(Class cls)
4254 assert(isRealized(newcls(cls)));
4255 return (newcls(cls)->data->flags & RW_FINALIZE_ON_MAIN_THREAD) ? YES : NO;
4259 /***********************************************************************
4261 **********************************************************************/
4262 __private_extern__ void
4263 _class_setFinalizeOnMainThread(Class cls)
4265 assert(isRealized(newcls(cls)));
4266 changeInfo(newcls(cls), RW_FINALIZE_ON_MAIN_THREAD, 0);
4270 /***********************************************************************
4271 * _class_instancesHaveAssociatedObjects
4272 * May manipulate unrealized future classes in the CF-bridged case.
4273 **********************************************************************/
4274 __private_extern__ BOOL
4275 _class_instancesHaveAssociatedObjects(Class cls_gen)
4277 class_t *cls = newcls(cls_gen);
4278 assert(isFuture(cls) || isRealized(cls));
4279 return (cls->data->flags & RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS) ? YES : NO;
4283 /***********************************************************************
4284 * _class_assertInstancesHaveAssociatedObjects
4285 * May manipulate unrealized future classes in the CF-bridged case.
4286 **********************************************************************/
4287 __private_extern__ void
4288 _class_assertInstancesHaveAssociatedObjects(Class cls_gen)
4290 class_t *cls = newcls(cls_gen);
4291 assert(isFuture(cls) || isRealized(cls));
4292 changeInfo(cls, RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS, 0);
4296 /***********************************************************************
4298 * fixme assert realized to get superclass remapping?
4299 **********************************************************************/
4300 __private_extern__ Class
4301 _class_getSuperclass(Class cls)
4303 return (Class)getSuperclass(newcls(cls));
4306 static struct class_t *
4307 getSuperclass(struct class_t *cls)
4309 if (!cls) return NULL;
4310 return cls->superclass;
4314 /***********************************************************************
4315 * class_getIvarLayout
4316 * Called by the garbage collector.
4317 * The class must be NULL or already realized.
4319 **********************************************************************/
4321 class_getIvarLayout(Class cls_gen)
4323 class_t *cls = newcls(cls_gen);
4324 if (cls) return (const char *)cls->data->ro->ivarLayout;
4329 /***********************************************************************
4330 * class_getWeakIvarLayout
4331 * Called by the garbage collector.
4332 * The class must be NULL or already realized.
4334 **********************************************************************/
4336 class_getWeakIvarLayout(Class cls_gen)
4338 class_t *cls = newcls(cls_gen);
4339 if (cls) return (const char *)cls->data->ro->weakIvarLayout;
4344 /***********************************************************************
4345 * class_setIvarLayout
4346 * Changes the class's GC scan layout.
4347 * NULL layout means no unscanned ivars
4348 * The class must be under construction.
4349 * fixme: sanity-check layout vs instance size?
4350 * fixme: sanity-check layout vs superclass?
4351 * Locking: acquires runtimeLock
4352 **********************************************************************/
4354 class_setIvarLayout(Class cls_gen, const char *layout)
4356 class_t *cls = newcls(cls_gen);
4359 rwlock_write(&runtimeLock);
4361 // Can only change layout of in-construction classes.
4362 // note: if modifications to post-construction classes were
4363 // allowed, there would be a race below (us vs. concurrent GC scan)
4364 if (!(cls->data->flags & RW_CONSTRUCTING)) {
4365 _objc_inform("*** Can't set ivar layout for already-registered "
4366 "class '%s'", getName(cls));
4367 rwlock_unlock_write(&runtimeLock);
4371 class_ro_t *ro_w = make_ro_writeable(cls->data);
4373 try_free(ro_w->ivarLayout);
4374 ro_w->ivarLayout = (unsigned char *)_strdup_internal(layout);
4376 rwlock_unlock_write(&runtimeLock);
4380 /***********************************************************************
4381 * class_setWeakIvarLayout
4382 * Changes the class's GC weak layout.
4383 * NULL layout means no weak ivars
4384 * The class must be under construction.
4385 * fixme: sanity-check layout vs instance size?
4386 * fixme: sanity-check layout vs superclass?
4387 * Locking: acquires runtimeLock
4388 **********************************************************************/
4390 class_setWeakIvarLayout(Class cls_gen, const char *layout)
4392 class_t *cls = newcls(cls_gen);
4395 rwlock_write(&runtimeLock);
4397 // Can only change layout of in-construction classes.
4398 // note: if modifications to post-construction classes were
4399 // allowed, there would be a race below (us vs. concurrent GC scan)
4400 if (!(cls->data->flags & RW_CONSTRUCTING)) {
4401 _objc_inform("*** Can't set weak ivar layout for already-registered "
4402 "class '%s'", getName(cls));
4403 rwlock_unlock_write(&runtimeLock);
4407 class_ro_t *ro_w = make_ro_writeable(cls->data);
4409 try_free(ro_w->weakIvarLayout);
4410 ro_w->weakIvarLayout = (unsigned char *)_strdup_internal(layout);
4412 rwlock_unlock_write(&runtimeLock);
4416 /***********************************************************************
4417 * _class_getVariable
4419 * Locking: read-locks runtimeLock
4420 **********************************************************************/
4421 __private_extern__ Ivar
4422 _class_getVariable(Class cls, const char *name)
4424 rwlock_read(&runtimeLock);
4426 for ( ; cls != Nil; cls = class_getSuperclass(cls)) {
4427 struct ivar_t *ivar = getIvar(newcls(cls), name);
4429 rwlock_unlock_read(&runtimeLock);
4434 rwlock_unlock_read(&runtimeLock);
4440 /***********************************************************************
4441 * class_conformsToProtocol
4443 * Locking: read-locks runtimeLock
4444 **********************************************************************/
4445 BOOL class_conformsToProtocol(Class cls_gen, Protocol *proto)
4447 Protocol **protocols;
4448 unsigned int count, i;
4451 if (!cls_gen) return NO;
4452 if (!proto) return NO;
4456 protocols = class_copyProtocolList(cls_gen, &count);
4458 for (i = 0; i < count; i++) {
4459 if (protocols[i] == proto ||
4460 protocol_conformsToProtocol(protocols[i], proto))
4467 if (protocols) free(protocols);
4473 /***********************************************************************
4476 * Locking: write-locks runtimeLock
4477 **********************************************************************/
4479 _class_addMethod(Class cls_gen, SEL name, IMP imp,
4480 const char *types, BOOL replace)
4482 struct class_t *cls = newcls(cls_gen);
4485 if (!types) types = "";
4487 rwlock_write(&runtimeLock);
4489 assert(isRealized(cls));
4492 if ((m = getMethodNoSuper_nolock(cls, name))) {
4495 result = _method_getImplementation(m);
4497 result = _method_setImplementation(cls, m, imp);
4501 method_list_t *newlist;
4502 newlist = _calloc_internal(sizeof(*newlist), 1);
4503 newlist->entsize_NEVER_USE = (uint32_t)sizeof(method_t) | fixed_up_method_list;
4505 newlist->first.name = name;
4506 newlist->first.types = strdup(types);
4507 if (name != (SEL)kIgnore) {
4508 newlist->first.imp = imp;
4510 newlist->first.imp = (IMP)&_objc_ignored_method;
4513 BOOL vtablesAffected;
4514 attachMethodLists(cls, &newlist, 1, NO, &vtablesAffected);
4516 if (vtablesAffected) flushVtables(cls);
4521 rwlock_unlock_write(&runtimeLock);
4528 class_addMethod(Class cls, SEL name, IMP imp, const char *types)
4530 if (!cls) return NO;
4532 IMP old = _class_addMethod(cls, name, imp, types, NO);
4533 return old ? NO : YES;
4538 class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
4540 if (!cls) return NULL;
4542 return _class_addMethod(cls, name, imp, types, YES);
4546 /***********************************************************************
4548 * Adds an ivar to a class.
4549 * Locking: acquires runtimeLock
4550 **********************************************************************/
4552 class_addIvar(Class cls_gen, const char *name, size_t size,
4553 uint8_t alignment, const char *type)
4555 struct class_t *cls = newcls(cls_gen);
4557 if (!cls) return NO;
4559 if (!type) type = "";
4560 if (name && 0 == strcmp(name, "")) name = NULL;
4562 rwlock_write(&runtimeLock);
4564 assert(isRealized(cls));
4566 // No class variables
4567 if (isMetaClass(cls)) {
4568 rwlock_unlock_write(&runtimeLock);
4572 // Can only add ivars to in-construction classes.
4573 if (!(cls->data->flags & RW_CONSTRUCTING)) {
4574 rwlock_unlock_write(&runtimeLock);
4578 // Check for existing ivar with this name, unless it's anonymous.
4579 // Check for too-big ivar.
4580 // fixme check for superclass ivar too?
4581 if ((name && getIvar(cls, name)) || size > UINT32_MAX) {
4582 rwlock_unlock_write(&runtimeLock);
4586 class_ro_t *ro_w = make_ro_writeable(cls->data);
4588 // fixme allocate less memory here
4590 ivar_list_t *oldlist, *newlist;
4591 if ((oldlist = (ivar_list_t *)cls->data->ro->ivars)) {
4592 size_t oldsize = ivar_list_size(oldlist);
4593 newlist = _calloc_internal(oldsize + oldlist->entsize, 1);
4594 memcpy(newlist, oldlist, oldsize);
4595 _free_internal(oldlist);
4597 newlist = _calloc_internal(sizeof(ivar_list_t), 1);
4598 newlist->entsize = (uint32_t)sizeof(ivar_t);
4601 uint32_t offset = instanceSize(cls);
4602 uint32_t alignMask = (1<<alignment)-1;
4603 offset = (offset + alignMask) & ~alignMask;
4605 ivar_t *ivar = ivar_list_nth(newlist, newlist->count++);
4606 ivar->offset = _malloc_internal(sizeof(*ivar->offset));
4607 *ivar->offset = offset;
4608 ivar->name = name ? _strdup_internal(name) : NULL;
4609 ivar->type = _strdup_internal(type);
4610 ivar->alignment = alignment;
4611 ivar->size = (uint32_t)size;
4613 ro_w->ivars = newlist;
4614 ro_w->instanceSize = (uint32_t)(offset + size);
4616 // Ivar layout updated in registerClass.
4618 rwlock_unlock_write(&runtimeLock);
4624 /***********************************************************************
4626 * Adds a protocol to a class.
4627 * Locking: acquires runtimeLock
4628 **********************************************************************/
4629 BOOL class_addProtocol(Class cls_gen, Protocol *protocol_gen)
4631 class_t *cls = newcls(cls_gen);
4632 protocol_t *protocol = newprotocol(protocol_gen);
4633 protocol_list_t *plist;
4634 protocol_list_t **plistp;
4636 if (!cls) return NO;
4637 if (class_conformsToProtocol(cls_gen, protocol_gen)) return NO;
4639 rwlock_write(&runtimeLock);
4641 assert(isRealized(cls));
4644 plist = _malloc_internal(sizeof(protocol_list_t) + sizeof(protocol_t *));
4646 plist->list[0] = (protocol_ref_t)protocol;
4648 unsigned int count = 0;
4649 for (plistp = cls->data->protocols; plistp && *plistp; plistp++) {
4653 cls->data->protocols =
4654 _realloc_internal(cls->data->protocols,
4655 (count+2) * sizeof(protocol_list_t *));
4656 cls->data->protocols[count] = plist;
4657 cls->data->protocols[count+1] = NULL;
4661 rwlock_unlock_write(&runtimeLock);
4667 /***********************************************************************
4669 * Look up a class by name, and realize it.
4670 * Locking: acquires runtimeLock
4671 **********************************************************************/
4672 __private_extern__ id
4673 look_up_class(const char *name,
4674 BOOL includeUnconnected __attribute__((unused)),
4675 BOOL includeClassHandler __attribute__((unused)))
4677 if (!name) return nil;
4679 rwlock_read(&runtimeLock);
4680 class_t *result = getClass(name);
4681 BOOL unrealized = result && !isRealized(result);
4682 rwlock_unlock_read(&runtimeLock);
4684 rwlock_write(&runtimeLock);
4685 realizeClass(result);
4686 rwlock_unlock_write(&runtimeLock);
4692 /***********************************************************************
4693 * objc_duplicateClass
4695 * Locking: acquires runtimeLock
4696 **********************************************************************/
4698 objc_duplicateClass(Class original_gen, const char *name,
4701 struct class_t *original = newcls(original_gen);
4702 struct class_t *duplicate;
4704 rwlock_write(&runtimeLock);
4706 assert(isRealized(original));
4707 assert(!isMetaClass(original));
4709 duplicate = (struct class_t *)
4710 _calloc_class(instanceSize(original->isa) + extraBytes);
4711 if (instanceSize(original->isa) < sizeof(class_t)) {
4712 _objc_inform("busted! %s\n", original->data->ro->name);
4716 duplicate->isa = original->isa;
4717 duplicate->superclass = original->superclass;
4718 duplicate->cache = (Cache)&_objc_empty_cache;
4719 duplicate->vtable = _objc_empty_vtable;
4721 duplicate->data = _calloc_internal(sizeof(*original->data), 1);
4722 duplicate->data->flags = (original->data->flags | RW_COPIED_RO) & ~RW_SPECIALIZED_VTABLE;
4723 duplicate->data->version = original->data->version;
4724 duplicate->data->firstSubclass = NULL;
4725 duplicate->data->nextSiblingClass = NULL;
4727 duplicate->data->ro =
4728 _memdup_internal(original->data->ro, sizeof(*original->data->ro));
4729 *(char **)&duplicate->data->ro->name = _strdup_internal(name);
4731 if (original->data->methods) {
4732 duplicate->data->methods =
4733 _memdup_internal(original->data->methods,
4734 malloc_size(original->data->methods));
4735 method_list_t **mlistp = duplicate->data->methods;
4736 for (mlistp = duplicate->data->methods; *mlistp; mlistp++) {
4737 *mlistp = _memdup_internal(*mlistp, method_list_size(*mlistp));
4741 // fixme dies when categories are added to the base
4742 duplicate->data->properties = original->data->properties;
4743 duplicate->data->protocols = original->data->protocols;
4745 if (duplicate->superclass) {
4746 addSubclass(duplicate->superclass, duplicate);
4749 // Don't methodize class - construction above is correct
4751 addNamedClass(duplicate, duplicate->data->ro->name);
4752 addRealizedClass(duplicate);
4753 // no: duplicate->isa == original->isa
4754 // addRealizedMetaclass(duplicate->isa);
4756 if (PrintConnecting) {
4757 _objc_inform("CLASS: realizing class '%s' (duplicate of %s) %p %p",
4758 name, original->data->ro->name,
4759 duplicate, duplicate->data->ro);
4762 rwlock_unlock_write(&runtimeLock);
4764 return (Class)duplicate;
4767 /***********************************************************************
4768 * objc_initializeClassPair
4769 * Locking: runtimeLock must be write-locked by the caller
4770 **********************************************************************/
4771 static void objc_initializeClassPair_internal(Class superclass_gen, const char *name, Class cls_gen, Class meta_gen)
4773 rwlock_assert_writing(&runtimeLock);
4775 class_t *superclass = newcls(superclass_gen);
4776 class_t *cls = newcls(cls_gen);
4777 class_t *meta = newcls(meta_gen);
4778 class_ro_t *cls_ro_w, *meta_ro_w;
4780 cls->data = _calloc_internal(sizeof(class_rw_t), 1);
4781 meta->data = _calloc_internal(sizeof(class_rw_t), 1);
4782 cls_ro_w = _calloc_internal(sizeof(class_ro_t), 1);
4783 meta_ro_w = _calloc_internal(sizeof(class_ro_t), 1);
4784 cls->data->ro = cls_ro_w;
4785 meta->data->ro = meta_ro_w;
4788 cls->cache = (Cache)&_objc_empty_cache;
4789 meta->cache = (Cache)&_objc_empty_cache;
4790 cls->vtable = _objc_empty_vtable;
4791 meta->vtable = _objc_empty_vtable;
4793 cls->data->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED;
4794 meta->data->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED;
4795 cls->data->version = 0;
4796 meta->data->version = 7;
4798 cls_ro_w->flags = 0;
4799 meta_ro_w->flags = RO_META;
4801 cls_ro_w->flags |= RO_ROOT;
4802 meta_ro_w->flags |= RO_ROOT;
4805 cls_ro_w->instanceStart = instanceSize(superclass);
4806 meta_ro_w->instanceStart = instanceSize(superclass->isa);
4807 cls_ro_w->instanceSize = cls_ro_w->instanceStart;
4808 meta_ro_w->instanceSize = meta_ro_w->instanceStart;
4810 cls_ro_w->instanceStart = 0;
4811 meta_ro_w->instanceStart = (uint32_t)sizeof(class_t);
4812 cls_ro_w->instanceSize = (uint32_t)sizeof(id); // just an isa
4813 meta_ro_w->instanceSize = meta_ro_w->instanceStart;
4816 cls_ro_w->name = _strdup_internal(name);
4817 meta_ro_w->name = _strdup_internal(name);
4819 // Connect to superclasses and metaclasses
4822 meta->isa = superclass->isa->isa;
4823 cls->superclass = superclass;
4824 meta->superclass = superclass->isa;
4825 addSubclass(superclass, cls);
4826 addSubclass(superclass->isa, meta);
4829 cls->superclass = Nil;
4830 meta->superclass = cls;
4831 addSubclass(cls, meta);
4835 /***********************************************************************
4836 * objc_initializeClassPair
4837 **********************************************************************/
4838 Class objc_initializeClassPair(Class superclass_gen, const char *name, Class cls_gen, Class meta_gen)
4840 class_t *superclass = newcls(superclass_gen);
4842 rwlock_write(&runtimeLock);
4845 // Common superclass integrity checks with objc_allocateClassPair
4847 if (getClass(name)) {
4848 rwlock_unlock_write(&runtimeLock);
4851 // fixme reserve class against simmultaneous allocation
4853 if (superclass) assert(isRealized(superclass));
4855 if (superclass && superclass->data->flags & RW_CONSTRUCTING) {
4856 // Can't make subclass of an in-construction class
4857 rwlock_unlock_write(&runtimeLock);
4862 // just initialize what was supplied
4863 objc_initializeClassPair_internal(superclass_gen, name, cls_gen, meta_gen);
4865 rwlock_unlock_write(&runtimeLock);
4869 /***********************************************************************
4870 * objc_allocateClassPair
4872 * Locking: acquires runtimeLock
4873 **********************************************************************/
4874 Class objc_allocateClassPair(Class superclass_gen, const char *name,
4877 class_t *superclass = newcls(superclass_gen);
4880 rwlock_write(&runtimeLock);
4883 // Common superclass integrity checks with objc_initializeClassPair
4885 if (getClass(name)) {
4886 rwlock_unlock_write(&runtimeLock);
4889 // fixme reserve class against simmultaneous allocation
4891 if (superclass) assert(isRealized(superclass));
4893 if (superclass && superclass->data->flags & RW_CONSTRUCTING) {
4894 // Can't make subclass of an in-construction class
4895 rwlock_unlock_write(&runtimeLock);
4901 // Allocate new classes.
4903 cls = _calloc_class(instanceSize(superclass->isa) + extraBytes);
4904 meta = _calloc_class(instanceSize(superclass->isa->isa) + extraBytes);
4906 cls = _calloc_class(sizeof(class_t) + extraBytes);
4907 meta = _calloc_class(sizeof(class_t) + extraBytes);
4911 objc_initializeClassPair_internal(superclass_gen, name, cls, meta);
4913 rwlock_unlock_write(&runtimeLock);
4919 /***********************************************************************
4920 * objc_registerClassPair
4922 * Locking: acquires runtimeLock
4923 **********************************************************************/
4924 void objc_registerClassPair(Class cls_gen)
4926 class_t *cls = newcls(cls_gen);
4928 rwlock_write(&runtimeLock);
4930 if ((cls->data->flags & RW_CONSTRUCTED) ||
4931 (cls->isa->data->flags & RW_CONSTRUCTED))
4933 _objc_inform("objc_registerClassPair: class '%s' was already "
4934 "registered!", cls->data->ro->name);
4935 rwlock_unlock_write(&runtimeLock);
4939 if (!(cls->data->flags & RW_CONSTRUCTING) ||
4940 !(cls->isa->data->flags & RW_CONSTRUCTING))
4942 _objc_inform("objc_registerClassPair: class '%s' was not "
4943 "allocated with objc_allocateClassPair!",
4944 cls->data->ro->name);
4945 rwlock_unlock_write(&runtimeLock);
4949 // Build ivar layouts
4951 struct class_t *supercls = getSuperclass(cls);
4952 class_ro_t *ro_w = (class_ro_t *)cls->data->ro;
4954 if (ro_w->ivarLayout) {
4955 // Class builder already called class_setIvarLayout.
4957 else if (!supercls) {
4958 // Root class. Scan conservatively (should be isa ivar only).
4959 // ivar_layout is already NULL.
4961 else if (ro_w->ivars == NULL) {
4962 // No local ivars. Use superclass's layouts.
4963 ro_w->ivarLayout = (unsigned char *)
4964 _strdup_internal((char *)supercls->data->ro->ivarLayout);
4967 // Has local ivars. Build layouts based on superclass.
4968 layout_bitmap bitmap =
4969 layout_bitmap_create(supercls->data->ro->ivarLayout,
4970 instanceSize(supercls),
4971 instanceSize(cls), NO);
4973 for (i = 0; i < ro_w->ivars->count; i++) {
4974 ivar_t *ivar = ivar_list_nth(ro_w->ivars, i);
4975 if (!ivar->offset) continue; // anonymous bitfield
4977 layout_bitmap_set_ivar(bitmap, ivar->type, *ivar->offset);
4979 ro_w->ivarLayout = layout_string_create(bitmap);
4980 layout_bitmap_free(bitmap);
4983 if (ro_w->weakIvarLayout) {
4984 // Class builder already called class_setWeakIvarLayout.
4986 else if (!supercls) {
4987 // Root class. No weak ivars (should be isa ivar only).
4988 // weak_ivar_layout is already NULL.
4990 else if (ro_w->ivars == NULL) {
4991 // No local ivars. Use superclass's layout.
4992 ro_w->weakIvarLayout = (unsigned char *)
4993 _strdup_internal((char *)supercls->data->ro->weakIvarLayout);
4996 // Has local ivars. Build layout based on superclass.
4997 // No way to add weak ivars yet.
4998 ro_w->weakIvarLayout = (unsigned char *)
4999 _strdup_internal((char *)supercls->data->ro->weakIvarLayout);
5003 // Clear "under construction" bit, set "done constructing" bit
5004 cls->data->flags &= ~RW_CONSTRUCTING;
5005 cls->isa->data->flags &= ~RW_CONSTRUCTING;
5006 cls->data->flags |= RW_CONSTRUCTED;
5007 cls->isa->data->flags |= RW_CONSTRUCTED;
5009 // Add to realized and uninitialized classes
5010 addNamedClass(cls, cls->data->ro->name);
5011 addRealizedClass(cls);
5012 addRealizedMetaclass(cls->isa);
5013 addUninitializedClass(cls, cls->isa);
5015 rwlock_unlock_write(&runtimeLock);
5019 static void unload_class(class_t *cls, BOOL isMeta)
5021 // Detach class from various lists
5023 // categories not yet attached to this class
5024 category_list *cats;
5025 cats = unattachedCategoriesForClass(cls);
5026 if (cats) free(cats);
5028 // class tables and +load queue
5030 removeNamedClass(cls, getName(cls));
5031 removeRealizedClass(cls);
5032 removeUninitializedClass(cls);
5034 removeRealizedMetaclass(cls);
5037 // superclass's subclass list
5038 if (isRealized(cls)) {
5039 class_t *supercls = getSuperclass(cls);
5040 if (supercls) removeSubclass(supercls, cls);
5044 // Dispose the class's own data structures
5046 if (isRealized(cls)) {
5049 // Dereferences the cache contents; do this before freeing methods
5050 if (cls->cache != (Cache)&_objc_empty_cache) _cache_free(cls->cache);
5052 if (cls->data->methods) {
5053 method_list_t **mlistp;
5054 for (mlistp = cls->data->methods; *mlistp; mlistp++) {
5055 for (i = 0; i < (**mlistp).count; i++) {
5056 method_t *m = method_list_nth(*mlistp, i);
5061 try_free(cls->data->methods);
5064 const ivar_list_t *ilist = cls->data->ro->ivars;
5066 for (i = 0; i < ilist->count; i++) {
5067 const ivar_t *ivar = ivar_list_nth(ilist, i);
5068 try_free(ivar->offset);
5069 try_free(ivar->name);
5070 try_free(ivar->type);
5075 protocol_list_t **plistp = cls->data->protocols;
5076 for (plistp = cls->data->protocols; plistp && *plistp; plistp++) {
5079 try_free(cls->data->protocols);
5084 if (cls->vtable != _objc_empty_vtable &&
5085 cls->data->flags & RW_SPECIALIZED_VTABLE) try_free(cls->vtable);
5086 try_free(cls->data->ro->ivarLayout);
5087 try_free(cls->data->ro->weakIvarLayout);
5088 try_free(cls->data->ro->name);
5089 try_free(cls->data->ro);
5090 try_free(cls->data);
5095 void objc_disposeClassPair(Class cls_gen)
5097 class_t *cls = newcls(cls_gen);
5099 rwlock_write(&runtimeLock);
5101 if (!(cls->data->flags & (RW_CONSTRUCTED|RW_CONSTRUCTING)) ||
5102 !(cls->isa->data->flags & (RW_CONSTRUCTED|RW_CONSTRUCTING)))
5104 // class not allocated with objc_allocateClassPair
5105 // disposing still-unregistered class is OK!
5106 _objc_inform("objc_disposeClassPair: class '%s' was not "
5107 "allocated with objc_allocateClassPair!",
5108 cls->data->ro->name);
5109 rwlock_unlock_write(&runtimeLock);
5113 if (isMetaClass(cls)) {
5114 _objc_inform("objc_disposeClassPair: class '%s' is a metaclass, "
5115 "not a class!", cls->data->ro->name);
5116 rwlock_unlock_write(&runtimeLock);
5120 // Shouldn't have any live subclasses.
5121 if (cls->data->firstSubclass) {
5122 _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, "
5123 "including '%s'!", cls->data->ro->name,
5124 getName(cls->data->firstSubclass));
5126 if (cls->isa->data->firstSubclass) {
5127 _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, "
5128 "including '%s'!", cls->data->ro->name,
5129 getName(cls->isa->data->firstSubclass));
5132 // don't remove_class_from_loadable_list()
5133 // - it's not there and we don't have the lock
5134 unload_class(cls->isa, YES);
5135 unload_class(cls, NO);
5137 rwlock_unlock_write(&runtimeLock);
5142 /***********************************************************************
5143 * class_createInstanceFromZone
5146 **********************************************************************/
5148 class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
5150 if (cls) assert(isRealized(newcls(cls)));
5151 return _internal_class_createInstanceFromZone(cls, extraBytes, zone);
5155 /***********************************************************************
5156 * class_createInstance
5159 **********************************************************************/
5161 class_createInstance(Class cls, size_t extraBytes)
5163 return class_createInstanceFromZone(cls, extraBytes, NULL);
5167 /***********************************************************************
5168 * object_copyFromZone
5171 **********************************************************************/
5173 object_copyFromZone(id oldObj, size_t extraBytes, void *zone)
5178 if (!oldObj) return nil;
5180 size = _class_getInstanceSize(oldObj->isa) + extraBytes;
5183 obj = (id) auto_zone_allocate_object(gc_zone, size,
5184 AUTO_OBJECT_SCANNED, 0, 1);
5188 obj = malloc_zone_calloc(zone, size, 1);
5190 obj = (id) calloc(1, size);
5192 if (!obj) return nil;
5194 // fixme this doesn't handle C++ ivars correctly (#4619414)
5195 objc_memmove_collectable(obj, oldObj, size);
5198 if (UseGC) gc_fixup_weakreferences(obj, oldObj);
5205 /***********************************************************************
5209 **********************************************************************/
5211 object_copy(id oldObj, size_t extraBytes)
5213 return object_copyFromZone(oldObj, extraBytes, malloc_default_zone());
5217 /***********************************************************************
5221 **********************************************************************/
5223 object_dispose(id obj)
5225 return _internal_object_dispose(obj);
5229 /***********************************************************************
5230 * _objc_getFreedObjectClass
5233 **********************************************************************/
5234 Class _objc_getFreedObjectClass (void)
5241 extern id objc_msgSend_fixup(id, SEL, ...);
5242 extern id objc_msgSend_fixedup(id, SEL, ...);
5243 extern id objc_msgSendSuper2_fixup(id, SEL, ...);
5244 extern id objc_msgSendSuper2_fixedup(id, SEL, ...);
5245 extern id objc_msgSend_stret_fixup(id, SEL, ...);
5246 extern id objc_msgSend_stret_fixedup(id, SEL, ...);
5247 extern id objc_msgSendSuper2_stret_fixup(id, SEL, ...);
5248 extern id objc_msgSendSuper2_stret_fixedup(id, SEL, ...);
5249 #if defined(__i386__) || defined(__x86_64__)
5250 extern id objc_msgSend_fpret_fixup(id, SEL, ...);
5251 extern id objc_msgSend_fpret_fixedup(id, SEL, ...);
5253 #if defined(__x86_64__)
5254 extern id objc_msgSend_fp2ret_fixup(id, SEL, ...);
5255 extern id objc_msgSend_fp2ret_fixedup(id, SEL, ...);
5258 /***********************************************************************
5259 * _objc_fixupMessageRef
5260 * Fixes up message ref *msg.
5261 * obj is the receiver. supr is NULL for non-super messages
5262 * Locking: acquires runtimeLock
5263 **********************************************************************/
5264 __private_extern__ IMP
5265 _objc_fixupMessageRef(id obj, struct objc_super2 *supr, message_ref *msg)
5270 rwlock_assert_unlocked(&runtimeLock);
5273 // normal message - search obj->isa for the method implementation
5274 isa = (class_t *)obj->isa;
5276 if (!isRealized(isa)) {
5277 // obj is a class object, isa is its metaclass
5279 rwlock_write(&runtimeLock);
5280 cls = realizeClass((class_t *)obj);
5281 rwlock_unlock_write(&runtimeLock);
5283 // shouldn't have instances of unrealized classes!
5284 assert(isMetaClass(isa));
5285 // shouldn't be relocating classes here!
5286 assert(cls == (class_t *)obj);
5290 // this is objc_msgSend_super, and supr->current_class->superclass
5291 // is the class to search for the method implementation
5292 assert(isRealized((class_t *)supr->current_class));
5293 isa = getSuperclass((class_t *)supr->current_class);
5296 msg->sel = sel_registerName((const char *)msg->sel);
5300 if (msg->imp == (IMP)&objc_msgSend_fixup &&
5301 (vtableIndex = vtable_getIndex(msg->sel)) >= 0)
5304 msg->imp = vtableTrampolines[vtableIndex];
5305 imp = isa->vtable[vtableIndex];
5310 // ordinary dispatch
5311 imp = lookUpMethod((Class)isa, msg->sel, YES/*initialize*/, YES/*cache*/);
5313 if (msg->imp == (IMP)&objc_msgSend_fixup) {
5314 msg->imp = (IMP)&objc_msgSend_fixedup;
5316 else if (msg->imp == (IMP)&objc_msgSendSuper2_fixup) {
5317 msg->imp = (IMP)&objc_msgSendSuper2_fixedup;
5319 else if (msg->imp == (IMP)&objc_msgSend_stret_fixup) {
5320 msg->imp = (IMP)&objc_msgSend_stret_fixedup;
5322 else if (msg->imp == (IMP)&objc_msgSendSuper2_stret_fixup) {
5323 msg->imp = (IMP)&objc_msgSendSuper2_stret_fixedup;
5325 #if defined(__i386__) || defined(__x86_64__)
5326 else if (msg->imp == (IMP)&objc_msgSend_fpret_fixup) {
5327 msg->imp = (IMP)&objc_msgSend_fpret_fixedup;
5330 #if defined(__x86_64__)
5331 else if (msg->imp == (IMP)&objc_msgSend_fp2ret_fixup) {
5332 msg->imp = (IMP)&objc_msgSend_fp2ret_fixedup;
5336 // The ref may already have been fixed up, either by another thread
5337 // or by +initialize via lookUpMethod above.
5348 #warning fixme delete after #4586306
5349 Class class_poseAs(Class imposter, Class original)
5351 _objc_fatal("Don't call class_poseAs.");
5356 static class_t *setSuperclass(class_t *cls, class_t *newSuper)
5360 rwlock_assert_writing(&runtimeLock);
5362 oldSuper = cls->superclass;
5363 removeSubclass(oldSuper, cls);
5364 removeSubclass(oldSuper->isa, cls->isa);
5366 cls->superclass = newSuper;
5367 cls->isa->superclass = newSuper->isa;
5368 addSubclass(newSuper, cls);
5369 addSubclass(newSuper->isa, cls->isa);
5372 flushCaches(cls->isa);
5374 flushVtables(cls->isa);
5380 Class class_setSuperclass(Class cls_gen, Class newSuper_gen)
5382 class_t *cls = newcls(cls_gen);
5383 class_t *newSuper = newcls(newSuper_gen);
5386 rwlock_write(&runtimeLock);
5387 oldSuper = setSuperclass(cls, newSuper);
5388 rwlock_unlock_write(&runtimeLock);
5390 return (Class)oldSuper;