#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 gen_rtx_REG(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;
IMP (*__objc_msg_forward)(SEL) = NULL;
static void __objc_send_initialize(Class);
static void __objc_install_dispatch_table_for_class (Class);
static void __objc_init_install_dtable(id, SEL);
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)
{
if (__objc_msg_forward)
{
IMP result;
if ((result = __objc_msg_forward (sel)))
return result;
}
else
{
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);
res = get_imp(class, sel);
}
else
{
res = __objc_get_forward_imp(sel);
}
}
return res;
}
__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);
}
__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)
{
__objc_init_install_dtable(receiver, op);
result = get_imp(receiver->class_pointer, op);
}
else
{
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);
}
static void
__objc_init_install_dtable(id receiver, SEL op)
{
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);
__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);
}
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);
}
}
}
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;
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);
}
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);
}
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;
for (class = cls; ((! method) && class); class = class->super_class)
method = search_for_method_in_list (class->methods, sel);
return method;
}
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;
}
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;
}
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
}
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);
}
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);
}
{
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);
}
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);
}
__inline__
struct sarray*
objc_get_uninstalled_dtable()
{
return __objc_uninstalled_dtable;
}