]>
Commit | Line | Data |
---|---|---|
b3962a83 A |
1 | /* |
2 | * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | /*********************************************************************** | |
25 | * objc-runtime-old.m | |
26 | * Support for old-ABI classes and images. | |
27 | **********************************************************************/ | |
28 | ||
29 | /*********************************************************************** | |
30 | * Class loading and connecting (GrP 2004-2-11) | |
31 | * | |
32 | * When images are loaded (during program startup or otherwise), the | |
33 | * runtime needs to load classes and categories from the images, connect | |
34 | * classes to superclasses and categories to parent classes, and call | |
35 | * +load methods. | |
36 | * | |
37 | * The Objective-C runtime can cope with classes arriving in any order. | |
38 | * That is, a class may be discovered by the runtime before some | |
39 | * superclass is known. To handle out-of-order class loads, the | |
40 | * runtime uses a "pending class" system. | |
41 | * | |
42 | * (Historical note) | |
43 | * Panther and earlier: many classes arrived out-of-order because of | |
44 | * the poorly-ordered callback from dyld. However, the runtime's | |
45 | * pending mechanism only handled "missing superclass" and not | |
46 | * "present superclass but missing higher class". See Radar #3225652. | |
47 | * Tiger: The runtime's pending mechanism was augmented to handle | |
48 | * arbitrary missing classes. In addition, dyld was rewritten and | |
49 | * now sends the callbacks in strictly bottom-up link order. | |
50 | * The pending mechanism may now be needed only for rare and | |
51 | * hard to construct programs. | |
52 | * (End historical note) | |
53 | * | |
54 | * A class when first seen in an image is considered "unconnected". | |
55 | * It is stored in `unconnected_class_hash`. If all of the class's | |
56 | * superclasses exist and are already "connected", then the new class | |
57 | * can be connected to its superclasses and moved to `class_hash` for | |
58 | * normal use. Otherwise, the class waits in `unconnected_class_hash` | |
59 | * until the superclasses finish connecting. | |
60 | * | |
61 | * A "connected" class is | |
62 | * (1) in `class_hash`, | |
63 | * (2) connected to its superclasses, | |
64 | * (3) has no unconnected superclasses, | |
65 | * (4) is otherwise initialized and ready for use, and | |
66 | * (5) is eligible for +load if +load has not already been called. | |
67 | * | |
68 | * An "unconnected" class is | |
69 | * (1) in `unconnected_class_hash`, | |
70 | * (2) not connected to its superclasses, | |
71 | * (3) has an immediate superclass which is either missing or unconnected, | |
72 | * (4) is not ready for use, and | |
73 | * (5) is not yet eligible for +load. | |
74 | * | |
75 | * Image mapping is NOT CURRENTLY THREAD-SAFE with respect to just about | |
ee974f79 | 76 | * anything. Image mapping IS RE-ENTRANT in several places: superclass |
b3962a83 A |
77 | * lookup may cause ZeroLink to load another image, and +load calls may |
78 | * cause dyld to load another image. | |
79 | * | |
80 | * Image mapping sequence: | |
81 | * | |
82 | * Read all classes in all new images. | |
83 | * Add them all to unconnected_class_hash. | |
84 | * Note any +load implementations before categories are attached. | |
b3962a83 A |
85 | * Attach any pending categories. |
86 | * Read all categories in all new images. | |
87 | * Attach categories whose parent class exists (connected or not), | |
88 | * and pend the rest. | |
89 | * Mark them all eligible for +load (if implemented), even if the | |
90 | * parent class is missing. | |
91 | * Try to connect all classes in all new images. | |
92 | * If the superclass is missing, pend the class | |
93 | * If the superclass is unconnected, try to recursively connect it | |
94 | * If the superclass is connected: | |
95 | * connect the class | |
96 | * mark the class eligible for +load, if implemented | |
ee974f79 | 97 | * fix up any pended classrefs referring to the class |
b3962a83 A |
98 | * connect any pended subclasses of the class |
99 | * Resolve selector refs and class refs in all new images. | |
100 | * Class refs whose classes still do not exist are pended. | |
101 | * Fix up protocol objects in all new images. | |
102 | * Call +load for classes and categories. | |
103 | * May include classes or categories that are not in these images, | |
104 | * but are newly eligible because of these image. | |
105 | * Class +loads will be called superclass-first because of the | |
106 | * superclass-first nature of the connecting process. | |
107 | * Category +load needs to be deferred until the parent class is | |
108 | * connected and has had its +load called. | |
109 | * | |
110 | * Performance: all classes are read before any categories are read. | |
111 | * Fewer categories need be pended for lack of a parent class. | |
112 | * | |
113 | * Performance: all categories are attempted to be attached before | |
114 | * any classes are connected. Fewer class caches need be flushed. | |
115 | * (Unconnected classes and their respective subclasses are guaranteed | |
116 | * to be un-messageable, so their caches will be empty.) | |
117 | * | |
118 | * Performance: all classes are read before any classes are connected. | |
119 | * Fewer classes need be pended for lack of a superclass. | |
120 | * | |
121 | * Correctness: all selector and class refs are fixed before any | |
122 | * protocol fixups or +load methods. libobjc itself contains selector | |
123 | * and class refs which are used in protocol fixup and +load. | |
124 | * | |
125 | * Correctness: +load methods are scheduled in bottom-up link order. | |
126 | * This constraint is in addition to superclass order. Some +load | |
127 | * implementations expect to use another class in a linked-to library, | |
128 | * even if the two classes don't share a direct superclass relationship. | |
129 | * | |
130 | * Correctness: all classes are scanned for +load before any categories | |
131 | * are attached. Otherwise, if a category implements +load and its class | |
132 | * has no class methods, the class's +load scan would find the category's | |
133 | * +load method, which would then be called twice. | |
ee974f79 A |
134 | * |
135 | * Correctness: pended class refs are not fixed up until the class is | |
136 | * connected. Classes with missing weak superclasses remain unconnected. | |
7257e56c | 137 | * Class refs to classes with missing weak superclasses must be nil. |
ee974f79 | 138 | * Therefore class refs to unconnected classes must remain un-fixed. |
b3962a83 A |
139 | * |
140 | **********************************************************************/ | |
141 | ||
142 | #if !__OBJC2__ | |
143 | ||
7af964d1 | 144 | #include "objc-private.h" |
8972963c | 145 | #include "objc-runtime-old.h" |
7257e56c A |
146 | #include "objc-file-old.h" |
147 | #include "objc-cache-old.h" | |
7af964d1 | 148 | #include "objc-loadmethod.h" |
b3962a83 | 149 | |
b3962a83 A |
150 | |
151 | typedef struct _objc_unresolved_category | |
152 | { | |
153 | struct _objc_unresolved_category *next; | |
7257e56c | 154 | old_category *cat; // may be nil |
b3962a83 A |
155 | long version; |
156 | } _objc_unresolved_category; | |
157 | ||
158 | typedef struct _PendingSubclass | |
159 | { | |
7257e56c | 160 | Class subclass; // subclass to finish connecting; may be nil |
b3962a83 A |
161 | struct _PendingSubclass *next; |
162 | } PendingSubclass; | |
163 | ||
164 | typedef struct _PendingClassRef | |
165 | { | |
7257e56c | 166 | Class *ref; // class reference to fix up; may be nil |
b3962a83 A |
167 | // (ref & 1) is a metaclass reference |
168 | struct _PendingClassRef *next; | |
169 | } PendingClassRef; | |
170 | ||
171 | ||
172 | static uintptr_t classHash(void *info, Class data); | |
173 | static int classIsEqual(void *info, Class name, Class cls); | |
174 | static int _objc_defaultClassHandler(const char *clsName); | |
b3962a83 A |
175 | static inline NXMapTable *pendingClassRefsMapTable(void); |
176 | static inline NXMapTable *pendingSubclassesMapTable(void); | |
7257e56c A |
177 | static void pendClassInstallation(Class cls, const char *superName); |
178 | static void pendClassReference(Class *ref, const char *className, BOOL isMeta); | |
179 | static void resolve_references_to_class(Class cls); | |
180 | static void resolve_subclasses_of_class(Class cls); | |
181 | static void really_connect_class(Class cls, Class supercls); | |
182 | static BOOL connect_class(Class cls); | |
b3962a83 | 183 | static void map_method_descs (struct objc_method_description_list * methods, BOOL copy); |
7257e56c A |
184 | static void _objcTweakMethodListPointerForClass(Class cls); |
185 | static inline void _objc_add_category(Class cls, old_category *category, int version); | |
186 | static BOOL _objc_add_category_flush_caches(Class cls, old_category *category, int version); | |
b3962a83 | 187 | static _objc_unresolved_category *reverse_cat(_objc_unresolved_category *cat); |
7257e56c A |
188 | static void resolve_categories_for_class(Class cls); |
189 | static BOOL _objc_register_category(old_category *cat, int version); | |
b3962a83 A |
190 | |
191 | ||
192 | // Function called when a class is loaded from an image | |
cd5f04f5 | 193 | void (*callbackFunction)(Class, Category) = 0; |
b3962a83 | 194 | |
b3962a83 | 195 | // Hash table of classes |
cd5f04f5 | 196 | NXHashTable * class_hash = 0; |
b3962a83 A |
197 | static NXHashTablePrototype classHashPrototype = |
198 | { | |
199 | (uintptr_t (*) (const void *, const void *)) classHash, | |
200 | (int (*)(const void *, const void *, const void *)) classIsEqual, | |
201 | NXNoEffectFree, 0 | |
202 | }; | |
203 | ||
204 | // Hash table of unconnected classes | |
7257e56c | 205 | static NXHashTable *unconnected_class_hash = nil; |
b3962a83 A |
206 | |
207 | // Exported copy of class_hash variable (hook for debugging tools) | |
7257e56c | 208 | NXHashTable *_objc_debug_class_hash = nil; |
b3962a83 A |
209 | |
210 | // Category and class registries | |
211 | // Keys are COPIES of strings, to prevent stale pointers with unloaded bundles | |
212 | // Use NXMapKeyCopyingInsert and NXMapKeyFreeingRemove | |
7257e56c | 213 | static NXMapTable * category_hash = nil; |
b3962a83 A |
214 | |
215 | // Keys are COPIES of strings, to prevent stale pointers with unloaded bundles | |
216 | // Use NXMapKeyCopyingInsert and NXMapKeyFreeingRemove | |
7257e56c A |
217 | static NXMapTable * pendingClassRefsMap = nil; |
218 | static NXMapTable * pendingSubclassesMap = nil; | |
b3962a83 A |
219 | |
220 | // Protocols | |
7257e56c A |
221 | static NXMapTable *protocol_map = nil; // name -> protocol |
222 | static NXMapTable *protocol_ext_map = nil; // protocol -> protocol ext | |
b3962a83 A |
223 | |
224 | // Function pointer objc_getClass calls through when class is not found | |
225 | static int (*objc_classHandler) (const char *) = _objc_defaultClassHandler; | |
226 | ||
227 | // Function pointer called by objc_getClass and objc_lookupClass when | |
228 | // class is not found. _objc_classLoader is called before objc_classHandler. | |
7257e56c | 229 | static BOOL (*_objc_classLoader)(const char *) = nil; |
b3962a83 A |
230 | |
231 | ||
b3962a83 A |
232 | /*********************************************************************** |
233 | * objc_dump_class_hash. Log names of all known classes. | |
234 | **********************************************************************/ | |
cd5f04f5 | 235 | void objc_dump_class_hash(void) |
b3962a83 A |
236 | { |
237 | NXHashTable *table; | |
238 | unsigned count; | |
239 | Class data; | |
240 | NXHashState state; | |
241 | ||
242 | table = class_hash; | |
243 | count = 0; | |
244 | state = NXInitHashState (table); | |
245 | while (NXNextHashState (table, &state, (void **) &data)) | |
8070259c | 246 | printf ("class %d: %s\n", ++count, data->nameForLogging()); |
b3962a83 A |
247 | } |
248 | ||
249 | ||
250 | /*********************************************************************** | |
251 | * _objc_init_class_hash. Return the class lookup table, create it if | |
252 | * necessary. | |
253 | **********************************************************************/ | |
cd5f04f5 | 254 | void _objc_init_class_hash(void) |
b3962a83 A |
255 | { |
256 | // Do nothing if class hash table already exists | |
257 | if (class_hash) | |
258 | return; | |
259 | ||
260 | // class_hash starts small, with only enough capacity for libobjc itself. | |
261 | // If a second library is found by map_images(), class_hash is immediately | |
262 | // resized to capacity 1024 to cut down on rehashes. | |
263 | // Old numbers: A smallish Foundation+AppKit program will have | |
264 | // about 520 classes. Larger apps (like IB or WOB) have more like | |
265 | // 800 classes. Some customers have massive quantities of classes. | |
266 | // Foundation-only programs aren't likely to notice the ~6K loss. | |
267 | class_hash = NXCreateHashTableFromZone (classHashPrototype, | |
268 | 16, | |
269 | nil, | |
270 | _objc_internal_zone ()); | |
271 | _objc_debug_class_hash = class_hash; | |
272 | } | |
273 | ||
274 | ||
275 | /*********************************************************************** | |
276 | * objc_getClassList. Return the known classes. | |
277 | **********************************************************************/ | |
278 | int objc_getClassList(Class *buffer, int bufferLen) | |
279 | { | |
280 | NXHashState state; | |
7af964d1 | 281 | Class cls; |
b3962a83 A |
282 | int cnt, num; |
283 | ||
7af964d1 A |
284 | mutex_lock(&classLock); |
285 | if (!class_hash) { | |
286 | mutex_unlock(&classLock); | |
287 | return 0; | |
288 | } | |
b3962a83 | 289 | num = NXCountHashTable(class_hash); |
7257e56c | 290 | if (nil == buffer) { |
7af964d1 | 291 | mutex_unlock(&classLock); |
b3962a83 A |
292 | return num; |
293 | } | |
294 | cnt = 0; | |
295 | state = NXInitHashState(class_hash); | |
296 | while (cnt < bufferLen && | |
7af964d1 | 297 | NXNextHashState(class_hash, &state, (void **)&cls)) |
b3962a83 | 298 | { |
7af964d1 | 299 | buffer[cnt++] = cls; |
b3962a83 | 300 | } |
7af964d1 | 301 | mutex_unlock(&classLock); |
b3962a83 A |
302 | return num; |
303 | } | |
304 | ||
305 | ||
8972963c A |
306 | /*********************************************************************** |
307 | * objc_copyClassList | |
308 | * Returns pointers to all classes. | |
309 | * This requires all classes be realized, which is regretfully non-lazy. | |
310 | * | |
7257e56c A |
311 | * outCount may be nil. *outCount is the number of classes returned. |
312 | * If the returned array is not nil, it is nil-terminated and must be | |
8972963c A |
313 | * freed with free(). |
314 | * Locking: acquires classLock | |
315 | **********************************************************************/ | |
316 | Class * | |
317 | objc_copyClassList(unsigned int *outCount) | |
318 | { | |
319 | Class *result; | |
320 | unsigned int count; | |
321 | ||
322 | mutex_lock(&classLock); | |
7257e56c | 323 | result = nil; |
8972963c A |
324 | count = class_hash ? NXCountHashTable(class_hash) : 0; |
325 | ||
326 | if (count > 0) { | |
327 | Class cls; | |
328 | NXHashState state = NXInitHashState(class_hash); | |
7257e56c | 329 | result = (Class *)malloc((1+count) * sizeof(Class)); |
8972963c A |
330 | count = 0; |
331 | while (NXNextHashState(class_hash, &state, (void **)&cls)) { | |
332 | result[count++] = cls; | |
333 | } | |
7257e56c | 334 | result[count] = nil; |
8972963c A |
335 | } |
336 | mutex_unlock(&classLock); | |
337 | ||
338 | if (outCount) *outCount = count; | |
339 | return result; | |
340 | } | |
341 | ||
342 | ||
b3962a83 A |
343 | /*********************************************************************** |
344 | * objc_copyProtocolList | |
345 | * Returns pointers to all protocols. | |
346 | * Locking: acquires classLock | |
347 | **********************************************************************/ | |
8972963c | 348 | Protocol * __unsafe_unretained * |
b3962a83 A |
349 | objc_copyProtocolList(unsigned int *outCount) |
350 | { | |
b3962a83 A |
351 | int count, i; |
352 | Protocol *proto; | |
353 | const char *name; | |
354 | NXMapState state; | |
355 | Protocol **result; | |
356 | ||
7af964d1 A |
357 | mutex_lock(&classLock); |
358 | ||
b3962a83 A |
359 | count = NXCountMapTable(protocol_map); |
360 | if (count == 0) { | |
7af964d1 | 361 | mutex_unlock(&classLock); |
b3962a83 | 362 | if (outCount) *outCount = 0; |
7257e56c | 363 | return nil; |
b3962a83 A |
364 | } |
365 | ||
7257e56c | 366 | result = (Protocol **)calloc(1 + count, sizeof(Protocol *)); |
b3962a83 A |
367 | |
368 | i = 0; | |
369 | state = NXInitMapState(protocol_map); | |
370 | while (NXNextMapState(protocol_map, &state, | |
371 | (const void **)&name, (const void **)&proto)) | |
372 | { | |
373 | result[i++] = proto; | |
374 | } | |
375 | ||
7257e56c | 376 | result[i++] = nil; |
b3962a83 A |
377 | assert(i == count+1); |
378 | ||
7af964d1 | 379 | mutex_unlock(&classLock); |
b3962a83 A |
380 | |
381 | if (outCount) *outCount = count; | |
382 | return result; | |
383 | } | |
384 | ||
385 | ||
386 | /*********************************************************************** | |
387 | * objc_getClasses. Return class lookup table. | |
388 | * | |
389 | * NOTE: This function is very dangerous, since you cannot safely use | |
390 | * the hashtable without locking it, and the lock is private! | |
391 | **********************************************************************/ | |
392 | void *objc_getClasses(void) | |
393 | { | |
394 | OBJC_WARN_DEPRECATED; | |
395 | ||
396 | // Return the class lookup hash table | |
397 | return class_hash; | |
398 | } | |
399 | ||
400 | ||
401 | /*********************************************************************** | |
402 | * classHash. | |
403 | **********************************************************************/ | |
404 | static uintptr_t classHash(void *info, Class data) | |
405 | { | |
406 | // Nil classes hash to zero | |
407 | if (!data) | |
408 | return 0; | |
409 | ||
410 | // Call through to real hash function | |
8070259c | 411 | return _objc_strhash (data->mangledName()); |
b3962a83 A |
412 | } |
413 | ||
414 | /*********************************************************************** | |
415 | * classIsEqual. Returns whether the class names match. If we ever | |
416 | * check more than the name, routines like objc_lookUpClass have to | |
417 | * change as well. | |
418 | **********************************************************************/ | |
419 | static int classIsEqual(void *info, Class name, Class cls) | |
420 | { | |
421 | // Standard string comparison | |
8070259c | 422 | return strcmp(name->mangledName(), cls->mangledName()) == 0; |
b3962a83 A |
423 | } |
424 | ||
425 | ||
426 | // Unresolved future classes | |
7257e56c | 427 | static NXHashTable *future_class_hash = nil; |
b3962a83 A |
428 | |
429 | // Resolved future<->original classes | |
7257e56c A |
430 | static NXMapTable *future_class_to_original_class_map = nil; |
431 | static NXMapTable *original_class_to_future_class_map = nil; | |
b3962a83 A |
432 | |
433 | // CF requests about 20 future classes; HIToolbox requests one. | |
434 | #define FUTURE_COUNT 32 | |
435 | ||
436 | ||
437 | /*********************************************************************** | |
438 | * setOriginalClassForFutureClass | |
439 | * Record resolution of a future class. | |
440 | **********************************************************************/ | |
7257e56c A |
441 | static void setOriginalClassForFutureClass(Class futureClass, |
442 | Class originalClass) | |
b3962a83 A |
443 | { |
444 | if (!future_class_to_original_class_map) { | |
445 | future_class_to_original_class_map = | |
446 | NXCreateMapTableFromZone (NXPtrValueMapPrototype, FUTURE_COUNT, | |
447 | _objc_internal_zone ()); | |
448 | original_class_to_future_class_map = | |
449 | NXCreateMapTableFromZone (NXPtrValueMapPrototype, FUTURE_COUNT, | |
450 | _objc_internal_zone ()); | |
451 | } | |
452 | ||
453 | NXMapInsert (future_class_to_original_class_map, | |
454 | futureClass, originalClass); | |
455 | NXMapInsert (original_class_to_future_class_map, | |
456 | originalClass, futureClass); | |
457 | ||
458 | if (PrintFuture) { | |
7257e56c | 459 | _objc_inform("FUTURE: using %p instead of %p for %s", (void*)futureClass, (void*)originalClass, originalClass->name); |
b3962a83 A |
460 | } |
461 | } | |
462 | ||
463 | /*********************************************************************** | |
464 | * getOriginalClassForFutureClass | |
465 | * getFutureClassForOriginalClass | |
466 | * Switch between a future class and its corresponding original class. | |
467 | * The future class is the one actually in use. | |
468 | * The original class is the one from disk. | |
469 | **********************************************************************/ | |
470 | /* | |
7257e56c A |
471 | static Class |
472 | getOriginalClassForFutureClass(Class futureClass) | |
b3962a83 A |
473 | { |
474 | if (!future_class_to_original_class_map) return Nil; | |
475 | return NXMapGet (future_class_to_original_class_map, futureClass); | |
476 | } | |
477 | */ | |
7257e56c A |
478 | static Class |
479 | getFutureClassForOriginalClass(Class originalClass) | |
b3962a83 A |
480 | { |
481 | if (!original_class_to_future_class_map) return Nil; | |
7257e56c | 482 | return (Class)NXMapGet(original_class_to_future_class_map, originalClass); |
b3962a83 A |
483 | } |
484 | ||
485 | ||
486 | /*********************************************************************** | |
487 | * makeFutureClass | |
488 | * Initialize the memory in *cls with an unresolved future class with the | |
489 | * given name. The memory is recorded in future_class_hash. | |
490 | **********************************************************************/ | |
7257e56c | 491 | static void makeFutureClass(Class cls, const char *name) |
b3962a83 A |
492 | { |
493 | // CF requests about 20 future classes, plus HIToolbox has one. | |
494 | if (!future_class_hash) { | |
495 | future_class_hash = | |
496 | NXCreateHashTableFromZone(classHashPrototype, FUTURE_COUNT, | |
7257e56c | 497 | nil, _objc_internal_zone()); |
b3962a83 A |
498 | } |
499 | ||
500 | cls->name = _strdup_internal(name); | |
501 | NXHashInsert(future_class_hash, cls); | |
502 | ||
503 | if (PrintFuture) { | |
7257e56c | 504 | _objc_inform("FUTURE: reserving %p for %s", (void*)cls, name); |
b3962a83 A |
505 | } |
506 | } | |
507 | ||
508 | ||
509 | /*********************************************************************** | |
510 | * _objc_allocateFutureClass | |
511 | * Allocate an unresolved future class for the given class name. | |
512 | * Returns any existing allocation if one was already made. | |
513 | * Assumes the named class doesn't exist yet. | |
514 | * Not thread safe. | |
515 | **********************************************************************/ | |
cd5f04f5 | 516 | Class _objc_allocateFutureClass(const char *name) |
b3962a83 | 517 | { |
7257e56c | 518 | Class cls; |
b3962a83 A |
519 | |
520 | if (future_class_hash) { | |
7257e56c | 521 | objc_class query; |
b3962a83 | 522 | query.name = name; |
7257e56c | 523 | if ((cls = (Class)NXHashGet(future_class_hash, &query))) { |
b3962a83 | 524 | // Already have a future class for this name. |
7257e56c | 525 | return cls; |
b3962a83 A |
526 | } |
527 | } | |
528 | ||
7257e56c | 529 | cls = _calloc_class(sizeof(objc_class)); |
b3962a83 | 530 | makeFutureClass(cls, name); |
7257e56c | 531 | return cls; |
b3962a83 A |
532 | } |
533 | ||
534 | ||
8070259c A |
535 | /*********************************************************************** |
536 | * objc_getFutureClass. Return the id of the named class. | |
537 | * If the class does not exist, return an uninitialized class | |
538 | * structure that will be used for the class when and if it | |
539 | * does get loaded. | |
540 | * Not thread safe. | |
541 | **********************************************************************/ | |
542 | Class objc_getFutureClass(const char *name) | |
543 | { | |
544 | Class cls; | |
545 | ||
546 | // YES unconnected, NO class handler | |
547 | // (unconnected is OK because it will someday be the real class) | |
548 | cls = look_up_class(name, YES, NO); | |
549 | if (cls) { | |
550 | if (PrintFuture) { | |
551 | _objc_inform("FUTURE: found %p already in use for %s", | |
552 | (void*)cls, name); | |
553 | } | |
554 | return cls; | |
555 | } | |
556 | ||
557 | // No class or future class with that name yet. Make one. | |
558 | // fixme not thread-safe with respect to | |
559 | // simultaneous library load or getFutureClass. | |
560 | return _objc_allocateFutureClass(name); | |
561 | } | |
562 | ||
563 | ||
b3962a83 A |
564 | /*********************************************************************** |
565 | * objc_setFutureClass. | |
566 | * Like objc_getFutureClass, but uses the provided memory block. | |
567 | * If the class already exists, a posing-like substitution is performed. | |
568 | * Not thread safe. | |
569 | **********************************************************************/ | |
570 | void objc_setFutureClass(Class cls, const char *name) | |
571 | { | |
7257e56c A |
572 | Class oldcls; |
573 | Class newcls = cls; // Not a real class! | |
b3962a83 | 574 | |
7257e56c | 575 | if ((oldcls = look_up_class(name, NO/*unconnected*/, NO/*classhandler*/))) { |
b3962a83 A |
576 | setOriginalClassForFutureClass(newcls, oldcls); |
577 | // fixme hack | |
578 | memcpy(newcls, oldcls, sizeof(struct objc_class)); | |
579 | newcls->info &= ~CLS_EXT; | |
580 | ||
7af964d1 | 581 | mutex_lock(&classLock); |
b3962a83 | 582 | NXHashRemove(class_hash, oldcls); |
7257e56c | 583 | objc_removeRegisteredClass(oldcls); |
b3962a83 A |
584 | change_class_references(newcls, oldcls, nil, YES); |
585 | NXHashInsert(class_hash, newcls); | |
7257e56c | 586 | objc_addRegisteredClass(newcls); |
7af964d1 | 587 | mutex_unlock(&classLock); |
b3962a83 A |
588 | } else { |
589 | makeFutureClass(newcls, name); | |
590 | } | |
591 | } | |
592 | ||
593 | ||
7257e56c A |
594 | BOOL _class_isFutureClass(Class cls) |
595 | { | |
596 | return cls && future_class_hash && NXHashGet(future_class_hash, cls); | |
597 | } | |
598 | ||
599 | ||
b3962a83 A |
600 | /*********************************************************************** |
601 | * _objc_defaultClassHandler. Default objc_classHandler. Does nothing. | |
602 | **********************************************************************/ | |
603 | static int _objc_defaultClassHandler(const char *clsName) | |
604 | { | |
605 | // Return zero so objc_getClass doesn't bother re-searching | |
606 | return 0; | |
607 | } | |
608 | ||
609 | /*********************************************************************** | |
610 | * objc_setClassHandler. Set objc_classHandler to the specified value. | |
611 | * | |
7257e56c | 612 | * NOTE: This should probably deal with userSuppliedHandler being nil, |
b3962a83 | 613 | * because the objc_classHandler caller does not check... it would bus |
7257e56c | 614 | * error. It would make sense to handle nil by restoring the default |
b3962a83 A |
615 | * handler. Is anyone hacking with this, though? |
616 | **********************************************************************/ | |
617 | void objc_setClassHandler(int (*userSuppliedHandler)(const char *)) | |
618 | { | |
619 | OBJC_WARN_DEPRECATED; | |
620 | ||
621 | objc_classHandler = userSuppliedHandler; | |
622 | } | |
623 | ||
624 | ||
625 | /*********************************************************************** | |
626 | * _objc_setClassLoader | |
627 | * Similar to objc_setClassHandler, but objc_classLoader is used for | |
628 | * both objc_getClass() and objc_lookupClass(), and objc_classLoader | |
629 | * pre-empts objc_classHandler. | |
630 | **********************************************************************/ | |
631 | void _objc_setClassLoader(BOOL (*newClassLoader)(const char *)) | |
632 | { | |
633 | _objc_classLoader = newClassLoader; | |
634 | } | |
635 | ||
636 | ||
637 | /*********************************************************************** | |
638 | * objc_getProtocol | |
7257e56c | 639 | * Get a protocol by name, or nil. |
b3962a83 A |
640 | **********************************************************************/ |
641 | Protocol *objc_getProtocol(const char *name) | |
642 | { | |
643 | Protocol *result; | |
7257e56c | 644 | if (!protocol_map) return nil; |
7af964d1 | 645 | mutex_lock(&classLock); |
b3962a83 | 646 | result = (Protocol *)NXMapGet(protocol_map, name); |
7af964d1 | 647 | mutex_unlock(&classLock); |
b3962a83 A |
648 | return result; |
649 | } | |
650 | ||
651 | ||
652 | /*********************************************************************** | |
653 | * look_up_class | |
654 | * Map a class name to a class using various methods. | |
655 | * This is the common implementation of objc_lookUpClass and objc_getClass, | |
656 | * and is also used internally to get additional search options. | |
657 | * Sequence: | |
658 | * 1. class_hash | |
659 | * 2. unconnected_class_hash (optional) | |
660 | * 3. classLoader callback | |
661 | * 4. classHandler callback (optional) | |
662 | **********************************************************************/ | |
7257e56c | 663 | Class look_up_class(const char *aClassName, BOOL includeUnconnected, BOOL includeClassHandler) |
b3962a83 A |
664 | { |
665 | BOOL includeClassLoader = YES; // class loader cannot be skipped | |
7257e56c A |
666 | Class result = nil; |
667 | struct objc_class query; | |
b3962a83 A |
668 | |
669 | query.name = aClassName; | |
670 | ||
671 | retry: | |
672 | ||
673 | if (!result && class_hash) { | |
674 | // Check ordinary classes | |
7af964d1 | 675 | mutex_lock (&classLock); |
7257e56c | 676 | result = (Class)NXHashGet(class_hash, &query); |
7af964d1 | 677 | mutex_unlock (&classLock); |
b3962a83 A |
678 | } |
679 | ||
680 | if (!result && includeUnconnected && unconnected_class_hash) { | |
681 | // Check not-yet-connected classes | |
7af964d1 | 682 | mutex_lock(&classLock); |
7257e56c | 683 | result = (Class)NXHashGet(unconnected_class_hash, &query); |
7af964d1 | 684 | mutex_unlock(&classLock); |
b3962a83 A |
685 | } |
686 | ||
687 | if (!result && includeClassLoader && _objc_classLoader) { | |
688 | // Try class loader callback | |
689 | if ((*_objc_classLoader)(aClassName)) { | |
690 | // Re-try lookup without class loader | |
691 | includeClassLoader = NO; | |
692 | goto retry; | |
693 | } | |
694 | } | |
695 | ||
696 | if (!result && includeClassHandler && objc_classHandler) { | |
697 | // Try class handler callback | |
698 | if ((*objc_classHandler)(aClassName)) { | |
699 | // Re-try lookup without class handler or class loader | |
700 | includeClassLoader = NO; | |
701 | includeClassHandler = NO; | |
702 | goto retry; | |
703 | } | |
704 | } | |
705 | ||
706 | return result; | |
707 | } | |
708 | ||
709 | ||
710 | /*********************************************************************** | |
7257e56c | 711 | * objc_class::isConnected |
b3962a83 | 712 | * Returns TRUE if class cls is connected. |
7257e56c | 713 | * A connected class has either a connected superclass or a nil superclass, |
b3962a83 A |
714 | * and is present in class_hash. |
715 | **********************************************************************/ | |
7257e56c | 716 | bool objc_class::isConnected() |
b3962a83 | 717 | { |
7257e56c | 718 | bool result; |
7af964d1 | 719 | mutex_lock(&classLock); |
7257e56c | 720 | result = NXHashMember(class_hash, this); |
7af964d1 | 721 | mutex_unlock(&classLock); |
b3962a83 A |
722 | return result; |
723 | } | |
724 | ||
725 | ||
b3962a83 A |
726 | /*********************************************************************** |
727 | * pendingClassRefsMapTable. Return a pointer to the lookup table for | |
728 | * pending class refs. | |
729 | **********************************************************************/ | |
730 | static inline NXMapTable *pendingClassRefsMapTable(void) | |
731 | { | |
732 | // Allocate table if needed | |
733 | if (!pendingClassRefsMap) { | |
734 | pendingClassRefsMap = | |
735 | NXCreateMapTableFromZone(NXStrValueMapPrototype, | |
736 | 10, _objc_internal_zone ()); | |
737 | } | |
738 | ||
739 | // Return table pointer | |
740 | return pendingClassRefsMap; | |
741 | } | |
742 | ||
743 | ||
744 | /*********************************************************************** | |
745 | * pendingSubclassesMapTable. Return a pointer to the lookup table for | |
746 | * pending subclasses. | |
747 | **********************************************************************/ | |
748 | static inline NXMapTable *pendingSubclassesMapTable(void) | |
749 | { | |
750 | // Allocate table if needed | |
751 | if (!pendingSubclassesMap) { | |
752 | pendingSubclassesMap = | |
753 | NXCreateMapTableFromZone(NXStrValueMapPrototype, | |
754 | 10, _objc_internal_zone ()); | |
755 | } | |
756 | ||
757 | // Return table pointer | |
758 | return pendingSubclassesMap; | |
759 | } | |
760 | ||
761 | ||
762 | /*********************************************************************** | |
763 | * pendClassInstallation | |
764 | * Finish connecting class cls when its superclass becomes connected. | |
765 | * Check for multiple pends of the same class because connect_class does not. | |
766 | **********************************************************************/ | |
7257e56c | 767 | static void pendClassInstallation(Class cls, const char *superName) |
b3962a83 A |
768 | { |
769 | NXMapTable *table; | |
770 | PendingSubclass *pending; | |
771 | PendingSubclass *oldList; | |
772 | PendingSubclass *l; | |
773 | ||
774 | // Create and/or locate pending class lookup table | |
775 | table = pendingSubclassesMapTable (); | |
776 | ||
777 | // Make sure this class isn't already in the pending list. | |
7257e56c A |
778 | oldList = (PendingSubclass *)NXMapGet(table, superName); |
779 | for (l = oldList; l != nil; l = l->next) { | |
b3962a83 A |
780 | if (l->subclass == cls) return; // already here, nothing to do |
781 | } | |
782 | ||
783 | // Create entry referring to this class | |
7257e56c | 784 | pending = (PendingSubclass *)_malloc_internal(sizeof(PendingSubclass)); |
b3962a83 A |
785 | pending->subclass = cls; |
786 | ||
787 | // Link new entry into head of list of entries for this class | |
788 | pending->next = oldList; | |
789 | ||
790 | // (Re)place entry list in the table | |
7af964d1 | 791 | NXMapKeyCopyingInsert (table, superName, pending); |
b3962a83 A |
792 | } |
793 | ||
794 | ||
795 | /*********************************************************************** | |
796 | * pendClassReference | |
797 | * Fix up a class ref when the class with the given name becomes connected. | |
798 | **********************************************************************/ | |
7257e56c | 799 | static void pendClassReference(Class *ref, const char *className, BOOL isMeta) |
b3962a83 A |
800 | { |
801 | NXMapTable *table; | |
802 | PendingClassRef *pending; | |
803 | ||
804 | // Create and/or locate pending class lookup table | |
805 | table = pendingClassRefsMapTable (); | |
806 | ||
807 | // Create entry containing the class reference | |
7257e56c | 808 | pending = (PendingClassRef *)_malloc_internal(sizeof(PendingClassRef)); |
b3962a83 A |
809 | pending->ref = ref; |
810 | if (isMeta) { | |
7257e56c | 811 | pending->ref = (Class *)((uintptr_t)pending->ref | 1); |
b3962a83 A |
812 | } |
813 | ||
814 | // Link new entry into head of list of entries for this class | |
7257e56c | 815 | pending->next = (PendingClassRef *)NXMapGet(table, className); |
b3962a83 A |
816 | |
817 | // (Re)place entry list in the table | |
7af964d1 | 818 | NXMapKeyCopyingInsert (table, className, pending); |
b3962a83 A |
819 | |
820 | if (PrintConnecting) { | |
821 | _objc_inform("CONNECT: pended reference to class '%s%s' at %p", | |
822 | className, isMeta ? " (meta)" : "", (void *)ref); | |
823 | } | |
824 | } | |
825 | ||
826 | ||
827 | /*********************************************************************** | |
828 | * resolve_references_to_class | |
829 | * Fix up any pending class refs to this class. | |
830 | **********************************************************************/ | |
7257e56c | 831 | static void resolve_references_to_class(Class cls) |
b3962a83 A |
832 | { |
833 | PendingClassRef *pending; | |
834 | ||
835 | if (!pendingClassRefsMap) return; // no unresolved refs for any class | |
836 | ||
7257e56c | 837 | pending = (PendingClassRef *)NXMapGet(pendingClassRefsMap, cls->name); |
b3962a83 A |
838 | if (!pending) return; // no unresolved refs for this class |
839 | ||
840 | NXMapKeyFreeingRemove(pendingClassRefsMap, cls->name); | |
841 | ||
842 | if (PrintConnecting) { | |
843 | _objc_inform("CONNECT: resolving references to class '%s'", cls->name); | |
844 | } | |
845 | ||
846 | while (pending) { | |
847 | PendingClassRef *next = pending->next; | |
848 | if (pending->ref) { | |
849 | BOOL isMeta = ((uintptr_t)pending->ref & 1) ? YES : NO; | |
7257e56c A |
850 | Class *ref = |
851 | (Class *)((uintptr_t)pending->ref & ~(uintptr_t)1); | |
852 | *ref = isMeta ? cls->ISA() : cls; | |
b3962a83 A |
853 | } |
854 | _free_internal(pending); | |
855 | pending = next; | |
856 | } | |
857 | ||
858 | if (NXCountMapTable(pendingClassRefsMap) == 0) { | |
859 | NXFreeMapTable(pendingClassRefsMap); | |
7257e56c | 860 | pendingClassRefsMap = nil; |
b3962a83 A |
861 | } |
862 | } | |
863 | ||
864 | ||
865 | /*********************************************************************** | |
866 | * resolve_subclasses_of_class | |
867 | * Fix up any pending subclasses of this class. | |
868 | **********************************************************************/ | |
7257e56c | 869 | static void resolve_subclasses_of_class(Class cls) |
b3962a83 A |
870 | { |
871 | PendingSubclass *pending; | |
872 | ||
873 | if (!pendingSubclassesMap) return; // no unresolved subclasses | |
874 | ||
7257e56c | 875 | pending = (PendingSubclass *)NXMapGet(pendingSubclassesMap, cls->name); |
b3962a83 A |
876 | if (!pending) return; // no unresolved subclasses for this class |
877 | ||
878 | NXMapKeyFreeingRemove(pendingSubclassesMap, cls->name); | |
879 | ||
880 | // Destroy the pending table if it's now empty, to save memory. | |
881 | if (NXCountMapTable(pendingSubclassesMap) == 0) { | |
882 | NXFreeMapTable(pendingSubclassesMap); | |
7257e56c | 883 | pendingSubclassesMap = nil; |
b3962a83 A |
884 | } |
885 | ||
886 | if (PrintConnecting) { | |
887 | _objc_inform("CONNECT: resolving subclasses of class '%s'", cls->name); | |
888 | } | |
889 | ||
890 | while (pending) { | |
891 | PendingSubclass *next = pending->next; | |
892 | if (pending->subclass) connect_class(pending->subclass); | |
893 | _free_internal(pending); | |
894 | pending = next; | |
895 | } | |
896 | } | |
897 | ||
898 | ||
899 | /*********************************************************************** | |
900 | * really_connect_class | |
901 | * Connect cls to superclass supercls unconditionally. | |
902 | * Also adjust the class hash tables and handle pended subclasses. | |
903 | * | |
904 | * This should be called from connect_class() ONLY. | |
905 | **********************************************************************/ | |
7257e56c A |
906 | static void really_connect_class(Class cls, |
907 | Class supercls) | |
b3962a83 | 908 | { |
7257e56c | 909 | Class oldCls; |
b3962a83 A |
910 | |
911 | // Connect superclass pointers. | |
8972963c | 912 | set_superclass(cls, supercls, YES); |
b3962a83 | 913 | |
7c0e6487 | 914 | // Update GC layouts |
7af964d1 A |
915 | // For paranoia, this is a conservative update: |
916 | // only non-strong -> strong and weak -> strong are corrected. | |
7c0e6487 A |
917 | if (UseGC && supercls && |
918 | (cls->info & CLS_EXT) && (supercls->info & CLS_EXT)) | |
919 | { | |
7af964d1 | 920 | BOOL layoutChanged; |
7c0e6487 A |
921 | layout_bitmap ivarBitmap = |
922 | layout_bitmap_create(cls->ivar_layout, | |
923 | cls->instance_size, | |
924 | cls->instance_size, NO); | |
925 | ||
926 | layout_bitmap superBitmap = | |
927 | layout_bitmap_create(supercls->ivar_layout, | |
928 | supercls->instance_size, | |
929 | supercls->instance_size, NO); | |
7af964d1 A |
930 | |
931 | // non-strong -> strong: bits set in super should be set in sub | |
932 | layoutChanged = layout_bitmap_or(ivarBitmap, superBitmap, cls->name); | |
7c0e6487 | 933 | layout_bitmap_free(superBitmap); |
7af964d1 A |
934 | |
935 | if (layoutChanged) { | |
cd5f04f5 | 936 | layout_bitmap weakBitmap = {}; |
7af964d1 A |
937 | BOOL weakLayoutChanged = NO; |
938 | ||
939 | if (cls->ext && cls->ext->weak_ivar_layout) { | |
940 | // weak -> strong: strong bits should be cleared in weak layout | |
941 | // This is a subset of non-strong -> strong | |
942 | weakBitmap = | |
943 | layout_bitmap_create(cls->ext->weak_ivar_layout, | |
944 | cls->instance_size, | |
945 | cls->instance_size, YES); | |
946 | ||
947 | weakLayoutChanged = | |
948 | layout_bitmap_clear(weakBitmap, ivarBitmap, cls->name); | |
949 | } else { | |
950 | // no existing weak ivars, so no weak -> strong changes | |
951 | } | |
952 | ||
7c0e6487 A |
953 | // Rebuild layout strings. |
954 | if (PrintIvars) { | |
7af964d1 A |
955 | _objc_inform("IVARS: gc layout changed " |
956 | "for class %s (super %s)", | |
7c0e6487 | 957 | cls->name, supercls->name); |
7af964d1 A |
958 | if (weakLayoutChanged) { |
959 | _objc_inform("IVARS: gc weak layout changed " | |
960 | "for class %s (super %s)", | |
961 | cls->name, supercls->name); | |
962 | } | |
7c0e6487 A |
963 | } |
964 | cls->ivar_layout = layout_string_create(ivarBitmap); | |
7af964d1 A |
965 | if (weakLayoutChanged) { |
966 | cls->ext->weak_ivar_layout = layout_string_create(weakBitmap); | |
967 | } | |
968 | ||
969 | layout_bitmap_free(weakBitmap); | |
7c0e6487 A |
970 | } |
971 | ||
972 | layout_bitmap_free(ivarBitmap); | |
973 | } | |
974 | ||
975 | // Done! | |
b3962a83 A |
976 | cls->info |= CLS_CONNECTED; |
977 | ||
7af964d1 | 978 | mutex_lock(&classLock); |
b3962a83 A |
979 | |
980 | // Update hash tables. | |
981 | NXHashRemove(unconnected_class_hash, cls); | |
7257e56c A |
982 | oldCls = (Class)NXHashInsert(class_hash, cls); |
983 | objc_addRegisteredClass(cls); | |
b3962a83 A |
984 | |
985 | // Delete unconnected_class_hash if it is now empty. | |
986 | if (NXCountHashTable(unconnected_class_hash) == 0) { | |
987 | NXFreeHashTable(unconnected_class_hash); | |
7257e56c | 988 | unconnected_class_hash = nil; |
b3962a83 A |
989 | } |
990 | ||
7af964d1 A |
991 | // No duplicate classes allowed. |
992 | // Duplicates should have been rejected by _objc_read_classes_from_image. | |
993 | assert(!oldCls); | |
b3962a83 | 994 | |
7af964d1 | 995 | mutex_unlock(&classLock); |
b3962a83 | 996 | |
ee974f79 A |
997 | // Fix up pended class refs to this class, if any |
998 | resolve_references_to_class(cls); | |
999 | ||
b3962a83 A |
1000 | // Connect newly-connectable subclasses |
1001 | resolve_subclasses_of_class(cls); | |
1002 | ||
1003 | // GC debugging: make sure all classes with -dealloc also have -finalize | |
1004 | if (DebugFinalizers) { | |
7257e56c | 1005 | extern IMP findIMPInClass(Class cls, SEL sel); |
b3962a83 A |
1006 | if (findIMPInClass(cls, sel_getUid("dealloc")) && |
1007 | ! findIMPInClass(cls, sel_getUid("finalize"))) | |
1008 | { | |
1009 | _objc_inform("GC: class '%s' implements -dealloc but not -finalize", cls->name); | |
1010 | } | |
1011 | } | |
1012 | ||
1013 | // Debugging: if this class has ivars, make sure this class's ivars don't | |
1014 | // overlap with its super's. This catches some broken fragile base classes. | |
1015 | // Do not use super->instance_size vs. self->ivar[0] to check this. | |
1016 | // Ivars may be packed across instance_size boundaries. | |
1017 | if (DebugFragileSuperclasses && cls->ivars && cls->ivars->ivar_count) { | |
7257e56c | 1018 | Class ivar_cls = supercls; |
b3962a83 A |
1019 | |
1020 | // Find closest superclass that has some ivars, if one exists. | |
1021 | while (ivar_cls && | |
1022 | (!ivar_cls->ivars || ivar_cls->ivars->ivar_count == 0)) | |
1023 | { | |
7257e56c | 1024 | ivar_cls = ivar_cls->superclass; |
b3962a83 A |
1025 | } |
1026 | ||
1027 | if (ivar_cls) { | |
1028 | // Compare superclass's last ivar to this class's first ivar | |
7257e56c | 1029 | old_ivar *super_ivar = |
b3962a83 | 1030 | &ivar_cls->ivars->ivar_list[ivar_cls->ivars->ivar_count - 1]; |
7257e56c | 1031 | old_ivar *self_ivar = |
b3962a83 A |
1032 | &cls->ivars->ivar_list[0]; |
1033 | ||
1034 | // fixme could be smarter about super's ivar size | |
1035 | if (self_ivar->ivar_offset <= super_ivar->ivar_offset) { | |
1036 | _objc_inform("WARNING: ivars of superclass '%s' and " | |
1037 | "subclass '%s' overlap; superclass may have " | |
1038 | "changed since subclass was compiled", | |
1039 | ivar_cls->name, cls->name); | |
1040 | } | |
1041 | } | |
1042 | } | |
1043 | } | |
1044 | ||
1045 | ||
1046 | /*********************************************************************** | |
1047 | * connect_class | |
1048 | * Connect class cls to its superclasses, if possible. | |
1049 | * If cls becomes connected, move it from unconnected_class_hash | |
1050 | * to connected_class_hash. | |
1051 | * Returns TRUE if cls is connected. | |
1052 | * Returns FALSE if cls could not be connected for some reason | |
1053 | * (missing superclass or still-unconnected superclass) | |
1054 | **********************************************************************/ | |
7257e56c | 1055 | static BOOL connect_class(Class cls) |
b3962a83 | 1056 | { |
7257e56c | 1057 | if (cls->isConnected()) { |
b3962a83 A |
1058 | // This class is already connected to its superclass. |
1059 | // Do nothing. | |
1060 | return TRUE; | |
1061 | } | |
7257e56c | 1062 | else if (cls->superclass == nil) { |
b3962a83 A |
1063 | // This class is a root class. |
1064 | // Connect it to itself. | |
1065 | ||
1066 | if (PrintConnecting) { | |
1067 | _objc_inform("CONNECT: class '%s' now connected (root class)", | |
1068 | cls->name); | |
1069 | } | |
1070 | ||
7257e56c | 1071 | really_connect_class(cls, nil); |
b3962a83 A |
1072 | return TRUE; |
1073 | } | |
1074 | else { | |
1075 | // This class is not a root class and is not yet connected. | |
1076 | // Connect it if its superclass and root class are already connected. | |
1077 | // Otherwise, add this class to the to-be-connected list, | |
1078 | // pending the completion of its superclass and root class. | |
1079 | ||
7257e56c A |
1080 | // At this point, cls->superclass and cls->ISA()->ISA() are still STRINGS |
1081 | char *supercls_name = (char *)cls->superclass; | |
1082 | Class supercls; | |
b3962a83 A |
1083 | |
1084 | // YES unconnected, YES class handler | |
7257e56c | 1085 | if (nil == (supercls = look_up_class(supercls_name, YES, YES))) { |
b3962a83 A |
1086 | // Superclass does not exist yet. |
1087 | // pendClassInstallation will handle duplicate pends of this class | |
1088 | pendClassInstallation(cls, supercls_name); | |
1089 | ||
1090 | if (PrintConnecting) { | |
1091 | _objc_inform("CONNECT: class '%s' NOT connected (missing super)", cls->name); | |
1092 | } | |
1093 | return FALSE; | |
1094 | } | |
1095 | ||
1096 | if (! connect_class(supercls)) { | |
1097 | // Superclass exists but is not yet connected. | |
1098 | // pendClassInstallation will handle duplicate pends of this class | |
1099 | pendClassInstallation(cls, supercls_name); | |
1100 | ||
1101 | if (PrintConnecting) { | |
1102 | _objc_inform("CONNECT: class '%s' NOT connected (unconnected super)", cls->name); | |
1103 | } | |
1104 | return FALSE; | |
1105 | } | |
1106 | ||
1107 | // Superclass exists and is connected. | |
1108 | // Connect this class to the superclass. | |
1109 | ||
1110 | if (PrintConnecting) { | |
1111 | _objc_inform("CONNECT: class '%s' now connected", cls->name); | |
1112 | } | |
1113 | ||
1114 | really_connect_class(cls, supercls); | |
1115 | return TRUE; | |
1116 | } | |
1117 | } | |
1118 | ||
1119 | ||
1120 | /*********************************************************************** | |
1121 | * _objc_read_categories_from_image. | |
1122 | * Read all categories from the given image. | |
1123 | * Install them on their parent classes, or register them for later | |
1124 | * installation. | |
1125 | * Returns YES if some method caches now need to be flushed. | |
1126 | **********************************************************************/ | |
1127 | static BOOL _objc_read_categories_from_image (header_info * hi) | |
1128 | { | |
1129 | Module mods; | |
1130 | size_t midx; | |
1131 | BOOL needFlush = NO; | |
1132 | ||
1133 | if (_objcHeaderIsReplacement(hi)) { | |
1134 | // Ignore any categories in this image | |
1135 | return NO; | |
1136 | } | |
1137 | ||
1138 | ||
1139 | // Major loop - process all modules in the header | |
1140 | mods = hi->mod_ptr; | |
1141 | ||
1142 | // NOTE: The module and category lists are traversed backwards | |
1143 | // to preserve the pre-10.4 processing order. Changing the order | |
1144 | // would have a small chance of introducing binary compatibility bugs. | |
1145 | midx = hi->mod_count; | |
1146 | while (midx-- > 0) { | |
1147 | unsigned int index; | |
1148 | unsigned int total; | |
1149 | ||
1150 | // Nothing to do for a module without a symbol table | |
7257e56c | 1151 | if (mods[midx].symtab == nil) |
b3962a83 A |
1152 | continue; |
1153 | ||
1154 | // Total entries in symbol table (class entries followed | |
1155 | // by category entries) | |
1156 | total = mods[midx].symtab->cls_def_cnt + | |
1157 | mods[midx].symtab->cat_def_cnt; | |
1158 | ||
1159 | // Minor loop - register all categories from given module | |
1160 | index = total; | |
1161 | while (index-- > mods[midx].symtab->cls_def_cnt) { | |
7257e56c | 1162 | old_category *cat = (old_category *)mods[midx].symtab->defs[index]; |
b3962a83 A |
1163 | needFlush |= _objc_register_category(cat, (int)mods[midx].version); |
1164 | } | |
1165 | } | |
1166 | ||
1167 | return needFlush; | |
1168 | } | |
1169 | ||
1170 | ||
1171 | /*********************************************************************** | |
1172 | * _objc_read_classes_from_image. | |
1173 | * Read classes from the given image, perform assorted minor fixups, | |
1174 | * scan for +load implementation. | |
1175 | * Does not connect classes to superclasses. | |
1176 | * Does attach pended categories to the classes. | |
1177 | * Adds all classes to unconnected_class_hash. class_hash is unchanged. | |
1178 | **********************************************************************/ | |
1179 | static void _objc_read_classes_from_image(header_info *hi) | |
1180 | { | |
1181 | unsigned int index; | |
1182 | unsigned int midx; | |
1183 | Module mods; | |
7af964d1 | 1184 | int isBundle = headerIsBundle(hi); |
b3962a83 A |
1185 | |
1186 | if (_objcHeaderIsReplacement(hi)) { | |
1187 | // Ignore any classes in this image | |
1188 | return; | |
1189 | } | |
1190 | ||
1191 | // class_hash starts small, enough only for libobjc itself. | |
1192 | // If other Objective-C libraries are found, immediately resize | |
1193 | // class_hash, assuming that Foundation and AppKit are about | |
1194 | // to add lots of classes. | |
7af964d1 A |
1195 | mutex_lock(&classLock); |
1196 | if (hi->mhdr != libobjc_header && _NXHashCapacity(class_hash) < 1024) { | |
b3962a83 A |
1197 | _NXHashRehashToCapacity(class_hash, 1024); |
1198 | } | |
7af964d1 | 1199 | mutex_unlock(&classLock); |
b3962a83 A |
1200 | |
1201 | // Major loop - process all modules in the image | |
1202 | mods = hi->mod_ptr; | |
1203 | for (midx = 0; midx < hi->mod_count; midx += 1) | |
1204 | { | |
1205 | // Skip module containing no classes | |
7257e56c | 1206 | if (mods[midx].symtab == nil) |
b3962a83 A |
1207 | continue; |
1208 | ||
1209 | // Minor loop - process all the classes in given module | |
1210 | for (index = 0; index < mods[midx].symtab->cls_def_cnt; index += 1) | |
1211 | { | |
7257e56c | 1212 | Class newCls, oldCls; |
7af964d1 | 1213 | BOOL rejected; |
b3962a83 A |
1214 | |
1215 | // Locate the class description pointer | |
7257e56c | 1216 | newCls = (Class)mods[midx].symtab->defs[index]; |
b3962a83 A |
1217 | |
1218 | // Classes loaded from Mach-O bundles can be unloaded later. | |
7257e56c | 1219 | // Nothing uses this class yet, so cls->setInfo is not needed. |
b3962a83 | 1220 | if (isBundle) newCls->info |= CLS_FROM_BUNDLE; |
7257e56c | 1221 | if (isBundle) newCls->ISA()->info |= CLS_FROM_BUNDLE; |
b3962a83 | 1222 | |
7257e56c A |
1223 | // Use common static empty cache instead of nil |
1224 | if (newCls->cache == nil) | |
b3962a83 | 1225 | newCls->cache = (Cache) &_objc_empty_cache; |
7257e56c A |
1226 | if (newCls->ISA()->cache == nil) |
1227 | newCls->ISA()->cache = (Cache) &_objc_empty_cache; | |
b3962a83 A |
1228 | |
1229 | // Set metaclass version | |
7257e56c | 1230 | newCls->ISA()->version = mods[midx].version; |
b3962a83 | 1231 | |
7257e56c | 1232 | // methodLists is nil or a single list, not an array |
b3962a83 | 1233 | newCls->info |= CLS_NO_METHOD_ARRAY|CLS_NO_PROPERTY_ARRAY; |
7257e56c | 1234 | newCls->ISA()->info |= CLS_NO_METHOD_ARRAY|CLS_NO_PROPERTY_ARRAY; |
b3962a83 A |
1235 | |
1236 | // class has no subclasses for cache flushing | |
1237 | newCls->info |= CLS_LEAF; | |
7257e56c | 1238 | newCls->ISA()->info |= CLS_LEAF; |
b3962a83 A |
1239 | |
1240 | if (mods[midx].version >= 6) { | |
1241 | // class structure has ivar_layout and ext fields | |
1242 | newCls->info |= CLS_EXT; | |
7257e56c | 1243 | newCls->ISA()->info |= CLS_EXT; |
b3962a83 A |
1244 | } |
1245 | ||
1246 | // Check for +load implementation before categories are attached | |
7257e56c A |
1247 | if (_class_hasLoadMethod(newCls)) { |
1248 | newCls->ISA()->info |= CLS_HAS_LOAD_METHOD; | |
b3962a83 A |
1249 | } |
1250 | ||
1251 | // Install into unconnected_class_hash. | |
7af964d1 | 1252 | mutex_lock(&classLock); |
b3962a83 A |
1253 | |
1254 | if (future_class_hash) { | |
7257e56c | 1255 | Class futureCls = (Class) |
b3962a83 A |
1256 | NXHashRemove(future_class_hash, newCls); |
1257 | if (futureCls) { | |
1258 | // Another class structure for this class was already | |
1259 | // prepared by objc_getFutureClass(). Use it instead. | |
1260 | _free_internal((char *)futureCls->name); | |
7257e56c | 1261 | memcpy(futureCls, newCls, sizeof(objc_class)); |
b3962a83 A |
1262 | setOriginalClassForFutureClass(futureCls, newCls); |
1263 | newCls = futureCls; | |
1264 | ||
1265 | if (NXCountHashTable(future_class_hash) == 0) { | |
1266 | NXFreeHashTable(future_class_hash); | |
7257e56c | 1267 | future_class_hash = nil; |
b3962a83 A |
1268 | } |
1269 | } | |
1270 | } | |
1271 | ||
1272 | if (!unconnected_class_hash) { | |
1273 | unconnected_class_hash = | |
1274 | NXCreateHashTableFromZone(classHashPrototype, 128, | |
7257e56c | 1275 | nil, _objc_internal_zone()); |
b3962a83 A |
1276 | } |
1277 | ||
7257e56c A |
1278 | if ((oldCls = (Class)NXHashGet(class_hash, newCls)) || |
1279 | (oldCls = (Class)NXHashGet(unconnected_class_hash, newCls))) | |
7af964d1 A |
1280 | { |
1281 | // Another class with this name exists. Complain and reject. | |
7257e56c | 1282 | inform_duplicate(newCls->name, oldCls, newCls); |
7af964d1 A |
1283 | rejected = YES; |
1284 | } | |
1285 | else { | |
1286 | NXHashInsert(unconnected_class_hash, newCls); | |
1287 | rejected = NO; | |
b3962a83 A |
1288 | } |
1289 | ||
7af964d1 | 1290 | mutex_unlock(&classLock); |
b3962a83 | 1291 | |
7af964d1 | 1292 | if (!rejected) { |
7af964d1 A |
1293 | // Attach pended categories for this class, if any |
1294 | resolve_categories_for_class(newCls); | |
1295 | } | |
b3962a83 A |
1296 | } |
1297 | } | |
1298 | } | |
1299 | ||
1300 | ||
1301 | /*********************************************************************** | |
1302 | * _objc_connect_classes_from_image. | |
1303 | * Connect the classes in the given image to their superclasses, | |
1304 | * or register them for later connection if any superclasses are missing. | |
1305 | **********************************************************************/ | |
1306 | static void _objc_connect_classes_from_image(header_info *hi) | |
1307 | { | |
1308 | unsigned int index; | |
1309 | unsigned int midx; | |
1310 | Module mods; | |
1311 | BOOL replacement = _objcHeaderIsReplacement(hi); | |
1312 | ||
1313 | // Major loop - process all modules in the image | |
1314 | mods = hi->mod_ptr; | |
1315 | for (midx = 0; midx < hi->mod_count; midx += 1) | |
1316 | { | |
1317 | // Skip module containing no classes | |
7257e56c | 1318 | if (mods[midx].symtab == nil) |
b3962a83 A |
1319 | continue; |
1320 | ||
1321 | // Minor loop - process all the classes in given module | |
1322 | for (index = 0; index < mods[midx].symtab->cls_def_cnt; index += 1) | |
1323 | { | |
7257e56c | 1324 | Class cls = (Class)mods[midx].symtab->defs[index]; |
b3962a83 A |
1325 | if (! replacement) { |
1326 | BOOL connected; | |
7257e56c | 1327 | Class futureCls = getFutureClassForOriginalClass(cls); |
b3962a83 A |
1328 | if (futureCls) { |
1329 | // objc_getFutureClass() requested a different class | |
7257e56c | 1330 | // struct. Fix up the original struct's superclass |
b3962a83 A |
1331 | // field for [super ...] use, but otherwise perform |
1332 | // fixups on the new class struct only. | |
7257e56c A |
1333 | const char *super_name = (const char *) cls->superclass; |
1334 | if (super_name) cls->superclass = objc_getClass(super_name); | |
b3962a83 A |
1335 | cls = futureCls; |
1336 | } | |
1337 | connected = connect_class(cls); | |
1338 | if (connected && callbackFunction) { | |
7257e56c | 1339 | (*callbackFunction)(cls, 0); |
b3962a83 A |
1340 | } |
1341 | } else { | |
7257e56c A |
1342 | // Replacement image - fix up superclass only (#3704817) |
1343 | // And metaclass's superclass (#5351107) | |
1344 | const char *super_name = (const char *) cls->superclass; | |
b3962a83 | 1345 | if (super_name) { |
7257e56c | 1346 | cls->superclass = objc_getClass(super_name); |
b3962a83 | 1347 | // metaclass's superclass is superclass's metaclass |
7257e56c | 1348 | cls->ISA()->superclass = cls->superclass->ISA(); |
b3962a83 A |
1349 | } else { |
1350 | // Replacement for a root class | |
7257e56c | 1351 | // cls->superclass already nil |
b3962a83 | 1352 | // root metaclass's superclass is root class |
7257e56c | 1353 | cls->ISA()->superclass = cls; |
b3962a83 A |
1354 | } |
1355 | } | |
1356 | } | |
1357 | } | |
1358 | } | |
1359 | ||
1360 | ||
1361 | /*********************************************************************** | |
1362 | * _objc_map_class_refs_for_image. Convert the class ref entries from | |
1363 | * a class name string pointer to a class pointer. If the class does | |
1364 | * not yet exist, the reference is added to a list of pending references | |
1365 | * to be fixed up at a later date. | |
1366 | **********************************************************************/ | |
7257e56c | 1367 | static void fix_class_ref(Class *ref, const char *name, BOOL isMeta) |
b3962a83 | 1368 | { |
7257e56c | 1369 | Class cls; |
b3962a83 A |
1370 | |
1371 | // Get pointer to class of this name | |
ee974f79 A |
1372 | // NO unconnected, YES class loader |
1373 | // (real class with weak-missing superclass is unconnected now) | |
7257e56c | 1374 | cls = look_up_class(name, NO, YES); |
b3962a83 A |
1375 | if (cls) { |
1376 | // Referenced class exists. Fix up the reference. | |
7257e56c | 1377 | *ref = isMeta ? cls->ISA() : cls; |
b3962a83 | 1378 | } else { |
7257e56c | 1379 | // Referenced class does not exist yet. Insert nil for now |
ee974f79 | 1380 | // (weak-linking) and fix up the reference if the class arrives later. |
b3962a83 | 1381 | pendClassReference (ref, name, isMeta); |
7257e56c | 1382 | *ref = nil; |
b3962a83 A |
1383 | } |
1384 | } | |
1385 | ||
1386 | static void _objc_map_class_refs_for_image (header_info * hi) | |
1387 | { | |
7257e56c | 1388 | Class *cls_refs; |
b3962a83 A |
1389 | size_t count; |
1390 | unsigned int index; | |
1391 | ||
1392 | // Locate class refs in image | |
1393 | cls_refs = _getObjcClassRefs (hi, &count); | |
1394 | if (cls_refs) { | |
1395 | // Process each class ref | |
1396 | for (index = 0; index < count; index += 1) { | |
1397 | // Ref is initially class name char* | |
1398 | const char *name = (const char *) cls_refs[index]; | |
7af964d1 | 1399 | if (!name) continue; |
b3962a83 A |
1400 | fix_class_ref(&cls_refs[index], name, NO /*never meta*/); |
1401 | } | |
1402 | } | |
1403 | } | |
1404 | ||
1405 | ||
1406 | /*********************************************************************** | |
1407 | * _objc_remove_pending_class_refs_in_image | |
1408 | * Delete any pending class ref fixups for class refs in the given image, | |
1409 | * because the image is about to be unloaded. | |
1410 | **********************************************************************/ | |
7257e56c | 1411 | static void removePendingReferences(Class *refs, size_t count) |
b3962a83 | 1412 | { |
7257e56c | 1413 | Class *end = refs + count; |
b3962a83 A |
1414 | |
1415 | if (!refs) return; | |
1416 | if (!pendingClassRefsMap) return; | |
1417 | ||
1418 | // Search the pending class ref table for class refs in this range. | |
7257e56c | 1419 | // The class refs may have already been stomped with nil, |
b3962a83 | 1420 | // so there's no way to recover the original class name. |
7af964d1 A |
1421 | |
1422 | { | |
1423 | const char *key; | |
1424 | PendingClassRef *pending; | |
1425 | NXMapState state = NXInitMapState(pendingClassRefsMap); | |
1426 | while(NXNextMapState(pendingClassRefsMap, &state, | |
1427 | (const void **)&key, (const void **)&pending)) | |
1428 | { | |
7257e56c | 1429 | for ( ; pending != nil; pending = pending->next) { |
7af964d1 | 1430 | if (pending->ref >= refs && pending->ref < end) { |
7257e56c | 1431 | pending->ref = nil; |
7af964d1 | 1432 | } |
b3962a83 A |
1433 | } |
1434 | } | |
1435 | } | |
1436 | } | |
1437 | ||
1438 | static void _objc_remove_pending_class_refs_in_image(header_info *hi) | |
1439 | { | |
7257e56c | 1440 | Class *cls_refs; |
b3962a83 A |
1441 | size_t count; |
1442 | ||
1443 | // Locate class refs in this image | |
1444 | cls_refs = _getObjcClassRefs(hi, &count); | |
1445 | removePendingReferences(cls_refs, count); | |
1446 | } | |
1447 | ||
1448 | ||
1449 | /*********************************************************************** | |
1450 | * map_selrefs. For each selector in the specified array, | |
1451 | * replace the name pointer with a uniqued selector. | |
1452 | * If copy is TRUE, all selector data is always copied. This is used | |
1453 | * for registering selectors from unloadable bundles, so the selector | |
1454 | * can still be used after the bundle's data segment is unmapped. | |
1455 | * Returns YES if dst was written to, NO if it was unchanged. | |
1456 | **********************************************************************/ | |
7af964d1 | 1457 | static inline void map_selrefs(SEL *sels, size_t count, BOOL copy) |
b3962a83 | 1458 | { |
b3962a83 A |
1459 | size_t index; |
1460 | ||
7af964d1 | 1461 | if (!sels) return; |
b3962a83 A |
1462 | |
1463 | sel_lock(); | |
1464 | ||
1465 | // Process each selector | |
7af964d1 | 1466 | for (index = 0; index < count; index += 1) |
b3962a83 A |
1467 | { |
1468 | SEL sel; | |
1469 | ||
1470 | // Lookup pointer to uniqued string | |
7af964d1 | 1471 | sel = sel_registerNameNoLock((const char *) sels[index], copy); |
b3962a83 A |
1472 | |
1473 | // Replace this selector with uniqued one (avoid | |
1474 | // modifying the VM page if this would be a NOP) | |
7af964d1 A |
1475 | if (sels[index] != sel) { |
1476 | sels[index] = sel; | |
b3962a83 A |
1477 | } |
1478 | } | |
1479 | ||
1480 | sel_unlock(); | |
b3962a83 A |
1481 | } |
1482 | ||
1483 | ||
1484 | /*********************************************************************** | |
1485 | * map_method_descs. For each method in the specified method list, | |
1486 | * replace the name pointer with a uniqued selector. | |
1487 | * If copy is TRUE, all selector data is always copied. This is used | |
1488 | * for registering selectors from unloadable bundles, so the selector | |
1489 | * can still be used after the bundle's data segment is unmapped. | |
1490 | **********************************************************************/ | |
1491 | static void map_method_descs (struct objc_method_description_list * methods, BOOL copy) | |
1492 | { | |
7af964d1 | 1493 | int index; |
b3962a83 A |
1494 | |
1495 | if (!methods) return; | |
1496 | ||
1497 | sel_lock(); | |
1498 | ||
1499 | // Process each method | |
1500 | for (index = 0; index < methods->count; index += 1) | |
1501 | { | |
1502 | struct objc_method_description * method; | |
1503 | SEL sel; | |
1504 | ||
1505 | // Get method entry to fix up | |
1506 | method = &methods->list[index]; | |
1507 | ||
1508 | // Lookup pointer to uniqued string | |
1509 | sel = sel_registerNameNoLock((const char *) method->name, copy); | |
1510 | ||
1511 | // Replace this selector with uniqued one (avoid | |
1512 | // modifying the VM page if this would be a NOP) | |
1513 | if (method->name != sel) | |
1514 | method->name = sel; | |
1515 | } | |
1516 | ||
1517 | sel_unlock(); | |
1518 | } | |
1519 | ||
1520 | ||
1521 | /*********************************************************************** | |
1522 | * ext_for_protocol | |
1523 | * Returns the protocol extension for the given protocol. | |
7257e56c | 1524 | * Returns nil if the protocol has no extension. |
b3962a83 | 1525 | **********************************************************************/ |
7257e56c | 1526 | static old_protocol_ext *ext_for_protocol(old_protocol *proto) |
b3962a83 | 1527 | { |
7257e56c A |
1528 | if (!proto) return nil; |
1529 | if (!protocol_ext_map) return nil; | |
1530 | else return (old_protocol_ext *)NXMapGet(protocol_ext_map, proto); | |
b3962a83 A |
1531 | } |
1532 | ||
1533 | ||
1534 | /*********************************************************************** | |
1535 | * lookup_method | |
1536 | * Search a protocol method list for a selector. | |
1537 | **********************************************************************/ | |
1538 | static struct objc_method_description * | |
1539 | lookup_method(struct objc_method_description_list *mlist, SEL aSel) | |
1540 | { | |
1541 | if (mlist) { | |
1542 | int i; | |
1543 | for (i = 0; i < mlist->count; i++) { | |
1544 | if (mlist->list[i].name == aSel) { | |
1545 | return mlist->list+i; | |
1546 | } | |
1547 | } | |
1548 | } | |
7257e56c | 1549 | return nil; |
b3962a83 A |
1550 | } |
1551 | ||
1552 | ||
1553 | /*********************************************************************** | |
1554 | * lookup_protocol_method | |
cd5f04f5 A |
1555 | * Search for a selector in a protocol |
1556 | * (and optionally recursively all incorporated protocols) | |
b3962a83 | 1557 | **********************************************************************/ |
cd5f04f5 | 1558 | struct objc_method_description * |
7257e56c | 1559 | lookup_protocol_method(old_protocol *proto, SEL aSel, |
cd5f04f5 A |
1560 | BOOL isRequiredMethod, BOOL isInstanceMethod, |
1561 | BOOL recursive) | |
b3962a83 | 1562 | { |
7257e56c A |
1563 | struct objc_method_description *m = nil; |
1564 | old_protocol_ext *ext; | |
b3962a83 A |
1565 | |
1566 | if (isRequiredMethod) { | |
1567 | if (isInstanceMethod) { | |
1568 | m = lookup_method(proto->instance_methods, aSel); | |
1569 | } else { | |
1570 | m = lookup_method(proto->class_methods, aSel); | |
1571 | } | |
1572 | } else if ((ext = ext_for_protocol(proto))) { | |
1573 | if (isInstanceMethod) { | |
1574 | m = lookup_method(ext->optional_instance_methods, aSel); | |
1575 | } else { | |
1576 | m = lookup_method(ext->optional_class_methods, aSel); | |
1577 | } | |
1578 | } | |
1579 | ||
cd5f04f5 | 1580 | if (!m && recursive && proto->protocol_list) { |
b3962a83 A |
1581 | int i; |
1582 | for (i = 0; !m && i < proto->protocol_list->count; i++) { | |
1583 | m = lookup_protocol_method(proto->protocol_list->list[i], aSel, | |
cd5f04f5 | 1584 | isRequiredMethod,isInstanceMethod,true); |
b3962a83 A |
1585 | } |
1586 | } | |
1587 | ||
1588 | return m; | |
1589 | } | |
1590 | ||
1591 | ||
1592 | /*********************************************************************** | |
1593 | * protocol_getName | |
1594 | * Returns the name of the given protocol. | |
1595 | **********************************************************************/ | |
1596 | const char *protocol_getName(Protocol *p) | |
1597 | { | |
7257e56c | 1598 | old_protocol *proto = oldprotocol(p); |
b3962a83 A |
1599 | if (!proto) return "nil"; |
1600 | return proto->protocol_name; | |
1601 | } | |
1602 | ||
1603 | ||
1604 | /*********************************************************************** | |
1605 | * protocol_getMethodDescription | |
1606 | * Returns the description of a named method. | |
1607 | * Searches either required or optional methods. | |
1608 | * Searches either instance or class methods. | |
1609 | **********************************************************************/ | |
1610 | struct objc_method_description | |
1611 | protocol_getMethodDescription(Protocol *p, SEL aSel, | |
1612 | BOOL isRequiredMethod, BOOL isInstanceMethod) | |
1613 | { | |
7257e56c A |
1614 | struct objc_method_description empty = {nil, nil}; |
1615 | old_protocol *proto = oldprotocol(p); | |
7af964d1 A |
1616 | struct objc_method_description *desc; |
1617 | if (!proto) return empty; | |
b3962a83 | 1618 | |
7af964d1 | 1619 | desc = lookup_protocol_method(proto, aSel, |
cd5f04f5 | 1620 | isRequiredMethod, isInstanceMethod, true); |
b3962a83 | 1621 | if (desc) return *desc; |
7af964d1 | 1622 | else return empty; |
b3962a83 A |
1623 | } |
1624 | ||
1625 | ||
1626 | /*********************************************************************** | |
1627 | * protocol_copyMethodDescriptionList | |
1628 | * Returns an array of method descriptions from a protocol. | |
1629 | * Copies either required or optional methods. | |
1630 | * Copies either instance or class methods. | |
1631 | **********************************************************************/ | |
1632 | struct objc_method_description * | |
1633 | protocol_copyMethodDescriptionList(Protocol *p, | |
1634 | BOOL isRequiredMethod, | |
1635 | BOOL isInstanceMethod, | |
1636 | unsigned int *outCount) | |
1637 | { | |
7257e56c A |
1638 | struct objc_method_description_list *mlist = nil; |
1639 | old_protocol *proto = oldprotocol(p); | |
1640 | old_protocol_ext *ext; | |
7af964d1 A |
1641 | unsigned int i, count; |
1642 | struct objc_method_description *result; | |
b3962a83 A |
1643 | |
1644 | if (!proto) { | |
1645 | if (outCount) *outCount = 0; | |
7257e56c | 1646 | return nil; |
b3962a83 A |
1647 | } |
1648 | ||
1649 | if (isRequiredMethod) { | |
1650 | if (isInstanceMethod) { | |
1651 | mlist = proto->instance_methods; | |
1652 | } else { | |
1653 | mlist = proto->class_methods; | |
1654 | } | |
1655 | } else if ((ext = ext_for_protocol(proto))) { | |
1656 | if (isInstanceMethod) { | |
1657 | mlist = ext->optional_instance_methods; | |
1658 | } else { | |
1659 | mlist = ext->optional_class_methods; | |
1660 | } | |
1661 | } | |
1662 | ||
1663 | if (!mlist) { | |
1664 | if (outCount) *outCount = 0; | |
7257e56c | 1665 | return nil; |
b3962a83 A |
1666 | } |
1667 | ||
7af964d1 | 1668 | count = mlist->count; |
7257e56c | 1669 | result = (struct objc_method_description *) |
b3962a83 A |
1670 | calloc(count + 1, sizeof(struct objc_method_description)); |
1671 | for (i = 0; i < count; i++) { | |
1672 | result[i] = mlist->list[i]; | |
1673 | } | |
1674 | ||
1675 | if (outCount) *outCount = count; | |
1676 | return result; | |
1677 | } | |
1678 | ||
1679 | ||
8972963c | 1680 | objc_property_t protocol_getProperty(Protocol *p, const char *name, |
b3962a83 A |
1681 | BOOL isRequiredProperty, BOOL isInstanceProperty) |
1682 | { | |
7257e56c A |
1683 | old_protocol *proto = oldprotocol(p); |
1684 | old_protocol_ext *ext; | |
1685 | old_protocol_list *proto_list; | |
b3962a83 | 1686 | |
7257e56c | 1687 | if (!proto || !name) return nil; |
b3962a83 A |
1688 | |
1689 | if (!isRequiredProperty || !isInstanceProperty) { | |
1690 | // Only required instance properties are currently supported | |
7257e56c | 1691 | return nil; |
b3962a83 A |
1692 | } |
1693 | ||
b3962a83 | 1694 | if ((ext = ext_for_protocol(proto))) { |
7257e56c | 1695 | old_property_list *plist; |
b3962a83 A |
1696 | if ((plist = ext->instance_properties)) { |
1697 | uint32_t i; | |
1698 | for (i = 0; i < plist->count; i++) { | |
7257e56c | 1699 | old_property *prop = property_list_nth(plist, i); |
b3962a83 | 1700 | if (0 == strcmp(name, prop->name)) { |
8972963c | 1701 | return (objc_property_t)prop; |
b3962a83 A |
1702 | } |
1703 | } | |
1704 | } | |
1705 | } | |
1706 | ||
cd5f04f5 | 1707 | if ((proto_list = proto->protocol_list)) { |
b3962a83 | 1708 | int i; |
cd5f04f5 | 1709 | for (i = 0; i < proto_list->count; i++) { |
8972963c | 1710 | objc_property_t prop = |
cd5f04f5 | 1711 | protocol_getProperty((Protocol *)proto_list->list[i], name, |
b3962a83 A |
1712 | isRequiredProperty, isInstanceProperty); |
1713 | if (prop) return prop; | |
1714 | } | |
1715 | } | |
1716 | ||
7257e56c | 1717 | return nil; |
b3962a83 A |
1718 | } |
1719 | ||
1720 | ||
8972963c | 1721 | objc_property_t *protocol_copyPropertyList(Protocol *p, unsigned int *outCount) |
b3962a83 | 1722 | { |
7257e56c A |
1723 | old_property **result = nil; |
1724 | old_protocol_ext *ext; | |
1725 | old_property_list *plist; | |
b3962a83 | 1726 | |
7257e56c | 1727 | old_protocol *proto = oldprotocol(p); |
b3962a83 A |
1728 | if (! (ext = ext_for_protocol(proto))) { |
1729 | if (outCount) *outCount = 0; | |
7257e56c | 1730 | return nil; |
b3962a83 A |
1731 | } |
1732 | ||
7af964d1 | 1733 | plist = ext->instance_properties; |
b3962a83 A |
1734 | result = copyPropertyList(plist, outCount); |
1735 | ||
8972963c | 1736 | return (objc_property_t *)result; |
b3962a83 A |
1737 | } |
1738 | ||
1739 | ||
1740 | /*********************************************************************** | |
1741 | * protocol_copyProtocolList | |
1742 | * Copies this protocol's incorporated protocols. | |
1743 | * Does not copy those protocol's incorporated protocols in turn. | |
1744 | **********************************************************************/ | |
8972963c A |
1745 | Protocol * __unsafe_unretained * |
1746 | protocol_copyProtocolList(Protocol *p, unsigned int *outCount) | |
b3962a83 A |
1747 | { |
1748 | unsigned int count = 0; | |
7257e56c A |
1749 | Protocol **result = nil; |
1750 | old_protocol *proto = oldprotocol(p); | |
b3962a83 A |
1751 | |
1752 | if (!proto) { | |
1753 | if (outCount) *outCount = 0; | |
7257e56c | 1754 | return nil; |
b3962a83 A |
1755 | } |
1756 | ||
1757 | if (proto->protocol_list) { | |
1758 | count = (unsigned int)proto->protocol_list->count; | |
1759 | } | |
1760 | if (count > 0) { | |
7af964d1 | 1761 | unsigned int i; |
7257e56c | 1762 | result = (Protocol **)malloc((count+1) * sizeof(Protocol *)); |
b3962a83 | 1763 | |
b3962a83 A |
1764 | for (i = 0; i < count; i++) { |
1765 | result[i] = (Protocol *)proto->protocol_list->list[i]; | |
1766 | } | |
7257e56c | 1767 | result[i] = nil; |
b3962a83 A |
1768 | } |
1769 | ||
1770 | if (outCount) *outCount = count; | |
1771 | return result; | |
1772 | } | |
1773 | ||
1774 | ||
1775 | BOOL protocol_conformsToProtocol(Protocol *self_gen, Protocol *other_gen) | |
1776 | { | |
7257e56c A |
1777 | old_protocol *self = oldprotocol(self_gen); |
1778 | old_protocol *other = oldprotocol(other_gen); | |
b3962a83 A |
1779 | |
1780 | if (!self || !other) { | |
1781 | return NO; | |
1782 | } | |
1783 | ||
1784 | if (0 == strcmp(self->protocol_name, other->protocol_name)) { | |
1785 | return YES; | |
1786 | } | |
1787 | ||
1788 | if (self->protocol_list) { | |
1789 | int i; | |
1790 | for (i = 0; i < self->protocol_list->count; i++) { | |
7257e56c | 1791 | old_protocol *proto = self->protocol_list->list[i]; |
b3962a83 A |
1792 | if (0 == strcmp(other->protocol_name, proto->protocol_name)) { |
1793 | return YES; | |
1794 | } | |
1795 | if (protocol_conformsToProtocol((Protocol *)proto, other_gen)) { | |
1796 | return YES; | |
1797 | } | |
1798 | } | |
1799 | } | |
1800 | ||
1801 | return NO; | |
1802 | } | |
1803 | ||
1804 | ||
1805 | BOOL protocol_isEqual(Protocol *self, Protocol *other) | |
1806 | { | |
1807 | if (self == other) return YES; | |
1808 | if (!self || !other) return NO; | |
1809 | ||
1810 | if (!protocol_conformsToProtocol(self, other)) return NO; | |
1811 | if (!protocol_conformsToProtocol(other, self)) return NO; | |
1812 | ||
1813 | return YES; | |
1814 | } | |
1815 | ||
1816 | ||
cd5f04f5 A |
1817 | /*********************************************************************** |
1818 | * _protocol_getMethodTypeEncoding | |
1819 | * Return the @encode string for the requested protocol method. | |
7257e56c | 1820 | * Returns nil if the compiler did not emit any extended @encode data. |
cd5f04f5 A |
1821 | * Locking: runtimeLock must not be held by the caller |
1822 | **********************************************************************/ | |
1823 | const char * | |
1824 | _protocol_getMethodTypeEncoding(Protocol *proto_gen, SEL sel, | |
1825 | BOOL isRequiredMethod, BOOL isInstanceMethod) | |
1826 | { | |
7257e56c A |
1827 | old_protocol *proto = oldprotocol(proto_gen); |
1828 | if (!proto) return nil; | |
1829 | old_protocol_ext *ext = ext_for_protocol(proto); | |
1830 | if (!ext) return nil; | |
1831 | if (ext->size < offsetof(old_protocol_ext, extendedMethodTypes) + sizeof(ext->extendedMethodTypes)) return nil; | |
1832 | if (! ext->extendedMethodTypes) return nil; | |
cd5f04f5 A |
1833 | |
1834 | struct objc_method_description *m = | |
1835 | lookup_protocol_method(proto, sel, | |
1836 | isRequiredMethod, isInstanceMethod, false); | |
1837 | if (!m) { | |
1838 | // No method with that name. Search incorporated protocols. | |
1839 | if (proto->protocol_list) { | |
1840 | for (int i = 0; i < proto->protocol_list->count; i++) { | |
1841 | const char *enc = | |
1842 | _protocol_getMethodTypeEncoding((Protocol *)proto->protocol_list->list[i], sel, isRequiredMethod, isInstanceMethod); | |
1843 | if (enc) return enc; | |
1844 | } | |
1845 | } | |
7257e56c | 1846 | return nil; |
cd5f04f5 A |
1847 | } |
1848 | ||
1849 | int i = 0; | |
1850 | if (isRequiredMethod && isInstanceMethod) { | |
1851 | i += ((uintptr_t)m - (uintptr_t)proto->instance_methods) / sizeof(proto->instance_methods->list[0]); | |
1852 | goto done; | |
1853 | } else if (proto->instance_methods) { | |
1854 | i += proto->instance_methods->count; | |
1855 | } | |
1856 | ||
1857 | if (isRequiredMethod && !isInstanceMethod) { | |
1858 | i += ((uintptr_t)m - (uintptr_t)proto->class_methods) / sizeof(proto->class_methods->list[0]); | |
1859 | goto done; | |
1860 | } else if (proto->class_methods) { | |
1861 | i += proto->class_methods->count; | |
1862 | } | |
1863 | ||
1864 | if (!isRequiredMethod && isInstanceMethod) { | |
1865 | i += ((uintptr_t)m - (uintptr_t)ext->optional_instance_methods) / sizeof(ext->optional_instance_methods->list[0]); | |
1866 | goto done; | |
1867 | } else if (ext->optional_instance_methods) { | |
1868 | i += ext->optional_instance_methods->count; | |
1869 | } | |
1870 | ||
1871 | if (!isRequiredMethod && !isInstanceMethod) { | |
1872 | i += ((uintptr_t)m - (uintptr_t)ext->optional_class_methods) / sizeof(ext->optional_class_methods->list[0]); | |
1873 | goto done; | |
1874 | } else if (ext->optional_class_methods) { | |
1875 | i += ext->optional_class_methods->count; | |
1876 | } | |
1877 | ||
1878 | done: | |
1879 | return ext->extendedMethodTypes[i]; | |
1880 | } | |
1881 | ||
1882 | ||
8972963c A |
1883 | /*********************************************************************** |
1884 | * objc_allocateProtocol | |
1885 | * Creates a new protocol. The protocol may not be used until | |
1886 | * objc_registerProtocol() is called. | |
7257e56c | 1887 | * Returns nil if a protocol with the same name already exists. |
8972963c A |
1888 | * Locking: acquires classLock |
1889 | **********************************************************************/ | |
1890 | Protocol * | |
1891 | objc_allocateProtocol(const char *name) | |
1892 | { | |
7257e56c | 1893 | Class cls = objc_getClass("__IncompleteProtocol"); |
8972963c A |
1894 | |
1895 | mutex_lock(&classLock); | |
1896 | ||
1897 | if (NXMapGet(protocol_map, name)) { | |
1898 | mutex_unlock(&classLock); | |
7257e56c | 1899 | return nil; |
8972963c A |
1900 | } |
1901 | ||
7257e56c A |
1902 | old_protocol *result = (old_protocol *) |
1903 | _calloc_internal(1, sizeof(old_protocol) | |
1904 | + sizeof(old_protocol_ext)); | |
1905 | old_protocol_ext *ext = (old_protocol_ext *)(result+1); | |
8972963c A |
1906 | |
1907 | result->isa = cls; | |
1908 | result->protocol_name = _strdup_internal(name); | |
7257e56c | 1909 | ext->size = sizeof(old_protocol_ext); |
8972963c A |
1910 | |
1911 | // fixme reserve name without installing | |
1912 | ||
1913 | NXMapInsert(protocol_ext_map, result, result+1); | |
1914 | ||
1915 | mutex_unlock(&classLock); | |
1916 | ||
1917 | return (Protocol *)result; | |
1918 | } | |
1919 | ||
1920 | ||
1921 | /*********************************************************************** | |
1922 | * objc_registerProtocol | |
1923 | * Registers a newly-constructed protocol. The protocol is now | |
1924 | * ready for use and immutable. | |
1925 | * Locking: acquires classLock | |
1926 | **********************************************************************/ | |
1927 | void objc_registerProtocol(Protocol *proto_gen) | |
1928 | { | |
7257e56c | 1929 | old_protocol *proto = oldprotocol(proto_gen); |
8972963c | 1930 | |
7257e56c A |
1931 | Class oldcls = objc_getClass("__IncompleteProtocol"); |
1932 | Class cls = objc_getClass("Protocol"); | |
8972963c A |
1933 | |
1934 | mutex_lock(&classLock); | |
1935 | ||
1936 | if (proto->isa == cls) { | |
1937 | _objc_inform("objc_registerProtocol: protocol '%s' was already " | |
1938 | "registered!", proto->protocol_name); | |
1939 | mutex_unlock(&classLock); | |
1940 | return; | |
1941 | } | |
1942 | if (proto->isa != oldcls) { | |
1943 | _objc_inform("objc_registerProtocol: protocol '%s' was not allocated " | |
1944 | "with objc_allocateProtocol!", proto->protocol_name); | |
1945 | mutex_unlock(&classLock); | |
1946 | return; | |
1947 | } | |
1948 | ||
1949 | proto->isa = cls; | |
1950 | ||
1951 | NXMapKeyCopyingInsert(protocol_map, proto->protocol_name, proto); | |
1952 | ||
1953 | mutex_unlock(&classLock); | |
1954 | } | |
1955 | ||
1956 | ||
1957 | /*********************************************************************** | |
1958 | * protocol_addProtocol | |
1959 | * Adds an incorporated protocol to another protocol. | |
1960 | * No method enforcement is performed. | |
1961 | * `proto` must be under construction. `addition` must not. | |
1962 | * Locking: acquires classLock | |
1963 | **********************************************************************/ | |
1964 | void | |
1965 | protocol_addProtocol(Protocol *proto_gen, Protocol *addition_gen) | |
1966 | { | |
7257e56c A |
1967 | old_protocol *proto = oldprotocol(proto_gen); |
1968 | old_protocol *addition = oldprotocol(addition_gen); | |
8972963c | 1969 | |
7257e56c | 1970 | Class cls = objc_getClass("__IncompleteProtocol"); |
8972963c A |
1971 | |
1972 | if (!proto_gen) return; | |
1973 | if (!addition_gen) return; | |
1974 | ||
1975 | mutex_lock(&classLock); | |
1976 | ||
1977 | if (proto->isa != cls) { | |
1978 | _objc_inform("protocol_addProtocol: modified protocol '%s' is not " | |
1979 | "under construction!", proto->protocol_name); | |
1980 | mutex_unlock(&classLock); | |
1981 | return; | |
1982 | } | |
1983 | if (addition->isa == cls) { | |
1984 | _objc_inform("protocol_addProtocol: added protocol '%s' is still " | |
1985 | "under construction!", addition->protocol_name); | |
1986 | mutex_unlock(&classLock); | |
1987 | return; | |
1988 | } | |
1989 | ||
7257e56c | 1990 | old_protocol_list *protolist = proto->protocol_list; |
8972963c | 1991 | if (protolist) { |
7257e56c | 1992 | size_t size = sizeof(old_protocol_list) |
8972963c | 1993 | + protolist->count * sizeof(protolist->list[0]); |
7257e56c | 1994 | protolist = (old_protocol_list *) |
8972963c A |
1995 | _realloc_internal(protolist, size); |
1996 | } else { | |
7257e56c A |
1997 | protolist = (old_protocol_list *) |
1998 | _calloc_internal(1, sizeof(old_protocol_list)); | |
8972963c A |
1999 | } |
2000 | ||
2001 | protolist->list[protolist->count++] = addition; | |
2002 | proto->protocol_list = protolist; | |
2003 | ||
2004 | mutex_unlock(&classLock); | |
2005 | } | |
2006 | ||
2007 | ||
2008 | /*********************************************************************** | |
2009 | * protocol_addMethodDescription | |
2010 | * Adds a method to a protocol. The protocol must be under construction. | |
2011 | * Locking: acquires classLock | |
2012 | **********************************************************************/ | |
2013 | static void | |
2014 | _protocol_addMethod(struct objc_method_description_list **list, SEL name, const char *types) | |
2015 | { | |
2016 | if (!*list) { | |
2017 | *list = (struct objc_method_description_list *) | |
2018 | _calloc_internal(sizeof(struct objc_method_description_list), 1); | |
2019 | } else { | |
2020 | size_t size = sizeof(struct objc_method_description_list) | |
2021 | + (*list)->count * sizeof(struct objc_method_description); | |
2022 | *list = (struct objc_method_description_list *) | |
2023 | _realloc_internal(*list, size); | |
2024 | } | |
2025 | ||
2026 | struct objc_method_description *desc = &(*list)->list[(*list)->count++]; | |
2027 | desc->name = name; | |
2028 | desc->types = _strdup_internal(types ?: ""); | |
2029 | } | |
2030 | ||
2031 | void | |
2032 | protocol_addMethodDescription(Protocol *proto_gen, SEL name, const char *types, | |
2033 | BOOL isRequiredMethod, BOOL isInstanceMethod) | |
2034 | { | |
7257e56c | 2035 | old_protocol *proto = oldprotocol(proto_gen); |
8972963c | 2036 | |
7257e56c | 2037 | Class cls = objc_getClass("__IncompleteProtocol"); |
8972963c A |
2038 | |
2039 | if (!proto_gen) return; | |
2040 | ||
2041 | mutex_lock(&classLock); | |
2042 | ||
2043 | if (proto->isa != cls) { | |
2044 | _objc_inform("protocol_addMethodDescription: protocol '%s' is not " | |
2045 | "under construction!", proto->protocol_name); | |
2046 | mutex_unlock(&classLock); | |
2047 | return; | |
2048 | } | |
2049 | ||
2050 | if (isRequiredMethod && isInstanceMethod) { | |
2051 | _protocol_addMethod(&proto->instance_methods, name, types); | |
2052 | } else if (isRequiredMethod && !isInstanceMethod) { | |
2053 | _protocol_addMethod(&proto->class_methods, name, types); | |
2054 | } else if (!isRequiredMethod && isInstanceMethod) { | |
7257e56c | 2055 | old_protocol_ext *ext = (old_protocol_ext *)(proto+1); |
8972963c A |
2056 | _protocol_addMethod(&ext->optional_instance_methods, name, types); |
2057 | } else /* !isRequiredMethod && !isInstanceMethod) */ { | |
7257e56c | 2058 | old_protocol_ext *ext = (old_protocol_ext *)(proto+1); |
8972963c A |
2059 | _protocol_addMethod(&ext->optional_class_methods, name, types); |
2060 | } | |
2061 | ||
2062 | mutex_unlock(&classLock); | |
2063 | } | |
2064 | ||
2065 | ||
2066 | /*********************************************************************** | |
2067 | * protocol_addProperty | |
2068 | * Adds a property to a protocol. The protocol must be under construction. | |
2069 | * Locking: acquires classLock | |
2070 | **********************************************************************/ | |
2071 | static void | |
7257e56c | 2072 | _protocol_addProperty(old_property_list **plist, const char *name, |
8972963c A |
2073 | const objc_property_attribute_t *attrs, |
2074 | unsigned int count) | |
2075 | { | |
2076 | if (!*plist) { | |
7257e56c A |
2077 | *plist = (old_property_list *) |
2078 | _calloc_internal(sizeof(old_property_list), 1); | |
2079 | (*plist)->entsize = sizeof(old_property); | |
8972963c | 2080 | } else { |
7257e56c A |
2081 | *plist = (old_property_list *) |
2082 | _realloc_internal(*plist, sizeof(old_property_list) | |
8972963c A |
2083 | + (*plist)->count * (*plist)->entsize); |
2084 | } | |
2085 | ||
7257e56c | 2086 | old_property *prop = property_list_nth(*plist, (*plist)->count++); |
8972963c A |
2087 | prop->name = _strdup_internal(name); |
2088 | prop->attributes = copyPropertyAttributeString(attrs, count); | |
2089 | } | |
2090 | ||
2091 | void | |
2092 | protocol_addProperty(Protocol *proto_gen, const char *name, | |
2093 | const objc_property_attribute_t *attrs, | |
2094 | unsigned int count, | |
2095 | BOOL isRequiredProperty, BOOL isInstanceProperty) | |
2096 | { | |
7257e56c | 2097 | old_protocol *proto = oldprotocol(proto_gen); |
8972963c | 2098 | |
7257e56c | 2099 | Class cls = objc_getClass("__IncompleteProtocol"); |
8972963c A |
2100 | |
2101 | if (!proto) return; | |
2102 | if (!name) return; | |
2103 | ||
2104 | mutex_lock(&classLock); | |
2105 | ||
2106 | if (proto->isa != cls) { | |
2107 | _objc_inform("protocol_addProperty: protocol '%s' is not " | |
2108 | "under construction!", proto->protocol_name); | |
2109 | mutex_unlock(&classLock); | |
2110 | return; | |
2111 | } | |
2112 | ||
7257e56c | 2113 | old_protocol_ext *ext = ext_for_protocol(proto); |
8972963c A |
2114 | |
2115 | if (isRequiredProperty && isInstanceProperty) { | |
2116 | _protocol_addProperty(&ext->instance_properties, name, attrs, count); | |
2117 | } | |
2118 | //else if (isRequiredProperty && !isInstanceProperty) { | |
2119 | // _protocol_addProperty(&ext->class_properties, name, attrs, count); | |
2120 | //} else if (!isRequiredProperty && isInstanceProperty) { | |
2121 | // _protocol_addProperty(&ext->optional_instance_properties, name, attrs, count); | |
2122 | //} else /* !isRequiredProperty && !isInstanceProperty) */ { | |
2123 | // _protocol_addProperty(&ext->optional_class_properties, name, attrs, count); | |
2124 | //} | |
2125 | ||
2126 | mutex_unlock(&classLock); | |
2127 | } | |
2128 | ||
2129 | ||
b3962a83 A |
2130 | /*********************************************************************** |
2131 | * _objc_fixup_protocol_objects_for_image. For each protocol in the | |
2132 | * specified image, selectorize the method names and add to the protocol hash. | |
2133 | **********************************************************************/ | |
2134 | ||
2135 | static BOOL versionIsExt(uintptr_t version, const char *names, size_t size) | |
2136 | { | |
2137 | // CodeWarrior used isa field for string "Protocol" | |
2138 | // from section __OBJC,__class_names. rdar://4951638 | |
2139 | // gcc (10.4 and earlier) used isa field for version number; | |
2140 | // the only version number used on Mac OS X was 2. | |
2141 | // gcc (10.5 and later) uses isa field for ext pointer | |
2142 | ||
8070259c | 2143 | if (version < 4096 /* not PAGE_SIZE */) { |
b3962a83 A |
2144 | return NO; |
2145 | } | |
2146 | ||
2147 | if (version >= (uintptr_t)names && version < (uintptr_t)(names + size)) { | |
2148 | return NO; | |
2149 | } | |
2150 | ||
2151 | return YES; | |
2152 | } | |
2153 | ||
7257e56c | 2154 | static void fix_protocol(old_protocol *proto, Class protocolClass, |
b3962a83 A |
2155 | BOOL isBundle, const char *names, size_t names_size) |
2156 | { | |
7af964d1 | 2157 | uintptr_t version; |
b3962a83 A |
2158 | if (!proto) return; |
2159 | ||
7af964d1 | 2160 | version = (uintptr_t)proto->isa; |
b3962a83 A |
2161 | |
2162 | // Set the protocol's isa | |
2163 | proto->isa = protocolClass; | |
2164 | ||
2165 | // Fix up method lists | |
2166 | // fixme share across duplicates | |
2167 | map_method_descs (proto->instance_methods, isBundle); | |
2168 | map_method_descs (proto->class_methods, isBundle); | |
2169 | ||
2170 | // Fix up ext, if any | |
2171 | if (versionIsExt(version, names, names_size)) { | |
7257e56c | 2172 | old_protocol_ext *ext = (old_protocol_ext *)version; |
b3962a83 A |
2173 | NXMapInsert(protocol_ext_map, proto, ext); |
2174 | map_method_descs (ext->optional_instance_methods, isBundle); | |
2175 | map_method_descs (ext->optional_class_methods, isBundle); | |
2176 | } | |
2177 | ||
2178 | // Record the protocol it if we don't have one with this name yet | |
2179 | // fixme bundles - copy protocol | |
2180 | // fixme unloading | |
2181 | if (!NXMapGet(protocol_map, proto->protocol_name)) { | |
2182 | NXMapKeyCopyingInsert(protocol_map, proto->protocol_name, proto); | |
2183 | if (PrintProtocols) { | |
2184 | _objc_inform("PROTOCOLS: protocol at %p is %s", | |
2185 | proto, proto->protocol_name); | |
2186 | } | |
2187 | } else { | |
2188 | // duplicate - do nothing | |
2189 | if (PrintProtocols) { | |
2190 | _objc_inform("PROTOCOLS: protocol at %p is %s (duplicate)", | |
2191 | proto, proto->protocol_name); | |
2192 | } | |
2193 | } | |
2194 | } | |
2195 | ||
2196 | static void _objc_fixup_protocol_objects_for_image (header_info * hi) | |
2197 | { | |
7257e56c | 2198 | Class protocolClass = objc_getClass("Protocol"); |
b3962a83 | 2199 | size_t count, i; |
7257e56c | 2200 | old_protocol **protos; |
7af964d1 | 2201 | int isBundle = headerIsBundle(hi); |
b3962a83 A |
2202 | const char *names; |
2203 | size_t names_size; | |
2204 | ||
7af964d1 | 2205 | mutex_lock(&classLock); |
b3962a83 A |
2206 | |
2207 | // Allocate the protocol registry if necessary. | |
2208 | if (!protocol_map) { | |
2209 | protocol_map = | |
2210 | NXCreateMapTableFromZone(NXStrValueMapPrototype, 32, | |
2211 | _objc_internal_zone()); | |
2212 | } | |
2213 | if (!protocol_ext_map) { | |
2214 | protocol_ext_map = | |
2215 | NXCreateMapTableFromZone(NXPtrValueMapPrototype, 32, | |
2216 | _objc_internal_zone()); | |
2217 | } | |
2218 | ||
2219 | protos = _getObjcProtocols(hi, &count); | |
2220 | names = _getObjcClassNames(hi, &names_size); | |
2221 | for (i = 0; i < count; i++) { | |
7af964d1 | 2222 | fix_protocol(protos[i], protocolClass, isBundle, names, names_size); |
b3962a83 A |
2223 | } |
2224 | ||
7af964d1 | 2225 | mutex_unlock(&classLock); |
b3962a83 A |
2226 | } |
2227 | ||
2228 | ||
2229 | /*********************************************************************** | |
2230 | * _objc_fixup_selector_refs. Register all of the selectors in each | |
2231 | * image, and fix them all up. | |
2232 | **********************************************************************/ | |
2233 | static void _objc_fixup_selector_refs (const header_info *hi) | |
2234 | { | |
2235 | size_t count; | |
2236 | SEL *sels; | |
2237 | ||
7af964d1 A |
2238 | if (PrintPreopt) { |
2239 | if (sel_preoptimizationValid(hi)) { | |
2240 | _objc_inform("PREOPTIMIZATION: honoring preoptimized selectors in %s", | |
cd5f04f5 | 2241 | hi->fname); |
7af964d1 A |
2242 | } |
2243 | else if (_objcHeaderOptimizedByDyld(hi)) { | |
2244 | _objc_inform("PREOPTIMIZATION: IGNORING preoptimized selectors in %s", | |
cd5f04f5 | 2245 | hi->fname); |
7af964d1 A |
2246 | } |
2247 | } | |
2248 | ||
2249 | if (sel_preoptimizationValid(hi)) return; | |
2250 | ||
b3962a83 | 2251 | sels = _getObjcSelectorRefs (hi, &count); |
7af964d1 A |
2252 | |
2253 | map_selrefs(sels, count, headerIsBundle(hi)); | |
2254 | } | |
2255 | ||
2256 | static inline BOOL _is_threaded() { | |
2257 | #if TARGET_OS_WIN32 | |
2258 | return YES; | |
2259 | #else | |
2260 | return pthread_is_threaded_np() != 0; | |
2261 | #endif | |
2262 | } | |
2263 | ||
8972963c | 2264 | #if !TARGET_OS_WIN32 |
7af964d1 A |
2265 | /*********************************************************************** |
2266 | * unmap_image | |
2267 | * Process the given image which is about to be unmapped by dyld. | |
2268 | * mh is mach_header instead of headerType because that's what | |
2269 | * dyld_priv.h says even for 64-bit. | |
2270 | **********************************************************************/ | |
cd5f04f5 | 2271 | void |
7af964d1 A |
2272 | unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide) |
2273 | { | |
2274 | recursive_mutex_lock(&loadMethodLock); | |
8972963c | 2275 | unmap_image_nolock(mh); |
7af964d1 A |
2276 | recursive_mutex_unlock(&loadMethodLock); |
2277 | } | |
2278 | ||
2279 | ||
2280 | /*********************************************************************** | |
2281 | * map_images | |
2282 | * Process the given images which are being mapped in by dyld. | |
2283 | * Calls ABI-agnostic code after taking ABI-specific locks. | |
2284 | **********************************************************************/ | |
cd5f04f5 | 2285 | const char * |
7af964d1 A |
2286 | map_images(enum dyld_image_states state, uint32_t infoCount, |
2287 | const struct dyld_image_info infoList[]) | |
2288 | { | |
2289 | const char *err; | |
2290 | ||
2291 | recursive_mutex_lock(&loadMethodLock); | |
2292 | err = map_images_nolock(state, infoCount, infoList); | |
2293 | recursive_mutex_unlock(&loadMethodLock); | |
2294 | ||
2295 | return err; | |
2296 | } | |
2297 | ||
2298 | ||
2299 | /*********************************************************************** | |
2300 | * load_images | |
2301 | * Process +load in the given images which are being mapped in by dyld. | |
2302 | * Calls ABI-agnostic code after taking ABI-specific locks. | |
2303 | * | |
2304 | * Locking: acquires classLock and loadMethodLock | |
2305 | **********************************************************************/ | |
cd5f04f5 | 2306 | const char * |
7af964d1 A |
2307 | load_images(enum dyld_image_states state, uint32_t infoCount, |
2308 | const struct dyld_image_info infoList[]) | |
2309 | { | |
2310 | BOOL found; | |
2311 | ||
2312 | recursive_mutex_lock(&loadMethodLock); | |
2313 | ||
2314 | // Discover +load methods | |
2315 | found = load_images_nolock(state, infoCount, infoList); | |
2316 | ||
2317 | // Call +load methods (without classLock - re-entrant) | |
2318 | if (found) { | |
2319 | call_load_methods(); | |
b3962a83 | 2320 | } |
7af964d1 A |
2321 | |
2322 | recursive_mutex_unlock(&loadMethodLock); | |
2323 | ||
7257e56c | 2324 | return nil; |
b3962a83 | 2325 | } |
8972963c | 2326 | #endif |
b3962a83 A |
2327 | |
2328 | ||
2329 | /*********************************************************************** | |
2330 | * _read_images | |
2331 | * Perform metadata processing for hCount images starting with firstNewHeader | |
2332 | **********************************************************************/ | |
cd5f04f5 | 2333 | void _read_images(header_info **hList, uint32_t hCount) |
b3962a83 A |
2334 | { |
2335 | uint32_t i; | |
7af964d1 | 2336 | BOOL categoriesLoaded = NO; |
b3962a83 A |
2337 | |
2338 | if (!class_hash) _objc_init_class_hash(); | |
2339 | ||
2340 | // Parts of this order are important for correctness or performance. | |
2341 | ||
2342 | // Read classes from all images. | |
2343 | for (i = 0; i < hCount; i++) { | |
2344 | _objc_read_classes_from_image(hList[i]); | |
2345 | } | |
2346 | ||
2347 | // Read categories from all images. | |
7af964d1 A |
2348 | // But not if any other threads are running - they might |
2349 | // call a category method before the fixups below are complete. | |
2350 | if (!_is_threaded()) { | |
2351 | BOOL needFlush = NO; | |
2352 | for (i = 0; i < hCount; i++) { | |
2353 | needFlush |= _objc_read_categories_from_image(hList[i]); | |
2354 | } | |
2355 | if (needFlush) flush_marked_caches(); | |
2356 | categoriesLoaded = YES; | |
b3962a83 | 2357 | } |
b3962a83 A |
2358 | |
2359 | // Connect classes from all images. | |
2360 | for (i = 0; i < hCount; i++) { | |
2361 | _objc_connect_classes_from_image(hList[i]); | |
2362 | } | |
2363 | ||
2364 | // Fix up class refs, selector refs, and protocol objects from all images. | |
2365 | for (i = 0; i < hCount; i++) { | |
2366 | _objc_map_class_refs_for_image(hList[i]); | |
2367 | _objc_fixup_selector_refs(hList[i]); | |
2368 | _objc_fixup_protocol_objects_for_image(hList[i]); | |
2369 | } | |
7af964d1 A |
2370 | |
2371 | // Read categories from all images. | |
2372 | // But not if this is the only thread - it's more | |
2373 | // efficient to attach categories earlier if safe. | |
2374 | if (!categoriesLoaded) { | |
2375 | BOOL needFlush = NO; | |
2376 | for (i = 0; i < hCount; i++) { | |
2377 | needFlush |= _objc_read_categories_from_image(hList[i]); | |
2378 | } | |
2379 | if (needFlush) flush_marked_caches(); | |
2380 | } | |
2381 | ||
2382 | // Multi-threaded category load MUST BE LAST to avoid a race. | |
b3962a83 A |
2383 | } |
2384 | ||
2385 | ||
2386 | /*********************************************************************** | |
2387 | * prepare_load_methods | |
2388 | * Schedule +load for classes in this image, any un-+load-ed | |
2389 | * superclasses in other images, and any categories in this image. | |
2390 | **********************************************************************/ | |
2391 | // Recursively schedule +load for cls and any un-+load-ed superclasses. | |
2392 | // cls must already be connected. | |
7257e56c | 2393 | static void schedule_class_load(Class cls) |
b3962a83 A |
2394 | { |
2395 | if (cls->info & CLS_LOADED) return; | |
7257e56c A |
2396 | if (cls->superclass) schedule_class_load(cls->superclass); |
2397 | add_class_to_loadable_list(cls); | |
b3962a83 A |
2398 | cls->info |= CLS_LOADED; |
2399 | } | |
2400 | ||
cd5f04f5 | 2401 | void prepare_load_methods(header_info *hi) |
b3962a83 A |
2402 | { |
2403 | Module mods; | |
2404 | unsigned int midx; | |
2405 | ||
2406 | ||
2407 | if (_objcHeaderIsReplacement(hi)) { | |
2408 | // Ignore any classes in this image | |
2409 | return; | |
2410 | } | |
2411 | ||
2412 | // Major loop - process all modules in the image | |
2413 | mods = hi->mod_ptr; | |
2414 | for (midx = 0; midx < hi->mod_count; midx += 1) | |
2415 | { | |
2416 | unsigned int index; | |
2417 | ||
2418 | // Skip module containing no classes | |
7257e56c | 2419 | if (mods[midx].symtab == nil) |
b3962a83 A |
2420 | continue; |
2421 | ||
2422 | // Minor loop - process all the classes in given module | |
2423 | for (index = 0; index < mods[midx].symtab->cls_def_cnt; index += 1) | |
2424 | { | |
2425 | // Locate the class description pointer | |
7257e56c | 2426 | Class cls = (Class)mods[midx].symtab->defs[index]; |
b3962a83 A |
2427 | if (cls->info & CLS_CONNECTED) { |
2428 | schedule_class_load(cls); | |
2429 | } | |
2430 | } | |
2431 | } | |
2432 | ||
2433 | ||
2434 | // Major loop - process all modules in the header | |
2435 | mods = hi->mod_ptr; | |
2436 | ||
2437 | // NOTE: The module and category lists are traversed backwards | |
2438 | // to preserve the pre-10.4 processing order. Changing the order | |
2439 | // would have a small chance of introducing binary compatibility bugs. | |
7af964d1 | 2440 | midx = (unsigned int)hi->mod_count; |
b3962a83 A |
2441 | while (midx-- > 0) { |
2442 | unsigned int index; | |
2443 | unsigned int total; | |
2444 | Symtab symtab = mods[midx].symtab; | |
2445 | ||
2446 | // Nothing to do for a module without a symbol table | |
7257e56c | 2447 | if (mods[midx].symtab == nil) |
b3962a83 A |
2448 | continue; |
2449 | // Total entries in symbol table (class entries followed | |
2450 | // by category entries) | |
2451 | total = mods[midx].symtab->cls_def_cnt + | |
2452 | mods[midx].symtab->cat_def_cnt; | |
2453 | ||
2454 | // Minor loop - register all categories from given module | |
2455 | index = total; | |
2456 | while (index-- > mods[midx].symtab->cls_def_cnt) { | |
7257e56c | 2457 | old_category *cat = (old_category *)symtab->defs[index]; |
b3962a83 A |
2458 | add_category_to_loadable_list((Category)cat); |
2459 | } | |
2460 | } | |
2461 | } | |
2462 | ||
2463 | ||
7af964d1 A |
2464 | #if TARGET_OS_WIN32 |
2465 | ||
7257e56c | 2466 | void unload_class(Class cls) |
7af964d1 A |
2467 | { |
2468 | } | |
2469 | ||
2470 | #else | |
2471 | ||
b3962a83 A |
2472 | /*********************************************************************** |
2473 | * _objc_remove_classes_in_image | |
2474 | * Remove all classes in the given image from the runtime, because | |
2475 | * the image is about to be unloaded. | |
2476 | * Things to clean up: | |
2477 | * class_hash | |
2478 | * unconnected_class_hash | |
2479 | * pending subclasses list (only if class is still unconnected) | |
2480 | * loadable class list | |
2481 | * class's method caches | |
2482 | * class refs in all other images | |
2483 | **********************************************************************/ | |
2484 | // Re-pend any class references in refs that point into [start..end) | |
7257e56c | 2485 | static void rependClassReferences(Class *refs, size_t count, |
b3962a83 A |
2486 | uintptr_t start, uintptr_t end) |
2487 | { | |
2488 | size_t i; | |
2489 | ||
2490 | if (!refs) return; | |
2491 | ||
2492 | // Process each class ref | |
2493 | for (i = 0; i < count; i++) { | |
2494 | if ((uintptr_t)(refs[i]) >= start && (uintptr_t)(refs[i]) < end) { | |
2495 | pendClassReference(&refs[i], refs[i]->name, | |
2496 | (refs[i]->info & CLS_META) ? YES : NO); | |
7257e56c | 2497 | refs[i] = nil; |
b3962a83 A |
2498 | } |
2499 | } | |
2500 | } | |
2501 | ||
2502 | ||
cd5f04f5 | 2503 | void try_free(const void *p) |
b3962a83 A |
2504 | { |
2505 | if (p && malloc_size(p)) free((void *)p); | |
2506 | } | |
2507 | ||
2508 | // Deallocate all memory in a method list | |
7257e56c | 2509 | static void unload_mlist(old_method_list *mlist) |
b3962a83 A |
2510 | { |
2511 | int i; | |
7af964d1 A |
2512 | for (i = 0; i < mlist->method_count; i++) { |
2513 | try_free(mlist->method_list[i].method_types); | |
b3962a83 | 2514 | } |
7af964d1 | 2515 | try_free(mlist); |
b3962a83 A |
2516 | } |
2517 | ||
7257e56c | 2518 | static void unload_property_list(old_property_list *proplist) |
8972963c A |
2519 | { |
2520 | uint32_t i; | |
2521 | ||
2522 | if (!proplist) return; | |
2523 | ||
2524 | for (i = 0; i < proplist->count; i++) { | |
7257e56c | 2525 | old_property *prop = property_list_nth(proplist, i); |
8972963c A |
2526 | try_free(prop->name); |
2527 | try_free(prop->attributes); | |
2528 | } | |
2529 | try_free(proplist); | |
2530 | } | |
2531 | ||
2532 | ||
b3962a83 | 2533 | // Deallocate all memory in a class. |
7257e56c | 2534 | void unload_class(Class cls) |
b3962a83 | 2535 | { |
7af964d1 A |
2536 | // Free method cache |
2537 | // This dereferences the cache contents; do this before freeing methods | |
2538 | if (cls->cache && cls->cache != &_objc_empty_cache) { | |
2539 | _cache_free(cls->cache); | |
2540 | } | |
2541 | ||
b3962a83 A |
2542 | // Free ivar lists |
2543 | if (cls->ivars) { | |
2544 | int i; | |
2545 | for (i = 0; i < cls->ivars->ivar_count; i++) { | |
2546 | try_free(cls->ivars->ivar_list[i].ivar_name); | |
2547 | try_free(cls->ivars->ivar_list[i].ivar_type); | |
2548 | } | |
2549 | try_free(cls->ivars); | |
2550 | } | |
2551 | ||
2552 | // Free fixed-up method lists and method list array | |
2553 | if (cls->methodLists) { | |
2554 | // more than zero method lists | |
2555 | if (cls->info & CLS_NO_METHOD_ARRAY) { | |
2556 | // one method list | |
7257e56c | 2557 | unload_mlist((old_method_list *)cls->methodLists); |
b3962a83 A |
2558 | } |
2559 | else { | |
2560 | // more than one method list | |
7257e56c | 2561 | old_method_list **mlistp; |
b3962a83 | 2562 | for (mlistp = cls->methodLists; |
7257e56c | 2563 | *mlistp != nil && *mlistp != END_OF_METHODS_LIST; |
b3962a83 A |
2564 | mlistp++) |
2565 | { | |
2566 | unload_mlist(*mlistp); | |
2567 | } | |
2568 | free(cls->methodLists); | |
2569 | } | |
2570 | } | |
2571 | ||
2572 | // Free protocol list | |
7257e56c | 2573 | old_protocol_list *protos = cls->protocols; |
b3962a83 | 2574 | while (protos) { |
7257e56c | 2575 | old_protocol_list *dead = protos; |
b3962a83 A |
2576 | protos = protos->next; |
2577 | try_free(dead); | |
2578 | } | |
2579 | ||
b3962a83 A |
2580 | if ((cls->info & CLS_EXT)) { |
2581 | if (cls->ext) { | |
2582 | // Free property lists and property list array | |
2583 | if (cls->ext->propertyLists) { | |
2584 | // more than zero property lists | |
2585 | if (cls->info & CLS_NO_PROPERTY_ARRAY) { | |
2586 | // one property list | |
7257e56c A |
2587 | old_property_list *proplist = |
2588 | (old_property_list *)cls->ext->propertyLists; | |
8972963c | 2589 | unload_property_list(proplist); |
b3962a83 A |
2590 | } else { |
2591 | // more than one property list | |
7257e56c | 2592 | old_property_list **plistp; |
b3962a83 | 2593 | for (plistp = cls->ext->propertyLists; |
7257e56c | 2594 | *plistp != nil; |
b3962a83 A |
2595 | plistp++) |
2596 | { | |
8972963c | 2597 | unload_property_list(*plistp); |
b3962a83 A |
2598 | } |
2599 | try_free(cls->ext->propertyLists); | |
2600 | } | |
2601 | } | |
2602 | ||
2603 | // Free weak ivar layout | |
2604 | try_free(cls->ext->weak_ivar_layout); | |
2605 | ||
2606 | // Free ext | |
2607 | try_free(cls->ext); | |
2608 | } | |
2609 | ||
2610 | // Free non-weak ivar layout | |
2611 | try_free(cls->ivar_layout); | |
2612 | } | |
2613 | ||
2614 | // Free class name | |
2615 | try_free(cls->name); | |
2616 | ||
2617 | // Free cls | |
2618 | try_free(cls); | |
2619 | } | |
2620 | ||
2621 | ||
2622 | static void _objc_remove_classes_in_image(header_info *hi) | |
2623 | { | |
2624 | unsigned int index; | |
2625 | unsigned int midx; | |
2626 | Module mods; | |
2627 | ||
7af964d1 | 2628 | mutex_lock(&classLock); |
b3962a83 A |
2629 | |
2630 | // Major loop - process all modules in the image | |
2631 | mods = hi->mod_ptr; | |
2632 | for (midx = 0; midx < hi->mod_count; midx += 1) | |
2633 | { | |
2634 | // Skip module containing no classes | |
7257e56c | 2635 | if (mods[midx].symtab == nil) |
b3962a83 A |
2636 | continue; |
2637 | ||
2638 | // Minor loop - process all the classes in given module | |
2639 | for (index = 0; index < mods[midx].symtab->cls_def_cnt; index += 1) | |
2640 | { | |
7257e56c | 2641 | Class cls; |
b3962a83 A |
2642 | |
2643 | // Locate the class description pointer | |
7257e56c | 2644 | cls = (Class)mods[midx].symtab->defs[index]; |
b3962a83 A |
2645 | |
2646 | // Remove from loadable class list, if present | |
7257e56c | 2647 | remove_class_from_loadable_list(cls); |
b3962a83 A |
2648 | |
2649 | // Remove from unconnected_class_hash and pending subclasses | |
2650 | if (unconnected_class_hash && NXHashMember(unconnected_class_hash, cls)) { | |
2651 | NXHashRemove(unconnected_class_hash, cls); | |
2652 | if (pendingSubclassesMap) { | |
2653 | // Find this class in its superclass's pending list | |
7257e56c A |
2654 | char *supercls_name = (char *)cls->superclass; |
2655 | PendingSubclass *pending = (PendingSubclass *) | |
b3962a83 | 2656 | NXMapGet(pendingSubclassesMap, supercls_name); |
7257e56c | 2657 | for ( ; pending != nil; pending = pending->next) { |
b3962a83 A |
2658 | if (pending->subclass == cls) { |
2659 | pending->subclass = Nil; | |
2660 | break; | |
2661 | } | |
2662 | } | |
2663 | } | |
2664 | } | |
2665 | ||
2666 | // Remove from class_hash | |
2667 | NXHashRemove(class_hash, cls); | |
7257e56c | 2668 | objc_removeRegisteredClass(cls); |
b3962a83 A |
2669 | |
2670 | // Free heap memory pointed to by the class | |
7257e56c | 2671 | unload_class(cls->ISA()); |
b3962a83 A |
2672 | unload_class(cls); |
2673 | } | |
2674 | } | |
2675 | ||
2676 | ||
2677 | // Search all other images for class refs that point back to this range. | |
2678 | // Un-fix and re-pend any such class refs. | |
2679 | ||
2680 | // Get the location of the dying image's __OBJC segment | |
8972963c A |
2681 | uintptr_t seg; |
2682 | unsigned long seg_size; | |
2683 | seg = (uintptr_t)getsegmentdata(hi->mhdr, "__OBJC", &seg_size); | |
b3962a83 A |
2684 | |
2685 | header_info *other_hi; | |
7257e56c A |
2686 | for (other_hi = FirstHeader; other_hi != nil; other_hi = other_hi->next) { |
2687 | Class *other_refs; | |
b3962a83 A |
2688 | size_t count; |
2689 | if (other_hi == hi) continue; // skip the image being unloaded | |
2690 | ||
2691 | // Fix class refs in the other image | |
2692 | other_refs = _getObjcClassRefs(other_hi, &count); | |
2693 | rependClassReferences(other_refs, count, seg, seg+seg_size); | |
2694 | } | |
2695 | ||
7af964d1 | 2696 | mutex_unlock(&classLock); |
b3962a83 A |
2697 | } |
2698 | ||
2699 | ||
2700 | /*********************************************************************** | |
2701 | * _objc_remove_categories_in_image | |
2702 | * Remove all categories in the given image from the runtime, because | |
2703 | * the image is about to be unloaded. | |
2704 | * Things to clean up: | |
2705 | * unresolved category list | |
2706 | * loadable category list | |
2707 | **********************************************************************/ | |
2708 | static void _objc_remove_categories_in_image(header_info *hi) | |
2709 | { | |
2710 | Module mods; | |
2711 | unsigned int midx; | |
2712 | ||
2713 | // Major loop - process all modules in the header | |
2714 | mods = hi->mod_ptr; | |
2715 | ||
2716 | for (midx = 0; midx < hi->mod_count; midx++) { | |
2717 | unsigned int index; | |
2718 | unsigned int total; | |
2719 | Symtab symtab = mods[midx].symtab; | |
2720 | ||
2721 | // Nothing to do for a module without a symbol table | |
7257e56c | 2722 | if (symtab == nil) continue; |
b3962a83 A |
2723 | |
2724 | // Total entries in symbol table (class entries followed | |
2725 | // by category entries) | |
2726 | total = symtab->cls_def_cnt + symtab->cat_def_cnt; | |
2727 | ||
2728 | // Minor loop - check all categories from given module | |
2729 | for (index = symtab->cls_def_cnt; index < total; index++) { | |
7257e56c | 2730 | old_category *cat = (old_category *)symtab->defs[index]; |
b3962a83 A |
2731 | |
2732 | // Clean up loadable category list | |
2733 | remove_category_from_loadable_list((Category)cat); | |
2734 | ||
2735 | // Clean up category_hash | |
2736 | if (category_hash) { | |
7257e56c A |
2737 | _objc_unresolved_category *cat_entry = (_objc_unresolved_category *)NXMapGet(category_hash, cat->class_name); |
2738 | for ( ; cat_entry != nil; cat_entry = cat_entry->next) { | |
b3962a83 | 2739 | if (cat_entry->cat == cat) { |
7257e56c | 2740 | cat_entry->cat = nil; |
b3962a83 A |
2741 | break; |
2742 | } | |
2743 | } | |
2744 | } | |
2745 | } | |
2746 | } | |
2747 | } | |
2748 | ||
2749 | ||
2750 | /*********************************************************************** | |
2751 | * unload_paranoia | |
2752 | * Various paranoid debugging checks that look for poorly-behaving | |
2753 | * unloadable bundles. | |
2754 | * Called by _objc_unmap_image when OBJC_UNLOAD_DEBUG is set. | |
2755 | **********************************************************************/ | |
2756 | static void unload_paranoia(header_info *hi) | |
2757 | { | |
2758 | // Get the location of the dying image's __OBJC segment | |
8972963c A |
2759 | uintptr_t seg; |
2760 | unsigned long seg_size; | |
2761 | seg = (uintptr_t)getsegmentdata(hi->mhdr, "__OBJC", &seg_size); | |
b3962a83 A |
2762 | |
2763 | _objc_inform("UNLOAD DEBUG: unloading image '%s' [%p..%p]", | |
cd5f04f5 | 2764 | hi->fname, (void *)seg, (void*)(seg+seg_size)); |
b3962a83 | 2765 | |
7af964d1 | 2766 | mutex_lock(&classLock); |
b3962a83 A |
2767 | |
2768 | // Make sure the image contains no categories on surviving classes. | |
2769 | { | |
2770 | Module mods; | |
2771 | unsigned int midx; | |
2772 | ||
2773 | // Major loop - process all modules in the header | |
2774 | mods = hi->mod_ptr; | |
2775 | ||
2776 | for (midx = 0; midx < hi->mod_count; midx++) { | |
2777 | unsigned int index; | |
2778 | unsigned int total; | |
2779 | Symtab symtab = mods[midx].symtab; | |
2780 | ||
2781 | // Nothing to do for a module without a symbol table | |
7257e56c | 2782 | if (symtab == nil) continue; |
b3962a83 A |
2783 | |
2784 | // Total entries in symbol table (class entries followed | |
2785 | // by category entries) | |
2786 | total = symtab->cls_def_cnt + symtab->cat_def_cnt; | |
2787 | ||
2788 | // Minor loop - check all categories from given module | |
2789 | for (index = symtab->cls_def_cnt; index < total; index++) { | |
7257e56c A |
2790 | old_category *cat = (old_category *)symtab->defs[index]; |
2791 | struct objc_class query; | |
b3962a83 A |
2792 | |
2793 | query.name = cat->class_name; | |
2794 | if (NXHashMember(class_hash, &query)) { | |
2795 | _objc_inform("UNLOAD DEBUG: dying image contains category '%s(%s)' on surviving class '%s'!", cat->class_name, cat->category_name, cat->class_name); | |
2796 | } | |
2797 | } | |
2798 | } | |
2799 | } | |
2800 | ||
2801 | // Make sure no surviving class is in the dying image. | |
2802 | // Make sure no surviving class has a superclass in the dying image. | |
2803 | // fixme check method implementations too | |
2804 | { | |
7257e56c | 2805 | Class cls; |
b3962a83 A |
2806 | NXHashState state; |
2807 | ||
2808 | state = NXInitHashState(class_hash); | |
2809 | while (NXNextHashState(class_hash, &state, (void **)&cls)) { | |
2810 | if ((vm_address_t)cls >= seg && | |
2811 | (vm_address_t)cls < seg+seg_size) | |
2812 | { | |
2813 | _objc_inform("UNLOAD DEBUG: dying image contains surviving class '%s'!", cls->name); | |
2814 | } | |
2815 | ||
7257e56c A |
2816 | if ((vm_address_t)cls->superclass >= seg && |
2817 | (vm_address_t)cls->superclass < seg+seg_size) | |
b3962a83 | 2818 | { |
7257e56c | 2819 | _objc_inform("UNLOAD DEBUG: dying image contains superclass '%s' of surviving class '%s'!", cls->superclass->name, cls->name); |
b3962a83 A |
2820 | } |
2821 | } | |
2822 | } | |
2823 | ||
7af964d1 | 2824 | mutex_unlock(&classLock); |
b3962a83 A |
2825 | } |
2826 | ||
2827 | ||
2828 | /*********************************************************************** | |
2829 | * _unload_image | |
2830 | * Only handles MH_BUNDLE for now. | |
7af964d1 | 2831 | * Locking: loadMethodLock acquired by unmap_image |
b3962a83 | 2832 | **********************************************************************/ |
cd5f04f5 | 2833 | void _unload_image(header_info *hi) |
b3962a83 | 2834 | { |
7af964d1 A |
2835 | recursive_mutex_assert_locked(&loadMethodLock); |
2836 | ||
b3962a83 A |
2837 | // Cleanup: |
2838 | // Remove image's classes from the class list and free auxiliary data. | |
2839 | // Remove image's unresolved or loadable categories and free auxiliary data | |
2840 | // Remove image's unresolved class refs. | |
2841 | _objc_remove_classes_in_image(hi); | |
2842 | _objc_remove_categories_in_image(hi); | |
2843 | _objc_remove_pending_class_refs_in_image(hi); | |
2844 | ||
2845 | // Perform various debugging checks if requested. | |
2846 | if (DebugUnload) unload_paranoia(hi); | |
2847 | } | |
2848 | ||
7af964d1 A |
2849 | #endif |
2850 | ||
b3962a83 A |
2851 | |
2852 | /*********************************************************************** | |
2853 | * objc_addClass. Add the specified class to the table of known classes, | |
2854 | * after doing a little verification and fixup. | |
2855 | **********************************************************************/ | |
7257e56c | 2856 | void objc_addClass (Class cls) |
b3962a83 | 2857 | { |
b3962a83 A |
2858 | OBJC_WARN_DEPRECATED; |
2859 | ||
2860 | // Synchronize access to hash table | |
7af964d1 | 2861 | mutex_lock (&classLock); |
b3962a83 A |
2862 | |
2863 | // Make sure both the class and the metaclass have caches! | |
2864 | // Clear all bits of the info fields except CLS_CLASS and CLS_META. | |
2865 | // Normally these bits are already clear but if someone tries to cons | |
2866 | // up their own class on the fly they might need to be cleared. | |
7257e56c | 2867 | if (cls->cache == nil) { |
b3962a83 A |
2868 | cls->cache = (Cache) &_objc_empty_cache; |
2869 | cls->info = CLS_CLASS; | |
2870 | } | |
2871 | ||
7257e56c A |
2872 | if (cls->ISA()->cache == nil) { |
2873 | cls->ISA()->cache = (Cache) &_objc_empty_cache; | |
2874 | cls->ISA()->info = CLS_META; | |
b3962a83 A |
2875 | } |
2876 | ||
2877 | // methodLists should be: | |
7257e56c | 2878 | // 1. nil (Tiger and later only) |
b3962a83 A |
2879 | // 2. A -1 terminated method list array |
2880 | // In either case, CLS_NO_METHOD_ARRAY remains clear. | |
2881 | // If the user manipulates the method list directly, | |
2882 | // they must use the magic private format. | |
2883 | ||
2884 | // Add the class to the table | |
2885 | (void) NXHashInsert (class_hash, cls); | |
7257e56c | 2886 | objc_addRegisteredClass(cls); |
b3962a83 A |
2887 | |
2888 | // Superclass is no longer a leaf for cache flushing | |
7257e56c A |
2889 | if (cls->superclass && (cls->superclass->info & CLS_LEAF)) { |
2890 | cls->superclass->clearInfo(CLS_LEAF); | |
2891 | cls->superclass->ISA()->clearInfo(CLS_LEAF); | |
b3962a83 A |
2892 | } |
2893 | ||
2894 | // Desynchronize | |
7af964d1 | 2895 | mutex_unlock (&classLock); |
b3962a83 A |
2896 | } |
2897 | ||
2898 | /*********************************************************************** | |
2899 | * _objcTweakMethodListPointerForClass. | |
2900 | * Change the class's method list pointer to a method list array. | |
2901 | * Does nothing if the method list pointer is already a method list array. | |
2902 | * If the class is currently in use, methodListLock must be held by the caller. | |
2903 | **********************************************************************/ | |
7257e56c | 2904 | static void _objcTweakMethodListPointerForClass(Class cls) |
b3962a83 | 2905 | { |
7257e56c | 2906 | old_method_list * originalList; |
b3962a83 A |
2907 | const int initialEntries = 4; |
2908 | size_t mallocSize; | |
7257e56c | 2909 | old_method_list ** ptr; |
b3962a83 A |
2910 | |
2911 | // Do nothing if methodLists is already an array. | |
2912 | if (cls->methodLists && !(cls->info & CLS_NO_METHOD_ARRAY)) return; | |
2913 | ||
2914 | // Remember existing list | |
7257e56c | 2915 | originalList = (old_method_list *) cls->methodLists; |
b3962a83 A |
2916 | |
2917 | // Allocate and zero a method list array | |
7257e56c A |
2918 | mallocSize = sizeof(old_method_list *) * initialEntries; |
2919 | ptr = (old_method_list **) _calloc_internal(1, mallocSize); | |
b3962a83 A |
2920 | |
2921 | // Insert the existing list into the array | |
2922 | ptr[initialEntries - 1] = END_OF_METHODS_LIST; | |
2923 | ptr[0] = originalList; | |
2924 | ||
2925 | // Replace existing list with array | |
2926 | cls->methodLists = ptr; | |
7257e56c | 2927 | cls->clearInfo(CLS_NO_METHOD_ARRAY); |
b3962a83 A |
2928 | } |
2929 | ||
2930 | ||
2931 | /*********************************************************************** | |
2932 | * _objc_insertMethods. | |
2933 | * Adds methods to a class. | |
2934 | * Does not flush any method caches. | |
2935 | * Does not take any locks. | |
2936 | * If the class is already in use, use class_addMethods() instead. | |
2937 | **********************************************************************/ | |
7257e56c | 2938 | void _objc_insertMethods(Class cls, old_method_list *mlist, old_category *cat) |
b3962a83 | 2939 | { |
7257e56c A |
2940 | old_method_list ***list; |
2941 | old_method_list **ptr; | |
b3962a83 A |
2942 | ptrdiff_t endIndex; |
2943 | size_t oldSize; | |
2944 | size_t newSize; | |
2945 | ||
2946 | if (!cls->methodLists) { | |
2947 | // cls has no methods - simply use this method list | |
7257e56c A |
2948 | cls->methodLists = (old_method_list **)mlist; |
2949 | cls->setInfo(CLS_NO_METHOD_ARRAY); | |
b3962a83 A |
2950 | return; |
2951 | } | |
2952 | ||
2953 | // Log any existing methods being replaced | |
2954 | if (PrintReplacedMethods) { | |
2955 | int i; | |
2956 | for (i = 0; i < mlist->method_count; i++) { | |
7257e56c | 2957 | extern IMP findIMPInClass(Class cls, SEL sel); |
b3962a83 A |
2958 | SEL sel = sel_registerName((char *)mlist->method_list[i].method_name); |
2959 | IMP newImp = mlist->method_list[i].method_imp; | |
2960 | IMP oldImp; | |
2961 | ||
2962 | if ((oldImp = findIMPInClass(cls, sel))) { | |
7af964d1 | 2963 | logReplacedMethod(cls->name, sel, ISMETA(cls), |
7257e56c | 2964 | cat ? cat->category_name : nil, |
7af964d1 | 2965 | oldImp, newImp); |
b3962a83 A |
2966 | } |
2967 | } | |
2968 | } | |
2969 | ||
2970 | // Create method list array if necessary | |
2971 | _objcTweakMethodListPointerForClass(cls); | |
2972 | ||
2973 | list = &cls->methodLists; | |
2974 | ||
2975 | // Locate unused entry for insertion point | |
2976 | ptr = *list; | |
2977 | while ((*ptr != 0) && (*ptr != END_OF_METHODS_LIST)) | |
2978 | ptr += 1; | |
2979 | ||
2980 | // If array is full, add to it | |
2981 | if (*ptr == END_OF_METHODS_LIST) | |
2982 | { | |
2983 | // Calculate old and new dimensions | |
2984 | endIndex = ptr - *list; | |
2985 | oldSize = (endIndex + 1) * sizeof(void *); | |
7257e56c | 2986 | newSize = oldSize + sizeof(old_method_list *); // only increase by 1 |
b3962a83 A |
2987 | |
2988 | // Grow the method list array by one. | |
2989 | // This block may be from user code; don't use _realloc_internal | |
7257e56c | 2990 | *list = (old_method_list **)realloc(*list, newSize); |
b3962a83 A |
2991 | |
2992 | // Zero out addition part of new array | |
2993 | bzero (&((*list)[endIndex]), newSize - oldSize); | |
2994 | ||
2995 | // Place new end marker | |
2996 | (*list)[(newSize/sizeof(void *)) - 1] = END_OF_METHODS_LIST; | |
2997 | ||
2998 | // Insertion point corresponds to old array end | |
2999 | ptr = &((*list)[endIndex]); | |
3000 | } | |
3001 | ||
3002 | // Right shift existing entries by one | |
7af964d1 | 3003 | bcopy (*list, (*list) + 1, (uint8_t *)ptr - (uint8_t *)*list); |
b3962a83 A |
3004 | |
3005 | // Insert at method list at beginning of array | |
3006 | **list = mlist; | |
3007 | } | |
3008 | ||
3009 | /*********************************************************************** | |
3010 | * _objc_removeMethods. | |
3011 | * Remove methods from a class. | |
3012 | * Does not take any locks. | |
3013 | * Does not flush any method caches. | |
3014 | * If the class is currently in use, use class_removeMethods() instead. | |
3015 | **********************************************************************/ | |
7257e56c | 3016 | void _objc_removeMethods(Class cls, old_method_list *mlist) |
b3962a83 | 3017 | { |
7257e56c A |
3018 | old_method_list ***list; |
3019 | old_method_list **ptr; | |
b3962a83 | 3020 | |
7257e56c | 3021 | if (cls->methodLists == nil) { |
b3962a83 A |
3022 | // cls has no methods |
3023 | return; | |
3024 | } | |
7257e56c | 3025 | if (cls->methodLists == (old_method_list **)mlist) { |
b3962a83 | 3026 | // mlist is the class's only method list - erase it |
7257e56c | 3027 | cls->methodLists = nil; |
b3962a83 A |
3028 | return; |
3029 | } | |
3030 | if (cls->info & CLS_NO_METHOD_ARRAY) { | |
3031 | // cls has only one method list, and this isn't it - do nothing | |
3032 | return; | |
3033 | } | |
3034 | ||
3035 | // cls has a method list array - search it | |
3036 | ||
3037 | list = &cls->methodLists; | |
3038 | ||
3039 | // Locate list in the array | |
3040 | ptr = *list; | |
3041 | while (*ptr != mlist) { | |
3042 | // fix for radar # 2538790 | |
3043 | if ( *ptr == END_OF_METHODS_LIST ) return; | |
3044 | ptr += 1; | |
3045 | } | |
3046 | ||
3047 | // Remove this entry | |
3048 | *ptr = 0; | |
3049 | ||
3050 | // Left shift the following entries | |
3051 | while (*(++ptr) != END_OF_METHODS_LIST) | |
3052 | *(ptr-1) = *ptr; | |
3053 | *(ptr-1) = 0; | |
3054 | } | |
3055 | ||
3056 | /*********************************************************************** | |
3057 | * _objc_add_category. Install the specified category's methods and | |
3058 | * protocols into the class it augments. | |
3059 | * The class is assumed not to be in use yet: no locks are taken and | |
3060 | * no method caches are flushed. | |
3061 | **********************************************************************/ | |
7257e56c | 3062 | static inline void _objc_add_category(Class cls, old_category *category, int version) |
b3962a83 A |
3063 | { |
3064 | if (PrintConnecting) { | |
3065 | _objc_inform("CONNECT: attaching category '%s (%s)'", cls->name, category->category_name); | |
3066 | } | |
3067 | ||
3068 | // Augment instance methods | |
3069 | if (category->instance_methods) | |
3070 | _objc_insertMethods (cls, category->instance_methods, category); | |
3071 | ||
3072 | // Augment class methods | |
3073 | if (category->class_methods) | |
7257e56c | 3074 | _objc_insertMethods (cls->ISA(), category->class_methods, category); |
b3962a83 A |
3075 | |
3076 | // Augment protocols | |
3077 | if ((version >= 5) && category->protocols) | |
3078 | { | |
7257e56c | 3079 | if (cls->ISA()->version >= 5) |
b3962a83 A |
3080 | { |
3081 | category->protocols->next = cls->protocols; | |
3082 | cls->protocols = category->protocols; | |
7257e56c | 3083 | cls->ISA()->protocols = category->protocols; |
b3962a83 A |
3084 | } |
3085 | else | |
3086 | { | |
3087 | _objc_inform ("unable to add protocols from category %s...\n", category->category_name); | |
3088 | _objc_inform ("class `%s' must be recompiled\n", category->class_name); | |
3089 | } | |
3090 | } | |
3091 | ||
3092 | // Augment properties | |
3093 | if (version >= 7 && category->instance_properties) { | |
7257e56c | 3094 | if (cls->ISA()->version >= 6) { |
b3962a83 A |
3095 | _class_addProperties(cls, category->instance_properties); |
3096 | } else { | |
3097 | _objc_inform ("unable to add properties from category %s...\n", category->category_name); | |
3098 | _objc_inform ("class `%s' must be recompiled\n", category->class_name); | |
3099 | } | |
3100 | } | |
3101 | } | |
3102 | ||
3103 | /*********************************************************************** | |
3104 | * _objc_add_category_flush_caches. Install the specified category's | |
3105 | * methods into the class it augments, and flush the class' method cache. | |
3106 | * Return YES if some method caches now need to be flushed. | |
3107 | **********************************************************************/ | |
7257e56c | 3108 | static BOOL _objc_add_category_flush_caches(Class cls, old_category *category, int version) |
b3962a83 A |
3109 | { |
3110 | BOOL needFlush = NO; | |
3111 | ||
3112 | // Install the category's methods into its intended class | |
7af964d1 | 3113 | mutex_lock(&methodListLock); |
b3962a83 | 3114 | _objc_add_category (cls, category, version); |
7af964d1 | 3115 | mutex_unlock(&methodListLock); |
b3962a83 A |
3116 | |
3117 | // Queue for cache flushing so category's methods can get called | |
3118 | if (category->instance_methods) { | |
7257e56c | 3119 | cls->setInfo(CLS_FLUSH_CACHE); |
b3962a83 A |
3120 | needFlush = YES; |
3121 | } | |
3122 | if (category->class_methods) { | |
7257e56c | 3123 | cls->ISA()->setInfo(CLS_FLUSH_CACHE); |
b3962a83 A |
3124 | needFlush = YES; |
3125 | } | |
3126 | ||
3127 | return needFlush; | |
3128 | } | |
3129 | ||
3130 | ||
3131 | /*********************************************************************** | |
3132 | * reverse_cat | |
3133 | * Reverse the given linked list of pending categories. | |
3134 | * The pending category list is built backwards, and needs to be | |
3135 | * reversed before actually attaching the categories to a class. | |
3136 | * Returns the head of the new linked list. | |
3137 | **********************************************************************/ | |
3138 | static _objc_unresolved_category *reverse_cat(_objc_unresolved_category *cat) | |
3139 | { | |
7af964d1 A |
3140 | _objc_unresolved_category *prev; |
3141 | _objc_unresolved_category *cur; | |
3142 | _objc_unresolved_category *ahead; | |
3143 | ||
7257e56c | 3144 | if (!cat) return nil; |
b3962a83 | 3145 | |
7257e56c | 3146 | prev = nil; |
7af964d1 A |
3147 | cur = cat; |
3148 | ahead = cat->next; | |
b3962a83 A |
3149 | |
3150 | while (cur) { | |
3151 | ahead = cur->next; | |
3152 | cur->next = prev; | |
3153 | prev = cur; | |
3154 | cur = ahead; | |
3155 | } | |
3156 | ||
3157 | return prev; | |
3158 | } | |
3159 | ||
3160 | ||
3161 | /*********************************************************************** | |
3162 | * resolve_categories_for_class. | |
3163 | * Install all existing categories intended for the specified class. | |
3164 | * cls must be a true class and not a metaclass. | |
3165 | **********************************************************************/ | |
7257e56c | 3166 | static void resolve_categories_for_class(Class cls) |
b3962a83 A |
3167 | { |
3168 | _objc_unresolved_category * pending; | |
3169 | _objc_unresolved_category * next; | |
3170 | ||
3171 | // Nothing to do if there are no categories at all | |
3172 | if (!category_hash) return; | |
3173 | ||
3174 | // Locate and remove first element in category list | |
3175 | // associated with this class | |
7257e56c A |
3176 | pending = (_objc_unresolved_category *) |
3177 | NXMapKeyFreeingRemove (category_hash, cls->name); | |
b3962a83 A |
3178 | |
3179 | // Traverse the list of categories, if any, registered for this class | |
3180 | ||
3181 | // The pending list is built backwards. Reverse it and walk forwards. | |
3182 | pending = reverse_cat(pending); | |
3183 | ||
3184 | while (pending) { | |
3185 | if (pending->cat) { | |
3186 | // Install the category | |
3187 | // use the non-flush-cache version since we are only | |
3188 | // called from the class intialization code | |
3189 | _objc_add_category(cls, pending->cat, (int)pending->version); | |
3190 | } | |
3191 | ||
3192 | // Delink and reclaim this registration | |
3193 | next = pending->next; | |
3194 | _free_internal(pending); | |
3195 | pending = next; | |
3196 | } | |
3197 | } | |
3198 | ||
3199 | ||
3200 | /*********************************************************************** | |
3201 | * _objc_resolve_categories_for_class. | |
3202 | * Public version of resolve_categories_for_class. This was | |
3203 | * exported pre-10.4 for Omni et al. to workaround a problem | |
3204 | * with too-lazy category attachment. | |
3205 | * cls should be a class, but this function can also cope with metaclasses. | |
3206 | **********************************************************************/ | |
7257e56c | 3207 | void _objc_resolve_categories_for_class(Class cls) |
b3962a83 | 3208 | { |
b3962a83 A |
3209 | // If cls is a metaclass, get the class. |
3210 | // resolve_categories_for_class() requires a real class to work correctly. | |
3211 | if (ISMETA(cls)) { | |
3212 | if (strncmp(cls->name, "_%", 2) == 0) { | |
3213 | // Posee's meta's name is smashed and isn't in the class_hash, | |
3214 | // so objc_getClass doesn't work. | |
7af964d1 | 3215 | const char *baseName = strchr(cls->name, '%'); // get posee's real name |
7257e56c | 3216 | cls = objc_getClass(baseName); |
b3962a83 | 3217 | } else { |
7257e56c | 3218 | cls = objc_getClass(cls->name); |
b3962a83 A |
3219 | } |
3220 | } | |
3221 | ||
3222 | resolve_categories_for_class(cls); | |
3223 | } | |
3224 | ||
3225 | ||
3226 | /*********************************************************************** | |
3227 | * _objc_register_category. | |
3228 | * Process a category read from an image. | |
3229 | * If the category's class exists, attach the category immediately. | |
3230 | * Classes that need cache flushing are marked but not flushed. | |
3231 | * If the category's class does not exist yet, pend the category for | |
3232 | * later attachment. Pending categories are attached in the order | |
3233 | * they were discovered. | |
3234 | * Returns YES if some method caches now need to be flushed. | |
3235 | **********************************************************************/ | |
7257e56c | 3236 | static BOOL _objc_register_category(old_category *cat, int version) |
b3962a83 A |
3237 | { |
3238 | _objc_unresolved_category * new_cat; | |
3239 | _objc_unresolved_category * old; | |
7257e56c | 3240 | Class theClass; |
b3962a83 A |
3241 | |
3242 | // If the category's class exists, attach the category. | |
7257e56c | 3243 | if ((theClass = objc_lookUpClass(cat->class_name))) { |
b3962a83 A |
3244 | return _objc_add_category_flush_caches(theClass, cat, version); |
3245 | } | |
3246 | ||
3247 | // If the category's class exists but is unconnected, | |
3248 | // then attach the category to the class but don't bother | |
3249 | // flushing any method caches (because they must be empty). | |
3250 | // YES unconnected, NO class_handler | |
7257e56c | 3251 | if ((theClass = look_up_class(cat->class_name, YES, NO))) { |
b3962a83 A |
3252 | _objc_add_category(theClass, cat, version); |
3253 | return NO; | |
3254 | } | |
3255 | ||
3256 | ||
3257 | // Category's class does not exist yet. | |
3258 | // Save the category for later attachment. | |
3259 | ||
3260 | if (PrintConnecting) { | |
3261 | _objc_inform("CONNECT: pending category '%s (%s)'", cat->class_name, cat->category_name); | |
3262 | } | |
3263 | ||
3264 | // Create category lookup table if needed | |
3265 | if (!category_hash) | |
3266 | category_hash = NXCreateMapTableFromZone (NXStrValueMapPrototype, | |
3267 | 128, | |
3268 | _objc_internal_zone ()); | |
3269 | ||
3270 | // Locate an existing list of categories, if any, for the class. | |
7257e56c A |
3271 | old = (_objc_unresolved_category *) |
3272 | NXMapGet (category_hash, cat->class_name); | |
b3962a83 A |
3273 | |
3274 | // Register the category to be fixed up later. | |
3275 | // The category list is built backwards, and is reversed again | |
3276 | // by resolve_categories_for_class(). | |
7257e56c A |
3277 | new_cat = (_objc_unresolved_category *) |
3278 | _malloc_internal(sizeof(_objc_unresolved_category)); | |
b3962a83 A |
3279 | new_cat->next = old; |
3280 | new_cat->cat = cat; | |
3281 | new_cat->version = version; | |
3282 | (void) NXMapKeyCopyingInsert (category_hash, cat->class_name, new_cat); | |
3283 | ||
3284 | return NO; | |
3285 | } | |
3286 | ||
3287 | ||
cd5f04f5 | 3288 | const char ** |
b3962a83 A |
3289 | _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount) |
3290 | { | |
3291 | Module mods; | |
7af964d1 | 3292 | unsigned int m; |
b3962a83 A |
3293 | const char **list; |
3294 | int count; | |
3295 | int allocated; | |
3296 | ||
7257e56c | 3297 | list = nil; |
b3962a83 A |
3298 | count = 0; |
3299 | allocated = 0; | |
3300 | ||
3301 | mods = hi->mod_ptr; | |
3302 | for (m = 0; m < hi->mod_count; m++) { | |
3303 | int d; | |
3304 | ||
3305 | if (!mods[m].symtab) continue; | |
3306 | ||
3307 | for (d = 0; d < mods[m].symtab->cls_def_cnt; d++) { | |
7257e56c | 3308 | Class cls = (Class)mods[m].symtab->defs[d]; |
b3962a83 | 3309 | // fixme what about future-ified classes? |
7257e56c | 3310 | if (cls->isConnected()) { |
b3962a83 A |
3311 | if (count == allocated) { |
3312 | allocated = allocated*2 + 16; | |
7af964d1 A |
3313 | list = (const char **) |
3314 | realloc((void *)list, allocated * sizeof(char *)); | |
b3962a83 A |
3315 | } |
3316 | list[count++] = cls->name; | |
3317 | } | |
3318 | } | |
3319 | } | |
3320 | ||
3321 | if (count > 0) { | |
7257e56c | 3322 | // nil-terminate non-empty list |
b3962a83 A |
3323 | if (count == allocated) { |
3324 | allocated = allocated+1; | |
7af964d1 A |
3325 | list = (const char **) |
3326 | realloc((void *)list, allocated * sizeof(char *)); | |
b3962a83 | 3327 | } |
7257e56c | 3328 | list[count] = nil; |
b3962a83 A |
3329 | } |
3330 | ||
3331 | if (outCount) *outCount = count; | |
3332 | return list; | |
3333 | } | |
3334 | ||
8972963c A |
3335 | Class gdb_class_getClass(Class cls) |
3336 | { | |
3337 | const char *className = cls->name; | |
3338 | if(!className || !strlen(className)) return Nil; | |
3339 | Class rCls = look_up_class(className, NO, NO); | |
3340 | return rCls; | |
3341 | ||
3342 | } | |
3343 | ||
3344 | Class gdb_object_getClass(id obj) | |
3345 | { | |
7257e56c A |
3346 | if (!obj) return nil; |
3347 | return gdb_class_getClass(obj->getIsa()); | |
7af964d1 A |
3348 | } |
3349 | ||
3350 | ||
3351 | /*********************************************************************** | |
3352 | * Lock management | |
7af964d1 | 3353 | **********************************************************************/ |
cd5f04f5 A |
3354 | rwlock_t selLock = {}; |
3355 | mutex_t classLock = MUTEX_INITIALIZER; | |
3356 | mutex_t methodListLock = MUTEX_INITIALIZER; | |
3357 | mutex_t cacheUpdateLock = MUTEX_INITIALIZER; | |
3358 | recursive_mutex_t loadMethodLock = RECURSIVE_MUTEX_INITIALIZER; | |
7af964d1 | 3359 | |
cd5f04f5 | 3360 | void lock_init(void) |
7af964d1 A |
3361 | { |
3362 | rwlock_init(&selLock); | |
3363 | recursive_mutex_init(&loadMethodLock); | |
3364 | } | |
3365 | ||
3366 | ||
b3962a83 | 3367 | #endif |