]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-loadmethod.mm
objc4-551.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 recursive_mutex_assert_locked(&loadMethodLock);
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", cls->getName());
73 }
74
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));
81 }
82
83 loadable_classes[loadable_classes_used].cls = cls;
84 loadable_classes[loadable_classes_used].method = method;
85 loadable_classes_used++;
86 }
87
88
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)
96 {
97 IMP method;
98
99 recursive_mutex_assert_locked(&loadMethodLock);
100
101 method = _category_getLoadMethod(cat);
102
103 // Don't bother if cat has no +load method
104 if (!method) return;
105
106 if (PrintLoading) {
107 _objc_inform("LOAD: category '%s(%s)' scheduled for +load",
108 _category_getClassName(cat), _category_getName(cat));
109 }
110
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));
117 }
118
119 loadable_categories[loadable_categories_used].cat = cat;
120 loadable_categories[loadable_categories_used].method = method;
121 loadable_categories_used++;
122 }
123
124
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)
131 {
132 recursive_mutex_assert_locked(&loadMethodLock);
133
134 if (loadable_classes) {
135 int i;
136 for (i = 0; i < loadable_classes_used; i++) {
137 if (loadable_classes[i].cls == cls) {
138 loadable_classes[i].cls = nil;
139 if (PrintLoading) {
140 _objc_inform("LOAD: class '%s' unscheduled for +load", cls->getName());
141 }
142 return;
143 }
144 }
145 }
146 }
147
148
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)
155 {
156 recursive_mutex_assert_locked(&loadMethodLock);
157
158 if (loadable_categories) {
159 int i;
160 for (i = 0; i < loadable_categories_used; i++) {
161 if (loadable_categories[i].cat == cat) {
162 loadable_categories[i].cat = nil;
163 if (PrintLoading) {
164 _objc_inform("LOAD: category '%s(%s)' unscheduled for +load",
165 _category_getClassName(cat),
166 _category_getName(cat));
167 }
168 return;
169 }
170 }
171 }
172 }
173
174
175 /***********************************************************************
176 * call_class_loads
177 * Call all pending class +load methods.
178 * If new classes become loadable, +load is NOT called for them.
179 *
180 * Called only by call_load_methods().
181 **********************************************************************/
182 static void call_class_loads(void)
183 {
184 int i;
185
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;
192
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;
197 if (!cls) continue;
198
199 if (PrintLoading) {
200 _objc_inform("LOAD: +[%s load]\n", cls->getName());
201 }
202 (*load_method)(cls, SEL_load);
203 }
204
205 // Destroy the detached list.
206 if (classes) _free_internal(classes);
207 }
208
209
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.
219 *
220 * Called only by call_load_methods().
221 **********************************************************************/
222 static BOOL call_category_loads(void)
223 {
224 int i, shift;
225 BOOL new_categories_added = NO;
226
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;
234
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;
239 Class cls;
240 if (!cat) continue;
241
242 cls = _category_getClass(cat);
243 if (cls && cls->isLoadable()) {
244 if (PrintLoading) {
245 _objc_inform("LOAD: +[%s(%s) load]\n",
246 cls->getName(),
247 _category_getName(cat));
248 }
249 (*load_method)(cls, SEL_load);
250 cats[i].cat = nil;
251 }
252 }
253
254 // Compact detached list (order-preserving)
255 shift = 0;
256 for (i = 0; i < used; i++) {
257 if (cats[i].cat) {
258 cats[i-shift] = cats[i];
259 } else {
260 shift++;
261 }
262 }
263 used -= shift;
264
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));
273 }
274 cats[used++] = loadable_categories[i];
275 }
276
277 // Destroy the new list.
278 if (loadable_categories) _free_internal(loadable_categories);
279
280 // Reattach the (now augmented) detached list.
281 // But if there's nothing left to load, destroy the list.
282 if (used) {
283 loadable_categories = cats;
284 loadable_categories_used = used;
285 loadable_categories_allocated = allocated;
286 } else {
287 if (cats) _free_internal(cats);
288 loadable_categories = nil;
289 loadable_categories_used = 0;
290 loadable_categories_allocated = 0;
291 }
292
293 if (PrintLoading) {
294 if (loadable_categories_used != 0) {
295 _objc_inform("LOAD: %d categories still waiting for +load\n",
296 loadable_categories_used);
297 }
298 }
299
300 return new_categories_added;
301 }
302
303
304 /***********************************************************************
305 * call_load_methods
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.
309 *
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.
316 *
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
320 * a +load call.
321 * Sequence:
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.
331 *
332 * Locking: loadMethodLock must be held by the caller
333 * All other locks must not be held.
334 **********************************************************************/
335 void call_load_methods(void)
336 {
337 static BOOL loading = NO;
338 BOOL more_categories;
339
340 recursive_mutex_assert_locked(&loadMethodLock);
341
342 // Re-entrant calls do nothing; the outermost call will finish the job.
343 if (loading) return;
344 loading = YES;
345
346 void *pool = objc_autoreleasePoolPush();
347
348 do {
349 // 1. Repeatedly call class +loads until there aren't any more
350 while (loadable_classes_used > 0) {
351 call_class_loads();
352 }
353
354 // 2. Call category +loads ONCE
355 more_categories = call_category_loads();
356
357 // 3. Run more +loads if there are classes OR more untried categories
358 } while (loadable_classes_used > 0 || more_categories);
359
360 objc_autoreleasePoolPop(pool);
361
362 loading = NO;
363 }
364
365