diff --git a/Makefile b/Makefile index fb05457..6cac559 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,9 @@ OBJ = \ c/malloc.o \ c/file.o \ c/execute.o \ - c/memory_allocator.o + c/memory_allocator.o \ + c/class_resolver.o \ + c/hash_table.o MAIN_OBJ = \ $(OBJ) \ @@ -24,7 +26,7 @@ PRINT_CLASS_OBJ = \ CC ?= gcc ARCH = -m32 CFLAGS ?= -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -std=c2x -DDEBUG -g -OPT ?= -Og +OPT ?= -O0 DEPFLAGS = -MMD -MP %.o: %.c diff --git a/c/a.out b/c/a.out index ef552a8..82903c4 100755 Binary files a/c/a.out and b/c/a.out differ diff --git a/c/class_resolver.c b/c/class_resolver.c new file mode 100644 index 0000000..e4ee467 --- /dev/null +++ b/c/class_resolver.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include + +#include "class_file.h" +#include "file.h" +#include "hash_table.h" +#include "malloc.h" +#include "class_resolver.h" +#include "string.h" +#include "debug_class_file.h" + +struct hash_table_entry * class_resolver_load_from_filenames(const char * filenames[], int length, int * hash_table_length) +{ + int class_hash_table_length = length * 2; + uint32_t class_hash_table_size = (sizeof (struct hash_table_entry)) * class_hash_table_length; + struct hash_table_entry * class_hash_table = malloc_class_arena(class_hash_table_size); + uint32_t class_entry_size = (sizeof (struct class_entry)) * length; + struct class_entry * class_entry = malloc_class_arena(class_entry_size); + + for (int i = 0; i < length; i++) { + uint32_t filename_length = string_length(filenames[i]); + const char * suffix = ".class"; + uint32_t suffix_length = string_length(suffix); + const char * filename_suffix = &filenames[i][filename_length - suffix_length]; + if (filename_length <= suffix_length || !string_equal(suffix, filename_suffix)) { + printf("invalid class filename: %s\n", filenames[i]); + continue; + } + uint32_t class_name_length = filename_length - suffix_length; + + printf("load class: %s\n", filenames[i]); + + uint8_t * buf = file_read(filenames[i]); + struct class_file * class_file = class_file_parse(buf); + free(buf); + + assert(class_file->magic == 0xcafebabe); + + class_entry[i].class_file = class_file; + + hash_table_add(class_hash_table_length, + class_hash_table, + (const uint8_t *)filenames[i], + class_name_length, + &class_entry[i]); + + // make hash table for interfaces + /* + if (class_file->interfaces_count != 0) { + uint32_t interfaces_hash_table_size = (sizeof (struct hash_table_entry)) * class_file->interfaces_count * 3 / 2; + struct hash_table_entry * interfaces_hash_table = malloc_class_arena(interfaces_hash_table_size); + for (int i = 0; i < class_file->interfaces_count; i++) { + + } + } + */ + // make hash table for fields + if (class_file->fields_count != 0) { + int fields_hash_table_length = class_file->fields_count * 2; + uint32_t fields_hash_table_size = (sizeof (struct hash_table_entry)) * fields_hash_table_length; + struct hash_table_entry * fields_hash_table = malloc_class_arena(fields_hash_table_size); + uint32_t field_entry_size = (sizeof (struct field_entry)) * length; + struct field_entry * field_entry = malloc_class_arena(field_entry_size); + for (int i = 0; i < class_file->fields_count; i++) { + u2 name_index = class_file->fields[i].name_index; + struct constant * name_constant = &class_file->constant_pool[name_index - 1]; + assert(name_constant->tag == CONSTANT_Utf8); + printf("hash table entry for field: "); + print_utf8_string(name_constant); + printf("\n"); + + field_entry[i].field_info = &class_file->fields[i]; + field_entry[i].value = 0; + + hash_table_add(fields_hash_table_length, + fields_hash_table, + name_constant->utf8.bytes, + name_constant->utf8.length, + (void *)&field_entry[i]); + } + + class_entry[i].fields.length = fields_hash_table_length; + class_entry[i].fields.entry = fields_hash_table; + } + + // make hash table for methods + if (class_file->methods_count != 0) { + int methods_hash_table_length = class_file->methods_count * 2; + uint32_t methods_hash_table_size = (sizeof (struct hash_table_entry)) * methods_hash_table_length; + struct hash_table_entry * methods_hash_table = malloc_class_arena(methods_hash_table_size); + for (int i = 0; i < class_file->methods_count; i++) { + u2 name_index = class_file->methods[i].name_index; + struct constant * name_constant = &class_file->constant_pool[name_index - 1]; + assert(name_constant->tag == CONSTANT_Utf8); + printf("hash table entry for method: "); + print_utf8_string(name_constant); + printf("\n"); + + hash_table_add(methods_hash_table_length, + methods_hash_table, + name_constant->utf8.bytes, + name_constant->utf8.length, + (void *)&class_file->methods[i]); + } + + class_entry[i].methods.length = methods_hash_table_length; + class_entry[i].methods.entry = methods_hash_table; + } + }; + + *hash_table_length = class_hash_table_length; + + return class_hash_table; +} + +struct class_entry * class_resolver_lookup_class(int class_hash_table_length, + struct hash_table_entry * class_hash_table, + const uint8_t * class_name, + int class_name_length) +{ + struct hash_table_entry * e = hash_table_find(class_hash_table_length, + class_hash_table, + class_name, + class_name_length); + assert(e != nullptr); + + return (struct class_entry *)e->value; +} + + +struct field_entry * class_resolver_lookup_field(struct class_entry * class_entry, + const uint8_t * field_name, + int field_name_length) +{ + int fields_hash_table_length = class_entry->fields.length; + struct hash_table_entry * fields_hash_table = class_entry->fields.entry; + + struct hash_table_entry * e = hash_table_find(fields_hash_table_length, + fields_hash_table, + field_name, + field_name_length); + assert(e != nullptr); + + return (struct field_entry *)e->value; +} + +struct method_info * class_resolver_lookup_method(struct class_entry * class_entry, + const uint8_t * method_name, + int method_name_length) +{ + int methods_hash_table_length = class_entry->methods.length; + struct hash_table_entry * methods_hash_table = class_entry->methods.entry; + + struct hash_table_entry * e = hash_table_find(methods_hash_table_length, + methods_hash_table, + method_name, + method_name_length); + assert(e != nullptr); + + return (struct method_info *)e->value; +} diff --git a/c/class_resolver.h b/c/class_resolver.h new file mode 100644 index 0000000..90805c7 --- /dev/null +++ b/c/class_resolver.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include "class_file.h" +#include "hash_table.h" + +struct field_entry { + struct field_info * field_info; + uint32_t value; +}; + +struct class_entry { + struct class_file * class_file; + + struct { + int length; + struct hash_table_entry * entry; + } interfaces; + + struct { + int length; + struct hash_table_entry * entry; + } fields; + + struct { + int length; + struct hash_table_entry * entry; + } methods; +}; + +struct hash_table_entry * class_resolver_load_from_filenames(const char * filenames[], int length, int * hash_table_length); +struct class_entry * class_resolver_lookup_class(int class_hash_table_length, + struct hash_table_entry * class_hash_table, + const uint8_t * class_name, + int class_name_length); +struct method_info * class_resolver_lookup_method(struct class_entry * class_entry, + const uint8_t * method_name, + int method_name_length); +struct field_entry * class_resolver_lookup_field(struct class_entry * class_entry, + const uint8_t * field_name, + int field_name_length); diff --git a/c/debug_class_file.c b/c/debug_class_file.c index 21c1dfa..2f4eb2d 100644 --- a/c/debug_class_file.c +++ b/c/debug_class_file.c @@ -8,6 +8,13 @@ #include "decode.h" #include "debug_class_file.h" +void print_utf8_string(struct constant * constant) +{ + for (int i = 0; i < constant->utf8.length; i++) { + fputc(constant->utf8.bytes[i], stdout); + } +} + void print_constant(struct constant * constant) { switch (constant->tag) { @@ -60,10 +67,8 @@ void print_constant(struct constant * constant) case CONSTANT_Utf8: printf("CONSTANT_Utf8 length=%d bytes=", constant->utf8.length); - for (int i = 0; i < constant->utf8.length; i++) { - fputc(constant->utf8.bytes[i], stdout); - } - fputc('\n', stdout); + print_utf8_string(constant); + printf("\n"); break; case CONSTANT_MethodHandle: printf("CONSTANT_MethodHandle reference_kind=%d reference_index=%d\n", diff --git a/c/debug_class_file.h b/c/debug_class_file.h index 895039d..4a543d7 100644 --- a/c/debug_class_file.h +++ b/c/debug_class_file.h @@ -2,6 +2,7 @@ #include "class_file.h" +void print_utf8_string(struct constant * constant); void print_constant(struct constant * constant); void print_attribute(const char * indent, struct attribute_info * attribute, struct constant * constant_pool); void print_class_file(struct class_file * class_file); diff --git a/c/execute.c b/c/execute.c index 8628d91..3d5bcee 100644 --- a/c/execute.c +++ b/c/execute.c @@ -3,6 +3,7 @@ #include "execute.h" #include "memory_allocator.h" #include "bswap.h" +#include "class_resolver.h" void op_aaload(struct vm * vm) { @@ -449,7 +450,40 @@ void op_getfield(struct vm * vm, uint32_t index) void op_getstatic(struct vm * vm, uint32_t index) { - assert(!"op_getstatic"); + struct constant * fieldref_constant = &vm->current_thread.current_class->constant_pool[index - 1]; + #ifdef DEBUG + assert(fieldref_constant->tag == CONSTANT_Fieldref); + #endif + struct constant * class_constant = &vm->current_thread.current_class->constant_pool[fieldref_constant->fieldref.class_index - 1]; + #ifdef DEBUG + assert(class_constant->tag == CONSTANT_Class); + #endif + struct constant * nameandtype_constant = &vm->current_thread.current_class->constant_pool[fieldref_constant->fieldref.name_and_type_index - 1]; + #ifdef DEBUG + assert(nameandtype_constant->tag == CONSTANT_NameAndType); + #endif + struct constant * class_name_constant = &vm->current_thread.current_class->constant_pool[class_constant->class.name_index - 1]; + #ifdef DEBUG + assert(class_name_constant->tag == CONSTANT_Utf8); + #endif + struct constant * field_name_constant = &vm->current_thread.current_class->constant_pool[nameandtype_constant->nameandtype.name_index - 1]; + #ifdef DEBUG + assert(field_name_constant->tag == CONSTANT_Utf8); + #endif + + struct class_entry * class_entry = class_resolver_lookup_class(vm->class_hash_table.length, + vm->class_hash_table.entry, + class_name_constant->utf8.bytes, + class_name_constant->utf8.length); + assert(class_entry != nullptr); + + struct field_entry * field_entry = class_resolver_lookup_field(class_entry, + field_name_constant->utf8.bytes, + field_name_constant->utf8.length); + assert(field_entry != nullptr); + + uint32_t value = field_entry->value; + operand_stack_push_u32(vm->current_frame, value); } void op_goto(struct vm * vm, int32_t branch) @@ -789,7 +823,39 @@ void op_invokespecial(struct vm * vm, uint32_t index) void op_invokestatic(struct vm * vm, uint32_t index) { - assert(!"op_invokestatic"); + struct constant * methodref_constant = &vm->current_thread.current_class->constant_pool[index - 1]; + #ifdef DEBUG + assert(methodref_constant->tag == CONSTANT_Methodref); + #endif + struct constant * class_constant = &vm->current_thread.current_class->constant_pool[methodref_constant->methodref.class_index - 1]; + #ifdef DEBUG + assert(class_constant->tag == CONSTANT_Class); + #endif + struct constant * nameandtype_constant = &vm->current_thread.current_class->constant_pool[methodref_constant->methodref.name_and_type_index - 1]; + #ifdef DEBUG + assert(nameandtype_constant->tag == CONSTANT_NameAndType); + #endif + struct constant * class_name_constant = &vm->current_thread.current_class->constant_pool[class_constant->class.name_index - 1]; + #ifdef DEBUG + assert(class_name_constant->tag == CONSTANT_Utf8); + #endif + struct constant * method_name_constant = &vm->current_thread.current_class->constant_pool[nameandtype_constant->nameandtype.name_index - 1]; + #ifdef DEBUG + assert(method_name_constant->tag == CONSTANT_Utf8); + #endif + + struct class_entry * class_entry = class_resolver_lookup_class(vm->class_hash_table.length, + vm->class_hash_table.entry, + class_name_constant->utf8.bytes, + class_name_constant->utf8.length); + assert(class_entry != nullptr); + + struct method_info * method_info = class_resolver_lookup_method(class_entry, + method_name_constant->utf8.bytes, + method_name_constant->utf8.length); + assert(method_info != nullptr); + + vm_static_method_call(vm, class_entry->class_file, method_info); } void op_invokevirtual(struct vm * vm, uint32_t index) @@ -815,6 +881,13 @@ void op_irem(struct vm * vm) void op_ireturn(struct vm * vm) { + /* Prior to pushing value onto the operand stack of the frame of the invoker, + it may have to be converted. If the return type of the invoked method was + byte, char, or short, then value is converted from int to the return type + as if by execution of i2b, i2c, or i2s, respectively. If the return type + of the invoked method was boolean, then value is narrowed from int to + boolean by taking the bitwise AND of value and 1. */ + uint32_t value = operand_stack_pop_u32(vm->current_frame); printf("return %d\n", value); } diff --git a/c/frame.c b/c/frame.c index 8fbf809..03e2eda 100644 --- a/c/frame.c +++ b/c/frame.c @@ -3,12 +3,13 @@ #include #include "class_file.h" -#include "file.h" #include "memory.h" #include "debug_class_file.h" #include "bytes.h" #include "decode.h" #include "frame.h" +#include "class_resolver.h" +#include "string.h" struct frame * stack_push_frame(struct stack * stack, int num_frames) { @@ -66,28 +67,70 @@ int find_code_name_index(struct class_file * class_file) return 0; } -void vm_execute(struct vm * vm) +static int descriptor_nargs(struct constant * descriptor_constant) { - printf("execute:\n"); - struct constant * class = &vm->current_thread.current_class->constant_pool[vm->current_thread.current_class->this_class - 1]; - print_constant(class); - print_constant(&vm->current_thread.current_class->constant_pool[class->class.name_index - 1]); - print_constant(&vm->current_thread.current_class->constant_pool[vm->current_thread.current_method->name_index - 1]); + assert(descriptor_constant->tag == CONSTANT_Utf8); + assert(descriptor_constant->utf8.length >= 2); + assert(descriptor_constant->utf8.bytes[0] == '('); - int code_name_index = find_code_name_index(vm->current_thread.current_class); + int i = 1; + int nargs = 0; + while (i < descriptor_constant->utf8.length) { + if (descriptor_constant->utf8.bytes[i] == ')') + break; + nargs += 1; + i += 1; + } + assert(i + 2 == descriptor_constant->utf8.length); + + return nargs; +} + +void vm_static_method_call(struct vm * vm, struct class_file * class_file, struct method_info * method_info) +{ + /* If the method is not native, the nargs argument values are popped from the + operand stack. A new frame is created on the Java Virtual Machine stack for + the method being invoked. The nargs argument values are consecutively made + the values of local variables of the new frame, with arg1 in local variable + 0 (or, if arg1 is of type long or double, in local variables 0 and 1) and + so on. The new frame is then made current, and the Java Virtual Machine pc + is set to the opcode of the first instruction of the method to be + invoked. Execution continues with the first instruction of the method. + */ + + int code_name_index = find_code_name_index(class_file); assert(code_name_index > 0); printf("code_name_index %d\n", code_name_index); struct Code_attribute * code = get_code_attribute(code_name_index, - vm->current_thread.current_method->attributes_count, - vm->current_thread.current_method->attributes); + method_info->attributes_count, + method_info->attributes); assert(code != nullptr); + struct frame * old_frame = vm->current_frame; + vm->current_frame = stack_push_frame(&vm->frame_stack, 1); + vm->current_frame->code = code; vm->current_frame->local_variable = stack_push_data(&vm->data_stack, code->max_locals); vm->current_frame->operand_stack = stack_push_data(&vm->data_stack, code->max_stack); vm->current_frame->operand_stack_ix = 0; + struct constant * descriptor_constant = &class_file->constant_pool[method_info->descriptor_index - 1]; + int nargs = descriptor_nargs(descriptor_constant); + printf("nargs %d\n", nargs); + for (int i = 0; i < nargs; i++) { + uint32_t value = operand_stack_pop_u32(old_frame); + vm->current_frame->local_variable[nargs - i - 1] = value; + } + vm->current_thread.pc = 0; + vm->current_thread.current_class = class_file; + vm->current_thread.current_method = method_info; +} + +void vm_execute(struct vm * vm) +{ + printf("execute:\n"); + while (true) { printf("[ "); for (int i = 5; i > 0; i--) { @@ -102,34 +145,47 @@ void vm_execute(struct vm * vm) printf("%10d ", value); } printf("]\n"); - decode_print_instruction(code->code, vm->current_thread.pc); + decode_print_instruction(vm->current_frame->code->code, vm->current_thread.pc); uint32_t old_pc = vm->current_thread.pc; - uint32_t next_pc = decode_execute_instruction(vm, code->code, vm->current_thread.pc); + uint32_t next_pc = decode_execute_instruction(vm, vm->current_frame->code->code, vm->current_thread.pc); if (vm->current_thread.pc == old_pc) { // if the instruction did not branch, increment pc vm->current_thread.pc = next_pc; } - if (vm->current_thread.pc >= code->code_length) { + if (vm->current_thread.pc >= vm->current_frame->code->code_length) { printf("terminate\n"); break; } } } -int main(int argc, char * argv[]) +int main(int argc, const char * argv[]) { - assert(argc >= 2); - uint8_t * buf = file_read(argv[1]); - struct class_file * class_file = class_file_parse(buf); + assert(argc >= 3); - assert(class_file->magic == 0xcafebabe); - assert(class_file->methods_count >= 1); + const char * main_class = argv[1]; + + const char ** class_filenames = &argv[2]; + int num_class_filenames = argc - 2; + + int class_hash_table_length; + struct hash_table_entry * class_hash_table = class_resolver_load_from_filenames(class_filenames, num_class_filenames, &class_hash_table_length); + + struct class_entry * class_entry = class_resolver_lookup_class(class_hash_table_length, + class_hash_table, + (const uint8_t *)main_class, + string_length(main_class)); + + const char * method_name = "test"; + int method_name_length = string_length(method_name); + struct method_info * method_info = class_resolver_lookup_method(class_entry, + (const uint8_t *)method_name, + method_name_length); struct vm vm; - vm.current_thread.pc = 0; - vm.current_thread.current_class = class_file; - vm.current_thread.current_method = &class_file->methods[1]; + vm.class_hash_table.entry = class_hash_table; + vm.class_hash_table.length = class_hash_table_length; vm.frame_stack.ix = 0; vm.frame_stack.capacity = 1024; @@ -141,5 +197,6 @@ int main(int argc, char * argv[]) uint32_t data[vm.data_stack.capacity]; vm.data_stack.data = data; + vm_static_method_call(&vm, class_entry->class_file, method_info); vm_execute(&vm); } diff --git a/c/frame.h b/c/frame.h index 8f2dc56..92ebf83 100644 --- a/c/frame.h +++ b/c/frame.h @@ -5,12 +5,10 @@ #include "class_file.h" struct frame { + struct Code_attribute * code; uint32_t * local_variable; uint32_t * operand_stack; uint16_t operand_stack_ix; - #ifdef DEBUG - uint32_t operand_stack_capacity; - #endif }; struct thread { @@ -33,6 +31,10 @@ struct vm { struct stack data_stack; struct thread current_thread; struct frame * current_frame; + struct { + int length; + struct hash_table_entry * entry; + } class_hash_table; }; static inline void operand_stack_push_u32(struct frame * frame, uint32_t value) @@ -55,3 +57,5 @@ static inline void operand_stack_dup_u32(struct frame * frame) frame->operand_stack[frame->operand_stack_ix] = value; frame->operand_stack_ix++; } + +void vm_static_method_call(struct vm * vm, struct class_file * class_file, struct method_info * method_info); diff --git a/c/hash_table.c b/c/hash_table.c index 3f77d0c..9c871df 100644 --- a/c/hash_table.c +++ b/c/hash_table.c @@ -1,4 +1,5 @@ -#include +#include "malloc.h" +#include "hash_table.h" static uint32_t fnv_1(const uint8_t * buf, int length) { @@ -15,31 +16,17 @@ static uint32_t fnv_1(const uint8_t * buf, int length) return hash; } -struct hash_table_entry { - const uint8_t * key; - int key_length; - void * value; - struct hash_table_entry * next; -}; - void hash_table_init(int hash_table_length, - struct hash_table_entry * entry, - struct hash_table_entry * overflow) + struct hash_table_entry * entry) { for (int i = 0; i < hash_table_length; i++) { entry[i].key = nullptr; entry[i].next = nullptr; - overflow[i].key = nullptr; - overflow[i].next = nullptr; } } -static int max = 0; - void hash_table_add(int hash_table_length, struct hash_table_entry * entry, - struct hash_table_entry * overflow, - int * overflow_length, const uint8_t * key, int key_length, void * value) @@ -51,12 +38,9 @@ void hash_table_add(int hash_table_length, e = e->next; } - int i = 0; if (e->key != nullptr) { - if ((++i) > max) max = i; - // allocate e from overflow - e->next = &overflow[(*overflow_length)++]; + e->next = malloc_class_arena((sizeof (struct hash_table_entry))); e = e->next; } @@ -74,195 +58,19 @@ static inline bool key_equal(const uint8_t * a, const uint8_t * b, int length) return true; } - -#include - struct hash_table_entry * hash_table_find(int hash_table_length, struct hash_table_entry * entry, - struct hash_table_entry * overflow, const uint8_t * key, int key_length) { uint32_t hash = fnv_1(key, key_length) & (hash_table_length - 1); struct hash_table_entry * e = &entry[hash]; - while (e->key != nullptr) { + while (e != nullptr && e->key != nullptr) { if (e->key_length == key_length && key_equal(e->key, key, key_length)) { return e; } - printf("collision %s %s\n", (char *)e->key, (char *)key); e = e->next; } return nullptr; } - -#include -#include -#include -#include - -static const char * names[] = { - "java/beans/Introspector.java", - "java/beans/PropertyDescriptor.java", - "java/beans/PropertyVetoException.java", - "java/beans/BeanInfo.java", - "java/beans/Customizer.java", - "java/beans/FeatureDescriptor.java", - "java/beans/BeanDescriptor.java", - "java/beans/Beans.java", - "java/beans/AppletInitializer.java", - "java/beans/Expression.java", - "java/beans/MethodDescriptor.java", - "java/beans/PropertyChangeSupport.java", - "java/beans/PropertyChangeEvent.java", - "java/beans/DesignMode.java", - "java/beans/EventSetDescriptor.java", - "java/beans/IndexedPropertyDescriptor.java", - "java/beans/VetoableChangeListener.java", - "java/beans/ConstructorProperties.java", - "java/beans/IndexedPropertyChangeEvent.java", - "java/beans/PropertyEditorManager.java", - "java/beans/IntrospectionException.java", - "java/beans/PropertyChangeListener.java", - "java/beans/XMLEncoder.java", - "java/beans/Visibility.java", - "java/beans/VetoableChangeSupport.java", - "java/beans/ParameterDescriptor.java", - "java/beans/XMLDecoder.java", - "java/beans/PropertyChangeListenerProxy.java", - "java/beans/ExceptionListener.java", - "java/beans/VetoableChangeListenerProxy.java", - "java/beans/DefaultPersistenceDelegate.java", - "java/beans/EventHandler.java", - "java/beans/Statement.java", - "java/beans/PropertyEditor.java", - "java/beans/PropertyEditorSupport.java", - "java/beans/SimpleBeanInfo.java", - "java/beans/PersistenceDelegate.java", - "java/beans/Encoder.java", - "java/beans/beancontext/BeanContextChildSupport.java", - "java/beans/beancontext/BeanContextServicesSupport.java", - "java/beans/beancontext/BeanContextServiceRevokedEvent.java", - "java/beans/beancontext/BeanContextSupport.java", - "java/beans/beancontext/BeanContextServiceAvailableEvent.java", - "java/beans/beancontext/BeanContextServiceProvider.java", - "java/beans/beancontext/BeanContextProxy.java", - "java/beans/beancontext/BeanContextServiceProviderBeanInfo.java", - "java/beans/beancontext/BeanContextContainerProxy.java", - "java/beans/beancontext/BeanContextMembershipListener.java", - "java/beans/beancontext/BeanContextServices.java", - "java/beans/beancontext/BeanContextServiceRevokedListener.java", - "java/beans/beancontext/BeanContext.java", - "java/beans/beancontext/BeanContextChildComponentProxy.java", - "java/beans/beancontext/BeanContextMembershipEvent.java", - "java/beans/beancontext/BeanContextEvent.java", - "java/beans/beancontext/BeanContextServicesListener.java", - "java/beans/beancontext/BeanContextChild.java", - "java/net/ContentHandler.java", - "java/net/URL.java", - "java/net/FileNameMap.java", - "java/net/SocketOptions.java", - "java/net/DatagramSocketImplFactory.java", - "java/net/UnknownServiceException.java", - "java/net/SocketPermission.java", - "java/net/NoRouteToHostException.java", - "java/net/NetPermission.java", - "java/net/URI.java", - "java/net/DatagramSocket.java", - "java/net/DatagramPacket.java", - "java/net/BindException.java", - "java/net/SocketTimeoutException.java", - "java/net/ProxySelector.java", - "java/net/MalformedURLException.java", - "java/net/URLStreamHandlerFactory.java", - "java/net/InetAddress.java", - "java/net/PortUnreachableException.java", - "java/net/DatagramSocketImpl.java", - "java/net/UnknownHostException.java", - "java/net/ProtocolException.java", - "java/net/Proxy.java", - "java/net/PasswordAuthentication.java", - "java/net/SocketAddress.java", - "java/net/MulticastSocket.java", - "java/net/ServerSocket.java", - "java/net/JarURLConnection.java", - "java/net/Authenticator.java", - "java/net/URLStreamHandler.java", - "java/net/ConnectException.java", - "java/net/NetworkInterface.java", - "java/net/URLConnection.java", - "java/net/URLEncoder.java", - "java/net/InetSocketAddress.java", - "java/net/URISyntaxException.java", - "java/net/ResolverCache.java", - "java/net/Socket.java", - "java/net/Inet4Address.java", - "java/net/SocketException.java", - "java/net/SocketImplFactory.java", - "java/net/ContentHandlerFactory.java", - "java/net/HttpURLConnection.java", - "java/net/URLDecoder.java", - "java/net/SocketImpl.java", - "java/net/URLClassLoader.java", - "java/net/Inet6Address.java", - "java/net/MimeTypeMapper.java", - "java/math/MathContext.java", - "java/math/BigInteger.java", - "java/math/BigDecimal.java", - "java/math/RoundingMode.java", - "java/io/FileWriter.java", - "java/io/FilePermission.java", - "java/io/OutputStreamWriter.java", - "java/io/ObjectInput.java", - "java/io/BufferedOutputStream.java", - "java/io/IOError.java", - "java/io/LineNumberReader.java", - "java/io/StringReader.java", - "java/io/BufferedInputStream.java", - "java/io/CharArrayWriter.java", - "java/io/InputStreamReader.java", - "java/io/Console.java", - "java/io/FileOutputStream.java", - "java/io/StringBufferInputStream.java", - "java/io/DataOutput.java", - "java/io/UTFDataFormatException.java", - "java/io/ObjectStreamConstants.java", - "java/io/ObjectStreamException.java", - "java/io/BufferedReader.java", - "java/io/FilenameFilter.java", -}; -const int names_length = (sizeof (names)) / (sizeof (names[0])); - -int main() -{ - int hash_table_length = 512; - struct hash_table_entry entry[hash_table_length]; - struct hash_table_entry overflow[hash_table_length]; - int overflow_length = 0; - - hash_table_init(hash_table_length, - entry, - overflow); - - for (int i = 0; i < names_length; i++) { - hash_table_add(hash_table_length, - entry, - overflow, - &overflow_length, - (const uint8_t *)names[i], - strlen(names[i]), - (void *)(ptrdiff_t)(i * 2)); - } - - printf("overflow_length %d %d\n", overflow_length, max); - - for (int j = 0; j < names_length; j++) { - struct hash_table_entry * e = hash_table_find(hash_table_length, - entry, - overflow, - (const uint8_t *)names[j], - strlen(names[j])); - assert(e != nullptr); - printf("%s %d\n", e->key, (int)e->value); - } -} diff --git a/c/hash_table.h b/c/hash_table.h new file mode 100644 index 0000000..e316a4e --- /dev/null +++ b/c/hash_table.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +struct hash_table_entry { + const uint8_t * key; + int key_length; + void * value; + struct hash_table_entry * next; +}; + +void hash_table_init(int hash_table_length, + struct hash_table_entry * entry); + +void hash_table_add(int hash_table_length, + struct hash_table_entry * entry, + const uint8_t * key, + int key_length, + void * value); + +struct hash_table_entry * hash_table_find(int hash_table_length, + struct hash_table_entry * entry, + const uint8_t * key, + int key_length); diff --git a/c/malloc.c b/c/malloc.c index 2ff6149..cafd2c5 100644 --- a/c/malloc.c +++ b/c/malloc.c @@ -1,5 +1,3 @@ -#include - #include "malloc.h" struct arena { diff --git a/c/malloc.h b/c/malloc.h index fd07794..c947ec3 100644 --- a/c/malloc.h +++ b/c/malloc.h @@ -1,3 +1,5 @@ #pragma once +#include + void * malloc_class_arena(uint32_t size); diff --git a/c/string.h b/c/string.h new file mode 100644 index 0000000..2da8d26 --- /dev/null +++ b/c/string.h @@ -0,0 +1,21 @@ +#pragma once + +static inline int string_length(const char * s) +{ + const char * si = s; + while (*si != 0) { + si++; + } + return si - s; +} + +static inline bool string_equal(const char * a, const char * b) +{ + int i = 0; + while (a[i] != 0) { + if (a[i] != b[i]) + return false; + i += 1; + } + return b[i] == 0; +} diff --git a/c/test_hash_table.c b/c/test_hash_table.c new file mode 100644 index 0000000..e22bba9 --- /dev/null +++ b/c/test_hash_table.c @@ -0,0 +1,175 @@ +#include +#include +#include + +#include "hash_table.h" + +static const char * names[] = { + "java/beans/Introspector.java", + "java/beans/PropertyDescriptor.java", + "java/beans/PropertyVetoException.java", + "java/beans/BeanInfo.java", + "java/beans/Customizer.java", + "java/beans/FeatureDescriptor.java", + "java/beans/BeanDescriptor.java", + "java/beans/Beans.java", + "java/beans/AppletInitializer.java", + "java/beans/Expression.java", + "java/beans/MethodDescriptor.java", + "java/beans/PropertyChangeSupport.java", + "java/beans/PropertyChangeEvent.java", + "java/beans/DesignMode.java", + "java/beans/EventSetDescriptor.java", + "java/beans/IndexedPropertyDescriptor.java", + "java/beans/VetoableChangeListener.java", + "java/beans/ConstructorProperties.java", + "java/beans/IndexedPropertyChangeEvent.java", + "java/beans/PropertyEditorManager.java", + "java/beans/IntrospectionException.java", + "java/beans/PropertyChangeListener.java", + "java/beans/XMLEncoder.java", + "java/beans/Visibility.java", + "java/beans/VetoableChangeSupport.java", + "java/beans/ParameterDescriptor.java", + "java/beans/XMLDecoder.java", + "java/beans/PropertyChangeListenerProxy.java", + "java/beans/ExceptionListener.java", + "java/beans/VetoableChangeListenerProxy.java", + "java/beans/DefaultPersistenceDelegate.java", + "java/beans/EventHandler.java", + "java/beans/Statement.java", + "java/beans/PropertyEditor.java", + "java/beans/PropertyEditorSupport.java", + "java/beans/SimpleBeanInfo.java", + "java/beans/PersistenceDelegate.java", + "java/beans/Encoder.java", + "java/beans/beancontext/BeanContextChildSupport.java", + "java/beans/beancontext/BeanContextServicesSupport.java", + "java/beans/beancontext/BeanContextServiceRevokedEvent.java", + "java/beans/beancontext/BeanContextSupport.java", + "java/beans/beancontext/BeanContextServiceAvailableEvent.java", + "java/beans/beancontext/BeanContextServiceProvider.java", + "java/beans/beancontext/BeanContextProxy.java", + "java/beans/beancontext/BeanContextServiceProviderBeanInfo.java", + "java/beans/beancontext/BeanContextContainerProxy.java", + "java/beans/beancontext/BeanContextMembershipListener.java", + "java/beans/beancontext/BeanContextServices.java", + "java/beans/beancontext/BeanContextServiceRevokedListener.java", + "java/beans/beancontext/BeanContext.java", + "java/beans/beancontext/BeanContextChildComponentProxy.java", + "java/beans/beancontext/BeanContextMembershipEvent.java", + "java/beans/beancontext/BeanContextEvent.java", + "java/beans/beancontext/BeanContextServicesListener.java", + "java/beans/beancontext/BeanContextChild.java", + "java/net/ContentHandler.java", + "java/net/URL.java", + "java/net/FileNameMap.java", + "java/net/SocketOptions.java", + "java/net/DatagramSocketImplFactory.java", + "java/net/UnknownServiceException.java", + "java/net/SocketPermission.java", + "java/net/NoRouteToHostException.java", + "java/net/NetPermission.java", + "java/net/URI.java", + "java/net/DatagramSocket.java", + "java/net/DatagramPacket.java", + "java/net/BindException.java", + "java/net/SocketTimeoutException.java", + "java/net/ProxySelector.java", + "java/net/MalformedURLException.java", + "java/net/URLStreamHandlerFactory.java", + "java/net/InetAddress.java", + "java/net/PortUnreachableException.java", + "java/net/DatagramSocketImpl.java", + "java/net/UnknownHostException.java", + "java/net/ProtocolException.java", + "java/net/Proxy.java", + "java/net/PasswordAuthentication.java", + "java/net/SocketAddress.java", + "java/net/MulticastSocket.java", + "java/net/ServerSocket.java", + "java/net/JarURLConnection.java", + "java/net/Authenticator.java", + "java/net/URLStreamHandler.java", + "java/net/ConnectException.java", + "java/net/NetworkInterface.java", + "java/net/URLConnection.java", + "java/net/URLEncoder.java", + "java/net/InetSocketAddress.java", + "java/net/URISyntaxException.java", + "java/net/ResolverCache.java", + "java/net/Socket.java", + "java/net/Inet4Address.java", + "java/net/SocketException.java", + "java/net/SocketImplFactory.java", + "java/net/ContentHandlerFactory.java", + "java/net/HttpURLConnection.java", + "java/net/URLDecoder.java", + "java/net/SocketImpl.java", + "java/net/URLClassLoader.java", + "java/net/Inet6Address.java", + "java/net/MimeTypeMapper.java", + "java/math/MathContext.java", + "java/math/BigInteger.java", + "java/math/BigDecimal.java", + "java/math/RoundingMode.java", + "java/io/FileWriter.java", + "java/io/FilePermission.java", + "java/io/OutputStreamWriter.java", + "java/io/ObjectInput.java", + "java/io/BufferedOutputStream.java", + "java/io/IOError.java", + "java/io/LineNumberReader.java", + "java/io/StringReader.java", + "java/io/BufferedInputStream.java", + "java/io/CharArrayWriter.java", + "java/io/InputStreamReader.java", + "java/io/Console.java", + "java/io/FileOutputStream.java", + "java/io/StringBufferInputStream.java", + "java/io/DataOutput.java", + "java/io/UTFDataFormatException.java", + "java/io/ObjectStreamConstants.java", + "java/io/ObjectStreamException.java", + "java/io/BufferedReader.java", + "java/io/FilenameFilter.java", +}; +const int names_length = (sizeof (names)) / (sizeof (names[0])); + +static uint32_t _strlen(const char * s) +{ + const char * si = s; + while (*si != 0) { + si++; + } + return si - s; +} + +int main() +{ + int hash_table_length = 128 * 2; + struct hash_table_entry entry[hash_table_length]; + + hash_table_init(hash_table_length, + entry); + + for (int i = 0; i < names_length; i++) { + hash_table_add(hash_table_length, + entry, + (const uint8_t *)names[i], + _strlen(names[i]), + (void *)(ptrdiff_t)(i * 2)); + } + + for (int j = 0; j < names_length; j++) { + struct hash_table_entry * e = hash_table_find(hash_table_length, + entry, + (const uint8_t *)names[j], + _strlen(names[j])); + if (e < entry || e >= entry + hash_table_length) { + printf("collision %s\n", e->key); + } + assert(e != nullptr); + printf("%s %d\n", e->key, (int)e->value); + } +} diff --git a/p/DataTest.java b/p/DataTest.java new file mode 100644 index 0000000..d1a893a --- /dev/null +++ b/p/DataTest.java @@ -0,0 +1,13 @@ +package p; + +import p.Data; + +class DataTest { + static int test() { + return test2(6, 3) * 5; + } + + static int test2(int a, int b) { + return a - b; + } +}