implement static fields
This commit is contained in:
parent
3791a78e19
commit
d2863035e4
5
Makefile
5
Makefile
@ -13,11 +13,12 @@ OBJ = \
|
||||
c/execute.o \
|
||||
c/memory_allocator.o \
|
||||
c/class_resolver.o \
|
||||
c/hash_table.o
|
||||
c/hash_table.o \
|
||||
c/frame.o
|
||||
|
||||
MAIN_OBJ = \
|
||||
$(OBJ) \
|
||||
c/frame.o
|
||||
c/main.o
|
||||
|
||||
PRINT_CLASS_OBJ = \
|
||||
$(OBJ) \
|
||||
|
@ -39,6 +39,7 @@ struct hash_table_entry * class_resolver_load_from_filenames(const char * filena
|
||||
assert(class_file->magic == 0xcafebabe);
|
||||
|
||||
class_entry[i].class_file = class_file;
|
||||
class_entry[i].initialization_state = CLASS_UNINITIALIZED;
|
||||
|
||||
hash_table_add(class_hash_table_length,
|
||||
class_hash_table,
|
||||
|
@ -10,8 +10,15 @@ struct field_entry {
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
enum initialization_state {
|
||||
CLASS_UNINITIALIZED,
|
||||
CLASS_INITIALIZING,
|
||||
CLASS_INITIALIZED,
|
||||
};
|
||||
|
||||
struct class_entry {
|
||||
struct class_file * class_file;
|
||||
enum initialization_state initialization_state;
|
||||
|
||||
struct {
|
||||
int length;
|
||||
|
78
c/execute.c
78
c/execute.c
@ -550,6 +550,13 @@ void op_getstatic(struct vm * vm, uint32_t index)
|
||||
field_name_constant->utf8.length);
|
||||
assert(field_entry != nullptr);
|
||||
|
||||
/* On successful resolution of the field, the class or interface that
|
||||
declared the resolved field is initialized if that class or interface has
|
||||
not already been initialized (§5.5). */
|
||||
|
||||
if (!vm_initialize_class(vm, class_entry))
|
||||
return;
|
||||
|
||||
uint32_t value = field_entry->value;
|
||||
operand_stack_push_u32(vm->current_frame, value);
|
||||
}
|
||||
@ -925,6 +932,10 @@ void op_invokestatic(struct vm * vm, uint32_t index)
|
||||
method_name_constant->utf8.length);
|
||||
assert(method_info != nullptr);
|
||||
|
||||
/* On successful resolution of the method, the class or interface that
|
||||
declared the resolved method is initialized if that class or interface has
|
||||
not already been initialized (§5.5). */
|
||||
|
||||
vm_static_method_call(vm, class_entry->class_file, method_info);
|
||||
}
|
||||
|
||||
@ -1266,6 +1277,17 @@ void op_multianewarray(struct vm * vm, uint32_t index, uint32_t dimensions)
|
||||
|
||||
void op_new(struct vm * vm, uint32_t index)
|
||||
{
|
||||
/* On successful resolution of the class, it is initialized if it has not
|
||||
already been initialized (§5.5).
|
||||
|
||||
(new) Upon execution of a new instruction, the class to be initialized is
|
||||
the class referenced by the instruction. */
|
||||
|
||||
/*
|
||||
if (!vm_initialize_class(vm, class_entry))
|
||||
return;
|
||||
*/
|
||||
|
||||
assert(!"op_new");
|
||||
}
|
||||
|
||||
@ -1307,8 +1329,20 @@ void op_newarray(struct vm * vm, uint32_t atype)
|
||||
int32_t count = operand_stack_pop_u32(vm->current_frame);
|
||||
int32_t size = element_size * count + 4;
|
||||
int32_t * arrayref = memory_allocate(size);
|
||||
|
||||
assert(arrayref != 0);
|
||||
arrayref[0] = count;
|
||||
|
||||
/* Each of the elements of the new array is initialized to the default initial
|
||||
value (§2.3, §2.4) for the element type of the array type. */
|
||||
/* round up u32_count, possibly initializing past the end of the array. This
|
||||
is acceptable because memory_allocate always allocates a multiple of 4
|
||||
greater than or equal to `size`. */
|
||||
int32_t u32_count = (size - 4 + 3) / 4;
|
||||
for (int i = 0; i < u32_count; i++) {
|
||||
arrayref[i + 1] = 0;
|
||||
}
|
||||
|
||||
operand_stack_push_u32(vm->current_frame, (uint32_t)arrayref);
|
||||
}
|
||||
|
||||
@ -1333,7 +1367,47 @@ void op_putfield(struct vm * vm, uint32_t index)
|
||||
|
||||
void op_putstatic(struct vm * vm, uint32_t index)
|
||||
{
|
||||
assert(!"op_putstatic");
|
||||
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);
|
||||
|
||||
/* On successful resolution of the field, the class or interface that declared
|
||||
the resolved field is initialized if that class or interface has not
|
||||
already been initialized (§5.5). */
|
||||
|
||||
if (!vm_initialize_class(vm, class_entry))
|
||||
return;
|
||||
|
||||
uint32_t value = operand_stack_pop_u32(vm->current_frame);
|
||||
field_entry->value = value;
|
||||
}
|
||||
|
||||
void op_ret(struct vm * vm, uint32_t index)
|
||||
@ -1358,7 +1432,7 @@ void op_sastore(struct vm * vm)
|
||||
|
||||
void op_sipush(struct vm * vm, int32_t byte)
|
||||
{
|
||||
assert(!"op_sipush2");
|
||||
operand_stack_push_u32(vm->current_frame, byte);
|
||||
}
|
||||
|
||||
void op_swap(struct vm * vm)
|
||||
|
167
c/frame.c
167
c/frame.c
@ -9,39 +9,6 @@
|
||||
#include "decode.h"
|
||||
#include "frame.h"
|
||||
#include "class_resolver.h"
|
||||
#include "string.h"
|
||||
|
||||
struct frame * stack_push_frame(struct stack * stack, int num_frames)
|
||||
{
|
||||
struct frame * frame = &stack->frame[stack->ix];
|
||||
stack->ix += num_frames;
|
||||
assert(stack->ix <= stack->capacity);
|
||||
return frame;
|
||||
}
|
||||
|
||||
struct frame * stack_pop_frame(struct stack * stack, int num_frames)
|
||||
{
|
||||
stack->ix -= num_frames;
|
||||
assert(stack->ix >= 0);
|
||||
struct frame * frame = &stack->frame[stack->ix - 1];
|
||||
return frame;
|
||||
}
|
||||
|
||||
uint32_t * stack_push_data(struct stack * stack, int num_data)
|
||||
{
|
||||
uint32_t * data = &stack->data[stack->ix];
|
||||
stack->ix += num_data;
|
||||
assert(stack->ix <= stack->capacity);
|
||||
return data;
|
||||
}
|
||||
|
||||
uint32_t * stack_pop_data(struct stack * stack, int num_data)
|
||||
{
|
||||
stack->ix -= num_data;
|
||||
assert(stack->ix >= 0);
|
||||
uint32_t * data = &stack->data[stack->ix];
|
||||
return data;
|
||||
}
|
||||
|
||||
struct Code_attribute * get_code_attribute(int code_name_index,
|
||||
int attributes_count,
|
||||
@ -67,6 +34,19 @@ int find_code_name_index(struct class_file * class_file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int find_constantvalue_name_index(struct class_file * class_file)
|
||||
{
|
||||
for (int i = 0; i < class_file->constant_pool_count; i++) {
|
||||
struct constant * constant = &class_file->constant_pool[i];
|
||||
if (constant->tag == CONSTANT_Utf8) {
|
||||
if (bytes_equal(constant->utf8.length, constant->utf8.bytes, "ConstantValue")) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int descriptor_nargs(struct constant * descriptor_constant)
|
||||
{
|
||||
assert(descriptor_constant->tag == CONSTANT_Utf8);
|
||||
@ -91,6 +71,75 @@ static int descriptor_nargs(struct constant * descriptor_constant)
|
||||
return nargs;
|
||||
}
|
||||
|
||||
bool vm_initialize_class(struct vm * vm, struct class_entry * class_entry)
|
||||
{
|
||||
if (class_entry->initialization_state == CLASS_INITIALIZED)
|
||||
return true;
|
||||
|
||||
if (class_entry->initialization_state == CLASS_INITIALIZING) {
|
||||
if (vm->current_thread.current_class == class_entry->class_file)
|
||||
return true;
|
||||
else
|
||||
assert(false); // possible infinite initialization loop
|
||||
}
|
||||
|
||||
class_entry->initialization_state = CLASS_INITIALIZING;
|
||||
|
||||
/* Then, initialize each static field of C with the constant value in its
|
||||
ConstantValue attribute (§4.7.2), in the order the fields appear in the
|
||||
ClassFile structure. */
|
||||
|
||||
struct class_file * class_file = class_entry->class_file;
|
||||
|
||||
int constantvalue_name_index = find_constantvalue_name_index(class_file);
|
||||
assert(constantvalue_name_index != 0);
|
||||
|
||||
for (int i = 0; i < class_file->fields_count; i++) {
|
||||
struct field_info * field_info = &class_file->fields[i];
|
||||
if (!(field_info->access_flags & FIELD_ACC_STATIC))
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < field_info->attributes_count; j++) {
|
||||
if (field_info->attributes[j].attribute_name_index == constantvalue_name_index) {
|
||||
struct attribute_info * attribute = &field_info->attributes[j];
|
||||
struct constant * constantvalue = &class_file->constant_pool[attribute->constantvalue->constantvalue_index - 1];
|
||||
assert(constantvalue->tag == CONSTANT_Integer); // also need to support CONSTANT_String
|
||||
|
||||
struct constant * name_constant = &class_file->constant_pool[field_info->name_index - 1];
|
||||
assert(name_constant->tag == CONSTANT_Utf8);
|
||||
struct field_entry * field_entry = class_resolver_lookup_field(class_entry,
|
||||
name_constant->utf8.bytes,
|
||||
name_constant->utf8.length);
|
||||
assert(field_entry != nullptr);
|
||||
field_entry->value = constantvalue->integer.bytes;
|
||||
printf(" constantvalue: %d\n", field_entry->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Next, if C declares a class or interface initialization method, execute
|
||||
that method. */
|
||||
const uint8_t * method_name = (const uint8_t *)"<clinit>";
|
||||
int method_length = 8;
|
||||
|
||||
struct method_info * method_info = class_resolver_lookup_method(class_entry,
|
||||
method_name,
|
||||
method_length);
|
||||
if (method_info != nullptr) {
|
||||
assert((method_info->access_flags & METHOD_ACC_STATIC) != 0);
|
||||
printf("<clinit>\n");
|
||||
|
||||
// tamper with next_pc
|
||||
vm->current_frame->next_pc = vm->current_frame->pc;
|
||||
|
||||
vm_static_method_call(vm, class_file, method_info);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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
|
||||
@ -132,6 +181,8 @@ void vm_static_method_call(struct vm * vm, struct class_file * class_file, struc
|
||||
vm->current_frame->pc = 0;
|
||||
vm->current_thread.current_class = class_file;
|
||||
vm->current_thread.current_method = method_info;
|
||||
|
||||
printf("operand_stack_ix: %d\n", vm->current_frame->operand_stack_ix);
|
||||
}
|
||||
|
||||
void vm_method_return(struct vm * vm)
|
||||
@ -225,53 +276,3 @@ void vm_execute(struct vm * vm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char * argv[])
|
||||
{
|
||||
assert(argc >= 3);
|
||||
|
||||
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 = "main";
|
||||
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.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;
|
||||
struct frame frames[vm.frame_stack.capacity];
|
||||
vm.frame_stack.frame = frames;
|
||||
|
||||
vm.data_stack.ix = 0;
|
||||
vm.data_stack.capacity = 0x100000;
|
||||
uint32_t data[vm.data_stack.capacity];
|
||||
vm.data_stack.data = data;
|
||||
|
||||
struct frame * entry_frame = stack_push_frame(&vm.frame_stack, 1);
|
||||
struct Code_attribute code;
|
||||
code.max_locals = 0;
|
||||
code.max_stack = 0;
|
||||
entry_frame->code = &code;
|
||||
entry_frame->local_variable = 0;
|
||||
entry_frame->operand_stack = 0;
|
||||
entry_frame->operand_stack_ix = 0;
|
||||
|
||||
vm_static_method_call(&vm, class_entry->class_file, method_info);
|
||||
vm_execute(&vm);
|
||||
}
|
||||
|
35
c/frame.h
35
c/frame.h
@ -3,6 +3,7 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "class_file.h"
|
||||
#include "class_resolver.h"
|
||||
|
||||
struct frame {
|
||||
struct Code_attribute * code;
|
||||
@ -39,6 +40,38 @@ struct vm {
|
||||
} class_hash_table;
|
||||
};
|
||||
|
||||
static inline struct frame * stack_push_frame(struct stack * stack, int num_frames)
|
||||
{
|
||||
struct frame * frame = &stack->frame[stack->ix];
|
||||
stack->ix += num_frames;
|
||||
assert(stack->ix <= stack->capacity);
|
||||
return frame;
|
||||
}
|
||||
|
||||
static inline struct frame * stack_pop_frame(struct stack * stack, int num_frames)
|
||||
{
|
||||
stack->ix -= num_frames;
|
||||
assert(stack->ix >= 0);
|
||||
struct frame * frame = &stack->frame[stack->ix - 1];
|
||||
return frame;
|
||||
}
|
||||
|
||||
static inline uint32_t * stack_push_data(struct stack * stack, int num_data)
|
||||
{
|
||||
uint32_t * data = &stack->data[stack->ix];
|
||||
stack->ix += num_data;
|
||||
assert(stack->ix <= stack->capacity);
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline uint32_t * stack_pop_data(struct stack * stack, int num_data)
|
||||
{
|
||||
stack->ix -= num_data;
|
||||
assert(stack->ix >= 0);
|
||||
uint32_t * data = &stack->data[stack->ix];
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void operand_stack_push_u32(struct frame * frame, uint32_t value)
|
||||
{
|
||||
frame->operand_stack[frame->operand_stack_ix] = value;
|
||||
@ -76,5 +109,7 @@ static inline float operand_stack_pop_f32(struct frame * frame)
|
||||
return f;
|
||||
}
|
||||
|
||||
bool vm_initialize_class(struct vm * vm, struct class_entry * class_entry);
|
||||
void vm_static_method_call(struct vm * vm, struct class_file * class_file, struct method_info * method_info);
|
||||
void vm_method_return(struct vm * vm);
|
||||
void vm_execute(struct vm * vm);
|
||||
|
53
c/main.c
Normal file
53
c/main.c
Normal file
@ -0,0 +1,53 @@
|
||||
#include "frame.h"
|
||||
#include "class_resolver.h"
|
||||
#include "string.h"
|
||||
|
||||
int main(int argc, const char * argv[])
|
||||
{
|
||||
assert(argc >= 3);
|
||||
|
||||
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 = "main";
|
||||
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.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;
|
||||
struct frame frames[vm.frame_stack.capacity];
|
||||
vm.frame_stack.frame = frames;
|
||||
|
||||
vm.data_stack.ix = 0;
|
||||
vm.data_stack.capacity = 0x100000;
|
||||
uint32_t data[vm.data_stack.capacity];
|
||||
vm.data_stack.data = data;
|
||||
|
||||
struct frame * entry_frame = stack_push_frame(&vm.frame_stack, 1);
|
||||
struct Code_attribute code;
|
||||
code.max_locals = 0;
|
||||
code.max_stack = 0;
|
||||
entry_frame->code = &code;
|
||||
entry_frame->local_variable = 0;
|
||||
entry_frame->operand_stack = 0;
|
||||
entry_frame->operand_stack_ix = 0;
|
||||
|
||||
vm_static_method_call(&vm, class_entry->class_file, method_info);
|
||||
vm_execute(&vm);
|
||||
}
|
21
p/StaticField.java
Normal file
21
p/StaticField.java
Normal file
@ -0,0 +1,21 @@
|
||||
package p;
|
||||
|
||||
class StaticField {
|
||||
final static int foo = 1234;
|
||||
|
||||
static int[] bar = {10, 11, 12, 13, 14};
|
||||
|
||||
int qux;
|
||||
|
||||
static int test() {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < bar.length; i++) {
|
||||
sum += bar[i] * foo;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static void main() {
|
||||
test();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user