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