]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-loadmethod.m
objc4-437.tar.gz
[apple/objc4.git] / runtime / objc-loadmethod.m
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 struct loadable_class {
33 Class cls; // may be NULL
34 IMP method;
35 };
36
37 struct loadable_category {
38 Category cat; // may be NULL
39 IMP method;
40 };
41
42
43 // List of classes that need +load called (pending superclass +load)
44 // This list always has superclasses first because of the way it is constructed
45 static struct loadable_class *loadable_classes NOBSS = NULL;
46 static int loadable_classes_used NOBSS = 0;
47 static int loadable_classes_allocated NOBSS = 0;
48
49 // List of categories that need +load called (pending parent class +load)
50 static struct loadable_category *loadable_categories NOBSS = NULL;
51 static int loadable_categories_used NOBSS = 0;
52 static int loadable_categories_allocated NOBSS = 0;
53
54
55 /***********************************************************************
56 * add_class_to_loadable_list
57 * Class cls has just become connected. Schedule it for +load if
58 * it implements a +load method.
59 **********************************************************************/
60 __private_extern__ void add_class_to_loadable_list(Class cls)
61 {
62 IMP method;
63
64 recursive_mutex_assert_locked(&loadMethodLock);
65
66 method = _class_getLoadMethod(cls);
67 if (!method) return; // Don't bother if cls has no +load method
68
69 if (PrintLoading) {
70 _objc_inform("LOAD: class '%s' scheduled for +load", _class_getName(cls));
71 }
72
73 if (loadable_classes_used == loadable_classes_allocated) {
74 loadable_classes_allocated = loadable_classes_allocated*2 + 16;
75 loadable_classes =
76 _realloc_internal(loadable_classes,
77 loadable_classes_allocated *
78 sizeof(struct loadable_class));
79 }
80
81 loadable_classes[loadable_classes_used].cls = cls;
82 loadable_classes[loadable_classes_used].method = method;
83 loadable_classes_used++;
84 }
85
86
87 /***********************************************************************
88 * add_category_to_loadable_list
89 * Category cat's parent class exists and the category has been attached
90 * to its class. Schedule this category for +load after its parent class
91 * becomes connected and has its own +load method called.
92 **********************************************************************/
93 __private_extern__ void add_category_to_loadable_list(Category cat)
94 {
95 IMP method;
96
97 recursive_mutex_assert_locked(&loadMethodLock);
98
99 method = _category_getLoadMethod(cat);
100
101 // Don't bother if cat has no +load method
102 if (!method) return;
103
104 if (PrintLoading) {
105 _objc_inform("LOAD: category '%s(%s)' scheduled for +load",
106 _category_getClassName(cat), _category_getName(cat));
107 }
108
109 if (loadable_categories_used == loadable_categories_allocated) {
110 loadable_categories_allocated = loadable_categories_allocated*2 + 16;
111 loadable_categories =
112 _realloc_internal(loadable_categories,
113 loadable_categories_allocated *
114 sizeof(struct loadable_category));
115 }
116
117 loadable_categories[loadable_categories_used].cat = cat;
118 loadable_categories[loadable_categories_used].method = method;
119 loadable_categories_used++;
120 }
121
122
123 /***********************************************************************
124 * remove_class_from_loadable_list
125 * Class cls may have been loadable before, but it is now no longer
126 * loadable (because its image is being unmapped).
127 **********************************************************************/
128 __private_extern__ void remove_class_from_loadable_list(Class cls)
129 {
130 recursive_mutex_assert_locked(&loadMethodLock);
131
132 if (loadable_classes) {
133 int i;
134 for (i = 0; i < loadable_classes_used; i++) {
135 if (loadable_classes[i].cls == cls) {
136 loadable_classes[i].cls = NULL;
137 if (PrintLoading) {
138 _objc_inform("LOAD: class '%s' unscheduled for +load", _class_getName(cls));
139 }
140 return;
141 }
142 }
143 }
144 }
145
146
147 /***********************************************************************
148 * remove_category_from_loadable_list
149 * Category cat may have been loadable before, but it is now no longer
150 * loadable (because its image is being unmapped).
151 **********************************************************************/
152 __private_extern__ void remove_category_from_loadable_list(Category cat)
153 {
154 recursive_mutex_assert_locked(&loadMethodLock);
155
156 if (loadable_categories) {
157 int i;
158 for (i = 0; i < loadable_categories_used; i++) {
159 if (loadable_categories[i].cat == cat) {
160 loadable_categories[i].cat = NULL;
161 if (PrintLoading) {
162 _objc_inform("LOAD: category '%s(%s)' unscheduled for +load",
163 _category_getClassName(cat),
164 _category_getName(cat));
165 }
166 return;
167 }
168 }
169 }
170 }
171
172
173 /***********************************************************************
174 * call_class_loads
175 * Call all pending class +load methods.
176 * If new classes become loadable, +load is NOT called for them.
177 *
178 * Called only by call_load_methods().
179 **********************************************************************/
180 static void call_class_loads(void)
181 {
182 int i;
183
184 // Detach current loadable list.
185 struct loadable_class *classes = loadable_classes;
186 int used = loadable_classes_used;
187 loadable_classes = NULL;
188 loadable_classes_allocated = 0;
189 loadable_classes_used = 0;
190
191 // Call all +loads for the detached list.
192 for (i = 0; i < used; i++) {
193 Class cls = classes[i].cls;
194 IMP load_method = classes[i].method;
195 if (!cls) continue;
196
197 if (PrintLoading) {
198 _objc_inform("LOAD: +[%s load]\n", _class_getName(cls));
199 }
200 (*load_method) ((id) cls, SEL_load);
201 }
202
203 // Destroy the detached list.
204 if (classes) _free_internal(classes);
205 }
206
207
208 /***********************************************************************
209 * call_category_loads
210 * Call some pending category +load methods.
211 * The parent class of the +load-implementing categories has all of
212 * its categories attached, in case some are lazily waiting for +initalize.
213 * Don't call +load unless the parent class is connected.
214 * If new categories become loadable, +load is NOT called, and they
215 * are added to the end of the loadable list, and we return TRUE.
216 * Return FALSE if no new categories became loadable.
217 *
218 * Called only by call_load_methods().
219 **********************************************************************/
220 static BOOL call_category_loads(void)
221 {
222 int i, shift;
223 BOOL new_categories_added = NO;
224
225 // Detach current loadable list.
226 struct loadable_category *cats = loadable_categories;
227 int used = loadable_categories_used;
228 int allocated = loadable_categories_allocated;
229 loadable_categories = NULL;
230 loadable_categories_allocated = 0;
231 loadable_categories_used = 0;
232
233 // Call all +loads for the detached list.
234 for (i = 0; i < used; i++) {
235 Category cat = cats[i].cat;
236 IMP load_method = cats[i].method;
237 Class cls;
238 if (!cat) continue;
239
240 cls = _category_getClass(cat);
241 if (cls && _class_isLoadable(cls)) {
242 if (PrintLoading) {
243 _objc_inform("LOAD: +[%s(%s) load]\n",
244 _class_getName(cls),
245 _category_getName(cat));
246 }
247 (*load_method) ((id) cls, SEL_load);
248 cats[i].cat = NULL;
249 }
250 }
251
252 // Compact detached list (order-preserving)
253 shift = 0;
254 for (i = 0; i < used; i++) {
255 if (cats[i].cat) {
256 cats[i-shift] = cats[i];
257 } else {
258 shift++;
259 }
260 }
261 used -= shift;
262
263 // Copy any new +load candidates from the new list to the detached list.
264 new_categories_added = (loadable_categories_used > 0);
265 for (i = 0; i < loadable_categories_used; i++) {
266 if (used == allocated) {
267 allocated = allocated*2 + 16;
268 cats = _realloc_internal(cats, allocated *
269 sizeof(struct loadable_category));
270 }
271 cats[used++] = loadable_categories[i];
272 }
273
274 // Destroy the new list.
275 if (loadable_categories) _free_internal(loadable_categories);
276
277 // Reattach the (now augmented) detached list.
278 // But if there's nothing left to load, destroy the list.
279 if (used) {
280 loadable_categories = cats;
281 loadable_categories_used = used;
282 loadable_categories_allocated = allocated;
283 } else {
284 if (cats) _free_internal(cats);
285 loadable_categories = NULL;
286 loadable_categories_used = 0;
287 loadable_categories_allocated = 0;
288 }
289
290 if (PrintLoading) {
291 if (loadable_categories_used != 0) {
292 _objc_inform("LOAD: %d categories still waiting for +load\n",
293 loadable_categories_used);
294 }
295 }
296
297 return new_categories_added;
298 }
299
300
301 /***********************************************************************
302 * call_load_methods
303 * Call all pending class and category +load methods.
304 * Class +load methods are called superclass-first.
305 * Category +load methods are not called until after the parent class's +load.
306 *
307 * This method must be RE-ENTRANT, because a +load could trigger
308 * more image mapping. In addition, the superclass-first ordering
309 * must be preserved in the face of re-entrant calls. Therefore,
310 * only the OUTERMOST call of this function will do anything, and
311 * that call will handle all loadable classes, even those generated
312 * while it was running.
313 *
314 * The sequence below preserves +load ordering in the face of
315 * image loading during a +load, and make sure that no
316 * +load method is forgotten because it was added during
317 * a +load call.
318 * Sequence:
319 * 1. Repeatedly call class +loads until there aren't any more
320 * 2. Call category +loads ONCE.
321 * 3. Run more +loads if:
322 * (a) there are more classes to load, OR
323 * (b) there are some potential category +loads that have
324 * still never been attempted.
325 * Category +loads are only run once to ensure "parent class first"
326 * ordering, even if a category +load triggers a new loadable class
327 * and a new loadable category attached to that class.
328 *
329 * Locking: loadMethodLock must be held by the caller
330 * All other locks must not be held.
331 **********************************************************************/
332 __private_extern__ void call_load_methods(void)
333 {
334 static BOOL loading = NO;
335 BOOL more_categories;
336
337 recursive_mutex_assert_locked(&loadMethodLock);
338
339 // Re-entrant calls do nothing; the outermost call will finish the job.
340 if (loading) return;
341 loading = YES;
342
343 do {
344 // 1. Repeatedly call class +loads until there aren't any more
345 while (loadable_classes_used > 0) {
346 call_class_loads();
347 }
348
349 // 2. Call category +loads ONCE
350 more_categories = call_category_loads();
351
352 // 3. Run more +loads if there are classes OR more untried categories
353 } while (loadable_classes_used > 0 || more_categories);
354
355 loading = NO;
356 }
357
358