This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / gnu / usr.bin / cc / libobjc / sendmsg.c
CommitLineData
9bf86ebb
PR
1/* GNU Objective C Runtime message lookup
2 Copyright (C) 1993 Free Software Foundation, Inc.
3
4Author: Kresten Krab Thorup
5
6This file is part of GNU CC.
7
8GNU CC 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
12GNU CC 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
17You should have received a copy of the GNU General Public License along with
18 GNU CC; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, 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
30/* The uninstalled dispatch table */
31struct sarray* __objc_uninstalled_dtable = 0;
32
33/* Send +initialize to class */
34static void __objc_send_initialize(Class*);
35
36static void __objc_install_dispatch_table_for_class (Class*);
37
38/* Forward declare some functions */
39static void __objc_init_install_dtable(id, SEL);
40static id __objc_missing_method(id, SEL, ...);
41static Method_t search_for_method_in_hierarchy (Class* class, SEL sel);
42static Method_t search_for_method_in_list(MethodList_t list, SEL op);
43id nil_method(id, SEL, ...);
44
45id
46nil_method(id receiver, SEL op, ...)
47{
48 return receiver;
49}
50
51/* Given a class and selector, return the selector's implementation. */
52__inline__ IMP
53get_imp (Class* class, SEL sel)
54{
55 void* res = sarray_get (class->dtable, (size_t) sel);
56 if(res == __objc_init_install_dtable)
57 __objc_install_dispatch_table_for_class (class);
58 return sarray_get (class->dtable, (size_t) sel);
59}
60
61__inline__ BOOL
62__objc_responds_to (id object, SEL sel)
63{
64 return get_imp (object->class_pointer, sel) != __objc_missing_method;
65}
66
67/* This is the lookup function. All entries in the table are either a
68 valid method *or* one of `__objc_missing_method' which calls
69 forward:: etc, or `__objc_init_install_dtable' which installs the
70 real dtable */
71__inline__ IMP
72objc_msg_lookup(id receiver, SEL op)
73{
74 if(receiver)
75 return sarray_get(receiver->class_pointer->dtable, (sidx)op);
76 else
77 return nil_method;
78}
79
80IMP
81objc_msg_lookup_super (Super_t super, SEL sel)
82{
83 if (super->self)
84 return get_imp (super->class, sel);
85 else
86 return nil_method;
87}
88
89retval_t
90objc_msg_sendv(id object, SEL op, size_t frame_size, arglist_t arg_frame)
91{
92#ifdef __objc_frame_receiver
93 __objc_frame_receiver(arg_frame) = object;
94 __objc_frame_selector(arg_frame) = op;
95 return __builtin_apply((apply_t)get_imp(object->class_pointer, op),
96 arg_frame,
97 frame_size);
98#else
99#warning performv:: will not work
78ed81a3 100 va_list nothing;
101 (*_objc_error)(object, "objc_msg_sendv (performv::) not supported\n", nothing);
9bf86ebb
PR
102 return 0;
103#endif
104}
105
106void __objc_init_dispatch_tables()
107{
108 __objc_uninstalled_dtable
109 = sarray_new(200, __objc_init_install_dtable);
110}
111
112/* This one is a bit hairy. This function is installed in the
113 premature dispatch table, and thus called once for each class,
114 namely when the very first message is send to it. */
115
116static void __objc_init_install_dtable(id receiver, SEL op)
117{
118 __label__ allready_initialized;
119 IMP imp;
120 void* args;
121 void* result;
122
123 /* This may happen, if the programmer has taken the address of a
124 method before the dtable was initialized... too bad for him! */
125 if(receiver->class_pointer->dtable != __objc_uninstalled_dtable)
126 goto allready_initialized;
127
128 if(CLS_ISCLASS(receiver->class_pointer))
129 {
130 /* receiver is an ordinary object */
131 assert(CLS_ISCLASS(receiver->class_pointer));
132
133 /* install instance methods table */
134 __objc_install_dispatch_table_for_class (receiver->class_pointer);
135
136 /* call +initialize -- this will in turn install the factory
137 dispatch table if not already done :-) */
138 __objc_send_initialize(receiver->class_pointer);
139 }
140 else
141 {
142 /* receiver is a class object */
143 assert(CLS_ISCLASS((Class*)receiver));
144 assert(CLS_ISMETA(receiver->class_pointer));
145
146 /* Install real dtable for factory methods */
147 __objc_install_dispatch_table_for_class (receiver->class_pointer);
148
149 if(op != sel_get_uid ("initialize"))
150 __objc_send_initialize((Class*)receiver);
151 else
152 CLS_SETINITIALIZED((Class*)receiver);
153 }
154
155allready_initialized:
156
157 /* Get real method for this in newly installed dtable */
158 imp = get_imp(receiver->class_pointer, op);
159
160 args = __builtin_apply_args();
161 result = __builtin_apply((apply_t)imp, args, 96);
162 __builtin_return (result);
163
164}
165
166/* Install dummy table for class which causes the first message to
167 that class (or instances hereof) to be initialized properly */
168void __objc_install_premature_dtable(Class* class)
169{
170 assert(__objc_uninstalled_dtable);
171 class->dtable = __objc_uninstalled_dtable;
172}
173
174/* Send +initialize to class if not already done */
175static void __objc_send_initialize(Class* class)
176{
177 Method_t m;
178
179 /* This *must* be a class object */
180 assert(CLS_ISCLASS(class));
181 assert(!CLS_ISMETA(class));
182
183 if (!CLS_ISINITIALIZED(class))
184 {
185 CLS_SETINITIALIZED(class);
186 CLS_SETINITIALIZED(class->class_pointer);
187
188 if(class->super_class)
189 __objc_send_initialize(class->super_class);
190
191 {
192 MethodList_t method_list = class->class_pointer->methods;
193 SEL op = sel_register_name ("initialize");
194
195 /* If not found then we'll search the list. */
196 while (method_list)
197 {
198 int i;
199
200 /* Search the method list. */
201 for (i = 0; i < method_list->method_count; ++i)
202 {
203 Method_t method = &method_list->method_list[i];
204
205
206 if (method->method_name == op)
207 (*method->method_imp)((id) class, op);
208 }
209
210 /* The method wasn't found. Follow the link to the next list of
211 methods. */
212 method_list = method_list->method_next;
213 }
214 }
215 }
216}
217
218static void
219__objc_install_dispatch_table_for_class (Class* class)
220{
221 Class* super;
222 MethodList_t mlist;
223 int counter;
224
225 /* If the class has not yet had it's class links resolved, we must
226 re-compute all class links */
227 if(!CLS_ISRESOLV(class))
228 __objc_resolve_class_links();
229
230 super = class->super_class;
231
232 if (super != 0 && (super->dtable == __objc_uninstalled_dtable))
233 __objc_install_dispatch_table_for_class (super);
234
235 /* Allocate dtable if nessecary */
236 if (super == 0)
237 {
238 class->dtable = sarray_new (__objc_selector_max_index,
239 __objc_missing_method);
240 }
241 else
242 class->dtable = sarray_lazy_copy (super->dtable);
243
244 for (mlist = class->methods; mlist; mlist = mlist->method_next)
245 {
246 counter = mlist->method_count - 1;
247 while (counter >= 0)
248 {
249 Method_t method = &(mlist->method_list[counter]);
250 sarray_at_put (class->dtable,
251 (sidx) method->method_name,
252 method->method_imp);
253 counter -= 1;
254 }
255 }
256}
257
258void __objc_update_dispatch_table_for_class (Class* class)
259{
260 Class* next;
261 struct sarray* save;
262
263 /* not yet installed -- skip it */
264 if (class->dtable == __objc_uninstalled_dtable)
265 return;
266
267 save = class->dtable;
268 __objc_install_premature_dtable (class);
269 sarray_free (save);
270
271
272 if (class->subclass_list) /* Traverse subclasses */
273 for (next = class->subclass_list; next; next = next->sibling_class)
274 __objc_update_dispatch_table_for_class (next);
275}
276
277
278/* This function adds a method list to a class. This function is
279 typically called by another function specific to the run-time. As
280 such this function does not worry about thread safe issued.
281
282 This one is only called for categories. Class objects have their
283 methods installed rightaway, and their selectors are made into
284 SEL's by the function __objc_register_selectors_from_class. */
285void
286class_add_method_list (Class* class, MethodList_t list)
287{
288 int i;
289 static SEL initialize_sel = 0;
290 if (!initialize_sel)
291 initialize_sel = sel_register_name ("initialize");
292
293 /* Passing of a linked list is not allowed. Do multiple calls. */
294 assert (!list->method_next);
295
296 /* Check for duplicates. */
297 for (i = 0; i < list->method_count; ++i)
298 {
299 Method_t method = &list->method_list[i];
300
301 if (method->method_name) /* Sometimes these are NULL */
302 {
303 /* This is where selector names are transmogriffed to SEL's */
304 method->method_name = sel_register_name ((char*)method->method_name);
305
306 if (search_for_method_in_list (class->methods, method->method_name)
307 && method->method_name != initialize_sel)
308 {
309 /* Duplication. Print a error message an change the method name
310 to NULL. */
311 fprintf (stderr, "attempt to add a existing method: %s\n",
312 sel_get_name(method->method_name));
313 method->method_name = 0;
314 }
315 }
316 }
317
318 /* Add the methods to the class's method list. */
319 list->method_next = class->methods;
320 class->methods = list;
321}
322
323
324Method_t
325class_get_instance_method(Class* class, SEL op)
326{
327 return search_for_method_in_hierarchy(class, op);
328}
329
330Method_t
331class_get_class_method(MetaClass* class, SEL op)
332{
333 return search_for_method_in_hierarchy(class, op);
334}
335
336
337/* Search for a method starting from the current class up its hierarchy.
338 Return a pointer to the method's method structure if found. NULL
339 otherwise. */
340
341static Method_t
342search_for_method_in_hierarchy (Class* cls, SEL sel)
343{
344 Method_t method = NULL;
345 Class* class;
346
347 if (! sel_is_mapped (sel))
348 return NULL;
349
350 /* Scan the method list of the class. If the method isn't found in the
351 list then step to its super class. */
352 for (class = cls; ((! method) && class); class = class->super_class)
353 method = search_for_method_in_list (class->methods, sel);
354
355 return method;
356}
357
358
359
360/* Given a linked list of method and a method's name. Search for the named
361 method's method structure. Return a pointer to the method's method
362 structure if found. NULL otherwise. */
363static Method_t
364search_for_method_in_list (MethodList_t list, SEL op)
365{
366 MethodList_t method_list = list;
367
368 if (! sel_is_mapped (op))
369 return NULL;
370
371 /* If not found then we'll search the list. */
372 while (method_list)
373 {
374 int i;
375
376 /* Search the method list. */
377 for (i = 0; i < method_list->method_count; ++i)
378 {
379 Method_t method = &method_list->method_list[i];
380
381 if (method->method_name)
382 if (method->method_name == op)
383 return method;
384 }
385
386 /* The method wasn't found. Follow the link to the next list of
387 methods. */
388 method_list = method_list->method_next;
389 }
390
391 return NULL;
392}
393
394
395/* This fuction is installed in the dispatch table for all methods which are
396 not implemented. Thus, it is called when a selector is not recognized. */
397static id
398__objc_missing_method (id object, SEL sel, ...)
399{
400 IMP imp;
401 SEL frwd_sel;
402 SEL err_sel;
403
404 /* first try if the object understands forward:: */
405 frwd_sel = sel_get_uid("forward::");
406 imp = get_imp(object->class_pointer, frwd_sel);
407 if(imp != __objc_missing_method)
408 {
409 void *result, *args = __builtin_apply_args();
410 result = (*imp)(object, frwd_sel, sel, args);
411 __builtin_return(result);
412 }
413
414 /* If the object recognizes the doesNotRecognize: method then we're going
415 to send it. */
416 err_sel = sel_get_uid ("doesNotRecognize:");
417 imp = get_imp (object->class_pointer, err_sel);
418 if (imp != __objc_missing_method)
419 {
420 return (*imp) (object, err_sel, sel);
421 }
422
423 /* The object doesn't recognize the method. Check for responding to
424 error:. If it does then sent it. */
425 {
426 char msg[256 + strlen ((char*)sel_get_name (sel))
427 + strlen ((char*)object->class_pointer->name)];
428
429 sprintf (msg, "(%s) %s does not recognize %s",
430 (CLS_ISMETA(object->class_pointer)
431 ? "class"
432 : "instance" ),
433 object->class_pointer->name, sel_get_name (sel));
434
435 err_sel = sel_get_uid ("error:");
436 imp = get_imp (object->class_pointer, err_sel);
437 if (imp != __objc_missing_method)
438 return (*imp) (object, sel_get_uid ("error:"), msg);
439
440 /* The object doesn't respond to doesNotRecognize: or error:; Therefore,
441 a default action is taken. */
442 fprintf (stderr, "fatal: %s\n", msg);
443 abort ();
444 }
445}
446
447void __objc_print_dtable_stats()
448{
449 int total = 0;
450 printf("memory usage: (%s)\n",
451#ifdef OBJC_SPARSE2
452 "2-level sparse arrays"
453#else
454 "3-level sparse arrays"
455#endif
456 );
457
458 printf("arrays: %d = %d bytes\n", narrays, narrays*sizeof(struct sarray));
459 total += narrays*sizeof(struct sarray);
460 printf("buckets: %d = %d bytes\n", nbuckets, nbuckets*sizeof(struct sbucket));
461 total += nbuckets*sizeof(struct sbucket);
462
463 printf("idxtables: %d = %d bytes\n", idxsize, idxsize*sizeof(void*));
464 total += idxsize*sizeof(void*);
465 printf("-----------------------------------\n");
466 printf("total: %d bytes\n", total);
467 printf("===================================\n");
468}
469
470
471