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