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