OSDN Git Service

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