[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.
This commit is contained in:
Bill Currie 2020-03-15 23:30:57 +09:00
parent ea042cf87a
commit 0fe3fda44d
5 changed files with 138 additions and 67 deletions

View file

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

View file

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

View file

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

View file

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

View file

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