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