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