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