[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.
This commit is contained in:
Bill Currie 2020-03-05 12:52:37 +09:00
parent 1459361cbd
commit 65a5e4f2a4
3 changed files with 72 additions and 5 deletions

View file

@ -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);

View file

@ -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);
}

View file

@ -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;
}