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