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", cls->getName());
75 if (loadable_classes_used == loadable_classes_allocated) {
76 loadable_classes_allocated = loadable_classes_allocated*2 + 16;
77 loadable_classes = (struct loadable_class *)
78 _realloc_internal(loadable_classes,
79 loadable_classes_allocated *
80 sizeof(struct loadable_class));
83 loadable_classes[loadable_classes_used].cls = cls;
84 loadable_classes[loadable_classes_used].method = method;
85 loadable_classes_used++;
89 /***********************************************************************
90 * add_category_to_loadable_list
91 * Category cat's parent class exists and the category has been attached
92 * to its class. Schedule this category for +load after its parent class
93 * becomes connected and has its own +load method called.
94 **********************************************************************/
95 void add_category_to_loadable_list(Category cat)
99 recursive_mutex_assert_locked(&loadMethodLock);
101 method = _category_getLoadMethod(cat);
103 // Don't bother if cat has no +load method
107 _objc_inform("LOAD: category '%s(%s)' scheduled for +load",
108 _category_getClassName(cat), _category_getName(cat));
111 if (loadable_categories_used == loadable_categories_allocated) {
112 loadable_categories_allocated = loadable_categories_allocated*2 + 16;
113 loadable_categories = (struct loadable_category *)
114 _realloc_internal(loadable_categories,
115 loadable_categories_allocated *
116 sizeof(struct loadable_category));
119 loadable_categories[loadable_categories_used].cat = cat;
120 loadable_categories[loadable_categories_used].method = method;
121 loadable_categories_used++;
125 /***********************************************************************
126 * remove_class_from_loadable_list
127 * Class cls may have been loadable before, but it is now no longer
128 * loadable (because its image is being unmapped).
129 **********************************************************************/
130 void remove_class_from_loadable_list(Class cls)
132 recursive_mutex_assert_locked(&loadMethodLock);
134 if (loadable_classes) {
136 for (i = 0; i < loadable_classes_used; i++) {
137 if (loadable_classes[i].cls == cls) {
138 loadable_classes[i].cls = nil;
140 _objc_inform("LOAD: class '%s' unscheduled for +load", cls->getName());
149 /***********************************************************************
150 * remove_category_from_loadable_list
151 * Category cat may have been loadable before, but it is now no longer
152 * loadable (because its image is being unmapped).
153 **********************************************************************/
154 void remove_category_from_loadable_list(Category cat)
156 recursive_mutex_assert_locked(&loadMethodLock);
158 if (loadable_categories) {
160 for (i = 0; i < loadable_categories_used; i++) {
161 if (loadable_categories[i].cat == cat) {
162 loadable_categories[i].cat = nil;
164 _objc_inform("LOAD: category '%s(%s)' unscheduled for +load",
165 _category_getClassName(cat),
166 _category_getName(cat));
175 /***********************************************************************
177 * Call all pending class +load methods.
178 * If new classes become loadable, +load is NOT called for them.
180 * Called only by call_load_methods().
181 **********************************************************************/
182 static void call_class_loads(void)
186 // Detach current loadable list.
187 struct loadable_class *classes = loadable_classes;
188 int used = loadable_classes_used;
189 loadable_classes = nil;
190 loadable_classes_allocated = 0;
191 loadable_classes_used = 0;
193 // Call all +loads for the detached list.
194 for (i = 0; i < used; i++) {
195 Class cls = classes[i].cls;
196 load_method_t load_method = (load_method_t)classes[i].method;
200 _objc_inform("LOAD: +[%s load]\n", cls->getName());
202 (*load_method)(cls, SEL_load);
205 // Destroy the detached list.
206 if (classes) _free_internal(classes);
210 /***********************************************************************
211 * call_category_loads
212 * Call some pending category +load methods.
213 * The parent class of the +load-implementing categories has all of
214 * its categories attached, in case some are lazily waiting for +initalize.
215 * Don't call +load unless the parent class is connected.
216 * If new categories become loadable, +load is NOT called, and they
217 * are added to the end of the loadable list, and we return TRUE.
218 * Return FALSE if no new categories became loadable.
220 * Called only by call_load_methods().
221 **********************************************************************/
222 static BOOL call_category_loads(void)
225 BOOL new_categories_added = NO;
227 // Detach current loadable list.
228 struct loadable_category *cats = loadable_categories;
229 int used = loadable_categories_used;
230 int allocated = loadable_categories_allocated;
231 loadable_categories = nil;
232 loadable_categories_allocated = 0;
233 loadable_categories_used = 0;
235 // Call all +loads for the detached list.
236 for (i = 0; i < used; i++) {
237 Category cat = cats[i].cat;
238 load_method_t load_method = (load_method_t)cats[i].method;
242 cls = _category_getClass(cat);
243 if (cls && cls->isLoadable()) {
245 _objc_inform("LOAD: +[%s(%s) load]\n",
247 _category_getName(cat));
249 (*load_method)(cls, SEL_load);
254 // Compact detached list (order-preserving)
256 for (i = 0; i < used; i++) {
258 cats[i-shift] = cats[i];
265 // Copy any new +load candidates from the new list to the detached list.
266 new_categories_added = (loadable_categories_used > 0);
267 for (i = 0; i < loadable_categories_used; i++) {
268 if (used == allocated) {
269 allocated = allocated*2 + 16;
270 cats = (struct loadable_category *)
271 _realloc_internal(cats, allocated *
272 sizeof(struct loadable_category));
274 cats[used++] = loadable_categories[i];
277 // Destroy the new list.
278 if (loadable_categories) _free_internal(loadable_categories);
280 // Reattach the (now augmented) detached list.
281 // But if there's nothing left to load, destroy the list.
283 loadable_categories = cats;
284 loadable_categories_used = used;
285 loadable_categories_allocated = allocated;
287 if (cats) _free_internal(cats);
288 loadable_categories = nil;
289 loadable_categories_used = 0;
290 loadable_categories_allocated = 0;
294 if (loadable_categories_used != 0) {
295 _objc_inform("LOAD: %d categories still waiting for +load\n",
296 loadable_categories_used);
300 return new_categories_added;
304 /***********************************************************************
306 * Call all pending class and category +load methods.
307 * Class +load methods are called superclass-first.
308 * Category +load methods are not called until after the parent class's +load.
310 * This method must be RE-ENTRANT, because a +load could trigger
311 * more image mapping. In addition, the superclass-first ordering
312 * must be preserved in the face of re-entrant calls. Therefore,
313 * only the OUTERMOST call of this function will do anything, and
314 * that call will handle all loadable classes, even those generated
315 * while it was running.
317 * The sequence below preserves +load ordering in the face of
318 * image loading during a +load, and make sure that no
319 * +load method is forgotten because it was added during
322 * 1. Repeatedly call class +loads until there aren't any more
323 * 2. Call category +loads ONCE.
324 * 3. Run more +loads if:
325 * (a) there are more classes to load, OR
326 * (b) there are some potential category +loads that have
327 * still never been attempted.
328 * Category +loads are only run once to ensure "parent class first"
329 * ordering, even if a category +load triggers a new loadable class
330 * and a new loadable category attached to that class.
332 * Locking: loadMethodLock must be held by the caller
333 * All other locks must not be held.
334 **********************************************************************/
335 void call_load_methods(void)
337 static BOOL loading = NO;
338 BOOL more_categories;
340 recursive_mutex_assert_locked(&loadMethodLock);
342 // Re-entrant calls do nothing; the outermost call will finish the job.
346 void *pool = objc_autoreleasePoolPush();
349 // 1. Repeatedly call class +loads until there aren't any more
350 while (loadable_classes_used > 0) {
354 // 2. Call category +loads ONCE
355 more_categories = call_category_loads();
357 // 3. Run more +loads if there are classes OR more untried categories
358 } while (loadable_classes_used > 0 || more_categories);
360 objc_autoreleasePoolPop(pool);