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