]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-loadmethod.mm
objc4-750.1.tar.gz
[apple/objc4.git] / runtime / objc-loadmethod.mm
1 /*
2 * Copyright (c) 2004-2006 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-loadmethod.m
26 * Support for +load methods.
27 **********************************************************************/
28
29 #include "objc-loadmethod.h"
30 #include "objc-private.h"
31
32 typedef void(*load_method_t)(id, SEL);
33
34 struct loadable_class {
35 Class cls; // may be nil
36 IMP method;
37 };
38
39 struct loadable_category {
40 Category cat; // may be nil
41 IMP method;
42 };
43
44
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;
50
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;
55
56
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)
63 {
64 IMP method;
65
66 loadMethodLock.assertLocked();
67
68 method = cls->getLoadMethod();
69 if (!method) return; // Don't bother if cls has no +load method
70
71 if (PrintLoading) {
72 _objc_inform("LOAD: class '%s' scheduled for +load",
73 cls->nameForLogging());
74 }
75
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(loadable_classes,
80 loadable_classes_allocated *
81 sizeof(struct loadable_class));
82 }
83
84 loadable_classes[loadable_classes_used].cls = cls;
85 loadable_classes[loadable_classes_used].method = method;
86 loadable_classes_used++;
87 }
88
89
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)
97 {
98 IMP method;
99
100 loadMethodLock.assertLocked();
101
102 method = _category_getLoadMethod(cat);
103
104 // Don't bother if cat has no +load method
105 if (!method) return;
106
107 if (PrintLoading) {
108 _objc_inform("LOAD: category '%s(%s)' scheduled for +load",
109 _category_getClassName(cat), _category_getName(cat));
110 }
111
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(loadable_categories,
116 loadable_categories_allocated *
117 sizeof(struct loadable_category));
118 }
119
120 loadable_categories[loadable_categories_used].cat = cat;
121 loadable_categories[loadable_categories_used].method = method;
122 loadable_categories_used++;
123 }
124
125
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)
132 {
133 loadMethodLock.assertLocked();
134
135 if (loadable_classes) {
136 int i;
137 for (i = 0; i < loadable_classes_used; i++) {
138 if (loadable_classes[i].cls == cls) {
139 loadable_classes[i].cls = nil;
140 if (PrintLoading) {
141 _objc_inform("LOAD: class '%s' unscheduled for +load",
142 cls->nameForLogging());
143 }
144 return;
145 }
146 }
147 }
148 }
149
150
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)
157 {
158 loadMethodLock.assertLocked();
159
160 if (loadable_categories) {
161 int i;
162 for (i = 0; i < loadable_categories_used; i++) {
163 if (loadable_categories[i].cat == cat) {
164 loadable_categories[i].cat = nil;
165 if (PrintLoading) {
166 _objc_inform("LOAD: category '%s(%s)' unscheduled for +load",
167 _category_getClassName(cat),
168 _category_getName(cat));
169 }
170 return;
171 }
172 }
173 }
174 }
175
176
177 /***********************************************************************
178 * call_class_loads
179 * Call all pending class +load methods.
180 * If new classes become loadable, +load is NOT called for them.
181 *
182 * Called only by call_load_methods().
183 **********************************************************************/
184 static void call_class_loads(void)
185 {
186 int i;
187
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;
194
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;
199 if (!cls) continue;
200
201 if (PrintLoading) {
202 _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
203 }
204 (*load_method)(cls, SEL_load);
205 }
206
207 // Destroy the detached list.
208 if (classes) free(classes);
209 }
210
211
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.
221 *
222 * Called only by call_load_methods().
223 **********************************************************************/
224 static bool call_category_loads(void)
225 {
226 int i, shift;
227 bool new_categories_added = NO;
228
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;
236
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;
241 Class cls;
242 if (!cat) continue;
243
244 cls = _category_getClass(cat);
245 if (cls && cls->isLoadable()) {
246 if (PrintLoading) {
247 _objc_inform("LOAD: +[%s(%s) load]\n",
248 cls->nameForLogging(),
249 _category_getName(cat));
250 }
251 (*load_method)(cls, SEL_load);
252 cats[i].cat = nil;
253 }
254 }
255
256 // Compact detached list (order-preserving)
257 shift = 0;
258 for (i = 0; i < used; i++) {
259 if (cats[i].cat) {
260 cats[i-shift] = cats[i];
261 } else {
262 shift++;
263 }
264 }
265 used -= shift;
266
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(cats, allocated *
274 sizeof(struct loadable_category));
275 }
276 cats[used++] = loadable_categories[i];
277 }
278
279 // Destroy the new list.
280 if (loadable_categories) free(loadable_categories);
281
282 // Reattach the (now augmented) detached list.
283 // But if there's nothing left to load, destroy the list.
284 if (used) {
285 loadable_categories = cats;
286 loadable_categories_used = used;
287 loadable_categories_allocated = allocated;
288 } else {
289 if (cats) free(cats);
290 loadable_categories = nil;
291 loadable_categories_used = 0;
292 loadable_categories_allocated = 0;
293 }
294
295 if (PrintLoading) {
296 if (loadable_categories_used != 0) {
297 _objc_inform("LOAD: %d categories still waiting for +load\n",
298 loadable_categories_used);
299 }
300 }
301
302 return new_categories_added;
303 }
304
305
306 /***********************************************************************
307 * call_load_methods
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.
311 *
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.
318 *
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
322 * a +load call.
323 * Sequence:
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.
333 *
334 * Locking: loadMethodLock must be held by the caller
335 * All other locks must not be held.
336 **********************************************************************/
337 void call_load_methods(void)
338 {
339 static bool loading = NO;
340 bool more_categories;
341
342 loadMethodLock.assertLocked();
343
344 // Re-entrant calls do nothing; the outermost call will finish the job.
345 if (loading) return;
346 loading = YES;
347
348 void *pool = objc_autoreleasePoolPush();
349
350 do {
351 // 1. Repeatedly call class +loads until there aren't any more
352 while (loadable_classes_used > 0) {
353 call_class_loads();
354 }
355
356 // 2. Call category +loads ONCE
357 more_categories = call_category_loads();
358
359 // 3. Run more +loads if there are classes OR more untried categories
360 } while (loadable_classes_used > 0 || more_categories);
361
362 objc_autoreleasePoolPop(pool);
363
364 loading = NO;
365 }
366
367