]>
Commit | Line | Data |
---|---|---|
88e17b57 | 1 | /* GNU Objective C Runtime message lookup |
0fc39d8a | 2 | Copyright (C) 1993, 1995, 1996, 1997, 1998, |
748086b7 | 3 | 2001, 2002, 2004, 2009 Free Software Foundation, Inc. |
88e17b57 BE |
4 | Contributed by Kresten Krab Thorup |
5 | ||
38709cad | 6 | This file is part of GCC. |
88e17b57 | 7 | |
38709cad | 8 | GCC is free software; you can redistribute it and/or modify it under the |
88e17b57 | 9 | terms of the GNU General Public License as published by the Free Software |
748086b7 | 10 | Foundation; either version 3, or (at your option) any later version. |
88e17b57 | 11 | |
38709cad | 12 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
88e17b57 BE |
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
14 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | |
15 | details. | |
16 | ||
748086b7 JJ |
17 | Under Section 7 of GPL version 3, you are granted additional |
18 | permissions described in the GCC Runtime Library Exception, version | |
19 | 3.1, as published by the Free Software Foundation. | |
20 | ||
21 | You should have received a copy of the GNU General Public License and | |
22 | a copy of the GCC Runtime Library Exception along with this program; | |
23 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
24 | <http://www.gnu.org/licenses/>. */ | |
88e17b57 | 25 | |
88e17b57 | 26 | |
4977bab6 | 27 | /* FIXME: This file has no business including tm.h. */ |
435317e2 AP |
28 | /* FIXME: This should be using libffi instead of __builtin_apply |
29 | and friends. */ | |
4977bab6 | 30 | |
6dead247 | 31 | #include "objc-private/common.h" |
7b869986 | 32 | #include "objc-private/error.h" |
bce1b489 | 33 | #include "tconfig.h" |
4977bab6 ZW |
34 | #include "coretypes.h" |
35 | #include "tm.h" | |
a19fac96 NP |
36 | #include "objc/objc.h" |
37 | #include "objc/objc-api.h" | |
38 | #include "objc/thr.h" | |
a19fac96 | 39 | #include "objc-private/runtime.h" |
5d3b14bd | 40 | #include "objc-private/sarray.h" |
348a3445 | 41 | #include "objc/encoding.h" |
88e17b57 | 42 | #include "runtime-info.h" |
5d3b14bd | 43 | #include <assert.h> /* For assert */ |
5be9cdc1 | 44 | #include <string.h> /* For strlen */ |
88e17b57 | 45 | |
435317e2 | 46 | /* This is how we hack STRUCT_VALUE to be 1 or 0. */ |
88e17b57 BE |
47 | #define gen_rtx(args...) 1 |
48 | #define gen_rtx_MEM(args...) 1 | |
68b61df9 | 49 | #define gen_rtx_REG(args...) 1 |
c24aadf3 KT |
50 | /* Alread defined in gcc/coretypes.h. So prevent double definition warning. */ |
51 | #undef rtx | |
88e17b57 BE |
52 | #define rtx int |
53 | ||
40165636 | 54 | #if ! defined (STRUCT_VALUE) || STRUCT_VALUE == 0 |
88e17b57 BE |
55 | #define INVISIBLE_STRUCT_RETURN 1 |
56 | #else | |
57 | #define INVISIBLE_STRUCT_RETURN 0 | |
58 | #endif | |
59 | ||
60 | /* The uninstalled dispatch table */ | |
40165636 | 61 | struct sarray *__objc_uninstalled_dtable = 0; /* !T:MUTEX */ |
88e17b57 | 62 | |
80ae8e8a AR |
63 | /* Two hooks for method forwarding. If either is set, it is invoked |
64 | * to return a function that performs the real forwarding. If both | |
65 | * are set, the result of __objc_msg_forward2 will be preferred over | |
66 | * that of __objc_msg_forward. If both return NULL or are unset, | |
67 | * the libgcc based functions (__builtin_apply and friends) are | |
68 | * used. | |
69 | */ | |
40165636 | 70 | IMP (*__objc_msg_forward) (SEL) = NULL; |
80ae8e8a | 71 | IMP (*__objc_msg_forward2) (id, SEL) = NULL; |
68b61df9 | 72 | |
88e17b57 | 73 | /* Send +initialize to class */ |
40165636 | 74 | static void __objc_send_initialize (Class); |
88e17b57 BE |
75 | |
76 | static void __objc_install_dispatch_table_for_class (Class); | |
77 | ||
78 | /* Forward declare some functions */ | |
40165636 | 79 | static void __objc_init_install_dtable (id, SEL); |
88e17b57 BE |
80 | |
81 | /* Various forwarding functions that are used based upon the | |
82 | return type for the selector. | |
83 | __objc_block_forward for structures. | |
84 | __objc_double_forward for floats/doubles. | |
80ae8e8a | 85 | __objc_word_forward for pointers or types that fit in registers. */ |
40165636 RB |
86 | static double __objc_double_forward (id, SEL, ...); |
87 | static id __objc_word_forward (id, SEL, ...); | |
88e17b57 BE |
88 | typedef struct { id many[8]; } __big; |
89 | #if INVISIBLE_STRUCT_RETURN | |
90 | static __big | |
91 | #else | |
92 | static id | |
93 | #endif | |
40165636 | 94 | __objc_block_forward (id, SEL, ...); |
88e17b57 | 95 | static Method_t search_for_method_in_hierarchy (Class class, SEL sel); |
40165636 | 96 | Method_t search_for_method_in_list (MethodList_t list, SEL op); |
faaa30fe | 97 | id nil_method (id, SEL); |
40165636 RB |
98 | |
99 | /* Given a selector, return the proper forwarding implementation. */ | |
435317e2 | 100 | inline |
40165636 | 101 | IMP |
80ae8e8a | 102 | __objc_get_forward_imp (id rcv, SEL sel) |
40165636 | 103 | { |
bd8d449d | 104 | /* If a custom forwarding hook was registered, try getting a forwarding |
80ae8e8a AR |
105 | function from it. There are two forward routine hooks, one that |
106 | takes the receiver as an argument and one that does not. */ | |
8972bcd8 AR |
107 | if (__objc_msg_forward2) |
108 | { | |
109 | IMP result; | |
110 | if ((result = __objc_msg_forward2 (rcv, sel)) != NULL) | |
111 | return result; | |
112 | } | |
40165636 RB |
113 | if (__objc_msg_forward) |
114 | { | |
115 | IMP result; | |
bd8d449d | 116 | if ((result = __objc_msg_forward (sel)) != NULL) |
40165636 RB |
117 | return result; |
118 | } | |
bd8d449d NP |
119 | |
120 | /* In all other cases, use the default forwarding functions built using | |
80ae8e8a | 121 | __builtin_apply and friends. */ |
40165636 RB |
122 | { |
123 | const char *t = sel->sel_types; | |
124 | ||
125 | if (t && (*t == '[' || *t == '(' || *t == '{') | |
126 | #ifdef OBJC_MAX_STRUCT_BY_VALUE | |
127 | && objc_sizeof_type (t) > OBJC_MAX_STRUCT_BY_VALUE | |
128 | #endif | |
129 | ) | |
130 | return (IMP)__objc_block_forward; | |
131 | else if (t && (*t == 'f' || *t == 'd')) | |
132 | return (IMP)__objc_double_forward; | |
133 | else | |
134 | return (IMP)__objc_word_forward; | |
135 | } | |
136 | } | |
88e17b57 BE |
137 | |
138 | /* Given a class and selector, return the selector's implementation. */ | |
bd74d88b | 139 | inline |
88e17b57 BE |
140 | IMP |
141 | get_imp (Class class, SEL sel) | |
142 | { | |
c19f8e35 NP |
143 | /* In a vanilla implementation we would first check if the dispatch |
144 | table is installed. Here instead, to get more speed in the | |
145 | standard case (that the dispatch table is installed) we first try | |
146 | to get the imp using brute force. Only if that fails, we do what | |
147 | we should have been doing from the very beginning, that is, check | |
148 | if the dispatch table needs to be installed, install it if it's | |
149 | not installed, and retrieve the imp from the table if it's | |
150 | installed. */ | |
40165636 | 151 | void *res = sarray_get_safe (class->dtable, (size_t) sel->sel_id); |
88e17b57 BE |
152 | if (res == 0) |
153 | { | |
154 | /* Not a valid method */ | |
40165636 | 155 | if (class->dtable == __objc_uninstalled_dtable) |
88e17b57 BE |
156 | { |
157 | /* The dispatch table needs to be installed. */ | |
40165636 | 158 | objc_mutex_lock (__objc_runtime_mutex); |
c19f8e35 NP |
159 | |
160 | /* Double-checked locking pattern: Check | |
161 | __objc_uninstalled_dtable again in case another thread | |
162 | installed the dtable while we were waiting for the lock | |
163 | to be released. */ | |
164 | if (class->dtable == __objc_uninstalled_dtable) | |
165 | { | |
166 | __objc_install_dispatch_table_for_class (class); | |
167 | } | |
168 | ||
40165636 | 169 | objc_mutex_unlock (__objc_runtime_mutex); |
88e17b57 BE |
170 | /* Call ourselves with the installed dispatch table |
171 | and get the real method */ | |
40165636 | 172 | res = get_imp (class, sel); |
88e17b57 BE |
173 | } |
174 | else | |
175 | { | |
c19f8e35 NP |
176 | /* The dispatch table has been installed. */ |
177 | ||
178 | /* Get the method from the dispatch table (we try to get it | |
179 | again in case another thread has installed the dtable just | |
180 | after we invoked sarray_get_safe, but before we checked | |
181 | class->dtable == __objc_uninstalled_dtable). | |
182 | */ | |
183 | res = sarray_get_safe (class->dtable, (size_t) sel->sel_id); | |
184 | if (res == 0) | |
185 | { | |
186 | /* The dispatch table has been installed, and the method | |
187 | is not in the dispatch table. So the method just | |
188 | doesn't exist for the class. Return the forwarding | |
189 | implementation. */ | |
80ae8e8a | 190 | res = __objc_get_forward_imp ((id)class, sel); |
c19f8e35 | 191 | } |
88e17b57 BE |
192 | } |
193 | } | |
194 | return res; | |
195 | } | |
196 | ||
197 | /* Query if an object can respond to a selector, returns YES if the | |
198 | object implements the selector otherwise NO. Does not check if the | |
199 | method can be forwarded. */ | |
435317e2 | 200 | inline |
88e17b57 BE |
201 | BOOL |
202 | __objc_responds_to (id object, SEL sel) | |
203 | { | |
40165636 | 204 | void *res; |
88e17b57 BE |
205 | |
206 | /* Install dispatch table if need be */ | |
207 | if (object->class_pointer->dtable == __objc_uninstalled_dtable) | |
208 | { | |
40165636 | 209 | objc_mutex_lock (__objc_runtime_mutex); |
c19f8e35 NP |
210 | if (object->class_pointer->dtable == __objc_uninstalled_dtable) |
211 | { | |
212 | __objc_install_dispatch_table_for_class (object->class_pointer); | |
213 | } | |
40165636 | 214 | objc_mutex_unlock (__objc_runtime_mutex); |
88e17b57 BE |
215 | } |
216 | ||
217 | /* Get the method from the dispatch table */ | |
218 | res = sarray_get_safe (object->class_pointer->dtable, (size_t) sel->sel_id); | |
219 | return (res != 0); | |
220 | } | |
221 | ||
222 | /* This is the lookup function. All entries in the table are either a | |
223 | valid method *or* zero. If zero then either the dispatch table | |
224 | needs to be installed or it doesn't exist and forwarding is attempted. */ | |
7116b6ea | 225 | |
88e17b57 | 226 | IMP |
40165636 | 227 | objc_msg_lookup (id receiver, SEL op) |
88e17b57 BE |
228 | { |
229 | IMP result; | |
40165636 | 230 | if (receiver) |
88e17b57 BE |
231 | { |
232 | result = sarray_get_safe (receiver->class_pointer->dtable, | |
233 | (sidx)op->sel_id); | |
234 | if (result == 0) | |
235 | { | |
236 | /* Not a valid method */ | |
40165636 | 237 | if (receiver->class_pointer->dtable == __objc_uninstalled_dtable) |
88e17b57 BE |
238 | { |
239 | /* The dispatch table needs to be installed. | |
240 | This happens on the very first method call to the class. */ | |
40165636 | 241 | __objc_init_install_dtable (receiver, op); |
88e17b57 BE |
242 | |
243 | /* Get real method for this in newly installed dtable */ | |
40165636 | 244 | result = get_imp (receiver->class_pointer, op); |
88e17b57 BE |
245 | } |
246 | else | |
247 | { | |
c19f8e35 NP |
248 | /* The dispatch table has been installed. Check again |
249 | if the method exists (just in case the dispatch table | |
250 | has been installed by another thread after we did the | |
251 | previous check that the method exists). | |
252 | */ | |
253 | result = sarray_get_safe (receiver->class_pointer->dtable, | |
254 | (sidx)op->sel_id); | |
255 | if (result == 0) | |
256 | { | |
257 | /* If the method still just doesn't exist for the | |
258 | class, attempt to forward the method. */ | |
80ae8e8a | 259 | result = __objc_get_forward_imp (receiver, op); |
c19f8e35 | 260 | } |
88e17b57 BE |
261 | } |
262 | } | |
263 | return result; | |
264 | } | |
265 | else | |
faaa30fe | 266 | return (IMP)nil_method; |
88e17b57 BE |
267 | } |
268 | ||
269 | IMP | |
270 | objc_msg_lookup_super (Super_t super, SEL sel) | |
271 | { | |
272 | if (super->self) | |
273 | return get_imp (super->class, sel); | |
274 | else | |
faaa30fe | 275 | return (IMP)nil_method; |
88e17b57 BE |
276 | } |
277 | ||
40165636 | 278 | int method_get_sizeof_arguments (Method *); |
88e17b57 BE |
279 | |
280 | retval_t | |
40165636 | 281 | objc_msg_sendv (id object, SEL op, arglist_t arg_frame) |
88e17b57 | 282 | { |
40165636 | 283 | Method *m = class_get_instance_method (object->class_pointer, op); |
88e17b57 | 284 | const char *type; |
40165636 RB |
285 | *((id *) method_get_first_argument (m, arg_frame, &type)) = object; |
286 | *((SEL *) method_get_next_argument (arg_frame, &type)) = op; | |
287 | return __builtin_apply ((apply_t) m->method_imp, | |
288 | arg_frame, | |
289 | method_get_sizeof_arguments (m)); | |
88e17b57 BE |
290 | } |
291 | ||
292 | void | |
40165636 | 293 | __objc_init_dispatch_tables () |
88e17b57 | 294 | { |
40165636 | 295 | __objc_uninstalled_dtable = sarray_new (200, 0); |
88e17b57 BE |
296 | } |
297 | ||
298 | /* This function is called by objc_msg_lookup when the | |
299 | dispatch table needs to be installed; thus it is called once | |
300 | for each class, namely when the very first message is sent to it. */ | |
301 | static void | |
40165636 | 302 | __objc_init_install_dtable (id receiver, SEL op __attribute__ ((__unused__))) |
88e17b57 | 303 | { |
c19f8e35 NP |
304 | objc_mutex_lock (__objc_runtime_mutex); |
305 | ||
88e17b57 BE |
306 | /* This may happen, if the programmer has taken the address of a |
307 | method before the dtable was initialized... too bad for him! */ | |
40165636 | 308 | if (receiver->class_pointer->dtable != __objc_uninstalled_dtable) |
c19f8e35 NP |
309 | { |
310 | objc_mutex_unlock (__objc_runtime_mutex); | |
311 | return; | |
312 | } | |
313 | ||
40165636 | 314 | if (CLS_ISCLASS (receiver->class_pointer)) |
88e17b57 BE |
315 | { |
316 | /* receiver is an ordinary object */ | |
40165636 | 317 | assert (CLS_ISCLASS (receiver->class_pointer)); |
88e17b57 BE |
318 | |
319 | /* install instance methods table */ | |
320 | __objc_install_dispatch_table_for_class (receiver->class_pointer); | |
321 | ||
322 | /* call +initialize -- this will in turn install the factory | |
323 | dispatch table if not already done :-) */ | |
40165636 | 324 | __objc_send_initialize (receiver->class_pointer); |
88e17b57 BE |
325 | } |
326 | else | |
327 | { | |
328 | /* receiver is a class object */ | |
40165636 RB |
329 | assert (CLS_ISCLASS ((Class)receiver)); |
330 | assert (CLS_ISMETA (receiver->class_pointer)); | |
88e17b57 BE |
331 | |
332 | /* Install real dtable for factory methods */ | |
333 | __objc_install_dispatch_table_for_class (receiver->class_pointer); | |
334 | ||
40165636 | 335 | __objc_send_initialize ((Class)receiver); |
88e17b57 | 336 | } |
40165636 | 337 | objc_mutex_unlock (__objc_runtime_mutex); |
88e17b57 BE |
338 | } |
339 | ||
340 | /* Install dummy table for class which causes the first message to | |
341 | that class (or instances hereof) to be initialized properly */ | |
342 | void | |
40165636 | 343 | __objc_install_premature_dtable (Class class) |
88e17b57 | 344 | { |
40165636 | 345 | assert (__objc_uninstalled_dtable); |
88e17b57 BE |
346 | class->dtable = __objc_uninstalled_dtable; |
347 | } | |
348 | ||
349 | /* Send +initialize to class if not already done */ | |
350 | static void | |
40165636 | 351 | __objc_send_initialize (Class class) |
88e17b57 BE |
352 | { |
353 | /* This *must* be a class object */ | |
40165636 RB |
354 | assert (CLS_ISCLASS (class)); |
355 | assert (! CLS_ISMETA (class)); | |
88e17b57 | 356 | |
40165636 | 357 | if (! CLS_ISINITIALIZED (class)) |
88e17b57 | 358 | { |
40165636 RB |
359 | CLS_SETINITIALIZED (class); |
360 | CLS_SETINITIALIZED (class->class_pointer); | |
88e17b57 BE |
361 | |
362 | /* Create the garbage collector type memory description */ | |
363 | __objc_generate_gc_type_description (class); | |
364 | ||
40165636 RB |
365 | if (class->super_class) |
366 | __objc_send_initialize (class->super_class); | |
88e17b57 BE |
367 | |
368 | { | |
369 | SEL op = sel_register_name ("initialize"); | |
370 | IMP imp = 0; | |
371 | MethodList_t method_list = class->class_pointer->methods; | |
372 | ||
373 | while (method_list) { | |
374 | int i; | |
375 | Method_t method; | |
376 | ||
40165636 | 377 | for (i = 0; i < method_list->method_count; i++) { |
88e17b57 BE |
378 | method = &(method_list->method_list[i]); |
379 | if (method->method_name | |
380 | && method->method_name->sel_id == op->sel_id) { | |
381 | imp = method->method_imp; | |
382 | break; | |
383 | } | |
384 | } | |
385 | ||
386 | if (imp) | |
387 | break; | |
388 | ||
389 | method_list = method_list->method_next; | |
390 | ||
391 | } | |
392 | if (imp) | |
40165636 | 393 | (*imp) ((id) class, op); |
88e17b57 BE |
394 | |
395 | } | |
396 | } | |
397 | } | |
398 | ||
399 | /* Walk on the methods list of class and install the methods in the reverse | |
400 | order of the lists. Since methods added by categories are before the methods | |
401 | of class in the methods list, this allows categories to substitute methods | |
402 | declared in class. However if more than one category replaces the same | |
403 | method nothing is guaranteed about what method will be used. | |
404 | Assumes that __objc_runtime_mutex is locked down. */ | |
405 | static void | |
406 | __objc_install_methods_in_dtable (Class class, MethodList_t method_list) | |
407 | { | |
408 | int i; | |
409 | ||
40165636 | 410 | if (! method_list) |
88e17b57 BE |
411 | return; |
412 | ||
413 | if (method_list->method_next) | |
414 | __objc_install_methods_in_dtable (class, method_list->method_next); | |
415 | ||
416 | for (i = 0; i < method_list->method_count; i++) | |
417 | { | |
418 | Method_t method = &(method_list->method_list[i]); | |
419 | sarray_at_put_safe (class->dtable, | |
420 | (sidx) method->method_name->sel_id, | |
421 | method->method_imp); | |
422 | } | |
423 | } | |
424 | ||
425 | /* Assumes that __objc_runtime_mutex is locked down. */ | |
426 | static void | |
427 | __objc_install_dispatch_table_for_class (Class class) | |
428 | { | |
429 | Class super; | |
430 | ||
431 | /* If the class has not yet had its class links resolved, we must | |
432 | re-compute all class links */ | |
40165636 RB |
433 | if (! CLS_ISRESOLV (class)) |
434 | __objc_resolve_class_links (); | |
88e17b57 BE |
435 | |
436 | super = class->super_class; | |
437 | ||
438 | if (super != 0 && (super->dtable == __objc_uninstalled_dtable)) | |
439 | __objc_install_dispatch_table_for_class (super); | |
440 | ||
441 | /* Allocate dtable if necessary */ | |
442 | if (super == 0) | |
443 | { | |
40165636 | 444 | objc_mutex_lock (__objc_runtime_mutex); |
88e17b57 | 445 | class->dtable = sarray_new (__objc_selector_max_index, 0); |
40165636 | 446 | objc_mutex_unlock (__objc_runtime_mutex); |
88e17b57 BE |
447 | } |
448 | else | |
449 | class->dtable = sarray_lazy_copy (super->dtable); | |
450 | ||
451 | __objc_install_methods_in_dtable (class, class->methods); | |
452 | } | |
453 | ||
454 | void | |
455 | __objc_update_dispatch_table_for_class (Class class) | |
456 | { | |
457 | Class next; | |
458 | struct sarray *arr; | |
459 | ||
460 | /* not yet installed -- skip it */ | |
461 | if (class->dtable == __objc_uninstalled_dtable) | |
462 | return; | |
463 | ||
40165636 | 464 | objc_mutex_lock (__objc_runtime_mutex); |
88e17b57 BE |
465 | |
466 | arr = class->dtable; | |
467 | __objc_install_premature_dtable (class); /* someone might require it... */ | |
468 | sarray_free (arr); /* release memory */ | |
469 | ||
470 | /* could have been lazy... */ | |
471 | __objc_install_dispatch_table_for_class (class); | |
472 | ||
473 | if (class->subclass_list) /* Traverse subclasses */ | |
474 | for (next = class->subclass_list; next; next = next->sibling_class) | |
475 | __objc_update_dispatch_table_for_class (next); | |
476 | ||
40165636 | 477 | objc_mutex_unlock (__objc_runtime_mutex); |
88e17b57 BE |
478 | } |
479 | ||
480 | ||
481 | /* This function adds a method list to a class. This function is | |
482 | typically called by another function specific to the run-time. As | |
483 | such this function does not worry about thread safe issues. | |
484 | ||
485 | This one is only called for categories. Class objects have their | |
486 | methods installed right away, and their selectors are made into | |
435317e2 | 487 | SEL's by the function __objc_register_selectors_from_class. */ |
88e17b57 BE |
488 | void |
489 | class_add_method_list (Class class, MethodList_t list) | |
490 | { | |
88e17b57 | 491 | /* Passing of a linked list is not allowed. Do multiple calls. */ |
40165636 | 492 | assert (! list->method_next); |
88e17b57 | 493 | |
435317e2 | 494 | __objc_register_selectors_from_list(list); |
88e17b57 BE |
495 | |
496 | /* Add the methods to the class's method list. */ | |
497 | list->method_next = class->methods; | |
498 | class->methods = list; | |
499 | ||
500 | /* Update the dispatch table of class */ | |
501 | __objc_update_dispatch_table_for_class (class); | |
502 | } | |
503 | ||
504 | Method_t | |
40165636 | 505 | class_get_instance_method (Class class, SEL op) |
88e17b57 | 506 | { |
40165636 | 507 | return search_for_method_in_hierarchy (class, op); |
88e17b57 BE |
508 | } |
509 | ||
510 | Method_t | |
40165636 | 511 | class_get_class_method (MetaClass class, SEL op) |
88e17b57 | 512 | { |
40165636 | 513 | return search_for_method_in_hierarchy (class, op); |
88e17b57 BE |
514 | } |
515 | ||
516 | ||
517 | /* Search for a method starting from the current class up its hierarchy. | |
518 | Return a pointer to the method's method structure if found. NULL | |
519 | otherwise. */ | |
520 | ||
521 | static Method_t | |
522 | search_for_method_in_hierarchy (Class cls, SEL sel) | |
523 | { | |
524 | Method_t method = NULL; | |
525 | Class class; | |
526 | ||
527 | if (! sel_is_mapped (sel)) | |
528 | return NULL; | |
529 | ||
530 | /* Scan the method list of the class. If the method isn't found in the | |
531 | list then step to its super class. */ | |
532 | for (class = cls; ((! method) && class); class = class->super_class) | |
533 | method = search_for_method_in_list (class->methods, sel); | |
534 | ||
535 | return method; | |
536 | } | |
537 | ||
538 | ||
539 | ||
540 | /* Given a linked list of method and a method's name. Search for the named | |
541 | method's method structure. Return a pointer to the method's method | |
542 | structure if found. NULL otherwise. */ | |
543 | Method_t | |
544 | search_for_method_in_list (MethodList_t list, SEL op) | |
545 | { | |
546 | MethodList_t method_list = list; | |
547 | ||
548 | if (! sel_is_mapped (op)) | |
549 | return NULL; | |
550 | ||
551 | /* If not found then we'll search the list. */ | |
552 | while (method_list) | |
553 | { | |
554 | int i; | |
555 | ||
556 | /* Search the method list. */ | |
557 | for (i = 0; i < method_list->method_count; ++i) | |
558 | { | |
559 | Method_t method = &method_list->method_list[i]; | |
560 | ||
561 | if (method->method_name) | |
562 | if (method->method_name->sel_id == op->sel_id) | |
563 | return method; | |
564 | } | |
565 | ||
566 | /* The method wasn't found. Follow the link to the next list of | |
567 | methods. */ | |
568 | method_list = method_list->method_next; | |
569 | } | |
570 | ||
571 | return NULL; | |
572 | } | |
573 | ||
574 | static retval_t __objc_forward (id object, SEL sel, arglist_t args); | |
575 | ||
576 | /* Forwarding pointers/integers through the normal registers */ | |
577 | static id | |
578 | __objc_word_forward (id rcv, SEL op, ...) | |
579 | { | |
580 | void *args, *res; | |
581 | ||
582 | args = __builtin_apply_args (); | |
583 | res = __objc_forward (rcv, op, args); | |
584 | if (res) | |
585 | __builtin_return (res); | |
586 | else | |
587 | return res; | |
588 | } | |
589 | ||
590 | /* Specific routine for forwarding floats/double because of | |
591 | architectural differences on some processors. i386s for | |
592 | example which uses a floating point stack versus general | |
593 | registers for floating point numbers. This forward routine | |
594 | makes sure that GCC restores the proper return values */ | |
595 | static double | |
596 | __objc_double_forward (id rcv, SEL op, ...) | |
597 | { | |
598 | void *args, *res; | |
599 | ||
600 | args = __builtin_apply_args (); | |
601 | res = __objc_forward (rcv, op, args); | |
602 | __builtin_return (res); | |
603 | } | |
604 | ||
605 | #if INVISIBLE_STRUCT_RETURN | |
606 | static __big | |
607 | #else | |
608 | static id | |
609 | #endif | |
610 | __objc_block_forward (id rcv, SEL op, ...) | |
611 | { | |
612 | void *args, *res; | |
613 | ||
614 | args = __builtin_apply_args (); | |
615 | res = __objc_forward (rcv, op, args); | |
616 | if (res) | |
617 | __builtin_return (res); | |
618 | else | |
619 | #if INVISIBLE_STRUCT_RETURN | |
620 | return (__big) {{0, 0, 0, 0, 0, 0, 0, 0}}; | |
621 | #else | |
622 | return nil; | |
623 | #endif | |
624 | } | |
625 | ||
626 | ||
627 | /* This function is installed in the dispatch table for all methods which are | |
628 | not implemented. Thus, it is called when a selector is not recognized. */ | |
629 | static retval_t | |
630 | __objc_forward (id object, SEL sel, arglist_t args) | |
631 | { | |
632 | IMP imp; | |
633 | static SEL frwd_sel = 0; /* !T:SAFE2 */ | |
634 | SEL err_sel; | |
635 | ||
636 | /* first try if the object understands forward:: */ | |
40165636 RB |
637 | if (! frwd_sel) |
638 | frwd_sel = sel_get_any_uid ("forward::"); | |
88e17b57 BE |
639 | |
640 | if (__objc_responds_to (object, frwd_sel)) | |
641 | { | |
40165636 RB |
642 | imp = get_imp (object->class_pointer, frwd_sel); |
643 | return (*imp) (object, frwd_sel, sel, args); | |
88e17b57 BE |
644 | } |
645 | ||
646 | /* If the object recognizes the doesNotRecognize: method then we're going | |
647 | to send it. */ | |
648 | err_sel = sel_get_any_uid ("doesNotRecognize:"); | |
649 | if (__objc_responds_to (object, err_sel)) | |
650 | { | |
651 | imp = get_imp (object->class_pointer, err_sel); | |
652 | return (*imp) (object, err_sel, sel); | |
653 | } | |
654 | ||
655 | /* The object doesn't recognize the method. Check for responding to | |
656 | error:. If it does then sent it. */ | |
657 | { | |
40165636 RB |
658 | char msg[256 + strlen ((const char *) sel_get_name (sel)) |
659 | + strlen ((const char *) object->class_pointer->name)]; | |
88e17b57 BE |
660 | |
661 | sprintf (msg, "(%s) %s does not recognize %s", | |
40165636 | 662 | (CLS_ISMETA (object->class_pointer) |
88e17b57 BE |
663 | ? "class" |
664 | : "instance" ), | |
665 | object->class_pointer->name, sel_get_name (sel)); | |
666 | ||
7b869986 | 667 | /* TODO: support for error: is surely deprecated ? */ |
88e17b57 BE |
668 | err_sel = sel_get_any_uid ("error:"); |
669 | if (__objc_responds_to (object, err_sel)) | |
670 | { | |
671 | imp = get_imp (object->class_pointer, err_sel); | |
672 | return (*imp) (object, sel_get_any_uid ("error:"), msg); | |
673 | } | |
674 | ||
675 | /* The object doesn't respond to doesNotRecognize: or error:; Therefore, | |
676 | a default action is taken. */ | |
7b869986 | 677 | _objc_abort ("%s\n", msg); |
88e17b57 BE |
678 | |
679 | return 0; | |
680 | } | |
681 | } | |
682 | ||
683 | void | |
40165636 | 684 | __objc_print_dtable_stats () |
88e17b57 BE |
685 | { |
686 | int total = 0; | |
687 | ||
40165636 | 688 | objc_mutex_lock (__objc_runtime_mutex); |
88e17b57 | 689 | |
88e17b57 | 690 | #ifdef OBJC_SPARSE2 |
40165636 | 691 | printf ("memory usage: (%s)\n", "2-level sparse arrays"); |
88e17b57 | 692 | #else |
40165636 | 693 | printf ("memory usage: (%s)\n", "3-level sparse arrays"); |
88e17b57 | 694 | #endif |
88e17b57 | 695 | |
40165636 | 696 | printf ("arrays: %d = %ld bytes\n", narrays, |
b15b7ef8 | 697 | (long) ((size_t) narrays * sizeof (struct sarray))); |
40165636 RB |
698 | total += narrays * sizeof (struct sarray); |
699 | printf ("buckets: %d = %ld bytes\n", nbuckets, | |
b15b7ef8 | 700 | (long) ((size_t) nbuckets * sizeof (struct sbucket))); |
40165636 RB |
701 | total += nbuckets * sizeof (struct sbucket); |
702 | ||
703 | printf ("idxtables: %d = %ld bytes\n", | |
b15b7ef8 | 704 | idxsize, (long) ((size_t) idxsize * sizeof (void *))); |
40165636 RB |
705 | total += idxsize * sizeof (void *); |
706 | printf ("-----------------------------------\n"); | |
707 | printf ("total: %d bytes\n", total); | |
708 | printf ("===================================\n"); | |
709 | ||
710 | objc_mutex_unlock (__objc_runtime_mutex); | |
88e17b57 BE |
711 | } |
712 | ||
713 | /* Returns the uninstalled dispatch table indicator. | |
714 | If a class' dispatch table points to __objc_uninstalled_dtable | |
715 | then that means it needs its dispatch table to be installed. */ | |
7116b6ea | 716 | |
40165636 RB |
717 | struct sarray * |
718 | objc_get_uninstalled_dtable () | |
88e17b57 BE |
719 | { |
720 | return __objc_uninstalled_dtable; | |
721 | } |