source: trunk/third/gcc/objc/sendmsg.c @ 8834

Revision 8834, 15.0 KB checked in by ghudson, 28 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r8833, which included commits to RCS files with non-trunk default branches.
Line 
1/* GNU Objective C Runtime message lookup
2   Copyright (C) 1993, 1995 Free Software Foundation, Inc.
3   Contributed by Kresten Krab Thorup
4
5This file is part of GNU CC.
6
7GNU CC is free software; you can redistribute it and/or modify it under the
8terms of the GNU General Public License as published by the Free Software
9Foundation; either version 2, or (at your option) any later version.
10
11GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14details.
15
16You should have received a copy of the GNU General Public License along with
17GNU CC; see the file COPYING.  If not, write to the Free Software
18Foundation, 59 Temple Place - Suite 330,
19Boston, 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
32/* this is how we hack STRUCT_VALUE to be 1 or 0 */
33#define gen_rtx(args...) 1
34#define rtx int
35
36#if STRUCT_VALUE == 0
37#define INVISIBLE_STRUCT_RETURN 1
38#else
39#define INVISIBLE_STRUCT_RETURN 0
40#endif
41
42/* The uninstalled dispatch table */
43struct sarray* __objc_uninstalled_dtable = 0;
44
45/* Send +initialize to class */
46static void __objc_send_initialize(Class);
47
48static void __objc_install_dispatch_table_for_class (Class);
49
50/* Forward declare some functions */
51static void __objc_init_install_dtable(id, SEL);
52static id __objc_word_forward(id, SEL, ...);
53typedef struct { id many[8]; } __big;
54#if INVISIBLE_STRUCT_RETURN
55static __big
56#else
57static id
58#endif
59__objc_block_forward(id, SEL, ...);
60static Method_t search_for_method_in_hierarchy (Class class, SEL sel);
61static Method_t search_for_method_in_list(MethodList_t list, SEL op);
62id nil_method(id, SEL, ...);
63
64id
65nil_method(id receiver, SEL op, ...)
66{
67  return receiver;
68}
69
70/* Given a class and selector, return the selector's implementation.  */
71__inline__
72IMP
73get_imp (Class class, SEL sel)
74{
75  IMP impl;
76  void* res = sarray_get (class->dtable, (size_t) sel->sel_id);
77  if(res == __objc_init_install_dtable)
78    {
79      __objc_install_dispatch_table_for_class (class);
80      res = sarray_get (class->dtable, (size_t) sel->sel_id);
81    }
82  if (res == 0)
83    {
84      const char *t = sel->sel_types;
85      if (t && (*t == '[' || *t == '(' || *t == '{'))
86        res = (IMP)__objc_block_forward;
87      else
88        res = (IMP)__objc_word_forward;
89    }
90  return res;
91}
92
93__inline__ BOOL
94__objc_responds_to (id object, SEL sel)
95{
96  void* res = sarray_get (object->class_pointer->dtable, (size_t) sel->sel_id);
97  if(res == __objc_init_install_dtable)
98    {
99      __objc_install_dispatch_table_for_class (object->class_pointer);
100      res = sarray_get (object->class_pointer->dtable, (size_t) sel->sel_id);
101    }
102  return (res != 0);
103}
104
105/* This is the lookup function.  All entries in the table are either a
106   valid method *or* one of `__objc_missing_method' which calls
107   forward:: etc, or `__objc_init_install_dtable' which installs the
108   real dtable */
109__inline__ IMP
110objc_msg_lookup(id receiver, SEL op)
111{
112  IMP result;
113  if(receiver)
114    {
115      result = sarray_get(receiver->class_pointer->dtable, (sidx)op->sel_id);
116      if (result == 0)
117        {
118          const char *t = op->sel_types;
119          if (t && (*t == '[' || *t == '(' || *t == '{'))
120            result = (IMP)__objc_block_forward;
121          else
122            result = (IMP)__objc_word_forward;
123        }
124      return result;
125    }
126  else
127    return nil_method;
128}
129
130IMP
131objc_msg_lookup_super (Super_t super, SEL sel)
132{
133  if (super->self)
134    return get_imp (super->class, sel);
135  else
136    return nil_method;
137}
138
139int method_get_sizeof_arguments (Method*);
140
141retval_t
142objc_msg_sendv(id object, SEL op, arglist_t arg_frame)
143{
144  Method* m = class_get_instance_method(object->class_pointer, op);
145  const char *type;
146  *((id*)method_get_first_argument (m, arg_frame, &type)) = object;
147  *((SEL*)method_get_next_argument (arg_frame, &type)) = op;
148  return __builtin_apply((apply_t)m->method_imp,
149                         arg_frame,
150                         method_get_sizeof_arguments (m));
151}
152
153void __objc_init_dispatch_tables()
154{
155  __objc_uninstalled_dtable
156    = sarray_new(200, __objc_init_install_dtable);
157}
158
159/* This one is a bit hairy.  This function is installed in the
160   premature dispatch table, and thus called once for each class,
161   namely when the very first message is send to it.  */
162
163static void __objc_init_install_dtable(id receiver, SEL op)
164{
165  __label__ already_initialized;
166  IMP imp;
167  void* args;
168  void* result;
169
170  /* This may happen, if the programmer has taken the address of a
171     method before the dtable was initialized... too bad for him! */
172  if(receiver->class_pointer->dtable != __objc_uninstalled_dtable)
173    goto already_initialized;
174
175  if(CLS_ISCLASS(receiver->class_pointer))
176    {
177      /* receiver is an ordinary object */
178      assert(CLS_ISCLASS(receiver->class_pointer));
179
180      /* install instance methods table */
181      __objc_install_dispatch_table_for_class (receiver->class_pointer);
182
183      /* call +initialize -- this will in turn install the factory
184         dispatch table if not already done :-) */
185      __objc_send_initialize(receiver->class_pointer);
186    }
187  else
188    {
189      /* receiver is a class object */
190      assert(CLS_ISCLASS((Class)receiver));
191      assert(CLS_ISMETA(receiver->class_pointer));
192
193      /* Install real dtable for factory methods */
194      __objc_install_dispatch_table_for_class (receiver->class_pointer);
195
196      if (strcmp (sel_get_name (op), "initialize"))
197        __objc_send_initialize((Class)receiver);
198      else
199        CLS_SETINITIALIZED((Class)receiver);
200    }
201
202already_initialized:
203 
204  /* Get real method for this in newly installed dtable */
205  imp = get_imp(receiver->class_pointer, op);
206
207  args = __builtin_apply_args();
208  result = __builtin_apply((apply_t)imp, args, 96);
209  if (result)
210    __builtin_return (result);
211  else
212    return;
213 
214}
215
216/* Install dummy table for class which causes the first message to
217   that class (or instances hereof) to be initialized properly */
218void __objc_install_premature_dtable(Class class)
219{
220  assert(__objc_uninstalled_dtable);
221  class->dtable = __objc_uninstalled_dtable;
222}   
223
224/* Send +initialize to class if not already done */
225static void __objc_send_initialize(Class class)
226{
227  /* This *must* be a class object */
228  assert(CLS_ISCLASS(class));
229  assert(!CLS_ISMETA(class));
230
231  if (!CLS_ISINITIALIZED(class))
232    {
233      CLS_SETINITIALIZED(class);
234      CLS_SETINITIALIZED(class->class_pointer);
235     
236      if(class->super_class)
237        __objc_send_initialize(class->super_class);
238
239      {
240        SEL     op = sel_register_name ("initialize");
241        Class   tmpclass = class;
242        IMP     imp = 0;
243
244        while (!imp && tmpclass) {
245          MethodList_t method_list = tmpclass->class_pointer->methods;
246
247          while(!imp && method_list) {
248            int i;
249            Method_t method;
250
251            for (i=0;i<method_list->method_count;i++) {
252              method = &(method_list->method_list[i]);
253              if (method->method_name->sel_id == op->sel_id) {
254                imp = method->method_imp;
255                break;
256              }
257            }
258
259            method_list = method_list->method_next;
260
261          }
262
263          tmpclass = tmpclass->super_class;
264        }
265        if (imp)
266            (*imp)((id)class, op);
267               
268      }
269    }
270
271
272static void
273__objc_install_dispatch_table_for_class (Class class)
274{
275  Class super;
276  MethodList_t mlist;
277  int counter;
278
279  /* If the class has not yet had it's class links resolved, we must
280     re-compute all class links */
281  if(!CLS_ISRESOLV(class))
282    __objc_resolve_class_links();
283
284  super = class->super_class;
285
286  if (super != 0 && (super->dtable == __objc_uninstalled_dtable))
287    __objc_install_dispatch_table_for_class (super);
288
289  /* Allocate dtable if necessary */
290  if (super == 0)
291    {
292      class->dtable = sarray_new (__objc_selector_max_index, 0);
293    }
294  else
295    class->dtable = sarray_lazy_copy (super->dtable);
296
297  for (mlist = class->methods; mlist; mlist = mlist->method_next)
298    {
299      counter = mlist->method_count - 1;
300      while (counter >= 0)
301        {
302          Method_t method = &(mlist->method_list[counter]);
303          sarray_at_put_safe (class->dtable,
304                              (sidx) method->method_name->sel_id,
305                              method->method_imp);
306          counter -= 1;
307        }
308    }
309}
310
311void __objc_update_dispatch_table_for_class (Class class)
312{
313  Class next;
314
315  /* not yet installed -- skip it */
316  if (class->dtable == __objc_uninstalled_dtable)
317    return;
318
319  sarray_free (class->dtable);  /* release memory */
320  __objc_install_premature_dtable (class); /* someone might require it... */
321  __objc_install_dispatch_table_for_class (class); /* could have been lazy... */
322
323  if (class->subclass_list)     /* Traverse subclasses */
324    for (next = class->subclass_list; next; next = next->sibling_class)
325      __objc_update_dispatch_table_for_class (next);
326
327}
328
329
330/* This function adds a method list to a class.  This function is
331   typically called by another function specific to the run-time.  As
332   such this function does not worry about thread safe issued.
333
334   This one is only called for categories. Class objects have their
335   methods installed right away, and their selectors are made into
336   SEL's by the function __objc_register_selectors_from_class. */
337void
338class_add_method_list (Class class, MethodList_t list)
339{
340  int i;
341  static SEL initialize_sel = 0;
342  if (!initialize_sel)
343    initialize_sel = sel_register_name ("initialize");
344
345  /* Passing of a linked list is not allowed.  Do multiple calls.  */
346  assert (!list->method_next);
347
348  /* Check for duplicates.  */
349  for (i = 0; i < list->method_count; ++i)
350    {
351      Method_t method = &list->method_list[i];
352
353      if (method->method_name)  /* Sometimes these are NULL */
354        {
355          /* This is where selector names are transmogrified to SEL's */
356          method->method_name =
357            sel_register_typed_name ((const char*)method->method_name,
358                                     method->method_types);
359
360          if (search_for_method_in_list (class->methods, method->method_name)
361              && method->method_name->sel_id != initialize_sel->sel_id)
362            {
363              /* Duplication. Print a error message an change the method name
364                 to NULL. */
365              fprintf (stderr, "attempt to add a existing method: %s\n",
366                       sel_get_name(method->method_name));
367              method->method_name = 0;
368            }
369        }
370    }
371
372  /* Add the methods to the class's method list.  */
373  list->method_next = class->methods;
374  class->methods = list;
375}
376
377
378Method_t
379class_get_instance_method(Class class, SEL op)
380{
381  return search_for_method_in_hierarchy(class, op);
382}
383
384Method_t
385class_get_class_method(MetaClass class, SEL op)
386{
387  return search_for_method_in_hierarchy(class, op);
388}
389
390
391/* Search for a method starting from the current class up its hierarchy.
392   Return a pointer to the method's method structure if found.  NULL
393   otherwise. */   
394
395static Method_t
396search_for_method_in_hierarchy (Class cls, SEL sel)
397{
398  Method_t method = NULL;
399  Class class;
400
401  if (! sel_is_mapped (sel))
402    return NULL;
403
404  /* Scan the method list of the class.  If the method isn't found in the
405     list then step to its super class. */
406  for (class = cls; ((! method) && class); class = class->super_class)
407    method = search_for_method_in_list (class->methods, sel);
408
409  return method;
410}
411
412
413
414/* Given a linked list of method and a method's name.  Search for the named
415   method's method structure.  Return a pointer to the method's method
416   structure if found.  NULL otherwise. */ 
417static Method_t
418search_for_method_in_list (MethodList_t list, SEL op)
419{
420  MethodList_t method_list = list;
421
422  if (! sel_is_mapped (op))
423    return NULL;
424
425  /* If not found then we'll search the list.  */
426  while (method_list)
427    {
428      int i;
429
430      /* Search the method list.  */
431      for (i = 0; i < method_list->method_count; ++i)
432        {
433          Method_t method = &method_list->method_list[i];
434
435          if (method->method_name)
436            if (method->method_name->sel_id == op->sel_id)
437              return method;
438        }
439
440      /* The method wasn't found.  Follow the link to the next list of
441         methods.  */
442      method_list = method_list->method_next;
443    }
444
445  return NULL;
446}
447
448static retval_t __objc_forward (id object, SEL sel, arglist_t args);
449
450static id
451__objc_word_forward (id rcv, SEL op, ...)
452{
453  void *args, *res;
454
455  args = __builtin_apply_args ();
456  res = __objc_forward (rcv, op, args);
457  if (res)
458    __builtin_return (res);
459  else
460    return res;
461}
462
463#if INVISIBLE_STRUCT_RETURN
464static __big
465#else
466static id
467#endif
468__objc_block_forward (id rcv, SEL op, ...)
469{
470  void *args, *res;
471
472  args = __builtin_apply_args ();
473  res = __objc_forward (rcv, op, args);
474  if (res)
475    __builtin_return (res);
476}
477
478
479/* This function is installed in the dispatch table for all methods which are
480   not implemented.  Thus, it is called when a selector is not recognized. */
481static retval_t
482__objc_forward (id object, SEL sel, arglist_t args)
483{
484  IMP imp;
485  static SEL frwd_sel = 0;
486  SEL err_sel;
487
488  /* first try if the object understands forward:: */
489  if (!frwd_sel)
490    frwd_sel = sel_get_any_uid("forward::");
491
492  if (__objc_responds_to (object, frwd_sel))
493    {
494      imp = get_imp(object->class_pointer, frwd_sel);
495      return (*imp)(object, frwd_sel, sel, args);
496    }
497
498  /* If the object recognizes the doesNotRecognize: method then we're going
499     to send it. */
500  err_sel = sel_get_any_uid ("doesNotRecognize:");
501  if (__objc_responds_to (object, err_sel))
502    {
503      imp = get_imp (object->class_pointer, err_sel);
504      return (*imp) (object, err_sel, sel);
505    }
506 
507  /* The object doesn't recognize the method.  Check for responding to
508     error:.  If it does then sent it. */
509  {
510    size_t strlen (const char*);
511    char msg[256 + strlen ((const char*)sel_get_name (sel))
512             + strlen ((const char*)object->class_pointer->name)];
513
514    sprintf (msg, "(%s) %s does not recognize %s",
515             (CLS_ISMETA(object->class_pointer)
516              ? "class"
517              : "instance" ),
518             object->class_pointer->name, sel_get_name (sel));
519
520    err_sel = sel_get_any_uid ("error:");
521    if (__objc_responds_to (object, err_sel))
522      {
523        imp = get_imp (object->class_pointer, err_sel);
524        return (*imp) (object, sel_get_any_uid ("error:"), msg);
525      }
526
527    /* The object doesn't respond to doesNotRecognize: or error:;  Therefore,
528       a default action is taken. */
529    fprintf (stderr, "fatal: %s\n", msg);
530    abort ();
531  }
532}
533
534void __objc_print_dtable_stats()
535{
536  int total = 0;
537  printf("memory usage: (%s)\n",
538#ifdef OBJC_SPARSE2
539         "2-level sparse arrays"
540#else
541         "3-level sparse arrays"
542#endif
543         );
544
545  printf("arrays: %d = %ld bytes\n", narrays, (int)narrays*sizeof(struct sarray));
546  total += narrays*sizeof(struct sarray);
547  printf("buckets: %d = %ld bytes\n", nbuckets, (int)nbuckets*sizeof(struct sbucket));
548  total += nbuckets*sizeof(struct sbucket);
549
550  printf("idxtables: %d = %ld bytes\n", idxsize, (int)idxsize*sizeof(void*));
551  total += idxsize*sizeof(void*);
552  printf("-----------------------------------\n");
553  printf("total: %d bytes\n", total);
554  printf("===================================\n");
555}
556
557
558
Note: See TracBrowser for help on using the repository browser.