From 65a5e4f2a49428629e86135f95b6bd1de15c275d Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Thu, 5 Mar 2020 12:52:37 +0900 Subject: [PATCH] [qfcc] Allow inherited methods to satisfy protocols I suspect that the current state of things will produce problems later on, but this works for now. --- tools/qfcc/include/method.h | 9 ++++++- tools/qfcc/source/class.c | 18 +++++++++++-- tools/qfcc/source/method.c | 50 +++++++++++++++++++++++++++++++++++-- 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/tools/qfcc/include/method.h b/tools/qfcc/include/method.h index 6b02bbe00..c64f04ade 100644 --- a/tools/qfcc/include/method.h +++ b/tools/qfcc/include/method.h @@ -58,6 +58,10 @@ typedef struct methodlist_s { int instance; ///< used only for emitting } methodlist_t; +typedef struct methodset_s { + struct hashtab_s *tab; +} methodset_t; + typedef struct keywordarg_s { // the first two fields match the first two fields of param_t in // functionl.h @@ -80,9 +84,12 @@ struct symbol_s *method_symbol (struct class_type_s *class_type, void method_set_param_names (method_t *dst, method_t *src); methodlist_t *new_methodlist (void); +methodset_t *new_methodset (void); +void methodset_add_methods (methodset_t *methodset, methodlist_t *methods); +int methodset_contains_method (methodset_t *methodset, method_t *method); //NOTE frees the source list and any methods not copied void merge_method_lists (methodlist_t *dst, methodlist_t *src); -void copy_methods (methodlist_t *dst, methodlist_t *src); +void copy_methods (methodlist_t *dst, methodlist_t *src, methodset_t *except); int method_compare (method_t *m1, method_t *m2); keywordarg_t *new_keywordarg (const char *selector, struct expr_s *expr); diff --git a/tools/qfcc/source/class.c b/tools/qfcc/source/class.c index 4b331d237..5a9c5c6e0 100644 --- a/tools/qfcc/source/class.c +++ b/tools/qfcc/source/class.c @@ -642,16 +642,23 @@ class_add_protocols (class_t *class, protocollist_t *protocols) int i; protocol_t *p; methodlist_t *methods; + methodset_t *except; + class_t *super; if (!protocols) return; methods = class->methods; + except = new_methodset (); + for (super = class->super_class; super; super = super->super_class) { + methodset_add_methods (except, super->methods); + } + for (i = 0; i < protocols->count; i++) { p = protocols->list[i]; if (p->methods) { - copy_methods (methods, p->methods); + copy_methods (methods, p->methods, except); } else { warning (0, "definition of protocol `%s' not found", p->name); } @@ -1269,15 +1276,22 @@ category_add_protocols (category_t *category, protocollist_t *protocols) int i; protocol_t *p; methodlist_t *methods; + methodset_t *except; + class_t *class; if (!protocols) return; methods = category->methods; + except = new_methodset (); + for (class = category->class; class; class = class->super_class) { + methodset_add_methods (except, class->methods); + } + for (i = 0; i < protocols->count; i++) { p = protocols->list[i]; - copy_methods (methods, p->methods); + copy_methods (methods, p->methods, except); if (p->protocols) category_add_protocols (category, p->protocols); } diff --git a/tools/qfcc/source/method.c b/tools/qfcc/source/method.c index c5bd8b334..e68ad857f 100644 --- a/tools/qfcc/source/method.c +++ b/tools/qfcc/source/method.c @@ -205,6 +205,52 @@ new_methodlist (void) return l; } +static uintptr_t +methodset_get_hash (const void *_method, void *unused) +{ + method_t *method = (method_t *) _method; + uintptr_t hash; + + hash = Hash_String (method->name); + return hash ^ (method->instance << 3); +} + +static int +methodset_compare (const void *_m1, const void *_m2, void *unused) +{ + method_t *m1 = (method_t *) _m1; + method_t *m2 = (method_t *) _m2; + int cmp; + + cmp = strcmp (m1->name, m2->name) == 0; + return cmp && m1->instance == m2->instance; +} + +methodset_t * +new_methodset (void) +{ + methodset_t *s = malloc (sizeof (*s)); + s->tab = Hash_NewTable (31, 0, 0, 0); + Hash_SetHashCompare (s->tab, methodset_get_hash, methodset_compare); + return s; +} + +void +methodset_add_methods (methodset_t *methodset, methodlist_t *methods) +{ + method_t *m; + + for (m = methods->head; m; m = m->next) { + Hash_AddElement (methodset->tab, m); + } +} + +int +methodset_contains_method (methodset_t *methodset, method_t *method) +{ + return Hash_FindElement (methodset->tab, method) != 0; +} + static int __attribute__((pure)) method_in_list (methodlist_t *method_list, method_t *method) { @@ -238,13 +284,13 @@ merge_method_lists (methodlist_t *dst, methodlist_t *src) } void -copy_methods (methodlist_t *dst, methodlist_t *src) +copy_methods (methodlist_t *dst, methodlist_t *src, methodset_t *except) { method_t *s, *d; param_t *self; for (s = src->head; s; s = s->next) { - if (method_in_list (dst, s)) { + if (methodset_contains_method (except, s) || method_in_list (dst, s)) { debug (0, "skipping duplicate method: %s", s->name); continue; }