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