OSDN Git Service

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