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