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