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