/* function.c QC function support code Copyright (C) 2001 Bill Currie Author: Bill Currie Date: 2002/5/7 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 */ static const char rcsid[] = "$Id$"; #include "QF/dstring.h" #include "QF/hash.h" #include "QF/pr_obj.h" #include "QF/va.h" #include "qfcc.h" #include "class.h" #include "expr.h" #include "method.h" #include "struct.h" #include "type.h" static hashtab_t *class_hash; static hashtab_t *category_hash; static hashtab_t *protocol_hash; class_t class_id = {1, "id", 0, 0, 0, 0, 0, 0, &type_id}; class_t class_Class = {1, "Class", 0, 0, 0, 0, 0, 0, &type_Class}; class_t class_Protocol = {1, "Protocl", 0, 0, 0, 0, 0, 0, &type_Protocol}; static const char * class_get_key (void *class, void *unused) { return ((class_t *) class)->class_name; } static const char * protocol_get_key (void *protocol, void *unused) { return ((protocol_t *) protocol)->name; } class_t * get_class (const char *name, int create) { class_t *c; type_t new; if (!class_hash) class_hash = Hash_NewTable (1021, class_get_key, 0, 0); if (name) { c = Hash_Find (class_hash, name); if (c || !create) return c; } c = calloc (sizeof (class_t), 1); c->class_name = name; new = *type_Class.aux_type; new.class = c; c->type = pointer_type (find_type (&new)); if (name) Hash_Add (class_hash, c); return c; } void class_add_methods (class_t *class, methodlist_t *methods) { if (!methods) return; if (!class->methods) class->methods = new_methodlist (); *class->methods->tail = methods->head; class->methods->tail = methods->tail; free (methods); } void class_add_protocol_methods (class_t *class, expr_t *protocols) { expr_t *e; protocol_t *p; if (!protocol_hash) protocol_hash = Hash_NewTable (1021, protocol_get_key, 0, 0); if (!class->methods) class->methods = new_methodlist (); for (e = protocols; e; e = e->next) { if (!(p = get_protocol (e->e.string_val, 0))) { error (e, "undefined protocol `%s'", e->e.string_val); continue; } copy_methods (class->methods, p->methods); } } void class_add_protocol (class_t *class, protocol_t *protocol) { if (!class->protocols) class->protocols = new_protocollist (); add_protocol (class->protocols, protocol); } void class_begin (class_t *class) { current_class = class; if (class->def) return; if (class->class_name && class->category_name) { def_t *category_def; category_def = PR_GetDef (type_category, va ("_OBJ_CATEGORY_%s_%s", class->class_name, class->category_name), 0, &numpr_globals); category_def->initialized = category_def->constant = 1; } else if (class->class_name) { def_t *meta_def; pr_class_t *meta; pr_class_t *cls; meta_def = PR_GetDef (type_Class.aux_type, va ("_OBJ_METACLASS_%s", class->class_name), 0, &numpr_globals); meta_def->initialized = meta_def->constant = 1; meta = &G_STRUCT (pr_class_t, meta_def->ofs); meta->class_pointer = ReuseString (class->class_name); if (class->super_class) meta->super_class = ReuseString (class->super_class->class_name); meta->name = meta->class_pointer; meta->info = _PR_CLS_META; meta->instance_size = type_size (type_Class.aux_type); meta->ivars = emit_struct (type_Class.aux_type, "Class"); meta->protocols = emit_protocol_list (class->protocols, class->class_name); class->def = PR_GetDef (type_Class.aux_type, va ("_OBJ_CLASS_%s", class->class_name), 0, &numpr_globals); class->def->initialized = class->def->constant = 1; cls = &G_STRUCT (pr_class_t, class->def->ofs); cls->class_pointer = meta_def->ofs; cls->super_class = meta->super_class; cls->name = meta->name; meta->info = _PR_CLS_CLASS; cls->protocols = meta->protocols; } } void class_finish (class_t *class) { if (class->class_name && class->category_name) { pr_category_t *category; category = &G_STRUCT (pr_category_t, class->def->ofs); } else if (class->class_name) { pr_class_t *meta; pr_class_t *cls; cls = &G_STRUCT (pr_class_t, class->def->ofs); meta = &G_STRUCT (pr_class_t, cls->class_pointer); meta->methods = emit_methods (class->methods, class->class_name, 0); cls->instance_size = type_size (class->ivars); cls->ivars = emit_struct (class->ivars, class->class_name); cls->methods = emit_methods (class->methods, class->class_name, 1); } } struct_field_t * class_find_ivar (class_t *class, int protected, const char *name) { struct_field_t *ivar; class_t *c; ivar = struct_find_field (class->ivars, name); if (ivar) { if (protected && ivar->visibility != vis_public) goto access_error; return ivar; } for (c = class->super_class; c; c = c->super_class) { ivar = struct_find_field (c->ivars, name); if (ivar) { if (ivar->visibility == vis_private || (protected && ivar->visibility == vis_protected)) goto access_error; return ivar; } } error (0, "%s.%s does not exist", class->class_name, name); return 0; access_error: error (0, "%s.%s is not accessable here", class->class_name, name); return 0; } expr_t * class_ivar_expr (class_t *class, const char *name) { struct_field_t *ivar; class_t *c; if (!class) return 0; ivar = struct_find_field (class->ivars, name); if (!ivar) { for (c = class->super_class; c; c = c->super_class) { ivar = struct_find_field (c->ivars, name); if (ivar) break; } } if (!ivar) return 0; if (ivar->visibility == vis_private) { error (0, "%s.%s is not accessable here", class->class_name, name); return 0; } return binary_expr ('.', new_name_expr ("self"), new_name_expr (name)); } method_t * class_find_method (class_t *class, method_t *method) { method_t *m; dstring_t *sel; for (m = class->methods->head; m; m = m->next) if (method_compare (method, m)) return m; sel = dstring_newstr (); selector_name (sel, (keywordarg_t *)method->selector); warning (0, "%s method %s not in %s%s", method->instance ? "instance" : "class", sel->str, class->class_name, class->category_name ? va (" (%s)", class->category_name) : ""); dstring_delete (sel); return method; } method_t * class_message_response (class_t *class, expr_t *sel) { pr_sel_t *selector; char *sel_name; method_t *m; class_t *c = class; if (sel->type != ex_pointer && sel->e.pointer.type != type_SEL.aux_type) { error (sel, "not a selector"); return 0; } selector = &G_STRUCT (pr_sel_t, sel->e.pointer.val); sel_name = strings + selector->sel_id; while (c) { for (m = c->methods->head; m; m = m->next) { if (strcmp (sel_name, m->name) == 0) return m; } c = c->super_class; } warning (sel, "%s does not respond to %s", class->class_name, sel_name); return 0; } static unsigned long category_get_hash (void *_c, void *unused) { class_t *c = (class_t *) _c; return Hash_String (c->class_name) ^ Hash_String (c->category_name); } static int category_compare (void *_c1, void *_c2, void *unused) { class_t *c1 = (class_t *) _c1; class_t *c2 = (class_t *) _c2; return strcmp (c1->class_name, c2->class_name) == 0 && strcmp (c1->category_name, c2->category_name) == 0; } void class_add_ivars (class_t *class, struct type_s *ivars) { class->ivars = ivars; } void class_check_ivars (class_t *class, struct type_s *ivars) { if (!struct_compare_fields (class->ivars, ivars)) warning (0, "instance variable missmatch for %s", class->class_name); class->ivars = ivars; } class_t * get_category (const char *class_name, const char *category_name, int create) { class_t *c; if (!category_hash) { category_hash = Hash_NewTable (1021, 0, 0, 0); Hash_SetHashCompare (category_hash, category_get_hash, category_compare); } if (class_name && category_name) { class_t _c = {0, class_name, category_name}; c = Hash_FindElement (category_hash, &_c); if (c || !create) return c; } c = calloc (sizeof (class_t), 1); c->class_name = class_name; c->class_name = category_name; if (class_name && category_name) Hash_AddElement (category_hash, c); return c; } def_t * class_def (class_t *class) { def_t *def; def = PR_GetDef (class->type, va ("_OBJ_CLASS_POINTER_%s", class->class_name), 0, &numpr_globals); if (def->initialized) return def; if (class->def) { //FIXME need externals? G_INT (def->ofs) = class->def->ofs; } else { warning (0, "%s not implemented", class->class_name); } def->initialized = def->constant = 1; return def; } protocol_t * get_protocol (const char *name, int create) { protocol_t *p; if (name) { p = Hash_Find (protocol_hash, name); if (p || !create) return p; } p = calloc (sizeof (protocol_t), 1); p->name = name; if (name) Hash_Add (protocol_hash, p); return p; } void protocol_add_methods (protocol_t *protocol, methodlist_t *methods) { if (!methods) return; if (!protocol->methods) protocol->methods = new_methodlist (); *protocol->methods->tail = methods->head; protocol->methods->tail = methods->tail; free (methods); } void protocol_add_protocol_methods (protocol_t *protocol, expr_t *protocols) { expr_t *e; protocol_t *p; if (!protocol->methods) protocol->methods = new_methodlist (); for (e = protocols; e; e = e->next) { if (!(p = get_protocol (e->e.string_val, 0))) { error (e, "undefined protocol `%s'", e->e.string_val); continue; } copy_methods (protocol->methods, p->methods); } } def_t * protocol_def (protocol_t *protocol) { return PR_GetDef (&type_Protocol, protocol->name, 0, &numpr_globals); } protocollist_t * new_protocollist (void) { protocollist_t *protocollist = malloc (sizeof (protocollist_t)); protocollist->count = 0; protocollist->list = 0; return protocollist; } void add_protocol (protocollist_t *protocollist, protocol_t *protocol) { protocollist->list = realloc (protocollist->list, (protocollist->count + 1) * sizeof (protocollist_t)); protocollist->list[protocollist->count++] = protocol; } int emit_protocol (protocol_t *protocol) { def_t *proto_def; pr_protocol_t *proto; proto_def = PR_GetDef (type_Protocol.aux_type, va ("_OBJ_PROTOCOL_%s", protocol->name), 0, &numpr_globals); proto_def->initialized = proto_def->constant = 1; proto = &G_STRUCT (pr_protocol_t, proto_def->ofs); proto->class_pointer = 0; proto->protocol_name = ReuseString (protocol->name); proto->protocol_list = emit_protocol_list (protocol->protocols, va ("PROTOCOL_%s", protocol->name)); proto->instance_methods = emit_methods (protocol->methods, protocol->name, 1); proto->class_methods = emit_methods (protocol->methods, protocol->name, 0); return proto_def->ofs; } int emit_protocol_list (protocollist_t *protocols, const char *name) { def_t *proto_list_def; type_t *protocol_list; pr_protocol_list_t *proto_list; int i; if (!protocols) return 0; protocol_list = new_struct (0); new_struct_field (protocol_list, &type_pointer, "next", vis_public); new_struct_field (protocol_list, &type_integer, "count", vis_public); for (i = 0; i < protocols->count; i++) new_struct_field (protocol_list, &type_pointer, 0, vis_public); proto_list_def = PR_GetDef (type_Protocol.aux_type, va ("_OBJ_PROTOCOLS_%s", name), 0, &numpr_globals); proto_list_def->initialized = proto_list_def->constant = 1; proto_list = &G_STRUCT (pr_protocol_list_t, proto_list_def->ofs); proto_list->next = 0; proto_list->count = protocols->count; for (i = 0; i < protocols->count; i++) proto_list->list[i] = emit_protocol (protocols->list[i]); return proto_list_def->ofs; }