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