diff --git a/c/class_resolver.c b/c/class_resolver.c index ac3939c..5ba8664 100644 --- a/c/class_resolver.c +++ b/c/class_resolver.c @@ -382,28 +382,87 @@ struct method_info * class_resolver_lookup_method(int methods_hash_table_length, return (struct method_info *)e->value; } +struct method_entry class_resolver_lookup_method_from_interfacemethodref_index(int class_hash_table_length, + struct hash_table_entry * class_hash_table, + int32_t interfacemethodref_index, + struct class_entry * objectref_class_entry, + struct class_entry * origin_class_entry) +{ + struct constant * interfacemethodref_constant = &origin_class_entry->class_file->constant_pool[interfacemethodref_index - 1]; + assert(interfacemethodref_constant->tag == CONSTANT_InterfaceMethodref); + struct constant * nameandtype_constant = &origin_class_entry->class_file->constant_pool[interfacemethodref_constant->interfacemethodref.name_and_type_index - 1]; + assert(nameandtype_constant->tag == CONSTANT_NameAndType); + struct constant * method_name_constant = &origin_class_entry->class_file->constant_pool[nameandtype_constant->nameandtype.name_index - 1]; + assert(method_name_constant->tag == CONSTANT_Utf8); + struct constant * method_descriptor_constant = &origin_class_entry->class_file->constant_pool[nameandtype_constant->nameandtype.descriptor_index - 1]; + assert(method_descriptor_constant->tag == CONSTANT_Utf8); + + struct class_entry * class_entry = objectref_class_entry; + + while (true) { + struct method_info * method_info = class_resolver_lookup_method(class_entry->methods.length, + class_entry->methods.entry, + method_name_constant->utf8.bytes, + method_name_constant->utf8.length, + method_descriptor_constant->utf8.bytes, + method_descriptor_constant->utf8.length); + if (method_info != nullptr) { + debugf("method resolved:\n"); + debugf(" class: "); + debug_print__class_entry__class_name(class_entry); + debugf("\n method: "); + debug_print__method_info__method_name(class_entry, method_info); + debugc('\n'); + + return (struct method_entry){ + .class_entry = class_entry, + .method_info = method_info + }; + } + + if (class_entry->class_file->super_class == 0) + break; + + struct constant * class_constant = &class_entry->class_file->constant_pool[class_entry->class_file->super_class - 1]; + assert(class_constant->tag == CONSTANT_Class); + struct constant * class_name_constant = &class_entry->class_file->constant_pool[class_constant->class.name_index - 1]; + assert(class_name_constant->tag == CONSTANT_Utf8); + print_utf8_string(class_name_constant); + debugf("\n"); + + // lookup the method from the superclass + class_entry = class_resolver_lookup_class_from_class_index(class_hash_table_length, + class_hash_table, + class_entry, + class_entry->class_file->super_class); + assert(class_entry != nullptr); + } + + assert(!"invokeinterface method does not exist"); +} + struct method_entry * class_resolver_lookup_method_from_methodref_index(int class_hash_table_length, struct hash_table_entry * class_hash_table, int32_t methodref_index, - struct class_entry * original_class_entry) + struct class_entry * origin_class_entry) { - if (original_class_entry->attribute_entry[methodref_index - 1].method_entry != nullptr) { + if (origin_class_entry->attribute_entry[methodref_index - 1].method_entry != nullptr) { debugf("class_resolver_lookup_method_from_methodref_index %d: [cached]\n", methodref_index); - return original_class_entry->attribute_entry[methodref_index - 1].method_entry; + return origin_class_entry->attribute_entry[methodref_index - 1].method_entry; } - struct constant * methodref_constant = &original_class_entry->class_file->constant_pool[methodref_index - 1]; + struct constant * methodref_constant = &origin_class_entry->class_file->constant_pool[methodref_index - 1]; assert(methodref_constant->tag == CONSTANT_Methodref); - struct constant * nameandtype_constant = &original_class_entry->class_file->constant_pool[methodref_constant->methodref.name_and_type_index - 1]; + struct constant * nameandtype_constant = &origin_class_entry->class_file->constant_pool[methodref_constant->methodref.name_and_type_index - 1]; assert(nameandtype_constant->tag == CONSTANT_NameAndType); - struct constant * method_name_constant = &original_class_entry->class_file->constant_pool[nameandtype_constant->nameandtype.name_index - 1]; + struct constant * method_name_constant = &origin_class_entry->class_file->constant_pool[nameandtype_constant->nameandtype.name_index - 1]; assert(method_name_constant->tag == CONSTANT_Utf8); - struct constant * method_descriptor_constant = &original_class_entry->class_file->constant_pool[nameandtype_constant->nameandtype.descriptor_index - 1]; + struct constant * method_descriptor_constant = &origin_class_entry->class_file->constant_pool[nameandtype_constant->nameandtype.descriptor_index - 1]; assert(method_descriptor_constant->tag == CONSTANT_Utf8); struct class_entry * class_entry = class_resolver_lookup_class_from_class_index(class_hash_table_length, class_hash_table, - original_class_entry, + origin_class_entry, methodref_constant->methodref.class_index); while (true) { @@ -425,7 +484,7 @@ struct method_entry * class_resolver_lookup_method_from_methodref_index(int clas struct method_entry * method_entry = malloc_class_arena((sizeof (struct method_entry))); method_entry->class_entry = class_entry; method_entry->method_info = method_info; - original_class_entry->attribute_entry[methodref_index - 1].method_entry = method_entry; + origin_class_entry->attribute_entry[methodref_index - 1].method_entry = method_entry; return method_entry; } diff --git a/c/class_resolver.h b/c/class_resolver.h index bc6d419..2d909aa 100644 --- a/c/class_resolver.h +++ b/c/class_resolver.h @@ -71,10 +71,15 @@ struct method_info * class_resolver_lookup_method(int methods_hash_table_length, int method_name_length, const uint8_t * method_descriptor, int method_descriptor_length); +struct method_entry class_resolver_lookup_method_from_interfacemethodref_index(int class_hash_table_length, + struct hash_table_entry * class_hash_table, + int32_t interfacemethodref_index, + struct class_entry * objectref_class_entry, + struct class_entry * origin_class_entry); struct method_entry * class_resolver_lookup_method_from_methodref_index(int class_hash_table_length, struct hash_table_entry * class_hash_table, int32_t methodref_index, - struct class_entry * original_class_entry); + struct class_entry * origin_class_entry); struct field_entry * class_resolver_lookup_field(int fields_hash_table_length, struct hash_table_entry * fields_hash_table, const uint8_t * field_name, diff --git a/c/execute.c b/c/execute.c index 655a289..f4b54e9 100644 --- a/c/execute.c +++ b/c/execute.c @@ -1117,7 +1117,18 @@ void op_invokedynamic(struct vm * vm, uint32_t index) void op_invokeinterface(struct vm * vm, uint32_t index, uint32_t count) { - assert(!"op_invokeinterface"); + int32_t * objectref = (int32_t *)operand_stack_peek_u32(vm->current_frame, count); + assert(objectref != nullptr); + struct class_entry * class_entry = (struct class_entry *)objectref[0]; + + struct method_entry method_entry = + class_resolver_lookup_method_from_interfacemethodref_index(vm->class_hash_table.length, + vm->class_hash_table.entry, + index, + class_entry, + vm->current_frame->class_entry); + + vm_special_method_call(vm, method_entry.class_entry, method_entry.method_info); } void op_invokespecial(struct vm * vm, uint32_t index) diff --git a/c/frame.h b/c/frame.h index 270c8e9..74fa9c1 100644 --- a/c/frame.h +++ b/c/frame.h @@ -73,6 +73,13 @@ static inline void operand_stack_push_u32(struct frame * frame, uint32_t value) frame->operand_stack_ix++; } +static inline uint32_t operand_stack_peek_u32(struct frame * frame, int index) +{ + assert((frame->operand_stack_ix - index) >= 0); + uint32_t value = frame->operand_stack[frame->operand_stack_ix - index]; + return value; +} + static inline uint32_t operand_stack_pop_u32(struct frame * frame) { frame->operand_stack_ix--; diff --git a/p/InvokeInterfaceTest.java b/p/InvokeInterfaceTest.java new file mode 100644 index 0000000..19d7102 --- /dev/null +++ b/p/InvokeInterfaceTest.java @@ -0,0 +1,21 @@ +package p; + +interface Op { + public int op(int a, int b); +} + +class Foo implements Op { + public int op(int a, int b) { + return a + b; + } +} + +public class InvokeInterfaceTest { + static int test(Op operator) { + return operator.op(5, 10); + } + + public static void main() { + test(new Foo()); + } +}