2 * Copyright (c) 2004-2006 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 /***********************************************************************
26 * Support for +load methods.
27 **********************************************************************/
29 #include "objc-loadmethod.h"
30 #include "objc-private.h"
32 typedef void(*load_method_t)(id, SEL);
34 struct loadable_class {
35 Class cls; // may be nil
39 struct loadable_category {
40 Category cat; // may be nil
45 // List of classes that need +load called (pending superclass +load)
46 // This list always has superclasses first because of the way it is constructed
47 static struct loadable_class *loadable_classes = nil;
48 static int loadable_classes_used = 0;
49 static int loadable_classes_allocated = 0;
51 // List of categories that need +load called (pending parent class +load)
52 static struct loadable_category *loadable_categories = nil;
53 static int loadable_categories_used = 0;
54 static int loadable_categories_allocated = 0;
57 /***********************************************************************
58 * add_class_to_loadable_list
59 * Class cls has just become connected. Schedule it for +load if
60 * it implements a +load method.
61 **********************************************************************/
62 void add_class_to_loadable_list(Class cls)
66 recursive_mutex_assert_locked(&loadMethodLock);
68 method = cls->getLoadMethod();
69 if (!method) return; // Don't bother if cls has no +load method
72 _objc_inform("LOAD: class '%s' scheduled for +load",
73 cls->nameForLogging());
76 if (loadable_classes_used == loadable_classes_allocated) {
77 loadable_classes_allocated = loadable_classes_allocated*2 + 16;
78 loadable_classes = (struct loadable_class *)
79 _realloc_internal(loadable_classes,
80 loadable_classes_allocated *
81 sizeof(struct loadable_class));
84 loadable_classes[loadable_classes_used].cls = cls;
85 loadable_classes[loadable_classes_used].method = method;
86 loadable_classes_used++;
90 /***********************************************************************
91 * add_category_to_loadable_list
92 * Category cat's parent class exists and the category has been attached
93 * to its class. Schedule this category for +load after its parent class
94 * becomes connected and has its own +load method called.
95 **********************************************************************/
96 void add_category_to_loadable_list(Category cat)
100 recursive_mutex_assert_locked(&loadMethodLock);
102 method = _category_getLoadMethod(cat);
104 // Don't bother if cat has no +load method
108 _objc_inform("LOAD: category '%s(%s)' scheduled for +load",
109 _category_getClassName(cat), _category_getName(cat));
112 if (loadable_categories_used == loadable_categories_allocated) {
113 loadable_categories_allocated = loadable_categories_allocated*2 + 16;
114 loadable_categories = (struct loadable_category *)
115 _realloc_internal(loadable_categories,
116 loadable_categories_allocated *
117 sizeof(struct loadable_category));
120 loadable_categories[loadable_categories_used].cat = cat;
121 loadable_categories[loadable_categories_used].method = method;
122 loadable_categories_used++;
126 /***********************************************************************
127 * remove_class_from_loadable_list
128 * Class cls may have been loadable before, but it is now no longer
129 * loadable (because its image is being unmapped).
130 **********************************************************************/
131 void remove_class_from_loadable_list(Class cls)
133 recursive_mutex_assert_locked(&loadMethodLock);
135 if (loadable_classes) {
137 for (i = 0; i < loadable_classes_used; i++) {
138 if (loadable_classes[i].cls == cls) {
139 loadable_classes[i].cls = nil;
141 _objc_inform("LOAD: class '%s' unscheduled for +load",
142 cls->nameForLogging());
151 /***********************************************************************
152 * remove_category_from_loadable_list
153 * Category cat may have been loadable before, but it is now no longer
154 * loadable (because its image is being unmapped).
155 **********************************************************************/
156 void remove_category_from_loadable_list(Category cat)
158 recursive_mutex_assert_locked(&loadMethodLock);
160 if (loadable_categories) {
162 for (i = 0; i < loadable_categories_used; i++) {
163 if (loadable_categories[i].cat == cat) {
164 loadable_categories[i].cat = nil;
166 _objc_inform("LOAD: category '%s(%s)' unscheduled for +load",
167 _category_getClassName(cat),
168 _category_getName(cat));
177 /***********************************************************************
179 * Call all pending class +load methods.
180 * If new classes become loadable, +load is NOT called for them.
182 * Called only by call_load_methods().
183 **********************************************************************/
184 static void call_class_loads(void)
188 // Detach current loadable list.
189 struct loadable_class *classes = loadable_classes;
190 int used = loadable_classes_used;
191 loadable_classes = nil;
192 loadable_classes_allocated = 0;
193 loadable_classes_used = 0;
195 // Call all +loads for the detached list.
196 for (i = 0; i < used; i++) {
197 Class cls = classes[i].cls;
198 load_method_t load_method = (load_method_t)classes[i].method;
202 _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
204 (*load_method)(cls, SEL_load);
207 // Destroy the detached list.
208 if (classes) _free_internal(classes);
212 /***********************************************************************
213 * call_category_loads
214 * Call some pending category +load methods.
215 * The parent class of the +load-implementing categories has all of
216 * its categories attached, in case some are lazily waiting for +initalize.
217 * Don't call +load unless the parent class is connected.
218 * If new categories become loadable, +load is NOT called, and they
219 * are added to the end of the loadable list, and we return TRUE.
220 * Return FALSE if no new categories became loadable.
222 * Called only by call_load_methods().
223 **********************************************************************/
224 static BOOL call_category_loads(void)
227 BOOL new_categories_added = NO;
229 // Detach current loadable list.
230 struct loadable_category *cats = loadable_categories;
231 int used = loadable_categories_used;
232 int allocated = loadable_categories_allocated;
233 loadable_categories = nil;
234 loadable_categories_allocated = 0;
235 loadable_categories_used = 0;
237 // Call all +loads for the detached list.
238 for (i = 0; i < used; i++) {
239 Category cat = cats[i].cat;
240 load_method_t load_method = (load_method_t)cats[i].method;
244 cls = _category_getClass(cat);
245 if (cls && cls->isLoadable()) {
247 _objc_inform("LOAD: +[%s(%s) load]\n",
248 cls->nameForLogging(),
249 _category_getName(cat));
251 (*load_method)(cls, SEL_load);
256 // Compact detached list (order-preserving)
258 for (i = 0; i < used; i++) {
260 cats[i-shift] = cats[i];
267 // Copy any new +load candidates from the new list to the detached list.
268 new_categories_added = (loadable_categories_used > 0);
269 for (i = 0; i < loadable_categories_used; i++) {
270 if (used == allocated) {
271 allocated = allocated*2 + 16;
272 cats = (struct loadable_category *)
273 _realloc_internal(cats, allocated *
274 sizeof(struct loadable_category));
276 cats[used++] = loadable_categories[i];
279 // Destroy the new list.
280 if (loadable_categories) _free_internal(loadable_categories);
282 // Reattach the (now augmented) detached list.
283 // But if there's nothing left to load, destroy the list.
285 loadable_categories = cats;
286 loadable_categories_used = used;
287 loadable_categories_allocated = allocated;
289 if (cats) _free_internal(cats);
290 loadable_categories = nil;
291 loadable_categories_used = 0;
292 loadable_categories_allocated = 0;
296 if (loadable_categories_used != 0) {
297 _objc_inform("LOAD: %d categories still waiting for +load\n",
298 loadable_categories_used);
302 return new_categories_added;
306 /***********************************************************************
308 * Call all pending class and category +load methods.
309 * Class +load methods are called superclass-first.
310 * Category +load methods are not called until after the parent class's +load.
312 * This method must be RE-ENTRANT, because a +load could trigger
313 * more image mapping. In addition, the superclass-first ordering
314 * must be preserved in the face of re-entrant calls. Therefore,
315 * only the OUTERMOST call of this function will do anything, and
316 * that call will handle all loadable classes, even those generated
317 * while it was running.
319 * The sequence below preserves +load ordering in the face of
320 * image loading during a +load, and make sure that no
321 * +load method is forgotten because it was added during
324 * 1. Repeatedly call class +loads until there aren't any more
325 * 2. Call category +loads ONCE.
326 * 3. Run more +loads if:
327 * (a) there are more classes to load, OR
328 * (b) there are some potential category +loads that have
329 * still never been attempted.
330 * Category +loads are only run once to ensure "parent class first"
331 * ordering, even if a category +load triggers a new loadable class
332 * and a new loadable category attached to that class.
334 * Locking: loadMethodLock must be held by the caller
335 * All other locks must not be held.
336 **********************************************************************/
337 void call_load_methods(void)
339 static BOOL loading = NO;
340 BOOL more_categories;
342 recursive_mutex_assert_locked(&loadMethodLock);
344 // Re-entrant calls do nothing; the outermost call will finish the job.
348 void *pool = objc_autoreleasePoolPush();
351 // 1. Repeatedly call class +loads until there aren't any more
352 while (loadable_classes_used > 0) {
356 // 2. Call category +loads ONCE
357 more_categories = call_category_loads();
359 // 3. Run more +loads if there are classes OR more untried categories
360 } while (loadable_classes_used > 0 || more_categories);
362 objc_autoreleasePoolPop(pool);