add class_resolver
Implement the getfield and invokestatic opcodes.
This commit is contained in:
parent
39f53e8ee4
commit
932a7c82aa
6
Makefile
6
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
|
||||
|
163
c/class_resolver.c
Normal file
163
c/class_resolver.c
Normal file
@ -0,0 +1,163 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#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;
|
||||
}
|
42
c/class_resolver.h
Normal file
42
c/class_resolver.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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);
|
@ -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",
|
||||
|
@ -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);
|
||||
|
77
c/execute.c
77
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);
|
||||
}
|
||||
|
101
c/frame.c
101
c/frame.c
@ -3,12 +3,13 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
10
c/frame.h
10
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);
|
||||
|
202
c/hash_table.c
202
c/hash_table.c
@ -1,4 +1,5 @@
|
||||
#include <stdint.h>
|
||||
#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 <stdio.h>
|
||||
|
||||
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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.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]));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
24
c/hash_table.h
Normal file
24
c/hash_table.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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);
|
@ -1,5 +1,3 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "malloc.h"
|
||||
|
||||
struct arena {
|
||||
|
@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void * malloc_class_arena(uint32_t size);
|
||||
|
21
c/string.h
Normal file
21
c/string.h
Normal file
@ -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;
|
||||
}
|
175
c/test_hash_table.c
Normal file
175
c/test_hash_table.c
Normal file
@ -0,0 +1,175 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
13
p/DataTest.java
Normal file
13
p/DataTest.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user