/* rua_obj.c Progs Obj runtime support Copyright (C) 2001 Bill Currie Author: Bill Currie Date: 2002/7/21 This program 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 of the License, or (at your option) any later version. This program 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 this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #if defined(_WIN32) && defined(HAVE_MALLOC_H) #include #endif #include "qfalloca.h" #include "QF/cvar.h" #include "QF/darray.h" #include "QF/dstring.h" #include "QF/hash.h" #include "QF/mathlib.h" #include "QF/progs.h" #include "QF/ruamoko.h" #include "QF/sys.h" #include "QF/progs/pr_obj.h" #include "QF/progs/pr_type.h" #include "compat.h" #include "rua_internal.h" #define always_inline inline __attribute__((__always_inline__)) /* Macros to help with setting up to call a function, and cleaning up * afterwards. The problem is that PR_CallFunction saves the CURRENT stack * pointer, which has been adjusted by PR_SetupParams in order to push * the function arguments. * * RUA_CALL_BEGIN and RUA_CALL_END must be used in pairs and this is enforced * by the unbalanced {}s in the macros. */ #define RUA_CALL_BEGIN(pr, argc) \ { \ pr_ptr_t saved_stack = 0; \ int call_depth = (pr)->pr_depth; \ if ((pr)->globals.stack) { \ saved_stack = *(pr)->globals.stack; \ } \ PR_SetupParams (pr, argc, 1); \ (pr)->pr_argc = argc; #define RUA_CALL_END(pr, imp) \ if (PR_CallFunction ((pr), (imp), (pr)->pr_return)) { \ /* the call is to a progs function so a frame was pushed, */ \ /* ensure the stack pointer is restored on return */ \ /* if there's no stack, then the following is effectively a */ \ /* noop */ \ (pr)->pr_stack[call_depth].stack_ptr = saved_stack; \ } else if ((pr)->globals.stack) { \ /* the call was to a builtin, restore the stack */ \ *(pr)->globals.stack = saved_stack; \ } \ } // number of selectors to allocate at once (to minimize memory waste) #define SELECTOR_BLOCK 64 typedef struct obj_list_s { struct obj_list_s *next; void *data; } obj_list; typedef struct listset_s DARRAY_TYPE (obj_list *) listset_t; typedef struct dtable_s { struct dtable_s *next; struct dtable_s **prev; size_t size; pr_func_t *imp; } dtable_t; typedef struct class_tree { pr_class_t *class; obj_list *subclasses; } class_tree; typedef struct treeset_s DARRAY_TYPE (class_tree *) treeset_t; typedef struct probj_resources_s { progs_t *pr; unsigned selector_index; unsigned selector_index_max; pr_sel_t *selector_block; int available_selectors; obj_list **selector_sels; pr_string_t *selector_names; pr_int_t *selector_argc; PR_RESMAP (dtable_t) dtables; dtable_t *dtable_list; pr_func_t obj_forward; pr_sel_t *forward_selector; dstring_t *msg; hashtab_t *selector_hash; hashtab_t *classes; hashtab_t *protocols; hashtab_t *load_methods; listset_t obj_list_sets; obj_list *obj_list_free_list; obj_list *unresolved_classes; obj_list *unclaimed_categories; obj_list *uninitialized_statics; obj_list *unclaimed_proto_list; obj_list *module_list; obj_list *class_tree_list; class_tree *class_tree_free_list; treeset_t class_tree_sets; } probj_t; static dtable_t * dtable_new (probj_t *probj) { dtable_t *dtable = PR_RESNEW (probj->dtables); dtable->next = probj->dtable_list; dtable->prev = &probj->dtable_list; if (probj->dtable_list) { probj->dtable_list->prev = &dtable->next; } probj->dtable_list = dtable; return dtable; } static void dtable_reset (probj_t *probj) { PR_RESRESET (probj->dtables); probj->dtable_list = 0; } static inline dtable_t * dtable_get (probj_t *probj, int index) { return PR_RESGET (probj->dtables, index); } static inline int __attribute__((pure)) dtable_index (probj_t *probj, dtable_t *dtable) { return PR_RESINDEX (probj->dtables, dtable); } static always_inline dtable_t * __attribute__((pure)) get_dtable (probj_t *probj, const char *name, int index) { dtable_t *dtable = dtable_get (probj, index); if (!dtable) { PR_RunError (probj->pr, "invalid dtable index in %s", name); } return dtable; } static obj_list * obj_list_new (probj_t *probj) { int i; obj_list *l; if (!probj->obj_list_free_list) { probj->obj_list_free_list = calloc (128, sizeof (obj_list)); for (i = 0; i < 127; i++) { __auto_type next = &probj->obj_list_free_list[i + 1]; probj->obj_list_free_list[i].next = next; } DARRAY_APPEND (&probj->obj_list_sets, probj->obj_list_free_list); } l = probj->obj_list_free_list; probj->obj_list_free_list = l->next; l->next = 0; return l; } static void obj_list_free (probj_t *probj, obj_list *l) { obj_list *e; if (!l) return; for (e = l; e->next; e = e->next) ; e->next = probj->obj_list_free_list; probj->obj_list_free_list = l; } static inline obj_list * list_cons (probj_t *probj, void *data, obj_list *next) { obj_list *l = obj_list_new (probj); l->data = data; l->next = next; return l; } static inline void list_remove (probj_t *probj, obj_list **list) { if ((*list)->next) { obj_list *l = *list; *list = (*list)->next; l->next = 0; obj_list_free (probj, l); } else { obj_list_free (probj, *list); *list = 0; } } static class_tree * class_tree_new (probj_t *probj) { int i; class_tree *t; if (!probj->class_tree_free_list) { probj->class_tree_free_list = calloc (128, sizeof (class_tree)); for (i = 0; i < 127; i++) { class_tree *x = &probj->class_tree_free_list[i]; x->subclasses = (obj_list *) (x + 1); } DARRAY_APPEND (&probj->class_tree_sets, probj->class_tree_free_list); } t = probj->class_tree_free_list; probj->class_tree_free_list = (class_tree *) t->subclasses; t->subclasses = 0; return t; } static int class_is_subclass_of_class (probj_t *probj, pr_class_t *class, pr_class_t *superclass) { while (class) { if (class == superclass) return 1; if (!class->super_class) break; class = Hash_Find (probj->classes, PR_GetString (probj->pr, class->super_class)); } return 0; } static class_tree * create_tree_of_subclasses_inherited_from (probj_t *probj, pr_class_t *bottom, pr_class_t *upper) { progs_t *pr = probj->pr; const char *super_class = PR_GetString (pr, bottom->super_class); pr_class_t *superclass; class_tree *tree, *prev; superclass = bottom->super_class ? Hash_Find (probj->classes, super_class) : 0; tree = prev = class_tree_new (probj); prev->class = bottom; while (superclass != upper) { tree = class_tree_new (probj); tree->class = superclass; tree->subclasses = list_cons (probj, prev, tree->subclasses); super_class = PR_GetString (pr, superclass->super_class); superclass = (superclass->super_class ? Hash_Find (probj->classes, super_class) : 0); prev = tree; } return tree; } static class_tree * _obj_tree_insert_class (probj_t *probj, class_tree *tree, pr_class_t *class) { progs_t *pr = probj->pr; obj_list *subclasses; class_tree *new_tree; if (!tree) return create_tree_of_subclasses_inherited_from (probj, class, 0); if (class == tree->class) return tree; if ((class->super_class ? Hash_Find (probj->classes, PR_GetString (pr, class->super_class)) : 0) == tree->class) { obj_list *list = tree->subclasses; class_tree *node; while (list) { if (((class_tree *) list->data)->class == class) return tree; list = list->next; } node = class_tree_new (probj); node->class = class; tree->subclasses = list_cons (probj, node, tree->subclasses); return tree; } if (!class_is_subclass_of_class (probj, class, tree->class)) return 0; for (subclasses = tree->subclasses; subclasses; subclasses = subclasses->next) { pr_class_t *aclass = ((class_tree *)subclasses->data)->class; if (class_is_subclass_of_class (probj, class, aclass)) { subclasses->data = _obj_tree_insert_class (probj, subclasses->data, class); return tree; } } new_tree = create_tree_of_subclasses_inherited_from (probj, class, tree->class); tree->subclasses = list_cons (probj, new_tree, tree->subclasses); return tree; } static void obj_tree_insert_class (probj_t *probj, pr_class_t *class) { obj_list *list_node; class_tree *tree; list_node = probj->class_tree_list; while (list_node) { tree = _obj_tree_insert_class (probj, list_node->data, class); if (tree) { list_node->data = tree; break; } else { list_node = list_node->next; } } if (!list_node) { tree = _obj_tree_insert_class (probj, 0, class); probj->class_tree_list = list_cons (probj, tree, probj->class_tree_list); } } static void obj_create_classes_tree (probj_t *probj, pr_module_t *module) { progs_t *pr = probj->pr; pr_symtab_t *symtab = &G_STRUCT (pr, pr_symtab_t, module->symtab); int i; for (i = 0; i < symtab->cls_def_cnt; i++) { pr_class_t *class = &G_STRUCT (pr, pr_class_t, symtab->defs[i]); obj_tree_insert_class (probj, class); } } static void obj_destroy_class_tree_node (probj_t *probj, class_tree *tree, int level) { tree->subclasses = (obj_list *) probj->class_tree_free_list; probj->class_tree_free_list = tree; } static void obj_preorder_traverse (probj_t *probj, class_tree *tree, int level, void (*func) (probj_t *, class_tree *, int)) { obj_list *node; func (probj, tree, level); for (node = tree->subclasses; node; node = node->next) obj_preorder_traverse (probj, node->data, level + 1, func); } static void obj_postorder_traverse (probj_t *probj, class_tree *tree, int level, void (*func) (probj_t *, class_tree *, int)) { obj_list *node; for (node = tree->subclasses; node; node = node->next) obj_postorder_traverse (probj, node->data, level + 1, func); func (probj, tree, level); } static const char * selector_get_key (const void *s, void *_probj) { __auto_type probj = (probj_t *) _probj; return PR_GetString (probj->pr, probj->selector_names[(intptr_t) s]); } static const char * class_get_key (const void *c, void *_probj) { __auto_type probj = (probj_t *) _probj; return PR_GetString (probj->pr, ((pr_class_t *)c)->name); } static const char * protocol_get_key (const void *p, void *_probj) { __auto_type probj = (probj_t *) _probj; return PR_GetString (probj->pr, ((pr_protocol_t *)p)->protocol_name); } static uintptr_t load_methods_get_hash (const void *m, void *_probj) { return (uintptr_t) m; } static int load_methods_compare (const void *m1, const void *m2, void *_probj) { return m1 == m2; } static inline int sel_eq (pr_sel_t *s1, pr_sel_t *s2) { if (!s1 || !s2) return s1 == s2; return s1->sel_id == s2->sel_id; } static int object_is_instance (probj_t *probj, pr_id_t *object) { progs_t *pr = probj->pr; pr_class_t *class; if (object) { class = &G_STRUCT (pr, pr_class_t, object->class_pointer); return PR_CLS_ISCLASS (class); } return 0; } static pr_string_t object_get_class_name (probj_t *probj, pr_id_t *object) { progs_t *pr = probj->pr; pr_class_t *class; if (object) { class = &G_STRUCT (pr, pr_class_t, object->class_pointer); if (PR_CLS_ISCLASS (class)) { R_INT (pr) = class->name; return class->name; } if (PR_CLS_ISMETA (class)) { R_INT (pr) = ((pr_class_t *)object)->name; return ((pr_class_t *)object)->name; } } return PR_SetString (pr, "Nil"); } //==================================================================== static void finish_class (probj_t *probj, pr_class_t *class, pr_ptr_t object_ptr) { progs_t *pr = probj->pr; pr_class_t *meta = &G_STRUCT (pr, pr_class_t, class->class_pointer); pr_class_t *val; meta->class_pointer = object_ptr; if (class->super_class) { const char *super_class = PR_GetString (pr, class->super_class); const char *class_name = PR_GetString (pr, class->name); val = Hash_Find (probj->classes, super_class); if (!val) PR_Error (pr, "broken class %s: super class %s not found", class_name, super_class); meta->super_class = val->class_pointer; class->super_class = PR_SetPointer (pr, val); } else { pr_ptr_t *ml = &meta->methods; while (*ml) ml = &G_STRUCT (pr, pr_method_list_t, *ml).method_next; *ml = class->methods; } Sys_MaskPrintf (SYS_rua_obj, " %x %x %x\n", meta->class_pointer, meta->super_class, class->super_class); } //==================================================================== static int add_sel_name (probj_t *probj, const char *name) { int ind = ++probj->selector_index; int size, i; if (probj->selector_index >= probj->selector_index_max) { size = probj->selector_index_max + 128; probj->selector_sels = realloc (probj->selector_sels, size * sizeof (obj_list *)); probj->selector_names = realloc (probj->selector_names, size * sizeof (pr_string_t)); probj->selector_argc = realloc (probj->selector_argc, size * sizeof (pr_int_t)); for (i = probj->selector_index_max; i < size; i++) { probj->selector_sels[i] = 0; probj->selector_names[i] = 0; probj->selector_argc[i] = 0; } probj->selector_index_max = size; } probj->selector_names[ind] = PR_SetString (probj->pr, name); return ind; } static pr_sel_t * sel_register_typed_name (probj_t *probj, const char *name, const char *types, pr_sel_t *sel) { progs_t *pr = probj->pr; intptr_t index; int is_new = 0; obj_list *l; index = (intptr_t) Hash_Find (probj->selector_hash, name); if (index) { for (l = probj->selector_sels[index]; l; l = l->next) { pr_sel_t *s = l->data; if (!types || !s->sel_types) { if (!s->sel_types && !types) { if (sel) { sel->sel_id = index; goto done; } return s; } } else if (strcmp (PR_GetString (pr, s->sel_types), types) == 0) { if (sel) { sel->sel_id = index; goto done; } return s; } } } else { Sys_MaskPrintf (SYS_rua_obj, " Registering SEL %s %s\n", name, types); index = add_sel_name (probj, name); is_new = 1; } if (!sel) { if (!probj->available_selectors) { probj->available_selectors = SELECTOR_BLOCK; sel = PR_Zone_Malloc (pr, SELECTOR_BLOCK*sizeof (pr_sel_t)); probj->selector_block = sel; } sel = &probj->selector_block[--probj->available_selectors]; } sel->sel_id = index; sel->sel_types = PR_SetString (pr, types); l = obj_list_new (probj); l->data = sel; l->next = probj->selector_sels[index]; probj->selector_sels[index] = l; if (sel->sel_types && pr->type_encodings) { const char *enc = PR_GetString (pr, sel->sel_types); __auto_type type = (qfot_type_t *) Hash_Find (pr->type_hash, enc); if (type->meta != ty_basic || type->type != ev_func) { PR_RunError (pr, "selector type encoing is not a function"); } probj->selector_argc[index] = type->func.num_params; } if (is_new) Hash_Add (probj->selector_hash, (void *) index); done: Sys_MaskPrintf (SYS_rua_obj, " %d @ %x\n", sel->sel_id, PR_SetPointer (pr, sel)); return sel; } static pr_sel_t * sel_register_name (probj_t *probj, const char *name) { return sel_register_typed_name (probj, name, "", 0); } static void obj_register_selectors_from_description_list (probj_t *probj, pr_method_description_list_t *method_list) { progs_t *pr = probj->pr; int i; if (!method_list) { return; } for (i = 0; i < method_list->count; i++) { pr_method_description_t *method = &method_list->list[i]; const char *name = PR_GetString (pr, method->name); const char *types = PR_GetString (pr, method->types); pr_sel_t *sel = sel_register_typed_name (probj, name, types, 0); method->name = PR_SetPointer (pr, sel); } } static void obj_register_selectors_from_list (probj_t *probj, pr_method_list_t *method_list) { progs_t *pr = probj->pr; int i; for (i = 0; i < method_list->method_count; i++) { pr_method_t *method = &method_list->method_list[i]; const char *name = PR_GetString (pr, method->method_name); const char *types = PR_GetString (pr, method->method_types); pr_sel_t *sel = sel_register_typed_name (probj, name, types, 0); method->method_name = PR_SetPointer (pr, sel); } } static void obj_register_selectors_from_class (probj_t *probj, pr_class_t *class) { progs_t *pr = probj->pr; pr_method_list_t *method_list = &G_STRUCT (pr, pr_method_list_t, class->methods); while (method_list) { obj_register_selectors_from_list (probj, method_list); method_list = &G_STRUCT (pr, pr_method_list_t, method_list->method_next); } } static void obj_init_protocols (probj_t *probj, pr_protocol_list_t *protos); static void obj_init_protocol (probj_t *probj, pr_class_t *proto_class, pr_protocol_t *proto) { progs_t *pr = probj->pr; if (!proto->class_pointer) { const char *protocol_name = PR_GetString (pr, proto->protocol_name); proto->class_pointer = PR_SetPointer (pr, proto_class); obj_register_selectors_from_description_list (probj, &G_STRUCT (pr, pr_method_description_list_t, proto->instance_methods)); obj_register_selectors_from_description_list (probj, &G_STRUCT (pr, pr_method_description_list_t, proto->class_methods)); if (!Hash_Find (probj->protocols, protocol_name)) { Hash_Add (probj->protocols, proto); } obj_init_protocols (probj, &G_STRUCT (pr, pr_protocol_list_t, proto->protocol_list)); } else { if (proto->class_pointer != PR_SetPointer (pr, proto_class)) PR_RunError (pr, "protocol broken"); } } static void obj_init_protocols (probj_t *probj, pr_protocol_list_t *protos) { progs_t *pr = probj->pr; pr_class_t *proto_class; pr_protocol_t *proto; int i; if (!protos) return; if (!(proto_class = Hash_Find (probj->classes, "Protocol"))) { probj->unclaimed_proto_list = list_cons (probj, protos, probj->unclaimed_proto_list); return; } for (i = 0; i < protos->count; i++) { proto = &G_STRUCT (pr, pr_protocol_t, protos->list[i]); obj_init_protocol (probj, proto_class, proto); } } static void class_add_method_list (probj_t *probj, pr_class_t *class, pr_method_list_t *list) { progs_t *pr = probj->pr; int i; for (i = 0; i < list->method_count; i++) { pr_method_t *method = &list->method_list[i]; if (method->method_name) { const char *name = PR_GetString (pr, method->method_name); const char *types = PR_GetString (pr, method->method_types); pr_sel_t *sel = sel_register_typed_name (probj, name, types, 0); method->method_name = PR_SetPointer (pr, sel); } } list->method_next = class->methods; class->methods = PR_SetPointer (pr, list); } static void obj_class_add_protocols (probj_t *probj, pr_class_t *class, pr_protocol_list_t *protos) { if (!protos) return; protos->next = class->protocols; class->protocols = protos->next; } static void finish_category (probj_t *probj, pr_category_t *category, pr_class_t *class) { progs_t *pr = probj->pr; pr_method_list_t *method_list; pr_protocol_list_t *protocol_list; if (category->instance_methods) { method_list = &G_STRUCT (pr, pr_method_list_t, category->instance_methods); class_add_method_list (probj, class, method_list); } if (category->class_methods) { pr_class_t *meta = &G_STRUCT (pr, pr_class_t, class->class_pointer); method_list = &G_STRUCT (pr, pr_method_list_t, category->class_methods); class_add_method_list (probj, meta, method_list); } if (category->protocols) { protocol_list = &G_STRUCT (pr, pr_protocol_list_t, category->protocols); obj_init_protocols (probj, protocol_list); obj_class_add_protocols (probj, class, protocol_list); } } static void obj_send_message_in_list (probj_t *probj, pr_method_list_t *method_list, pr_class_t *class, pr_sel_t *op) { progs_t *pr = probj->pr; int i; if (!method_list) return; obj_send_message_in_list (probj, &G_STRUCT (pr, pr_method_list_t, method_list->method_next), class, op); for (i = 0; i < method_list->method_count; i++) { pr_method_t *mth = &method_list->method_list[i]; if (mth->method_name && sel_eq (&G_STRUCT (pr, pr_sel_t, mth->method_name), op) && !Hash_FindElement (probj->load_methods, (void *) (intptr_t) mth->method_imp)) { Hash_AddElement (probj->load_methods, (void *) (intptr_t) mth->method_imp); //FIXME need to wrap in save/restore params? PR_ExecuteProgram (pr, mth->method_imp); break; } } } static void send_load (probj_t *probj, class_tree *tree, int level) { progs_t *pr = probj->pr; pr_sel_t *load_sel = sel_register_name (probj, "load"); pr_class_t *class = tree->class; pr_class_t *meta = &G_STRUCT (pr, pr_class_t, class->class_pointer); pr_method_list_t *method_list = &G_STRUCT (pr, pr_method_list_t, meta->methods); obj_send_message_in_list (probj, method_list, class, load_sel); } static void obj_send_load (probj_t *probj) { progs_t *pr = probj->pr; obj_list *m; if (probj->unresolved_classes) { pr_class_t *class = probj->unresolved_classes->data; const char *super_class = PR_GetString (pr, class->super_class); while (Hash_Find (probj->classes, super_class)) { list_remove (probj, &probj->unresolved_classes); if (probj->unresolved_classes) { class = probj->unresolved_classes->data; super_class = PR_GetString (pr, class->super_class); } else { break; } } if (probj->unresolved_classes) return; } //XXX constant string stuff here (see init.c in libobjc source) for (m = probj->module_list; m; m = m->next) obj_create_classes_tree (probj, m->data); while (probj->class_tree_list) { obj_preorder_traverse (probj, probj->class_tree_list->data, 0, send_load); obj_postorder_traverse (probj, probj->class_tree_list->data, 0, obj_destroy_class_tree_node); list_remove (probj, &probj->class_tree_list); } //XXX callback //for (m = probj->module_list; m; m = m->next) // obj_create_classes_tree (probj, m->data); obj_list_free (probj, probj->module_list); probj->module_list = 0; } static pr_method_t * obj_find_message (probj_t *probj, pr_class_t *class, pr_sel_t *selector) { progs_t *pr = probj->pr; pr_class_t *c = class; pr_method_list_t *method_list; pr_method_t *method; pr_sel_t *sel; int i; int dev = developer; pr_string_t *names; if (dev & SYS_rua_msg) { names = probj->selector_names; Sys_Printf ("Searching for %s\n", PR_GetString (pr, names[selector->sel_id])); } while (c) { if (dev & SYS_rua_msg) Sys_Printf ("Checking class %s @ %x\n", PR_GetString (pr, c->name), PR_SetPointer (pr, c)); method_list = &G_STRUCT (pr, pr_method_list_t, c->methods); while (method_list) { if (dev & SYS_rua_msg) { Sys_Printf ("method list %x\n", PR_SetPointer (pr, method_list)); } for (i = 0, method = method_list->method_list; i < method_list->method_count; i++, method++) { sel = &G_STRUCT (pr, pr_sel_t, method->method_name); if (developer & SYS_rua_msg) { names = probj->selector_names; Sys_Printf (" %s\n", PR_GetString (pr, names[sel->sel_id])); } if (sel->sel_id == selector->sel_id) { if (dev & SYS_rua_msg) { names = probj->selector_names; Sys_Printf ("found %s: %x\n", PR_GetString (pr, names[selector->sel_id]), method->method_imp); } return method; } } method_list = &G_STRUCT (pr, pr_method_list_t, method_list->method_next); } c = c->super_class ? &G_STRUCT (pr, pr_class_t, c->super_class) : 0; } return 0; } static void obj_send_initialize (probj_t *probj, pr_class_t *class) { progs_t *pr = probj->pr; pr_method_list_t *method_list; pr_method_t *method; pr_sel_t *sel; pr_class_t *class_pointer; pr_sel_t *selector = sel_register_name (probj, "initialize"); int i; if (PR_CLS_ISINITIALIZED (class)) return; class_pointer = &G_STRUCT (pr, pr_class_t, class->class_pointer); PR_CLS_SETINITIALIZED (class); PR_CLS_SETINITIALIZED (class_pointer); if (class->super_class) obj_send_initialize (probj, &G_STRUCT (pr, pr_class_t, class->super_class)); method_list = &G_STRUCT (pr, pr_method_list_t, class_pointer->methods); while (method_list) { for (i = 0, method = method_list->method_list; i < method_list->method_count; i++, method++) { sel = &G_STRUCT (pr, pr_sel_t, method->method_name); if (sel->sel_id == selector->sel_id) { PR_PushFrame (pr); __auto_type params = PR_SaveParams (pr); // param 0 is known to be the class pointer P_POINTER (pr, 1) = method->method_name; // pr->pr_argc is known to be 2 PR_ExecuteProgram (pr, method->method_imp); PR_RestoreParams (pr, params); PR_PopFrame (pr); return; } } method_list = &G_STRUCT (pr, pr_method_list_t, method_list->method_next); } } static void obj_install_methods_in_dtable (probj_t *probj, pr_class_t *class, pr_method_list_t *method_list) { progs_t *pr = probj->pr; dtable_t *dtable; if (!method_list) { return; } if (method_list->method_next) { obj_install_methods_in_dtable (probj, class, &G_STRUCT (pr, pr_method_list_t, method_list->method_next)); } dtable = get_dtable (probj, __FUNCTION__, class->dtable); for (int i = 0; i < method_list->method_count; i++) { pr_method_t *method = &method_list->method_list[i]; pr_sel_t *sel = &G_STRUCT (pr, pr_sel_t, method->method_name); if (sel->sel_id < dtable->size) { dtable->imp[sel->sel_id] = method->method_imp; } } } static void obj_install_dispatch_table_for_class (probj_t *probj, pr_class_t *class) { progs_t *pr = probj->pr; pr_class_t *super = &G_STRUCT (pr, pr_class_t, class->super_class); dtable_t *dtable; Sys_MaskPrintf (SYS_rua_obj, " install dispatch for class %s %x %d\n", PR_GetString (pr, class->name), class->methods, PR_CLS_ISMETA(class)); if (super && !super->dtable) { obj_install_dispatch_table_for_class (probj, super); } dtable = dtable_new (probj); class->dtable = dtable_index (probj, dtable); dtable->size = probj->selector_index + 1; dtable->imp = calloc (dtable->size, sizeof (pr_func_t)); if (super) { dtable_t *super_dtable = get_dtable (probj, __FUNCTION__, super->dtable); memcpy (dtable->imp, super_dtable->imp, super_dtable->size * sizeof (*dtable->imp)); } obj_install_methods_in_dtable (probj, class, &G_STRUCT (pr, pr_method_list_t, class->methods)); } static inline dtable_t * obj_check_dtable_installed (probj_t *probj, pr_class_t *class) { if (!class->dtable) { obj_install_dispatch_table_for_class (probj, class); } return get_dtable (probj, __FUNCTION__, class->dtable); } static pr_func_t get_imp (probj_t *probj, pr_class_t *class, pr_sel_t *sel) { pr_func_t imp = 0; if (class->dtable) { dtable_t *dtable = get_dtable (probj, __FUNCTION__, class->dtable); if (sel->sel_id < dtable->size) { imp = dtable->imp[sel->sel_id]; } } if (!imp) { if (!class->dtable) { obj_install_dispatch_table_for_class (probj, class); imp = get_imp (probj, class, sel); } else { imp = probj->obj_forward; } } return imp; } static int obj_reponds_to (probj_t *probj, pr_id_t *obj, pr_sel_t *sel) { progs_t *pr = probj->pr; pr_class_t *class; dtable_t *dtable; pr_func_t imp = 0; class = &G_STRUCT (pr, pr_class_t, obj->class_pointer); dtable = obj_check_dtable_installed (probj, class); if (sel->sel_id < dtable->size) { imp = dtable->imp[sel->sel_id]; } return imp != 0; } static pr_func_t obj_msg_lookup (probj_t *probj, pr_id_t *receiver, pr_sel_t *op) { progs_t *pr = probj->pr; pr_class_t *class; if (!receiver) return 0; class = &G_STRUCT (pr, pr_class_t, receiver->class_pointer); if (PR_CLS_ISCLASS (class)) { if (!PR_CLS_ISINITIALIZED (class)) obj_send_initialize (probj, class); } else if (PR_CLS_ISMETA (class) && PR_CLS_ISCLASS ((pr_class_t *) receiver)) { if (!PR_CLS_ISINITIALIZED ((pr_class_t *) receiver)) obj_send_initialize (probj, (pr_class_t *) receiver); } return get_imp (probj, class, op); } static pr_func_t obj_msg_lookup_super (probj_t *probj, pr_super_t *super, pr_sel_t *op) { progs_t *pr = probj->pr; pr_class_t *class; if (!super->self) return 0; class = &G_STRUCT (pr, pr_class_t, super->class); return get_imp (probj, class, op); } static void obj_verror (probj_t *probj, pr_id_t *object, int code, const char *fmt, int count, pr_type_t **args) { progs_t *pr = probj->pr; const char *name = "nil"; if (object) { __auto_type class = &G_STRUCT (pr, pr_class_t, object->class_pointer); name = PR_GetString (pr, class->name); } PR_Sprintf (pr, probj->msg, "obj_verror", fmt, count, args); PR_RunError (pr, "%s: %s", name, probj->msg->str); } static void dump_ivars (probj_t *probj, pr_ptr_t _ivars) { progs_t *pr = probj->pr; pr_ivar_list_t *ivars; int i; if (!_ivars) return; ivars = &G_STRUCT (pr, pr_ivar_list_t, _ivars); for (i = 0; i < ivars->ivar_count; i++) { Sys_Printf (" %s %s %d\n", PR_GetString (pr, ivars->ivar_list[i].ivar_name), PR_GetString (pr, ivars->ivar_list[i].ivar_type), ivars->ivar_list[i].ivar_offset); } } static void obj_init_statics (probj_t *probj) { progs_t *pr = probj->pr; obj_list **cell = &probj->uninitialized_statics; pr_ptr_t *ptr; pr_ptr_t *inst; Sys_MaskPrintf (SYS_rua_obj, "Initializing statics\n"); while (*cell) { int initialized = 1; for (ptr = (*cell)->data; *ptr; ptr++) { __auto_type statics = &G_STRUCT (pr, pr_static_instances_t, *ptr); const char *class_name = PR_GetString (pr, statics->class_name); pr_class_t *class = Hash_Find (probj->classes, class_name); Sys_MaskPrintf (SYS_rua_obj, " %s %p\n", class_name, class); if (!class) { initialized = 0; continue; } if (strcmp (class_name, "Protocol") == 0) { // protocols are special for (inst = statics->instances; *inst; inst++) { obj_init_protocol (probj, class, &G_STRUCT (pr, pr_protocol_t, *inst)); } } else { for (inst = statics->instances; *inst; inst++) { pr_id_t *id = &G_STRUCT (pr, pr_id_t, *inst); id->class_pointer = PR_SetPointer (pr, class); } } } if (initialized) { list_remove (probj, cell); } else { cell = &(*cell)->next; } } } static void rua___obj_exec_class (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_module_t *module = &P_STRUCT (pr, pr_module_t, 0); pr_symtab_t *symtab; pr_sel_t *sel; pr_ptr_t *ptr; int i; obj_list **cell; if (!module) return; symtab = &G_STRUCT (pr, pr_symtab_t, module->symtab); if (!symtab) return; Sys_MaskPrintf (SYS_rua_obj, "Initializing %s module\n" "symtab @ %x : %d selector%s @ %x, " "%d class%s and %d categor%s\n" "static instance lists: %s\n", PR_GetString (pr, module->name), module->symtab, symtab->sel_ref_cnt, symtab->sel_ref_cnt == 1 ? "" : "s", symtab->refs, symtab->cls_def_cnt, symtab->cls_def_cnt == 1 ? "" : "es", symtab->cat_def_cnt, symtab->cat_def_cnt == 1 ? "y" : "ies", symtab->defs[symtab->cls_def_cnt + symtab->cat_def_cnt] ? "yes" : "no"); probj->module_list = list_cons (probj, module, probj->module_list); sel = &G_STRUCT (pr, pr_sel_t, symtab->refs); for (i = 0; i < symtab->sel_ref_cnt; i++) { const char *name, *types; name = PR_GetString (pr, sel->sel_id); types = PR_GetString (pr, sel->sel_types); sel_register_typed_name (probj, name, types, sel); sel++; } ptr = symtab->defs; for (i = 0; i < symtab->cls_def_cnt; i++, ptr++) { pr_class_t *class = &G_STRUCT (pr, pr_class_t, *ptr); pr_class_t *meta = &G_STRUCT (pr, pr_class_t, class->class_pointer); const char *super_class = PR_GetString (pr, class->super_class); Sys_MaskPrintf (SYS_rua_obj, "Class %s @ %x\n", PR_GetString (pr, class->name), *ptr); Sys_MaskPrintf (SYS_rua_obj, " class pointer: %x\n", class->class_pointer); Sys_MaskPrintf (SYS_rua_obj, " super class: %s\n", PR_GetString (pr, class->super_class)); Sys_MaskPrintf (SYS_rua_obj, " instance variables: %d @ %x\n", class->instance_size, class->ivars); if (developer & SYS_rua_obj) dump_ivars (probj, class->ivars); Sys_MaskPrintf (SYS_rua_obj, " instance methods: %x\n", class->methods); Sys_MaskPrintf (SYS_rua_obj, " protocols: %x\n", class->protocols); Sys_MaskPrintf (SYS_rua_obj, " class methods: %x\n", meta->methods); Sys_MaskPrintf (SYS_rua_obj, " instance variables: %d @ %x\n", meta->instance_size, meta->ivars); if (developer & SYS_rua_obj) dump_ivars (probj, meta->ivars); class->subclass_list = 0; Hash_Add (probj->classes, class); obj_register_selectors_from_class (probj, class); obj_register_selectors_from_class (probj, meta); if (class->protocols) { pr_protocol_list_t *protocol_list; protocol_list = &G_STRUCT (pr, pr_protocol_list_t, class->protocols); obj_init_protocols (probj, protocol_list); } if (class->super_class && !Hash_Find (probj->classes, super_class)) probj->unresolved_classes = list_cons (probj, class, probj->unresolved_classes); } for (i = 0; i < symtab->cat_def_cnt; i++, ptr++) { pr_category_t *category = &G_STRUCT (pr, pr_category_t, *ptr); const char *class_name = PR_GetString (pr, category->class_name); pr_class_t *class = Hash_Find (probj->classes, class_name); Sys_MaskPrintf (SYS_rua_obj, "Category %s (%s) @ %x\n", PR_GetString (pr, category->class_name), PR_GetString (pr, category->category_name), *ptr); Sys_MaskPrintf (SYS_rua_obj, " instance methods: %x\n", category->instance_methods); Sys_MaskPrintf (SYS_rua_obj, " class methods: %x\n", category->class_methods); Sys_MaskPrintf (SYS_rua_obj, " protocols: %x\n", category->protocols); if (class) { finish_category (probj, category, class); } else { probj->unclaimed_categories = list_cons (probj, category, probj->unclaimed_categories); } } if (*ptr) { Sys_MaskPrintf (SYS_rua_obj, "Static instances lists: %x\n", *ptr); probj->uninitialized_statics = list_cons (probj, &G_STRUCT (pr, pr_ptr_t, *ptr), probj->uninitialized_statics); } if (probj->uninitialized_statics) { obj_init_statics (probj); } for (cell = &probj->unclaimed_categories; *cell; ) { pr_category_t *category = (*cell)->data; const char *class_name = PR_GetString (pr, category->class_name); pr_class_t *class = Hash_Find (probj->classes, class_name); if (class) { list_remove (probj, cell); finish_category (probj, category, class); } else { cell = &(*cell)->next; } } if (probj->unclaimed_proto_list && Hash_Find (probj->classes, "Protocol")) { for (cell = &probj->unclaimed_proto_list; *cell; ) { obj_init_protocols (probj, (*cell)->data); list_remove (probj, cell); } } Sys_MaskPrintf (SYS_rua_obj, "Finished initializing %s module\n", PR_GetString (pr, module->name)); obj_send_load (probj); Sys_MaskPrintf (SYS_rua_obj, "Leaving %s module init\n", PR_GetString (pr, module->name)); } static void rua___obj_forward (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_id_t *obj = &P_STRUCT (pr, pr_id_t, 0); pr_sel_t *sel = &P_STRUCT (pr, pr_sel_t, 1); pr_sel_t *fwd_sel = probj->forward_selector; pr_sel_t *err_sel; pr_class_t *class = &G_STRUCT (pr, pr_class_t, obj->class_pointer); pr_func_t imp; if (!fwd_sel) { //FIXME sel_register_typed_name is really not the way to go about //looking for a selector by name fwd_sel = sel_register_typed_name (probj, "forward::", "", 0); probj->forward_selector = fwd_sel; } if (obj_reponds_to (probj, obj, fwd_sel)) { imp = get_imp (probj, class, fwd_sel); // forward:(SEL) sel :(@va_list) args // args is full param list as a va_list pr_string_t args_block = 0; int argc; pr_type_t *argv; if (pr->globals.stack) { argv = pr->pr_params[0]; argc = probj->selector_argc[sel->sel_id]; if (argc < 0) { // -ve values indicate varargs functions and is the ones // complement of the number of real parameters before the // ellipsis. However, Ruamoko ISA progs pass va_list through // ... so in the end, a -ve value indicates the total number // of arguments (including va_list) passed to the function. argc = -argc; } } else { size_t parm_size = pr->pr_param_size * sizeof(pr_type_t); size_t size = pr->pr_argc * parm_size; args_block = PR_AllocTempBlock (pr, size); argc = pr->pr_argc; argv = (pr_type_t *) PR_GetString (pr, args_block); // can't memcpy all params because 0 and 1 could be anywhere memcpy (argv + 0, &P_INT (pr, 0), 4 * sizeof (pr_type_t)); memcpy (argv + 4, &P_INT (pr, 1), 4 * sizeof (pr_type_t)); memcpy (argv + 8, &P_INT (pr, 2), (argc - 2) * parm_size); } RUA_CALL_BEGIN (pr, 4); P_POINTER (pr, 0) = PR_SetPointer (pr, obj); P_POINTER (pr, 1) = PR_SetPointer (pr, fwd_sel); P_POINTER (pr, 2) = PR_SetPointer (pr, sel); P_PACKED (pr, pr_va_list_t, 3).count = argc; P_PACKED (pr, pr_va_list_t, 3).list = PR_SetPointer (pr, argv); if (args_block) { PR_PushTempString (pr, args_block); } RUA_CALL_END (pr, imp); return; } err_sel = sel_register_typed_name (probj, "doesNotRecognize:", "", 0); if (obj_reponds_to (probj, obj, err_sel)) { RUA_CALL_BEGIN (pr, 3) P_POINTER (pr, 0) = PR_SetPointer (pr, obj); P_POINTER (pr, 1) = PR_SetPointer (pr, err_sel); P_POINTER (pr, 2) = PR_SetPointer (pr, sel); RUA_CALL_END (pr, get_imp (probj, class, err_sel)) return; } dsprintf (probj->msg, "(%s) %s does not recognize %s", PR_CLS_ISMETA (class) ? "class" : "instance", PR_GetString (pr, class->name), PR_GetString (pr, probj->selector_names[sel->sel_id])); err_sel = sel_register_typed_name (probj, "error:", "", 0); if (obj_reponds_to (probj, obj, err_sel)) { RUA_CALL_BEGIN (pr, 3) P_POINTER (pr, 0) = PR_SetPointer (pr, obj); P_POINTER (pr, 1) = PR_SetPointer (pr, err_sel); P_POINTER (pr, 2) = PR_SetTempString (pr, probj->msg->str); RUA_CALL_END (pr, get_imp (probj, class, err_sel)) return; } PR_RunError (pr, "%s", probj->msg->str); } static void rua___obj_responds_to (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_id_t *obj = &P_STRUCT (pr, pr_id_t, 0); pr_sel_t *sel = &P_STRUCT (pr, pr_sel_t, 1); R_INT (pr) = obj_reponds_to (probj, obj, sel); } static void rua_obj_error (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); int code = P_INT (pr, 1); const char *fmt = P_GSTRING (pr, 2); int count = pr->pr_argc - 3; pr_type_t **args = &pr->pr_params[3]; if (pr->progs->version == PROG_VERSION) { __auto_type va_list = &P_PACKED (pr, pr_va_list_t, 3); count = va_list->count; if (count) { args = alloca (count * sizeof (pr_type_t *)); for (int i = 0; i < count; i++) { args[i] = &pr->pr_globals[va_list->list + i * 4]; } } else { args = 0; } } obj_verror (probj, object, code, fmt, count, args); } static void rua_obj_verror (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); int code = P_INT (pr, 1); const char *fmt = P_GSTRING (pr, 2); pr_va_list_t *val = (pr_va_list_t *) pr->pr_params[3]; pr_type_t *params = &G_STRUCT (pr, pr_type_t, val->list); pr_type_t **args = alloca (val->count * sizeof (pr_type_t *)); int i; for (i = 0; i < val->count; i++) args[i] = params + i * pr->pr_param_size; obj_verror (probj, object, code, fmt, val->count, args); } static void rua_obj_set_error_handler (progs_t *pr, void *data) { //probj_t *probj = pr->pr_objective_resources; //pr_func_t func = P_INT (pr, 0); //arglist //XXX PR_RunError (pr, "%s, not implemented", __FUNCTION__); } static const char * rua_at_handler (progs_t *pr, pr_ptr_t at_param, void *_probj) { if (!at_param) { return "nil"; } probj_t *probj = _probj; pr_id_t *obj = &G_STRUCT (pr, pr_id_t, at_param); pr_class_t *class = &G_STRUCT (pr, pr_class_t, obj->class_pointer); //FIXME sel_register_typed_name is really not the way to go about //looking for a selector by name pr_sel_t *describe_sel=sel_register_typed_name (probj, "describe", "", 0); pr_func_t imp = get_imp (probj, class, describe_sel); PR_PushFrame (pr); PR_RESET_PARAMS (pr); P_POINTER (pr, 0) = at_param; P_POINTER (pr, 1) = PR_SetPointer (pr, describe_sel); pr->pr_argc = 2; PR_ExecuteProgram (pr, imp); PR_PopFrame (pr); //FIXME the lifetime of the string may be a problem return PR_GetString (pr, R_STRING (pr)); } static void rua_obj_msg_lookup (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_id_t *receiver = &P_STRUCT (pr, pr_id_t, 0); pr_sel_t *op = &P_STRUCT (pr, pr_sel_t, 1); R_INT (pr) = obj_msg_lookup (probj, receiver, op); } static void rua_obj_msg_lookup_super (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_super_t *super = &P_STRUCT (pr, pr_super_t, 0); pr_sel_t *_cmd = &P_STRUCT (pr, pr_sel_t, 1); R_INT (pr) = obj_msg_lookup_super (probj, super, _cmd); } static void rua_obj_msg_sendv (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_ptr_t obj = P_POINTER (pr, 0); pr_id_t *receiver = &P_STRUCT (pr, pr_id_t, 0); pr_ptr_t sel = P_POINTER (pr, 1); pr_sel_t *op = &P_STRUCT (pr, pr_sel_t, 1); pr_func_t imp = obj_msg_lookup (probj, receiver, op); __auto_type args = &P_PACKED (pr, pr_va_list_t, 2); int count = args->count; pr_type_t *params = G_GPOINTER (pr, args->list); if (count < 2 || count > PR_MAX_PARAMS) { PR_RunError (pr, "bad args count in obj_msg_sendv: %d", count); } if (pr_boundscheck) { PR_BoundsCheckSize (pr, args->list, count * pr->pr_param_size); } if (!imp) { PR_RunError (pr, "%s does not respond to %s", PR_GetString (pr, object_get_class_name (probj, receiver)), PR_GetString (pr, probj->selector_names[op->sel_id])); } RUA_CALL_BEGIN (pr, count) // skip over the first two parameters because receiver and op will // replace them count -= 2; params += 2 * pr->pr_param_size; P_POINTER (pr, 0) = obj; P_POINTER (pr, 1) = sel; if (count) { memcpy (&P_INT (pr, 2), params, count * sizeof (pr_type_t) * pr->pr_param_size); } RUA_CALL_END (pr, imp) } #define RETAIN_COUNT(obj) PR_PTR (int, &(obj)[-1]) static void rua_obj_increment_retaincount (progs_t *pr, void *data) { pr_type_t *obj = &P_STRUCT (pr, pr_type_t, 0); R_INT (pr) = ++RETAIN_COUNT (obj); } static void rua_obj_decrement_retaincount (progs_t *pr, void *data) { pr_type_t *obj = &P_STRUCT (pr, pr_type_t, 0); R_INT (pr) = --RETAIN_COUNT (obj); } static void rua_obj_get_retaincount (progs_t *pr, void *data) { pr_type_t *obj = &P_STRUCT (pr, pr_type_t, 0); R_INT (pr) = RETAIN_COUNT (obj); } static void rua_obj_malloc (progs_t *pr, void *data) { int size = P_INT (pr, 0) * sizeof (pr_type_t); void *mem = PR_Zone_Malloc (pr, size); RETURN_POINTER (pr, mem); } static void rua_obj_atomic_malloc (progs_t *pr, void *data) { int size = P_INT (pr, 0) * sizeof (pr_type_t); void *mem = PR_Zone_Malloc (pr, size); RETURN_POINTER (pr, mem); } static void rua_obj_valloc (progs_t *pr, void *data) { int size = P_INT (pr, 0) * sizeof (pr_type_t); void *mem = PR_Zone_Malloc (pr, size); RETURN_POINTER (pr, mem); } static void rua_obj_realloc (progs_t *pr, void *data) { void *mem = (void*)P_GPOINTER (pr, 0); int size = P_INT (pr, 1) * sizeof (pr_type_t); mem = PR_Zone_Realloc (pr, mem, size); RETURN_POINTER (pr, mem); } static void rua_obj_calloc (progs_t *pr, void *data) { int size = P_INT (pr, 0) * P_INT (pr, 1) * sizeof (pr_type_t); void *mem = PR_Zone_Malloc (pr, size); memset (mem, 0, size); RETURN_POINTER (pr, mem); } static void rua_obj_free (progs_t *pr, void *data) { void *mem = (void*)P_GPOINTER (pr, 0); PR_Zone_Free (pr, mem); } static void rua_obj_get_uninstalled_dtable (progs_t *pr, void *data) { //probj_t *probj = pr->pr_objective_resources; //XXX PR_RunError (pr, "%s, not implemented", __FUNCTION__); } static void rua_obj_msgSend (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_id_t *self = &P_STRUCT (pr, pr_id_t, 0); pr_sel_t *_cmd = &P_STRUCT (pr, pr_sel_t, 1); pr_func_t imp; if (!self) { R_INT (pr) = P_INT (pr, 0); return; } if (P_UINT (pr, 0) >= pr->globals_size) { PR_RunError (pr, "invalid self: %x", P_UINT (pr, 0)); } if (!_cmd) PR_RunError (pr, "null selector"); imp = obj_msg_lookup (probj, self, _cmd); if (!imp) PR_RunError (pr, "%s does not respond to %s", PR_GetString (pr, object_get_class_name (probj, self)), PR_GetString (pr, probj->selector_names[_cmd->sel_id])); PR_CallFunction (pr, imp, pr->pr_return); } static void rua_obj_msgSend_super (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_super_t *super = &P_STRUCT (pr, pr_super_t, 0); pr_sel_t *_cmd = &P_STRUCT (pr, pr_sel_t, 1); pr_func_t imp; imp = obj_msg_lookup_super (probj, super, _cmd); if (!imp) { pr_id_t *self = &G_STRUCT (pr, pr_id_t, super->self); PR_RunError (pr, "%s does not respond to %s", PR_GetString (pr, object_get_class_name (probj, self)), PR_GetString (pr, probj->selector_names[_cmd->sel_id])); } if (pr->progs->version < PROG_VERSION) { pr->pr_params[0] = pr->pr_real_params[0]; } P_POINTER (pr, 0) = super->self; PR_CallFunction (pr, imp, pr->pr_return); } static void rua_obj_get_class (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; const char *name = P_GSTRING (pr, 0); pr_class_t *class; class = Hash_Find (probj->classes, name); if (!class) PR_RunError (pr, "could not find class %s", name); RETURN_POINTER (pr, class); } static void rua_obj_lookup_class (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; const char *name = P_GSTRING (pr, 0); pr_class_t *class; class = Hash_Find (probj->classes, name); RETURN_POINTER (pr, class); } static void rua_obj_next_class (progs_t *pr, void *data) { //probj_t *probj = pr->pr_objective_resources; //XXX PR_RunError (pr, "%s, not implemented", __FUNCTION__); } //==================================================================== static void rua_sel_get_name (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_sel_t *sel = &P_STRUCT (pr, pr_sel_t, 0); if (sel->sel_id > 0 && sel->sel_id <= probj->selector_index) R_STRING (pr) = probj->selector_names[sel->sel_id]; else R_STRING (pr) = 0; } static void rua_sel_get_type (progs_t *pr, void *data) { pr_sel_t *sel = &P_STRUCT (pr, pr_sel_t, 0); R_INT (pr) = sel->sel_types; } static void rua_sel_get_uid (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; const char *name = P_GSTRING (pr, 0); RETURN_POINTER (pr, sel_register_typed_name (probj, name, "", 0)); } static void rua_sel_register_name (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; const char *name = P_GSTRING (pr, 0); RETURN_POINTER (pr, sel_register_typed_name (probj, name, "", 0)); } static void rua_sel_is_mapped (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; // FIXME might correspond to a string pr_sel_t *sel = &P_STRUCT (pr, pr_sel_t, 0); R_INT (pr) = sel->sel_id > 0 && sel->sel_id <= probj->selector_index; } //==================================================================== static void rua_class_get_class_method (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); pr_sel_t *aSel = &P_STRUCT (pr, pr_sel_t, 1); pr_method_t *method; class = &G_STRUCT (pr, pr_class_t, class->class_pointer); method = obj_find_message (probj, class, aSel); RETURN_POINTER (pr, method); } static void rua_class_get_instance_method (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); pr_sel_t *aSel = &P_STRUCT (pr, pr_sel_t, 1); pr_method_t *method = obj_find_message (probj, class, aSel); RETURN_POINTER (pr, method); } #define CLASSOF(x) (&G_STRUCT (pr, pr_class_t, (x)->class_pointer)) static void rua_class_pose_as (progs_t *pr, void *data) { pr_class_t *impostor = &P_STRUCT (pr, pr_class_t, 0); pr_class_t *superclass = &P_STRUCT (pr, pr_class_t, 1); pr_ptr_t *subclass; subclass = &superclass->subclass_list; while (*subclass) { pr_class_t *sub = &P_STRUCT (pr, pr_class_t, *subclass); pr_ptr_t nextSub = sub->sibling_class; if (sub != impostor) { sub->sibling_class = impostor->subclass_list; sub->super_class = P_POINTER (pr, 0); // impostor impostor->subclass_list = *subclass; // sub CLASSOF (sub)->sibling_class = CLASSOF (impostor)->sibling_class; CLASSOF (sub)->super_class = impostor->class_pointer; CLASSOF (impostor)->subclass_list = sub->class_pointer; } *subclass = nextSub; } superclass->subclass_list = P_POINTER (pr, 0); // impostor CLASSOF (superclass)->subclass_list = impostor->class_pointer; impostor->sibling_class = 0; CLASSOF (impostor)->sibling_class = 0; //XXX how much do I need to do here?!? //class_table_replace (super_class, impostor); R_INT (pr) = P_POINTER (pr, 0); // impostor } #undef CLASSOF static inline pr_id_t * class_create_instance (progs_t *pr, pr_class_t *class) { int size = class->instance_size * sizeof (pr_type_t); pr_type_t *mem; pr_id_t *id = 0; mem = PR_Zone_TagMalloc (pr, size, class->name); if (mem) { memset (mem, 0, size); id = (pr_id_t *) mem; id->class_pointer = PR_SetPointer (pr, class); } return id; } static void rua_class_create_instance (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); pr_id_t *id = class_create_instance (pr, class); RETURN_POINTER (pr, id); } static void rua_class_get_class_name (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISCLASS (class) ? class->name : PR_SetString (pr, "Nil"); } static void rua_class_get_instance_size (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISCLASS (class) ? class->instance_size : 0; } static void rua_class_get_meta_class (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISCLASS (class) ? class->class_pointer : 0; } static void rua_class_get_super_class (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISCLASS (class) ? class->super_class : 0; } static void rua_class_get_version (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISCLASS (class) ? class->version : -1; } static void rua_class_is_class (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISCLASS (class); } static void rua_class_is_meta_class (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISMETA (class); } static void rua_class_set_version (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); if (PR_CLS_ISCLASS (class)) class->version = P_INT (pr, 1); } static void rua_class_get_gc_object_type (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISCLASS (class) ? class->gc_object_type : 0; } static void rua_class_ivar_set_gcinvisible (progs_t *pr, void *data) { //probj_t *probj = pr->pr_objective_resources; //pr_class_t *impostor = &P_STRUCT (pr, pr_class_t, 0); //const char *ivarname = P_GSTRING (pr, 1); //int gcInvisible = P_INT (pr, 2); //XXX PR_RunError (pr, "%s, not implemented", __FUNCTION__); } //==================================================================== static void rua_method_get_imp (progs_t *pr, void *data) { pr_method_t *method = &P_STRUCT (pr, pr_method_t, 0); R_INT (pr) = method ? method->method_imp : 0; } static void rua_get_imp (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); pr_sel_t *sel = &P_STRUCT (pr, pr_sel_t, 1); R_INT (pr) = get_imp (probj, class, sel); } //==================================================================== static void rua_object_dispose (progs_t *pr, void *data) { pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); pr_type_t *mem = (pr_type_t *) object; PR_Zone_Free (pr, mem); } static void rua_object_copy (progs_t *pr, void *data) { pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); pr_class_t *class = &G_STRUCT (pr, pr_class_t, object->class_pointer); pr_id_t *id; id = class_create_instance (pr, class); if (id) { memcpy (id, object, sizeof (pr_type_t) * class->instance_size); // copy the retain count ((pr_type_t *) id)[-1] = ((pr_type_t *) object)[-1]; } RETURN_POINTER (pr, id); } static void rua_object_get_class (progs_t *pr, void *data) { pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); pr_class_t *class; if (object) { class = &G_STRUCT (pr, pr_class_t, object->class_pointer); if (PR_CLS_ISCLASS (class)) { RETURN_POINTER (pr, class); return; } if (PR_CLS_ISMETA (class)) { R_INT (pr) = P_INT (pr, 0); return; } } R_INT (pr) = 0; } static void rua_object_get_super_class (progs_t *pr, void *data) { pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); pr_class_t *class; if (object) { class = &G_STRUCT (pr, pr_class_t, object->class_pointer); if (PR_CLS_ISCLASS (class)) { R_INT (pr) = class->super_class; return; } if (PR_CLS_ISMETA (class)) { R_INT (pr) = ((pr_class_t *)object)->super_class; return; } } R_INT (pr) = 0; } static void rua_object_get_meta_class (progs_t *pr, void *data) { pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); pr_class_t *class; if (object) { class = &G_STRUCT (pr, pr_class_t, object->class_pointer); if (PR_CLS_ISCLASS (class)) { R_INT (pr) = class->class_pointer; return; } if (PR_CLS_ISMETA (class)) { R_INT (pr) = ((pr_class_t *)object)->class_pointer; return; } } R_INT (pr) = 0; } static void rua_object_get_class_name (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); R_STRING (pr) = object_get_class_name (probj, object); } static void rua_object_is_class (progs_t *pr, void *data) { pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); if (object) { R_INT (pr) = PR_CLS_ISCLASS ((pr_class_t*)object); return; } R_INT (pr) = 0; } static void rua_object_is_instance (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); R_INT (pr) = object_is_instance (probj, object); } static void rua_object_is_meta_class (progs_t *pr, void *data) { pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); if (object) { R_INT (pr) = PR_CLS_ISMETA ((pr_class_t*)object); return; } R_INT (pr) = 0; } //==================================================================== static void rua__i_Object__hash (progs_t *pr, void *data) { R_INT (pr) = P_INT (pr, 0); } static void rua__i_Object_error_error_ (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; pr_id_t *self = &P_STRUCT (pr, pr_id_t, 0); const char *fmt = P_GSTRING (pr, 2); int count = pr->pr_argc - 3; pr_type_t **args = &pr->pr_params[3]; if (pr->progs->version == PROG_VERSION) { __auto_type va_list = &P_PACKED (pr, pr_va_list_t, 3); count = va_list->count; if (count) { args = alloca (count * sizeof (pr_type_t *)); for (int i = 0; i < count; i++) { args[i] = &pr->pr_globals[va_list->list + i * 4]; } } else { args = 0; } } dsprintf (probj->msg, "error: %s (%s)\n%s", PR_GetString (pr, object_get_class_name (probj, self)), object_is_instance (probj, self) ? "instance" : "class", fmt); obj_verror (probj, self, 0, probj->msg->str, count, args); } static int obj_protocol_conformsToProtocol (probj_t *probj, pr_protocol_t *proto, pr_protocol_t *protocol) { progs_t *pr = probj->pr; pr_protocol_list_t *proto_list; if (!proto || !protocol) { return 0; } if (proto == protocol) { return 1; } if (proto->protocol_name == protocol->protocol_name || strcmp (PR_GetString (pr, proto->protocol_name), PR_GetString (pr, protocol->protocol_name)) == 0) { return 1; } proto_list = &G_STRUCT (pr, pr_protocol_list_t, proto->protocol_list); while (proto_list) { Sys_MaskPrintf (SYS_rua_obj, "%x %x %d\n", PR_SetPointer (pr, proto_list), proto_list->next, proto_list->count); for (int i = 0; i < proto_list->count; i++) { proto = &G_STRUCT (pr, pr_protocol_t, proto_list->list[i]); if (proto == protocol || obj_protocol_conformsToProtocol (probj, proto, protocol)) { return 1; } } proto_list = &G_STRUCT (pr, pr_protocol_list_t, proto_list->next); } return 0; } static void rua__c_Object__conformsToProtocol_ (progs_t *pr, void *data) { probj_t *probj = pr->pr_objective_resources; // class points to _OBJ_CLASS_foo, and class->class_pointer points to // _OBJ_METACLASS_foo pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); // protocol->class_pointer must point to _OBJ_CLASS_Protocol (ie, if // protocol is not actually a protocol, then the class cannot conform // to it) pr_protocol_t *protocol = &P_STRUCT (pr, pr_protocol_t, 2); pr_protocol_list_t *proto_list; if (!class || !protocol) { goto not_conforms; } if (protocol->class_pointer != PR_SetPointer (pr, Hash_Find (probj->classes, "Protocol"))) { goto not_conforms; } proto_list = &G_STRUCT (pr, pr_protocol_list_t, class->protocols); while (proto_list) { Sys_MaskPrintf (SYS_rua_obj, "%x %x %d\n", PR_SetPointer (pr, proto_list), proto_list->next, proto_list->count); for (int i = 0; i < proto_list->count; i++) { __auto_type proto = &G_STRUCT (pr, pr_protocol_t, proto_list->list[i]); if (proto == protocol || obj_protocol_conformsToProtocol (probj, proto, protocol)) { goto conforms; } } proto_list = &G_STRUCT (pr, pr_protocol_list_t, proto_list->next); } not_conforms: Sys_MaskPrintf (SYS_rua_obj, "does not conform\n"); R_INT (pr) = 0; return; conforms: Sys_MaskPrintf (SYS_rua_obj, "conforms\n"); R_INT (pr) = 1; return; } static void rua_PR_FindGlobal (progs_t *pr, void *data) { const char *name = P_GSTRING (pr, 0); pr_def_t *def; R_POINTER (pr) = 0; def = PR_FindGlobal (pr, name); if (def) R_POINTER (pr) = def->ofs; } //==================================================================== #define bi(x,np,params...) {#x, rua_##x, -1, np, {params}} #define p(type) PR_PARAM(type) #define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } static builtin_t obj_methods [] = { bi(__obj_exec_class, 1, p(ptr)), bi(__obj_forward, -3, p(ptr), p(ptr)), bi(__obj_responds_to, 2, p(ptr), p(ptr)), bi(obj_error, -4, p(ptr), p(int), p(string)), bi(obj_verror, 4, p(ptr), p(int), p(string), P(1, 2)), bi(obj_set_error_handler, 1, p(func)), bi(obj_msg_lookup, 2, p(ptr), p(ptr)), bi(obj_msg_lookup_super, 2, p(ptr), p(ptr)), bi(obj_msg_sendv, 3, p(ptr), p(ptr), P(1, 2)), bi(obj_increment_retaincount, 1, p(ptr)), bi(obj_decrement_retaincount, 1, p(ptr)), bi(obj_get_retaincount, 1, p(ptr)), bi(obj_malloc, 1, p(int)), bi(obj_atomic_malloc, 1, p(int)), bi(obj_valloc, 1, p(int)), bi(obj_realloc, 2, p(ptr), p(int)), bi(obj_calloc, 2, p(int), p(int)), bi(obj_free, 1, p(ptr)), bi(obj_get_uninstalled_dtable, 0), bi(obj_msgSend, 2, p(ptr), p(ptr)),//magic bi(obj_msgSend_super, 2, p(ptr), p(ptr)),//magic bi(obj_get_class, 1, p(string)), bi(obj_lookup_class, 1, p(string)), bi(obj_next_class, 1, p(ptr)), bi(sel_get_name, 1, p(ptr)), bi(sel_get_type, 1, p(ptr)), bi(sel_get_uid, 1, p(string)), bi(sel_register_name, 1, p(string)), bi(sel_is_mapped, 1, p(ptr)), bi(class_get_class_method, 2, p(ptr), p(ptr)), bi(class_get_instance_method, 2, p(ptr), p(ptr)), bi(class_pose_as, 2, p(ptr), p(ptr)), bi(class_create_instance, 1, p(ptr)), bi(class_get_class_name, 1, p(ptr)), bi(class_get_instance_size, 1, p(ptr)), bi(class_get_meta_class, 1, p(ptr)), bi(class_get_super_class, 1, p(ptr)), bi(class_get_version, 1, p(ptr)), bi(class_is_class, 1, p(ptr)), bi(class_is_meta_class, 1, p(ptr)), bi(class_set_version, 2, p(ptr), p(int)), bi(class_get_gc_object_type, 1, p(ptr)), bi(class_ivar_set_gcinvisible, 3, p(ptr), p(string), p(int)), bi(method_get_imp, 1, p(ptr)), bi(get_imp, 1, p(ptr), p(ptr)), bi(object_copy, 1, p(ptr)), bi(object_dispose, 1, p(ptr)), bi(object_get_class, 1, p(ptr)), bi(object_get_class_name, 1, p(ptr)), bi(object_get_meta_class, 1, p(ptr)), bi(object_get_super_class, 1, p(ptr)), bi(object_is_class, 1, p(ptr)), bi(object_is_instance, 1, p(ptr)), bi(object_is_meta_class, 1, p(ptr)), bi(_i_Object__hash, 2, p(ptr), p(ptr)), bi(_i_Object_error_error_, -4, p(ptr), p(ptr), p(string)), bi(_c_Object__conformsToProtocol_, 3, p(ptr), p(ptr), p(ptr)), bi(PR_FindGlobal, 1, p(string)),//FIXME {0} }; static int rua_init_finish (progs_t *pr) { probj_t *probj = pr->pr_objective_resources; pr_class_t **class_list, **class; class_list = (pr_class_t **) Hash_GetList (probj->classes); if (*class_list) { pr_class_t *object_class; pr_ptr_t object_ptr; object_class = Hash_Find (probj->classes, "Object"); if (object_class && !object_class->super_class) object_ptr = (pr_type_t *)object_class - pr->pr_globals; else PR_Error (pr, "root class Object not found"); for (class = class_list; *class; class++) finish_class (probj, *class, object_ptr); } free (class_list); return 1; } static int rua_obj_init_runtime (progs_t *pr) { probj_t *probj = pr->pr_objective_resources; pr_def_t *def; dfunction_t *obj_forward; if ((def = PR_FindField (pr, ".this"))) pr->fields.this = def->ofs; probj->obj_forward = 0; if ((obj_forward = PR_FindFunction (pr, "__obj_forward"))) { probj->obj_forward = (intptr_t) (obj_forward - pr->pr_functions); } PR_AddLoadFinishFunc (pr, rua_init_finish); return 1; } static void rua_obj_cleanup (progs_t *pr, void *data) { unsigned i; __auto_type probj = (probj_t *) data; pr->pr_objective_resources = probj; Hash_FlushTable (probj->selector_hash); probj->selector_index = 0; probj->available_selectors = 0; probj->selector_block = 0; for (i = 0; i < probj->selector_index_max; i++) { obj_list_free (probj, probj->selector_sels[i]); probj->selector_sels[i] = 0; probj->selector_names[i] = 0; } for (dtable_t *dtable = probj->dtable_list; dtable; dtable = dtable->next) { free (dtable->imp); } dtable_reset (probj); Hash_FlushTable (probj->classes); Hash_FlushTable (probj->protocols); Hash_FlushTable (probj->load_methods); probj->unresolved_classes = 0; probj->unclaimed_categories = 0; probj->unclaimed_proto_list = 0; probj->module_list = 0; probj->class_tree_list = 0; } static void rua_obj_destroy (progs_t *pr, void *_res) { probj_t *probj = _res; dstring_delete (probj->msg); Hash_DelTable (probj->selector_hash); Hash_DelTable (probj->classes); Hash_DelTable (probj->protocols); Hash_DelTable (probj->load_methods); free (probj->selector_sels); free (probj->selector_names); free (probj->selector_argc); for (size_t i = 0; i < probj->obj_list_sets.size; i++) { free (probj->obj_list_sets.a[i]); } DARRAY_CLEAR (&probj->obj_list_sets); for (size_t i = 0; i < probj->class_tree_sets.size; i++) { free (probj->class_tree_sets.a[i]); } DARRAY_CLEAR (&probj->class_tree_sets); PR_RESDELMAP (probj->dtables); free (probj); } void RUA_Obj_Init (progs_t *pr, int secure) { probj_t *probj = calloc (1, sizeof (*probj)); probj->pr = pr; probj->selector_hash = Hash_NewTable (1021, selector_get_key, 0, probj, pr->hashctx); probj->classes = Hash_NewTable (1021, class_get_key, 0, probj, pr->hashctx); probj->protocols = Hash_NewTable (1021, protocol_get_key, 0, probj, pr->hashctx); probj->load_methods = Hash_NewTable (1021, 0, 0, probj, pr->hashctx); Hash_SetHashCompare (probj->load_methods, load_methods_get_hash, load_methods_compare); probj->msg = dstring_newstr(); DARRAY_INIT (&probj->obj_list_sets, 32); DARRAY_INIT (&probj->class_tree_sets, 32); PR_Sprintf_SetAtHandler (pr, rua_at_handler, probj); PR_Resources_Register (pr, "RUA_ObjectiveQuakeC", probj, rua_obj_cleanup, rua_obj_destroy); PR_RegisterBuiltins (pr, obj_methods, probj); PR_AddLoadFunc (pr, rua_obj_init_runtime); } pr_func_t RUA_Obj_msg_lookup (progs_t *pr, pr_ptr_t _self, pr_ptr_t __cmd) { probj_t *probj = pr->pr_objective_resources; pr_id_t *self = &G_STRUCT (pr, pr_id_t, _self); pr_sel_t *_cmd = &G_STRUCT (pr, pr_sel_t, __cmd); pr_func_t imp; if (!self) return 0; if (!_cmd) PR_RunError (pr, "null selector"); imp = obj_msg_lookup (probj, self, _cmd); if (!imp) PR_RunError (pr, "%s does not respond to %s", PR_GetString (pr, object_get_class_name (probj, self)), PR_GetString (pr, probj->selector_names[_cmd->sel_id])); return imp; } int RUA_obj_increment_retaincount (progs_t *pr, void *data) { rua_obj_increment_retaincount (pr, data); return R_INT (pr); } int RUA_obj_decrement_retaincount (progs_t *pr, void *data) { rua_obj_decrement_retaincount (pr, data); return R_INT (pr); }