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 * Locking: runtimeLock must be read- or write-locked by the caller
1763 **********************************************************************/
1764 static NXMapTable *remappedClasses(BOOL create)
1766 static NXMapTable *remapped_class_map = NULL;
1768 rwlock_assert_locked(&runtimeLock);
1770 if (remapped_class_map) return remapped_class_map;
1771 if (!create) return NULL;
1773 // remapped_class_map is big enough to hold CF's classes and a few others
1774 INIT_ONCE_PTR(remapped_class_map,
1775 NXCreateMapTableFromZone(NXPtrValueMapPrototype, 32,
1776 _objc_internal_zone()),
1779 return remapped_class_map;
1783 /***********************************************************************
1785 * Returns YES if no classes have been remapped
1786 * Locking: runtimeLock must be read- or write-locked by the caller
1787 **********************************************************************/
1788 static BOOL noClassesRemapped(void)
1790 rwlock_assert_locked(&runtimeLock);
1792 BOOL result = (remappedClasses(NO) == NULL);
1797 /***********************************************************************
1799 * newcls is a realized future class, replacing oldcls.
1800 * Locking: runtimeLock must be write-locked by the caller
1801 **********************************************************************/
1802 static void addRemappedClass(class_t *oldcls, class_t *newcls)
1804 rwlock_assert_writing(&runtimeLock);
1807 _objc_inform("FUTURE: using %p instead of %p for %s",
1808 oldcls, newcls, getName(newcls));
1812 old = NXMapInsert(remappedClasses(YES), oldcls, newcls);
1817 /***********************************************************************
1819 * Returns the live class pointer for cls, which may be pointing to
1820 * a class struct that has been reallocated.
1821 * Locking: runtimeLock must be read- or write-locked by the caller
1822 **********************************************************************/
1823 static class_t *remapClass(class_t *cls)
1825 rwlock_assert_locked(&runtimeLock);
1827 class_t *newcls = NXMapGet(remappedClasses(YES), cls);
1828 return newcls ? newcls : cls;
1832 /***********************************************************************
1834 * Fix up a class ref, in case the class referenced has been reallocated.
1835 * Locking: runtimeLock must be read- or write-locked by the caller
1836 **********************************************************************/
1837 static void remapClassRef(class_t **clsref)
1839 rwlock_assert_locked(&runtimeLock);
1841 class_t *newcls = remapClass(*clsref);
1842 if (*clsref != newcls) *clsref = newcls;
1846 /***********************************************************************
1848 * Adds subcls as a subclass of supercls.
1849 * Locking: runtimeLock must be held by the caller.
1850 **********************************************************************/
1851 static void addSubclass(class_t *supercls, class_t *subcls)
1853 rwlock_assert_writing(&runtimeLock);
1855 if (supercls && subcls) {
1856 assert(isRealized(supercls));
1857 assert(isRealized(subcls));
1858 subcls->data->nextSiblingClass = supercls->data->firstSubclass;
1859 supercls->data->firstSubclass = subcls;
1864 /***********************************************************************
1866 * Removes subcls as a subclass of supercls.
1867 * Locking: runtimeLock must be held by the caller.
1868 **********************************************************************/
1869 static void removeSubclass(class_t *supercls, class_t *subcls)
1871 rwlock_assert_writing(&runtimeLock);
1872 assert(getSuperclass(subcls) == supercls);
1875 for (cp = &supercls->data->firstSubclass;
1876 *cp && *cp != subcls;
1877 cp = &(*cp)->data->nextSiblingClass)
1879 assert(*cp == subcls);
1880 *cp = subcls->data->nextSiblingClass;
1885 /***********************************************************************
1887 * Returns the protocol name => protocol map for protocols.
1888 * Locking: runtimeLock must read- or write-locked by the caller
1889 **********************************************************************/
1890 static NXMapTable *protocols(void)
1892 static NXMapTable *protocol_map = NULL;
1894 rwlock_assert_locked(&runtimeLock);
1896 INIT_ONCE_PTR(protocol_map,
1897 NXCreateMapTableFromZone(NXStrValueMapPrototype, 16,
1898 _objc_internal_zone()),
1899 NXFreeMapTable(v) );
1901 return protocol_map;
1905 /***********************************************************************
1907 * Returns the live protocol pointer for proto, which may be pointing to
1908 * a protocol struct that has been reallocated.
1909 * Locking: runtimeLock must be read- or write-locked by the caller
1910 **********************************************************************/
1911 static protocol_t *remapProtocol(protocol_ref_t proto)
1913 rwlock_assert_locked(&runtimeLock);
1915 protocol_t *newproto = NXMapGet(protocols(), ((protocol_t *)proto)->name);
1916 return newproto ? newproto : (protocol_t *)proto;
1920 /***********************************************************************
1922 * Fix up a protocol ref, in case the protocol referenced has been reallocated.
1923 * Locking: runtimeLock must be read- or write-locked by the caller
1924 **********************************************************************/
1925 static void remapProtocolRef(protocol_t **protoref)
1927 rwlock_assert_locked(&runtimeLock);
1929 protocol_t *newproto = remapProtocol((protocol_ref_t)*protoref);
1930 if (*protoref != newproto) *protoref = newproto;
1934 /***********************************************************************
1936 * Slides a class's ivars to accommodate the given superclass size.
1937 * Also slides ivar and weak GC layouts if provided.
1938 * Ivars are NOT compacted to compensate for a superclass that shrunk.
1939 * Locking: runtimeLock must be held by the caller.
1940 **********************************************************************/
1941 static void moveIvars(class_ro_t *ro, uint32_t superSize,
1942 layout_bitmap *ivarBitmap, layout_bitmap *weakBitmap)
1944 rwlock_assert_writing(&runtimeLock);
1949 assert(superSize > ro->instanceStart);
1950 diff = superSize - ro->instanceStart;
1953 // Find maximum alignment in this class's ivars
1954 uint32_t maxAlignment = 1;
1955 for (i = 0; i < ro->ivars->count; i++) {
1956 ivar_t *ivar = ivar_list_nth(ro->ivars, i);
1957 if (!ivar->offset) continue; // anonymous bitfield
1959 uint32_t alignment = ivar_alignment(ivar);
1960 if (alignment > maxAlignment) maxAlignment = alignment;
1963 // Compute a slide value that preserves that alignment
1964 uint32_t alignMask = maxAlignment - 1;
1965 if (diff & alignMask) diff = (diff + alignMask) & ~alignMask;
1967 // Slide all of this class's ivars en masse
1968 for (i = 0; i < ro->ivars->count; i++) {
1969 ivar_t *ivar = ivar_list_nth(ro->ivars, i);
1970 if (!ivar->offset) continue; // anonymous bitfield
1972 uint32_t oldOffset = (uint32_t)*ivar->offset;
1973 uint32_t newOffset = oldOffset + diff;
1974 *ivar->offset = newOffset;
1977 _objc_inform("IVARS: offset %u -> %u for %s (size %u, align %u)",
1978 oldOffset, newOffset, ivar->name,
1979 ivar->size, ivar_alignment(ivar));
1984 uint32_t oldOffset = ro->instanceStart;
1985 uint32_t newOffset = ro->instanceStart + diff;
1988 layout_bitmap_slide(ivarBitmap,
1989 oldOffset >> WORD_SHIFT,
1990 newOffset >> WORD_SHIFT);
1993 layout_bitmap_slide(weakBitmap,
1994 oldOffset >> WORD_SHIFT,
1995 newOffset >> WORD_SHIFT);
1999 *(uint32_t *)&ro->instanceStart += diff;
2000 *(uint32_t *)&ro->instanceSize += diff;
2003 // No ivars slid, but superclass changed size.
2004 // Expand bitmap in preparation for layout_bitmap_splat().
2005 if (ivarBitmap) layout_bitmap_grow(ivarBitmap, ro->instanceSize >> WORD_SHIFT);
2006 if (weakBitmap) layout_bitmap_grow(weakBitmap, ro->instanceSize >> WORD_SHIFT);
2011 /***********************************************************************
2013 * Look up an ivar by name.
2014 * Locking: runtimeLock must be read- or write-locked by the caller.
2015 **********************************************************************/
2016 static ivar_t *getIvar(class_t *cls, const char *name)
2018 rwlock_assert_locked(&runtimeLock);
2020 const ivar_list_t *ivars;
2021 assert(isRealized(cls));
2022 if ((ivars = cls->data->ro->ivars)) {
2024 for (i = 0; i < ivars->count; i++) {
2025 struct ivar_t *ivar = ivar_list_nth(ivars, i);
2026 if (!ivar->offset) continue; // anonymous bitfield
2028 // ivar->name may be NULL for anonymous bitfields etc.
2029 if (ivar->name && 0 == strcmp(name, ivar->name)) {
2039 /***********************************************************************
2041 * Performs first-time initialization on class cls,
2042 * including allocating its read-write data.
2043 * Returns the real class structure for the class.
2044 * Locking: runtimeLock must be write-locked by the caller
2045 **********************************************************************/
2046 static class_t *realizeClass(class_t *cls)
2048 rwlock_assert_writing(&runtimeLock);
2050 const class_ro_t *ro;
2056 if (!cls) return NULL;
2057 if (isRealized(cls)) return cls;
2058 assert(cls == remapClass(cls));
2060 ro = (const class_ro_t *)cls->data;
2061 if (ro->flags & RO_FUTURE) {
2062 // This was a future class. rw data is already allocated.
2065 changeInfo(cls, RW_REALIZED, RW_FUTURE);
2067 // Normal class. Allocate writeable class data.
2068 rw = _calloc_internal(sizeof(class_rw_t), 1);
2070 rw->flags = RW_REALIZED;
2074 isMeta = (ro->flags & RO_META) ? YES : NO;
2076 rw->version = isMeta ? 7 : 0; // old runtime went up to 6
2078 if (PrintConnecting) {
2079 _objc_inform("CLASS: realizing class '%s' %s %p %p",
2080 ro->name, isMeta ? "(meta)" : "", cls, ro);
2083 // Realize superclass and metaclass, if they aren't already.
2084 // This needs to be done after RW_REALIZED is set above, for root classes.
2085 supercls = realizeClass(remapClass(cls->superclass));
2086 metacls = realizeClass(remapClass(cls->isa));
2088 // Check for remapped superclass
2089 // fixme doesn't handle remapped metaclass
2090 assert(metacls == cls->isa);
2091 if (supercls != cls->superclass) {
2092 cls->superclass = supercls;
2095 /* debug: print them all
2098 for (i = 0; i < ro->ivars->count; i++) {
2099 ivar_t *ivar = ivar_list_nth(ro->ivars, i);
2100 if (!ivar->offset) continue; // anonymous bitfield
2102 _objc_inform("IVARS: %s.%s (offset %u, size %u, align %u)",
2103 ro->name, ivar->name,
2104 *ivar->offset, ivar->size, ivar_alignment(ivar));
2111 // Non-fragile ivars - reconcile this class with its superclass
2112 layout_bitmap ivarBitmap;
2113 layout_bitmap weakBitmap;
2114 BOOL layoutsChanged = NO;
2117 // fixme can optimize for "class has no new ivars", etc
2118 // WARNING: gcc c++ sets instanceStart/Size=0 for classes with
2119 // no local ivars, but does provide a layout bitmap.
2120 // Handle that case specially so layout_bitmap_create doesn't die
2121 // The other ivar sliding code below still works fine, and
2122 // the final result is a good class.
2123 if (ro->instanceStart == 0 && ro->instanceSize == 0) {
2124 // We can't use ro->ivarLayout because we don't know
2125 // how long it is. Force a new layout to be created.
2127 _objc_inform("IVARS: instanceStart/Size==0 for class %s; "
2128 "disregarding ivar layout", ro->name);
2131 layout_bitmap_create(NULL,
2132 supercls->data->ro->instanceSize,
2133 supercls->data->ro->instanceSize, NO);
2135 layout_bitmap_create(NULL,
2136 supercls->data->ro->instanceSize,
2137 supercls->data->ro->instanceSize, YES);
2138 layoutsChanged = YES;
2141 layout_bitmap_create(ro->ivarLayout,
2143 ro->instanceSize, NO);
2145 layout_bitmap_create(ro->weakIvarLayout,
2147 ro->instanceSize, YES);
2151 if (ro->instanceStart < supercls->data->ro->instanceSize) {
2152 // Superclass has changed size. This class's ivars must move.
2153 // Also slide layout bits in parallel.
2154 // This code is incapable of compacting the subclass to
2155 // compensate for a superclass that shrunk, so don't do that.
2157 _objc_inform("IVARS: sliding ivars for class %s "
2158 "(superclass was %u bytes, now %u)",
2159 ro->name, ro->instanceStart,
2160 supercls->data->ro->instanceSize);
2162 class_ro_t *ro_w = make_ro_writeable(rw);
2164 moveIvars(ro_w, supercls->data->ro->instanceSize,
2165 UseGC ? &ivarBitmap : NULL, UseGC ? &weakBitmap : NULL);
2166 gdb_objc_class_changed((Class)cls, OBJC_CLASS_IVARS_CHANGED, ro->name);
2167 layoutsChanged = YES;
2171 // Check superclass's layout against this class's layout.
2172 // This needs to be done even if the superclass is not bigger.
2173 layout_bitmap superBitmap =
2174 layout_bitmap_create(supercls->data->ro->ivarLayout,
2175 supercls->data->ro->instanceSize,
2176 supercls->data->ro->instanceSize, NO);
2177 layoutsChanged |= layout_bitmap_splat(ivarBitmap, superBitmap,
2179 layout_bitmap_free(superBitmap);
2182 layout_bitmap_create(supercls->data->ro->weakIvarLayout,
2183 supercls->data->ro->instanceSize,
2184 supercls->data->ro->instanceSize, YES);
2185 layoutsChanged |= layout_bitmap_splat(weakBitmap, superBitmap,
2187 layout_bitmap_free(superBitmap);
2189 if (layoutsChanged) {
2190 // Rebuild layout strings.
2192 _objc_inform("IVARS: gc layout changed for class %s",
2195 class_ro_t *ro_w = make_ro_writeable(rw);
2197 ro_w->ivarLayout = layout_string_create(ivarBitmap);
2198 ro_w->weakIvarLayout = layout_string_create(weakBitmap);
2201 layout_bitmap_free(ivarBitmap);
2202 layout_bitmap_free(weakBitmap);
2206 // Connect this class to its superclass's subclass lists
2208 addSubclass(supercls, cls);
2211 // Attach categories
2212 methodizeClass(cls);
2215 addRealizedClass(cls);
2217 addRealizedMetaclass(cls);
2224 /***********************************************************************
2226 * Looks up a class by name. The class MIGHT NOT be realized.
2227 * Locking: runtimeLock must be read- or write-locked by the caller.
2228 **********************************************************************/
2229 static class_t *getClass(const char *name)
2231 rwlock_assert_locked(&runtimeLock);
2233 return (class_t *)NXMapGet(namedClasses(), name);
2237 /***********************************************************************
2238 * realizeAllClassesInImage
2239 * Non-lazily realizes all unrealized classes in the given image.
2240 * Locking: runtimeLock must be held by the caller.
2241 **********************************************************************/
2242 static void realizeAllClassesInImage(header_info *hi)
2244 rwlock_assert_writing(&runtimeLock);
2247 class_t **classlist;
2249 if (hi->allClassesRealized) return;
2251 classlist = _getObjc2ClassList(hi, &count);
2253 for (i = 0; i < count; i++) {
2254 realizeClass(remapClass(classlist[i]));
2257 hi->allClassesRealized = YES;
2261 /***********************************************************************
2263 * Non-lazily realizes all unrealized classes in all known images.
2264 * Locking: runtimeLock must be held by the caller.
2265 **********************************************************************/
2266 static void realizeAllClasses(void)
2268 rwlock_assert_writing(&runtimeLock);
2271 for (hi = FirstHeader; hi; hi = hi->next) {
2272 realizeAllClassesInImage(hi);
2277 /***********************************************************************
2278 * _objc_allocateFutureClass
2279 * Allocate an unresolved future class for the given class name.
2280 * Returns any existing allocation if one was already made.
2281 * Assumes the named class doesn't exist yet.
2282 * Locking: acquires runtimeLock
2283 **********************************************************************/
2284 __private_extern__ Class _objc_allocateFutureClass(const char *name)
2286 rwlock_write(&runtimeLock);
2288 struct class_t *cls;
2289 NXMapTable *future_class_map = futureClasses();
2291 if ((cls = NXMapGet(future_class_map, name))) {
2292 // Already have a future class for this name.
2293 rwlock_unlock_write(&runtimeLock);
2297 cls = (class_t *)_calloc_class(sizeof(*cls));
2298 addFutureClass(name, cls);
2300 rwlock_unlock_write(&runtimeLock);
2305 /***********************************************************************
2307 **********************************************************************/
2308 void objc_setFutureClass(Class cls, const char *name)
2310 // fixme hack do nothing - NSCFString handled specially elsewhere
2314 #define FOREACH_REALIZED_SUBCLASS(_c, _cls, code) \
2316 rwlock_assert_writing(&runtimeLock); \
2317 class_t *_top = _cls; \
2318 class_t *_c = _top; \
2322 if (_c->data->firstSubclass) { \
2323 _c = _c->data->firstSubclass; \
2325 while (!_c->data->nextSiblingClass && _c != _top) { \
2326 _c = getSuperclass(_c); \
2328 if (_c == _top) break; \
2329 _c = _c->data->nextSiblingClass; \
2333 /* nil means all realized classes */ \
2334 NXHashTable *_classes = realizedClasses(); \
2335 NXHashTable *_metaclasses = realizedMetaclasses(); \
2336 NXHashState _state; \
2337 _state = NXInitHashState(_classes); \
2338 while (NXNextHashState(_classes, &_state, (void**)&_c)) \
2342 _state = NXInitHashState(_metaclasses); \
2343 while (NXNextHashState(_metaclasses, &_state, (void**)&_c)) \
2351 /***********************************************************************
2353 * Rebuilds vtables for cls and its realized subclasses.
2354 * If cls is Nil, all realized classes and metaclasses are touched.
2355 * Locking: runtimeLock must be held by the caller.
2356 **********************************************************************/
2357 static void flushVtables(class_t *cls)
2359 rwlock_assert_writing(&runtimeLock);
2361 if (PrintVtables && !cls) {
2362 _objc_inform("VTABLES: ### EXPENSIVE ### global vtable flush!");
2365 FOREACH_REALIZED_SUBCLASS(c, cls, {
2366 updateVtable(c, NO);
2371 /***********************************************************************
2373 * Flushes caches for cls and its realized subclasses.
2374 * Does not update vtables.
2375 * If cls is Nil, all realized and metaclasses classes are touched.
2376 * Locking: runtimeLock must be held by the caller.
2377 **********************************************************************/
2378 static void flushCaches(class_t *cls)
2380 rwlock_assert_writing(&runtimeLock);
2382 FOREACH_REALIZED_SUBCLASS(c, cls, {
2383 flush_cache((Class)c);
2388 /***********************************************************************
2390 * Flushes caches and rebuilds vtables for cls, its subclasses,
2391 * and optionally its metaclass.
2392 * Locking: acquires runtimeLock
2393 **********************************************************************/
2394 __private_extern__ void flush_caches(Class cls_gen, BOOL flush_meta)
2396 class_t *cls = newcls(cls_gen);
2397 rwlock_write(&runtimeLock);
2398 // fixme optimize vtable flushing? (only needed for vtable'd selectors)
2401 // don't flush root class's metaclass twice (it's a subclass of the root)
2402 if (flush_meta && getSuperclass(cls)) {
2403 flushCaches(cls->isa);
2404 flushVtables(cls->isa);
2406 rwlock_unlock_write(&runtimeLock);
2410 /***********************************************************************
2412 * Process the given images which are being mapped in by dyld.
2413 * Calls ABI-agnostic code after taking ABI-specific locks.
2415 * Locking: write-locks runtimeLock
2416 **********************************************************************/
2417 __private_extern__ const char *
2418 map_images(enum dyld_image_states state, uint32_t infoCount,
2419 const struct dyld_image_info infoList[])
2423 rwlock_write(&runtimeLock);
2424 err = map_images_nolock(state, infoCount, infoList);
2425 rwlock_unlock_write(&runtimeLock);
2430 /***********************************************************************
2432 * Process +load in the given images which are being mapped in by dyld.
2433 * Calls ABI-agnostic code after taking ABI-specific locks.
2435 * Locking: write-locks runtimeLock and loadMethodLock
2436 **********************************************************************/
2437 __private_extern__ const char *
2438 load_images(enum dyld_image_states state, uint32_t infoCount,
2439 const struct dyld_image_info infoList[])
2443 recursive_mutex_lock(&loadMethodLock);
2445 // Discover load methods
2446 rwlock_write(&runtimeLock);
2447 found = load_images_nolock(state, infoCount, infoList);
2448 rwlock_unlock_write(&runtimeLock);
2450 // Call +load methods (without runtimeLock - re-entrant)
2452 call_load_methods();
2455 recursive_mutex_unlock(&loadMethodLock);
2461 /***********************************************************************
2463 * Process the given image which is about to be unmapped by dyld.
2464 * mh is mach_header instead of headerType because that's what
2465 * dyld_priv.h says even for 64-bit.
2467 * Locking: write-locks runtimeLock and loadMethodLock
2468 **********************************************************************/
2469 __private_extern__ void
2470 unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide)
2472 recursive_mutex_lock(&loadMethodLock);
2473 rwlock_write(&runtimeLock);
2475 unmap_image_nolock(mh, vmaddr_slide);
2477 rwlock_unlock_write(&runtimeLock);
2478 recursive_mutex_unlock(&loadMethodLock);
2482 /***********************************************************************
2484 * Perform initial processing of the headers in the linked
2485 * list beginning with headerList.
2487 * Called by: map_images_nolock
2489 * Locking: runtimeLock acquired by map_images
2490 **********************************************************************/
2491 __private_extern__ void _read_images(header_info **hList, uint32_t hCount)
2497 class_t **resolvedFutureClasses = NULL;
2498 size_t resolvedFutureClassCount = 0;
2499 static BOOL doneOnce;
2501 rwlock_assert_writing(&runtimeLock);
2508 #define EACH_HEADER \
2510 hIndex < hCount && (hi = hList[hIndex]); \
2513 // Complain about images that contain old-ABI data
2514 // fixme new-ABI compiler still emits some bits into __OBJC segment
2517 if (_getObjcSelectorRefs(hi, &count) || _getObjcModules(hi, &count)) {
2518 _objc_inform("found old-ABI metadata in image %s !",
2519 hi->os.dl_info.dli_fname);
2524 static BOOL hackedNSCFString = NO;
2525 if (!hackedNSCFString) {
2526 // Insert future class __CFConstantStringClassReference == NSCFString
2527 void *dlh = dlopen("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", RTLD_LAZY | RTLD_NOLOAD | RTLD_FIRST);
2529 void *addr = dlsym(dlh, "__CFConstantStringClassReference");
2531 addFutureClass("NSCFString", (class_t *)addr);
2532 hackedNSCFString = YES;
2538 // Discover classes. Fix up unresolved future classes. Mark bundle classes.
2539 NXMapTable *future_class_map = futureClasses();
2541 class_t **classlist = _getObjc2ClassList(hi, &count);
2542 for (i = 0; i < count; i++) {
2543 const char *name = getName(classlist[i]);
2544 if (NXCountMapTable(future_class_map) > 0) {
2545 class_t *newCls = NXMapGet(future_class_map, name);
2547 // Copy class_t to future class's struct.
2548 // Preserve future's rw data block.
2549 class_rw_t *rw = newCls->data;
2550 memcpy(newCls, classlist[i], sizeof(class_t));
2551 rw->ro = (class_ro_t *)newCls->data;
2554 removeFutureClass(name);
2555 addRemappedClass(classlist[i], newCls);
2556 classlist[i] = newCls;
2557 // Non-lazily realize the class below.
2558 resolvedFutureClasses = (class_t **)
2559 _realloc_internal(resolvedFutureClasses,
2560 (resolvedFutureClassCount+1)
2561 * sizeof(class_t *));
2562 resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
2565 addNamedClass(classlist[i], name);
2566 addUninitializedClass(classlist[i], classlist[i]->isa);
2567 if (hi->mhdr->filetype == MH_BUNDLE) {
2568 classlist[i]->data->flags |= RO_FROM_BUNDLE;
2569 classlist[i]->isa->data->flags |= RO_FROM_BUNDLE;
2574 // Fix up remapped classes
2575 // classlist is up to date, but classrefs may not be
2577 if (!noClassesRemapped()) {
2579 class_t **classrefs = _getObjc2ClassRefs(hi, &count);
2580 for (i = 0; i < count; i++) {
2581 remapClassRef(&classrefs[i]);
2583 // fixme why doesn't test future1 catch the absence of this?
2584 classrefs = _getObjc2SuperRefs(hi, &count);
2585 for (i = 0; i < count; i++) {
2586 remapClassRef(&classrefs[i]);
2592 // Fix up @selector references
2596 if (sel_preoptimizationValid(hi)) {
2597 _objc_inform("PREOPTIMIZATION: honoring preoptimized selectors in %s",
2598 _nameForHeader(hi->mhdr));
2600 else if (_objcHeaderOptimizedByDyld(hi)) {
2601 _objc_inform("PREOPTIMIZATION: IGNORING preoptimized selectors in %s",
2602 _nameForHeader(hi->mhdr));
2606 if (sel_preoptimizationValid(hi)) continue;
2608 SEL *sels = _getObjc2SelectorRefs(hi, &count);
2609 BOOL isBundle = hi->mhdr->filetype == MH_BUNDLE;
2610 for (i = 0; i < count; i++) {
2611 sels[i] = sel_registerNameNoLock((const char *)sels[i], isBundle);
2616 // Discover protocols. Fix up protocol refs.
2617 NXMapTable *protocol_map = protocols();
2619 extern struct class_t OBJC_CLASS_$_Protocol;
2620 Class cls = (Class)&OBJC_CLASS_$_Protocol;
2622 protocol_t **protocols = _getObjc2ProtocolList(hi, &count);
2623 // fixme duplicate protocol from bundle
2624 for (i = 0; i < count; i++) {
2625 if (!NXMapGet(protocol_map, protocols[i]->name)) {
2626 protocols[i]->isa = cls;
2627 NXMapKeyCopyingInsert(protocol_map,
2628 protocols[i]->name, protocols[i]);
2629 if (PrintProtocols) {
2630 _objc_inform("PROTOCOLS: protocol at %p is %s",
2631 protocols[i], protocols[i]->name);
2634 if (PrintProtocols) {
2635 _objc_inform("PROTOCOLS: protocol at %p is %s (duplicate)",
2636 protocols[i], protocols[i]->name);
2642 protocol_t **protocols;
2643 protocols = _getObjc2ProtocolRefs(hi, &count);
2644 for (i = 0; i < count; i++) {
2645 remapProtocolRef(&protocols[i]);
2649 // Realize non-lazy classes (for +load methods and static instances)
2651 class_t **classlist =
2652 _getObjc2NonlazyClassList(hi, &count);
2653 for (i = 0; i < count; i++) {
2654 realizeClass(remapClass(classlist[i]));
2658 // Realize newly-resolved future classes, in case CF manipulates them
2659 if (resolvedFutureClasses) {
2660 for (i = 0; i < resolvedFutureClassCount; i++) {
2661 realizeClass(resolvedFutureClasses[i]);
2663 _free_internal(resolvedFutureClasses);
2666 // Discover categories.
2668 category_t **catlist =
2669 _getObjc2CategoryList(hi, &count);
2670 for (i = 0; i < count; i++) {
2671 category_t *cat = catlist[i];
2672 // Do NOT use cat->cls! It may have been remapped.
2673 class_t *cls = remapClass(cat->cls);
2675 // Process this category.
2676 // First, register the category with its target class.
2677 // Then, rebuild the class's method lists (etc) if
2678 // the class is realized.
2679 BOOL classExists = NO;
2680 if (cat->instanceMethods || cat->protocols
2681 || cat->instanceProperties)
2683 addUnattachedCategoryForClass(cat, cls, hi);
2684 if (isRealized(cls)) {
2685 remethodizeClass(cls);
2688 if (PrintConnecting) {
2689 _objc_inform("CLASS: found category -%s(%s) %s",
2690 getName(cls), cat->name,
2691 classExists ? "on existing class" : "");
2695 if (cat->classMethods || cat->protocols
2696 /* || cat->classProperties */)
2698 addUnattachedCategoryForClass(cat, cls->isa, hi);
2699 if (isRealized(cls->isa)) {
2700 remethodizeClass(cls->isa);
2702 if (PrintConnecting) {
2703 _objc_inform("CLASS: found category +%s(%s)",
2704 getName(cls), cat->name);
2710 // Category discovery MUST BE LAST to avoid potential races
2711 // when other threads call the new category code before
2712 // this thread finishes its fixups.
2714 // +load handled by prepare_load_methods()
2720 /***********************************************************************
2721 * prepare_load_methods
2722 * Schedule +load for classes in this image, any un-+load-ed
2723 * superclasses in other images, and any categories in this image.
2724 **********************************************************************/
2725 // Recursively schedule +load for cls and any un-+load-ed superclasses.
2726 // cls must already be connected.
2727 static void schedule_class_load(class_t *cls)
2729 assert(isRealized(cls)); // _read_images should realize
2731 if (cls->data->flags & RW_LOADED) return;
2733 class_t *supercls = getSuperclass(cls);
2734 if (supercls) schedule_class_load(supercls);
2736 add_class_to_loadable_list((Class)cls);
2737 changeInfo(cls, RW_LOADED, 0);
2740 __private_extern__ void prepare_load_methods(header_info *hi)
2744 rwlock_assert_writing(&runtimeLock);
2746 class_t **classlist =
2747 _getObjc2NonlazyClassList(hi, &count);
2748 for (i = 0; i < count; i++) {
2749 class_t *cls = remapClass(classlist[i]);
2750 schedule_class_load(cls);
2753 category_t **categorylist = _getObjc2NonlazyCategoryList(hi, &count);
2754 for (i = 0; i < count; i++) {
2755 category_t *cat = categorylist[i];
2756 // Do NOT use cat->cls! It may have been remapped.
2757 class_t *cls = remapClass(cat->cls);
2759 assert(isRealized(cls->isa));
2760 add_category_to_loadable_list((Category)cat);
2765 /***********************************************************************
2767 * Only handles MH_BUNDLE for now.
2768 * Locking: write-lock and loadMethodLock acquired by unmap_image
2769 **********************************************************************/
2770 __private_extern__ void _unload_image(header_info *hi)
2774 recursive_mutex_assert_locked(&loadMethodLock);
2775 rwlock_assert_writing(&runtimeLock);
2777 // Unload unattached categories and categories waiting for +load.
2779 category_t **catlist = _getObjc2CategoryList(hi, &count);
2780 for (i = 0; i < count; i++) {
2781 category_t *cat = catlist[i];
2782 class_t *cls = remapClass(cat->cls);
2783 // fixme for MH_DYLIB cat's class may have been unloaded already
2786 removeUnattachedCategoryForClass(cat, cls);
2789 remove_category_from_loadable_list((Category)cat);
2794 class_t **classlist = _getObjc2ClassList(hi, &count);
2795 for (i = 0; i < count; i++) {
2796 class_t *cls = classlist[i];
2797 // fixme remapped classes?
2798 remove_class_from_loadable_list((Class)cls);
2799 unload_class(cls->isa, YES);
2800 unload_class(cls, NO);
2803 // Clean up protocols.
2804 #warning fixme protocol unload
2806 // fixme DebugUnload
2810 /***********************************************************************
2811 * method_getDescription
2812 * Returns a pointer to this method's objc_method_description.
2814 **********************************************************************/
2815 struct objc_method_description *
2816 method_getDescription(Method m)
2818 if (!m) return NULL;
2819 return (struct objc_method_description *)newmethod(m);
2823 /***********************************************************************
2824 * method_getImplementation
2825 * Returns this method's IMP.
2827 **********************************************************************/
2829 _method_getImplementation(method_t *m)
2831 if (!m) return NULL;
2836 method_getImplementation(Method m)
2838 return _method_getImplementation(newmethod(m));
2842 /***********************************************************************
2844 * Returns this method's selector.
2845 * The method must not be NULL.
2846 * The method must already have been fixed-up.
2848 **********************************************************************/
2850 method_getName(Method m_gen)
2852 struct method_t *m = newmethod(m_gen);
2853 if (!m) return NULL;
2855 assert((SEL)m->name == sel_registerName((char *)m->name));
2856 return (SEL)m->name;
2860 /***********************************************************************
2861 * method_getTypeEncoding
2862 * Returns this method's old-style type encoding string.
2863 * The method must not be NULL.
2865 **********************************************************************/
2867 method_getTypeEncoding(Method m)
2869 if (!m) return NULL;
2870 return newmethod(m)->types;
2874 /***********************************************************************
2875 * method_setImplementation
2876 * Sets this method's implementation to imp.
2877 * The previous implementation is returned.
2878 **********************************************************************/
2880 _method_setImplementation(class_t *cls, method_t *m, IMP imp)
2882 rwlock_assert_writing(&runtimeLock);
2884 if (!m) return NULL;
2885 if (!imp) return NULL;
2887 if (m->name == (SEL)kIgnore) {
2888 // Ignored methods stay ignored
2892 IMP old = _method_getImplementation(m);
2895 // No cache flushing needed - cache contains Methods not IMPs.
2897 if (vtable_containsSelector(newmethod(m)->name)) {
2898 // Will be slow if cls is NULL (i.e. unknown)
2899 // fixme build list of classes whose Methods are known externally?
2903 // fixme update monomorphism if necessary
2909 method_setImplementation(Method m, IMP imp)
2911 // Don't know the class - will be slow if vtables are affected
2912 // fixme build list of classes whose Methods are known externally?
2914 rwlock_write(&runtimeLock);
2915 result = _method_setImplementation(Nil, newmethod(m), imp);
2916 rwlock_unlock_write(&runtimeLock);
2921 void method_exchangeImplementations(Method m1_gen, Method m2_gen)
2923 method_t *m1 = newmethod(m1_gen);
2924 method_t *m2 = newmethod(m2_gen);
2925 if (!m1 || !m2) return;
2927 rwlock_write(&runtimeLock);
2929 if (m1->name == (SEL)kIgnore || m2->name == (SEL)kIgnore) {
2930 // Ignored methods stay ignored. Now they're both ignored.
2931 m1->imp = (IMP)&_objc_ignored_method;
2932 m2->imp = (IMP)&_objc_ignored_method;
2933 rwlock_unlock_write(&runtimeLock);
2937 IMP m1_imp = m1->imp;
2941 if (vtable_containsSelector(m1->name) ||
2942 vtable_containsSelector(m2->name))
2944 // Don't know the class - will be slow if vtables are affected
2945 // fixme build list of classes whose Methods are known externally?
2949 // fixme update monomorphism if necessary
2951 rwlock_unlock_write(&runtimeLock);
2955 /***********************************************************************
2959 **********************************************************************/
2961 ivar_getOffset(Ivar ivar)
2963 if (!ivar) return 0;
2964 return *newivar(ivar)->offset;
2968 /***********************************************************************
2972 **********************************************************************/
2974 ivar_getName(Ivar ivar)
2976 if (!ivar) return NULL;
2977 return newivar(ivar)->name;
2981 /***********************************************************************
2982 * ivar_getTypeEncoding
2985 **********************************************************************/
2987 ivar_getTypeEncoding(Ivar ivar)
2989 if (!ivar) return NULL;
2990 return newivar(ivar)->type;
2994 /***********************************************************************
2995 * _protocol_getMethod_nolock
2996 * Locking: runtimeLock must be write-locked by the caller
2997 **********************************************************************/
2999 _protocol_getMethod_nolock(protocol_t *proto, SEL sel,
3000 BOOL isRequiredMethod, BOOL isInstanceMethod)
3002 rwlock_assert_writing(&runtimeLock);
3005 if (!proto || !sel) return NULL;
3007 method_list_t **mlistp = NULL;
3009 if (isRequiredMethod) {
3010 if (isInstanceMethod) {
3011 mlistp = &proto->instanceMethods;
3013 mlistp = &proto->classMethods;
3016 if (isInstanceMethod) {
3017 mlistp = &proto->optionalInstanceMethods;
3019 mlistp = &proto->optionalClassMethods;
3024 method_list_t *mlist = *mlistp;
3025 if (!isMethodListFixedUp(mlist)) {
3026 mlist = _memdup_internal(mlist, method_list_size(mlist));
3027 fixupMethodList(mlist, YES/*always copy for simplicity*/);
3030 for (i = 0; i < mlist->count; i++) {
3031 method_t *m = method_list_nth(mlist, i);
3032 if (sel == m->name) return (Method)m;
3036 if (proto->protocols) {
3038 for (i = 0; i < proto->protocols->count; i++) {
3039 protocol_t *realProto = remapProtocol(proto->protocols->list[i]);
3040 m = _protocol_getMethod_nolock(realProto, sel,
3041 isRequiredMethod, isInstanceMethod);
3050 /***********************************************************************
3051 * _protocol_getMethod
3053 * Locking: write-locks runtimeLock
3054 **********************************************************************/
3055 __private_extern__ Method
3056 _protocol_getMethod(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod)
3058 rwlock_write(&runtimeLock);
3059 Method result = _protocol_getMethod_nolock(newprotocol(p), sel,
3062 rwlock_unlock_write(&runtimeLock);
3067 /***********************************************************************
3069 * Returns the name of the given protocol.
3070 * Locking: runtimeLock must not be held by the caller
3071 **********************************************************************/
3073 protocol_getName(Protocol *proto)
3075 return newprotocol(proto)->name;
3079 /***********************************************************************
3080 * protocol_getInstanceMethodDescription
3081 * Returns the description of a named instance method.
3082 * Locking: runtimeLock must not be held by the caller
3083 **********************************************************************/
3084 struct objc_method_description
3085 protocol_getMethodDescription(Protocol *p, SEL aSel,
3086 BOOL isRequiredMethod, BOOL isInstanceMethod)
3089 _protocol_getMethod(p, aSel, isRequiredMethod, isInstanceMethod);
3090 if (m) return *method_getDescription(m);
3091 else return (struct objc_method_description){NULL, NULL};
3095 /***********************************************************************
3096 * _protocol_conformsToProtocol_nolock
3097 * Returns YES if self conforms to other.
3098 * Locking: runtimeLock must be held by the caller.
3099 **********************************************************************/
3100 static BOOL _protocol_conformsToProtocol_nolock(protocol_t *self, protocol_t *other)
3102 if (!self || !other) {
3106 if (0 == strcmp(self->name, other->name)) {
3110 if (self->protocols) {
3112 for (i = 0; i < self->protocols->count; i++) {
3113 protocol_t *proto = remapProtocol(self->protocols->list[i]);
3114 if (0 == strcmp(other->name, proto->name)) {
3117 if (_protocol_conformsToProtocol_nolock(proto, other)) {
3127 /***********************************************************************
3128 * protocol_conformsToProtocol
3129 * Returns YES if self conforms to other.
3130 * Locking: acquires runtimeLock
3131 **********************************************************************/
3132 BOOL protocol_conformsToProtocol(Protocol *self, Protocol *other)
3135 rwlock_read(&runtimeLock);
3136 result = _protocol_conformsToProtocol_nolock(newprotocol(self),
3137 newprotocol(other));
3138 rwlock_unlock_read(&runtimeLock);
3143 /***********************************************************************
3145 * Return YES if two protocols are equal (i.e. conform to each other)
3146 * Locking: acquires runtimeLock
3147 **********************************************************************/
3148 BOOL protocol_isEqual(Protocol *self, Protocol *other)
3150 if (self == other) return YES;
3151 if (!self || !other) return NO;
3153 if (!protocol_conformsToProtocol(self, other)) return NO;
3154 if (!protocol_conformsToProtocol(other, self)) return NO;
3160 /***********************************************************************
3161 * protocol_copyMethodDescriptionList
3162 * Returns descriptions of a protocol's methods.
3163 * Locking: acquires runtimeLock
3164 **********************************************************************/
3165 struct objc_method_description *
3166 protocol_copyMethodDescriptionList(Protocol *p,
3167 BOOL isRequiredMethod,BOOL isInstanceMethod,
3168 unsigned int *outCount)
3170 struct protocol_t *proto = newprotocol(p);
3171 struct objc_method_description *result = NULL;
3172 unsigned int count = 0;
3175 if (outCount) *outCount = 0;
3179 rwlock_read(&runtimeLock);
3181 method_list_t *mlist = NULL;
3183 if (isRequiredMethod) {
3184 if (isInstanceMethod) {
3185 mlist = proto->instanceMethods;
3187 mlist = proto->classMethods;
3190 if (isInstanceMethod) {
3191 mlist = proto->optionalInstanceMethods;
3193 mlist = proto->optionalClassMethods;
3199 count = mlist->count;
3200 result = calloc(count + 1, sizeof(struct objc_method_description));
3201 for (i = 0; i < count; i++) {
3202 method_t *m = method_list_nth(mlist, i);
3203 result[i].name = sel_registerName((const char *)m->name);
3204 result[i].types = (char *)m->types;
3208 rwlock_unlock_read(&runtimeLock);
3210 if (outCount) *outCount = count;
3215 /***********************************************************************
3216 * protocol_getProperty
3218 * Locking: acquires runtimeLock
3219 **********************************************************************/
3221 _protocol_getProperty_nolock(protocol_t *proto, const char *name,
3222 BOOL isRequiredProperty, BOOL isInstanceProperty)
3224 if (!isRequiredProperty || !isInstanceProperty) {
3225 // Only required instance properties are currently supported
3229 struct objc_property_list *plist;
3230 if ((plist = proto->instanceProperties)) {
3232 for (i = 0; i < plist->count; i++) {
3233 Property prop = property_list_nth(plist, i);
3234 if (0 == strcmp(name, prop->name)) {
3240 if (proto->protocols) {
3242 for (i = 0; i < proto->protocols->count; i++) {
3243 protocol_t *p = remapProtocol(proto->protocols->list[i]);
3245 _protocol_getProperty_nolock(p, name,
3247 isInstanceProperty);
3248 if (prop) return prop;
3255 Property protocol_getProperty(Protocol *p, const char *name,
3256 BOOL isRequiredProperty, BOOL isInstanceProperty)
3260 if (!p || !name) return NULL;
3262 rwlock_read(&runtimeLock);
3263 result = _protocol_getProperty_nolock(newprotocol(p), name,
3265 isInstanceProperty);
3266 rwlock_unlock_read(&runtimeLock);
3272 /***********************************************************************
3273 * protocol_copyPropertyList
3275 * Locking: acquires runtimeLock
3276 **********************************************************************/
3277 Property *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
3279 Property *result = NULL;
3282 if (outCount) *outCount = 0;
3286 rwlock_read(&runtimeLock);
3288 struct objc_property_list *plist = newprotocol(proto)->instanceProperties;
3289 result = copyPropertyList(plist, outCount);
3291 rwlock_unlock_read(&runtimeLock);
3297 /***********************************************************************
3298 * protocol_copyProtocolList
3299 * Copies this protocol's incorporated protocols.
3300 * Does not copy those protocol's incorporated protocols in turn.
3301 * Locking: acquires runtimeLock
3302 **********************************************************************/
3303 Protocol **protocol_copyProtocolList(Protocol *p, unsigned int *outCount)
3305 unsigned int count = 0;
3306 Protocol **result = NULL;
3307 protocol_t *proto = newprotocol(p);
3310 if (outCount) *outCount = 0;
3314 rwlock_read(&runtimeLock);
3316 if (proto->protocols) {
3317 count = (unsigned int)proto->protocols->count;
3320 result = malloc((count+1) * sizeof(Protocol *));
3323 for (i = 0; i < count; i++) {
3324 result[i] = (Protocol *)remapProtocol(proto->protocols->list[i]);
3329 rwlock_unlock_read(&runtimeLock);
3331 if (outCount) *outCount = count;
3336 /***********************************************************************
3338 * Returns pointers to all classes.
3339 * This requires all classes be realized, which is regretfully non-lazy.
3340 * Locking: acquires runtimeLock
3341 **********************************************************************/
3343 objc_getClassList(Class *buffer, int bufferLen)
3345 rwlock_write(&runtimeLock);
3347 realizeAllClasses();
3352 NXHashTable *classes = realizedClasses();
3353 int allCount = NXCountHashTable(classes);
3356 rwlock_unlock_write(&runtimeLock);
3361 state = NXInitHashState(classes);
3362 while (count < bufferLen &&
3363 NXNextHashState(classes, &state, (void **)&cls))
3365 buffer[count++] = (Class)cls;
3368 rwlock_unlock_write(&runtimeLock);
3374 /***********************************************************************
3375 * objc_copyProtocolList
3376 * Returns pointers to all protocols.
3377 * Locking: read-locks runtimeLock
3378 **********************************************************************/
3380 objc_copyProtocolList(unsigned int *outCount)
3382 rwlock_read(&runtimeLock);
3388 NXMapTable *protocol_map = protocols();
3391 count = NXCountMapTable(protocol_map);
3393 rwlock_unlock_read(&runtimeLock);
3394 if (outCount) *outCount = 0;
3398 result = calloc(1 + count, sizeof(Protocol *));
3401 state = NXInitMapState(protocol_map);
3402 while (NXNextMapState(protocol_map, &state,
3403 (const void **)&name, (const void **)&proto))
3405 result[i++] = proto;
3409 assert(i == count+1);
3411 rwlock_unlock_read(&runtimeLock);
3413 if (outCount) *outCount = count;
3418 /***********************************************************************
3420 * Get a protocol by name, or return NULL
3421 * Locking: read-locks runtimeLock
3422 **********************************************************************/
3423 Protocol *objc_getProtocol(const char *name)
3425 rwlock_read(&runtimeLock);
3426 Protocol *result = (Protocol *)NXMapGet(protocols(), name);
3427 rwlock_unlock_read(&runtimeLock);
3432 /***********************************************************************
3433 * class_copyMethodList
3435 * Locking: read-locks runtimeLock
3436 **********************************************************************/
3438 class_copyMethodList(Class cls_gen, unsigned int *outCount)
3440 struct class_t *cls = newcls(cls_gen);
3441 unsigned int count = 0;
3442 Method *result = NULL;
3445 if (outCount) *outCount = 0;
3449 rwlock_read(&runtimeLock);
3451 assert(isRealized(cls));
3453 FOREACH_METHOD_LIST(mlist, cls, {
3454 count += mlist->count;
3459 result = malloc((count + 1) * sizeof(Method));
3462 FOREACH_METHOD_LIST(mlist, cls, {
3464 for (i = 0; i < mlist->count; i++) {
3465 Method aMethod = (Method)method_list_nth(mlist, i);
3466 if (method_getName(aMethod) == (SEL)kIgnore) {
3470 result[m++] = aMethod;
3476 rwlock_unlock_read(&runtimeLock);
3478 if (outCount) *outCount = count;
3483 /***********************************************************************
3484 * class_copyIvarList
3486 * Locking: read-locks runtimeLock
3487 **********************************************************************/
3489 class_copyIvarList(Class cls_gen, unsigned int *outCount)
3491 struct class_t *cls = newcls(cls_gen);
3492 const ivar_list_t *ivars;
3493 Ivar *result = NULL;
3494 unsigned int count = 0;
3498 if (outCount) *outCount = 0;
3502 rwlock_read(&runtimeLock);
3504 assert(isRealized(cls));
3506 if ((ivars = cls->data->ro->ivars) && ivars->count) {
3507 result = malloc((ivars->count+1) * sizeof(Ivar));
3509 for (i = 0; i < ivars->count; i++) {
3510 ivar_t *ivar = ivar_list_nth(ivars, i);
3511 if (!ivar->offset) continue; // anonymous bitfield
3512 result[count++] = (Ivar)ivar;
3514 result[count] = NULL;
3517 rwlock_unlock_read(&runtimeLock);
3519 if (outCount) *outCount = count;
3524 /***********************************************************************
3525 * class_copyPropertyList. Returns a heap block containing the
3526 * properties declared in the class, or NULL if the class
3527 * declares no properties. Caller must free the block.
3528 * Does not copy any superclass's properties.
3529 * Locking: read-locks runtimeLock
3530 **********************************************************************/
3532 class_copyPropertyList(Class cls_gen, unsigned int *outCount)
3534 struct class_t *cls = newcls(cls_gen);
3535 chained_property_list *plist;
3536 unsigned int count = 0;
3537 Property *result = NULL;
3540 if (outCount) *outCount = 0;
3544 rwlock_read(&runtimeLock);
3546 assert(isRealized(cls));
3548 for (plist = cls->data->properties; plist; plist = plist->next) {
3549 count += plist->count;
3554 result = malloc((count + 1) * sizeof(Property));
3557 for (plist = cls->data->properties; plist; plist = plist->next) {
3559 for (i = 0; i < plist->count; i++) {
3560 result[p++] = (Property)&plist->list[i];
3566 rwlock_unlock_read(&runtimeLock);
3568 if (outCount) *outCount = count;
3573 /***********************************************************************
3574 * _class_getLoadMethod
3576 * Called only from add_class_to_loadable_list.
3577 * Locking: runtimeLock must be read- or write-locked by the caller.
3578 **********************************************************************/
3579 __private_extern__ IMP
3580 _class_getLoadMethod(Class cls_gen)
3582 rwlock_assert_locked(&runtimeLock);
3584 struct class_t *cls = newcls(cls_gen);
3585 const method_list_t *mlist;
3588 assert(isRealized(cls));
3589 assert(isRealized(cls->isa));
3590 assert(!isMetaClass(cls));
3591 assert(isMetaClass(cls->isa));
3593 mlist = cls->isa->data->ro->baseMethods;
3594 if (mlist) for (i = 0; i < mlist->count; i++) {
3595 method_t *m = method_list_nth(mlist, i);
3596 if (0 == strcmp((const char *)m->name, "load")) {
3605 /***********************************************************************
3607 * Returns a category's name.
3609 **********************************************************************/
3610 __private_extern__ const char *
3611 _category_getName(Category cat)
3613 return newcategory(cat)->name;
3617 /***********************************************************************
3618 * _category_getClassName
3619 * Returns a category's class's name
3620 * Called only from add_category_to_loadable_list and
3621 * remove_category_from_loadable_list.
3622 * Locking: runtimeLock must be read- or write-locked by the caller
3623 **********************************************************************/
3624 __private_extern__ const char *
3625 _category_getClassName(Category cat)
3627 rwlock_assert_locked(&runtimeLock);
3628 // cat->cls may have been remapped
3629 return getName(remapClass(newcategory(cat)->cls));
3633 /***********************************************************************
3634 * _category_getClass
3635 * Returns a category's class
3636 * Called only by call_category_loads.
3637 * Locking: read-locks runtimeLock
3638 **********************************************************************/
3639 __private_extern__ Class
3640 _category_getClass(Category cat)
3642 rwlock_read(&runtimeLock);
3643 // cat->cls may have been remapped
3644 struct class_t *result = remapClass(newcategory(cat)->cls);
3645 assert(isRealized(result)); // ok for call_category_loads' usage
3646 rwlock_unlock_read(&runtimeLock);
3647 return (Class)result;
3651 /***********************************************************************
3652 * _category_getLoadMethod
3654 * Called only from add_category_to_loadable_list
3655 * Locking: runtimeLock must be read- or write-locked by the caller
3656 **********************************************************************/
3657 __private_extern__ IMP
3658 _category_getLoadMethod(Category cat)
3660 rwlock_assert_locked(&runtimeLock);
3662 const method_list_t *mlist;
3665 mlist = newcategory(cat)->classMethods;
3666 if (mlist) for (i = 0; i < mlist->count; i++) {
3667 method_t *m = method_list_nth(mlist, i);
3668 if (0 == strcmp((const char *)m->name, "load")) {
3677 /***********************************************************************
3678 * class_copyProtocolList
3680 * Locking: read-locks runtimeLock
3681 **********************************************************************/
3683 class_copyProtocolList(Class cls_gen, unsigned int *outCount)
3685 struct class_t *cls = newcls(cls_gen);
3687 struct protocol_list_t **p;
3688 unsigned int count = 0;
3690 Protocol **result = NULL;
3693 if (outCount) *outCount = 0;
3697 rwlock_read(&runtimeLock);
3699 assert(isRealized(cls));
3701 for (p = cls->data->protocols; p && *p; p++) {
3702 count += (uint32_t)(*p)->count;
3706 result = malloc((count+1) * sizeof(Protocol *));
3708 for (p = cls->data->protocols; p && *p; p++) {
3709 for (i = 0; i < (*p)->count; i++) {
3710 *r++ = (Protocol *)remapProtocol((*p)->list[i]);
3716 rwlock_unlock_read(&runtimeLock);
3718 if (outCount) *outCount = count;
3723 /***********************************************************************
3724 * _objc_copyClassNamesForImage
3726 * Locking: read-locks runtimeLock
3727 **********************************************************************/
3728 __private_extern__ const char **
3729 _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount)
3732 class_t **classlist;
3735 rwlock_read(&runtimeLock);
3737 classlist = _getObjc2ClassList(hi, &count);
3738 names = malloc((count+1) * sizeof(const char *));
3740 for (i = 0; i < count; i++) {
3741 names[i] = getName(classlist[i]);
3743 names[count] = NULL;
3745 rwlock_unlock_read(&runtimeLock);
3747 if (outCount) *outCount = (unsigned int)count;
3752 /***********************************************************************
3756 **********************************************************************/
3757 __private_extern__ Cache
3758 _class_getCache(Class cls)
3760 return newcls(cls)->cache;
3764 /***********************************************************************
3765 * _class_getInstanceSize
3768 **********************************************************************/
3769 __private_extern__ size_t
3770 _class_getInstanceSize(Class cls)
3773 return instanceSize(newcls(cls));
3777 instanceSize(struct class_t *cls)
3780 assert(isRealized(cls));
3781 // fixme rdar://5244378
3782 return (uint32_t)((cls->data->ro->instanceSize + WORD_MASK) & ~WORD_MASK);
3786 /***********************************************************************
3790 **********************************************************************/
3792 class_getVersion(Class cls)
3795 assert(isRealized(newcls(cls)));
3796 return newcls(cls)->data->version;
3800 /***********************************************************************
3804 **********************************************************************/
3805 __private_extern__ void
3806 _class_setCache(Class cls, Cache cache)
3808 newcls(cls)->cache = cache;
3812 /***********************************************************************
3816 **********************************************************************/
3818 class_setVersion(Class cls, int version)
3821 assert(isRealized(newcls(cls)));
3822 newcls(cls)->data->version = version;
3826 /***********************************************************************
3829 * Locking: acquires runtimeLock
3830 **********************************************************************/
3831 __private_extern__ const char *_class_getName(Class cls)
3833 if (!cls) return "nil";
3834 // fixme hack rwlock_write(&runtimeLock);
3835 const char *name = getName(newcls(cls));
3836 // rwlock_unlock_write(&runtimeLock);
3841 /***********************************************************************
3844 * Locking: runtimeLock must be held by the caller
3845 **********************************************************************/
3847 getName(struct class_t *cls)
3849 // fixme hack rwlock_assert_writing(&runtimeLock);
3852 if (isRealized(cls)) {
3853 return cls->data->ro->name;
3855 return ((const struct class_ro_t *)cls->data)->name;
3860 /***********************************************************************
3861 * getMethodNoSuper_nolock
3863 * Locking: runtimeLock must be read- or write-locked by the caller
3864 **********************************************************************/
3866 getMethodNoSuper_nolock(struct class_t *cls, SEL sel)
3868 rwlock_assert_locked(&runtimeLock);
3872 assert(isRealized(cls));
3876 FOREACH_METHOD_LIST(mlist, cls, {
3877 for (i = 0; i < mlist->count; i++) {
3878 method_t *m = method_list_nth(mlist, i);
3879 if (m->name == sel) return m;
3887 /***********************************************************************
3888 * _class_getMethodNoSuper
3890 * Locking: read-locks runtimeLock
3891 **********************************************************************/
3892 __private_extern__ Method
3893 _class_getMethodNoSuper(Class cls, SEL sel)
3895 rwlock_read(&runtimeLock);
3896 Method result = (Method)getMethodNoSuper_nolock(newcls(cls), sel);
3897 rwlock_unlock_read(&runtimeLock);
3901 /***********************************************************************
3902 * _class_getMethodNoSuper
3903 * For use inside lockForMethodLookup() only.
3904 * Locking: read-locks runtimeLock
3905 **********************************************************************/
3906 __private_extern__ Method
3907 _class_getMethodNoSuper_nolock(Class cls, SEL sel)
3909 return (Method)getMethodNoSuper_nolock(newcls(cls), sel);
3913 /***********************************************************************
3916 * Locking: runtimeLock must be read- or write-locked by the caller
3917 **********************************************************************/
3919 getMethod_nolock(class_t *cls, SEL sel)
3923 rwlock_assert_locked(&runtimeLock);
3928 assert(isRealized(cls));
3930 while (cls && ((m = getMethodNoSuper_nolock(cls, sel))) == NULL) {
3931 cls = getSuperclass(cls);
3938 /***********************************************************************
3941 * Locking: read-locks runtimeLock
3942 **********************************************************************/
3943 __private_extern__ Method _class_getMethod(Class cls, SEL sel)
3946 rwlock_read(&runtimeLock);
3947 m = (Method)getMethod_nolock(newcls(cls), sel);
3948 rwlock_unlock_read(&runtimeLock);
3953 /***********************************************************************
3954 * ABI-specific lookUpMethod helpers.
3955 * Locking: read- and write-locks runtimeLock.
3956 **********************************************************************/
3957 __private_extern__ void lockForMethodLookup(void)
3959 rwlock_read(&runtimeLock);
3961 __private_extern__ void unlockForMethodLookup(void)
3963 rwlock_unlock_read(&runtimeLock);
3966 __private_extern__ IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init)
3968 rwlock_assert_unlocked(&runtimeLock);
3970 if (!isRealized(newcls(cls))) {
3971 rwlock_write(&runtimeLock);
3972 realizeClass(newcls(cls));
3973 rwlock_unlock_write(&runtimeLock);
3976 if (init && !_class_isInitialized(cls)) {
3977 _class_initialize (cls);
3978 // If sel == initialize, _class_initialize will send +initialize and
3979 // then the messenger will send +initialize again after this
3980 // procedure finishes. Of course, if this is not being called
3981 // from the messenger then it won't happen. 2778172
3988 /***********************************************************************
3991 * Locking: read-locks runtimeLock
3992 **********************************************************************/
3993 Property class_getProperty(Class cls_gen, const char *name)
3995 Property result = NULL;
3996 chained_property_list *plist;
3997 struct class_t *cls = newcls(cls_gen);
3999 if (!cls || !name) return NULL;
4001 rwlock_read(&runtimeLock);
4003 assert(isRealized(cls));
4005 for ( ; cls; cls = getSuperclass(cls)) {
4006 for (plist = cls->data->properties; plist; plist = plist->next) {
4008 for (i = 0; i < plist->count; i++) {
4009 if (0 == strcmp(name, plist->list[i].name)) {
4010 result = &plist->list[i];
4018 rwlock_unlock_read(&runtimeLock);
4024 /***********************************************************************
4026 **********************************************************************/
4027 __private_extern__ BOOL _class_isMetaClass(Class cls)
4029 if (!cls) return NO;
4030 return isMetaClass(newcls(cls));
4034 isMetaClass(struct class_t *cls)
4037 assert(isRealized(cls));
4038 return (cls->data->ro->flags & RO_META) ? YES : NO;
4042 __private_extern__ Class _class_getMeta(Class cls)
4045 if (isMetaClass(newcls(cls))) return cls;
4046 else return ((id)cls)->isa;
4049 Class gdb_class_getClass(Class cls)
4051 const char *className = strdup(getName(newcls(cls)));
4052 if(!className) return Nil;
4053 Class rCls = look_up_class(className, NO, NO);
4054 free((char*)className);
4058 BOOL gdb_objc_isRuntimeLocked()
4060 if (rwlock_try_write(&runtimeLock)) {
4061 rwlock_unlock_write(&runtimeLock);
4065 if (mutex_try_lock(&cacheUpdateLock)) {
4066 mutex_unlock(&cacheUpdateLock);
4073 /***********************************************************************
4075 **********************************************************************/
4076 __private_extern__ BOOL
4077 _class_isInitializing(Class cls_gen)
4079 struct class_t *cls = newcls(_class_getMeta(cls_gen));
4080 return (cls->data->flags & RW_INITIALIZING) ? YES : NO;
4084 /***********************************************************************
4086 **********************************************************************/
4087 __private_extern__ BOOL
4088 _class_isInitialized(Class cls_gen)
4090 struct class_t *cls = newcls(_class_getMeta(cls_gen));
4091 return (cls->data->flags & RW_INITIALIZED) ? YES : NO;
4095 /***********************************************************************
4097 **********************************************************************/
4098 __private_extern__ void
4099 _class_setInitializing(Class cls_gen)
4101 struct class_t *cls = newcls(_class_getMeta(cls_gen));
4102 changeInfo(cls, RW_INITIALIZING, 0);
4106 /***********************************************************************
4107 * Locking: write-locks runtimeLock
4108 **********************************************************************/
4109 __private_extern__ void
4110 _class_setInitialized(Class cls_gen)
4113 struct class_t *metacls;
4114 struct class_t *cls;
4116 rwlock_write(&runtimeLock);
4117 metacls = newcls(_class_getMeta(cls_gen));
4118 cls = getNonMetaClass(metacls);
4120 // Update vtables (initially postponed pending +initialize completion)
4121 // Do cls first because root metacls is a subclass of root cls
4122 updateVtable(cls, YES);
4123 updateVtable(metacls, YES);
4125 rwlock_unlock_write(&runtimeLock);
4127 changeInfo(metacls, RW_INITIALIZED, RW_INITIALIZING);
4131 /***********************************************************************
4133 **********************************************************************/
4134 __private_extern__ BOOL
4135 _class_shouldGrowCache(Class cls)
4137 return YES; // fixme good or bad for memory use?
4141 /***********************************************************************
4143 **********************************************************************/
4144 __private_extern__ void
4145 _class_setGrowCache(Class cls, BOOL grow)
4147 // fixme good or bad for memory use?
4151 /***********************************************************************
4155 **********************************************************************/
4156 __private_extern__ BOOL
4157 _class_isLoadable(Class cls)
4159 assert(isRealized(newcls(cls)));
4160 return YES; // any class registered for +load is definitely loadable
4164 /***********************************************************************
4166 **********************************************************************/
4167 __private_extern__ BOOL
4168 _class_hasCxxStructorsNoSuper(Class cls)
4170 assert(isRealized(newcls(cls)));
4171 return (newcls(cls)->data->ro->flags & RO_HAS_CXX_STRUCTORS) ? YES : NO;
4175 /***********************************************************************
4177 **********************************************************************/
4178 __private_extern__ BOOL
4179 _class_shouldFinalizeOnMainThread(Class cls)
4181 assert(isRealized(newcls(cls)));
4182 return (newcls(cls)->data->flags & RW_FINALIZE_ON_MAIN_THREAD) ? YES : NO;
4186 /***********************************************************************
4188 **********************************************************************/
4189 __private_extern__ void
4190 _class_setFinalizeOnMainThread(Class cls)
4192 assert(isRealized(newcls(cls)));
4193 changeInfo(newcls(cls), RW_FINALIZE_ON_MAIN_THREAD, 0);
4197 /***********************************************************************
4198 * _class_instancesHaveAssociatedObjects
4199 * May manipulate unrealized future classes in the CF-bridged case.
4200 **********************************************************************/
4201 __private_extern__ BOOL
4202 _class_instancesHaveAssociatedObjects(Class cls_gen)
4204 class_t *cls = newcls(cls_gen);
4205 assert(isFuture(cls) || isRealized(cls));
4206 return (cls->data->flags & RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS) ? YES : NO;
4210 /***********************************************************************
4211 * _class_assertInstancesHaveAssociatedObjects
4212 * May manipulate unrealized future classes in the CF-bridged case.
4213 **********************************************************************/
4214 __private_extern__ void
4215 _class_assertInstancesHaveAssociatedObjects(Class cls_gen)
4217 class_t *cls = newcls(cls_gen);
4218 assert(isFuture(cls) || isRealized(cls));
4219 changeInfo(cls, RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS, 0);
4223 /***********************************************************************
4225 * fixme assert realized to get superclass remapping?
4226 **********************************************************************/
4227 __private_extern__ Class
4228 _class_getSuperclass(Class cls)
4230 return (Class)getSuperclass(newcls(cls));
4233 static struct class_t *
4234 getSuperclass(struct class_t *cls)
4236 if (!cls) return NULL;
4237 return cls->superclass;
4241 /***********************************************************************
4242 * class_getIvarLayout
4243 * Called by the garbage collector.
4244 * The class must be NULL or already realized.
4246 **********************************************************************/
4248 class_getIvarLayout(Class cls_gen)
4250 class_t *cls = newcls(cls_gen);
4251 if (cls) return (const char *)cls->data->ro->ivarLayout;
4256 /***********************************************************************
4257 * class_getWeakIvarLayout
4258 * Called by the garbage collector.
4259 * The class must be NULL or already realized.
4261 **********************************************************************/
4263 class_getWeakIvarLayout(Class cls_gen)
4265 class_t *cls = newcls(cls_gen);
4266 if (cls) return (const char *)cls->data->ro->weakIvarLayout;
4271 /***********************************************************************
4272 * class_setIvarLayout
4273 * Changes the class's GC scan layout.
4274 * NULL layout means no unscanned ivars
4275 * The class must be under construction.
4276 * fixme: sanity-check layout vs instance size?
4277 * fixme: sanity-check layout vs superclass?
4278 * Locking: acquires runtimeLock
4279 **********************************************************************/
4281 class_setIvarLayout(Class cls_gen, const char *layout)
4283 class_t *cls = newcls(cls_gen);
4286 rwlock_write(&runtimeLock);
4288 // Can only change layout of in-construction classes.
4289 // note: if modifications to post-construction classes were
4290 // allowed, there would be a race below (us vs. concurrent GC scan)
4291 if (!(cls->data->flags & RW_CONSTRUCTING)) {
4292 _objc_inform("*** Can't set ivar layout for already-registered "
4293 "class '%s'", getName(cls));
4294 rwlock_unlock_write(&runtimeLock);
4298 class_ro_t *ro_w = make_ro_writeable(cls->data);
4300 try_free(ro_w->ivarLayout);
4301 ro_w->ivarLayout = (unsigned char *)_strdup_internal(layout);
4303 rwlock_unlock_write(&runtimeLock);
4307 /***********************************************************************
4308 * class_setWeakIvarLayout
4309 * Changes the class's GC weak layout.
4310 * NULL layout means no weak ivars
4311 * The class must be under construction.
4312 * fixme: sanity-check layout vs instance size?
4313 * fixme: sanity-check layout vs superclass?
4314 * Locking: acquires runtimeLock
4315 **********************************************************************/
4317 class_setWeakIvarLayout(Class cls_gen, const char *layout)
4319 class_t *cls = newcls(cls_gen);
4322 rwlock_write(&runtimeLock);
4324 // Can only change layout of in-construction classes.
4325 // note: if modifications to post-construction classes were
4326 // allowed, there would be a race below (us vs. concurrent GC scan)
4327 if (!(cls->data->flags & RW_CONSTRUCTING)) {
4328 _objc_inform("*** Can't set weak ivar layout for already-registered "
4329 "class '%s'", getName(cls));
4330 rwlock_unlock_write(&runtimeLock);
4334 class_ro_t *ro_w = make_ro_writeable(cls->data);
4336 try_free(ro_w->weakIvarLayout);
4337 ro_w->weakIvarLayout = (unsigned char *)_strdup_internal(layout);
4339 rwlock_unlock_write(&runtimeLock);
4343 /***********************************************************************
4344 * _class_getVariable
4346 * Locking: read-locks runtimeLock
4347 **********************************************************************/
4348 __private_extern__ Ivar
4349 _class_getVariable(Class cls, const char *name)
4351 rwlock_read(&runtimeLock);
4353 for ( ; cls != Nil; cls = class_getSuperclass(cls)) {
4354 struct ivar_t *ivar = getIvar(newcls(cls), name);
4356 rwlock_unlock_read(&runtimeLock);
4361 rwlock_unlock_read(&runtimeLock);
4367 /***********************************************************************
4368 * class_conformsToProtocol
4370 * Locking: read-locks runtimeLock
4371 **********************************************************************/
4372 BOOL class_conformsToProtocol(Class cls_gen, Protocol *proto)
4374 Protocol **protocols;
4375 unsigned int count, i;
4378 if (!cls_gen) return NO;
4379 if (!proto) return NO;
4383 protocols = class_copyProtocolList(cls_gen, &count);
4385 for (i = 0; i < count; i++) {
4386 if (protocols[i] == proto ||
4387 protocol_conformsToProtocol(protocols[i], proto))
4394 if (protocols) free(protocols);
4400 /***********************************************************************
4403 * Locking: write-locks runtimeLock
4404 **********************************************************************/
4406 _class_addMethod(Class cls_gen, SEL name, IMP imp,
4407 const char *types, BOOL replace)
4409 struct class_t *cls = newcls(cls_gen);
4412 if (!types) types = "";
4414 rwlock_write(&runtimeLock);
4416 assert(isRealized(cls));
4419 if ((m = getMethodNoSuper_nolock(cls, name))) {
4422 result = _method_getImplementation(m);
4424 result = _method_setImplementation(cls, m, imp);
4428 method_list_t *newlist;
4429 newlist = _calloc_internal(sizeof(*newlist), 1);
4430 newlist->entsize_NEVER_USE = (uint32_t)sizeof(method_t) | fixed_up_method_list;
4432 newlist->first.name = name;
4433 newlist->first.types = strdup(types);
4434 if (name != (SEL)kIgnore) {
4435 newlist->first.imp = imp;
4437 newlist->first.imp = (IMP)&_objc_ignored_method;
4440 BOOL vtablesAffected;
4441 attachMethodLists(cls, &newlist, 1, NO, &vtablesAffected);
4443 if (vtablesAffected) flushVtables(cls);
4448 rwlock_unlock_write(&runtimeLock);
4455 class_addMethod(Class cls, SEL name, IMP imp, const char *types)
4457 if (!cls) return NO;
4459 IMP old = _class_addMethod(cls, name, imp, types, NO);
4460 return old ? NO : YES;
4465 class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
4467 if (!cls) return NULL;
4469 return _class_addMethod(cls, name, imp, types, YES);
4473 /***********************************************************************
4475 * Adds an ivar to a class.
4476 * Locking: acquires runtimeLock
4477 **********************************************************************/
4479 class_addIvar(Class cls_gen, const char *name, size_t size,
4480 uint8_t alignment, const char *type)
4482 struct class_t *cls = newcls(cls_gen);
4484 if (!cls) return NO;
4486 if (!type) type = "";
4487 if (name && 0 == strcmp(name, "")) name = NULL;
4489 rwlock_write(&runtimeLock);
4491 assert(isRealized(cls));
4493 // No class variables
4494 if (isMetaClass(cls)) {
4495 rwlock_unlock_write(&runtimeLock);
4499 // Can only add ivars to in-construction classes.
4500 if (!(cls->data->flags & RW_CONSTRUCTING)) {
4501 rwlock_unlock_write(&runtimeLock);
4505 // Check for existing ivar with this name, unless it's anonymous.
4506 // Check for too-big ivar.
4507 // fixme check for superclass ivar too?
4508 if ((name && getIvar(cls, name)) || size > UINT32_MAX) {
4509 rwlock_unlock_write(&runtimeLock);
4513 class_ro_t *ro_w = make_ro_writeable(cls->data);
4515 // fixme allocate less memory here
4517 ivar_list_t *oldlist, *newlist;
4518 if ((oldlist = (ivar_list_t *)cls->data->ro->ivars)) {
4519 size_t oldsize = ivar_list_size(oldlist);
4520 newlist = _calloc_internal(oldsize + oldlist->entsize, 1);
4521 memcpy(newlist, oldlist, oldsize);
4522 _free_internal(oldlist);
4524 newlist = _calloc_internal(sizeof(ivar_list_t), 1);
4525 newlist->entsize = (uint32_t)sizeof(ivar_t);
4528 uint32_t offset = instanceSize(cls);
4529 uint32_t alignMask = (1<<alignment)-1;
4530 offset = (offset + alignMask) & ~alignMask;
4532 ivar_t *ivar = ivar_list_nth(newlist, newlist->count++);
4533 ivar->offset = _malloc_internal(sizeof(*ivar->offset));
4534 *ivar->offset = offset;
4535 ivar->name = name ? _strdup_internal(name) : NULL;
4536 ivar->type = _strdup_internal(type);
4537 ivar->alignment = alignment;
4538 ivar->size = (uint32_t)size;
4540 ro_w->ivars = newlist;
4541 ro_w->instanceSize = (uint32_t)(offset + size);
4543 // Ivar layout updated in registerClass.
4545 rwlock_unlock_write(&runtimeLock);
4551 /***********************************************************************
4553 * Adds a protocol to a class.
4554 * Locking: acquires runtimeLock
4555 **********************************************************************/
4556 BOOL class_addProtocol(Class cls_gen, Protocol *protocol_gen)
4558 class_t *cls = newcls(cls_gen);
4559 protocol_t *protocol = newprotocol(protocol_gen);
4560 protocol_list_t *plist;
4561 protocol_list_t **plistp;
4563 if (!cls) return NO;
4564 if (class_conformsToProtocol(cls_gen, protocol_gen)) return NO;
4566 rwlock_write(&runtimeLock);
4568 assert(isRealized(cls));
4571 plist = _malloc_internal(sizeof(protocol_list_t) + sizeof(protocol_t *));
4573 plist->list[0] = (protocol_ref_t)protocol;
4575 unsigned int count = 0;
4576 for (plistp = cls->data->protocols; plistp && *plistp; plistp++) {
4580 cls->data->protocols =
4581 _realloc_internal(cls->data->protocols,
4582 (count+2) * sizeof(protocol_list_t *));
4583 cls->data->protocols[count] = plist;
4584 cls->data->protocols[count+1] = NULL;
4588 rwlock_unlock_write(&runtimeLock);
4594 /***********************************************************************
4596 * Look up a class by name, and realize it.
4597 * Locking: acquires runtimeLock
4598 **********************************************************************/
4599 __private_extern__ id
4600 look_up_class(const char *name,
4601 BOOL includeUnconnected __attribute__((unused)),
4602 BOOL includeClassHandler __attribute__((unused)))
4604 if (!name) return nil;
4606 rwlock_read(&runtimeLock);
4607 class_t *result = getClass(name);
4608 BOOL unrealized = result && !isRealized(result);
4609 rwlock_unlock_read(&runtimeLock);
4611 rwlock_write(&runtimeLock);
4612 realizeClass(result);
4613 rwlock_unlock_write(&runtimeLock);
4619 /***********************************************************************
4620 * objc_duplicateClass
4622 * Locking: acquires runtimeLock
4623 **********************************************************************/
4625 objc_duplicateClass(Class original_gen, const char *name,
4628 struct class_t *original = newcls(original_gen);
4629 struct class_t *duplicate;
4631 rwlock_write(&runtimeLock);
4633 assert(isRealized(original));
4634 assert(!isMetaClass(original));
4636 duplicate = (struct class_t *)
4637 _calloc_class(instanceSize(original->isa) + extraBytes);
4638 if (instanceSize(original->isa) < sizeof(class_t)) {
4639 _objc_inform("busted! %s\n", original->data->ro->name);
4643 duplicate->isa = original->isa;
4644 duplicate->superclass = original->superclass;
4645 duplicate->cache = (Cache)&_objc_empty_cache;
4646 duplicate->vtable = _objc_empty_vtable;
4648 duplicate->data = _calloc_internal(sizeof(*original->data), 1);
4649 duplicate->data->flags = (original->data->flags | RW_COPIED_RO) & ~RW_SPECIALIZED_VTABLE;
4650 duplicate->data->version = original->data->version;
4651 duplicate->data->firstSubclass = NULL;
4652 duplicate->data->nextSiblingClass = NULL;
4654 duplicate->data->ro =
4655 _memdup_internal(original->data->ro, sizeof(*original->data->ro));
4656 *(char **)&duplicate->data->ro->name = _strdup_internal(name);
4658 if (original->data->methods) {
4659 duplicate->data->methods =
4660 _memdup_internal(original->data->methods,
4661 malloc_size(original->data->methods));
4662 method_list_t **mlistp = duplicate->data->methods;
4663 for (mlistp = duplicate->data->methods; *mlistp; mlistp++) {
4664 *mlistp = _memdup_internal(*mlistp, method_list_size(*mlistp));
4668 // fixme dies when categories are added to the base
4669 duplicate->data->properties = original->data->properties;
4670 duplicate->data->protocols = original->data->protocols;
4672 if (duplicate->superclass) {
4673 addSubclass(duplicate->superclass, duplicate);
4676 // Don't methodize class - construction above is correct
4678 addNamedClass(duplicate, duplicate->data->ro->name);
4679 addRealizedClass(duplicate);
4680 // no: duplicate->isa == original->isa
4681 // addRealizedMetaclass(duplicate->isa);
4683 if (PrintConnecting) {
4684 _objc_inform("CLASS: realizing class '%s' (duplicate of %s) %p %p",
4685 name, original->data->ro->name,
4686 duplicate, duplicate->data->ro);
4689 rwlock_unlock_write(&runtimeLock);
4691 return (Class)duplicate;
4694 /***********************************************************************
4695 * objc_initializeClassPair
4696 * Locking: runtimeLock must be write-locked by the caller
4697 **********************************************************************/
4698 static void objc_initializeClassPair_internal(Class superclass_gen, const char *name, Class cls_gen, Class meta_gen)
4700 rwlock_assert_writing(&runtimeLock);
4702 class_t *superclass = newcls(superclass_gen);
4703 class_t *cls = newcls(cls_gen);
4704 class_t *meta = newcls(meta_gen);
4705 class_ro_t *cls_ro_w, *meta_ro_w;
4707 cls->data = _calloc_internal(sizeof(class_rw_t), 1);
4708 meta->data = _calloc_internal(sizeof(class_rw_t), 1);
4709 cls_ro_w = _calloc_internal(sizeof(class_ro_t), 1);
4710 meta_ro_w = _calloc_internal(sizeof(class_ro_t), 1);
4711 cls->data->ro = cls_ro_w;
4712 meta->data->ro = meta_ro_w;
4715 cls->cache = (Cache)&_objc_empty_cache;
4716 meta->cache = (Cache)&_objc_empty_cache;
4717 cls->vtable = _objc_empty_vtable;
4718 meta->vtable = _objc_empty_vtable;
4720 cls->data->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED;
4721 meta->data->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED;
4722 cls->data->version = 0;
4723 meta->data->version = 7;
4725 cls_ro_w->flags = 0;
4726 meta_ro_w->flags = RO_META;
4728 cls_ro_w->flags |= RO_ROOT;
4729 meta_ro_w->flags |= RO_ROOT;
4732 cls_ro_w->instanceStart = instanceSize(superclass);
4733 meta_ro_w->instanceStart = instanceSize(superclass->isa);
4734 cls_ro_w->instanceSize = cls_ro_w->instanceStart;
4735 meta_ro_w->instanceSize = meta_ro_w->instanceStart;
4737 cls_ro_w->instanceStart = 0;
4738 meta_ro_w->instanceStart = (uint32_t)sizeof(class_t);
4739 cls_ro_w->instanceSize = (uint32_t)sizeof(id); // just an isa
4740 meta_ro_w->instanceSize = meta_ro_w->instanceStart;
4743 cls_ro_w->name = _strdup_internal(name);
4744 meta_ro_w->name = _strdup_internal(name);
4746 // Connect to superclasses and metaclasses
4749 meta->isa = superclass->isa->isa;
4750 cls->superclass = superclass;
4751 meta->superclass = superclass->isa;
4752 addSubclass(superclass, cls);
4753 addSubclass(superclass->isa, meta);
4756 cls->superclass = Nil;
4757 meta->superclass = cls;
4758 addSubclass(cls, meta);
4762 /***********************************************************************
4763 * objc_initializeClassPair
4764 **********************************************************************/
4765 Class objc_initializeClassPair(Class superclass_gen, const char *name, Class cls_gen, Class meta_gen)
4767 class_t *superclass = newcls(superclass_gen);
4769 rwlock_write(&runtimeLock);
4772 // Common superclass integrity checks with objc_allocateClassPair
4774 if (getClass(name)) {
4775 rwlock_unlock_write(&runtimeLock);
4778 // fixme reserve class against simmultaneous allocation
4780 if (superclass) assert(isRealized(superclass));
4782 if (superclass && superclass->data->flags & RW_CONSTRUCTING) {
4783 // Can't make subclass of an in-construction class
4784 rwlock_unlock_write(&runtimeLock);
4789 // just initialize what was supplied
4790 objc_initializeClassPair_internal(superclass_gen, name, cls_gen, meta_gen);
4792 rwlock_unlock_write(&runtimeLock);
4796 /***********************************************************************
4797 * objc_allocateClassPair
4799 * Locking: acquires runtimeLock
4800 **********************************************************************/
4801 Class objc_allocateClassPair(Class superclass_gen, const char *name,
4804 class_t *superclass = newcls(superclass_gen);
4807 rwlock_write(&runtimeLock);
4810 // Common superclass integrity checks with objc_initializeClassPair
4812 if (getClass(name)) {
4813 rwlock_unlock_write(&runtimeLock);
4816 // fixme reserve class against simmultaneous allocation
4818 if (superclass) assert(isRealized(superclass));
4820 if (superclass && superclass->data->flags & RW_CONSTRUCTING) {
4821 // Can't make subclass of an in-construction class
4822 rwlock_unlock_write(&runtimeLock);
4828 // Allocate new classes.
4830 cls = _calloc_class(instanceSize(superclass->isa) + extraBytes);
4831 meta = _calloc_class(instanceSize(superclass->isa->isa) + extraBytes);
4833 cls = _calloc_class(sizeof(class_t) + extraBytes);
4834 meta = _calloc_class(sizeof(class_t) + extraBytes);
4838 objc_initializeClassPair_internal(superclass_gen, name, cls, meta);
4840 rwlock_unlock_write(&runtimeLock);
4846 /***********************************************************************
4847 * objc_registerClassPair
4849 * Locking: acquires runtimeLock
4850 **********************************************************************/
4851 void objc_registerClassPair(Class cls_gen)
4853 class_t *cls = newcls(cls_gen);
4855 rwlock_write(&runtimeLock);
4857 if ((cls->data->flags & RW_CONSTRUCTED) ||
4858 (cls->isa->data->flags & RW_CONSTRUCTED))
4860 _objc_inform("objc_registerClassPair: class '%s' was already "
4861 "registered!", cls->data->ro->name);
4862 rwlock_unlock_write(&runtimeLock);
4866 if (!(cls->data->flags & RW_CONSTRUCTING) ||
4867 !(cls->isa->data->flags & RW_CONSTRUCTING))
4869 _objc_inform("objc_registerClassPair: class '%s' was not "
4870 "allocated with objc_allocateClassPair!",
4871 cls->data->ro->name);
4872 rwlock_unlock_write(&runtimeLock);
4876 // Build ivar layouts
4878 struct class_t *supercls = getSuperclass(cls);
4879 class_ro_t *ro_w = (class_ro_t *)cls->data->ro;
4881 if (ro_w->ivarLayout) {
4882 // Class builder already called class_setIvarLayout.
4884 else if (!supercls) {
4885 // Root class. Scan conservatively (should be isa ivar only).
4886 // ivar_layout is already NULL.
4888 else if (ro_w->ivars == NULL) {
4889 // No local ivars. Use superclass's layouts.
4890 ro_w->ivarLayout = (unsigned char *)
4891 _strdup_internal((char *)supercls->data->ro->ivarLayout);
4894 // Has local ivars. Build layouts based on superclass.
4895 layout_bitmap bitmap =
4896 layout_bitmap_create(supercls->data->ro->ivarLayout,
4897 instanceSize(supercls),
4898 instanceSize(cls), NO);
4900 for (i = 0; i < ro_w->ivars->count; i++) {
4901 ivar_t *ivar = ivar_list_nth(ro_w->ivars, i);
4902 if (!ivar->offset) continue; // anonymous bitfield
4904 layout_bitmap_set_ivar(bitmap, ivar->type, *ivar->offset);
4906 ro_w->ivarLayout = layout_string_create(bitmap);
4907 layout_bitmap_free(bitmap);
4910 if (ro_w->weakIvarLayout) {
4911 // Class builder already called class_setWeakIvarLayout.
4913 else if (!supercls) {
4914 // Root class. No weak ivars (should be isa ivar only).
4915 // weak_ivar_layout is already NULL.
4917 else if (ro_w->ivars == NULL) {
4918 // No local ivars. Use superclass's layout.
4919 ro_w->weakIvarLayout = (unsigned char *)
4920 _strdup_internal((char *)supercls->data->ro->weakIvarLayout);
4923 // Has local ivars. Build layout based on superclass.
4924 // No way to add weak ivars yet.
4925 ro_w->weakIvarLayout = (unsigned char *)
4926 _strdup_internal((char *)supercls->data->ro->weakIvarLayout);
4930 // Clear "under construction" bit, set "done constructing" bit
4931 cls->data->flags &= ~RW_CONSTRUCTING;
4932 cls->isa->data->flags &= ~RW_CONSTRUCTING;
4933 cls->data->flags |= RW_CONSTRUCTED;
4934 cls->isa->data->flags |= RW_CONSTRUCTED;
4936 // Add to realized and uninitialized classes
4937 addNamedClass(cls, cls->data->ro->name);
4938 addRealizedClass(cls);
4939 addRealizedMetaclass(cls->isa);
4940 addUninitializedClass(cls, cls->isa);
4942 rwlock_unlock_write(&runtimeLock);
4946 static void unload_class(class_t *cls, BOOL isMeta)
4948 // Detach class from various lists
4950 // categories not yet attached to this class
4951 category_list *cats;
4952 cats = unattachedCategoriesForClass(cls);
4953 if (cats) free(cats);
4955 // class tables and +load queue
4957 removeNamedClass(cls, getName(cls));
4958 removeRealizedClass(cls);
4959 removeUninitializedClass(cls);
4961 removeRealizedMetaclass(cls);
4964 // superclass's subclass list
4965 if (isRealized(cls)) {
4966 class_t *supercls = getSuperclass(cls);
4967 if (supercls) removeSubclass(supercls, cls);
4971 // Dispose the class's own data structures
4973 if (isRealized(cls)) {
4976 // Dereferences the cache contents; do this before freeing methods
4977 if (cls->cache != (Cache)&_objc_empty_cache) _cache_free(cls->cache);
4979 if (cls->data->methods) {
4980 method_list_t **mlistp;
4981 for (mlistp = cls->data->methods; *mlistp; mlistp++) {
4982 for (i = 0; i < (**mlistp).count; i++) {
4983 method_t *m = method_list_nth(*mlistp, i);
4988 try_free(cls->data->methods);
4991 const ivar_list_t *ilist = cls->data->ro->ivars;
4993 for (i = 0; i < ilist->count; i++) {
4994 const ivar_t *ivar = ivar_list_nth(ilist, i);
4995 try_free(ivar->offset);
4996 try_free(ivar->name);
4997 try_free(ivar->type);
5002 protocol_list_t **plistp = cls->data->protocols;
5003 for (plistp = cls->data->protocols; plistp && *plistp; plistp++) {
5006 try_free(cls->data->protocols);
5011 if (cls->vtable != _objc_empty_vtable &&
5012 cls->data->flags & RW_SPECIALIZED_VTABLE) try_free(cls->vtable);
5013 try_free(cls->data->ro->ivarLayout);
5014 try_free(cls->data->ro->weakIvarLayout);
5015 try_free(cls->data->ro->name);
5016 try_free(cls->data->ro);
5017 try_free(cls->data);
5022 void objc_disposeClassPair(Class cls_gen)
5024 class_t *cls = newcls(cls_gen);
5026 rwlock_write(&runtimeLock);
5028 if (!(cls->data->flags & (RW_CONSTRUCTED|RW_CONSTRUCTING)) ||
5029 !(cls->isa->data->flags & (RW_CONSTRUCTED|RW_CONSTRUCTING)))
5031 // class not allocated with objc_allocateClassPair
5032 // disposing still-unregistered class is OK!
5033 _objc_inform("objc_disposeClassPair: class '%s' was not "
5034 "allocated with objc_allocateClassPair!",
5035 cls->data->ro->name);
5036 rwlock_unlock_write(&runtimeLock);
5040 if (isMetaClass(cls)) {
5041 _objc_inform("objc_disposeClassPair: class '%s' is a metaclass, "
5042 "not a class!", cls->data->ro->name);
5043 rwlock_unlock_write(&runtimeLock);
5047 // Shouldn't have any live subclasses.
5048 if (cls->data->firstSubclass) {
5049 _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, "
5050 "including '%s'!", cls->data->ro->name,
5051 getName(cls->data->firstSubclass));
5053 if (cls->isa->data->firstSubclass) {
5054 _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, "
5055 "including '%s'!", cls->data->ro->name,
5056 getName(cls->isa->data->firstSubclass));
5059 // don't remove_class_from_loadable_list()
5060 // - it's not there and we don't have the lock
5061 unload_class(cls->isa, YES);
5062 unload_class(cls, NO);
5064 rwlock_unlock_write(&runtimeLock);
5069 /***********************************************************************
5070 * class_createInstanceFromZone
5073 **********************************************************************/
5075 class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
5077 if (cls) assert(isRealized(newcls(cls)));
5078 return _internal_class_createInstanceFromZone(cls, extraBytes, zone);
5082 /***********************************************************************
5083 * class_createInstance
5086 **********************************************************************/
5088 class_createInstance(Class cls, size_t extraBytes)
5090 return class_createInstanceFromZone(cls, extraBytes, NULL);
5094 /***********************************************************************
5095 * object_copyFromZone
5098 **********************************************************************/
5100 object_copyFromZone(id oldObj, size_t extraBytes, void *zone)
5105 if (!oldObj) return nil;
5107 size = _class_getInstanceSize(oldObj->isa) + extraBytes;
5110 obj = (id) auto_zone_allocate_object(gc_zone, size,
5111 AUTO_OBJECT_SCANNED, 0, 1);
5115 obj = malloc_zone_calloc(zone, size, 1);
5117 obj = (id) calloc(1, size);
5119 if (!obj) return nil;
5121 // fixme this doesn't handle C++ ivars correctly (#4619414)
5122 objc_memmove_collectable(obj, oldObj, size);
5125 if (UseGC) gc_fixup_weakreferences(obj, oldObj);
5132 /***********************************************************************
5136 **********************************************************************/
5138 object_copy(id oldObj, size_t extraBytes)
5140 return object_copyFromZone(oldObj, extraBytes, malloc_default_zone());
5144 /***********************************************************************
5148 **********************************************************************/
5150 object_dispose(id obj)
5152 return _internal_object_dispose(obj);
5156 /***********************************************************************
5157 * _objc_getFreedObjectClass
5160 **********************************************************************/
5161 Class _objc_getFreedObjectClass (void)
5168 extern id objc_msgSend_fixup(id, SEL, ...);
5169 extern id objc_msgSend_fixedup(id, SEL, ...);
5170 extern id objc_msgSendSuper2_fixup(id, SEL, ...);
5171 extern id objc_msgSendSuper2_fixedup(id, SEL, ...);
5172 extern id objc_msgSend_stret_fixup(id, SEL, ...);
5173 extern id objc_msgSend_stret_fixedup(id, SEL, ...);
5174 extern id objc_msgSendSuper2_stret_fixup(id, SEL, ...);
5175 extern id objc_msgSendSuper2_stret_fixedup(id, SEL, ...);
5176 #if defined(__i386__) || defined(__x86_64__)
5177 extern id objc_msgSend_fpret_fixup(id, SEL, ...);
5178 extern id objc_msgSend_fpret_fixedup(id, SEL, ...);
5180 #if defined(__x86_64__)
5181 extern id objc_msgSend_fp2ret_fixup(id, SEL, ...);
5182 extern id objc_msgSend_fp2ret_fixedup(id, SEL, ...);
5185 /***********************************************************************
5186 * _objc_fixupMessageRef
5187 * Fixes up message ref *msg.
5188 * obj is the receiver. supr is NULL for non-super messages
5189 * Locking: acquires runtimeLock
5190 **********************************************************************/
5191 __private_extern__ IMP
5192 _objc_fixupMessageRef(id obj, struct objc_super2 *supr, message_ref *msg)
5197 rwlock_assert_unlocked(&runtimeLock);
5200 // normal message - search obj->isa for the method implementation
5201 isa = (class_t *)obj->isa;
5203 if (!isRealized(isa)) {
5204 // obj is a class object, isa is its metaclass
5206 rwlock_write(&runtimeLock);
5207 cls = realizeClass((class_t *)obj);
5208 rwlock_unlock_write(&runtimeLock);
5210 // shouldn't have instances of unrealized classes!
5211 assert(isMetaClass(isa));
5212 // shouldn't be relocating classes here!
5213 assert(cls == (class_t *)obj);
5217 // this is objc_msgSend_super, and supr->current_class->superclass
5218 // is the class to search for the method implementation
5219 assert(isRealized((class_t *)supr->current_class));
5220 isa = getSuperclass((class_t *)supr->current_class);
5223 msg->sel = sel_registerName((const char *)msg->sel);
5227 if (msg->imp == (IMP)&objc_msgSend_fixup &&
5228 (vtableIndex = vtable_getIndex(msg->sel)) >= 0)
5231 msg->imp = vtableTrampolines[vtableIndex];
5232 imp = isa->vtable[vtableIndex];
5237 // ordinary dispatch
5238 imp = lookUpMethod((Class)isa, msg->sel, YES/*initialize*/, YES/*cache*/);
5240 if (msg->imp == (IMP)&objc_msgSend_fixup) {
5241 msg->imp = (IMP)&objc_msgSend_fixedup;
5243 else if (msg->imp == (IMP)&objc_msgSendSuper2_fixup) {
5244 msg->imp = (IMP)&objc_msgSendSuper2_fixedup;
5246 else if (msg->imp == (IMP)&objc_msgSend_stret_fixup) {
5247 msg->imp = (IMP)&objc_msgSend_stret_fixedup;
5249 else if (msg->imp == (IMP)&objc_msgSendSuper2_stret_fixup) {
5250 msg->imp = (IMP)&objc_msgSendSuper2_stret_fixedup;
5252 #if defined(__i386__) || defined(__x86_64__)
5253 else if (msg->imp == (IMP)&objc_msgSend_fpret_fixup) {
5254 msg->imp = (IMP)&objc_msgSend_fpret_fixedup;
5257 #if defined(__x86_64__)
5258 else if (msg->imp == (IMP)&objc_msgSend_fp2ret_fixup) {
5259 msg->imp = (IMP)&objc_msgSend_fp2ret_fixedup;
5263 // The ref may already have been fixed up, either by another thread
5264 // or by +initialize via lookUpMethod above.
5275 #warning fixme delete after #4586306
5276 Class class_poseAs(Class imposter, Class original)
5278 _objc_fatal("Don't call class_poseAs.");
5283 static class_t *setSuperclass(class_t *cls, class_t *newSuper)
5287 rwlock_assert_writing(&runtimeLock);
5289 oldSuper = cls->superclass;
5290 removeSubclass(oldSuper, cls);
5291 removeSubclass(oldSuper->isa, cls->isa);
5293 cls->superclass = newSuper;
5294 cls->isa->superclass = newSuper->isa;
5295 addSubclass(newSuper, cls);
5296 addSubclass(newSuper->isa, cls->isa);
5299 flushCaches(cls->isa);
5301 flushVtables(cls->isa);
5307 Class class_setSuperclass(Class cls_gen, Class newSuper_gen)
5309 class_t *cls = newcls(cls_gen);
5310 class_t *newSuper = newcls(newSuper_gen);
5313 rwlock_write(&runtimeLock);
5314 oldSuper = setSuperclass(cls, newSuper);
5315 rwlock_unlock_write(&runtimeLock);
5317 return (Class)oldSuper;