]>
Commit | Line | Data |
---|---|---|
88e17b57 | 1 | /* GNU Objective C Runtime message lookup |
0fc39d8a | 2 | Copyright (C) 1993, 1995, 1996, 1997, 1998, |
ad9eef11 | 3 | 2001, 2002, 2004, 2009, 2010 Free Software Foundation, Inc. |
88e17b57 BE |
4 | Contributed by Kresten Krab Thorup |
5 | ||
38709cad | 6 | This file is part of GCC. |
88e17b57 | 7 | |
38709cad | 8 | GCC is free software; you can redistribute it and/or modify it under the |
88e17b57 | 9 | terms of the GNU General Public License as published by the Free Software |
748086b7 | 10 | Foundation; either version 3, or (at your option) any later version. |
88e17b57 | 11 | |
38709cad | 12 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
88e17b57 BE |
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
14 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | |
15 | details. | |
16 | ||
748086b7 JJ |
17 | Under Section 7 of GPL version 3, you are granted additional |
18 | permissions described in the GCC Runtime Library Exception, version | |
19 | 3.1, as published by the Free Software Foundation. | |
20 | ||
21 | You should have received a copy of the GNU General Public License and | |
22 | a copy of the GCC Runtime Library Exception along with this program; | |
23 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
24 | <http://www.gnu.org/licenses/>. */ | |
88e17b57 | 25 | |
e99776d8 NP |
26 | /* Uncommented the following line to enable debug logging. Use this |
27 | only while debugging the runtime. */ | |
28 | /* #define DEBUG 1 */ | |
88e17b57 | 29 | |
4977bab6 | 30 | /* FIXME: This file has no business including tm.h. */ |
435317e2 AP |
31 | /* FIXME: This should be using libffi instead of __builtin_apply |
32 | and friends. */ | |
4977bab6 | 33 | |
6dead247 | 34 | #include "objc-private/common.h" |
7b869986 | 35 | #include "objc-private/error.h" |
bce1b489 | 36 | #include "tconfig.h" |
4977bab6 ZW |
37 | #include "coretypes.h" |
38 | #include "tm.h" | |
114dae43 | 39 | #include "objc/runtime.h" |
5ec582f9 | 40 | #include "objc/message.h" /* For objc_msg_lookup(), objc_msg_lookup_super(). */ |
a19fac96 | 41 | #include "objc/thr.h" |
114dae43 | 42 | #include "objc-private/module-abi-8.h" |
a19fac96 | 43 | #include "objc-private/runtime.h" |
b5a3450f | 44 | #include "objc-private/hash.h" |
5d3b14bd | 45 | #include "objc-private/sarray.h" |
114dae43 | 46 | #include "objc-private/selector.h" /* For sel_is_mapped() */ |
88e17b57 | 47 | #include "runtime-info.h" |
5d3b14bd | 48 | #include <assert.h> /* For assert */ |
5be9cdc1 | 49 | #include <string.h> /* For strlen */ |
88e17b57 | 50 | |
435317e2 | 51 | /* This is how we hack STRUCT_VALUE to be 1 or 0. */ |
88e17b57 BE |
52 | #define gen_rtx(args...) 1 |
53 | #define gen_rtx_MEM(args...) 1 | |
68b61df9 | 54 | #define gen_rtx_REG(args...) 1 |
114dae43 | 55 | /* Already defined in gcc/coretypes.h. So prevent double definition warning. */ |
c24aadf3 | 56 | #undef rtx |
88e17b57 BE |
57 | #define rtx int |
58 | ||
40165636 | 59 | #if ! defined (STRUCT_VALUE) || STRUCT_VALUE == 0 |
88e17b57 BE |
60 | #define INVISIBLE_STRUCT_RETURN 1 |
61 | #else | |
62 | #define INVISIBLE_STRUCT_RETURN 0 | |
63 | #endif | |
64 | ||
d05809af NP |
65 | /* The uninstalled dispatch table. If a class' dispatch table points |
66 | to __objc_uninstalled_dtable then that means it needs its dispatch | |
67 | table to be installed. */ | |
40165636 | 68 | struct sarray *__objc_uninstalled_dtable = 0; /* !T:MUTEX */ |
88e17b57 | 69 | |
b427203d NP |
70 | /* Two hooks for method forwarding. If either is set, it is invoked to |
71 | * return a function that performs the real forwarding. If both are | |
72 | * set, the result of __objc_msg_forward2 will be preferred over that | |
73 | * of __objc_msg_forward. If both return NULL or are unset, the | |
74 | * libgcc based functions (__builtin_apply and friends) are used. */ | |
40165636 | 75 | IMP (*__objc_msg_forward) (SEL) = NULL; |
80ae8e8a | 76 | IMP (*__objc_msg_forward2) (id, SEL) = NULL; |
68b61df9 | 77 | |
b427203d | 78 | /* Send +initialize to class. */ |
40165636 | 79 | static void __objc_send_initialize (Class); |
88e17b57 | 80 | |
b5a3450f RFM |
81 | /* Forward declare some functions */ |
82 | static void __objc_install_dtable_for_class (Class cls); | |
83 | static void __objc_prepare_dtable_for_class (Class cls); | |
84 | static void __objc_install_prepared_dtable_for_class (Class cls); | |
88e17b57 | 85 | |
b5a3450f RFM |
86 | static struct sarray *__objc_prepared_dtable_for_class (Class cls); |
87 | static IMP __objc_get_prepared_imp (Class cls,SEL sel); | |
88 | ||
88e17b57 BE |
89 | |
90 | /* Various forwarding functions that are used based upon the | |
91 | return type for the selector. | |
92 | __objc_block_forward for structures. | |
93 | __objc_double_forward for floats/doubles. | |
b427203d | 94 | __objc_word_forward for pointers or types that fit in registers. */ |
40165636 RB |
95 | static double __objc_double_forward (id, SEL, ...); |
96 | static id __objc_word_forward (id, SEL, ...); | |
88e17b57 BE |
97 | typedef struct { id many[8]; } __big; |
98 | #if INVISIBLE_STRUCT_RETURN | |
99 | static __big | |
100 | #else | |
101 | static id | |
102 | #endif | |
40165636 | 103 | __objc_block_forward (id, SEL, ...); |
ad9eef11 NP |
104 | static struct objc_method * search_for_method_in_hierarchy (Class class, SEL sel); |
105 | struct objc_method * search_for_method_in_list (struct objc_method_list * list, SEL op); | |
faaa30fe | 106 | id nil_method (id, SEL); |
40165636 | 107 | |
b427203d | 108 | /* Given a selector, return the proper forwarding implementation. */ |
435317e2 | 109 | inline |
40165636 | 110 | IMP |
80ae8e8a | 111 | __objc_get_forward_imp (id rcv, SEL sel) |
40165636 | 112 | { |
b427203d NP |
113 | /* If a custom forwarding hook was registered, try getting a |
114 | forwarding function from it. There are two forward routine hooks, | |
115 | one that takes the receiver as an argument and one that does | |
116 | not. */ | |
8972bcd8 AR |
117 | if (__objc_msg_forward2) |
118 | { | |
119 | IMP result; | |
120 | if ((result = __objc_msg_forward2 (rcv, sel)) != NULL) | |
121 | return result; | |
122 | } | |
40165636 RB |
123 | if (__objc_msg_forward) |
124 | { | |
125 | IMP result; | |
bd8d449d | 126 | if ((result = __objc_msg_forward (sel)) != NULL) |
b5a3450f | 127 | return result; |
40165636 | 128 | } |
bd8d449d | 129 | |
b427203d NP |
130 | /* In all other cases, use the default forwarding functions built |
131 | using __builtin_apply and friends. */ | |
40165636 RB |
132 | { |
133 | const char *t = sel->sel_types; | |
b427203d | 134 | |
40165636 RB |
135 | if (t && (*t == '[' || *t == '(' || *t == '{') |
136 | #ifdef OBJC_MAX_STRUCT_BY_VALUE | |
137 | && objc_sizeof_type (t) > OBJC_MAX_STRUCT_BY_VALUE | |
138 | #endif | |
139 | ) | |
140 | return (IMP)__objc_block_forward; | |
141 | else if (t && (*t == 'f' || *t == 'd')) | |
142 | return (IMP)__objc_double_forward; | |
143 | else | |
144 | return (IMP)__objc_word_forward; | |
145 | } | |
146 | } | |
88e17b57 | 147 | |
e97cfd97 NP |
148 | /* Selectors for +resolveClassMethod: and +resolveInstanceMethod:. |
149 | These are set up at startup. */ | |
150 | static SEL selector_resolveClassMethod = NULL; | |
151 | static SEL selector_resolveInstanceMethod = NULL; | |
152 | ||
153 | /* Internal routines use to resolve a class method using | |
154 | +resolveClassMethod:. 'class' is always a non-Nil class (*not* a | |
155 | meta-class), and 'sel' is the selector that we are trying to | |
156 | resolve. This must be called when class is not Nil, and the | |
157 | dispatch table for class methods has already been installed. | |
158 | ||
159 | This routine tries to call +resolveClassMethod: to give an | |
160 | opportunity to resolve the method. If +resolveClassMethod: returns | |
161 | YES, it tries looking up the method again, and if found, it returns | |
162 | it. Else, it returns NULL. */ | |
163 | static inline | |
164 | IMP | |
165 | __objc_resolve_class_method (Class class, SEL sel) | |
166 | { | |
167 | /* We need to lookup +resolveClassMethod:. */ | |
168 | BOOL (*resolveMethodIMP) (id, SEL, SEL); | |
169 | ||
170 | /* The dispatch table for class methods is already installed and we | |
171 | don't want any forwarding to happen when looking up this method, | |
172 | so we just look it up directly. Note that if 'sel' is precisely | |
173 | +resolveClassMethod:, this would look it up yet again and find | |
174 | nothing. That's no problem and there's no recursion. */ | |
175 | resolveMethodIMP = (BOOL (*) (id, SEL, SEL))sarray_get_safe | |
176 | (class->class_pointer->dtable, (size_t) selector_resolveClassMethod->sel_id); | |
177 | ||
178 | if (resolveMethodIMP && resolveMethodIMP ((id)class, selector_resolveClassMethod, sel)) | |
179 | { | |
180 | /* +resolveClassMethod: returned YES. Look the method up again. | |
181 | We already know the dtable is installed. */ | |
182 | ||
183 | /* TODO: There is the case where +resolveClassMethod: is buggy | |
184 | and returned YES without actually adding the method. We | |
185 | could maybe print an error message. */ | |
186 | return sarray_get_safe (class->class_pointer->dtable, (size_t) sel->sel_id); | |
187 | } | |
188 | ||
189 | return NULL; | |
190 | } | |
191 | ||
192 | /* Internal routines use to resolve a instance method using | |
193 | +resolveInstanceMethod:. 'class' is always a non-Nil class, and | |
194 | 'sel' is the selector that we are trying to resolve. This must be | |
195 | called when class is not Nil, and the dispatch table for instance | |
196 | methods has already been installed. | |
197 | ||
198 | This routine tries to call +resolveInstanceMethod: to give an | |
199 | opportunity to resolve the method. If +resolveInstanceMethod: | |
200 | returns YES, it tries looking up the method again, and if found, it | |
201 | returns it. Else, it returns NULL. */ | |
202 | static inline | |
203 | IMP | |
204 | __objc_resolve_instance_method (Class class, SEL sel) | |
205 | { | |
206 | /* We need to lookup +resolveInstanceMethod:. */ | |
207 | BOOL (*resolveMethodIMP) (id, SEL, SEL); | |
208 | ||
209 | /* The dispatch table for class methods may not be already installed | |
210 | so we have to install it if needed. */ | |
211 | resolveMethodIMP = sarray_get_safe (class->class_pointer->dtable, | |
212 | (size_t) selector_resolveInstanceMethod->sel_id); | |
213 | if (resolveMethodIMP == 0) | |
214 | { | |
215 | /* Try again after installing the dtable. */ | |
216 | if (class->class_pointer->dtable == __objc_uninstalled_dtable) | |
217 | { | |
218 | objc_mutex_lock (__objc_runtime_mutex); | |
219 | if (class->class_pointer->dtable == __objc_uninstalled_dtable) | |
b5a3450f | 220 | __objc_install_dtable_for_class (class->class_pointer); |
e97cfd97 NP |
221 | objc_mutex_unlock (__objc_runtime_mutex); |
222 | } | |
223 | resolveMethodIMP = sarray_get_safe (class->class_pointer->dtable, | |
224 | (size_t) selector_resolveInstanceMethod->sel_id); | |
225 | } | |
226 | ||
227 | if (resolveMethodIMP && resolveMethodIMP ((id)class, selector_resolveInstanceMethod, sel)) | |
228 | { | |
229 | /* +resolveInstanceMethod: returned YES. Look the method up | |
230 | again. We already know the dtable is installed. */ | |
231 | ||
232 | /* TODO: There is the case where +resolveInstanceMethod: is | |
233 | buggy and returned YES without actually adding the method. | |
234 | We could maybe print an error message. */ | |
235 | return sarray_get_safe (class->dtable, (size_t) sel->sel_id); | |
236 | } | |
237 | ||
238 | return NULL; | |
239 | } | |
240 | ||
b5a3450f RFM |
241 | /* Given a CLASS and selector, return the implementation corresponding |
242 | to the method of the selector. | |
243 | ||
244 | If CLASS is a class, the instance method is returned. | |
245 | If CLASS is a meta class, the class method is returned. | |
246 | ||
247 | Since this requires the dispatch table to be installed, this function | |
248 | will implicitly invoke +initialize for CLASS if it hasn't been | |
249 | invoked yet. This also insures that +initialize has been invoked | |
250 | when the returned implementation is called directly. | |
251 | ||
252 | The forwarding hooks require the receiver as an argument (if they are to | |
253 | perform dynamic lookup in proxy objects etc), so this function has a | |
254 | receiver argument to be used with those hooks. */ | |
255 | static inline | |
256 | IMP | |
257 | get_implementation (id receiver, Class class, SEL sel) | |
258 | { | |
259 | void *res; | |
260 | ||
261 | if (class->dtable == __objc_uninstalled_dtable) | |
262 | { | |
263 | /* The dispatch table needs to be installed. */ | |
264 | objc_mutex_lock (__objc_runtime_mutex); | |
265 | ||
266 | /* Double-checked locking pattern: Check | |
267 | __objc_uninstalled_dtable again in case another thread | |
0dd5327a NP |
268 | installed the dtable while we were waiting for the lock to be |
269 | released. */ | |
b5a3450f | 270 | if (class->dtable == __objc_uninstalled_dtable) |
0dd5327a | 271 | __objc_install_dtable_for_class (class); |
b5a3450f | 272 | |
0dd5327a NP |
273 | /* If the dispatch table is not yet installed, we are still in |
274 | the process of executing +initialize. But the implementation | |
275 | pointer should be available in the prepared ispatch table if | |
276 | it exists at all. */ | |
b5a3450f RFM |
277 | if (class->dtable == __objc_uninstalled_dtable) |
278 | { | |
279 | assert (__objc_prepared_dtable_for_class (class) != 0); | |
280 | res = __objc_get_prepared_imp (class, sel); | |
281 | } | |
282 | else | |
0dd5327a NP |
283 | res = 0; |
284 | ||
b5a3450f | 285 | objc_mutex_unlock (__objc_runtime_mutex); |
0dd5327a NP |
286 | /* Call ourselves with the installed dispatch table and get the |
287 | real method. */ | |
b5a3450f RFM |
288 | if (!res) |
289 | res = get_implementation (receiver, class, sel); | |
290 | } | |
291 | else | |
292 | { | |
293 | /* The dispatch table has been installed. */ | |
294 | res = sarray_get_safe (class->dtable, (size_t) sel->sel_id); | |
295 | if (res == 0) | |
296 | { | |
0dd5327a NP |
297 | /* The dispatch table has been installed, and the method is |
298 | not in the dispatch table. So the method just doesn't | |
299 | exist for the class. */ | |
b5a3450f RFM |
300 | |
301 | /* Try going through the +resolveClassMethod: or | |
302 | +resolveInstanceMethod: process. */ | |
303 | if (CLS_ISMETA (class)) | |
304 | { | |
305 | /* We have the meta class, but we need to invoke the | |
306 | +resolveClassMethod: method on the class. So, we | |
0dd5327a NP |
307 | need to obtain the class from the meta class, which |
308 | we do using the fact that both the class and the | |
309 | meta-class have the same name. */ | |
b5a3450f RFM |
310 | Class realClass = objc_lookUpClass (class->name); |
311 | if (realClass) | |
312 | res = __objc_resolve_class_method (realClass, sel); | |
313 | } | |
314 | else | |
315 | res = __objc_resolve_instance_method (class, sel); | |
316 | ||
317 | if (res == 0) | |
0dd5327a | 318 | res = __objc_get_forward_imp (receiver, sel); |
b5a3450f RFM |
319 | } |
320 | } | |
321 | return res; | |
322 | } | |
323 | ||
bd74d88b | 324 | inline |
88e17b57 BE |
325 | IMP |
326 | get_imp (Class class, SEL sel) | |
327 | { | |
c19f8e35 NP |
328 | /* In a vanilla implementation we would first check if the dispatch |
329 | table is installed. Here instead, to get more speed in the | |
330 | standard case (that the dispatch table is installed) we first try | |
331 | to get the imp using brute force. Only if that fails, we do what | |
332 | we should have been doing from the very beginning, that is, check | |
333 | if the dispatch table needs to be installed, install it if it's | |
334 | not installed, and retrieve the imp from the table if it's | |
335 | installed. */ | |
40165636 | 336 | void *res = sarray_get_safe (class->dtable, (size_t) sel->sel_id); |
88e17b57 BE |
337 | if (res == 0) |
338 | { | |
b5a3450f | 339 | res = get_implementation(nil, class, sel); |
88e17b57 BE |
340 | } |
341 | return res; | |
342 | } | |
343 | ||
ad9eef11 NP |
344 | /* The new name of get_imp(). */ |
345 | IMP | |
346 | class_getMethodImplementation (Class class_, SEL selector) | |
347 | { | |
348 | if (class_ == Nil || selector == NULL) | |
349 | return NULL; | |
350 | ||
351 | /* get_imp is inlined, so we're good. */ | |
352 | return get_imp (class_, selector); | |
353 | } | |
354 | ||
fea78205 NP |
355 | /* Given a method, return its implementation. This has been replaced |
356 | by method_getImplementation() in the modern API. */ | |
cf3822f1 | 357 | IMP |
ad9eef11 | 358 | method_get_imp (struct objc_method * method) |
cf3822f1 | 359 | { |
ad9eef11 | 360 | return (method != (struct objc_method *)0) ? method->method_imp : (IMP)0; |
cf3822f1 NP |
361 | } |
362 | ||
88e17b57 | 363 | /* Query if an object can respond to a selector, returns YES if the |
b427203d | 364 | object implements the selector otherwise NO. Does not check if the |
0dd5327a NP |
365 | method can be forwarded. Since this requires the dispatch table to |
366 | installed, this function will implicitly invoke +initialize for the | |
367 | class of OBJECT if it hasn't been invoked yet. */ | |
435317e2 | 368 | inline |
88e17b57 BE |
369 | BOOL |
370 | __objc_responds_to (id object, SEL sel) | |
371 | { | |
40165636 | 372 | void *res; |
b5a3450f | 373 | struct sarray *dtable; |
88e17b57 | 374 | |
b5a3450f RFM |
375 | /* Install dispatch table if need be */ |
376 | dtable = object->class_pointer->dtable; | |
377 | if (dtable == __objc_uninstalled_dtable) | |
88e17b57 | 378 | { |
40165636 | 379 | objc_mutex_lock (__objc_runtime_mutex); |
c19f8e35 | 380 | if (object->class_pointer->dtable == __objc_uninstalled_dtable) |
b5a3450f RFM |
381 | __objc_install_dtable_for_class (object->class_pointer); |
382 | ||
0dd5327a NP |
383 | /* If the dispatch table is not yet installed, we are still in |
384 | the process of executing +initialize. Yet the dispatch table | |
385 | should be available. */ | |
b5a3450f RFM |
386 | if (object->class_pointer->dtable == __objc_uninstalled_dtable) |
387 | { | |
388 | dtable = __objc_prepared_dtable_for_class (object->class_pointer); | |
389 | assert (dtable); | |
390 | } | |
391 | else | |
392 | dtable = object->class_pointer->dtable; | |
393 | ||
40165636 | 394 | objc_mutex_unlock (__objc_runtime_mutex); |
88e17b57 BE |
395 | } |
396 | ||
b427203d | 397 | /* Get the method from the dispatch table. */ |
b5a3450f RFM |
398 | res = sarray_get_safe (dtable, (size_t) sel->sel_id); |
399 | return (res != 0) ? YES : NO; | |
88e17b57 BE |
400 | } |
401 | ||
ad9eef11 NP |
402 | BOOL |
403 | class_respondsToSelector (Class class_, SEL selector) | |
404 | { | |
b5a3450f | 405 | struct sarray *dtable; |
ad9eef11 NP |
406 | void *res; |
407 | ||
408 | if (class_ == Nil || selector == NULL) | |
409 | return NO; | |
410 | ||
b427203d | 411 | /* Install dispatch table if need be. */ |
b5a3450f RFM |
412 | dtable = class_->dtable; |
413 | if (dtable == __objc_uninstalled_dtable) | |
ad9eef11 NP |
414 | { |
415 | objc_mutex_lock (__objc_runtime_mutex); | |
416 | if (class_->dtable == __objc_uninstalled_dtable) | |
0dd5327a NP |
417 | __objc_install_dtable_for_class (class_); |
418 | ||
b5a3450f RFM |
419 | /* If the dispatch table is not yet installed, |
420 | we are still in the process of executing +initialize. | |
421 | Yet the dispatch table should be available. */ | |
422 | if (class_->dtable == __objc_uninstalled_dtable) | |
423 | { | |
424 | dtable = __objc_prepared_dtable_for_class (class_); | |
425 | assert (dtable); | |
426 | } | |
427 | else | |
428 | dtable = class_->dtable; | |
0dd5327a | 429 | |
ad9eef11 NP |
430 | objc_mutex_unlock (__objc_runtime_mutex); |
431 | } | |
432 | ||
b427203d | 433 | /* Get the method from the dispatch table. */ |
b5a3450f RFM |
434 | res = sarray_get_safe (dtable, (size_t) selector->sel_id); |
435 | return (res != 0) ? YES : NO; | |
ad9eef11 NP |
436 | } |
437 | ||
b427203d | 438 | /* This is the lookup function. All entries in the table are either a |
88e17b57 | 439 | valid method *or* zero. If zero then either the dispatch table |
b427203d NP |
440 | needs to be installed or it doesn't exist and forwarding is |
441 | attempted. */ | |
88e17b57 | 442 | IMP |
40165636 | 443 | objc_msg_lookup (id receiver, SEL op) |
88e17b57 BE |
444 | { |
445 | IMP result; | |
40165636 | 446 | if (receiver) |
88e17b57 | 447 | { |
b5a3450f | 448 | /* First try a quick lookup assuming the dispatch table exists. */ |
88e17b57 BE |
449 | result = sarray_get_safe (receiver->class_pointer->dtable, |
450 | (sidx)op->sel_id); | |
451 | if (result == 0) | |
452 | { | |
0dd5327a NP |
453 | /* Not found ... call get_implementation () to install the |
454 | dispatch table and call +initialize as required, | |
455 | providing the method implementation or a forwarding | |
456 | function. */ | |
b5a3450f | 457 | result = get_implementation (receiver, receiver->class_pointer, op); |
88e17b57 BE |
458 | } |
459 | return result; | |
460 | } | |
461 | else | |
faaa30fe | 462 | return (IMP)nil_method; |
88e17b57 BE |
463 | } |
464 | ||
465 | IMP | |
53f672ca | 466 | objc_msg_lookup_super (struct objc_super *super, SEL sel) |
88e17b57 BE |
467 | { |
468 | if (super->self) | |
114dae43 | 469 | return get_imp (super->super_class, sel); |
88e17b57 | 470 | else |
faaa30fe | 471 | return (IMP)nil_method; |
88e17b57 BE |
472 | } |
473 | ||
88e17b57 | 474 | void |
40165636 | 475 | __objc_init_dispatch_tables () |
88e17b57 | 476 | { |
40165636 | 477 | __objc_uninstalled_dtable = sarray_new (200, 0); |
e97cfd97 NP |
478 | |
479 | /* TODO: It would be cool to register typed selectors here. */ | |
114dae43 | 480 | selector_resolveClassMethod = sel_registerName ("resolveClassMethod:"); |
0dd5327a | 481 | selector_resolveInstanceMethod = sel_registerName ("resolveInstanceMethod:"); |
88e17b57 BE |
482 | } |
483 | ||
88e17b57 BE |
484 | |
485 | /* Install dummy table for class which causes the first message to | |
b427203d | 486 | that class (or instances hereof) to be initialized properly. */ |
88e17b57 | 487 | void |
40165636 | 488 | __objc_install_premature_dtable (Class class) |
88e17b57 | 489 | { |
40165636 | 490 | assert (__objc_uninstalled_dtable); |
88e17b57 BE |
491 | class->dtable = __objc_uninstalled_dtable; |
492 | } | |
493 | ||
b427203d | 494 | /* Send +initialize to class if not already done. */ |
88e17b57 | 495 | static void |
40165636 | 496 | __objc_send_initialize (Class class) |
88e17b57 | 497 | { |
b427203d | 498 | /* This *must* be a class object. */ |
40165636 RB |
499 | assert (CLS_ISCLASS (class)); |
500 | assert (! CLS_ISMETA (class)); | |
88e17b57 | 501 | |
0dd5327a NP |
502 | /* class_add_method_list/__objc_update_dispatch_table_for_class may |
503 | have reset the dispatch table. The canonical way to insure that | |
504 | we send +initialize just once, is this flag. */ | |
40165636 | 505 | if (! CLS_ISINITIALIZED (class)) |
88e17b57 | 506 | { |
e99776d8 | 507 | DEBUG_PRINTF ("+initialize: need to initialize class '%s'\n", class->name); |
40165636 RB |
508 | CLS_SETINITIALIZED (class); |
509 | CLS_SETINITIALIZED (class->class_pointer); | |
88e17b57 | 510 | |
b427203d | 511 | /* Create the garbage collector type memory description. */ |
88e17b57 BE |
512 | __objc_generate_gc_type_description (class); |
513 | ||
40165636 RB |
514 | if (class->super_class) |
515 | __objc_send_initialize (class->super_class); | |
88e17b57 BE |
516 | |
517 | { | |
114dae43 | 518 | SEL op = sel_registerName ("initialize"); |
b427203d | 519 | IMP imp = 0; |
ad9eef11 | 520 | struct objc_method_list * method_list = class->class_pointer->methods; |
b427203d NP |
521 | |
522 | while (method_list) | |
523 | { | |
524 | int i; | |
525 | struct objc_method * method; | |
526 | ||
527 | for (i = 0; i < method_list->method_count; i++) | |
528 | { | |
529 | method = &(method_list->method_list[i]); | |
530 | if (method->method_name | |
531 | && method->method_name->sel_id == op->sel_id) | |
532 | { | |
533 | imp = method->method_imp; | |
534 | break; | |
535 | } | |
536 | } | |
537 | ||
538 | if (imp) | |
539 | break; | |
540 | ||
541 | method_list = method_list->method_next; | |
542 | } | |
88e17b57 | 543 | if (imp) |
e99776d8 NP |
544 | { |
545 | DEBUG_PRINTF (" begin of [%s +initialize]\n", class->name); | |
546 | (*imp) ((id) class, op); | |
547 | DEBUG_PRINTF (" end of [%s +initialize]\n", class->name); | |
548 | } | |
549 | #ifdef DEBUG | |
550 | else | |
551 | { | |
552 | DEBUG_PRINTF (" class '%s' has no +initialize method\n", class->name); | |
553 | } | |
554 | #endif | |
88e17b57 BE |
555 | } |
556 | } | |
557 | } | |
558 | ||
b427203d NP |
559 | /* Walk on the methods list of class and install the methods in the |
560 | reverse order of the lists. Since methods added by categories are | |
561 | before the methods of class in the methods list, this allows | |
562 | categories to substitute methods declared in class. However if | |
563 | more than one category replaces the same method nothing is | |
564 | guaranteed about what method will be used. Assumes that | |
565 | __objc_runtime_mutex is locked down. */ | |
88e17b57 | 566 | static void |
b5a3450f | 567 | __objc_install_methods_in_dtable (struct sarray *dtable, struct objc_method_list * method_list) |
88e17b57 BE |
568 | { |
569 | int i; | |
b427203d | 570 | |
40165636 | 571 | if (! method_list) |
88e17b57 | 572 | return; |
b427203d | 573 | |
88e17b57 | 574 | if (method_list->method_next) |
b5a3450f | 575 | __objc_install_methods_in_dtable (dtable, method_list->method_next); |
b427203d | 576 | |
88e17b57 BE |
577 | for (i = 0; i < method_list->method_count; i++) |
578 | { | |
ad9eef11 | 579 | struct objc_method * method = &(method_list->method_list[i]); |
b5a3450f | 580 | sarray_at_put_safe (dtable, |
88e17b57 BE |
581 | (sidx) method->method_name->sel_id, |
582 | method->method_imp); | |
583 | } | |
584 | } | |
585 | ||
88e17b57 BE |
586 | void |
587 | __objc_update_dispatch_table_for_class (Class class) | |
588 | { | |
589 | Class next; | |
590 | struct sarray *arr; | |
591 | ||
b5a3450f | 592 | DEBUG_PRINTF (" _objc_update_dtable_for_class (%s)\n", class->name); |
e99776d8 | 593 | |
40165636 | 594 | objc_mutex_lock (__objc_runtime_mutex); |
88e17b57 | 595 | |
0dd5327a | 596 | /* Not yet installed -- skip it unless in +initialize. */ |
b5a3450f RFM |
597 | if (class->dtable == __objc_uninstalled_dtable) |
598 | { | |
599 | if (__objc_prepared_dtable_for_class (class)) | |
600 | { | |
601 | /* There is a prepared table so we must be initialising this | |
0dd5327a | 602 | class ... we must re-do the table preparation. */ |
b5a3450f RFM |
603 | __objc_prepare_dtable_for_class (class); |
604 | } | |
605 | objc_mutex_unlock (__objc_runtime_mutex); | |
606 | return; | |
607 | } | |
608 | ||
88e17b57 BE |
609 | arr = class->dtable; |
610 | __objc_install_premature_dtable (class); /* someone might require it... */ | |
611 | sarray_free (arr); /* release memory */ | |
b427203d NP |
612 | |
613 | /* Could have been lazy... */ | |
b5a3450f | 614 | __objc_install_dtable_for_class (class); |
88e17b57 | 615 | |
b427203d | 616 | if (class->subclass_list) /* Traverse subclasses. */ |
88e17b57 BE |
617 | for (next = class->subclass_list; next; next = next->sibling_class) |
618 | __objc_update_dispatch_table_for_class (next); | |
619 | ||
40165636 | 620 | objc_mutex_unlock (__objc_runtime_mutex); |
88e17b57 BE |
621 | } |
622 | ||
88e17b57 BE |
623 | /* This function adds a method list to a class. This function is |
624 | typically called by another function specific to the run-time. As | |
625 | such this function does not worry about thread safe issues. | |
626 | ||
627 | This one is only called for categories. Class objects have their | |
628 | methods installed right away, and their selectors are made into | |
b427203d | 629 | SEL's by the function __objc_register_selectors_from_class. */ |
88e17b57 | 630 | void |
ad9eef11 | 631 | class_add_method_list (Class class, struct objc_method_list * list) |
88e17b57 | 632 | { |
88e17b57 | 633 | /* Passing of a linked list is not allowed. Do multiple calls. */ |
40165636 | 634 | assert (! list->method_next); |
88e17b57 | 635 | |
435317e2 | 636 | __objc_register_selectors_from_list(list); |
88e17b57 BE |
637 | |
638 | /* Add the methods to the class's method list. */ | |
639 | list->method_next = class->methods; | |
640 | class->methods = list; | |
641 | ||
b427203d | 642 | /* Update the dispatch table of class. */ |
88e17b57 BE |
643 | __objc_update_dispatch_table_for_class (class); |
644 | } | |
645 | ||
ad9eef11 NP |
646 | struct objc_method * |
647 | class_getInstanceMethod (Class class_, SEL selector) | |
648 | { | |
e97cfd97 NP |
649 | struct objc_method *m; |
650 | ||
ad9eef11 NP |
651 | if (class_ == Nil || selector == NULL) |
652 | return NULL; | |
653 | ||
e97cfd97 NP |
654 | m = search_for_method_in_hierarchy (class_, selector); |
655 | if (m) | |
656 | return m; | |
657 | ||
b427203d NP |
658 | /* Try going through +resolveInstanceMethod:, and do the search |
659 | again if successful. */ | |
e97cfd97 NP |
660 | if (__objc_resolve_instance_method (class_, selector)) |
661 | return search_for_method_in_hierarchy (class_, selector); | |
662 | ||
663 | return NULL; | |
ad9eef11 NP |
664 | } |
665 | ||
666 | struct objc_method * | |
667 | class_getClassMethod (Class class_, SEL selector) | |
668 | { | |
e97cfd97 NP |
669 | struct objc_method *m; |
670 | ||
ad9eef11 NP |
671 | if (class_ == Nil || selector == NULL) |
672 | return NULL; | |
673 | ||
e97cfd97 NP |
674 | m = search_for_method_in_hierarchy (class_->class_pointer, |
675 | selector); | |
676 | if (m) | |
677 | return m; | |
678 | ||
679 | /* Try going through +resolveClassMethod:, and do the search again | |
680 | if successful. */ | |
681 | if (__objc_resolve_class_method (class_, selector)) | |
682 | return search_for_method_in_hierarchy (class_->class_pointer, | |
683 | selector); | |
684 | ||
685 | return NULL; | |
ad9eef11 | 686 | } |
88e17b57 | 687 | |
6c5c7efd NP |
688 | BOOL |
689 | class_addMethod (Class class_, SEL selector, IMP implementation, | |
690 | const char *method_types) | |
691 | { | |
692 | struct objc_method_list *method_list; | |
693 | struct objc_method *method; | |
694 | const char *method_name; | |
695 | ||
696 | if (class_ == Nil || selector == NULL || implementation == NULL | |
697 | || method_types == NULL || (strcmp (method_types, "") == 0)) | |
698 | return NO; | |
699 | ||
114dae43 | 700 | method_name = sel_getName (selector); |
6c5c7efd NP |
701 | if (method_name == NULL) |
702 | return NO; | |
703 | ||
82883986 NP |
704 | /* If the method already exists in the class, return NO. It is fine |
705 | if the method already exists in the superclass; in that case, we | |
706 | are overriding it. */ | |
707 | if (CLS_IS_IN_CONSTRUCTION (class_)) | |
708 | { | |
709 | /* The class only contains a list of methods; they have not been | |
710 | registered yet, ie, the method_name of each of them is still | |
711 | a string, not a selector. Iterate manually over them to | |
712 | check if we have already added the method. */ | |
713 | struct objc_method_list * method_list = class_->methods; | |
714 | while (method_list) | |
715 | { | |
716 | int i; | |
717 | ||
718 | /* Search the method list. */ | |
719 | for (i = 0; i < method_list->method_count; ++i) | |
720 | { | |
721 | struct objc_method * method = &method_list->method_list[i]; | |
722 | ||
723 | if (method->method_name | |
724 | && strcmp ((char *)method->method_name, method_name) == 0) | |
725 | return NO; | |
726 | } | |
727 | ||
728 | /* The method wasn't found. Follow the link to the next list of | |
729 | methods. */ | |
730 | method_list = method_list->method_next; | |
731 | } | |
732 | /* The method wasn't found. It's a new one. Go ahead and add | |
733 | it. */ | |
734 | } | |
735 | else | |
736 | { | |
737 | /* Do the standard lookup. This assumes the selectors are | |
738 | mapped. */ | |
739 | if (search_for_method_in_list (class_->methods, selector)) | |
740 | return NO; | |
741 | } | |
742 | ||
6c5c7efd NP |
743 | method_list = (struct objc_method_list *)objc_calloc (1, sizeof (struct objc_method_list)); |
744 | method_list->method_count = 1; | |
745 | ||
746 | method = &(method_list->method_list[0]); | |
747 | method->method_name = objc_malloc (strlen (method_name) + 1); | |
748 | strcpy ((char *)method->method_name, method_name); | |
749 | ||
750 | method->method_types = objc_malloc (strlen (method_types) + 1); | |
751 | strcpy ((char *)method->method_types, method_types); | |
752 | ||
753 | method->method_imp = implementation; | |
754 | ||
755 | if (CLS_IS_IN_CONSTRUCTION (class_)) | |
756 | { | |
757 | /* We only need to add the method to the list. It will be | |
758 | registered with the runtime when the class pair is registered | |
759 | (if ever). */ | |
760 | method_list->method_next = class_->methods; | |
761 | class_->methods = method_list; | |
762 | } | |
763 | else | |
764 | { | |
765 | /* Add the method to a live class. */ | |
766 | objc_mutex_lock (__objc_runtime_mutex); | |
767 | class_add_method_list (class_, method_list); | |
768 | objc_mutex_unlock (__objc_runtime_mutex); | |
769 | } | |
770 | ||
771 | return YES; | |
772 | } | |
773 | ||
6c5c7efd NP |
774 | IMP |
775 | class_replaceMethod (Class class_, SEL selector, IMP implementation, | |
776 | const char *method_types) | |
777 | { | |
778 | struct objc_method * method; | |
779 | ||
780 | if (class_ == Nil || selector == NULL || implementation == NULL | |
781 | || method_types == NULL) | |
782 | return NULL; | |
783 | ||
784 | method = search_for_method_in_hierarchy (class_, selector); | |
785 | ||
786 | if (method) | |
787 | { | |
788 | return method_setImplementation (method, implementation); | |
789 | } | |
790 | else | |
791 | { | |
792 | class_addMethod (class_, selector, implementation, method_types); | |
793 | return NULL; | |
794 | } | |
795 | } | |
796 | ||
b427203d NP |
797 | /* Search for a method starting from the current class up its |
798 | hierarchy. Return a pointer to the method's method structure if | |
799 | found. NULL otherwise. */ | |
ad9eef11 | 800 | static struct objc_method * |
88e17b57 BE |
801 | search_for_method_in_hierarchy (Class cls, SEL sel) |
802 | { | |
ad9eef11 | 803 | struct objc_method * method = NULL; |
88e17b57 BE |
804 | Class class; |
805 | ||
806 | if (! sel_is_mapped (sel)) | |
807 | return NULL; | |
808 | ||
b427203d NP |
809 | /* Scan the method list of the class. If the method isn't found in |
810 | the list then step to its super class. */ | |
88e17b57 BE |
811 | for (class = cls; ((! method) && class); class = class->super_class) |
812 | method = search_for_method_in_list (class->methods, sel); | |
813 | ||
814 | return method; | |
815 | } | |
816 | ||
817 | ||
818 | ||
b427203d NP |
819 | /* Given a linked list of method and a method's name. Search for the |
820 | named method's method structure. Return a pointer to the method's | |
821 | method structure if found. NULL otherwise. */ | |
ad9eef11 NP |
822 | struct objc_method * |
823 | search_for_method_in_list (struct objc_method_list * list, SEL op) | |
88e17b57 | 824 | { |
ad9eef11 | 825 | struct objc_method_list * method_list = list; |
88e17b57 BE |
826 | |
827 | if (! sel_is_mapped (op)) | |
828 | return NULL; | |
829 | ||
830 | /* If not found then we'll search the list. */ | |
831 | while (method_list) | |
832 | { | |
833 | int i; | |
834 | ||
835 | /* Search the method list. */ | |
836 | for (i = 0; i < method_list->method_count; ++i) | |
837 | { | |
ad9eef11 | 838 | struct objc_method * method = &method_list->method_list[i]; |
88e17b57 BE |
839 | |
840 | if (method->method_name) | |
841 | if (method->method_name->sel_id == op->sel_id) | |
842 | return method; | |
843 | } | |
844 | ||
845 | /* The method wasn't found. Follow the link to the next list of | |
846 | methods. */ | |
847 | method_list = method_list->method_next; | |
848 | } | |
849 | ||
850 | return NULL; | |
851 | } | |
852 | ||
80e4b9e5 NP |
853 | typedef void * retval_t; |
854 | typedef void * arglist_t; | |
855 | ||
88e17b57 BE |
856 | static retval_t __objc_forward (id object, SEL sel, arglist_t args); |
857 | ||
b427203d | 858 | /* Forwarding pointers/integers through the normal registers. */ |
88e17b57 BE |
859 | static id |
860 | __objc_word_forward (id rcv, SEL op, ...) | |
861 | { | |
862 | void *args, *res; | |
863 | ||
864 | args = __builtin_apply_args (); | |
865 | res = __objc_forward (rcv, op, args); | |
866 | if (res) | |
867 | __builtin_return (res); | |
868 | else | |
869 | return res; | |
870 | } | |
871 | ||
872 | /* Specific routine for forwarding floats/double because of | |
b427203d NP |
873 | architectural differences on some processors. i386s for example |
874 | which uses a floating point stack versus general registers for | |
875 | floating point numbers. This forward routine makes sure that GCC | |
876 | restores the proper return values. */ | |
88e17b57 BE |
877 | static double |
878 | __objc_double_forward (id rcv, SEL op, ...) | |
879 | { | |
880 | void *args, *res; | |
881 | ||
882 | args = __builtin_apply_args (); | |
883 | res = __objc_forward (rcv, op, args); | |
884 | __builtin_return (res); | |
885 | } | |
886 | ||
887 | #if INVISIBLE_STRUCT_RETURN | |
888 | static __big | |
889 | #else | |
890 | static id | |
891 | #endif | |
892 | __objc_block_forward (id rcv, SEL op, ...) | |
893 | { | |
894 | void *args, *res; | |
895 | ||
896 | args = __builtin_apply_args (); | |
897 | res = __objc_forward (rcv, op, args); | |
898 | if (res) | |
899 | __builtin_return (res); | |
900 | else | |
901 | #if INVISIBLE_STRUCT_RETURN | |
902 | return (__big) {{0, 0, 0, 0, 0, 0, 0, 0}}; | |
903 | #else | |
904 | return nil; | |
905 | #endif | |
906 | } | |
907 | ||
908 | ||
4236b2e5 NP |
909 | /* This function is called for methods which are not implemented, |
910 | unless a custom forwarding routine has been installed. Please note | |
911 | that most serious users of libobjc (eg, GNUstep base) do install | |
912 | their own forwarding routines, and hence this is never actually | |
913 | used. But, if no custom forwarding routine is installed, this is | |
914 | called when a selector is not recognized. */ | |
88e17b57 BE |
915 | static retval_t |
916 | __objc_forward (id object, SEL sel, arglist_t args) | |
917 | { | |
918 | IMP imp; | |
919 | static SEL frwd_sel = 0; /* !T:SAFE2 */ | |
920 | SEL err_sel; | |
921 | ||
b427203d | 922 | /* First try if the object understands forward::. */ |
40165636 RB |
923 | if (! frwd_sel) |
924 | frwd_sel = sel_get_any_uid ("forward::"); | |
88e17b57 BE |
925 | |
926 | if (__objc_responds_to (object, frwd_sel)) | |
927 | { | |
b5a3450f | 928 | imp = get_implementation (object, object->class_pointer, frwd_sel); |
40165636 | 929 | return (*imp) (object, frwd_sel, sel, args); |
88e17b57 BE |
930 | } |
931 | ||
b427203d NP |
932 | /* If the object recognizes the doesNotRecognize: method then we're |
933 | going to send it. */ | |
88e17b57 BE |
934 | err_sel = sel_get_any_uid ("doesNotRecognize:"); |
935 | if (__objc_responds_to (object, err_sel)) | |
936 | { | |
b5a3450f | 937 | imp = get_implementation (object, object->class_pointer, err_sel); |
88e17b57 BE |
938 | return (*imp) (object, err_sel, sel); |
939 | } | |
940 | ||
941 | /* The object doesn't recognize the method. Check for responding to | |
b427203d | 942 | error:. If it does then sent it. */ |
88e17b57 | 943 | { |
114dae43 | 944 | char msg[256 + strlen ((const char *) sel_getName (sel)) |
40165636 | 945 | + strlen ((const char *) object->class_pointer->name)]; |
88e17b57 BE |
946 | |
947 | sprintf (msg, "(%s) %s does not recognize %s", | |
40165636 | 948 | (CLS_ISMETA (object->class_pointer) |
88e17b57 BE |
949 | ? "class" |
950 | : "instance" ), | |
114dae43 | 951 | object->class_pointer->name, sel_getName (sel)); |
88e17b57 | 952 | |
e79132c3 NP |
953 | /* The object doesn't respond to doesNotRecognize:. Therefore, a |
954 | default action is taken. */ | |
7b869986 | 955 | _objc_abort ("%s\n", msg); |
88e17b57 BE |
956 | |
957 | return 0; | |
958 | } | |
959 | } | |
960 | ||
961 | void | |
d9df3365 | 962 | __objc_print_dtable_stats (void) |
88e17b57 BE |
963 | { |
964 | int total = 0; | |
965 | ||
40165636 | 966 | objc_mutex_lock (__objc_runtime_mutex); |
88e17b57 | 967 | |
88e17b57 | 968 | #ifdef OBJC_SPARSE2 |
40165636 | 969 | printf ("memory usage: (%s)\n", "2-level sparse arrays"); |
88e17b57 | 970 | #else |
40165636 | 971 | printf ("memory usage: (%s)\n", "3-level sparse arrays"); |
88e17b57 | 972 | #endif |
88e17b57 | 973 | |
40165636 | 974 | printf ("arrays: %d = %ld bytes\n", narrays, |
b15b7ef8 | 975 | (long) ((size_t) narrays * sizeof (struct sarray))); |
40165636 RB |
976 | total += narrays * sizeof (struct sarray); |
977 | printf ("buckets: %d = %ld bytes\n", nbuckets, | |
b15b7ef8 | 978 | (long) ((size_t) nbuckets * sizeof (struct sbucket))); |
40165636 RB |
979 | total += nbuckets * sizeof (struct sbucket); |
980 | ||
981 | printf ("idxtables: %d = %ld bytes\n", | |
b15b7ef8 | 982 | idxsize, (long) ((size_t) idxsize * sizeof (void *))); |
40165636 RB |
983 | total += idxsize * sizeof (void *); |
984 | printf ("-----------------------------------\n"); | |
985 | printf ("total: %d bytes\n", total); | |
986 | printf ("===================================\n"); | |
987 | ||
988 | objc_mutex_unlock (__objc_runtime_mutex); | |
88e17b57 BE |
989 | } |
990 | ||
b5a3450f RFM |
991 | static cache_ptr prepared_dtable_table = 0; |
992 | ||
0dd5327a NP |
993 | /* This function is called by: objc_msg_lookup, get_imp and |
994 | __objc_responds_to (and the dispatch table installation functions | |
995 | themselves) to install a dispatch table for a class. | |
b5a3450f RFM |
996 | |
997 | If CLS is a class, it installs instance methods. | |
998 | If CLS is a meta class, it installs class methods. | |
999 | ||
1000 | In either case +initialize is invoked for the corresponding class. | |
1001 | ||
1002 | The implementation must insure that the dispatch table is not | |
1003 | installed until +initialize completes. Otherwise it opens a | |
0dd5327a NP |
1004 | potential race since the installation of the dispatch table is used |
1005 | as gate in regular method dispatch and we need to guarantee that | |
1006 | +initialize is the first method invoked an that no other thread my | |
1007 | dispatch messages to the class before +initialize completes. */ | |
b5a3450f RFM |
1008 | static void |
1009 | __objc_install_dtable_for_class (Class cls) | |
1010 | { | |
0dd5327a NP |
1011 | /* If the class has not yet had its class links resolved, we must |
1012 | re-compute all class links. */ | |
b5a3450f RFM |
1013 | if (! CLS_ISRESOLV (cls)) |
1014 | __objc_resolve_class_links (); | |
1015 | ||
0dd5327a NP |
1016 | /* Make sure the super class has its dispatch table installed or is |
1017 | at least preparing. We do not need to send initialize for the | |
1018 | super class since __objc_send_initialize will insure that. */ | |
b5a3450f | 1019 | if (cls->super_class |
0dd5327a NP |
1020 | && cls->super_class->dtable == __objc_uninstalled_dtable |
1021 | && !__objc_prepared_dtable_for_class (cls->super_class)) | |
b5a3450f RFM |
1022 | { |
1023 | __objc_install_dtable_for_class (cls->super_class); | |
1024 | /* The superclass initialisation may have also initialised the | |
0dd5327a | 1025 | current class, in which case there is no more to do. */ |
b5a3450f | 1026 | if (cls->dtable != __objc_uninstalled_dtable) |
0dd5327a | 1027 | return; |
b5a3450f RFM |
1028 | } |
1029 | ||
1030 | /* We have already been prepared but +initialize hasn't completed. | |
0dd5327a NP |
1031 | The +initialize implementation is probably sending 'self' |
1032 | messages. We rely on _objc_get_prepared_imp to retrieve the | |
1033 | implementation pointers. */ | |
b5a3450f | 1034 | if (__objc_prepared_dtable_for_class (cls)) |
0dd5327a | 1035 | return; |
b5a3450f | 1036 | |
0dd5327a NP |
1037 | /* We have this function cache the implementation pointers for |
1038 | _objc_get_prepared_imp but the dispatch table won't be initilized | |
1039 | until __objc_send_initialize completes. */ | |
b5a3450f RFM |
1040 | __objc_prepare_dtable_for_class (cls); |
1041 | ||
0dd5327a NP |
1042 | /* We may have already invoked +initialize but |
1043 | __objc_update_dispatch_table_for_class invoked by | |
b5a3450f RFM |
1044 | class_add_method_list may have reset dispatch table. */ |
1045 | ||
0dd5327a NP |
1046 | /* Call +initialize. If we are a real class, we are installing |
1047 | instance methods. If we are a meta class, we are installing | |
1048 | class methods. The __objc_send_initialize itself will insure | |
1049 | that the message is called only once per class. */ | |
b5a3450f RFM |
1050 | if (CLS_ISCLASS (cls)) |
1051 | __objc_send_initialize (cls); | |
1052 | else | |
1053 | { | |
0dd5327a | 1054 | /* Retrieve the class from the meta class. */ |
1e49849c | 1055 | Class c = objc_getClass (cls->name); |
b5a3450f RFM |
1056 | assert (CLS_ISMETA (cls)); |
1057 | assert (c); | |
1058 | __objc_send_initialize (c); | |
1059 | } | |
1060 | ||
1061 | /* We install the dispatch table correctly when +initialize completed. */ | |
1062 | __objc_install_prepared_dtable_for_class (cls); | |
1063 | } | |
1064 | ||
0dd5327a NP |
1065 | /* Builds the dispatch table for the class CLS and stores it in a |
1066 | place where it can be retrieved by __objc_get_prepared_imp until | |
1067 | __objc_install_prepared_dtable_for_class installs it into the | |
1068 | class. The dispatch table should not be installed into the class | |
1069 | until +initialize has completed. */ | |
b5a3450f RFM |
1070 | static void |
1071 | __objc_prepare_dtable_for_class (Class cls) | |
1072 | { | |
1073 | struct sarray *dtable; | |
1074 | struct sarray *super_dtable; | |
1075 | ||
0dd5327a NP |
1076 | /* This table could be initialized in init.c. We can not use the |
1077 | class name since the class maintains the instance methods and the | |
1078 | meta class maintains the the class methods yet both share the | |
1079 | same name. Classes should be unique in any program. */ | |
b5a3450f RFM |
1080 | if (! prepared_dtable_table) |
1081 | prepared_dtable_table | |
0dd5327a NP |
1082 | = objc_hash_new (32, |
1083 | (hash_func_type) objc_hash_ptr, | |
1084 | (compare_func_type) objc_compare_ptrs); | |
1085 | ||
1086 | /* If the class has not yet had its class links resolved, we must | |
1087 | re-compute all class links. */ | |
b5a3450f RFM |
1088 | if (! CLS_ISRESOLV (cls)) |
1089 | __objc_resolve_class_links (); | |
1090 | ||
1091 | assert (cls); | |
1092 | assert (cls->dtable == __objc_uninstalled_dtable); | |
1093 | ||
0dd5327a NP |
1094 | /* If there is already a prepared dtable for this class, we must |
1095 | replace it with a new version (since there must have been methods | |
1096 | added to or otherwise modified in the class while executing | |
1097 | +initialize, and the table needs to be recomputed. */ | |
b5a3450f | 1098 | dtable = __objc_prepared_dtable_for_class (cls); |
0dd5327a | 1099 | if (dtable != 0) |
b5a3450f RFM |
1100 | { |
1101 | objc_hash_remove (prepared_dtable_table, cls); | |
1102 | sarray_free (dtable); | |
1103 | } | |
1104 | ||
1105 | /* Now prepare the dtable for population. */ | |
1106 | assert (cls != cls->super_class); | |
1107 | if (cls->super_class) | |
1108 | { | |
0dd5327a NP |
1109 | /* Inherit the method list from the super class. Yet the super |
1110 | class may still be initializing in the case when a class | |
1111 | cluster sub class initializes its super classes. */ | |
b5a3450f RFM |
1112 | if (cls->super_class->dtable == __objc_uninstalled_dtable) |
1113 | __objc_install_dtable_for_class (cls->super_class); | |
1114 | ||
1115 | super_dtable = cls->super_class->dtable; | |
0dd5327a NP |
1116 | /* If the dispatch table is not yet installed, we are still in |
1117 | the process of executing +initialize. Yet the dispatch table | |
1118 | should be available. */ | |
b5a3450f RFM |
1119 | if (super_dtable == __objc_uninstalled_dtable) |
1120 | super_dtable = __objc_prepared_dtable_for_class (cls->super_class); | |
1121 | ||
1122 | assert (super_dtable); | |
1123 | dtable = sarray_lazy_copy (super_dtable); | |
1124 | } | |
1125 | else | |
1126 | dtable = sarray_new (__objc_selector_max_index, 0); | |
1127 | ||
1128 | __objc_install_methods_in_dtable (dtable, cls->methods); | |
1129 | ||
1130 | objc_hash_add (&prepared_dtable_table, | |
1131 | cls, | |
1132 | dtable); | |
1133 | } | |
1134 | ||
0dd5327a NP |
1135 | /* This wrapper only exists to allow an easy replacement of the lookup |
1136 | implementation and it is expected that the compiler will optimize | |
1137 | it away. */ | |
b5a3450f RFM |
1138 | static struct sarray * |
1139 | __objc_prepared_dtable_for_class (Class cls) | |
1140 | { | |
1141 | struct sarray *dtable = 0; | |
1142 | assert (cls); | |
1143 | if (prepared_dtable_table) | |
1144 | dtable = objc_hash_value_for_key (prepared_dtable_table, cls); | |
0dd5327a NP |
1145 | /* dtable my be nil, since we call this to check whether we are |
1146 | currently preparing before we start preparing. */ | |
b5a3450f RFM |
1147 | return dtable; |
1148 | } | |
1149 | ||
1150 | /* Helper function for messages sent to CLS or implementation pointers | |
0dd5327a NP |
1151 | retrieved from CLS during +initialize before the dtable is |
1152 | installed. When a class implicitly initializes another class which | |
1153 | in turn implicitly invokes methods in this class, before the | |
1154 | implementation of +initialize of CLS completes, this returns the | |
1155 | expected implementation. Forwarding remains the responsibility of | |
1156 | objc_msg_lookup. This function should only be called under the | |
1157 | global lock. */ | |
b5a3450f RFM |
1158 | static IMP |
1159 | __objc_get_prepared_imp (Class cls,SEL sel) | |
1160 | { | |
1161 | struct sarray *dtable; | |
1162 | IMP imp; | |
1163 | ||
1164 | assert (cls); | |
1165 | assert (sel); | |
1166 | assert (cls->dtable == __objc_uninstalled_dtable); | |
1167 | dtable = __objc_prepared_dtable_for_class (cls); | |
1168 | ||
1169 | assert (dtable); | |
1170 | assert (dtable != __objc_uninstalled_dtable); | |
1171 | imp = sarray_get_safe (dtable, (size_t) sel->sel_id); | |
1172 | ||
0dd5327a NP |
1173 | /* imp may be Nil if the method does not exist and we may fallback |
1174 | to the forwarding implementation later. */ | |
b5a3450f RFM |
1175 | return imp; |
1176 | } | |
1177 | ||
0dd5327a NP |
1178 | /* When this function is called +initialize should be completed. So |
1179 | now we are safe to install the dispatch table for the class so that | |
1180 | they become available for other threads that may be waiting in the | |
1181 | lock. */ | |
b5a3450f RFM |
1182 | static void |
1183 | __objc_install_prepared_dtable_for_class (Class cls) | |
1184 | { | |
1185 | assert (cls); | |
1186 | assert (cls->dtable == __objc_uninstalled_dtable); | |
1187 | cls->dtable = __objc_prepared_dtable_for_class (cls); | |
1188 | ||
1189 | assert (cls->dtable); | |
1190 | assert (cls->dtable != __objc_uninstalled_dtable); | |
1191 | objc_hash_remove (prepared_dtable_table, cls); | |
1192 | } |