]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-runtime-new.m
ced3cc1a253481dc8b4555d7d89abaac7c41fddd
[apple/objc4.git] / runtime / objc-runtime-new.m
1 /*
2 * Copyright (c) 2005-2007 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /***********************************************************************
25 * objc-runtime-new.m
26 * Support for new-ABI classes and images.
27 **********************************************************************/
28
29 #if __OBJC2__
30
31 #include "objc-private.h"
32 #include "objc-runtime-new.h"
33 #include "objc-loadmethod.h"
34 #include "objc-rtp.h"
35 #include "maptable.h"
36 #include <assert.h>
37 #include <mach-o/dyld.h>
38 #include <mach-o/ldsyms.h>
39
40 #define newcls(cls) ((struct class_t *)cls)
41 #define newcat(cat) ((struct category_t *)cat)
42 #define newmethod(meth) ((struct method_t *)meth)
43 #define newivar(ivar) ((struct ivar_t *)ivar)
44 #define newcategory(cat) ((struct category_t *)cat)
45 #define newprotocol(p) ((struct protocol_t *)p)
46
47 static const char *getName(struct class_t *cls);
48 static uint32_t instanceSize(struct class_t *cls);
49 static BOOL isMetaClass(struct class_t *cls);
50 static struct class_t *getSuperclass(struct class_t *cls);
51 static void unload_class(class_t *cls);
52 static class_t *setSuperclass(class_t *cls, class_t *newSuper);
53 static class_t *realizeClass(class_t *cls);
54
55 static OBJC_DECLARE_LOCK (runtimeLock);
56 // fixme use more fine-grained locks
57
58
59 typedef struct {
60 uint32_t count;
61 category_t *list[0]; // variable-size
62 } category_list;
63
64 typedef struct chained_method_list {
65 struct chained_method_list *next;
66 uint32_t count;
67 method_t list[0]; // variable-size
68 } chained_method_list;
69
70 static size_t chained_mlist_size(const chained_method_list *mlist)
71 {
72 return sizeof(chained_method_list) + mlist->count * sizeof(method_t);
73 }
74
75 // fixme don't chain property lists
76 typedef struct chained_property_list {
77 struct chained_property_list *next;
78 uint32_t count;
79 struct objc_property list[0]; // variable-size
80 } chained_property_list;
81
82 /*
83 static size_t chained_property_list_size(const chained_property_list *plist)
84 {
85 return sizeof(chained_property_list) +
86 plist->count * sizeof(struct objc_property);
87 }
88
89 static size_t protocol_list_size(const protocol_list_t *plist)
90 {
91 return sizeof(protocol_list_t) + plist->count * sizeof(protocol_t *);
92 }
93 */
94
95 static size_t ivar_list_size(const ivar_list_t *ilist)
96 {
97 return sizeof(ivar_list_t) + (ilist->count-1) * ilist->entsize;
98 }
99
100 static method_t *method_list_nth(const method_list_t *mlist, uint32_t i)
101 {
102 return (method_t *)(i*mlist->entsize + (char *)&mlist->first);
103 }
104
105 static ivar_t *ivar_list_nth(const ivar_list_t *ilist, uint32_t i)
106 {
107 return (ivar_t *)(i*ilist->entsize + (char *)&ilist->first);
108 }
109
110
111 static void try_free(const void *p)
112 {
113 if (p && malloc_size(p)) free((void *)p);
114 }
115
116
117 /***********************************************************************
118 * make_ro_writeable
119 * Reallocates rw->ro if necessary to make it writeable.
120 * Locking: runtimeLock must be held by the caller.
121 **********************************************************************/
122 static class_ro_t *make_ro_writeable(class_rw_t *rw)
123 {
124 OBJC_CHECK_LOCKED(&runtimeLock);
125
126 if (rw->flags & RW_COPIED_RO) {
127 // already writeable, do nothing
128 } else {
129 class_ro_t *ro = _memdup_internal(rw->ro, sizeof(*rw->ro));
130 rw->ro = ro;
131 rw->flags |= RW_COPIED_RO;
132 }
133 return (class_ro_t *)rw->ro;
134 }
135
136
137 /***********************************************************************
138 * unattachedCategories
139 * Returns the class => categories map of unattached categories.
140 * Locking: runtimeLock must be held by the caller.
141 **********************************************************************/
142 static NXMapTable *unattachedCategories(void)
143 {
144 OBJC_CHECK_LOCKED(&runtimeLock);
145
146 static NXMapTable *category_map = NULL;
147
148 if (category_map) return category_map;
149
150 // fixme initial map size
151 category_map = NXCreateMapTableFromZone(NXPtrValueMapPrototype, 16,
152 _objc_internal_zone());
153
154 return category_map;
155 }
156
157
158 /***********************************************************************
159 * addUnattachedCategoryForClass
160 * Records an unattached category.
161 * Locking: runtimeLock must be held by the caller.
162 **********************************************************************/
163 static void addUnattachedCategoryForClass(category_t *cat, class_t *cls)
164 {
165 OBJC_CHECK_LOCKED(&runtimeLock);
166
167 // DO NOT use cat->cls!
168 // cls may be cat->cls->isa, or cat->cls may have been remapped.
169 NXMapTable *cats = unattachedCategories();
170 category_list *list;
171
172 list = NXMapGet(cats, cls);
173 if (!list) {
174 list = _calloc_internal(sizeof(*list) + sizeof(category_t *), 1);
175 } else {
176 list = _realloc_internal(list, sizeof(*list) + sizeof(category_t *) * (list->count + 1));
177 }
178 list->list[list->count++] = cat;
179 NXMapInsert(cats, cls, list);
180 }
181
182
183 /***********************************************************************
184 * removeUnattachedCategoryForClass
185 * Removes an unattached category.
186 * Locking: runtimeLock must be held by the caller.
187 **********************************************************************/
188 static void removeUnattachedCategoryForClass(category_t *cat, class_t *cls)
189 {
190 OBJC_CHECK_LOCKED(&runtimeLock);
191
192 // DO NOT use cat->cls!
193 // cls may be cat->cls->isa, or cat->cls may have been remapped.
194 NXMapTable *cats = unattachedCategories();
195 category_list *list;
196
197 list = NXMapGet(cats, cls);
198 if (!list) return;
199
200 uint32_t i;
201 for (i = 0; i < list->count; i++) {
202 if (list->list[i] == cat) {
203 // shift entries to preserve list order
204 memmove(&list->list[i], &list->list[i+1],
205 (list->count-i-1) * sizeof(category_t *));
206 list->count--;
207 return;
208 }
209 }
210 }
211
212
213 /***********************************************************************
214 * unattachedCategoriesForClass
215 * Returns the list of unattached categories for a class, and
216 * deletes them from the list.
217 * The result must be freed by the caller.
218 * Locking: runtimeLock must be held by the caller.
219 **********************************************************************/
220 static category_list *unattachedCategoriesForClass(class_t *cls)
221 {
222 OBJC_CHECK_LOCKED(&runtimeLock);
223 return NXMapRemove(unattachedCategories(), cls);
224 }
225
226
227 /***********************************************************************
228 * isRealized
229 * Returns YES if class cls has been realized.
230 * Locking: To prevent concurrent realization, hold runtimeLock.
231 **********************************************************************/
232 static BOOL isRealized(class_t *cls)
233 {
234 return (cls->data->flags & RW_REALIZED) ? YES : NO;
235 }
236
237
238 /***********************************************************************
239 * isMethodized.
240 * Returns YES if class cls has ever been methodized.
241 * Note that its method lists may still be out of date.
242 * Locking: To prevent concurrent methodization, hold runtimeLock.
243 **********************************************************************/
244 static BOOL isMethodized(class_t *cls)
245 {
246 if (!isRealized(cls)) return NO;
247 return (cls->data->flags & RW_METHODIZED) ? YES : NO;
248 }
249
250 static chained_method_list *
251 buildMethodList(const method_list_t *mlist, category_list *cats, BOOL isMeta)
252 {
253 // Do NOT use cat->cls! It may have been remapped.
254 chained_method_list *newlist;
255 uint32_t count = 0;
256 uint32_t m, c;
257
258 // Count methods in all lists.
259 if (mlist) count = mlist->count;
260 if (cats) {
261 for (c = 0; c < cats->count; c++) {
262 if (isMeta && cats->list[c]->classMethods) {
263 count += cats->list[c]->classMethods->count;
264 }
265 else if (!isMeta && cats->list[c]->instanceMethods) {
266 count += cats->list[c]->instanceMethods->count;
267 }
268 }
269 }
270
271 // Allocate new list.
272 newlist = _malloc_internal(sizeof(*newlist) + count * sizeof(method_t));
273 newlist->count = 0;
274 newlist->next = NULL;
275
276 // Copy methods; newest categories first, then ordinary methods
277 if (cats) {
278 c = cats->count;
279 while (c--) {
280 method_list_t *cmlist;
281 if (isMeta) {
282 cmlist = cats->list[c]->classMethods;
283 } else {
284 cmlist = cats->list[c]->instanceMethods;
285 }
286 if (cmlist) {
287 for (m = 0; m < cmlist->count; m++) {
288 newlist->list[newlist->count++] =
289 *method_list_nth(cmlist, m);
290 }
291 }
292 }
293 }
294 if (mlist) {
295 for (m = 0; m < mlist->count; m++) {
296 newlist->list[newlist->count++] = *method_list_nth(mlist, m);
297 }
298 }
299
300 assert(newlist->count == count);
301 for (m = 0; m < newlist->count; m++) {
302 newlist->list[m].name =
303 sel_registerName((const char *)newlist->list[m].name);
304 if (newlist->list[m].name == (SEL)kRTAddress_ignoredSelector) {
305 newlist->list[m].imp = (IMP)&_objc_ignored_method;
306 }
307 }
308
309 return newlist;
310 }
311
312
313 static chained_property_list *
314 buildPropertyList(const struct objc_property_list *plist, category_list *cats, BOOL isMeta)
315 {
316 // Do NOT use cat->cls! It may have been remapped.
317 chained_property_list *newlist;
318 uint32_t count = 0;
319 uint32_t p, c;
320
321 // Count properties in all lists.
322 if (plist) count = plist->count;
323 if (cats) {
324 for (c = 0; c < cats->count; c++) {
325 /*
326 if (isMeta && cats->list[c]->classProperties) {
327 count += cats->list[c]->classProperties->count;
328 }
329 else*/
330 if (!isMeta && cats->list[c]->instanceProperties) {
331 count += cats->list[c]->instanceProperties->count;
332 }
333 }
334 }
335
336 if (count == 0) return NULL;
337
338 // Allocate new list.
339 newlist = _malloc_internal(sizeof(*newlist) + count * sizeof(struct objc_property));
340 newlist->count = 0;
341 newlist->next = NULL;
342
343 // Copy properties; newest categories first, then ordinary properties
344 if (cats) {
345 c = cats->count;
346 while (c--) {
347 struct objc_property_list *cplist;
348 /*
349 if (isMeta) {
350 cplist = cats->list[c]->classProperties;
351 } else */
352 {
353 cplist = cats->list[c]->instanceProperties;
354 }
355 if (cplist) {
356 for (p = 0; p < cplist->count; p++) {
357 newlist->list[newlist->count++] =
358 *property_list_nth(cplist, p);
359 }
360 }
361 }
362 }
363 if (plist) {
364 for (p = 0; p < plist->count; p++) {
365 newlist->list[newlist->count++] = *property_list_nth(plist, p);
366 }
367 }
368
369 assert(newlist->count == count);
370
371 return newlist;
372 }
373
374
375 static protocol_list_t **
376 buildProtocolList(category_list *cats, struct protocol_list_t *base,
377 struct protocol_list_t **protos)
378 {
379 // Do NOT use cat->cls! It may have been remapped.
380 struct protocol_list_t **p, **newp;
381 struct protocol_list_t **newprotos;
382 int count = 0;
383 int i;
384
385 // count protocol list in base
386 if (base) count++;
387
388 // count protocol lists in cats
389 if (cats) for (i = 0; i < cats->count; i++) {
390 category_t *cat = cats->list[i];
391 if (cat->protocols) count++;
392 }
393
394 // no base or category protocols? return existing protocols unchanged
395 if (count == 0) return protos;
396
397 // count protocol lists in protos
398 for (p = protos; p && *p; p++) {
399 count++;
400 }
401
402 if (count == 0) return NULL;
403
404 newprotos = (struct protocol_list_t **)
405 _malloc_internal((count+1) * sizeof(struct protocol_list_t *));
406 newp = newprotos;
407
408 if (base) {
409 *newp++ = base;
410 }
411
412 for (p = protos; p && *p; p++) {
413 *newp++ = *p;
414 }
415
416 if (cats) for (i = 0; i < cats->count; i++) {
417 category_t *cat = cats->list[i];
418 if (cat->protocols) {
419 *newp++ = cat->protocols;
420 }
421 }
422
423 *newp = NULL;
424
425 return newprotos;
426 }
427
428
429 /***********************************************************************
430 * methodizeClass
431 * Fixes up cls's method list, protocol list, and property list.
432 * Attaches any outstanding categories.
433 * Locking: runtimeLock must be held by the caller
434 **********************************************************************/
435 static void methodizeClass(struct class_t *cls)
436 {
437 OBJC_CHECK_LOCKED(&runtimeLock);
438
439 category_list *cats;
440
441 if (!cls) return;
442 assert(isRealized(cls));
443
444 if (!(cls->data->flags & RW_METHODIZED)) {
445 // Methodizing for the first time
446 if (PrintConnecting) {
447 _objc_inform("CLASS: methodizing class '%s' %s",
448 getName(cls),
449 isMetaClass(cls) ? "(meta)" : "");
450 }
451
452 // Build method and protocol and property lists.
453 // Include methods and protocols and properties from categories, if any
454 // Do NOT use cat->cls! It may have been remapped.
455 cats = unattachedCategoriesForClass(cls);
456 if (cats || cls->data->ro->baseMethods) {
457 cls->data->methods =
458 buildMethodList(cls->data->ro->baseMethods, cats,
459 isMetaClass(cls));
460 }
461
462 if (cats || cls->data->ro->baseProperties) {
463 cls->data->properties =
464 buildPropertyList(cls->data->ro->baseProperties, cats,
465 isMetaClass(cls));
466 }
467
468 if (cats || cls->data->ro->baseProtocols) {
469 cls->data->protocols =
470 buildProtocolList(cats, cls->data->ro->baseProtocols, NULL);
471 }
472
473 if (PrintConnecting) {
474 uint32_t i;
475 if (cats) for (i = 0; i < cats->count; i++) {
476 _objc_inform("CLASS: attached category %c%s(%s)",
477 isMetaClass(cls) ? '+' : '-',
478 getName(cls), cats->list[i]->name);
479 }
480 }
481
482 if (cats) _free_internal(cats);
483
484 cls->data->flags |= RW_METHODIZED;
485 }
486 else {
487 // Re-methodizing: check for more categories
488 if ((cats = unattachedCategoriesForClass(cls))) {
489 chained_method_list *newlist;
490 chained_property_list *newproperties;
491 struct protocol_list_t **newprotos;
492
493 if (PrintConnecting) {
494 _objc_inform("CLASS: attaching categories to class '%s' %s",
495 getName(cls),
496 isMetaClass(cls) ? "(meta)" : "");
497 }
498
499 newlist = buildMethodList(NULL, cats, isMetaClass(cls));
500 newlist->next = cls->data->methods;
501 cls->data->methods = newlist;
502
503 newproperties = buildPropertyList(NULL, cats, isMetaClass(cls));
504 if (newproperties) {
505 newproperties->next = cls->data->properties;
506 cls->data->properties = newproperties;
507 }
508
509 newprotos = buildProtocolList(cats, NULL, cls->data->protocols);
510 if (cls->data->protocols && cls->data->protocols != newprotos) {
511 _free_internal(cls->data->protocols);
512 }
513 cls->data->protocols = newprotos;
514
515 _free_internal(cats);
516 }
517 }
518 }
519
520
521 /***********************************************************************
522 * changeInfo
523 * Atomically sets and clears some bits in cls's info field.
524 * set and clear must not overlap.
525 **********************************************************************/
526 static OBJC_DECLARE_LOCK(infoLock);
527 // fixme use atomic ops instead of lock
528 static void changeInfo(class_t *cls, unsigned int set, unsigned int clear)
529 {
530 assert(isRealized(cls));
531 OBJC_LOCK(&infoLock);
532 cls->data->flags = (cls->data->flags | set) & ~clear;
533 OBJC_UNLOCK(&infoLock);
534 }
535
536
537 /***********************************************************************
538 * realizedClasses
539 * Returns the classname => class map for realized non-meta classes.
540 * Locking: runtimeLock must be held by the caller
541 **********************************************************************/
542 static NXMapTable *realizedClasses(void)
543 {
544 static NXMapTable *class_map = NULL;
545
546 OBJC_CHECK_LOCKED(&runtimeLock);
547
548 if (class_map) return class_map;
549
550 // fixme this doesn't work yet
551 // class_map starts small, with only enough capacity for libobjc itself.
552 // If a second library is found by map_images(), class_hash is immediately
553 // resized to capacity 1024 to cut down on rehashes.
554 class_map = NXCreateMapTableFromZone(NXStrValueMapPrototype, 16,
555 _objc_internal_zone());
556
557 return class_map;
558 }
559
560
561 /***********************************************************************
562 * unrealizedClasses
563 * Returns the classname => class map for unrealized non-meta classes.
564 * Locking: runtimeLock must be held by the caller
565 **********************************************************************/
566 static NXMapTable *unrealizedClasses(void)
567 {
568 static NXMapTable *class_map = NULL;
569
570 OBJC_CHECK_LOCKED(&runtimeLock);
571
572 if (class_map) return class_map;
573
574 // fixme this doesn't work yet
575 // class_map starts small, with only enough capacity for libobjc itself.
576 // If a second library is found by map_images(), class_hash is immediately
577 // resized to capacity 1024 to cut down on rehashes.
578 class_map = NXCreateMapTableFromZone(NXStrValueMapPrototype, 16,
579 _objc_internal_zone());
580
581 return class_map;
582 }
583
584
585 /***********************************************************************
586 * addRealizedClass
587 * Adds name => cls to the realized non-meta class map.
588 * Also removes name => cls from the unrealized non-meta class map.
589 * Locking: runtimeLock must be held by the caller
590 **********************************************************************/
591 static void addRealizedClass(class_t *cls, const char *name)
592 {
593 OBJC_CHECK_LOCKED(&runtimeLock);
594 void *old;
595 old = NXMapInsert(realizedClasses(), name, cls);
596 assert(!isMetaClass(cls));
597 NXMapRemove(unrealizedClasses(), name);
598 }
599
600
601 /***********************************************************************
602 * removeRealizedClass
603 * Removes cls from the realized class map.
604 * Locking: runtimeLock must be held by the caller
605 **********************************************************************/
606 static void removeRealizedClass(class_t *cls)
607 {
608 OBJC_CHECK_LOCKED(&runtimeLock);
609 assert(isRealized(cls));
610 assert(!isMetaClass(cls));
611 NXMapRemove(realizedClasses(), cls->data->ro->name);
612 }
613
614
615 /***********************************************************************
616 * addUnrealizedClass
617 * Adds name => cls to the unrealized non-meta class map.
618 * Locking: runtimeLock must be held by the caller
619 **********************************************************************/
620 static void addUnrealizedClass(class_t *cls, const char *name)
621 {
622 OBJC_CHECK_LOCKED(&runtimeLock);
623 void *old;
624 old = NXMapInsert(unrealizedClasses(), name, cls);
625 assert(!isRealized(cls));
626 assert(!(cls->data->flags & RO_META));
627 }
628
629
630 /***********************************************************************
631 * uninitializedClasses
632 * Returns the metaclass => class map for un-+initialized classes
633 * Replaces the 32-bit cls = objc_getName(metacls) during +initialize.
634 * Locking: runtimeLock must be held by the caller
635 **********************************************************************/
636 static NXMapTable *uninitializedClasses(void)
637 {
638 static NXMapTable *class_map = NULL;
639
640 OBJC_CHECK_LOCKED(&runtimeLock);
641
642 if (class_map) return class_map;
643
644 // fixme this doesn't work yet
645 // class_map starts small, with only enough capacity for libobjc itself.
646 // If a second library is found by map_images(), class_hash is immediately
647 // resized to capacity 1024 to cut down on rehashes.
648 class_map = NXCreateMapTableFromZone(NXPtrValueMapPrototype, 16,
649 _objc_internal_zone());
650
651 return class_map;
652 }
653
654
655 /***********************************************************************
656 * addUninitializedClass
657 * Adds metacls => cls to the un-+initialized class map
658 * Locking: runtimeLock must be held by the caller
659 **********************************************************************/
660 static void addUninitializedClass(class_t *cls, class_t *metacls)
661 {
662 OBJC_CHECK_LOCKED(&runtimeLock);
663 void *old;
664 old = NXMapInsert(uninitializedClasses(), metacls, cls);
665 assert(isRealized(metacls) ? isMetaClass(metacls) : metacls->data->flags & RO_META);
666 assert(! (isRealized(cls) ? isMetaClass(cls) : cls->data->flags & RO_META));
667 assert(!old);
668 }
669
670
671 static void removeUninitializedClass(class_t *cls)
672 {
673 OBJC_CHECK_LOCKED(&runtimeLock);
674 NXMapRemove(uninitializedClasses(), cls->isa);
675 }
676
677
678 /***********************************************************************
679 * _class_getNonMetaClass
680 * Return the ordinary class for this class or metaclass.
681 * Used by +initialize.
682 * Locking: acquires runtimeLock
683 **********************************************************************/
684 __private_extern__ Class _class_getNonMetaClass(Class cls_gen)
685 {
686 class_t *cls = newcls(cls_gen);
687 OBJC_LOCK(&runtimeLock);
688 if (isMetaClass(cls)) {
689 cls = NXMapGet(uninitializedClasses(), cls);
690 realizeClass(cls);
691 }
692 OBJC_UNLOCK(&runtimeLock);
693
694 return (Class)cls;
695 }
696
697
698
699 /***********************************************************************
700 * futureClasses
701 * Returns the classname => future class map for unrealized future classes.
702 * Locking: runtimeLock must be held by the caller
703 **********************************************************************/
704 static NXMapTable *futureClasses(void)
705 {
706 OBJC_CHECK_LOCKED(&runtimeLock);
707
708 static NXMapTable *future_class_map = NULL;
709
710 if (future_class_map) return future_class_map;
711
712 // future_class_map is big enough to hold CF's classes and a few others
713 future_class_map = NXCreateMapTableFromZone(NXStrValueMapPrototype, 32,
714 _objc_internal_zone());
715
716 return future_class_map;
717 }
718
719
720 /***********************************************************************
721 * addFutureClass
722 * Installs cls as the class structure to use for the named class if it appears.
723 * Locking: runtimeLock must be held by the caller
724 **********************************************************************/
725 static void addFutureClass(const char *name, class_t *cls)
726 {
727 OBJC_CHECK_LOCKED(&runtimeLock);
728
729 if (PrintFuture) {
730 _objc_inform("FUTURE: reserving %p for %s", cls, name);
731 }
732
733 void *old;
734 old = NXMapKeyCopyingInsert(futureClasses(), name, cls);
735 assert(!old);
736 }
737
738
739 /***********************************************************************
740 * removeFutureClass
741 * Removes the named class from the unrealized future class list,
742 * because it has been realized.
743 * Locking: runtimeLock must be held by the caller
744 **********************************************************************/
745 static void removeFutureClass(const char *name)
746 {
747 OBJC_CHECK_LOCKED(&runtimeLock);
748
749 NXMapKeyFreeingRemove(futureClasses(), name);
750 }
751
752
753 /***********************************************************************
754 * remappedClasses
755 * Returns the oldClass => newClass map for realized future classes.
756 * Locking: remapLock must be held by the caller
757 **********************************************************************/
758 static OBJC_DECLARE_LOCK(remapLock);
759 static NXMapTable *remappedClasses(BOOL create)
760 {
761 static NXMapTable *remapped_class_map = NULL;
762
763 OBJC_CHECK_LOCKED(&remapLock);
764
765 if (remapped_class_map) return remapped_class_map;
766
767 if (!create) return NULL;
768
769 // remapped_class_map is big enough to hold CF's classes and a few others
770 remapped_class_map = NXCreateMapTableFromZone(NXPtrValueMapPrototype, 32,
771 _objc_internal_zone());
772
773 return remapped_class_map;
774 }
775
776
777 /***********************************************************************
778 * noClassesRemapped
779 * Returns YES if no classes have been remapped
780 * Locking: acquires remapLock
781 **********************************************************************/
782 static BOOL noClassesRemapped(void)
783 {
784 OBJC_LOCK(&remapLock);
785 BOOL result = (remappedClasses(NO) == NULL);
786 OBJC_UNLOCK(&remapLock);
787 return result;
788 }
789
790
791 /***********************************************************************
792 * addRemappedClass
793 * newcls is a realized future class, replacing oldcls.
794 * Locking: acquires remapLock
795 **********************************************************************/
796 static void addRemappedClass(class_t *oldcls, class_t *newcls)
797 {
798 OBJC_LOCK(&remapLock);
799
800 if (PrintFuture) {
801 _objc_inform("FUTURE: using %p instead of %p for %s",
802 oldcls, newcls, getName(newcls));
803 }
804
805 void *old;
806 old = NXMapInsert(remappedClasses(YES), oldcls, newcls);
807 assert(!old);
808
809 OBJC_UNLOCK(&remapLock);
810 }
811
812
813 /***********************************************************************
814 * remapClass
815 * Returns the live class pointer for cls, which may be pointing to
816 * a class struct that has been reallocated.
817 * Locking: acquires remapLock
818 **********************************************************************/
819 static class_t *remapClass(class_t *cls)
820 {
821 OBJC_LOCK(&remapLock);
822 class_t *newcls = NXMapGet(remappedClasses(YES), cls);
823 OBJC_UNLOCK(&remapLock);
824 return newcls ? newcls : cls;
825 }
826
827
828 /***********************************************************************
829 * remapClassRef
830 * Fix up a class ref, in case the class referenced has been reallocated.
831 * Locking: acquires remapLock
832 **********************************************************************/
833 static void remapClassRef(class_t **clsref)
834 {
835 class_t *newcls = remapClass(*clsref);
836 if (*clsref != newcls) *clsref = newcls;
837 }
838
839
840 /***********************************************************************
841 * addSubclass
842 * Adds subcls as a subclass of supercls.
843 * Locking: runtimeLock must be held by the caller.
844 **********************************************************************/
845 static void addSubclass(class_t *supercls, class_t *subcls)
846 {
847 OBJC_CHECK_LOCKED(&runtimeLock);
848
849 if (supercls && subcls) {
850 assert(isRealized(supercls));
851 assert(isRealized(subcls));
852 subcls->data->nextSiblingClass = supercls->data->firstSubclass;
853 supercls->data->firstSubclass = subcls;
854 }
855 }
856
857
858 /***********************************************************************
859 * removeSubclass
860 * Removes subcls as a subclass of supercls.
861 * Locking: runtimeLock must be held by the caller.
862 **********************************************************************/
863 static void removeSubclass(class_t *supercls, class_t *subcls)
864 {
865 OBJC_CHECK_LOCKED(&runtimeLock);
866 assert(getSuperclass(subcls) == supercls);
867
868 class_t **cp;
869 for (cp = &supercls->data->firstSubclass;
870 *cp && *cp != subcls;
871 cp = &(*cp)->data->nextSiblingClass)
872 ;
873 assert(*cp == subcls);
874 *cp = subcls->data->nextSiblingClass;
875 }
876
877
878
879 /***********************************************************************
880 * protocols
881 * Returns the protocol name => protocol map for protocols.
882 * Locking: runtimeLock must be held by the caller
883 **********************************************************************/
884 static NXMapTable *protocols(void)
885 {
886 static NXMapTable *protocol_map = NULL;
887
888 OBJC_CHECK_LOCKED(&runtimeLock);
889
890 if (protocol_map) return protocol_map;
891
892 // fixme this doesn't work yet
893 // class_map starts small, with only enough capacity for libobjc itself.
894 // If a second library is found by map_images(), class_hash is immediately
895 // resized to capacity 1024 to cut down on rehashes.
896 protocol_map = NXCreateMapTableFromZone(NXStrValueMapPrototype, 16,
897 _objc_internal_zone());
898
899 return protocol_map;
900 }
901
902
903 /***********************************************************************
904 * remapProtocol
905 * Returns the live protocol pointer for proto, which may be pointing to
906 * a protocol struct that has been reallocated.
907 * Locking: fixme
908 **********************************************************************/
909 static protocol_t *remapProtocol(protocol_t *proto)
910 {
911 // OBJC_LOCK(&remapLock);
912 protocol_t *newproto = NXMapGet(protocols(), proto->name);
913 // OBJC_UNLOCK(&remapLock);
914 return newproto ? newproto : proto;
915 }
916
917
918 /***********************************************************************
919 * remapProtocolRef
920 * Fix up a protocol ref, in case the protocol referenced has been reallocated.
921 * Locking: fixme
922 **********************************************************************/
923 static void remapProtocolRef(protocol_t **protoref)
924 {
925 protocol_t *newproto = remapProtocol(*protoref);
926 if (*protoref != newproto) *protoref = newproto;
927 }
928
929
930 /***********************************************************************
931 * moveIvars
932 * Slides a class's ivars to accommodate the given superclass size.
933 * Also slides ivar and weak GC layouts if provided.
934 * Ivars are NOT compacted to compensate for a superclass that shrunk.
935 * Locking: runtimeLock must be held by the caller.
936 **********************************************************************/
937 static void moveIvars(class_ro_t *ro, uint32_t superSize,
938 layout_bitmap *ivarBitmap, layout_bitmap *weakBitmap)
939 {
940 OBJC_CHECK_LOCKED(&runtimeLock);
941
942 uint32_t diff;
943 uint32_t gcdiff;
944 uint32_t i;
945
946 assert(superSize > ro->instanceStart);
947 diff = superSize - ro->instanceStart;
948 gcdiff = diff;
949 *(uint32_t *)&ro->instanceStart += diff;
950
951 if (ro->ivars) {
952 for (i = 0; i < ro->ivars->count; i++) {
953 ivar_t *ivar = ivar_list_nth(ro->ivars, i);
954 // naively slide ivar
955 uint32_t oldOffset = (uint32_t)*ivar->offset;
956 uint32_t newOffset = oldOffset + diff;
957 // realign if needed
958 uint32_t alignMask = (1<<ivar->alignment)-1;
959 if (newOffset & alignMask) {
960 uint32_t alignedOffset = (newOffset + alignMask) & ~alignMask;
961 assert(alignedOffset > newOffset);
962 diff += alignedOffset - newOffset;
963 gcdiff += alignedOffset - newOffset;
964 newOffset = alignedOffset;
965 }
966 // newOffset is ready
967 *ivar->offset = newOffset;
968 // update ivar layouts
969 if (gcdiff != 0 && (oldOffset & 7) == 0) {
970 // this ivar's alignment hasn't been accounted for yet
971 if (ivarBitmap) {
972 layout_bitmap_slide(ivarBitmap,
973 (newOffset-gcdiff)>>3, newOffset>>3);
974 }
975 if (weakBitmap) {
976 layout_bitmap_slide(weakBitmap,
977 (newOffset-gcdiff)>>3, newOffset>>3);
978 }
979 gcdiff = 0;
980 }
981
982 if (PrintIvars) {
983 _objc_inform("IVARS: offset %u -> %u for %s (size %u, align %u)",
984 oldOffset, newOffset, ivar->name,
985 ivar->size, 1<<ivar->alignment);
986 }
987 }
988 }
989
990 *(uint32_t *)&ro->instanceSize += diff; // diff now includes alignment pad
991
992 if (!ro->ivars) {
993 // No ivars slid, but superclass changed size.
994 // Expand bitmap in preparation for layout_bitmap_splat().
995 if (ivarBitmap) layout_bitmap_grow(ivarBitmap, ro->instanceSize>>3);
996 if (weakBitmap) layout_bitmap_grow(weakBitmap, ro->instanceSize>>3);
997 }
998
999 #if !__LP64__
1000 #error wrong word size used in this function
1001 #endif
1002 }
1003
1004
1005 /***********************************************************************
1006 * getIvar
1007 * Look up an ivar by name.
1008 * Locking: runtimeLock must be held by the caller.
1009 **********************************************************************/
1010 static ivar_t *getIvar(class_t *cls, const char *name)
1011 {
1012 OBJC_CHECK_LOCKED(&runtimeLock);
1013
1014 const ivar_list_t *ivars;
1015 assert(isRealized(cls));
1016 if ((ivars = cls->data->ro->ivars)) {
1017 uint32_t i;
1018 for (i = 0; i < ivars->count; i++) {
1019 struct ivar_t *ivar = ivar_list_nth(ivars, i);
1020 // ivar->name may be NULL for anonymous bitfields etc.
1021 if (ivar->name && 0 == strcmp(name, ivar->name)) {
1022 return ivar;
1023 }
1024 }
1025 }
1026
1027 return NULL;
1028 }
1029
1030
1031 /***********************************************************************
1032 * realizeClass
1033 * Performs first-time initialization on class cls,
1034 * including allocating its read-write data.
1035 * Returns the real class structure for the class.
1036 * Locking: runtimeLock must be held by the caller
1037 **********************************************************************/
1038 static class_t *realizeClass(class_t *cls)
1039 {
1040 OBJC_CHECK_LOCKED(&runtimeLock);
1041
1042 const class_ro_t *ro;
1043 class_rw_t *rw;
1044 class_t *supercls;
1045 class_t *metacls;
1046 BOOL isMeta;
1047
1048 if (!cls) return NULL;
1049 if (isRealized(cls)) return cls;
1050 assert(cls == remapClass(cls));
1051
1052 ro = (const class_ro_t *)cls->data;
1053
1054 isMeta = (ro->flags & RO_META) ? YES : NO;
1055
1056 if (PrintConnecting) {
1057 _objc_inform("CLASS: realizing class '%s' %s %p %p",
1058 ro->name, isMeta ? "(meta)" : "", cls, ro);
1059 }
1060
1061 // Allocate writeable class data
1062 rw = _calloc_internal(sizeof(class_rw_t), 1);
1063 rw->flags = RW_REALIZED;
1064 rw->version = isMeta ? 7 : 0; // old runtime went up to 6
1065 rw->ro = ro;
1066
1067 cls->data = rw;
1068
1069 // Realize superclass and metaclass, if they aren't already.
1070 // This needs to be done after RW_REALIZED is set above, for root classes.
1071 supercls = realizeClass(remapClass(cls->superclass));
1072 metacls = realizeClass(remapClass(cls->isa));
1073
1074 // Check for remapped superclass
1075 // fixme doesn't handle remapped metaclass
1076 assert(metacls == cls->isa);
1077 if (supercls != cls->superclass) {
1078 cls->superclass = supercls;
1079 }
1080
1081 /* debug: print them all
1082 if (ro->ivars) {
1083 uint32_t i;
1084 for (i = 0; i < ro->ivars->count; i++) {
1085 ivar_t *ivar = ivar_list_nth(ro->ivars, i);
1086 _objc_inform("IVARS: %s.%s (offset %u, size %u, align %u)",
1087 ro->name, ivar->name,
1088 *ivar->offset, ivar->size, 1<<ivar->alignment);
1089 }
1090 }
1091 */
1092
1093
1094 if (supercls) {
1095 // Non-fragile ivars - reconcile this class with its superclass
1096 layout_bitmap ivarBitmap;
1097 layout_bitmap weakBitmap;
1098 BOOL layoutsChanged = NO;
1099
1100 if (UseGC) {
1101 // fixme can optimize for "class has no new ivars", etc
1102 // WARNING: gcc c++ sets instanceStart/Size=0 for classes with
1103 // no local ivars, but does provide a layout bitmap.
1104 // Handle that case specially so layout_bitmap_create doesn't die
1105 // The other ivar sliding code below still works fine, and
1106 // the final result is a good class.
1107 if (ro->instanceStart == 0 && ro->instanceSize == 0) {
1108 // We can't use ro->ivarLayout because we don't know
1109 // how long it is. Force a new layout to be created.
1110 if (PrintIvars) {
1111 _objc_inform("IVARS: instanceStart/Size==0 for class %s; "
1112 "disregarding ivar layout", ro->name);
1113 }
1114 ivarBitmap =
1115 layout_bitmap_create(NULL,
1116 supercls->data->ro->instanceSize,
1117 supercls->data->ro->instanceSize, NO);
1118 weakBitmap =
1119 layout_bitmap_create(NULL,
1120 supercls->data->ro->instanceSize,
1121 supercls->data->ro->instanceSize, YES);
1122 layoutsChanged = YES;
1123 } else {
1124 ivarBitmap =
1125 layout_bitmap_create(ro->ivarLayout,
1126 ro->instanceSize,
1127 ro->instanceSize, NO);
1128 weakBitmap =
1129 layout_bitmap_create(ro->weakIvarLayout,
1130 ro->instanceSize,
1131 ro->instanceSize, YES);
1132 }
1133 }
1134
1135 if (ro->instanceStart < supercls->data->ro->instanceSize) {
1136 // Superclass has changed size. This class's ivars must move.
1137 // Also slide layout bits in parallel.
1138 // This code is incapable of compacting the subclass to
1139 // compensate for a superclass that shrunk, so don't do that.
1140 if (PrintIvars) {
1141 _objc_inform("IVARS: sliding ivars for class %s "
1142 "(superclass was %u bytes, now %u)",
1143 ro->name, ro->instanceStart,
1144 supercls->data->ro->instanceSize);
1145 }
1146 class_ro_t *ro_w = make_ro_writeable(rw);
1147 ro = rw->ro;
1148 moveIvars(ro_w, supercls->data->ro->instanceSize,
1149 UseGC ? &ivarBitmap : NULL, UseGC ? &weakBitmap : NULL);
1150 layoutsChanged = YES;
1151 }
1152
1153 if (UseGC) {
1154 // Check superclass's layout against this class's layout.
1155 // This needs to be done even if the superclass is not bigger.
1156 layout_bitmap superBitmap =
1157 layout_bitmap_create(supercls->data->ro->ivarLayout,
1158 supercls->data->ro->instanceSize,
1159 supercls->data->ro->instanceSize, NO);
1160 layoutsChanged |= layout_bitmap_splat(ivarBitmap, superBitmap,
1161 ro->instanceStart);
1162 layout_bitmap_free(superBitmap);
1163
1164 superBitmap =
1165 layout_bitmap_create(supercls->data->ro->weakIvarLayout,
1166 supercls->data->ro->instanceSize,
1167 supercls->data->ro->instanceSize, YES);
1168 layoutsChanged |= layout_bitmap_splat(weakBitmap, superBitmap,
1169 ro->instanceStart);
1170 layout_bitmap_free(superBitmap);
1171
1172 if (layoutsChanged) {
1173 // Rebuild layout strings.
1174 if (PrintIvars) {
1175 _objc_inform("IVARS: gc layout changed for class %s",
1176 ro->name);
1177 }
1178 class_ro_t *ro_w = make_ro_writeable(rw);
1179 ro = rw->ro;
1180 ro_w->ivarLayout = layout_string_create(ivarBitmap);
1181 ro_w->weakIvarLayout = layout_string_create(weakBitmap);
1182 }
1183
1184 layout_bitmap_free(ivarBitmap);
1185 layout_bitmap_free(weakBitmap);
1186 }
1187 }
1188
1189 // Connect this class to its superclass's subclass lists
1190 if (supercls) {
1191 addSubclass(supercls, cls);
1192 }
1193
1194 if (!isMeta) {
1195 addRealizedClass(cls, cls->data->ro->name);
1196 } else {
1197 // metaclasses don't go in the realized class map
1198 }
1199
1200 return cls;
1201 }
1202
1203
1204 /***********************************************************************
1205 * getClass
1206 * Looks up a class by name with no hints, and realizes it.
1207 * Locking: runtimeLock must be held by the caller.
1208 **********************************************************************/
1209 static class_t *getClass(const char *name)
1210 {
1211 OBJC_CHECK_LOCKED(&runtimeLock);
1212
1213 class_t *result;
1214
1215 // Try realized classes
1216 result = (class_t *)NXMapGet(realizedClasses(), name);
1217 if (result) return result;
1218
1219 // Try unrealized classes
1220 result = (class_t *)NXMapGet(unrealizedClasses(), name);
1221 if (result) return result;
1222
1223 #if 0
1224 // Try a classname symbol
1225 result = getClassBySymbol(NULL, name);
1226 if (result) {
1227 result = realizeClass(remapClass(result));
1228 return result;
1229 }
1230
1231 if (!result) {
1232 // fixme suck
1233 realizeAllClasses();
1234 result = (class_t *)NXMapGet(realizedClasses(), name);
1235 }
1236 #endif
1237
1238
1239 // darn
1240 return NULL;
1241 }
1242
1243
1244 /***********************************************************************
1245 * realizeAllClassesInImage
1246 * Non-lazily realizes all unrealized classes in the given image.
1247 * Locking: runtimeLock must be held by the caller.
1248 **********************************************************************/
1249 static void realizeAllClassesInImage(header_info *hi)
1250 {
1251 OBJC_CHECK_LOCKED(&runtimeLock);
1252
1253 size_t count, i;
1254 class_t **classlist;
1255
1256 if (hi->allClassesRealized) return;
1257
1258 classlist = _getObjc2ClassList(hi, &count);
1259
1260 for (i = 0; i < count; i++) {
1261 realizeClass(remapClass(classlist[i]));
1262 }
1263
1264 hi->allClassesRealized = YES;
1265 }
1266
1267
1268 /***********************************************************************
1269 * realizeAllClasses
1270 * Non-lazily realizes all unrealized classes in all known images.
1271 * Locking: runtimeLock must be held by the caller.
1272 **********************************************************************/
1273 static void realizeAllClasses(void)
1274 {
1275 OBJC_CHECK_LOCKED(&runtimeLock);
1276
1277 header_info *hi;
1278 for (hi = _objc_headerStart(); hi; hi = hi->next) {
1279 realizeAllClassesInImage(hi);
1280 }
1281 }
1282
1283
1284 /***********************************************************************
1285 * _objc_allocateFutureClass
1286 * Allocate an unresolved future class for the given class name.
1287 * Returns any existing allocation if one was already made.
1288 * Assumes the named class doesn't exist yet.
1289 * Locking: acquires runtimeLock
1290 **********************************************************************/
1291 __private_extern__ Class _objc_allocateFutureClass(const char *name)
1292 {
1293 OBJC_LOCK(&runtimeLock);
1294
1295 struct class_t *cls;
1296 NXMapTable *future_class_map = futureClasses();
1297
1298 if ((cls = NXMapGet(future_class_map, name))) {
1299 // Already have a future class for this name.
1300 OBJC_UNLOCK(&runtimeLock);
1301 return (Class)cls;
1302 }
1303
1304 cls = _calloc_internal(sizeof(*cls), 1);
1305 addFutureClass(name, cls);
1306
1307 OBJC_UNLOCK(&runtimeLock);
1308 return (Class)cls;
1309 }
1310
1311
1312 /***********************************************************************
1313 *
1314 **********************************************************************/
1315 void objc_setFutureClass(Class cls, const char *name)
1316 {
1317 // fixme hack do nothing - NSCFString handled specially elsewhere
1318 }
1319
1320
1321 static BOOL addrInSeg(const void *addr_ptr, const segmentType *segment,
1322 ptrdiff_t slide)
1323 {
1324 uintptr_t base = segment->vmaddr + slide;
1325 uintptr_t addr = (uintptr_t)addr_ptr;
1326 size_t size = segment->filesize;
1327
1328 return (addr >= base && addr < base + size);
1329 }
1330
1331 static BOOL ptrInImageList(header_info **hList, uint32_t hCount,
1332 const void *ptr)
1333 {
1334 uint32_t i;
1335
1336 for (i = 0; i < hCount; i++) {
1337 header_info *hi = hList[i];
1338 if (addrInSeg(ptr, hi->dataSegmentHeader, hi->image_slide)) {
1339 return YES;
1340 }
1341 }
1342
1343 return NO;
1344 }
1345
1346
1347 #define FOREACH_SUBCLASS(c, cls, code) \
1348 do { \
1349 OBJC_CHECK_LOCKED(&runtimeLock); \
1350 class_t *top = cls; \
1351 class_t *c = top; \
1352 if (c) while (1) { \
1353 code \
1354 if (c->data->firstSubclass) { \
1355 c = c->data->firstSubclass; \
1356 } else { \
1357 while (!c->data->nextSiblingClass && c != top) { \
1358 c = getSuperclass(c); \
1359 } \
1360 if (c == top) break; \
1361 c = c->data->nextSiblingClass; \
1362 } \
1363 } \
1364 } while (0)
1365
1366
1367 /***********************************************************************
1368 * flushCaches
1369 * Flushes caches for cls and its subclasses.
1370 * Locking: runtimeLock must be held by the caller.
1371 **********************************************************************/
1372 static void flushCaches(class_t *cls)
1373 {
1374 OBJC_CHECK_LOCKED(&runtimeLock);
1375
1376 FOREACH_SUBCLASS(c, cls, {
1377 flush_cache((Class)c);
1378 });
1379 }
1380
1381
1382 /***********************************************************************
1383 * flush_caches
1384 * Flushes caches for cls, its subclasses, and optionally its metaclass.
1385 * Locking: acquires runtimeLock
1386 **********************************************************************/
1387 __private_extern__ void flush_caches(Class cls, BOOL flush_meta)
1388 {
1389 OBJC_LOCK(&runtimeLock);
1390 flushCaches(newcls(cls));
1391 if (flush_meta) flushCaches(newcls(cls)->isa);
1392 OBJC_UNLOCK(&runtimeLock);
1393 }
1394
1395
1396 /***********************************************************************
1397 * _read_images
1398 * Perform initial processing of the headers in the linked
1399 * list beginning with headerList.
1400 *
1401 * Called by: map_images
1402 *
1403 * Locking: acquires runtimeLock
1404 **********************************************************************/
1405 __private_extern__ void _read_images(header_info **hList, uint32_t hCount)
1406 {
1407 header_info *hi;
1408 uint32_t hIndex;
1409 size_t count;
1410 size_t i, j;
1411 class_t **resolvedFutureClasses = NULL;
1412 size_t resolvedFutureClassCount = 0;
1413
1414 #define EACH_HEADER \
1415 hIndex = 0; \
1416 hIndex < hCount && (hi = hList[hIndex]); \
1417 hIndex++
1418
1419 OBJC_LOCK(&runtimeLock);
1420
1421 // Complain about images that contain old-ABI data
1422 // fixme new-ABI compiler still emits some bits into __OBJC segment
1423 for (EACH_HEADER) {
1424 size_t count;
1425 if (_getObjcSelectorRefs(hi, &count) ||
1426 _getObjcModules(hi->mhdr, hi->image_slide, &count))
1427 {
1428 _objc_inform("found old-ABI metadata in image %s !",
1429 hi->dl_info.dli_fname);
1430 }
1431 }
1432
1433 // fixme hack
1434 static BOOL hackedNSCFString = NO;
1435 if (!hackedNSCFString) {
1436 // Insert future class __CFConstantStringClassReference == NSCFString
1437 void *dlh = dlopen("/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation", RTLD_LAZY | RTLD_NOLOAD | RTLD_FIRST);
1438 if (dlh) {
1439 void *addr = dlsym(dlh, "__CFConstantStringClassReference");
1440 if (addr) {
1441 addFutureClass("NSCFString", (class_t *)addr);
1442 hackedNSCFString = YES;
1443 }
1444 dlclose(dlh);
1445 }
1446 }
1447
1448 // Discover classes. Fix up unresolved future classes
1449 NXMapTable *future_class_map = futureClasses();
1450 for (EACH_HEADER) {
1451 class_t **classlist = _getObjc2ClassList(hi, &count);
1452 for (i = 0; i < count; i++) {
1453 const char *name = getName(classlist[i]);
1454 if (NXCountMapTable(future_class_map) > 0) {
1455 class_t *newCls = NXMapGet(future_class_map, name);
1456 if (newCls) {
1457 memcpy(newCls, classlist[i], sizeof(class_t));
1458 removeFutureClass(name);
1459 addRemappedClass(classlist[i], newCls);
1460 classlist[i] = newCls;
1461 // Non-lazily realize the class below.
1462 resolvedFutureClasses = (class_t **)
1463 _realloc_internal(resolvedFutureClasses,
1464 (resolvedFutureClassCount+1)
1465 * sizeof(class_t *));
1466 resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
1467 }
1468 }
1469 addUnrealizedClass(classlist[i], name);
1470 addUninitializedClass(classlist[i], classlist[i]->isa);
1471 }
1472 }
1473
1474 // Fix up remapped classes
1475 // classlist is up to date, but classrefs may not be
1476
1477 if (!noClassesRemapped()) {
1478 for (EACH_HEADER) {
1479 class_t **classrefs = _getObjc2ClassRefs(hi, &count);
1480 for (i = 0; i < count; i++) {
1481 remapClassRef(&classrefs[i]);
1482 }
1483 // fixme why doesn't test future1 catch the absence of this?
1484 classrefs = _getObjc2SuperRefs(hi, &count);
1485 for (i = 0; i < count; i++) {
1486 remapClassRef(&classrefs[i]);
1487 }
1488 }
1489 }
1490
1491
1492 // Fix up @selector references
1493 sel_lock();
1494 for (EACH_HEADER) {
1495 SEL *sels = _getObjc2SelectorRefs(hi, &count);
1496 BOOL isBundle = hi->mhdr->filetype == MH_BUNDLE;
1497 for (i = 0; i < count; i++) {
1498 sels[i] = sel_registerNameNoLock((const char *)sels[i], isBundle);
1499 }
1500 }
1501 sel_unlock();
1502
1503 // Discover protocols. Fix up protocol refs.
1504 NXMapTable *protocol_map = protocols();
1505 for (EACH_HEADER) {
1506 extern struct class_t OBJC_CLASS_$_Protocol;
1507 Class cls = (Class)&OBJC_CLASS_$_Protocol;
1508 assert(cls);
1509 protocol_t **protocols = _getObjc2ProtocolList(hi, &count);
1510 // fixme duplicate protocol from bundle
1511 for (i = 0; i < count; i++) {
1512 if (!NXMapGet(protocol_map, protocols[i]->name)) {
1513 protocols[i]->isa = cls;
1514 NXMapKeyCopyingInsert(protocol_map,
1515 protocols[i]->name, protocols[i]);
1516 if (PrintProtocols) {
1517 _objc_inform("PROTOCOLS: protocol at %p is %s",
1518 protocols[i], protocols[i]->name);
1519 }
1520 } else {
1521 if (PrintProtocols) {
1522 _objc_inform("PROTOCOLS: protocol at %p is %s (duplicate)",
1523 protocols[i], protocols[i]->name);
1524 }
1525 }
1526 }
1527 }
1528 for (EACH_HEADER) {
1529 protocol_t **protocols;
1530 protocols = _getObjc2ProtocolRefs(hi, &count);
1531 for (i = 0; i < count; i++) {
1532 remapProtocolRef(&protocols[i]);
1533 }
1534
1535 protocols = _getObjc2ProtocolList(hi, &count);
1536 for (i = 0; i < count; i++) {
1537 protocol_t *protocol = NXMapGet(protocol_map, protocols[i]->name);
1538 assert(protocol);
1539 if (protocol == protocols[i] && protocol->protocols) {
1540 if (PrintProtocols) {
1541 _objc_inform("PROTOCOLS: remapping superprotocols of %p %s",
1542 protocol, protocol->name);
1543 }
1544 for (j = 0; j < protocol->protocols->count; j++) {
1545 remapProtocolRef(&protocol->protocols->list[j]);
1546 }
1547 }
1548 }
1549 }
1550
1551 // Discover categories.
1552 for (EACH_HEADER) {
1553 category_t **catlist =
1554 _getObjc2CategoryList(hi, &count);
1555 for (i = 0; i < count; i++) {
1556 category_t *cat = catlist[i];
1557 // Do NOT use cat->cls! It may have been remapped.
1558 class_t *cls = remapClass(cat->cls);
1559
1560 // Process this category.
1561 // First, register the category with its target class.
1562 // Then, flush the class's cache (and its subclasses) if
1563 // the class is methodized. The ptrInImageList() check
1564 // can discover !methodized without touching the class's memory.
1565 // GrP fixme class's memory is already touched.
1566 BOOL classExists = NO;
1567 if (cat->instanceMethods || cat->protocols
1568 || cat->instanceProperties)
1569 {
1570 addUnattachedCategoryForClass(cat, cls);
1571 if (!ptrInImageList(hList, hCount, cls) &&
1572 isMethodized(cls))
1573 {
1574 flushCaches(cls);
1575 classExists = YES;
1576 }
1577 if (PrintConnecting) {
1578 _objc_inform("CLASS: found category -%s(%s) %s\n",
1579 getName(cls), cat->name,
1580 classExists ? "on existing class" : "");
1581 }
1582 }
1583
1584 if (cat->classMethods || cat->protocols
1585 /* || cat->classProperties */)
1586 {
1587 addUnattachedCategoryForClass(cat, cls->isa);
1588 if (!ptrInImageList(hList, hCount, cls->isa) &&
1589 isRealized(cls->isa))
1590 {
1591 flushCaches(cls->isa);
1592 }
1593 if (PrintConnecting) {
1594 _objc_inform("CLASS: found category +%s(%s)",
1595 getName(cls), cat->name);
1596 }
1597 }
1598 }
1599 }
1600
1601
1602 // Realize non-lazy classes (for +load methods and static instances)
1603
1604 for (EACH_HEADER) {
1605 class_t **classlist =
1606 _getObjc2NonlazyClassList(hi, &count);
1607 for (i = 0; i < count; i++) {
1608 realizeClass(remapClass(classlist[i]));
1609 }
1610 }
1611
1612 // Realize newly-resolved future classes, in case CF manipulates them
1613 if (resolvedFutureClasses) {
1614 for (i = 0; i < resolvedFutureClassCount; i++) {
1615 realizeClass(resolvedFutureClasses[i]);
1616 }
1617 _free_internal(resolvedFutureClasses);
1618 }
1619
1620 // +load handled by prepare_load_methods()
1621
1622
1623 OBJC_UNLOCK(&runtimeLock);
1624
1625 #undef EACH_HEADER
1626 }
1627
1628
1629 /***********************************************************************
1630 * prepare_load_methods
1631 * Schedule +load for classes in this image, any un-+load-ed
1632 * superclasses in other images, and any categories in this image.
1633 **********************************************************************/
1634 // Recursively schedule +load for cls and any un-+load-ed superclasses.
1635 // cls must already be connected.
1636 static void schedule_class_load(class_t *cls)
1637 {
1638 assert(isRealized(cls)); // _read_images should realize
1639
1640 if (cls->data->flags & RW_LOADED) return;
1641
1642 class_t *supercls = getSuperclass(cls);
1643 if (supercls) schedule_class_load(supercls);
1644
1645 add_class_to_loadable_list((Class)cls);
1646 changeInfo(cls, RW_LOADED, 0);
1647 }
1648
1649 __private_extern__ void prepare_load_methods(header_info *hi)
1650 {
1651 size_t count, i;
1652
1653 OBJC_LOCK(&runtimeLock);
1654
1655 class_t **classlist =
1656 _getObjc2NonlazyClassList(hi, &count);
1657 for (i = 0; i < count; i++) {
1658 class_t *cls = remapClass(classlist[i]);
1659 schedule_class_load(cls);
1660 }
1661
1662 category_t **categorylist = _getObjc2NonlazyCategoryList(hi, &count);
1663 for (i = 0; i < count; i++) {
1664 category_t *cat = categorylist[i];
1665 // Do NOT use cat->cls! It may have been remapped.
1666 class_t *cls = remapClass(cat->cls);
1667 realizeClass(cls);
1668 assert(isRealized(cls->isa));
1669 add_category_to_loadable_list((Category)cat);
1670 }
1671
1672 OBJC_UNLOCK(&runtimeLock);
1673 }
1674
1675
1676 /***********************************************************************
1677 * _unload_image
1678 * Only handles MH_BUNDLE for now.
1679 **********************************************************************/
1680 __private_extern__ void _unload_image(header_info *hi)
1681 {
1682 size_t count, i;
1683
1684 OBJC_LOCK(&runtimeLock);
1685
1686 // Unload unattached categories and categories waiting for +load.
1687
1688 category_t **catlist = _getObjc2CategoryList(hi, &count);
1689 for (i = 0; i < count; i++) {
1690 category_t *cat = catlist[i];
1691 class_t *cls = remapClass(cat->cls);
1692 // fixme for MH_DYLIB cat's class may have been unloaded already
1693
1694 // unattached list
1695 removeUnattachedCategoryForClass(cat, cls);
1696
1697 // +load queue
1698 remove_category_from_loadable_list((Category)cat);
1699 }
1700
1701 // Unload classes.
1702
1703 class_t **classlist = _getObjc2ClassList(hi, &count);
1704 for (i = 0; i < count; i++) {
1705 class_t *cls = classlist[i];
1706 const char *name = getName(cls);
1707 // fixme remapped classes?
1708
1709 // +load queue
1710 remove_class_from_loadable_list((Class)cls);
1711
1712 // categories not yet attached to this class
1713 category_list *cats;
1714 cats = unattachedCategoriesForClass(cls);
1715 if (cats) free(cats);
1716 cats = unattachedCategoriesForClass(cls);
1717 if (cats) free(cats);
1718
1719 // subclass lists
1720 class_t *supercls;
1721 if ((supercls = getSuperclass(cls))) {
1722 removeSubclass(supercls, cls);
1723 }
1724 if ((supercls = getSuperclass(cls->isa))) {
1725 removeSubclass(supercls, cls->isa);
1726 }
1727
1728 // class tables
1729 NXMapRemove(unrealizedClasses(), name);
1730 NXMapRemove(realizedClasses(), name);
1731 NXMapRemove(uninitializedClasses(), cls->isa);
1732
1733 // the class itself
1734 if (isRealized(cls->isa)) unload_class(cls->isa);
1735 if (isRealized(cls)) unload_class(cls);
1736 }
1737
1738 // Clean up protocols.
1739 #warning fixme protocol unload
1740
1741 // fixme DebugUnload
1742
1743 OBJC_UNLOCK(&runtimeLock);
1744 }
1745
1746
1747 /***********************************************************************
1748 * method_getDescription
1749 * Returns a pointer to this method's objc_method_description.
1750 * Locking: none
1751 **********************************************************************/
1752 struct objc_method_description *
1753 method_getDescription(Method m)
1754 {
1755 if (!m) return NULL;
1756 return (struct objc_method_description *)newmethod(m);
1757 }
1758
1759
1760 /***********************************************************************
1761 * method_getImplementation
1762 * Returns this method's IMP.
1763 * Locking: none
1764 **********************************************************************/
1765 IMP
1766 method_getImplementation(Method m)
1767 {
1768 if (!m) return NULL;
1769 if (newmethod(m)->name == (SEL)kRTAddress_ignoredSelector) {
1770 return (IMP)_objc_ignored_method;
1771 }
1772 return newmethod(m)->imp;
1773 }
1774
1775
1776 /***********************************************************************
1777 * method_getName
1778 * Returns this method's selector.
1779 * The method must not be NULL.
1780 * The method must already have been fixed-up.
1781 * Locking: none
1782 **********************************************************************/
1783 SEL
1784 method_getName(Method m_gen)
1785 {
1786 struct method_t *m = newmethod(m_gen);
1787 if (!m) return NULL;
1788 assert((SEL)m->name == sel_registerName((char *)m->name));
1789 return (SEL)m->name;
1790 }
1791
1792
1793 /***********************************************************************
1794 * method_getTypeEncoding
1795 * Returns this method's old-style type encoding string.
1796 * The method must not be NULL.
1797 * Locking: none
1798 **********************************************************************/
1799 const char *
1800 method_getTypeEncoding(Method m)
1801 {
1802 if (!m) return NULL;
1803 return newmethod(m)->types;
1804 }
1805
1806
1807 /***********************************************************************
1808 * method_setImplementation
1809 * Sets this method's implementation to imp.
1810 * The previous implementation is returned.
1811 **********************************************************************/
1812 IMP
1813 method_setImplementation(Method m, IMP imp)
1814 {
1815 static OBJC_DECLARE_LOCK(impLock);
1816 IMP old;
1817
1818 OBJC_LOCK(&impLock);
1819 old = method_getImplementation(m);
1820 newmethod(m)->imp = imp;
1821 OBJC_UNLOCK(&impLock);
1822
1823 // No cache flushing needed.
1824 // fixme update vtables if necessary
1825 // fixme update monomorphism if necessary
1826 return old;
1827 }
1828
1829
1830 /***********************************************************************
1831 * _class_realize
1832 * Realizes the given class.
1833 * Called by _class_lookupMethodAndLoadCache only.
1834 * Locking: acquires runtimeLock
1835 **********************************************************************/
1836 __private_extern__ void
1837 _class_realize(struct class_t *cls)
1838 {
1839 OBJC_LOCK(&runtimeLock);
1840 realizeClass(cls);
1841 OBJC_UNLOCK(&runtimeLock);
1842 }
1843
1844
1845
1846 /***********************************************************************
1847 * ivar_getOffset
1848 * fixme
1849 * Locking: none
1850 **********************************************************************/
1851 ptrdiff_t
1852 ivar_getOffset(Ivar ivar)
1853 {
1854 if (!ivar) return 0;
1855 return *newivar(ivar)->offset;
1856 }
1857
1858
1859 /***********************************************************************
1860 * ivar_getName
1861 * fixme
1862 * Locking: none
1863 **********************************************************************/
1864 const char *
1865 ivar_getName(Ivar ivar)
1866 {
1867 if (!ivar) return NULL;
1868 return newivar(ivar)->name;
1869 }
1870
1871
1872 /***********************************************************************
1873 * ivar_getTypeEncoding
1874 * fixme
1875 * Locking: none
1876 **********************************************************************/
1877 const char *
1878 ivar_getTypeEncoding(Ivar ivar)
1879 {
1880 if (!ivar) return NULL;
1881 return newivar(ivar)->type;
1882 }
1883
1884
1885 /***********************************************************************
1886 * _protocol_getMethod_nolock
1887 * Locking: runtimeLock must be held by the caller
1888 **********************************************************************/
1889 static Method
1890 _protocol_getMethod_nolock(protocol_t *proto, SEL sel,
1891 BOOL isRequiredMethod, BOOL isInstanceMethod)
1892 {
1893 OBJC_CHECK_LOCKED(&runtimeLock);
1894
1895 uint32_t i;
1896 if (!proto || !sel) return NULL;
1897
1898 method_list_t *mlist = NULL;
1899
1900 if (isRequiredMethod) {
1901 if (isInstanceMethod) {
1902 mlist = proto->instanceMethods;
1903 } else {
1904 mlist = proto->classMethods;
1905 }
1906 } else {
1907 if (isInstanceMethod) {
1908 mlist = proto->optionalInstanceMethods;
1909 } else {
1910 mlist = proto->optionalClassMethods;
1911 }
1912 }
1913
1914 if (mlist) {
1915 for (i = 0; i < mlist->count; i++) {
1916 method_t *m = method_list_nth(mlist, i);
1917 if (sel != m->name) {
1918 m->name = sel_registerName((char *)m->name);
1919 }
1920 if (sel == m->name) {
1921 return (Method)m;
1922 }
1923 }
1924 }
1925
1926 if (proto->protocols) {
1927 Method m;
1928 for (i = 0; i < proto->protocols->count; i++) {
1929 protocol_t *realProto = remapProtocol(proto->protocols->list[i]);
1930 m = _protocol_getMethod_nolock(realProto, sel,
1931 isRequiredMethod, isInstanceMethod);
1932 if (m) return m;
1933 }
1934 }
1935
1936 return NULL;
1937 }
1938
1939
1940 /***********************************************************************
1941 * _protocol_getMethod
1942 * fixme
1943 * Locking: acquires runtimeLock
1944 **********************************************************************/
1945 __private_extern__ Method
1946 _protocol_getMethod(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod)
1947 {
1948 OBJC_LOCK(&runtimeLock);
1949 Method result = _protocol_getMethod_nolock(newprotocol(p), sel,
1950 isRequiredMethod,
1951 isInstanceMethod);
1952 OBJC_UNLOCK(&runtimeLock);
1953 return result;
1954 }
1955
1956
1957 /***********************************************************************
1958 * protocol_getName
1959 * Returns the name of the given protocol.
1960 * Locking: runtimeLock must not be held by the caller
1961 **********************************************************************/
1962 const char *
1963 protocol_getName(Protocol *proto)
1964 {
1965 return newprotocol(proto)->name;
1966 }
1967
1968
1969 /***********************************************************************
1970 * protocol_getInstanceMethodDescription
1971 * Returns the description of a named instance method.
1972 * Locking: runtimeLock must not be held by the caller
1973 **********************************************************************/
1974 struct objc_method_description
1975 protocol_getMethodDescription(Protocol *p, SEL aSel,
1976 BOOL isRequiredMethod, BOOL isInstanceMethod)
1977 {
1978 Method m =
1979 _protocol_getMethod(p, aSel, isRequiredMethod, isInstanceMethod);
1980 if (m) return *method_getDescription(m);
1981 else return (struct objc_method_description){NULL, NULL};
1982 }
1983
1984
1985 /***********************************************************************
1986 * protocol_conformsToProtocol
1987 * Returns YES if self conforms to other.
1988 * Locking: runtimeLock must not be held by the caller
1989 **********************************************************************/
1990 BOOL protocol_conformsToProtocol(Protocol *self_gen, Protocol *other_gen)
1991 {
1992 protocol_t *self = newprotocol(self_gen);
1993 protocol_t *other = newprotocol(other_gen);
1994
1995 if (!self || !other) {
1996 return NO;
1997 }
1998
1999 if (0 == strcmp(self->name, other->name)) {
2000 return YES;
2001 }
2002
2003 if (self->protocols) {
2004 int i;
2005 for (i = 0; i < self->protocols->count; i++) {
2006 protocol_t *proto = self->protocols->list[i];
2007 if (0 == strcmp(other->name, proto->name)) {
2008 return YES;
2009 }
2010 if (protocol_conformsToProtocol((Protocol *)proto, other_gen)) {
2011 return YES;
2012 }
2013 }
2014 }
2015
2016 return NO;
2017 }
2018
2019
2020 /***********************************************************************
2021 * protocol_isEqual
2022 * Return YES if two protocols are equal (i.e. conform to each other)
2023 * Locking: acquires runtimeLock
2024 **********************************************************************/
2025 BOOL protocol_isEqual(Protocol *self, Protocol *other)
2026 {
2027 if (self == other) return YES;
2028 if (!self || !other) return NO;
2029
2030 if (!protocol_conformsToProtocol(self, other)) return NO;
2031 if (!protocol_conformsToProtocol(other, self)) return NO;
2032
2033 return YES;
2034 }
2035
2036
2037 /***********************************************************************
2038 * protocol_copyMethodDescriptionList
2039 * Returns descriptions of a protocol's methods.
2040 * Locking: acquires runtimeLock
2041 **********************************************************************/
2042 struct objc_method_description *
2043 protocol_copyMethodDescriptionList(Protocol *p,
2044 BOOL isRequiredMethod,BOOL isInstanceMethod,
2045 unsigned int *outCount)
2046 {
2047 struct protocol_t *proto = newprotocol(p);
2048 struct objc_method_description *result = NULL;
2049 unsigned int count = 0;
2050
2051 if (!proto) {
2052 if (outCount) *outCount = 0;
2053 return NULL;
2054 }
2055
2056 OBJC_LOCK(&runtimeLock);
2057
2058 method_list_t *mlist = NULL;
2059
2060 if (isRequiredMethod) {
2061 if (isInstanceMethod) {
2062 mlist = proto->instanceMethods;
2063 } else {
2064 mlist = proto->classMethods;
2065 }
2066 } else {
2067 if (isInstanceMethod) {
2068 mlist = proto->optionalInstanceMethods;
2069 } else {
2070 mlist = proto->optionalClassMethods;
2071 }
2072 }
2073
2074 if (mlist) {
2075 unsigned int i;
2076 count = mlist->count;
2077 result = calloc(count + 1, sizeof(struct objc_method_description));
2078 for (i = 0; i < count; i++) {
2079 method_t *m = method_list_nth(mlist, i);
2080 result[i].name = sel_registerName((const char *)m->name);
2081 result[i].types = (char *)m->types;
2082 }
2083 }
2084
2085 OBJC_UNLOCK(&runtimeLock);
2086
2087 if (outCount) *outCount = count;
2088 return result;
2089 }
2090
2091
2092 /***********************************************************************
2093 * protocol_getProperty
2094 * fixme
2095 * Locking: acquires runtimeLock
2096 **********************************************************************/
2097 static Property
2098 _protocol_getProperty_nolock(protocol_t *proto, const char *name,
2099 BOOL isRequiredProperty, BOOL isInstanceProperty)
2100 {
2101 if (!isRequiredProperty || !isInstanceProperty) {
2102 // Only required instance properties are currently supported
2103 return NULL;
2104 }
2105
2106 struct objc_property_list *plist;
2107 if ((plist = proto->instanceProperties)) {
2108 uint32_t i;
2109 for (i = 0; i < plist->count; i++) {
2110 Property prop = property_list_nth(plist, i);
2111 if (0 == strcmp(name, prop->name)) {
2112 return prop;
2113 }
2114 }
2115 }
2116
2117 if (proto->protocols) {
2118 uintptr_t i;
2119 for (i = 0; i < proto->protocols->count; i++) {
2120 Property prop =
2121 _protocol_getProperty_nolock(proto->protocols->list[i], name,
2122 isRequiredProperty,
2123 isInstanceProperty);
2124 if (prop) return prop;
2125 }
2126 }
2127
2128 return NULL;
2129 }
2130
2131 Property protocol_getProperty(Protocol *p, const char *name,
2132 BOOL isRequiredProperty, BOOL isInstanceProperty)
2133 {
2134 Property result;
2135
2136 if (!p || !name) return NULL;
2137
2138 OBJC_LOCK(&runtimeLock);
2139 result = _protocol_getProperty_nolock(newprotocol(p), name,
2140 isRequiredProperty,
2141 isInstanceProperty);
2142 OBJC_UNLOCK(&runtimeLock);
2143
2144 return result;
2145 }
2146
2147
2148 /***********************************************************************
2149 * protocol_copyPropertyList
2150 * fixme
2151 * Locking: acquires runtimeLock
2152 **********************************************************************/
2153 Property *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
2154 {
2155 Property *result = NULL;
2156
2157 if (!proto) {
2158 if (outCount) *outCount = 0;
2159 return NULL;
2160 }
2161
2162 OBJC_LOCK(&runtimeLock);
2163
2164 struct objc_property_list *plist = newprotocol(proto)->instanceProperties;
2165 result = copyPropertyList(plist, outCount);
2166
2167 OBJC_UNLOCK(&runtimeLock);
2168
2169 return result;
2170 }
2171
2172
2173 /***********************************************************************
2174 * protocol_copyProtocolList
2175 * Copies this protocol's incorporated protocols.
2176 * Does not copy those protocol's incorporated protocols in turn.
2177 * Locking: acquires runtimeLock
2178 **********************************************************************/
2179 Protocol **protocol_copyProtocolList(Protocol *p, unsigned int *outCount)
2180 {
2181 unsigned int count = 0;
2182 Protocol **result = NULL;
2183 protocol_t *proto = newprotocol(p);
2184
2185 if (!proto) {
2186 if (outCount) *outCount = 0;
2187 return NULL;
2188 }
2189
2190 OBJC_LOCK(&runtimeLock);
2191
2192 if (proto->protocols) {
2193 count = (unsigned int)proto->protocols->count;
2194 }
2195 if (count > 0) {
2196 result = malloc((count+1) * sizeof(Protocol *));
2197
2198 unsigned int i;
2199 for (i = 0; i < count; i++) {
2200 result[i] = (Protocol *)remapProtocol(proto->protocols->list[i]);
2201 }
2202 result[i] = NULL;
2203 }
2204
2205 OBJC_UNLOCK(&runtimeLock);
2206
2207 if (outCount) *outCount = count;
2208 return result;
2209 }
2210
2211
2212 /***********************************************************************
2213 * objc_getClassList
2214 * Returns pointers to all classes.
2215 * This requires all classes be realized, which is regretfully non-lazy.
2216 * Locking: acquires runtimeLock
2217 **********************************************************************/
2218 int
2219 objc_getClassList(Class *buffer, int bufferLen)
2220 {
2221 OBJC_LOCK(&runtimeLock);
2222
2223 int count;
2224 Class cls;
2225 const char *name;
2226 NXMapState state;
2227 NXMapTable *classes = realizedClasses();
2228 NXMapTable *unrealized = unrealizedClasses();
2229
2230 if (!buffer) {
2231 count = NXCountMapTable(classes) + NXCountMapTable(unrealized);
2232 OBJC_UNLOCK(&runtimeLock);
2233 return count;
2234 }
2235
2236 if (bufferLen > NXCountMapTable(classes) &&
2237 NXCountMapTable(unrealized) != 0)
2238 {
2239 // bummer
2240 realizeAllClasses();
2241 }
2242
2243 count = 0;
2244 state = NXInitMapState(classes);
2245 while (count < bufferLen &&
2246 NXNextMapState(classes, &state,
2247 (const void **)&name, (const void **)&cls))
2248 {
2249 buffer[count++] = (Class)cls;
2250 }
2251
2252 OBJC_UNLOCK(&runtimeLock);
2253
2254 return count;
2255 }
2256
2257
2258 /***********************************************************************
2259 * objc_copyProtocolList
2260 * Returns pointers to all protocols.
2261 * Locking: acquires runtimeLock
2262 **********************************************************************/
2263 Protocol **
2264 objc_copyProtocolList(unsigned int *outCount)
2265 {
2266 OBJC_LOCK(&runtimeLock);
2267
2268 int count, i;
2269 Protocol *proto;
2270 const char *name;
2271 NXMapState state;
2272 NXMapTable *protocol_map = protocols();
2273 Protocol **result;
2274
2275 count = NXCountMapTable(protocol_map);
2276 if (count == 0) {
2277 OBJC_UNLOCK(&runtimeLock);
2278 if (outCount) *outCount = 0;
2279 return NULL;
2280 }
2281
2282 result = calloc(1 + count, sizeof(Protocol *));
2283
2284 i = 0;
2285 state = NXInitMapState(protocol_map);
2286 while (NXNextMapState(protocol_map, &state,
2287 (const void **)&name, (const void **)&proto))
2288 {
2289 result[i++] = proto;
2290 }
2291
2292 result[i++] = NULL;
2293 assert(i == count+1);
2294
2295 OBJC_UNLOCK(&runtimeLock);
2296
2297 if (outCount) *outCount = count;
2298 return result;
2299 }
2300
2301
2302 /***********************************************************************
2303 * objc_getProtocol
2304 * Get a protocol by name, or return NULL
2305 * Locking: acquires runtimeLock
2306 **********************************************************************/
2307 Protocol *objc_getProtocol(const char *name)
2308 {
2309 OBJC_LOCK(&runtimeLock);
2310 Protocol *result = (Protocol *)NXMapGet(protocols(), name);
2311 OBJC_UNLOCK(&runtimeLock);
2312 return result;
2313 }
2314
2315
2316 /***********************************************************************
2317 * class_copyMethodList
2318 * fixme
2319 * Locking: acquires runtimeLock
2320 **********************************************************************/
2321 Method *
2322 class_copyMethodList(Class cls_gen, unsigned int *outCount)
2323 {
2324 struct class_t *cls = newcls(cls_gen);
2325 chained_method_list *mlist;
2326 unsigned int count = 0;
2327 Method *result = NULL;
2328
2329 if (!cls) {
2330 if (outCount) *outCount = 0;
2331 return NULL;
2332 }
2333
2334 OBJC_LOCK(&runtimeLock);
2335
2336 assert(isRealized(cls));
2337
2338 methodizeClass(cls);
2339
2340 for (mlist = cls->data->methods; mlist; mlist = mlist->next) {
2341 count += mlist->count;
2342 }
2343
2344 if (count > 0) {
2345 unsigned int m;
2346 result = malloc((count + 1) * sizeof(Method));
2347
2348 m = 0;
2349 for (mlist = cls->data->methods; mlist; mlist = mlist->next) {
2350 unsigned int i;
2351 for (i = 0; i < mlist->count; i++) {
2352 result[m++] = (Method)&mlist->list[i];
2353 }
2354 }
2355 result[m] = NULL;
2356 }
2357
2358 OBJC_UNLOCK(&runtimeLock);
2359
2360 if (outCount) *outCount = count;
2361 return result;
2362 }
2363
2364
2365 /***********************************************************************
2366 * class_copyIvarList
2367 * fixme
2368 * Locking: acquires runtimeLock
2369 **********************************************************************/
2370 Ivar *
2371 class_copyIvarList(Class cls_gen, unsigned int *outCount)
2372 {
2373 struct class_t *cls = newcls(cls_gen);
2374 const ivar_list_t *ivars;
2375 Ivar *result = NULL;
2376 unsigned int count = 0;
2377 unsigned int i;
2378
2379 if (!cls) {
2380 if (outCount) *outCount = 0;
2381 return NULL;
2382 }
2383
2384 OBJC_LOCK(&runtimeLock);
2385
2386 assert(isRealized(cls));
2387
2388 if ((ivars = cls->data->ro->ivars) && (count = ivars->count)) {
2389 result = malloc((count+1) * sizeof(Ivar));
2390
2391 for (i = 0; i < ivars->count; i++) {
2392 result[i] = (Ivar)ivar_list_nth(ivars, i);
2393 }
2394 result[i] = NULL;
2395 }
2396
2397 OBJC_UNLOCK(&runtimeLock);
2398
2399 if (outCount) *outCount = count;
2400 return result;
2401 }
2402
2403
2404 /***********************************************************************
2405 * class_copyPropertyList. Returns a heap block containing the
2406 * properties declared in the class, or NULL if the class
2407 * declares no properties. Caller must free the block.
2408 * Does not copy any superclass's properties.
2409 * Locking: acquires runtimeLock
2410 **********************************************************************/
2411 Property *
2412 class_copyPropertyList(Class cls_gen, unsigned int *outCount)
2413 {
2414 struct class_t *cls = newcls(cls_gen);
2415 chained_property_list *plist;
2416 unsigned int count = 0;
2417 Property *result = NULL;
2418
2419 if (!cls) {
2420 if (outCount) *outCount = 0;
2421 return NULL;
2422 }
2423
2424 OBJC_LOCK(&runtimeLock);
2425
2426 assert(isRealized(cls));
2427
2428 // Attach any categories because they may provide more properties
2429 methodizeClass(cls);
2430
2431 for (plist = cls->data->properties; plist; plist = plist->next) {
2432 count += plist->count;
2433 }
2434
2435 if (count > 0) {
2436 unsigned int p;
2437 result = malloc((count + 1) * sizeof(Property));
2438
2439 p = 0;
2440 for (plist = cls->data->properties; plist; plist = plist->next) {
2441 unsigned int i;
2442 for (i = 0; i < plist->count; i++) {
2443 result[p++] = (Property)&plist->list[i];
2444 }
2445 }
2446 result[p] = NULL;
2447 }
2448
2449 OBJC_UNLOCK(&runtimeLock);
2450
2451 if (outCount) *outCount = count;
2452 return result;
2453 }
2454
2455
2456 /***********************************************************************
2457 * _class_getLoadMethod
2458 * fixme
2459 * Called only from add_class_to_loadable_list.
2460 * Locking: runtimeLock must be held by the caller.
2461 **********************************************************************/
2462 __private_extern__ IMP
2463 _class_getLoadMethod(Class cls_gen)
2464 {
2465 OBJC_CHECK_LOCKED(&runtimeLock);
2466
2467 struct class_t *cls = newcls(cls_gen);
2468 const method_list_t *mlist;
2469 int i;
2470
2471 assert(isRealized(cls));
2472 assert(isRealized(cls->isa));
2473 assert(!isMethodized(cls));
2474 assert(!isMethodized(cls->isa));
2475 assert(!isMetaClass(cls));
2476 assert(isMetaClass(cls->isa));
2477
2478 mlist = cls->isa->data->ro->baseMethods;
2479 if (mlist) for (i = 0; i < mlist->count; i++) {
2480 method_t *m = method_list_nth(mlist, i);
2481 if (0 == strcmp((const char *)m->name, "load")) {
2482 return m->imp;
2483 }
2484 }
2485
2486 return NULL;
2487 }
2488
2489
2490 /***********************************************************************
2491 * _category_getName
2492 * Returns a category's name.
2493 * Locking: none
2494 **********************************************************************/
2495 __private_extern__ const char *
2496 _category_getName(Category cat)
2497 {
2498 return newcategory(cat)->name;
2499 }
2500
2501
2502 /***********************************************************************
2503 * _category_getClassName
2504 * Returns a category's class's name
2505 * Called only from add_category_to_loadable_list and
2506 * remove_category_from_loadable_list.
2507 * Locking: runtimeLock must be held by the caller
2508 **********************************************************************/
2509 __private_extern__ const char *
2510 _category_getClassName(Category cat)
2511 {
2512 OBJC_CHECK_LOCKED(&runtimeLock);
2513 // cat->cls may have been remapped
2514 return getName(remapClass(newcategory(cat)->cls));
2515 }
2516
2517
2518 /***********************************************************************
2519 * _category_getClass
2520 * Returns a category's class
2521 * Called only by call_category_loads.
2522 * Locking: none
2523 **********************************************************************/
2524 __private_extern__ Class
2525 _category_getClass(Category cat)
2526 {
2527 // cat->cls may have been remapped
2528 struct class_t *result = remapClass(newcategory(cat)->cls);
2529 assert(isRealized(result)); // ok for call_category_loads' usage
2530 return (Class)result;
2531 }
2532
2533
2534 /***********************************************************************
2535 * _category_getLoadMethod
2536 * fixme
2537 * Called only from add_category_to_loadable_list
2538 * Locking: runtimeLock must be held by the caller
2539 **********************************************************************/
2540 __private_extern__ IMP
2541 _category_getLoadMethod(Category cat)
2542 {
2543 OBJC_CHECK_LOCKED(&runtimeLock);
2544
2545 const method_list_t *mlist;
2546 int i;
2547
2548 mlist = newcategory(cat)->classMethods;
2549 if (mlist) for (i = 0; i < mlist->count; i++) {
2550 method_t *m = method_list_nth(mlist, i);
2551 if (0 == strcmp((const char *)m->name, "load")) {
2552 return m->imp;
2553 }
2554 }
2555
2556 return NULL;
2557 }
2558
2559
2560 /***********************************************************************
2561 * class_copyProtocolList
2562 * fixme
2563 * Locking: acquires runtimeLock
2564 **********************************************************************/
2565 Protocol **
2566 class_copyProtocolList(Class cls_gen, unsigned int *outCount)
2567 {
2568 struct class_t *cls = newcls(cls_gen);
2569 Protocol **r;
2570 struct protocol_list_t **p;
2571 unsigned int count = 0;
2572 unsigned int i;
2573 Protocol **result = NULL;
2574
2575 if (!cls) {
2576 if (outCount) *outCount = 0;
2577 return NULL;
2578 }
2579
2580 OBJC_LOCK(&runtimeLock);
2581
2582 assert(isRealized(cls));
2583
2584 // Attach any categories because they may provide more protocols
2585 methodizeClass(cls);
2586
2587 for (p = cls->data->protocols; p && *p; p++) {
2588 count += (uint32_t)(*p)->count;
2589 }
2590
2591 if (count) {
2592 result = malloc((count+1) * sizeof(Protocol *));
2593 r = result;
2594 for (p = cls->data->protocols; p && *p; p++) {
2595 for (i = 0; i < (*p)->count; i++) {
2596 *r++ = (Protocol *)remapProtocol((*p)->list[i]);
2597 }
2598 }
2599 *r++ = NULL;
2600 }
2601
2602 OBJC_UNLOCK(&runtimeLock);
2603
2604 if (outCount) *outCount = count;
2605 return result;
2606 }
2607
2608
2609 /***********************************************************************
2610 * _objc_copyClassNamesForImage
2611 * fixme
2612 * Locking: acquires runtimeLock
2613 **********************************************************************/
2614 __private_extern__ const char **
2615 _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount)
2616 {
2617 size_t count, i;
2618 class_t **classlist;
2619 const char **names;
2620
2621 OBJC_LOCK(&runtimeLock);
2622
2623 classlist = _getObjc2ClassList(hi, &count);
2624 names = malloc((count+1) * sizeof(const char *));
2625
2626 for (i = 0; i < count; i++) {
2627 names[i] = getName(classlist[i]);
2628 }
2629 names[count] = NULL;
2630
2631 OBJC_UNLOCK(&runtimeLock);
2632
2633 if (outCount) *outCount = (unsigned int)count;
2634 return names;
2635 }
2636
2637
2638 /***********************************************************************
2639 * _class_getCache
2640 * fixme
2641 * Locking: none
2642 **********************************************************************/
2643 __private_extern__ Cache
2644 _class_getCache(Class cls)
2645 {
2646 return newcls(cls)->cache;
2647 }
2648
2649
2650 /***********************************************************************
2651 * _class_getInstanceSize
2652 * fixme
2653 * Locking: none
2654 **********************************************************************/
2655 __private_extern__ size_t
2656 _class_getInstanceSize(Class cls)
2657 {
2658 if (!cls) return 0;
2659 return instanceSize(newcls(cls));
2660 }
2661
2662 static uint32_t
2663 instanceSize(struct class_t *cls)
2664 {
2665 assert(cls);
2666 assert(isRealized(cls));
2667 // fixme rdar://5244378
2668 return (uint32_t)((cls->data->ro->instanceSize + 7) & ~7UL);
2669 }
2670
2671
2672 /***********************************************************************
2673 * class_getVersion
2674 * fixme
2675 * Locking: none
2676 **********************************************************************/
2677 int
2678 class_getVersion(Class cls)
2679 {
2680 if (!cls) return 0;
2681 assert(isRealized(newcls(cls)));
2682 return newcls(cls)->data->version;
2683 }
2684
2685
2686 /***********************************************************************
2687 * _class_setCache
2688 * fixme
2689 * Locking: none
2690 **********************************************************************/
2691 __private_extern__ void
2692 _class_setCache(Class cls, Cache cache)
2693 {
2694 newcls(cls)->cache = cache;
2695 }
2696
2697
2698 /***********************************************************************
2699 * class_setVersion
2700 * fixme
2701 * Locking: none
2702 **********************************************************************/
2703 void
2704 class_setVersion(Class cls, int version)
2705 {
2706 if (!cls) return;
2707 assert(isRealized(newcls(cls)));
2708 newcls(cls)->data->version = version;
2709 }
2710
2711
2712 /***********************************************************************
2713 * _class_getName
2714 * fixme
2715 * Locking: acquires runtimeLock
2716 **********************************************************************/
2717 __private_extern__ const char *_class_getName(Class cls)
2718 {
2719 if (!cls) return "nil";
2720 // fixme hack OBJC_LOCK(&runtimeLock);
2721 const char *name = getName(newcls(cls));
2722 // OBJC_UNLOCK(&runtimeLock);
2723 return name;
2724 }
2725
2726
2727 /***********************************************************************
2728 * getName
2729 * fixme
2730 * Locking: runtimeLock must be held by the caller
2731 **********************************************************************/
2732 static const char *
2733 getName(struct class_t *cls)
2734 {
2735 // fixme hack OBJC_CHECK_LOCKED(&runtimeLock);
2736 assert(cls);
2737
2738 if (isRealized(cls)) {
2739 return cls->data->ro->name;
2740 } else {
2741 return ((const struct class_ro_t *)cls->data)->name;
2742 }
2743 }
2744
2745
2746 /***********************************************************************
2747 * _class_getMethodNoSuper_nolock
2748 * fixme
2749 * Locking: runtimeLock must be held by the caller
2750 **********************************************************************/
2751 static Method
2752 _class_getMethodNoSuper_nolock(struct class_t *cls, SEL sel)
2753 {
2754 OBJC_CHECK_LOCKED(&runtimeLock);
2755
2756 chained_method_list *mlist;
2757 uint32_t i;
2758
2759 assert(isRealized(cls));
2760 // fixme nil cls?
2761 // fixme NULL sel?
2762
2763 methodizeClass(cls);
2764
2765 for (mlist = cls->data->methods; mlist; mlist = mlist->next) {
2766 for (i = 0; i < mlist->count; i++) {
2767 method_t *m = &mlist->list[i];
2768 if (m->name == sel) return (Method)m;
2769 }
2770 }
2771
2772 return NULL;
2773 }
2774
2775
2776 /***********************************************************************
2777 * _class_getMethodNoSuper
2778 * fixme
2779 * Locking: acquires runtimeLock
2780 **********************************************************************/
2781 __private_extern__ Method
2782 _class_getMethodNoSuper(Class cls, SEL sel)
2783 {
2784 OBJC_LOCK(&runtimeLock);
2785 Method result = _class_getMethodNoSuper_nolock(newcls(cls), sel);
2786 OBJC_UNLOCK(&runtimeLock);
2787 return result;
2788 }
2789
2790
2791 /***********************************************************************
2792 * _class_getMethod
2793 * fixme
2794 * Locking: acquires runtimeLock
2795 **********************************************************************/
2796 __private_extern__ Method _class_getMethod(Class cls, SEL sel)
2797 {
2798 Method m = NULL;
2799
2800 // fixme nil cls?
2801 // fixme NULL sel?
2802
2803 assert(isRealized(newcls(cls)));
2804
2805 while (cls && ((m = _class_getMethodNoSuper(cls, sel))) == NULL) {
2806 cls = class_getSuperclass(cls);
2807 }
2808
2809 return m;
2810 }
2811
2812
2813 /***********************************************************************
2814 * class_getProperty
2815 * fixme
2816 * Locking: acquires runtimeLock
2817 **********************************************************************/
2818 Property class_getProperty(Class cls_gen, const char *name)
2819 {
2820 Property result = NULL;
2821 chained_property_list *plist;
2822 struct class_t *cls = newcls(cls_gen);
2823
2824 if (!cls || !name) return NULL;
2825
2826 OBJC_LOCK(&runtimeLock);
2827
2828 assert(isRealized(cls));
2829 methodizeClass(cls);
2830
2831 for ( ; cls; cls = getSuperclass(cls)) {
2832 for (plist = cls->data->properties; plist; plist = plist->next) {
2833 uint32_t i;
2834 for (i = 0; i < plist->count; i++) {
2835 if (0 == strcmp(name, plist->list[i].name)) {
2836 result = &plist->list[i];
2837 goto done;
2838 }
2839 }
2840 }
2841 }
2842
2843 done:
2844 OBJC_UNLOCK(&runtimeLock);
2845
2846 return result;
2847 }
2848
2849
2850 /***********************************************************************
2851 * Locking: fixme
2852 **********************************************************************/
2853 __private_extern__ BOOL _class_isMetaClass(Class cls)
2854 {
2855 if (!cls) return NO;
2856 return isMetaClass(newcls(cls));
2857 }
2858
2859 static BOOL
2860 isMetaClass(struct class_t *cls)
2861 {
2862 assert(cls);
2863 assert(isRealized(cls));
2864 return (cls->data->ro->flags & RO_META) ? YES : NO;
2865 }
2866
2867
2868 __private_extern__ Class _class_getMeta(Class cls)
2869 {
2870 assert(cls);
2871 if (isMetaClass(newcls(cls))) return cls;
2872 else return ((id)cls)->isa;
2873 }
2874
2875
2876 /***********************************************************************
2877 * Locking: fixme
2878 **********************************************************************/
2879 __private_extern__ BOOL
2880 _class_isInitializing(Class cls_gen)
2881 {
2882 struct class_t *cls = newcls(_class_getMeta(cls_gen));
2883 return (cls->data->flags & RW_INITIALIZING) ? YES : NO;
2884 }
2885
2886
2887 /***********************************************************************
2888 * Locking: fixme
2889 **********************************************************************/
2890 __private_extern__ BOOL
2891 _class_isInitialized(Class cls_gen)
2892 {
2893 struct class_t *cls = newcls(_class_getMeta(cls_gen));
2894 return (cls->data->flags & RW_INITIALIZED) ? YES : NO;
2895 }
2896
2897
2898 /***********************************************************************
2899 * Locking: fixme
2900 **********************************************************************/
2901 __private_extern__ void
2902 _class_setInitializing(Class cls_gen)
2903 {
2904 struct class_t *cls = newcls(_class_getMeta(cls_gen));
2905 changeInfo(cls, RW_INITIALIZING, 0);
2906 }
2907
2908
2909 /***********************************************************************
2910 * Locking: fixme
2911 **********************************************************************/
2912 __private_extern__ void
2913 _class_setInitialized(Class cls_gen)
2914 {
2915 struct class_t *cls = newcls(_class_getMeta(cls_gen));
2916 changeInfo(cls, RW_INITIALIZED, RW_INITIALIZING);
2917 }
2918
2919
2920 /***********************************************************************
2921 * Locking: fixme
2922 **********************************************************************/
2923 __private_extern__ BOOL
2924 _class_shouldGrowCache(Class cls)
2925 {
2926 return YES; // fixme good or bad for memory use?
2927 }
2928
2929
2930 /***********************************************************************
2931 * Locking: fixme
2932 **********************************************************************/
2933 __private_extern__ void
2934 _class_setGrowCache(Class cls, BOOL grow)
2935 {
2936 // fixme good or bad for memory use?
2937 }
2938
2939
2940 /***********************************************************************
2941 * _class_isLoadable
2942 * fixme
2943 * Locking: none
2944 **********************************************************************/
2945 __private_extern__ BOOL
2946 _class_isLoadable(Class cls)
2947 {
2948 assert(isRealized(newcls(cls)));
2949 return YES; // any class registered for +load is definitely loadable
2950 }
2951
2952
2953 /***********************************************************************
2954 * Locking: fixme
2955 **********************************************************************/
2956 __private_extern__ BOOL
2957 _class_hasCxxStructorsNoSuper(Class cls)
2958 {
2959 assert(isRealized(newcls(cls)));
2960 return (newcls(cls)->data->ro->flags & RO_HAS_CXX_STRUCTORS) ? YES : NO;
2961 }
2962
2963
2964 /***********************************************************************
2965 * Locking: fixme
2966 **********************************************************************/
2967 __private_extern__ BOOL
2968 _class_shouldFinalizeOnMainThread(Class cls)
2969 {
2970 assert(isRealized(newcls(cls)));
2971 return (newcls(cls)->data->flags & RW_FINALIZE_ON_MAIN_THREAD) ? YES : NO;
2972 }
2973
2974
2975 /***********************************************************************
2976 * Locking: fixme
2977 **********************************************************************/
2978 __private_extern__ void
2979 _class_setFinalizeOnMainThread(Class cls)
2980 {
2981 assert(isRealized(newcls(cls)));
2982 changeInfo(newcls(cls), RW_FINALIZE_ON_MAIN_THREAD, 0);
2983 }
2984
2985
2986 /***********************************************************************
2987 * Locking: none
2988 * fixme assert realized to get superclass remapping?
2989 **********************************************************************/
2990 __private_extern__ Class
2991 _class_getSuperclass(Class cls)
2992 {
2993 return (Class)getSuperclass(newcls(cls));
2994 }
2995
2996 static struct class_t *
2997 getSuperclass(struct class_t *cls)
2998 {
2999 if (!cls) return NULL;
3000 return cls->superclass;
3001 }
3002
3003
3004 /***********************************************************************
3005 * class_getIvarLayout
3006 * Called by the garbage collector.
3007 * The class must be NULL or already realized.
3008 * Locking: none
3009 **********************************************************************/
3010 const char *
3011 class_getIvarLayout(Class cls_gen)
3012 {
3013 class_t *cls = newcls(cls_gen);
3014 if (cls) return (const char *)cls->data->ro->ivarLayout;
3015 else return NULL;
3016 }
3017
3018
3019 /***********************************************************************
3020 * class_getWeakIvarLayout
3021 * Called by the garbage collector.
3022 * The class must be NULL or already realized.
3023 * Locking: none
3024 **********************************************************************/
3025 const char *
3026 class_getWeakIvarLayout(Class cls_gen)
3027 {
3028 class_t *cls = newcls(cls_gen);
3029 if (cls) return (const char *)cls->data->ro->weakIvarLayout;
3030 else return NULL;
3031 }
3032
3033
3034 /***********************************************************************
3035 * class_setIvarLayout
3036 * Changes the class's GC scan layout.
3037 * NULL layout means no unscanned ivars
3038 * The class must be under construction.
3039 * fixme: sanity-check layout vs instance size?
3040 * fixme: sanity-check layout vs superclass?
3041 * Locking: acquires runtimeLock
3042 **********************************************************************/
3043 void
3044 class_setIvarLayout(Class cls_gen, const char *layout)
3045 {
3046 class_t *cls = newcls(cls_gen);
3047 if (!cls) return;
3048
3049 OBJC_LOCK(&runtimeLock);
3050
3051 // Can only change layout of in-construction classes.
3052 // note: if modifications to post-construction classes were
3053 // allowed, there would be a race below (us vs. concurrent GC scan)
3054 if (!(cls->data->flags & RW_CONSTRUCTING)) {
3055 _objc_inform("*** Can't set ivar layout for already-registered "
3056 "class '%s'", getName(cls));
3057 OBJC_UNLOCK(&runtimeLock);
3058 return;
3059 }
3060
3061 class_ro_t *ro_w = make_ro_writeable(cls->data);
3062
3063 try_free(ro_w->ivarLayout);
3064 ro_w->ivarLayout = (unsigned char *)_strdup_internal(layout);
3065
3066 OBJC_UNLOCK(&runtimeLock);
3067 }
3068
3069
3070 /***********************************************************************
3071 * class_setWeakIvarLayout
3072 * Changes the class's GC weak layout.
3073 * NULL layout means no weak ivars
3074 * The class must be under construction.
3075 * fixme: sanity-check layout vs instance size?
3076 * fixme: sanity-check layout vs superclass?
3077 * Locking: acquires runtimeLock
3078 **********************************************************************/
3079 void
3080 class_setWeakIvarLayout(Class cls_gen, const char *layout)
3081 {
3082 class_t *cls = newcls(cls_gen);
3083 if (!cls) return;
3084
3085 OBJC_LOCK(&runtimeLock);
3086
3087 // Can only change layout of in-construction classes.
3088 // note: if modifications to post-construction classes were
3089 // allowed, there would be a race below (us vs. concurrent GC scan)
3090 if (!(cls->data->flags & RW_CONSTRUCTING)) {
3091 _objc_inform("*** Can't set weak ivar layout for already-registered "
3092 "class '%s'", getName(cls));
3093 OBJC_UNLOCK(&runtimeLock);
3094 return;
3095 }
3096
3097 class_ro_t *ro_w = make_ro_writeable(cls->data);
3098
3099 try_free(ro_w->weakIvarLayout);
3100 ro_w->weakIvarLayout = (unsigned char *)_strdup_internal(layout);
3101
3102 OBJC_UNLOCK(&runtimeLock);
3103 }
3104
3105
3106 /***********************************************************************
3107 * _class_getVariable
3108 * fixme
3109 * Locking: acquires runtimeLock
3110 **********************************************************************/
3111 __private_extern__ Ivar
3112 _class_getVariable(Class cls, const char *name)
3113 {
3114 OBJC_LOCK(&runtimeLock);
3115
3116 for ( ; cls != Nil; cls = class_getSuperclass(cls)) {
3117 struct ivar_t *ivar = getIvar(newcls(cls), name);
3118 if (ivar) {
3119 OBJC_UNLOCK(&runtimeLock);
3120 return (Ivar)ivar;
3121 }
3122 }
3123
3124 OBJC_UNLOCK(&runtimeLock);
3125
3126 return NULL;
3127 }
3128
3129
3130 /***********************************************************************
3131 * class_conformsToProtocol
3132 * fixme
3133 * Locking: acquires runtimeLock
3134 **********************************************************************/
3135 BOOL class_conformsToProtocol(Class cls_gen, Protocol *proto)
3136 {
3137 Protocol **protocols;
3138 unsigned int count, i;
3139 BOOL result = NO;
3140
3141 // fixme null cls?
3142
3143 protocols = class_copyProtocolList(cls_gen, &count);
3144
3145 for (i = 0; i < count; i++) {
3146 if (protocols[i] == proto ||
3147 protocol_conformsToProtocol(protocols[i], proto))
3148 {
3149 result = YES;
3150 break;
3151 }
3152 }
3153
3154 if (protocols) free(protocols);
3155
3156 return result;
3157 }
3158
3159
3160 /***********************************************************************
3161 * class_addMethod
3162 * fixme
3163 * Locking: acquires runtimeLock
3164 **********************************************************************/
3165 static IMP
3166 _class_addMethod(Class cls_gen, SEL name, IMP imp,
3167 const char *types, BOOL replace)
3168 {
3169 struct class_t *cls = newcls(cls_gen);
3170 IMP result = NULL;
3171
3172 if (!types) types = "";
3173
3174 OBJC_LOCK(&runtimeLock);
3175
3176 assert(isRealized(cls));
3177 // methodizeClass(cls); _class_getMethodNoSuper() does this below
3178
3179 Method m;
3180 if ((m = _class_getMethodNoSuper_nolock(cls, name))) {
3181 // already exists
3182 // fixme atomic
3183 result = method_getImplementation(m);
3184 if (replace) {
3185 method_setImplementation(m, imp);
3186 }
3187 } else {
3188 // fixme optimize
3189 chained_method_list *newlist;
3190 newlist = _calloc_internal(sizeof(*newlist) + sizeof(method_t), 1);
3191 newlist->count = 1;
3192 newlist->list[0].name = name;
3193 newlist->list[0].types = strdup(types);
3194 newlist->list[0].imp = imp;
3195
3196 newlist->next = cls->data->methods;
3197 cls->data->methods = newlist;
3198 flushCaches(cls);
3199 result = NULL;
3200 }
3201
3202 OBJC_UNLOCK(&runtimeLock);
3203
3204 return result;
3205 }
3206
3207
3208 BOOL
3209 class_addMethod(Class cls, SEL name, IMP imp, const char *types)
3210 {
3211 if (!cls) return NO;
3212
3213 IMP old = _class_addMethod(cls, name, imp, types, NO);
3214 return old ? NO : YES;
3215 }
3216
3217
3218 IMP
3219 class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
3220 {
3221 if (!cls) return NULL;
3222
3223 return _class_addMethod(cls, name, imp, types, YES);
3224 }
3225
3226
3227 /***********************************************************************
3228 * class_addIvar
3229 * Adds an ivar to a class.
3230 * Locking: acquires runtimeLock
3231 **********************************************************************/
3232 BOOL
3233 class_addIvar(Class cls_gen, const char *name, size_t size,
3234 uint8_t alignment, const char *type)
3235 {
3236 struct class_t *cls = newcls(cls_gen);
3237
3238 if (!cls) return NO;
3239
3240 if (!type) type = "";
3241 if (name && 0 == strcmp(name, "")) name = NULL;
3242
3243 OBJC_LOCK(&runtimeLock);
3244
3245 assert(isRealized(cls));
3246
3247 // No class variables
3248 if (isMetaClass(cls)) {
3249 OBJC_UNLOCK(&runtimeLock);
3250 return NO;
3251 }
3252
3253 // Can only add ivars to in-construction classes.
3254 if (!(cls->data->flags & RW_CONSTRUCTING)) {
3255 OBJC_UNLOCK(&runtimeLock);
3256 return NO;
3257 }
3258
3259 // Check for existing ivar with this name, unless it's anonymous.
3260 // Check for too-big ivar.
3261 // fixme check for superclass ivar too?
3262 if ((name && getIvar(cls, name)) || size > UINT32_MAX) {
3263 OBJC_UNLOCK(&runtimeLock);
3264 return NO;
3265 }
3266
3267 class_ro_t *ro_w = make_ro_writeable(cls->data);
3268
3269 // fixme allocate less memory here
3270
3271 ivar_list_t *oldlist, *newlist;
3272 if ((oldlist = (ivar_list_t *)cls->data->ro->ivars)) {
3273 size_t oldsize = ivar_list_size(oldlist);
3274 newlist = _calloc_internal(oldsize + oldlist->entsize, 1);
3275 memcpy(newlist, oldlist, oldsize);
3276 _free_internal(oldlist);
3277 } else {
3278 newlist = _calloc_internal(sizeof(ivar_list_t), 1);
3279 newlist->entsize = (uint32_t)sizeof(ivar_t);
3280 }
3281
3282 uint32_t offset = instanceSize(cls);
3283 uint32_t alignMask = (1<<alignment)-1;
3284 offset = (offset + alignMask) & ~alignMask;
3285
3286 ivar_t *ivar = ivar_list_nth(newlist, newlist->count++);
3287 ivar->offset = _malloc_internal(sizeof(*ivar->offset));
3288 *ivar->offset = offset;
3289 ivar->name = name ? _strdup_internal(name) : NULL;
3290 ivar->type = _strdup_internal(type);
3291 ivar->alignment = alignment;
3292 ivar->size = (uint32_t)size;
3293
3294 ro_w->ivars = newlist;
3295 ro_w->instanceSize = (uint32_t)(offset + size);
3296
3297 // Ivar layout updated in registerClass.
3298
3299 OBJC_UNLOCK(&runtimeLock);
3300
3301 return YES;
3302 }
3303
3304
3305 /***********************************************************************
3306 * class_addProtocol
3307 * Adds a protocol to a class.
3308 * Locking: acquires runtimeLock
3309 **********************************************************************/
3310 BOOL class_addProtocol(Class cls_gen, Protocol *protocol_gen)
3311 {
3312 class_t *cls = newcls(cls_gen);
3313 protocol_t *protocol = newprotocol(protocol_gen);
3314 protocol_list_t *plist;
3315 protocol_list_t **plistp;
3316
3317 if (!cls) return NO;
3318 if (class_conformsToProtocol(cls_gen, protocol_gen)) return NO;
3319
3320 OBJC_LOCK(&runtimeLock);
3321
3322 assert(isRealized(cls));
3323
3324 // fixme optimize
3325 plist = _malloc_internal(sizeof(protocol_list_t) + sizeof(protocol_t *));
3326 plist->count = 1;
3327 plist->list[0] = protocol;
3328
3329 unsigned int count = 0;
3330 for (plistp = cls->data->protocols; plistp && *plistp; plistp++) {
3331 count++;
3332 }
3333
3334 cls->data->protocols =
3335 _realloc_internal(cls->data->protocols,
3336 (count+2) * sizeof(protocol_list_t *));
3337 cls->data->protocols[count] = plist;
3338 cls->data->protocols[count+1] = NULL;
3339
3340 // fixme metaclass?
3341
3342 OBJC_UNLOCK(&runtimeLock);
3343
3344 return YES;
3345 }
3346
3347
3348 /***********************************************************************
3349 * look_up_class
3350 * Look up a class by name, and realize it.
3351 * Locking: acquires runtimeLock
3352 * GrP fixme zerolink needs class handler for objc_getClass
3353 **********************************************************************/
3354 __private_extern__ id
3355 look_up_class(const char *name,
3356 BOOL includeUnconnected __attribute__((unused)),
3357 BOOL includeClassHandler __attribute__((unused)))
3358 {
3359 if (!name) return nil;
3360
3361 OBJC_LOCK(&runtimeLock);
3362 id result = (id)getClass(name);
3363 realizeClass(result);
3364 OBJC_UNLOCK(&runtimeLock);
3365 return result;
3366 }
3367
3368
3369 /***********************************************************************
3370 * objc_duplicateClass
3371 * fixme
3372 * Locking: acquires runtimeLock
3373 **********************************************************************/
3374 Class
3375 objc_duplicateClass(Class original_gen, const char *name,
3376 size_t extraBytes)
3377 {
3378 struct class_t *original = newcls(original_gen);
3379 chained_method_list **m;
3380 struct class_t *duplicate;
3381
3382 OBJC_LOCK(&runtimeLock);
3383
3384 assert(isRealized(original));
3385 methodizeClass(original);
3386 assert(!isMetaClass(original));
3387
3388 duplicate = (struct class_t *)
3389 calloc(instanceSize(original->isa) + extraBytes, 1);
3390 if (instanceSize(original->isa) < sizeof(class_t)) {
3391 _objc_inform("busted! %s\n", original->data->ro->name);
3392 }
3393
3394
3395 duplicate->isa = original->isa;
3396 duplicate->superclass = original->superclass;
3397 duplicate->cache = (Cache)&_objc_empty_cache;
3398 #warning GrP fixme vtable
3399 // duplicate->vtable = (IMP *)&_objc_empty_vtable;
3400
3401 duplicate->data = _calloc_internal(sizeof(*original->data), 1);
3402 duplicate->data->flags = original->data->flags | RW_COPIED_RO;
3403 duplicate->data->version = original->data->version;
3404 duplicate->data->firstSubclass = NULL;
3405 duplicate->data->nextSiblingClass = NULL;
3406
3407 duplicate->data->ro =
3408 _memdup_internal(original->data->ro, sizeof(*original->data->ro));
3409 *(char **)&duplicate->data->ro->name = _strdup_internal(name);
3410
3411 duplicate->data->methods = original->data->methods;
3412 for (m = &duplicate->data->methods; *m != NULL; m = &(*m)->next) {
3413 *m = _memdup_internal(*m, chained_mlist_size(*m));
3414 }
3415
3416 // fixme dies when categories are added to the base
3417 duplicate->data->properties = original->data->properties;
3418 duplicate->data->protocols = original->data->protocols;
3419
3420 if (duplicate->superclass) {
3421 addSubclass(duplicate->superclass, duplicate);
3422 }
3423
3424 addRealizedClass(duplicate, duplicate->data->ro->name);
3425
3426 if (PrintConnecting) {
3427 _objc_inform("CLASS: realizing class '%s' (duplicate of %s) %p %p",
3428 name, original->data->ro->name,
3429 duplicate, duplicate->data->ro);
3430 }
3431
3432 OBJC_UNLOCK(&runtimeLock);
3433
3434 return (Class)duplicate;
3435 }
3436
3437
3438 /***********************************************************************
3439 * objc_allocateClassPair
3440 * fixme
3441 * Locking: acquires runtimeLock
3442 **********************************************************************/
3443 Class objc_allocateClassPair(Class superclass_gen, const char *name,
3444 size_t extraBytes)
3445 {
3446 class_t *superclass = newcls(superclass_gen);
3447 class_t *cls, *meta;
3448 class_ro_t *cls_ro_w, *meta_ro_w;
3449
3450 OBJC_LOCK(&runtimeLock);
3451
3452 if (getClass(name)) {
3453 OBJC_UNLOCK(&runtimeLock);
3454 return NO;
3455 }
3456 // fixme reserve class against simmultaneous allocation
3457
3458 if (superclass) assert(isRealized(superclass));
3459
3460 if (superclass && superclass->data->flags & RW_CONSTRUCTING) {
3461 // Can't make subclass of an in-construction class
3462 OBJC_UNLOCK(&runtimeLock);
3463 return NO;
3464 }
3465
3466 // Allocate new classes.
3467 if (superclass) {
3468 cls = _calloc_internal(instanceSize(superclass->isa) + extraBytes, 1);
3469 meta = _calloc_internal(instanceSize(superclass->isa->isa) + extraBytes, 1);
3470 } else {
3471 cls = _calloc_internal(sizeof(class_t) + extraBytes, 1);
3472 meta = _calloc_internal(sizeof(class_t) + extraBytes, 1);
3473 }
3474
3475 cls->data = _calloc_internal(sizeof(class_rw_t), 1);
3476 meta->data = _calloc_internal(sizeof(class_rw_t), 1);
3477 cls_ro_w = _calloc_internal(sizeof(class_ro_t), 1);
3478 meta_ro_w = _calloc_internal(sizeof(class_ro_t), 1);
3479 cls->data->ro = cls_ro_w;
3480 meta->data->ro = meta_ro_w;
3481
3482 // Set basic info
3483 cls->cache = (Cache)&_objc_empty_cache;
3484 meta->cache = (Cache)&_objc_empty_cache;
3485 cls->vtable = (IMP *)&_objc_empty_vtable;
3486 meta->vtable = (IMP *)&_objc_empty_vtable;
3487
3488 cls->data->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED;
3489 meta->data->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED;
3490 cls->data->version = 0;
3491 meta->data->version = 7;
3492
3493 cls_ro_w->flags = 0;
3494 meta_ro_w->flags = RO_META;
3495 if (!superclass) {
3496 cls_ro_w->flags |= RO_ROOT;
3497 meta_ro_w->flags |= RO_ROOT;
3498 }
3499 if (superclass) {
3500 cls_ro_w->instanceStart = instanceSize(superclass);
3501 meta_ro_w->instanceStart = instanceSize(superclass->isa);
3502 cls_ro_w->instanceSize = cls_ro_w->instanceStart;
3503 meta_ro_w->instanceSize = meta_ro_w->instanceStart;
3504 } else {
3505 cls_ro_w->instanceStart = 0;
3506 meta_ro_w->instanceStart = (uint32_t)sizeof(class_t);
3507 cls_ro_w->instanceSize = (uint32_t)sizeof(id); // just an isa
3508 meta_ro_w->instanceSize = meta_ro_w->instanceStart;
3509 }
3510
3511 cls_ro_w->name = _strdup_internal(name);
3512 meta_ro_w->name = _strdup_internal(name);
3513
3514 // Connect to superclasses and metaclasses
3515 cls->isa = meta;
3516 if (superclass) {
3517 meta->isa = superclass->isa->isa;
3518 cls->superclass = superclass;
3519 meta->superclass = superclass->isa;
3520 addSubclass(superclass, cls);
3521 addSubclass(superclass->isa, meta);
3522 } else {
3523 meta->isa = meta;
3524 cls->superclass = Nil;
3525 meta->superclass = cls;
3526 addSubclass(cls, meta);
3527 }
3528
3529 OBJC_UNLOCK(&runtimeLock);
3530
3531 return (Class)cls;
3532 }
3533
3534
3535 /***********************************************************************
3536 * objc_registerClassPair
3537 * fixme
3538 * Locking: acquires runtimeLock
3539 **********************************************************************/
3540 void objc_registerClassPair(Class cls_gen)
3541 {
3542 class_t *cls = newcls(cls_gen);
3543
3544 OBJC_LOCK(&runtimeLock);
3545
3546 if ((cls->data->flags & RW_CONSTRUCTED) ||
3547 (cls->isa->data->flags & RW_CONSTRUCTED))
3548 {
3549 _objc_inform("objc_registerClassPair: class '%s' was already "
3550 "registered!", cls->data->ro->name);
3551 OBJC_UNLOCK(&runtimeLock);
3552 return;
3553 }
3554
3555 if (!(cls->data->flags & RW_CONSTRUCTING) ||
3556 !(cls->isa->data->flags & RW_CONSTRUCTING))
3557 {
3558 _objc_inform("objc_registerClassPair: class '%s' was not "
3559 "allocated with objc_allocateClassPair!",
3560 cls->data->ro->name);
3561 OBJC_UNLOCK(&runtimeLock);
3562 return;
3563 }
3564
3565 // Build ivar layouts
3566 if (UseGC) {
3567 struct class_t *supercls = getSuperclass(cls);
3568 class_ro_t *ro_w = (class_ro_t *)cls->data->ro;
3569
3570 if (ro_w->ivarLayout) {
3571 // Class builder already called class_setIvarLayout.
3572 }
3573 else if (!supercls) {
3574 // Root class. Scan conservatively (should be isa ivar only).
3575 // ivar_layout is already NULL.
3576 }
3577 else if (ro_w->ivars == NULL) {
3578 // No local ivars. Use superclass's layouts.
3579 ro_w->ivarLayout = (unsigned char *)
3580 _strdup_internal((char *)supercls->data->ro->ivarLayout);
3581 }
3582 else {
3583 // Has local ivars. Build layouts based on superclass.
3584 layout_bitmap bitmap =
3585 layout_bitmap_create(supercls->data->ro->ivarLayout,
3586 instanceSize(supercls),
3587 instanceSize(cls), NO);
3588 uint32_t i;
3589 for (i = 0; i < ro_w->ivars->count; i++) {
3590 ivar_t *iv = ivar_list_nth(ro_w->ivars, i);
3591 layout_bitmap_set_ivar(bitmap, iv->type, *iv->offset);
3592 }
3593 ro_w->ivarLayout = layout_string_create(bitmap);
3594 layout_bitmap_free(bitmap);
3595 }
3596
3597 if (ro_w->weakIvarLayout) {
3598 // Class builder already called class_setWeakIvarLayout.
3599 }
3600 else if (!supercls) {
3601 // Root class. No weak ivars (should be isa ivar only).
3602 // weak_ivar_layout is already NULL.
3603 }
3604 else if (ro_w->ivars == NULL) {
3605 // No local ivars. Use superclass's layout.
3606 ro_w->weakIvarLayout = (unsigned char *)
3607 _strdup_internal((char *)supercls->data->ro->weakIvarLayout);
3608 }
3609 else {
3610 // Has local ivars. Build layout based on superclass.
3611 // No way to add weak ivars yet.
3612 ro_w->weakIvarLayout = (unsigned char *)
3613 _strdup_internal((char *)supercls->data->ro->weakIvarLayout);
3614 }
3615 }
3616
3617 // Clear "under construction" bit, set "done constructing" bit
3618 cls->data->flags &= ~RW_CONSTRUCTING;
3619 cls->isa->data->flags &= ~RW_CONSTRUCTING;
3620 cls->data->flags |= RW_CONSTRUCTED;
3621 cls->isa->data->flags |= RW_CONSTRUCTED;
3622
3623 // Add to realized and uninitialized classes
3624 addRealizedClass(cls, cls->data->ro->name);
3625 addUninitializedClass(cls, cls->isa);
3626
3627 OBJC_UNLOCK(&runtimeLock);
3628 }
3629
3630
3631 static void unload_class(class_t *cls)
3632 {
3633 uint32_t i;
3634
3635 chained_method_list *mlist = cls->data->methods;
3636 while (mlist) {
3637 chained_method_list *dead = mlist;
3638 mlist = mlist->next;
3639 for (i = 0; i < dead->count; i++) {
3640 try_free(dead->list[i].types);
3641 }
3642 try_free(dead);
3643 }
3644
3645 const ivar_list_t *ilist = cls->data->ro->ivars;
3646 if (ilist) {
3647 for (i = 0; i < ilist->count; i++) {
3648 const ivar_t *ivar = ivar_list_nth(ilist, i);
3649 try_free(ivar->offset);
3650 try_free(ivar->name);
3651 try_free(ivar->type);
3652 }
3653 try_free(ilist);
3654 }
3655
3656 protocol_list_t **plistp = cls->data->protocols;
3657 for (plistp = cls->data->protocols; plistp && *plistp; plistp++) {
3658 try_free(*plistp);
3659 }
3660 try_free(cls->data->protocols);
3661
3662 // fixme:
3663 // properties
3664
3665 try_free(cls->data->ro->ivarLayout);
3666 try_free(cls->data->ro->weakIvarLayout);
3667 try_free(cls->data->ro->name);
3668 try_free(cls->data->ro);
3669 try_free(cls->data);
3670 if (cls->cache != (Cache)&_objc_empty_cache) _cache_free(cls->cache);
3671 try_free(cls);
3672 }
3673
3674 void objc_disposeClassPair(Class cls_gen)
3675 {
3676 class_t *cls = newcls(cls_gen);
3677
3678 OBJC_LOCK(&runtimeLock);
3679
3680 if (!(cls->data->flags & (RW_CONSTRUCTED|RW_CONSTRUCTING)) ||
3681 !(cls->isa->data->flags & (RW_CONSTRUCTED|RW_CONSTRUCTING)))
3682 {
3683 // class not allocated with objc_allocateClassPair
3684 // disposing still-unregistered class is OK!
3685 _objc_inform("objc_disposeClassPair: class '%s' was not "
3686 "allocated with objc_allocateClassPair!",
3687 cls->data->ro->name);
3688 OBJC_UNLOCK(&runtimeLock);
3689 return;
3690 }
3691
3692 if (isMetaClass(cls)) {
3693 _objc_inform("objc_disposeClassPair: class '%s' is a metaclass, "
3694 "not a class!", cls->data->ro->name);
3695 OBJC_UNLOCK(&runtimeLock);
3696 return;
3697 }
3698
3699 class_t *supercls = getSuperclass(cls);
3700
3701 // Shouldn't have any live subclasses.
3702 if (cls->data->firstSubclass) {
3703 _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, "
3704 "including '%s'!", cls->data->ro->name,
3705 getName(cls->data->firstSubclass));
3706 }
3707 if (cls->isa->data->firstSubclass) {
3708 _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, "
3709 "including '%s'!", cls->data->ro->name,
3710 getName(cls->isa->data->firstSubclass));
3711 }
3712
3713 // Remove from superclass's subclass list
3714 // Note that cls and cls->isa may have different lists.
3715 if (supercls) {
3716 removeSubclass(getSuperclass(cls), cls);
3717 removeSubclass(getSuperclass(cls->isa), cls->isa);
3718 }
3719
3720 // Remove from class hashes
3721 removeRealizedClass(cls);
3722 removeUninitializedClass(cls);
3723
3724 // Deallocate memory
3725 unload_class(cls->isa);
3726 unload_class(cls);
3727
3728 OBJC_UNLOCK(&runtimeLock);
3729 }
3730
3731
3732
3733 /***********************************************************************
3734 * class_createInstanceFromZone
3735 * fixme
3736 * Locking: none
3737 **********************************************************************/
3738 id
3739 class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
3740 {
3741 if (cls) assert(isRealized(newcls(cls)));
3742 return _internal_class_createInstanceFromZone(cls, extraBytes, zone);
3743 }
3744
3745
3746 /***********************************************************************
3747 * class_createInstance
3748 * fixme
3749 * Locking: none
3750 **********************************************************************/
3751 id
3752 class_createInstance(Class cls, size_t extraBytes)
3753 {
3754 return class_createInstanceFromZone(cls, extraBytes, NULL);
3755 }
3756
3757
3758 /***********************************************************************
3759 * object_copyFromZone
3760 * fixme
3761 * Locking: none
3762 **********************************************************************/
3763 id
3764 object_copyFromZone(id oldObj, size_t extraBytes, void *zone)
3765 {
3766 id obj;
3767 size_t size;
3768
3769 if (!oldObj) return nil;
3770
3771 size = _class_getInstanceSize(oldObj->isa) + extraBytes;
3772 obj = malloc_zone_calloc(zone, size, 1);
3773 if (!obj) return nil;
3774
3775 // fixme this doesn't handle C++ ivars correctly (#4619414)
3776 bcopy(oldObj, obj, size);
3777
3778 return obj;
3779 }
3780
3781
3782 /***********************************************************************
3783 * object_copy
3784 * fixme
3785 * Locking: none
3786 **********************************************************************/
3787 id
3788 object_copy(id oldObj, size_t extraBytes)
3789 {
3790 return object_copyFromZone(oldObj, extraBytes, malloc_default_zone());
3791 }
3792
3793
3794 /***********************************************************************
3795 * object_dispose
3796 * fixme
3797 * Locking: none
3798 **********************************************************************/
3799 id
3800 object_dispose(id obj)
3801 {
3802 return _internal_object_dispose(obj);
3803 }
3804
3805
3806 /***********************************************************************
3807 * _class_getFreedObjectClass
3808 * fixme
3809 * Locking: none
3810 **********************************************************************/
3811 __private_extern__ Class
3812 _class_getFreedObjectClass(void)
3813 {
3814 return Nil; // fixme
3815 }
3816
3817 /***********************************************************************
3818 * _class_getNonexistentObjectClass
3819 * fixme
3820 * Locking: none
3821 **********************************************************************/
3822 __private_extern__ Class
3823 _class_getNonexistentObjectClass(void)
3824 {
3825 return Nil; // fixme
3826 }
3827
3828 /***********************************************************************
3829 * _objc_getFreedObjectClass
3830 * fixme
3831 * Locking: none
3832 **********************************************************************/
3833 Class _objc_getFreedObjectClass (void)
3834 {
3835 return _class_getFreedObjectClass();
3836 }
3837
3838 extern id objc_msgSend_fixup(id, SEL, ...);
3839 extern id objc_msgSend_fixedup(id, SEL, ...);
3840 extern id objc_msgSendSuper2_fixup(id, SEL, ...);
3841 extern id objc_msgSendSuper2_fixedup(id, SEL, ...);
3842 extern id objc_msgSend_stret_fixup(id, SEL, ...);
3843 extern id objc_msgSend_stret_fixedup(id, SEL, ...);
3844 extern id objc_msgSendSuper2_stret_fixup(id, SEL, ...);
3845 extern id objc_msgSendSuper2_stret_fixedup(id, SEL, ...);
3846
3847 /***********************************************************************
3848 * _objc_fixupMessageRef
3849 * Fixes up message ref *msg.
3850 * obj is the receiver. supr is NULL for non-super messages
3851 * Locking: acquires runtimeLock
3852 **********************************************************************/
3853 __private_extern__ IMP
3854 _objc_fixupMessageRef(id obj, struct objc_super2 *supr, message_ref *msg)
3855 {
3856 IMP imp;
3857 class_t *isa;
3858
3859 OBJC_CHECK_UNLOCKED(&runtimeLock);
3860
3861 if (!supr) {
3862 // normal message - search obj->isa for the method implementation
3863 isa = (class_t *)obj->isa;
3864
3865 if (!isRealized(isa)) {
3866 // obj is a class object, isa is its metaclass
3867 class_t *cls;
3868 OBJC_LOCK(&runtimeLock);
3869 if (!isRealized(isa)) {
3870 cls = realizeClass((class_t *)obj);
3871
3872 // shouldn't have instances of unrealized classes!
3873 assert(isMetaClass(isa));
3874 // shouldn't be relocating classes here!
3875 assert(cls == (class_t *)obj);
3876 }
3877 OBJC_UNLOCK(&runtimeLock);
3878 }
3879 }
3880 else {
3881 // this is objc_msgSend_super, and supr->current_class->superclass
3882 // is the class to search for the method implementation
3883 assert(isRealized((class_t *)supr->current_class));
3884 isa = getSuperclass((class_t *)supr->current_class);
3885 }
3886
3887 msg->sel = sel_registerName((const char *)msg->sel);
3888 imp = _class_lookupMethodAndLoadCache((Class)isa, msg->sel);
3889
3890 if (msg->imp == (IMP)&objc_msgSend_fixup) {
3891 msg->imp = (IMP)&objc_msgSend_fixedup;
3892 }
3893 else if (msg->imp == (IMP)&objc_msgSendSuper2_fixup) {
3894 msg->imp = (IMP)&objc_msgSendSuper2_fixedup;
3895 }
3896 else if (msg->imp == (IMP)&objc_msgSend_stret_fixup) {
3897 msg->imp = (IMP)&objc_msgSend_stret_fixedup;
3898 }
3899 else if (msg->imp == (IMP)&objc_msgSendSuper2_stret_fixup) {
3900 msg->imp = (IMP)&objc_msgSendSuper2_stret_fixedup;
3901 }
3902 else {
3903 // The ref may already have been fixed up, either by another thread,
3904 // or by +initialize via class_lookupMethodAndLoadCache above.
3905 }
3906
3907 return imp;
3908 }
3909
3910 #warning fixme delete after #4586306
3911 Class class_poseAs(Class imposter, Class original)
3912 {
3913 _objc_fatal("Don't call class_poseAs.");
3914 }
3915
3916
3917 // ProKit SPI
3918 static class_t *setSuperclass(class_t *cls, class_t *newSuper)
3919 {
3920 class_t *oldSuper;
3921
3922 OBJC_CHECK_LOCKED(&runtimeLock);
3923
3924 oldSuper = cls->superclass;
3925 removeSubclass(oldSuper, cls);
3926 removeSubclass(oldSuper->isa, cls->isa);
3927
3928 cls->superclass = newSuper;
3929 cls->isa->superclass = newSuper->isa;
3930 addSubclass(newSuper, cls);
3931 addSubclass(newSuper->isa, cls->isa);
3932
3933 flushCaches(cls);
3934 flushCaches(cls->isa);
3935
3936 return oldSuper;
3937 }
3938
3939
3940 Class class_setSuperclass(Class cls_gen, Class newSuper_gen)
3941 {
3942 class_t *cls = newcls(cls_gen);
3943 class_t *newSuper = newcls(newSuper_gen);
3944 class_t *oldSuper;
3945
3946 OBJC_LOCK(&runtimeLock);
3947 oldSuper = setSuperclass(cls, newSuper);
3948 OBJC_UNLOCK(&runtimeLock);
3949
3950 return (Class)oldSuper;
3951 }
3952
3953 #endif