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