From 0fe3fda44dd76e9648a3b1ec11b51296d38fc916 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sun, 15 Mar 2020 23:30:57 +0900 Subject: [PATCH] [qfcc] Fix protocol adorned id as message receiver This took a bit as type_id has no class data, only protocols attached to the type_obj_object instance, and then protocol lists can get deep. --- tools/qfcc/include/class.h | 8 ++- tools/qfcc/include/method.h | 3 + tools/qfcc/source/class.c | 107 ++++++++++++++++++++++++++++------- tools/qfcc/source/expr_obj.c | 73 +++++++++--------------- tools/qfcc/source/method.c | 14 +++++ 5 files changed, 138 insertions(+), 67 deletions(-) diff --git a/tools/qfcc/include/class.h b/tools/qfcc/include/class.h index a468e19b5..68cefcd12 100644 --- a/tools/qfcc/include/class.h +++ b/tools/qfcc/include/class.h @@ -113,6 +113,7 @@ struct dstring_s; struct expr_s; struct method_s; struct symbol_s; +struct selector_s; int obj_is_id (const struct type_s *type) __attribute__((pure)); int obj_is_class (const struct type_s *type) __attribute__((pure)); @@ -141,7 +142,7 @@ void class_finish_ivar_scope (class_type_t *class_type, struct symtab_s *param_scope); struct method_s *class_find_method (class_type_t *class_type, struct method_s *method); -struct method_s *class_message_response (class_t *class, int class_msg, +struct method_s *class_message_response (struct type_s *clstype, int class_msg, struct expr_s *sel); struct symbol_s *class_pointer_symbol (class_t *class_type); category_t *get_category (struct symbol_s *class_name, @@ -161,6 +162,11 @@ struct def_s *protocol_def (protocol_t *protocol); protocollist_t *new_protocol_list (void); protocollist_t *add_protocol (protocollist_t *protocollist, const char *name); int procollist_find_protocol (protocollist_t *protocollist, protocol_t *proto) __attribute__((pure)); +struct method_s *protocollist_find_method (protocollist_t *protocollist, + struct selector_s *selector, + int nstance) + __attribute__((pure)); + int compare_protocols (protocollist_t *protos1, protocollist_t *protos2) __attribute__((pure)); void print_protocollist (struct dstring_s *dstr, protocollist_t *protocollist); struct def_s *emit_protocol (protocol_t *protocol); diff --git a/tools/qfcc/include/method.h b/tools/qfcc/include/method.h index c64f04ade..598e151ee 100644 --- a/tools/qfcc/include/method.h +++ b/tools/qfcc/include/method.h @@ -98,6 +98,9 @@ keywordarg_t *copy_keywordargs (const keywordarg_t *kwargs); struct expr_s *send_message (int super); method_t *find_method (const char *sel_name); +method_t *methodlist_find_method (methodlist_t *methodlist, + selector_t *selector, int instance) + __attribute__((pure)); void selector_name (struct dstring_s *sel_id, keywordarg_t *selector); void method_types (struct dstring_s *sel_types, method_t *method); diff --git a/tools/qfcc/source/class.c b/tools/qfcc/source/class.c index d1322c174..6f4473e1d 100644 --- a/tools/qfcc/source/class.c +++ b/tools/qfcc/source/class.c @@ -1104,42 +1104,79 @@ class_find_method (class_type_t *class_type, method_t *method) return method; } +static method_t * +cls_find_method (methodlist_t *methodlist, selector_t *selector, + int class_msg, int is_root) +{ + method_t *m = 0; + m = methodlist_find_method (methodlist, selector, !class_msg); + if (!m && is_root && class_msg + && (m = methodlist_find_method (methodlist, selector, 1))) { + return m; + } + return m; +} + method_t * -class_message_response (class_t *class, int class_msg, expr_t *sel) +class_message_response (type_t *clstype, int class_msg, expr_t *sel) { selector_t *selector; method_t *m; - class_t *c = class; + class_t *c; + class_t *class = 0; category_t *cat; + dstring_t *dstr; selector = get_selector (sel); if (!selector) return 0; - if (class && class->type != &type_obj_object) { - if (!class->interface_declared) { - class->interface_declared = 1; - warning (0, "cannot find interface declaration for `%s'", - class->name); + + if (!obj_is_classptr (clstype) && !obj_is_class (clstype)) { + error (0, "neither class nor object"); + return 0; + } + if (obj_is_id (clstype)) { + protocollist_t *protos = clstype->t.fldptr.type->protos; + if (protos) { + if ((m = protocollist_find_method (protos, selector, !class_msg))) { + return m; + } + dstr = dstring_new (); + print_protocollist (dstr, protos); + warning (sel, "id%s may not respond to %c%s", dstr->str, + class_msg ? '+' : '-', selector->name); + dstring_delete (dstr); } - while (c) { - for (cat = c->categories; cat; cat = cat->next) { - for (m = cat->methods->head; m; m = m->next) { - if (((!c->super_class && class_msg) - || class_msg != m->instance) - && strcmp (selector->name, m->name) == 0) + } else { + if (obj_is_class (clstype)) { + class = clstype->t.class; + } else if (obj_is_class (clstype->t.fldptr.type)) { + class = clstype->t.fldptr.type->t.class; + } + if (class && class->type != &type_obj_object) { + if (!class->interface_declared) { + class->interface_declared = 1; + warning (0, "cannot find interface declaration for `%s'", + class->name); + } + c = class; + while (c) { + for (cat = c->categories; cat; cat = cat->next) { + if ((m = cls_find_method (cat->methods, selector, + class_msg, + !c->super_class))) { return m; + } } - } - for (m = c->methods->head; m; m = m->next) { - if (((!c->super_class && class_msg) - || class_msg != m->instance) - && strcmp (selector->name, m->name) == 0) + if ((m = cls_find_method (c->methods, selector, class_msg, + !c->super_class))) { return m; + } + c = c->super_class; } - c = c->super_class; + warning (sel, "%s may not respond to %c%s", class->name, + class_msg ? '+' : '-', selector->name); } - warning (sel, "%s may not respond to %c%s", class->name, - class_msg ? '+' : '-', selector->name); } m = find_method (selector->name); if (!m && (!class || class->type == &type_obj_object)) { @@ -1592,6 +1629,34 @@ procollist_find_protocol (protocollist_t *protocollist, protocol_t *proto) return 0; } +static method_t * +protocol_find_method (protocol_t *protocol, selector_t *selector, int instance) +{ + method_t *m = 0; + if (protocol->methods) { + m = methodlist_find_method (protocol->methods, selector, instance); + } + if (!m && protocol->protocols) { + return protocollist_find_method (protocol->protocols, selector, + instance); + } + return m; +} + +method_t * +protocollist_find_method (protocollist_t *protocollist, selector_t *selector, + int instance) +{ + method_t *m; + for (int i = 0; i < protocollist->count; i++) { + if ((m = protocol_find_method (protocollist->list[i], selector, + instance))) { + return m; + } + } + return 0; +} + int compare_protocols (protocollist_t *protos1, protocollist_t *protos2) { diff --git a/tools/qfcc/source/expr_obj.c b/tools/qfcc/source/expr_obj.c index fe04e2c98..6858adb54 100644 --- a/tools/qfcc/source/expr_obj.c +++ b/tools/qfcc/source/expr_obj.c @@ -179,62 +179,45 @@ message_expr (expr_t *receiver, keywordarg_t *message) expr_t *selector = selector_expr (message); expr_t *call; keywordarg_t *m; - int self = 0, super = 0, class_msg = 0; - type_t *rec_type; + int super = 0, class_msg = 0; + type_t *rec_type = 0; type_t *return_type; type_t *method_type = &type_IMP; - class_t *class = 0; method_t *method; expr_t *send_msg; - if (receiver->type == ex_symbol - && strcmp (receiver->e.symbol->name, "super") == 0) { - super = 1; + if (receiver->type == ex_nil) { + rec_type = &type_id; + convert_nil (receiver, rec_type); + } else if (receiver->type == ex_symbol) { + if (strcmp (receiver->e.symbol->name, "self") == 0) { + rec_type = get_type (receiver); + } else if (strcmp (receiver->e.symbol->name, "super") == 0) { + super = 1; - receiver = super_expr (current_class); + receiver = super_expr (current_class); - if (receiver->type == ex_error) - return receiver; - receiver = cast_expr (&type_id, receiver); //FIXME better way? - class = extract_class (current_class); - } else { - if (receiver->type == ex_symbol) { - if (strcmp (receiver->e.symbol->name, "self") == 0) - self = 1; - if (receiver->e.symbol->sy_type == sy_class) { - class = receiver->e.symbol->type->t.class; - class_msg = 1; - receiver = new_symbol_expr (class_pointer_symbol (class)); - } - } else if (receiver->type == ex_nil) { - convert_nil (receiver, &type_id); - } - rec_type = get_type (receiver); - - if (receiver->type == ex_error) - return receiver; - - if (rec_type == &type_id || rec_type == &type_Class) { - } else { - if (rec_type->type == ev_pointer) - rec_type = rec_type->t.fldptr.type; - if (!obj_is_class (rec_type)) - return error (receiver, "not a class/object"); - - if (self) { - if (!class) - class = extract_class (current_class); - if (rec_type == &type_obj_class) - class_msg = 1; - } else { - if (!class) - class = rec_type->t.class; - } + if (receiver->type == ex_error) + return receiver; + receiver = cast_expr (&type_id, receiver); //FIXME better way? + rec_type = extract_class (current_class)->type; + } else if (receiver->e.symbol->sy_type == sy_class) { + class_t *class; + rec_type = receiver->e.symbol->type; + class = rec_type->t.class; + class_msg = 1; + receiver = new_symbol_expr (class_pointer_symbol (class)); } } + if (!rec_type) { + rec_type = get_type (receiver); + } + + if (receiver->type == ex_error) + return receiver; return_type = &type_id; - method = class_message_response (class, class_msg, selector); + method = class_message_response (rec_type, class_msg, selector); if (method) return_type = method->type->t.func.type; diff --git a/tools/qfcc/source/method.c b/tools/qfcc/source/method.c index 5b818afad..e8e342c7a 100644 --- a/tools/qfcc/source/method.c +++ b/tools/qfcc/source/method.c @@ -375,6 +375,20 @@ find_method (const char *sel_name) return Hash_Find (known_methods, sel_name); } +method_t * +methodlist_find_method (methodlist_t *methodlist, selector_t *selector, + int instance) +{ + method_t *m; + + for (m = methodlist->head; m; m = m->next) { + if (m->instance == instance && strcmp (selector->name, m->name) == 0) { + return m; + } + } + return 0; +} + void selector_name (dstring_t *sel_id, keywordarg_t *selector) {