Copyright (C) 1993, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
Contributed by Kresten Krab Thorup
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 2, or (at your option) any later version.
GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
GNU CC; see the file COPYING. If not, write to the Free Software
Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
GCC to produce an executable, this does not cause the resulting executable
to be covered by the GNU General Public License. This exception does not
however invalidate any other reasons why the executable file might be
covered by the GNU General Public License. */
#include "tconfig.h"
#include "runtime.h"
#include "sarray.h"
#include "encoding.h"
#include "runtime-info.h"
#define gen_rtx(args...) 1
#define gen_rtx_MEM(args...) 1
#define rtx int
#if !defined(STRUCT_VALUE) || STRUCT_VALUE == 0
#define INVISIBLE_STRUCT_RETURN 1
#else
#define INVISIBLE_STRUCT_RETURN 0
#endif
struct sarray* __objc_uninstalled_dtable = 0;
static void __objc_send_initialize(Class);
static void __objc_install_dispatch_table_for_class (Class);
static void __objc_init_install_dtable(id, SEL);
return type for the selector.
__objc_block_forward for structures.
__objc_double_forward for floats/doubles.
__objc_word_forward for pointers or types that fit in registers.
*/
static double __objc_double_forward(id, SEL, ...);
static id __objc_word_forward(id, SEL, ...);
typedef struct { id many[8]; } __big;
#if INVISIBLE_STRUCT_RETURN
static __big
#else
static id
#endif
__objc_block_forward(id, SEL, ...);
static Method_t search_for_method_in_hierarchy (Class class, SEL sel);
Method_t search_for_method_in_list(MethodList_t list, SEL op);
id nil_method(id, SEL, ...);
__inline__
IMP
__objc_get_forward_imp (SEL sel)
{
const char *t = sel->sel_types;
if (t && (*t == '[' || *t == '(' || *t == '{')
#ifdef OBJC_MAX_STRUCT_BY_VALUE
&& objc_sizeof_type(t) > OBJC_MAX_STRUCT_BY_VALUE
#endif
)
return (IMP)__objc_block_forward;
else if (t && (*t == 'f' || *t == 'd'))
return (IMP)__objc_double_forward;
else
return (IMP)__objc_word_forward;
}
__inline__
IMP
get_imp (Class class, SEL sel)
{
void* res = sarray_get_safe (class->dtable, (size_t) sel->sel_id);
if (res == 0)
{
if(class->dtable == __objc_uninstalled_dtable)
{
objc_mutex_lock(__objc_runtime_mutex);
__objc_install_dispatch_table_for_class (class);
objc_mutex_unlock(__objc_runtime_mutex);
and get the real method */
res = get_imp(class, sel);
}
else
{
method just doesn't exist for the class.
Return the forwarding implementation. */
res = __objc_get_forward_imp(sel);
}
}
return res;
}
object implements the selector otherwise NO. Does not check if the
method can be forwarded. */
__inline__
BOOL
__objc_responds_to (id object, SEL sel)
{
void* res;
if (object->class_pointer->dtable == __objc_uninstalled_dtable)
{
objc_mutex_lock(__objc_runtime_mutex);
__objc_install_dispatch_table_for_class (object->class_pointer);
objc_mutex_unlock(__objc_runtime_mutex);
}
res = sarray_get_safe (object->class_pointer->dtable, (size_t) sel->sel_id);
return (res != 0);
}
valid method *or* zero. If zero then either the dispatch table
needs to be installed or it doesn't exist and forwarding is attempted. */
__inline__
IMP
objc_msg_lookup(id receiver, SEL op)
{
IMP result;
if(receiver)
{
result = sarray_get_safe (receiver->class_pointer->dtable,
(sidx)op->sel_id);
if (result == 0)
{
if(receiver->class_pointer->dtable == __objc_uninstalled_dtable)
{
This happens on the very first method call to the class. */
__objc_init_install_dtable(receiver, op);
result = get_imp(receiver->class_pointer, op);
}
else
{
method just doesn't exist for the class.
Attempt to forward the method. */
result = __objc_get_forward_imp(op);
}
}
return result;
}
else
return nil_method;
}
IMP
objc_msg_lookup_super (Super_t super, SEL sel)
{
if (super->self)
return get_imp (super->class, sel);
else
return nil_method;
}
int method_get_sizeof_arguments (Method*);
retval_t
objc_msg_sendv(id object, SEL op, arglist_t arg_frame)
{
Method* m = class_get_instance_method(object->class_pointer, op);
const char *type;
*((id*)method_get_first_argument (m, arg_frame, &type)) = object;
*((SEL*)method_get_next_argument (arg_frame, &type)) = op;
return __builtin_apply((apply_t)m->method_imp,
arg_frame,
method_get_sizeof_arguments (m));
}
void
__objc_init_dispatch_tables()
{
__objc_uninstalled_dtable
= sarray_new(200, 0);
}
dispatch table needs to be installed; thus it is called once
for each class, namely when the very first message is sent to it. */
static void
__objc_init_install_dtable(id receiver, SEL op)
{
method before the dtable was initialized... too bad for him! */
if(receiver->class_pointer->dtable != __objc_uninstalled_dtable)
return;
objc_mutex_lock(__objc_runtime_mutex);
if(CLS_ISCLASS(receiver->class_pointer))
{
assert(CLS_ISCLASS(receiver->class_pointer));
__objc_install_dispatch_table_for_class (receiver->class_pointer);
dispatch table if not already done :-) */
__objc_send_initialize(receiver->class_pointer);
}
else
{
assert(CLS_ISCLASS((Class)receiver));
assert(CLS_ISMETA(receiver->class_pointer));
__objc_install_dispatch_table_for_class (receiver->class_pointer);
__objc_send_initialize((Class)receiver);
}
objc_mutex_unlock(__objc_runtime_mutex);
}
that class (or instances hereof) to be initialized properly */
void
__objc_install_premature_dtable(Class class)
{
assert(__objc_uninstalled_dtable);
class->dtable = __objc_uninstalled_dtable;
}
static void
__objc_send_initialize(Class class)
{
assert(CLS_ISCLASS(class));
assert(!CLS_ISMETA(class));
if (!CLS_ISINITIALIZED(class))
{
CLS_SETINITIALIZED(class);
CLS_SETINITIALIZED(class->class_pointer);
__objc_generate_gc_type_description (class);
if(class->super_class)
__objc_send_initialize(class->super_class);
{
SEL op = sel_register_name ("initialize");
IMP imp = 0;
MethodList_t method_list = class->class_pointer->methods;
while (method_list) {
int i;
Method_t method;
for (i = 0; i< method_list->method_count; i++) {
method = &(method_list->method_list[i]);
if (method->method_name
&& method->method_name->sel_id == op->sel_id) {
imp = method->method_imp;
break;
}
}
if (imp)
break;
method_list = method_list->method_next;
}
if (imp)
(*imp)((id)class, op);
}
}
}
order of the lists. Since methods added by categories are before the methods
of class in the methods list, this allows categories to substitute methods
declared in class. However if more than one category replaces the same
method nothing is guaranteed about what method will be used.
Assumes that __objc_runtime_mutex is locked down. */
static void
__objc_install_methods_in_dtable (Class class, MethodList_t method_list)
{
int i;
if (!method_list)
return;
if (method_list->method_next)
__objc_install_methods_in_dtable (class, method_list->method_next);
for (i = 0; i < method_list->method_count; i++)
{
Method_t method = &(method_list->method_list[i]);
sarray_at_put_safe (class->dtable,
(sidx) method->method_name->sel_id,
method->method_imp);
}
}
static void
__objc_install_dispatch_table_for_class (Class class)
{
Class super;
re-compute all class links */
if(!CLS_ISRESOLV(class))
__objc_resolve_class_links();
super = class->super_class;
if (super != 0 && (super->dtable == __objc_uninstalled_dtable))
__objc_install_dispatch_table_for_class (super);
if (super == 0)
{
objc_mutex_lock(__objc_runtime_mutex);
class->dtable = sarray_new (__objc_selector_max_index, 0);
objc_mutex_unlock(__objc_runtime_mutex);
}
else
class->dtable = sarray_lazy_copy (super->dtable);
__objc_install_methods_in_dtable (class, class->methods);
}
void
__objc_update_dispatch_table_for_class (Class class)
{
Class next;
struct sarray *arr;
if (class->dtable == __objc_uninstalled_dtable)
return;
objc_mutex_lock(__objc_runtime_mutex);
arr = class->dtable;
__objc_install_premature_dtable (class);
sarray_free (arr);
__objc_install_dispatch_table_for_class (class);
if (class->subclass_list)
for (next = class->subclass_list; next; next = next->sibling_class)
__objc_update_dispatch_table_for_class (next);
objc_mutex_unlock(__objc_runtime_mutex);
}
typically called by another function specific to the run-time. As
such this function does not worry about thread safe issues.
This one is only called for categories. Class objects have their
methods installed right away, and their selectors are made into
SEL's by the function __objc_register_selectors_from_class. */
void
class_add_method_list (Class class, MethodList_t list)
{
int i;
assert (!list->method_next);
for (i = 0; i < list->method_count; ++i)
{
Method_t method = &list->method_list[i];
if (method->method_name)
{
method->method_name =
sel_register_typed_name ((const char*)method->method_name,
method->method_types);
}
}
list->method_next = class->methods;
class->methods = list;
__objc_update_dispatch_table_for_class (class);
}
Method_t
class_get_instance_method(Class class, SEL op)
{
return search_for_method_in_hierarchy(class, op);
}
Method_t
class_get_class_method(MetaClass class, SEL op)
{
return search_for_method_in_hierarchy(class, op);
}
Return a pointer to the method's method structure if found. NULL
otherwise. */
static Method_t
search_for_method_in_hierarchy (Class cls, SEL sel)
{
Method_t method = NULL;
Class class;
if (! sel_is_mapped (sel))
return NULL;
list then step to its super class. */
for (class = cls; ((! method) && class); class = class->super_class)
method = search_for_method_in_list (class->methods, sel);
return method;
}
method's method structure. Return a pointer to the method's method
structure if found. NULL otherwise. */
Method_t
search_for_method_in_list (MethodList_t list, SEL op)
{
MethodList_t method_list = list;
if (! sel_is_mapped (op))
return NULL;
while (method_list)
{
int i;
for (i = 0; i < method_list->method_count; ++i)
{
Method_t method = &method_list->method_list[i];
if (method->method_name)
if (method->method_name->sel_id == op->sel_id)
return method;
}
methods. */
method_list = method_list->method_next;
}
return NULL;
}
static retval_t __objc_forward (id object, SEL sel, arglist_t args);
static id
__objc_word_forward (id rcv, SEL op, ...)
{
void *args, *res;
args = __builtin_apply_args ();
res = __objc_forward (rcv, op, args);
if (res)
__builtin_return (res);
else
return res;
}
architectural differences on some processors. i386s for
example which uses a floating point stack versus general
registers for floating point numbers. This forward routine
makes sure that GCC restores the proper return values */
static double
__objc_double_forward (id rcv, SEL op, ...)
{
void *args, *res;
args = __builtin_apply_args ();
res = __objc_forward (rcv, op, args);
__builtin_return (res);
}
#if INVISIBLE_STRUCT_RETURN
static __big
#else
static id
#endif
__objc_block_forward (id rcv, SEL op, ...)
{
void *args, *res;
args = __builtin_apply_args ();
res = __objc_forward (rcv, op, args);
if (res)
__builtin_return (res);
else
#if INVISIBLE_STRUCT_RETURN
return (__big) {{0, 0, 0, 0, 0, 0, 0, 0}};
#else
return nil;
#endif
}
not implemented. Thus, it is called when a selector is not recognized. */
static retval_t
__objc_forward (id object, SEL sel, arglist_t args)
{
IMP imp;
static SEL frwd_sel = 0;
SEL err_sel;
if (!frwd_sel)
frwd_sel = sel_get_any_uid("forward::");
if (__objc_responds_to (object, frwd_sel))
{
imp = get_imp(object->class_pointer, frwd_sel);
return (*imp)(object, frwd_sel, sel, args);
}
to send it. */
err_sel = sel_get_any_uid ("doesNotRecognize:");
if (__objc_responds_to (object, err_sel))
{
imp = get_imp (object->class_pointer, err_sel);
return (*imp) (object, err_sel, sel);
}
error:. If it does then sent it. */
{
size_t strlen (const char*);
char msg[256 + strlen ((const char*)sel_get_name (sel))
+ strlen ((const char*)object->class_pointer->name)];
sprintf (msg, "(%s) %s does not recognize %s",
(CLS_ISMETA(object->class_pointer)
? "class"
: "instance" ),
object->class_pointer->name, sel_get_name (sel));
err_sel = sel_get_any_uid ("error:");
if (__objc_responds_to (object, err_sel))
{
imp = get_imp (object->class_pointer, err_sel);
return (*imp) (object, sel_get_any_uid ("error:"), msg);
}
a default action is taken. */
objc_error (object, OBJC_ERR_UNIMPLEMENTED, "%s\n", msg);
return 0;
}
}
void
__objc_print_dtable_stats()
{
int total = 0;
objc_mutex_lock(__objc_runtime_mutex);
#ifdef OBJC_SPARSE2
printf("memory usage: (%s)\n", "2-level sparse arrays");
#else
printf("memory usage: (%s)\n", "3-level sparse arrays");
#endif
printf("arrays: %d = %ld bytes\n", narrays,
(long)narrays*sizeof(struct sarray));
total += narrays*sizeof(struct sarray);
printf("buckets: %d = %ld bytes\n", nbuckets,
(long)nbuckets*sizeof(struct sbucket));
total += nbuckets*sizeof(struct sbucket);
printf("idxtables: %d = %ld bytes\n", idxsize, (long)idxsize*sizeof(void*));
total += idxsize*sizeof(void*);
printf("-----------------------------------\n");
printf("total: %d bytes\n", total);
printf("===================================\n");
objc_mutex_unlock(__objc_runtime_mutex);
}
If a class' dispatch table points to __objc_uninstalled_dtable
then that means it needs its dispatch table to be installed. */
__inline__
struct sarray*
objc_get_uninstalled_dtable()
{
return __objc_uninstalled_dtable;
}