OSDN Git Service

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