initial
This commit is contained in:
commit
39f53e8ee4
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.~*
|
||||||
|
*.o
|
||||||
|
*.d
|
||||||
|
*.gch
|
||||||
|
*.csv
|
||||||
|
*.class
|
||||||
|
main
|
||||||
|
print_class
|
51
Makefile
Normal file
51
Makefile
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
%.csv: %.ods
|
||||||
|
libreoffice --headless --convert-to csv:"Text - txt - csv (StarCalc)":44,34,76,,,,true --outdir $(dir $@) $<
|
||||||
|
|
||||||
|
%.class: %.java
|
||||||
|
javac $<
|
||||||
|
|
||||||
|
OBJ = \
|
||||||
|
c/decode.o \
|
||||||
|
c/class_file.o \
|
||||||
|
c/debug_class_file.o \
|
||||||
|
c/malloc.o \
|
||||||
|
c/file.o \
|
||||||
|
c/execute.o \
|
||||||
|
c/memory_allocator.o
|
||||||
|
|
||||||
|
MAIN_OBJ = \
|
||||||
|
$(OBJ) \
|
||||||
|
c/frame.o
|
||||||
|
|
||||||
|
PRINT_CLASS_OBJ = \
|
||||||
|
$(OBJ) \
|
||||||
|
c/print_class.o \
|
||||||
|
|
||||||
|
CC ?= gcc
|
||||||
|
ARCH = -m32
|
||||||
|
CFLAGS ?= -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -std=c2x -DDEBUG -g
|
||||||
|
OPT ?= -Og
|
||||||
|
DEPFLAGS = -MMD -MP
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(ARCH) $(CFLAGS) $(OPT) $(DEPFLAGS) -MF ${<}.d -c $< -o $@
|
||||||
|
|
||||||
|
print_class: $(PRINT_CLASS_OBJ)
|
||||||
|
$(CC) $(ARCH) $^ -o $@
|
||||||
|
|
||||||
|
main: $(MAIN_OBJ)
|
||||||
|
$(CC) $(ARCH) $^ -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f main print_class c/*.o
|
||||||
|
|
||||||
|
.SUFFIXES:
|
||||||
|
.INTERMEDIATE:
|
||||||
|
.SECONDARY:
|
||||||
|
.PHONY: all clean phony
|
||||||
|
|
||||||
|
%: RCS/%,v
|
||||||
|
%: RCS/%
|
||||||
|
%: %,v
|
||||||
|
%: s.%
|
||||||
|
%: SCCS/s.%
|
7
c/bswap.h
Normal file
7
c/bswap.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
#define BE_BSWAP32(n) (n)
|
||||||
|
#else
|
||||||
|
#define BE_BSWAP32(n) (__builtin_bswap32(n))
|
||||||
|
#endif
|
80
c/bytes.h
Normal file
80
c/bytes.h
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "malloc.h"
|
||||||
|
|
||||||
|
static inline uint32_t parse_u4(const uint8_t ** buf)
|
||||||
|
{
|
||||||
|
uint32_t n =
|
||||||
|
(*buf)[0] << 24
|
||||||
|
| (*buf)[1] << 16
|
||||||
|
| (*buf)[2] << 8
|
||||||
|
| (*buf)[3] << 0
|
||||||
|
;
|
||||||
|
(*buf) += 4;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t parse_u2(const uint8_t ** buf)
|
||||||
|
{
|
||||||
|
uint32_t n =
|
||||||
|
(*buf)[0] << 8
|
||||||
|
| (*buf)[1] << 0
|
||||||
|
;
|
||||||
|
(*buf) += 2;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t parse_u1(const uint8_t ** buf)
|
||||||
|
{
|
||||||
|
uint32_t n = (*buf)[0];
|
||||||
|
(*buf) += 1;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int32_t parse_s4(const uint8_t ** buf)
|
||||||
|
{
|
||||||
|
int32_t n =
|
||||||
|
(*buf)[0] << 24
|
||||||
|
| (*buf)[1] << 16
|
||||||
|
| (*buf)[2] << 8
|
||||||
|
| (*buf)[3] << 0
|
||||||
|
;
|
||||||
|
(*buf) += 4;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int32_t parse_s2(const uint8_t ** buf)
|
||||||
|
{
|
||||||
|
int16_t n =
|
||||||
|
(*buf)[0] << 8
|
||||||
|
| (*buf)[1] << 0
|
||||||
|
;
|
||||||
|
(*buf) += 2;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int32_t parse_s1(const uint8_t ** buf)
|
||||||
|
{
|
||||||
|
int8_t n = (*buf)[0];
|
||||||
|
(*buf) += 1;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void * parse_bytes(const uint8_t ** buf, int length)
|
||||||
|
{
|
||||||
|
uint8_t * dest = malloc_class_arena(length);
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
dest[i] = (*buf)[i];
|
||||||
|
(*buf) += length;
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool bytes_equal(int length, const uint8_t * a, const char * b)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
if (((char)a[i]) != ((char)b[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return b[i] == 0;
|
||||||
|
}
|
225
c/class_file.c
Normal file
225
c/class_file.c
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
#ifdef DEBUG
|
||||||
|
#include <assert.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "class_file.h"
|
||||||
|
#include "malloc.h"
|
||||||
|
#include "bytes.h"
|
||||||
|
|
||||||
|
struct attribute_info * attribute_info_parse(const uint8_t ** buf, int attribute_count, struct constant * constant_pool);
|
||||||
|
|
||||||
|
void * attribute_info_parse_info(const uint8_t * buf, struct attribute_info * attribute, struct constant * constant_pool)
|
||||||
|
{
|
||||||
|
struct constant * attribute_name = &constant_pool[attribute->attribute_name_index - 1];
|
||||||
|
#ifdef DEBUG
|
||||||
|
assert(attribute_name->tag == CONSTANT_Utf8);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (bytes_equal(attribute_name->utf8.length, attribute_name->utf8.bytes, "ConstantValue")) {
|
||||||
|
struct ConstantValue_attribute * constantvalue = malloc_class_arena((sizeof (struct ConstantValue_attribute)));
|
||||||
|
constantvalue->constantvalue_index = parse_u2(&buf);
|
||||||
|
return constantvalue;
|
||||||
|
} else if (bytes_equal(attribute_name->utf8.length, attribute_name->utf8.bytes, "Code")) {
|
||||||
|
// parse Code
|
||||||
|
struct Code_attribute * code = malloc_class_arena((sizeof (struct Code_attribute)));
|
||||||
|
|
||||||
|
code->max_stack = parse_u2(&buf);
|
||||||
|
code->max_locals = parse_u2(&buf);
|
||||||
|
code->code_length = parse_u4(&buf);
|
||||||
|
|
||||||
|
code->code = parse_bytes(&buf, code->code_length);
|
||||||
|
code->exception_table_length = parse_u2(&buf);
|
||||||
|
uint32_t exception_table_size = (sizeof (struct exception_table_entry)) * code->exception_table_length;
|
||||||
|
code->exception_table = malloc_class_arena(exception_table_size);
|
||||||
|
for (int i = 0; i < code->exception_table_length; i++) {
|
||||||
|
// parse exception_table_entry
|
||||||
|
code->exception_table[i].start_pc = parse_u2(&buf);
|
||||||
|
code->exception_table[i].end_pc = parse_u2(&buf);
|
||||||
|
code->exception_table[i].handler_pc = parse_u2(&buf);
|
||||||
|
code->exception_table[i].catch_type = parse_u2(&buf);
|
||||||
|
}
|
||||||
|
code->attributes_count = parse_u2(&buf);
|
||||||
|
|
||||||
|
code->attributes = attribute_info_parse(&buf, code->attributes_count, constant_pool);
|
||||||
|
|
||||||
|
return code;
|
||||||
|
} else if (bytes_equal(attribute_name->utf8.length, attribute_name->utf8.bytes, "BootstrapMethods")) {
|
||||||
|
// parse BootstrapMethods
|
||||||
|
struct BootstrapMethods_attribute * bootstrapmethods = malloc_class_arena((sizeof (struct BootstrapMethods_attribute)));
|
||||||
|
|
||||||
|
bootstrapmethods->num_bootstrap_methods = parse_u2(&buf);
|
||||||
|
|
||||||
|
uint32_t bootstrap_methods_size = (sizeof (struct bootstrap_method)) * bootstrapmethods->num_bootstrap_methods;
|
||||||
|
bootstrapmethods->bootstrap_methods = malloc_class_arena(bootstrap_methods_size);
|
||||||
|
|
||||||
|
for (int i = 0; i < bootstrapmethods->num_bootstrap_methods; i++) {
|
||||||
|
bootstrapmethods->bootstrap_methods[i].bootstrap_method_ref = parse_u2(&buf);
|
||||||
|
bootstrapmethods->bootstrap_methods[i].num_bootstrap_arguments = parse_u2(&buf);
|
||||||
|
|
||||||
|
uint32_t bootstrap_arguments_size = (sizeof (u2)) * bootstrapmethods->bootstrap_methods[i].num_bootstrap_arguments;
|
||||||
|
bootstrapmethods->bootstrap_methods[i].bootstrap_arguments = malloc_class_arena(bootstrap_arguments_size);
|
||||||
|
for (int j = 0; j < bootstrapmethods->bootstrap_methods[i].num_bootstrap_arguments; j++) {
|
||||||
|
bootstrapmethods->bootstrap_methods[i].bootstrap_arguments[j] = parse_u2(&buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bootstrapmethods;
|
||||||
|
} else if (bytes_equal(attribute_name->utf8.length, attribute_name->utf8.bytes, "BootstrapMethods")) {
|
||||||
|
} else if (bytes_equal(attribute_name->utf8.length, attribute_name->utf8.bytes, "NestHost")) {
|
||||||
|
struct NestHost_attribute * nesthost = malloc_class_arena((sizeof (struct NestHost_attribute)));
|
||||||
|
nesthost->host_class_index = parse_u2(&buf);
|
||||||
|
|
||||||
|
return nesthost;
|
||||||
|
} else if (bytes_equal(attribute_name->utf8.length, attribute_name->utf8.bytes, "NestMembers")) {
|
||||||
|
struct NestMembers_attribute * nestmembers = malloc_class_arena((sizeof (struct NestMembers_attribute)));
|
||||||
|
|
||||||
|
nestmembers->number_of_classes = parse_u2(&buf);
|
||||||
|
uint32_t classes_size = (sizeof (u2)) * nestmembers->number_of_classes;
|
||||||
|
nestmembers->classes = malloc_class_arena(classes_size);
|
||||||
|
|
||||||
|
for (int i = 0; i < nestmembers->number_of_classes; i++) {
|
||||||
|
nestmembers->classes[i] = parse_u2(&buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nestmembers;
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct attribute_info * attribute_info_parse(const uint8_t ** buf, int attribute_count, struct constant * constant_pool)
|
||||||
|
{
|
||||||
|
uint32_t attributes_size = (sizeof (struct attribute_info)) * attribute_count;
|
||||||
|
struct attribute_info * attributes = malloc_class_arena(attributes_size);
|
||||||
|
for (int i = 0; i < attribute_count; i++) {
|
||||||
|
attributes[i].attribute_name_index = parse_u2(buf);
|
||||||
|
attributes[i].attribute_length = parse_u4(buf);
|
||||||
|
|
||||||
|
attributes[i].info = attribute_info_parse_info(*buf, &attributes[i], constant_pool);
|
||||||
|
|
||||||
|
(*buf) += attributes[i].attribute_length;
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct class_file * class_file_parse(const uint8_t * buf)
|
||||||
|
{
|
||||||
|
struct class_file * class_file = malloc_class_arena((sizeof (struct class_file)));
|
||||||
|
|
||||||
|
class_file->magic = parse_u4(&buf);
|
||||||
|
class_file->minor_version = parse_u2(&buf);
|
||||||
|
class_file->major_version = parse_u2(&buf);
|
||||||
|
class_file->constant_pool_count = parse_u2(&buf);
|
||||||
|
|
||||||
|
uint32_t constant_pool_size = (sizeof (struct constant)) * (class_file->constant_pool_count - 1);
|
||||||
|
class_file->constant_pool = malloc_class_arena(constant_pool_size);
|
||||||
|
|
||||||
|
// parse constants
|
||||||
|
for (int i = 0; i < class_file->constant_pool_count - 1; i++) {
|
||||||
|
struct constant * constant = &class_file->constant_pool[i];
|
||||||
|
u1 tag = parse_u1(&buf);
|
||||||
|
#ifdef DEBUG
|
||||||
|
constant->tag = tag;
|
||||||
|
#endif
|
||||||
|
switch (tag) {
|
||||||
|
case CONSTANT_Class:
|
||||||
|
constant->class.name_index = parse_u2(&buf);
|
||||||
|
break;
|
||||||
|
case CONSTANT_Fieldref: [[fallthrough]];
|
||||||
|
case CONSTANT_Methodref: [[fallthrough]];
|
||||||
|
case CONSTANT_InterfaceMethodref:
|
||||||
|
constant->fieldref.class_index = parse_u2(&buf);
|
||||||
|
constant->fieldref.name_and_type_index = parse_u2(&buf);
|
||||||
|
break;
|
||||||
|
case CONSTANT_String:
|
||||||
|
constant->string.string_index = parse_u2(&buf);
|
||||||
|
break;
|
||||||
|
case CONSTANT_Integer: [[fallthrough]];
|
||||||
|
case CONSTANT_Float:
|
||||||
|
constant->integer.bytes = parse_u4(&buf);
|
||||||
|
break;
|
||||||
|
case CONSTANT_Long: [[fallthrough]];
|
||||||
|
case CONSTANT_Double:
|
||||||
|
constant->_long.high_bytes = parse_u4(&buf);
|
||||||
|
constant->_long.low_bytes = parse_u4(&buf);
|
||||||
|
break;
|
||||||
|
case CONSTANT_NameAndType:
|
||||||
|
constant->nameandtype.name_index = parse_u2(&buf);
|
||||||
|
constant->nameandtype.descriptor_index = parse_u2(&buf);
|
||||||
|
break;
|
||||||
|
case CONSTANT_Utf8:
|
||||||
|
constant->utf8.length = parse_u2(&buf);
|
||||||
|
constant->utf8.bytes = parse_bytes(&buf, constant->utf8.length);
|
||||||
|
break;
|
||||||
|
case CONSTANT_MethodHandle:
|
||||||
|
constant->methodhandle.reference_kind = parse_u1(&buf);
|
||||||
|
constant->methodhandle.reference_index = parse_u2(&buf);
|
||||||
|
break;
|
||||||
|
case CONSTANT_MethodType:
|
||||||
|
constant->methodtype.descriptor_index = parse_u2(&buf);
|
||||||
|
break;
|
||||||
|
case CONSTANT_Dynamic: [[fallthrough]];
|
||||||
|
case CONSTANT_InvokeDynamic:
|
||||||
|
constant->dynamic.bootstrap_method_attr_index = parse_u2(&buf);
|
||||||
|
constant->dynamic.name_and_type_index = parse_u2(&buf);
|
||||||
|
break;
|
||||||
|
case CONSTANT_Module: [[fallthrough]];
|
||||||
|
case CONSTANT_Package:
|
||||||
|
constant->module.name_index = parse_u2(&buf);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
#ifdef DEBUG
|
||||||
|
assert(false);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class_file->access_flags = parse_u2(&buf);
|
||||||
|
class_file->this_class = parse_u2(&buf);
|
||||||
|
class_file->super_class = parse_u2(&buf);
|
||||||
|
class_file->interfaces_count = parse_u2(&buf);
|
||||||
|
|
||||||
|
// parse interfaces
|
||||||
|
uint32_t interfaces_size = (sizeof (class_file->interfaces[0])) * class_file->interfaces_count;
|
||||||
|
class_file->interfaces = malloc_class_arena(interfaces_size);
|
||||||
|
for (int i = 0; i < class_file->interfaces_count; i++)
|
||||||
|
class_file->interfaces[i] = parse_u2(&buf);
|
||||||
|
|
||||||
|
// parse fields
|
||||||
|
class_file->fields_count = parse_u2(&buf);
|
||||||
|
uint32_t fields_size = (sizeof (struct field_info)) * class_file->fields_count;
|
||||||
|
class_file->fields = malloc_class_arena(fields_size);
|
||||||
|
for (int i = 0; i < class_file->fields_count; i++) {
|
||||||
|
class_file->fields[i].access_flags = parse_u2(&buf);
|
||||||
|
class_file->fields[i].name_index = parse_u2(&buf);
|
||||||
|
class_file->fields[i].descriptor_index = parse_u2(&buf);
|
||||||
|
class_file->fields[i].attributes_count = parse_u2(&buf);
|
||||||
|
|
||||||
|
// parse attributes
|
||||||
|
class_file->fields[i].attributes = attribute_info_parse(&buf, class_file->fields[i].attributes_count, class_file->constant_pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// parse methods
|
||||||
|
class_file->methods_count = parse_u2(&buf);
|
||||||
|
uint32_t methods_size = (sizeof (struct method_info)) * class_file->methods_count;
|
||||||
|
class_file->methods = malloc_class_arena(methods_size);
|
||||||
|
for (int i = 0; i < class_file->methods_count; i++) {
|
||||||
|
class_file->methods[i].access_flags = parse_u2(&buf);
|
||||||
|
class_file->methods[i].name_index = parse_u2(&buf);
|
||||||
|
class_file->methods[i].descriptor_index = parse_u2(&buf);
|
||||||
|
class_file->methods[i].attributes_count = parse_u2(&buf);
|
||||||
|
|
||||||
|
// parse attributes
|
||||||
|
class_file->methods[i].attributes = attribute_info_parse(&buf, class_file->methods[i].attributes_count, class_file->constant_pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse attributes
|
||||||
|
class_file->attributes_count = parse_u2(&buf);
|
||||||
|
class_file->attributes = attribute_info_parse(&buf, class_file->attributes_count, class_file->constant_pool);
|
||||||
|
|
||||||
|
return class_file;
|
||||||
|
}
|
271
c/class_file.h
Normal file
271
c/class_file.h
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef uint32_t u4;
|
||||||
|
typedef uint16_t u2;
|
||||||
|
typedef uint8_t u1;
|
||||||
|
|
||||||
|
enum CONSTANT {
|
||||||
|
CONSTANT_Class = 7, // §4.4.1
|
||||||
|
CONSTANT_Fieldref = 9, // §4.4.2
|
||||||
|
CONSTANT_Methodref = 10, // §4.4.2
|
||||||
|
CONSTANT_InterfaceMethodref = 11, // §4.4.2
|
||||||
|
CONSTANT_String = 8, // §4.4.3
|
||||||
|
CONSTANT_Integer = 3, // §4.4.4
|
||||||
|
CONSTANT_Float = 4, // §4.4.4
|
||||||
|
CONSTANT_Long = 5, // §4.4.5
|
||||||
|
CONSTANT_Double = 6, // §4.4.5
|
||||||
|
CONSTANT_NameAndType = 12, // §4.4.6
|
||||||
|
CONSTANT_Utf8 = 1, // §4.4.7
|
||||||
|
CONSTANT_MethodHandle = 15, // §4.4.8
|
||||||
|
CONSTANT_MethodType = 16, // §4.4.9
|
||||||
|
CONSTANT_Dynamic = 17, // §4.4.10
|
||||||
|
CONSTANT_InvokeDynamic = 18, // §4.4.10
|
||||||
|
CONSTANT_Module = 19, // §4.4.11
|
||||||
|
CONSTANT_Package = 20, // §4.4.12
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_Class_info {
|
||||||
|
u2 name_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_Fieldref_info {
|
||||||
|
u2 class_index;
|
||||||
|
u2 name_and_type_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_Methodref_info {
|
||||||
|
u2 class_index;
|
||||||
|
u2 name_and_type_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_InterfaceMethodref_info {
|
||||||
|
u2 class_index;
|
||||||
|
u2 name_and_type_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_String_info {
|
||||||
|
u2 string_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_Integer_info {
|
||||||
|
u4 bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_Float_info {
|
||||||
|
u4 bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_Long_info {
|
||||||
|
u4 high_bytes;
|
||||||
|
u4 low_bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_Double_info {
|
||||||
|
u4 high_bytes;
|
||||||
|
u4 low_bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_NameAndType_info {
|
||||||
|
u2 name_index;
|
||||||
|
u2 descriptor_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_Utf8_info {
|
||||||
|
u2 length;
|
||||||
|
u1 * bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_MethodHandle_info {
|
||||||
|
u1 reference_kind;
|
||||||
|
u2 reference_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_MethodType_info {
|
||||||
|
u2 descriptor_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_Dynamic_info {
|
||||||
|
u2 bootstrap_method_attr_index;
|
||||||
|
u2 name_and_type_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_InvokeDynamic_info {
|
||||||
|
u2 bootstrap_method_attr_index;
|
||||||
|
u2 name_and_type_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_Module_info {
|
||||||
|
u2 name_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant_Package_info {
|
||||||
|
u2 name_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct constant {
|
||||||
|
#ifdef DEBUG
|
||||||
|
u1 tag;
|
||||||
|
#endif
|
||||||
|
union {
|
||||||
|
struct constant_Class_info class;
|
||||||
|
struct constant_Fieldref_info fieldref;
|
||||||
|
struct constant_Methodref_info methodref;
|
||||||
|
struct constant_InterfaceMethodref_info interfacemethodref;
|
||||||
|
struct constant_String_info string;
|
||||||
|
struct constant_Integer_info integer;
|
||||||
|
struct constant_Float_info _float;
|
||||||
|
struct constant_Long_info _long;
|
||||||
|
struct constant_Double_info _double;
|
||||||
|
struct constant_NameAndType_info nameandtype;
|
||||||
|
struct constant_Utf8_info utf8;
|
||||||
|
struct constant_MethodHandle_info methodhandle;
|
||||||
|
struct constant_MethodType_info methodtype;
|
||||||
|
struct constant_Dynamic_info dynamic;
|
||||||
|
struct constant_InvokeDynamic_info invokedynamic;
|
||||||
|
struct constant_Module_info module;
|
||||||
|
struct constant_Package_info package;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConstantValue_attribute;
|
||||||
|
struct Code_attribute;
|
||||||
|
struct StackMapTable_attribute;
|
||||||
|
struct BootstrapMethods_attribute;
|
||||||
|
struct NestHost_attribute;
|
||||||
|
struct NestMembers_attribute;
|
||||||
|
struct PermittedSubclasses_attribute;
|
||||||
|
|
||||||
|
struct attribute_info {
|
||||||
|
u2 attribute_name_index;
|
||||||
|
u4 attribute_length;
|
||||||
|
union {
|
||||||
|
void * info;
|
||||||
|
struct ConstantValue_attribute * constantvalue;
|
||||||
|
struct Code_attribute * code;
|
||||||
|
//struct StackMapTable_attribute * stackmaptable;
|
||||||
|
struct BootstrapMethods_attribute * bootstrapmethods;
|
||||||
|
struct NestHost_attribute * nesthost;
|
||||||
|
struct NestMembers_attribute * nestmembers;
|
||||||
|
//struct PermittedSubclasses_attribute * permittedsubclasses;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConstantValue_attribute {
|
||||||
|
u2 constantvalue_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct exception_table_entry {
|
||||||
|
u2 start_pc;
|
||||||
|
u2 end_pc;
|
||||||
|
u2 handler_pc;
|
||||||
|
u2 catch_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Code_attribute {
|
||||||
|
u2 max_stack;
|
||||||
|
u2 max_locals;
|
||||||
|
u4 code_length;
|
||||||
|
u1 * code;
|
||||||
|
u2 exception_table_length;
|
||||||
|
struct exception_table_entry * exception_table;
|
||||||
|
u2 attributes_count;
|
||||||
|
struct attribute_info * attributes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bootstrap_method {
|
||||||
|
u2 bootstrap_method_ref;
|
||||||
|
u2 num_bootstrap_arguments;
|
||||||
|
u2 * bootstrap_arguments;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BootstrapMethods_attribute {
|
||||||
|
u2 num_bootstrap_methods;
|
||||||
|
struct bootstrap_method * bootstrap_methods;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NestHost_attribute {
|
||||||
|
u2 host_class_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NestMembers_attribute {
|
||||||
|
u2 number_of_classes;
|
||||||
|
u2 * classes;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum FIELD_ACC {
|
||||||
|
FIELD_ACC_PUBLIC = 0x0001, // Declared public; may be accessed from outside its package.
|
||||||
|
FIELD_ACC_PRIVATE = 0x0002, // Declared private; accessible only within the defining class and other classes belonging to the same nest (§5.4.4).
|
||||||
|
FIELD_ACC_PROTECTED = 0x0004, // Declared protected; may be accessed within subclasses.
|
||||||
|
FIELD_ACC_STATIC = 0x0008, // Declared static.
|
||||||
|
FIELD_ACC_FINAL = 0x0010, // Declared final; never directly assigned to after object construction (JLS §17.5).
|
||||||
|
FIELD_ACC_VOLATILE = 0x0040, // Declared volatile; cannot be cached.
|
||||||
|
FIELD_ACC_TRANSIENT = 0x0080, // Declared transient; not written or read by a persistent object manager.
|
||||||
|
FIELD_ACC_SYNTHETIC = 0x1000, // Declared synthetic; not present in the source code.
|
||||||
|
FIELD_ACC_ENUM = 0x4000, // Declared as an element of an enum class.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct field_info {
|
||||||
|
u2 access_flags;
|
||||||
|
u2 name_index;
|
||||||
|
u2 descriptor_index;
|
||||||
|
u2 attributes_count;
|
||||||
|
struct attribute_info * attributes;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum METHOD_ACC {
|
||||||
|
METHOD_ACC_PUBLIC = 0x0001, // Declared public; may be accessed from outside its package.
|
||||||
|
METHOD_ACC_PRIVATE = 0x0002, // Declared private; accessible only within the defining class and other classes belonging to the same nest (§5.4.4).
|
||||||
|
METHOD_ACC_PROTECTED = 0x0004, // Declared protected; may be accessed within subclasses.
|
||||||
|
METHOD_ACC_STATIC = 0x0008, // Declared static.
|
||||||
|
METHOD_ACC_FINAL = 0x0010, // Declared final; must not be overridden (§5.4.5).
|
||||||
|
METHOD_ACC_SYNCHRONIZED = 0x0020, // Declared synchronized; invocation is wrapped by a monitor use.
|
||||||
|
METHOD_ACC_BRIDGE = 0x0040, // A bridge method, generated by the compiler.
|
||||||
|
METHOD_ACC_VARARGS = 0x0080, // Declared with variable number of arguments.
|
||||||
|
METHOD_ACC_NATIVE = 0x0100, // Declared native; implemented in a language other than the Java programming language.
|
||||||
|
METHOD_ACC_ABSTRACT = 0x0400, // Declared abstract; no implementation is provided.
|
||||||
|
METHOD_ACC_STRICT = 0x0800, // In a class file whose major version number is at least 46 and at most 60: Declared strictfp.
|
||||||
|
METHOD_ACC_SYNTHETIC = 0x1000, // Declared synthetic; not present in the source code.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct method_info {
|
||||||
|
u2 access_flags;
|
||||||
|
u2 name_index;
|
||||||
|
u2 descriptor_index;
|
||||||
|
u2 attributes_count;
|
||||||
|
struct attribute_info * attributes;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum CLASS_ACC {
|
||||||
|
CLASS_ACC_PUBLIC = 0x0001, // Declared public; may be accessed from outside its package.
|
||||||
|
CLASS_ACC_FINAL = 0x0010, // Declared final; no subclasses allowed.
|
||||||
|
CLASS_ACC_SUPER = 0x0020, // Treat superclass methods specially when invoked by the invokespecial instruction.
|
||||||
|
CLASS_ACC_INTERFACE = 0x0200, // Is an interface, not a class.
|
||||||
|
CLASS_ACC_ABSTRACT = 0x0400, // Declared abstract; must not be instantiated.
|
||||||
|
CLASS_ACC_SYNTHETIC = 0x1000, // Declared synthetic; not present in the source code.
|
||||||
|
CLASS_ACC_ANNOTATION = 0x2000, // Declared as an annotation interface.
|
||||||
|
CLASS_ACC_ENUM = 0x4000, // Declared as an enum class.
|
||||||
|
CLASS_ACC_MODULE = 0x8000, // Is a module, not a class or interface.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct class_file {
|
||||||
|
u4 magic;
|
||||||
|
u2 minor_version;
|
||||||
|
u2 major_version;
|
||||||
|
u2 constant_pool_count;
|
||||||
|
struct constant * constant_pool;
|
||||||
|
u2 access_flags;
|
||||||
|
u2 this_class;
|
||||||
|
u2 super_class;
|
||||||
|
u2 interfaces_count;
|
||||||
|
u2 * interfaces;
|
||||||
|
u2 fields_count;
|
||||||
|
struct field_info * fields;
|
||||||
|
u2 methods_count;
|
||||||
|
struct method_info * methods;
|
||||||
|
u2 attributes_count;
|
||||||
|
struct attribute_info * attributes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct class_file * class_file_parse(const uint8_t * buf);
|
0
c/constant.h
Normal file
0
c/constant.h
Normal file
278
c/debug_class_file.c
Normal file
278
c/debug_class_file.c
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "class_file.h"
|
||||||
|
#include "bytes.h"
|
||||||
|
#include "decode.h"
|
||||||
|
#include "debug_class_file.h"
|
||||||
|
|
||||||
|
void print_constant(struct constant * constant)
|
||||||
|
{
|
||||||
|
switch (constant->tag) {
|
||||||
|
case CONSTANT_Class:
|
||||||
|
printf("CONSTANT_Class name_index=%d\n",
|
||||||
|
constant->class.name_index);
|
||||||
|
break;
|
||||||
|
case CONSTANT_Fieldref:
|
||||||
|
printf("CONSTANT_Fieldref class_index=%d name_and_type_index=%d\n",
|
||||||
|
constant->fieldref.class_index,
|
||||||
|
constant->fieldref.name_and_type_index);
|
||||||
|
break;
|
||||||
|
case CONSTANT_Methodref:
|
||||||
|
printf("CONSTANT_Methodref class_index=%d name_and_type_index=%d\n",
|
||||||
|
constant->methodref.class_index,
|
||||||
|
constant->methodref.name_and_type_index);
|
||||||
|
break;
|
||||||
|
case CONSTANT_InterfaceMethodref:
|
||||||
|
printf("CONSTANT_InterfaceMethodref class_index=%d name_and_type_index=%d\n",
|
||||||
|
constant->interfacemethodref.class_index,
|
||||||
|
constant->interfacemethodref.name_and_type_index);
|
||||||
|
break;
|
||||||
|
case CONSTANT_String:
|
||||||
|
printf("CONSTANT_String string_index=%d\n",
|
||||||
|
constant->string.string_index);
|
||||||
|
break;
|
||||||
|
case CONSTANT_Integer:
|
||||||
|
printf("CONSTANT_Integer bytes=%d\n",
|
||||||
|
constant->integer.bytes);
|
||||||
|
break;
|
||||||
|
case CONSTANT_Float:
|
||||||
|
printf("CONSTANT_Float bytes=%f\n",
|
||||||
|
*(float *)(&constant->_float.bytes));
|
||||||
|
break;
|
||||||
|
case CONSTANT_Long:
|
||||||
|
printf("CONSTANT_Long high_bytes=%d low_bytes=%d\n",
|
||||||
|
constant->_long.high_bytes,
|
||||||
|
constant->_long.low_bytes);
|
||||||
|
break;
|
||||||
|
case CONSTANT_Double:
|
||||||
|
printf("CONSTANT_Double high_bytes=%d low_bytes=%d\n",
|
||||||
|
constant->_long.high_bytes,
|
||||||
|
constant->_long.low_bytes);
|
||||||
|
break;
|
||||||
|
case CONSTANT_NameAndType:
|
||||||
|
printf("CONSTANT_NameAndType %d %d\n",
|
||||||
|
constant->nameandtype.name_index,
|
||||||
|
constant->nameandtype.descriptor_index);
|
||||||
|
break;
|
||||||
|
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);
|
||||||
|
break;
|
||||||
|
case CONSTANT_MethodHandle:
|
||||||
|
printf("CONSTANT_MethodHandle reference_kind=%d reference_index=%d\n",
|
||||||
|
constant->methodhandle.reference_kind,
|
||||||
|
constant->methodhandle.reference_index);
|
||||||
|
break;
|
||||||
|
case CONSTANT_MethodType:
|
||||||
|
printf("CONSTANT_MethodType descriptor_index=%d\n",
|
||||||
|
constant->methodtype.descriptor_index);
|
||||||
|
break;
|
||||||
|
case CONSTANT_Dynamic:
|
||||||
|
printf("CONSTANT_Dynamic bootstrap_method_attr_index=%d name_and_type_index=%d\n",
|
||||||
|
constant->dynamic.bootstrap_method_attr_index,
|
||||||
|
constant->dynamic.name_and_type_index);
|
||||||
|
break;
|
||||||
|
case CONSTANT_InvokeDynamic:
|
||||||
|
printf("CONSTANT_InvokeDynamic bootstrap_method_attr_index=%d name_and_type_index=%d\n",
|
||||||
|
constant->invokedynamic.bootstrap_method_attr_index,
|
||||||
|
constant->invokedynamic.name_and_type_index);
|
||||||
|
break;
|
||||||
|
case CONSTANT_Module:
|
||||||
|
printf("CONSTANT_Module name_index=%d\n",
|
||||||
|
constant->module.name_index);
|
||||||
|
break;
|
||||||
|
case CONSTANT_Package:
|
||||||
|
printf("CONSTANT_Package name_index=%d\n",
|
||||||
|
constant->package.name_index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_attribute(const char * indent, struct attribute_info * attribute, struct constant * constant_pool)
|
||||||
|
{
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("attribute_name_index: %d\n", attribute->attribute_name_index);
|
||||||
|
struct constant * attribute_name = &constant_pool[attribute->attribute_name_index - 1];
|
||||||
|
fputs(indent, stdout);
|
||||||
|
fputs(" ", stdout);
|
||||||
|
print_constant(attribute_name);
|
||||||
|
|
||||||
|
if (bytes_equal(attribute_name->utf8.length, attribute_name->utf8.bytes, "ConstantValue")) {
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("constantvalue_index %d\n", attribute->constantvalue->constantvalue_index);
|
||||||
|
|
||||||
|
|
||||||
|
struct constant * value = &constant_pool[attribute->constantvalue->constantvalue_index - 1];
|
||||||
|
fputs(indent, stdout);
|
||||||
|
fputs(" ", stdout);
|
||||||
|
print_constant(value);
|
||||||
|
if (value->tag == CONSTANT_String) {
|
||||||
|
fputs(indent, stdout);
|
||||||
|
fputs(" ", stdout);
|
||||||
|
print_constant(&constant_pool[value->string.string_index - 1]);
|
||||||
|
}
|
||||||
|
} else if (bytes_equal(attribute_name->utf8.length, attribute_name->utf8.bytes, "Code")) {
|
||||||
|
// print code
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("max_stack %d\n", attribute->code->max_stack);
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("max_locals %d\n", attribute->code->max_locals);
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("code_length %d\n", attribute->code->code_length);
|
||||||
|
|
||||||
|
// dump code
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("code:\n");
|
||||||
|
uint32_t pc = 0;
|
||||||
|
while (pc < attribute->code->code_length) {
|
||||||
|
fputs(indent, stdout);
|
||||||
|
fputs(" ", stdout);
|
||||||
|
pc = decode_print_instruction(attribute->code->code, pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("exception_table_length: %d\n", attribute->code->exception_table_length);
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("exceptions:\n");
|
||||||
|
for (int i = 0; i < attribute->code->exception_table_length; i++) {
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf(" exception %d:\n", i);
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf(" start_pc: %d\n", attribute->code->exception_table[i].start_pc);
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf(" end_pc: %d\n", attribute->code->exception_table[i].end_pc);
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf(" handler_pc: %d\n", attribute->code->exception_table[i].handler_pc);
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf(" catch_type: %d\n", attribute->code->exception_table[i].catch_type);
|
||||||
|
}
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("attributes_count: %d\n", attribute->code->attributes_count);
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("attributes:\n");
|
||||||
|
for (int i = 0; i < attribute->code->attributes_count; i++) {
|
||||||
|
char indent2[strlen(indent) + 2 + 1];
|
||||||
|
strcpy(indent2, indent);
|
||||||
|
strcpy(indent2 + strlen(indent), " ");
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf(" attribute %d:\n", i);
|
||||||
|
print_attribute(indent2, &attribute->code->attributes[i], constant_pool);
|
||||||
|
}
|
||||||
|
} else if (bytes_equal(attribute_name->utf8.length, attribute_name->utf8.bytes, "BootstrapMethods")) {
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("num_bootstrap_methods: %d\n", attribute->bootstrapmethods->num_bootstrap_methods);
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("bootstrap methods:\n");
|
||||||
|
for (int i = 0; i < attribute->bootstrapmethods->num_bootstrap_methods; i++) {
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf(" bootstrap_method %d:\n", i);
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf(" bootstrap_method_ref: %d\n", attribute->bootstrapmethods->bootstrap_methods[i].bootstrap_method_ref);
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf(" num_bootstrap_arguments: %d\n", attribute->bootstrapmethods->bootstrap_methods[i].num_bootstrap_arguments);
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf(" bootstrap_arguments:\n");
|
||||||
|
for (int j = 0; j < attribute->bootstrapmethods->bootstrap_methods[i].num_bootstrap_arguments; j++) {
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf(" bootstrap_argument %d: %d\n", j, attribute->bootstrapmethods->bootstrap_methods[i].bootstrap_arguments[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (bytes_equal(attribute_name->utf8.length, attribute_name->utf8.bytes, "NestHost")) {
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("host_class_index: %d\n", attribute->nesthost->host_class_index);
|
||||||
|
} else if (bytes_equal(attribute_name->utf8.length, attribute_name->utf8.bytes, "NestMembers")) {
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("number_of_classes: %d\n", attribute->nestmembers->number_of_classes);
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf("classes:\n");
|
||||||
|
for (int i = 0; i < attribute->nestmembers->number_of_classes; i++) {
|
||||||
|
fputs(indent, stdout);
|
||||||
|
printf(" class %d:\n", i);
|
||||||
|
fputs(indent, stdout);
|
||||||
|
fputs(" ", stdout);
|
||||||
|
print_constant(&constant_pool[attribute->nestmembers->classes[i] - 1]);
|
||||||
|
int ix = constant_pool[attribute->nestmembers->classes[i] - 1].class.name_index;
|
||||||
|
fputs(indent, stdout);
|
||||||
|
fputs(" ", stdout);
|
||||||
|
print_constant(&constant_pool[ix - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_class_file(struct class_file * class_file)
|
||||||
|
{
|
||||||
|
printf("magic %08x\n", class_file->magic);
|
||||||
|
printf("minor_version %d\n", class_file->minor_version);
|
||||||
|
printf("major_version %d\n", class_file->major_version);
|
||||||
|
printf("constant_pool_count %d\n", class_file->constant_pool_count);
|
||||||
|
|
||||||
|
printf("constants:\n");
|
||||||
|
for (int i = 0; i < class_file->constant_pool_count - 1; i++) {
|
||||||
|
printf("% 3d: ", i + 1);
|
||||||
|
print_constant(&class_file->constant_pool[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("access_flags %04x\n", class_file->access_flags);
|
||||||
|
printf("this_class %d\n", class_file->this_class);
|
||||||
|
printf("super_class %d\n", class_file->super_class);
|
||||||
|
printf("interfaces_count %d\n", class_file->interfaces_count);
|
||||||
|
|
||||||
|
printf("interfaces:\n");
|
||||||
|
for (int i = 0; i < class_file->interfaces_count; i++) {
|
||||||
|
printf("% 3d: %d\n", i + 1, class_file->interfaces[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("fields_count %d\n", class_file->fields_count);
|
||||||
|
printf("fields:\n");
|
||||||
|
for (int i = 0; i < class_file->fields_count; i++) {
|
||||||
|
printf(" field %d:\n", i);
|
||||||
|
printf(" access_flags %d\n", class_file->fields[i].access_flags);
|
||||||
|
printf(" name_index %d\n", class_file->fields[i].name_index);
|
||||||
|
printf(" ");
|
||||||
|
print_constant(&class_file->constant_pool[class_file->fields[i].name_index - 1]);
|
||||||
|
printf(" descriptor_index %d\n", class_file->fields[i].descriptor_index);
|
||||||
|
printf(" ");
|
||||||
|
print_constant(&class_file->constant_pool[class_file->fields[i].descriptor_index - 1]);
|
||||||
|
printf(" attributes_count %d\n", class_file->fields[i].attributes_count);
|
||||||
|
printf(" attributes:\n");
|
||||||
|
for (int j = 0; j < class_file->fields[i].attributes_count; j++) {
|
||||||
|
printf(" attribute %d:\n", j);
|
||||||
|
print_attribute(" ", &class_file->fields[i].attributes[j], class_file->constant_pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("methods_count %d\n", class_file->methods_count);
|
||||||
|
printf("methods:\n");
|
||||||
|
for (int i = 0; i < class_file->methods_count; i++) {
|
||||||
|
printf(" method %d:\n", i);
|
||||||
|
printf(" access_flags %04x\n", class_file->methods[i].access_flags);
|
||||||
|
printf(" name_index %d\n", class_file->methods[i].name_index);
|
||||||
|
printf(" ");
|
||||||
|
print_constant(&class_file->constant_pool[class_file->methods[i].name_index - 1]);
|
||||||
|
printf(" descriptor_index %d\n", class_file->methods[i].descriptor_index);
|
||||||
|
printf(" ");
|
||||||
|
print_constant(&class_file->constant_pool[class_file->methods[i].descriptor_index - 1]);
|
||||||
|
printf(" attributes_count %d\n", class_file->methods[i].attributes_count);
|
||||||
|
printf(" attributes:\n");
|
||||||
|
for (int j = 0; j < class_file->methods[i].attributes_count; j++) {
|
||||||
|
printf(" attribute %d:\n", j);
|
||||||
|
print_attribute(" ", &class_file->methods[i].attributes[j], class_file->constant_pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
printf("attributes_count %d\n", class_file->attributes_count);
|
||||||
|
printf("attributes:\n");
|
||||||
|
for (int i = 0; i < class_file->attributes_count; i++) {
|
||||||
|
printf(" attribute %d:\n", i);
|
||||||
|
print_attribute(" ", &class_file->attributes[i], class_file->constant_pool);
|
||||||
|
}
|
||||||
|
}
|
7
c/debug_class_file.h
Normal file
7
c/debug_class_file.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "class_file.h"
|
||||||
|
|
||||||
|
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);
|
97
c/decode.c
Normal file
97
c/decode.c
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "decode.h"
|
||||||
|
#include "execute.h"
|
||||||
|
#include "bswap.h"
|
||||||
|
|
||||||
|
static inline uint32_t _u4(const uint8_t * buf)
|
||||||
|
{
|
||||||
|
uint32_t n =
|
||||||
|
buf[0] << 24
|
||||||
|
| buf[1] << 16
|
||||||
|
| buf[2] << 8
|
||||||
|
| buf[3] << 0
|
||||||
|
;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t _u2(const uint8_t * buf)
|
||||||
|
{
|
||||||
|
uint32_t n =
|
||||||
|
buf[0] << 8
|
||||||
|
| buf[1] << 0
|
||||||
|
;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t _u1(const uint8_t * buf)
|
||||||
|
{
|
||||||
|
uint32_t n = buf[0];
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int32_t _s4(const uint8_t * buf)
|
||||||
|
{
|
||||||
|
int32_t n =
|
||||||
|
buf[0] << 24
|
||||||
|
| buf[1] << 16
|
||||||
|
| buf[2] << 8
|
||||||
|
| buf[3] << 0
|
||||||
|
;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int32_t _s2(const uint8_t * buf)
|
||||||
|
{
|
||||||
|
int16_t n =
|
||||||
|
buf[0] << 8
|
||||||
|
| buf[1] << 0
|
||||||
|
;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int32_t _s1(const uint8_t * buf)
|
||||||
|
{
|
||||||
|
int8_t n = buf[0];
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int32_t aligned_s4(const void * buf)
|
||||||
|
{
|
||||||
|
uint32_t n = *((uint32_t *)buf);
|
||||||
|
return BE_BSWAP32(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TABLESWITCH_ARGS \
|
||||||
|
uint32_t args = ((pc + 1) + 3) & (~3); \
|
||||||
|
int32_t defaultbyte = aligned_s4(&code[args + 0]); \
|
||||||
|
int32_t lowbyte = aligned_s4(&code[args + 4]); \
|
||||||
|
int32_t highbyte = aligned_s4(&code[args + 8]); \
|
||||||
|
const int32_t * table = (const int32_t *)&code[args + 12];
|
||||||
|
|
||||||
|
#define TABLESWITCH_PRINT_ARGS() \
|
||||||
|
do { \
|
||||||
|
for (int i = lowbyte; i <= highbyte; i++) { \
|
||||||
|
printf(" %d: %d\n", i, aligned_s4(&table[i - lowbyte])); \
|
||||||
|
} \
|
||||||
|
printf("default: %d\n", defaultbyte); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
#define TABLESWITCH_NEXT_PC \
|
||||||
|
(args + (3 * 4) + ((highbyte - lowbyte + 1) * 4))
|
||||||
|
|
||||||
|
#define LOOKUPSWITCH_ARGS assert(false);
|
||||||
|
|
||||||
|
#define LOOKUPSWITCH_PRINT_ARGS()
|
||||||
|
|
||||||
|
#define LOOKUPSWITCH_NEXT_PC 0
|
||||||
|
|
||||||
|
#define WIDE_ARGS assert(false);
|
||||||
|
|
||||||
|
#define WIDE_PRINT_ARGS()
|
||||||
|
|
||||||
|
#define WIDE_NEXT_PC 0
|
||||||
|
|
||||||
|
#include "decode.inc.c"
|
6
c/decode.h
Normal file
6
c/decode.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "frame.h"
|
||||||
|
|
||||||
|
uint32_t decode_print_instruction(const uint8_t * code, uint32_t pc);
|
||||||
|
uint32_t decode_execute_instruction(struct vm * vm, const uint8_t * code, uint32_t pc);
|
2192
c/decode.inc.c
Normal file
2192
c/decode.inc.c
Normal file
File diff suppressed because it is too large
Load Diff
1217
c/execute.c
Normal file
1217
c/execute.c
Normal file
File diff suppressed because it is too large
Load Diff
211
c/execute.h
Normal file
211
c/execute.h
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "frame.h"
|
||||||
|
|
||||||
|
void op_aaload(struct vm * vm);
|
||||||
|
void op_aastore(struct vm * vm);
|
||||||
|
void op_aconst_null(struct vm * vm);
|
||||||
|
void op_aload(struct vm * vm, uint32_t index);
|
||||||
|
void op_aload_0(struct vm * vm);
|
||||||
|
void op_aload_1(struct vm * vm);
|
||||||
|
void op_aload_2(struct vm * vm);
|
||||||
|
void op_aload_3(struct vm * vm);
|
||||||
|
void op_anewarray(struct vm * vm, uint32_t index);
|
||||||
|
void op_areturn(struct vm * vm);
|
||||||
|
void op_arraylength(struct vm * vm);
|
||||||
|
void op_astore(struct vm * vm, uint32_t index);
|
||||||
|
void op_astore_0(struct vm * vm);
|
||||||
|
void op_astore_1(struct vm * vm);
|
||||||
|
void op_astore_2(struct vm * vm);
|
||||||
|
void op_astore_3(struct vm * vm);
|
||||||
|
void op_athrow(struct vm * vm);
|
||||||
|
void op_baload(struct vm * vm);
|
||||||
|
void op_bastore(struct vm * vm);
|
||||||
|
void op_bipush(struct vm * vm, int32_t byte);
|
||||||
|
void op_breakpoint(struct vm * vm);
|
||||||
|
void op_caload(struct vm * vm);
|
||||||
|
void op_castore(struct vm * vm);
|
||||||
|
void op_checkcast(struct vm * vm, uint32_t index);
|
||||||
|
void op_d2f(struct vm * vm);
|
||||||
|
void op_d2i(struct vm * vm);
|
||||||
|
void op_d2l(struct vm * vm);
|
||||||
|
void op_dadd(struct vm * vm);
|
||||||
|
void op_daload(struct vm * vm);
|
||||||
|
void op_dastore(struct vm * vm);
|
||||||
|
void op_dcmpg(struct vm * vm);
|
||||||
|
void op_dcmpl(struct vm * vm);
|
||||||
|
void op_dconst_0(struct vm * vm);
|
||||||
|
void op_dconst_1(struct vm * vm);
|
||||||
|
void op_ddiv(struct vm * vm);
|
||||||
|
void op_dload(struct vm * vm, uint32_t index);
|
||||||
|
void op_dload_0(struct vm * vm);
|
||||||
|
void op_dload_1(struct vm * vm);
|
||||||
|
void op_dload_2(struct vm * vm);
|
||||||
|
void op_dload_3(struct vm * vm);
|
||||||
|
void op_dmul(struct vm * vm);
|
||||||
|
void op_dneg(struct vm * vm);
|
||||||
|
void op_drem(struct vm * vm);
|
||||||
|
void op_dreturn(struct vm * vm);
|
||||||
|
void op_dstore(struct vm * vm, uint32_t index);
|
||||||
|
void op_dstore_0(struct vm * vm);
|
||||||
|
void op_dstore_1(struct vm * vm);
|
||||||
|
void op_dstore_2(struct vm * vm);
|
||||||
|
void op_dstore_3(struct vm * vm);
|
||||||
|
void op_dsub(struct vm * vm);
|
||||||
|
void op_dup(struct vm * vm);
|
||||||
|
void op_dup2(struct vm * vm);
|
||||||
|
void op_dup2_x1(struct vm * vm);
|
||||||
|
void op_dup2_x2(struct vm * vm);
|
||||||
|
void op_dup_x1(struct vm * vm);
|
||||||
|
void op_dup_x2(struct vm * vm);
|
||||||
|
void op_f2d(struct vm * vm);
|
||||||
|
void op_f2i(struct vm * vm);
|
||||||
|
void op_f2l(struct vm * vm);
|
||||||
|
void op_fadd(struct vm * vm);
|
||||||
|
void op_faload(struct vm * vm);
|
||||||
|
void op_fastore(struct vm * vm);
|
||||||
|
void op_fcmpg(struct vm * vm);
|
||||||
|
void op_fcmpl(struct vm * vm);
|
||||||
|
void op_fconst_0(struct vm * vm);
|
||||||
|
void op_fconst_1(struct vm * vm);
|
||||||
|
void op_fconst_2(struct vm * vm);
|
||||||
|
void op_fdiv(struct vm * vm);
|
||||||
|
void op_fload(struct vm * vm, uint32_t index);
|
||||||
|
void op_fload_0(struct vm * vm);
|
||||||
|
void op_fload_1(struct vm * vm);
|
||||||
|
void op_fload_2(struct vm * vm);
|
||||||
|
void op_fload_3(struct vm * vm);
|
||||||
|
void op_fmul(struct vm * vm);
|
||||||
|
void op_fneg(struct vm * vm);
|
||||||
|
void op_frem(struct vm * vm);
|
||||||
|
void op_freturn(struct vm * vm);
|
||||||
|
void op_fstore(struct vm * vm, uint32_t index);
|
||||||
|
void op_fstore_0(struct vm * vm);
|
||||||
|
void op_fstore_1(struct vm * vm);
|
||||||
|
void op_fstore_2(struct vm * vm);
|
||||||
|
void op_fstore_3(struct vm * vm);
|
||||||
|
void op_fsub(struct vm * vm);
|
||||||
|
void op_getfield(struct vm * vm, uint32_t index);
|
||||||
|
void op_getstatic(struct vm * vm, uint32_t index);
|
||||||
|
void op_goto(struct vm * vm, int32_t branch);
|
||||||
|
void op_goto_w(struct vm * vm, int32_t branch);
|
||||||
|
void op_i2b(struct vm * vm);
|
||||||
|
void op_i2c(struct vm * vm);
|
||||||
|
void op_i2d(struct vm * vm);
|
||||||
|
void op_i2f(struct vm * vm);
|
||||||
|
void op_i2l(struct vm * vm);
|
||||||
|
void op_i2s(struct vm * vm);
|
||||||
|
void op_iadd(struct vm * vm);
|
||||||
|
void op_iaload(struct vm * vm);
|
||||||
|
void op_iand(struct vm * vm);
|
||||||
|
void op_iastore(struct vm * vm);
|
||||||
|
void op_iconst_0(struct vm * vm);
|
||||||
|
void op_iconst_1(struct vm * vm);
|
||||||
|
void op_iconst_2(struct vm * vm);
|
||||||
|
void op_iconst_3(struct vm * vm);
|
||||||
|
void op_iconst_4(struct vm * vm);
|
||||||
|
void op_iconst_5(struct vm * vm);
|
||||||
|
void op_iconst_m1(struct vm * vm);
|
||||||
|
void op_idiv(struct vm * vm);
|
||||||
|
void op_if_acmpeq(struct vm * vm, int32_t branch);
|
||||||
|
void op_if_acmpne(struct vm * vm, int32_t branch);
|
||||||
|
void op_if_icmpeq(struct vm * vm, int32_t branch);
|
||||||
|
void op_if_icmpge(struct vm * vm, int32_t branch);
|
||||||
|
void op_if_icmpgt(struct vm * vm, int32_t branch);
|
||||||
|
void op_if_icmple(struct vm * vm, int32_t branch);
|
||||||
|
void op_if_icmplt(struct vm * vm, int32_t branch);
|
||||||
|
void op_if_icmpne(struct vm * vm, int32_t branch);
|
||||||
|
void op_ifeq(struct vm * vm, int32_t branch);
|
||||||
|
void op_ifge(struct vm * vm, int32_t branch);
|
||||||
|
void op_ifgt(struct vm * vm, int32_t branch);
|
||||||
|
void op_ifle(struct vm * vm, int32_t branch);
|
||||||
|
void op_iflt(struct vm * vm, int32_t branch);
|
||||||
|
void op_ifne(struct vm * vm, int32_t branch);
|
||||||
|
void op_ifnonnull(struct vm * vm, int32_t branch);
|
||||||
|
void op_ifnull(struct vm * vm, int32_t branch);
|
||||||
|
void op_iinc(struct vm * vm, uint32_t index, uint32_t _const);
|
||||||
|
void op_iload(struct vm * vm, uint32_t index);
|
||||||
|
void op_iload_0(struct vm * vm);
|
||||||
|
void op_iload_1(struct vm * vm);
|
||||||
|
void op_iload_2(struct vm * vm);
|
||||||
|
void op_iload_3(struct vm * vm);
|
||||||
|
void op_impdep1(struct vm * vm);
|
||||||
|
void op_impdep2(struct vm * vm);
|
||||||
|
void op_imul(struct vm * vm);
|
||||||
|
void op_ineg(struct vm * vm);
|
||||||
|
void op_instanceof(struct vm * vm, uint32_t index);
|
||||||
|
void op_invokedynamic(struct vm * vm, uint32_t index);
|
||||||
|
void op_invokeinterface(struct vm * vm, uint32_t index, uint32_t count);
|
||||||
|
void op_invokespecial(struct vm * vm, uint32_t index);
|
||||||
|
void op_invokestatic(struct vm * vm, uint32_t index);
|
||||||
|
void op_invokevirtual(struct vm * vm, uint32_t index);
|
||||||
|
void op_ior(struct vm * vm);
|
||||||
|
void op_irem(struct vm * vm);
|
||||||
|
void op_ireturn(struct vm * vm);
|
||||||
|
void op_ishl(struct vm * vm);
|
||||||
|
void op_ishr(struct vm * vm);
|
||||||
|
void op_istore(struct vm * vm, uint32_t index);
|
||||||
|
void op_istore_0(struct vm * vm);
|
||||||
|
void op_istore_1(struct vm * vm);
|
||||||
|
void op_istore_2(struct vm * vm);
|
||||||
|
void op_istore_3(struct vm * vm);
|
||||||
|
void op_isub(struct vm * vm);
|
||||||
|
void op_iushr(struct vm * vm);
|
||||||
|
void op_ixor(struct vm * vm);
|
||||||
|
void op_jsr(struct vm * vm, int32_t branch);
|
||||||
|
void op_jsr_w(struct vm * vm, int32_t branch);
|
||||||
|
void op_l2d(struct vm * vm);
|
||||||
|
void op_l2f(struct vm * vm);
|
||||||
|
void op_l2i(struct vm * vm);
|
||||||
|
void op_ladd(struct vm * vm);
|
||||||
|
void op_laload(struct vm * vm);
|
||||||
|
void op_land(struct vm * vm);
|
||||||
|
void op_lastore(struct vm * vm);
|
||||||
|
void op_lcmp(struct vm * vm);
|
||||||
|
void op_lconst_0(struct vm * vm);
|
||||||
|
void op_lconst_1(struct vm * vm);
|
||||||
|
void op_ldc(struct vm * vm, uint32_t index);
|
||||||
|
void op_ldc2_w(struct vm * vm, uint32_t index);
|
||||||
|
void op_ldc_w(struct vm * vm, uint32_t index);
|
||||||
|
void op_ldiv(struct vm * vm);
|
||||||
|
void op_lload(struct vm * vm, uint32_t index);
|
||||||
|
void op_lload_0(struct vm * vm);
|
||||||
|
void op_lload_1(struct vm * vm);
|
||||||
|
void op_lload_2(struct vm * vm);
|
||||||
|
void op_lload_3(struct vm * vm);
|
||||||
|
void op_lmul(struct vm * vm);
|
||||||
|
void op_lneg(struct vm * vm);
|
||||||
|
void op_lookupswitch(struct vm * vm);
|
||||||
|
void op_lor(struct vm * vm);
|
||||||
|
void op_lrem(struct vm * vm);
|
||||||
|
void op_lreturn(struct vm * vm);
|
||||||
|
void op_lshl(struct vm * vm);
|
||||||
|
void op_lshr(struct vm * vm);
|
||||||
|
void op_lstore(struct vm * vm, uint32_t index);
|
||||||
|
void op_lstore_0(struct vm * vm);
|
||||||
|
void op_lstore_1(struct vm * vm);
|
||||||
|
void op_lstore_2(struct vm * vm);
|
||||||
|
void op_lstore_3(struct vm * vm);
|
||||||
|
void op_lsub(struct vm * vm);
|
||||||
|
void op_lushr(struct vm * vm);
|
||||||
|
void op_lxor(struct vm * vm);
|
||||||
|
void op_monitorenter(struct vm * vm);
|
||||||
|
void op_monitorexit(struct vm * vm);
|
||||||
|
void op_multianewarray(struct vm * vm, uint32_t index, uint32_t dimensions);
|
||||||
|
void op_new(struct vm * vm, uint32_t index);
|
||||||
|
void op_newarray(struct vm * vm, uint32_t atype);
|
||||||
|
void op_nop(struct vm * vm);
|
||||||
|
void op_pop(struct vm * vm);
|
||||||
|
void op_pop2(struct vm * vm);
|
||||||
|
void op_putfield(struct vm * vm, uint32_t index);
|
||||||
|
void op_putstatic(struct vm * vm, uint32_t index);
|
||||||
|
void op_ret(struct vm * vm, uint32_t index);
|
||||||
|
void op_return(struct vm * vm);
|
||||||
|
void op_saload(struct vm * vm);
|
||||||
|
void op_sastore(struct vm * vm);
|
||||||
|
void op_sipush(struct vm * vm, int32_t byte);
|
||||||
|
void op_swap(struct vm * vm);
|
||||||
|
void op_tableswitch(struct vm * vm, int32_t defaultbyte, int32_t lowbyte, int32_t highbyte, const int32_t * table);
|
||||||
|
void op_wide(struct vm * vm);
|
25
c/file.c
Normal file
25
c/file.c
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
uint8_t * file_read(const char * path)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
FILE * f = fopen(path, "rb");
|
||||||
|
assert(f != nullptr);
|
||||||
|
ret = fseek(f, 0L, SEEK_END);
|
||||||
|
assert(ret != -1);
|
||||||
|
long size = ftell(f);
|
||||||
|
assert(size != -1);
|
||||||
|
ret = fseek(f, 0L, SEEK_SET);
|
||||||
|
assert(ret != -1);
|
||||||
|
|
||||||
|
uint8_t * buf = malloc(size);
|
||||||
|
size_t read = fread(buf, 1, size, f);
|
||||||
|
assert(read == size);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
5
c/file.h
Normal file
5
c/file.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
uint8_t * file_read(const char * path);
|
145
c/frame.c
Normal file
145
c/frame.c
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#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"
|
||||||
|
|
||||||
|
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];
|
||||||
|
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,
|
||||||
|
struct attribute_info * attributes)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < attributes_count; i++) {
|
||||||
|
if (attributes[i].attribute_name_index == code_name_index)
|
||||||
|
return attributes[i].code;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_code_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, "Code")) {
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm_execute(struct vm * vm)
|
||||||
|
{
|
||||||
|
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]);
|
||||||
|
|
||||||
|
int code_name_index = find_code_name_index(vm->current_thread.current_class);
|
||||||
|
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);
|
||||||
|
assert(code != nullptr);
|
||||||
|
|
||||||
|
vm->current_frame = stack_push_frame(&vm->frame_stack, 1);
|
||||||
|
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;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
printf("[ ");
|
||||||
|
for (int i = 5; i > 0; i--) {
|
||||||
|
if (i > vm->current_frame->operand_stack_ix) {
|
||||||
|
printf(" ");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int32_t value = vm->current_frame->operand_stack[vm->current_frame->operand_stack_ix - i];
|
||||||
|
if (value > 65536)
|
||||||
|
printf("0x%08x ", value);
|
||||||
|
else
|
||||||
|
printf("%10d ", value);
|
||||||
|
}
|
||||||
|
printf("]\n");
|
||||||
|
decode_print_instruction(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);
|
||||||
|
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) {
|
||||||
|
printf("terminate\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char * argv[])
|
||||||
|
{
|
||||||
|
assert(argc >= 2);
|
||||||
|
uint8_t * buf = file_read(argv[1]);
|
||||||
|
struct class_file * class_file = class_file_parse(buf);
|
||||||
|
|
||||||
|
assert(class_file->magic == 0xcafebabe);
|
||||||
|
assert(class_file->methods_count >= 1);
|
||||||
|
|
||||||
|
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.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;
|
||||||
|
|
||||||
|
vm_execute(&vm);
|
||||||
|
}
|
57
c/frame.h
Normal file
57
c/frame.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "class_file.h"
|
||||||
|
|
||||||
|
struct frame {
|
||||||
|
uint32_t * local_variable;
|
||||||
|
uint32_t * operand_stack;
|
||||||
|
uint16_t operand_stack_ix;
|
||||||
|
#ifdef DEBUG
|
||||||
|
uint32_t operand_stack_capacity;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct thread {
|
||||||
|
uint32_t pc;
|
||||||
|
struct class_file * current_class;
|
||||||
|
struct method_info * current_method;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stack {
|
||||||
|
union {
|
||||||
|
struct frame * frame;
|
||||||
|
uint32_t * data;
|
||||||
|
};
|
||||||
|
uint32_t ix;
|
||||||
|
uint32_t capacity;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vm {
|
||||||
|
struct stack frame_stack;
|
||||||
|
struct stack data_stack;
|
||||||
|
struct thread current_thread;
|
||||||
|
struct frame * current_frame;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void operand_stack_push_u32(struct frame * frame, uint32_t value)
|
||||||
|
{
|
||||||
|
frame->operand_stack[frame->operand_stack_ix] = value;
|
||||||
|
frame->operand_stack_ix++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t operand_stack_pop_u32(struct frame * frame)
|
||||||
|
{
|
||||||
|
frame->operand_stack_ix--;
|
||||||
|
uint32_t value = frame->operand_stack[frame->operand_stack_ix];
|
||||||
|
frame->operand_stack[frame->operand_stack_ix] = -1;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void operand_stack_dup_u32(struct frame * frame)
|
||||||
|
{
|
||||||
|
uint32_t value = frame->operand_stack[frame->operand_stack_ix - 1];
|
||||||
|
frame->operand_stack[frame->operand_stack_ix] = value;
|
||||||
|
frame->operand_stack_ix++;
|
||||||
|
}
|
268
c/hash_table.c
Normal file
268
c/hash_table.c
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static uint32_t fnv_1(const uint8_t * buf, int length)
|
||||||
|
{
|
||||||
|
const uint32_t fnv_offset_basis = 0x811c9dc5;
|
||||||
|
const uint32_t fnv_prime = 0x01000193;
|
||||||
|
|
||||||
|
uint32_t hash = fnv_offset_basis;
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
hash = hash * fnv_prime;
|
||||||
|
hash = hash ^ buf[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
uint32_t hash = fnv_1(key, key_length) & (hash_table_length - 1);
|
||||||
|
struct hash_table_entry * e = &entry[hash];
|
||||||
|
|
||||||
|
while (e->next != nullptr) {
|
||||||
|
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 = e->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
e->key = key;
|
||||||
|
e->key_length = key_length;
|
||||||
|
e->value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool key_equal(const uint8_t * a, const uint8_t * b, int length)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
if (a[i] != b[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
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/malloc.c
Normal file
24
c/malloc.c
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "malloc.h"
|
||||||
|
|
||||||
|
struct arena {
|
||||||
|
uint8_t * mem;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t ix;
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t class_mem[0x100000];
|
||||||
|
|
||||||
|
struct arena class_arena = {
|
||||||
|
.mem = class_mem,
|
||||||
|
.size = (sizeof (class_mem)),
|
||||||
|
.ix = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
void * malloc_class_arena(uint32_t size)
|
||||||
|
{
|
||||||
|
void * ptr = &class_arena.mem[class_arena.ix];
|
||||||
|
class_arena.ix += size;
|
||||||
|
return ptr;
|
||||||
|
}
|
3
c/malloc.h
Normal file
3
c/malloc.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void * malloc_class_arena(uint32_t size);
|
113
c/memory_allocator.c
Normal file
113
c/memory_allocator.c
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define block_power (5UL)
|
||||||
|
#define block_size (1UL << block_power)
|
||||||
|
//static uint8_t memory[0x100];
|
||||||
|
static uint8_t memory[0x100000];
|
||||||
|
#define free_list_length ((sizeof (memory)) / block_size)
|
||||||
|
static uint8_t free_list[free_list_length];
|
||||||
|
static uint32_t free_ix;
|
||||||
|
|
||||||
|
void memory_reset_free_list()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < (sizeof (free_list)); i++) {
|
||||||
|
free_list[i] = 0;
|
||||||
|
}
|
||||||
|
free_ix = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t find_contiguous_blocks(uint32_t blocks, int * zero_crossing)
|
||||||
|
{
|
||||||
|
if (free_ix + blocks > free_list_length) {
|
||||||
|
free_ix = 0; // non-contiguous
|
||||||
|
(*zero_crossing) += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < blocks; i++) {
|
||||||
|
if (free_list[free_ix + i] != 0)
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void * memory_allocate(uint32_t size)
|
||||||
|
{
|
||||||
|
assert(size != 0);
|
||||||
|
|
||||||
|
uint32_t blocks = ((size + (block_size - 1)) & ~(block_size - 1)) >> block_power;
|
||||||
|
int zero_crossings = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
uint32_t ix_offset = find_contiguous_blocks(blocks, &zero_crossings);
|
||||||
|
if (zero_crossings > 1)
|
||||||
|
return nullptr; // memory allocation failed
|
||||||
|
if (ix_offset == blocks)
|
||||||
|
break;
|
||||||
|
free_ix = (free_ix + ix_offset) & (free_list_length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < (blocks - 1); i++) {
|
||||||
|
free_list[free_ix + i] = 2;
|
||||||
|
}
|
||||||
|
free_list[free_ix + (blocks - 1)] = 1;
|
||||||
|
|
||||||
|
void * mem = &memory[free_ix << block_power];
|
||||||
|
free_ix = (free_ix + blocks) & (free_list_length - 1);
|
||||||
|
return mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_free(void * p)
|
||||||
|
{
|
||||||
|
assert(((uint8_t*)p) >= memory);
|
||||||
|
uint32_t offset = (((uint8_t*)p) - memory) >> block_power;
|
||||||
|
assert(free_list[offset] != 0);
|
||||||
|
|
||||||
|
while (free_list[offset] == 2) {
|
||||||
|
free_list[offset] = 0;
|
||||||
|
offset += 1;
|
||||||
|
}
|
||||||
|
assert(free_list[offset] == 1);
|
||||||
|
free_list[offset] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
memory_reset_free_list();
|
||||||
|
printf("%p\n", memory);
|
||||||
|
|
||||||
|
void * p1 = memory_allocate(32);
|
||||||
|
printf("p1 %p\n", p1);
|
||||||
|
void * p2 = memory_allocate(16);
|
||||||
|
printf("p2 %p\n", p2);
|
||||||
|
void * p3 = memory_allocate(256);
|
||||||
|
printf("p3 %p\n", p3);
|
||||||
|
void * p4 = memory_allocate(90);
|
||||||
|
printf("p4 %p\n", p4);
|
||||||
|
for (int i = 0; i < free_list_length; i++) { printf("%d ", free_list[i]); }
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
memory_free(p2);
|
||||||
|
memory_free(p1);
|
||||||
|
void * p5 = memory_allocate(256);
|
||||||
|
printf("%p\n", p4);
|
||||||
|
|
||||||
|
for (int i = 0; i < free_list_length; i++) { printf("%d ", free_list[i]); }
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
void * p6 = memory_allocate(128);
|
||||||
|
printf("p5 %p\n", p5);
|
||||||
|
|
||||||
|
memory_free(p4);
|
||||||
|
|
||||||
|
void * p7 = memory_allocate(128);
|
||||||
|
printf("p6 %p\n", p6);
|
||||||
|
void * p8 = memory_allocate(128);
|
||||||
|
printf("p7 %p\n", p7);
|
||||||
|
void * p9 = memory_allocate(128);
|
||||||
|
printf("p8 %p\n", p8);
|
||||||
|
}
|
||||||
|
#endif
|
5
c/memory_allocator.h
Normal file
5
c/memory_allocator.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void memory_reset_free_list();
|
||||||
|
void * memory_allocate(uint32_t size);
|
||||||
|
void memory_free(void * p);
|
16
c/print_class.c
Normal file
16
c/print_class.c
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "debug_class_file.h"
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
int main(int argc, char * argv[])
|
||||||
|
{
|
||||||
|
assert(argc >= 2);
|
||||||
|
uint8_t * buf = file_read(argv[1]);
|
||||||
|
|
||||||
|
struct class_file * class_file = class_file_parse(buf);
|
||||||
|
print_class_file(class_file);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
237
class_file.py
Normal file
237
class_file.py
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
from binascii import hexlify
|
||||||
|
|
||||||
|
def parse_row(s):
|
||||||
|
a, b, c, d = s.strip().split(',')
|
||||||
|
return int(a), c, int(d)
|
||||||
|
|
||||||
|
def parse_opcode_table(buf):
|
||||||
|
rows = [parse_row(i) for i in buf.strip().split('\n')]
|
||||||
|
return {opcode: (name, args) for opcode, name, args in rows}
|
||||||
|
|
||||||
|
with open('opcodes.csv', 'r') as f:
|
||||||
|
opcode_table = parse_opcode_table(f.read())
|
||||||
|
|
||||||
|
def size_to_format(size):
|
||||||
|
if size == 4:
|
||||||
|
return ">I"
|
||||||
|
elif size == 2:
|
||||||
|
return ">H"
|
||||||
|
elif size == 1:
|
||||||
|
return ">B"
|
||||||
|
else:
|
||||||
|
assert False, size
|
||||||
|
|
||||||
|
def field(buf, size):
|
||||||
|
format = size_to_format(size)
|
||||||
|
value, = struct.unpack(format, buf[0:size])
|
||||||
|
return buf[size:], value
|
||||||
|
|
||||||
|
constant_lookup = {
|
||||||
|
7: ("CONSTANT_Class", [("name_index", 2)]),
|
||||||
|
9: ("CONSTANT_Fieldref", [("class_index", 2), ("name_and_type_index", 2)]),
|
||||||
|
10: ("CONSTANT_Methodref", [("class_index", 2), ("name_and_type_index", 2)]),
|
||||||
|
11: ("CONSTANT_InterfaceMethodref", [("class_index", 2), ("name_and_type_index", 2)]),
|
||||||
|
8: ("CONSTANT_String", [("string_index", 2)]),
|
||||||
|
3: ("CONSTANT_Integer", [("bytes", 4)]),
|
||||||
|
4: ("CONSTANT_Float", [("bytes", 4)]),
|
||||||
|
5: ("CONSTANT_Long", [("high_bytes", 4), ("low_bytes", 4)]),
|
||||||
|
6: ("CONSTANT_Double", [("high_bytes", 4), ("low_bytes", 4)]),
|
||||||
|
12: ("CONSTANT_NameAndType", [("name_index", 2), ("descriptor_index", 2)]),
|
||||||
|
1: ("CONSTANT_Utf8", [("length", 2), ("bytes", 0)]),
|
||||||
|
15: ("CONSTANT_MethodHandle", [("reference_kind", 2), ("reference_index", 2)]),
|
||||||
|
16: ("CONSTANT_MethodType", [("descriptor_index", 2)]),
|
||||||
|
18: ("CONSTANT_InvokeDynamic", [("bootstrap_method_attr_index", 2), ("name_and_type_index", 2)]),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def parse_cp_info(buf):
|
||||||
|
buf, tag = field(buf, 1)
|
||||||
|
assert tag in constant_lookup, tag
|
||||||
|
name, fields = constant_lookup[tag]
|
||||||
|
d = {}
|
||||||
|
last_value = None
|
||||||
|
for field_name, field_size in fields:
|
||||||
|
if field_size == 0:
|
||||||
|
value = bytes(buf[0:last_value])
|
||||||
|
buf = buf[last_value:]
|
||||||
|
else:
|
||||||
|
buf, value = field(buf, field_size)
|
||||||
|
last_value = value
|
||||||
|
d[field_name] = value
|
||||||
|
return buf, (name, d)
|
||||||
|
|
||||||
|
def parse_attribute_info(buf):
|
||||||
|
buf, attribute_name_index = field(buf, 2)
|
||||||
|
buf, attribute_length = field(buf, 4)
|
||||||
|
info = bytes(buf[0:attribute_length])
|
||||||
|
buf = buf[attribute_length:]
|
||||||
|
return buf, (attribute_name_index, attribute_length, info)
|
||||||
|
|
||||||
|
def parse_field_info(buf):
|
||||||
|
buf, access_flags = field(buf, 2)
|
||||||
|
buf, name_index = field(buf, 2)
|
||||||
|
buf, descriptor_index = field(buf, 2)
|
||||||
|
buf, attributes_count = field(buf, 2)
|
||||||
|
attributes = []
|
||||||
|
for i in range(attributes_count):
|
||||||
|
buf, attribute = parse_attribute_info(buf)
|
||||||
|
attributes.append(attribute)
|
||||||
|
return buf, (access_flags, name_index, descriptor_index, attributes_count, attributes)
|
||||||
|
|
||||||
|
def parse_exception_table(buf, indent):
|
||||||
|
exception_table = [
|
||||||
|
("start_pc", 2),
|
||||||
|
("end_pc", 2),
|
||||||
|
("handler_pc", 2),
|
||||||
|
("catch_type", 2),
|
||||||
|
]
|
||||||
|
for name, size in exception_table:
|
||||||
|
buf, value = field(buf, size)
|
||||||
|
print(indent, name, value)
|
||||||
|
return buf
|
||||||
|
|
||||||
|
def dump_opcodes(buf, indent):
|
||||||
|
ix = 0
|
||||||
|
while len(buf) > 0:
|
||||||
|
op = buf[0]
|
||||||
|
name, arg_length = opcode_table[op]
|
||||||
|
buf = buf[1:]
|
||||||
|
args = list(buf[0:arg_length])
|
||||||
|
print(indent, f"{ix:> 3}", name, args)
|
||||||
|
ix += 1 + arg_length
|
||||||
|
buf = buf[arg_length:]
|
||||||
|
|
||||||
|
def print_code_info(buf, indent, constant_pool):
|
||||||
|
code_attribute = [
|
||||||
|
("max_stack", 2),
|
||||||
|
("max_locals", 2),
|
||||||
|
("code_length", 4),
|
||||||
|
("code", 0),
|
||||||
|
("exception_table_length", 2),
|
||||||
|
("exception_table", 0),
|
||||||
|
("attributes_count", 2),
|
||||||
|
("attributes", 0)
|
||||||
|
]
|
||||||
|
last_value = None
|
||||||
|
for name, size in code_attribute:
|
||||||
|
if name == "code":
|
||||||
|
print(indent, "code:")
|
||||||
|
size = last_value
|
||||||
|
dump_opcodes(buf[0:last_value], indent + " ")
|
||||||
|
buf = buf[last_value:]
|
||||||
|
elif name == "exception_table":
|
||||||
|
print(indent, "exception_table:")
|
||||||
|
for i in range(last_value):
|
||||||
|
buf = parse_exception_table(buf, indent + " ")
|
||||||
|
elif name == "attributes":
|
||||||
|
print(indent, "attributes:")
|
||||||
|
for i in range(last_value):
|
||||||
|
buf, attribute_info = parse_attribute_info(buf)
|
||||||
|
print(indent + " ", f"attribute {i}:")
|
||||||
|
attribute_name_index, attribute_length, info = attribute_info
|
||||||
|
|
||||||
|
constant_type, constant = constant_pool[attribute_name_index - 1]
|
||||||
|
assert constant_type == 'CONSTANT_Utf8', constant_type
|
||||||
|
attribute_name_bytes = constant["bytes"]
|
||||||
|
|
||||||
|
print(indent + " ", "attribute_name_index", attribute_name_index, attribute_name_bytes)
|
||||||
|
print(indent + " ", "attribute_length", attribute_length)
|
||||||
|
print(indent + " ", "info", info)
|
||||||
|
else:
|
||||||
|
buf, value = field(buf, size)
|
||||||
|
last_value = value
|
||||||
|
print(indent, name, value)
|
||||||
|
assert len(buf) == 0
|
||||||
|
|
||||||
|
def parse_class(buf):
|
||||||
|
buf, magic = field(buf, 4)
|
||||||
|
buf, minor_version = field(buf, 2)
|
||||||
|
buf, major_version = field(buf, 2)
|
||||||
|
buf, constant_pool_count = field(buf, 2)
|
||||||
|
|
||||||
|
print("magic", hex(magic))
|
||||||
|
print("minor_version", minor_version)
|
||||||
|
print("major_version", major_version)
|
||||||
|
print("constant_pool_count", constant_pool_count)
|
||||||
|
|
||||||
|
print("constant_pool:")
|
||||||
|
constant_pool = []
|
||||||
|
for i in range(constant_pool_count - 1):
|
||||||
|
buf, cp_info = parse_cp_info(buf)
|
||||||
|
constant_pool.append(cp_info)
|
||||||
|
print(i + 1, cp_info)
|
||||||
|
|
||||||
|
buf, access_flags = field(buf, 2)
|
||||||
|
buf, this_class = field(buf, 2)
|
||||||
|
buf, super_class = field(buf, 2)
|
||||||
|
buf, interfaces_count = field(buf, 2)
|
||||||
|
|
||||||
|
print("access_flags", hex(access_flags))
|
||||||
|
print("this_class", this_class)
|
||||||
|
print("super_class", super_class)
|
||||||
|
print("interfaces_count", interfaces_count)
|
||||||
|
|
||||||
|
print("interfaces:")
|
||||||
|
for i in range(interfaces_count):
|
||||||
|
buf, interface = field(buf, 2)
|
||||||
|
print(i, interface)
|
||||||
|
|
||||||
|
buf, fields_count = field(buf, 2)
|
||||||
|
print("fields_count", fields_count)
|
||||||
|
for i in range(fields_count):
|
||||||
|
buf, field_info = parse_field_info(buf)
|
||||||
|
print(i, field_info)
|
||||||
|
|
||||||
|
buf, methods_count = field(buf, 2)
|
||||||
|
print("methods_count", methods_count)
|
||||||
|
print("methods:")
|
||||||
|
for i in range(methods_count):
|
||||||
|
buf, method_info = parse_field_info(buf)
|
||||||
|
print(f" method {i}:")
|
||||||
|
access_flags, name_index, descriptor_index, attributes_count, attributes = method_info
|
||||||
|
print(" access_flags", hex(access_flags))
|
||||||
|
|
||||||
|
constant_type, constant = constant_pool[name_index - 1]
|
||||||
|
assert constant_type == 'CONSTANT_Utf8', constant_type
|
||||||
|
name_bytes = constant["bytes"]
|
||||||
|
|
||||||
|
print(" name_index", name_index, name_bytes)
|
||||||
|
|
||||||
|
constant_type, constant = constant_pool[descriptor_index - 1]
|
||||||
|
assert constant_type == 'CONSTANT_Utf8', constant_type
|
||||||
|
descriptor_bytes = constant["bytes"]
|
||||||
|
|
||||||
|
print(" descriptor_index", descriptor_index, descriptor_bytes)
|
||||||
|
print(" attributes_count", attributes_count)
|
||||||
|
print(" attributes:")
|
||||||
|
for j in range(attributes_count):
|
||||||
|
print(f" attribute {j}:")
|
||||||
|
attribute_name_index, attribute_length, info = attributes[j]
|
||||||
|
|
||||||
|
constant_type, constant = constant_pool[attribute_name_index - 1]
|
||||||
|
assert constant_type == 'CONSTANT_Utf8', constant_type
|
||||||
|
attribute_name_bytes = constant["bytes"]
|
||||||
|
|
||||||
|
print(" attribute_name_index", attribute_name_index, attribute_name_bytes)
|
||||||
|
print(" attribute_length", attribute_length)
|
||||||
|
#print(" info", info)
|
||||||
|
if attribute_name_bytes == b'Code':
|
||||||
|
print_code_info(info, " ", constant_pool)
|
||||||
|
|
||||||
|
buf, attributes_count = field(buf, 2)
|
||||||
|
print("attributes_count", attributes_count)
|
||||||
|
print("attributes:")
|
||||||
|
for i in range(attributes_count):
|
||||||
|
buf, attribute_info = parse_attribute_info(buf)
|
||||||
|
print(i, attribute_info)
|
||||||
|
|
||||||
|
assert len(buf) == 0, bytes(buf)
|
||||||
|
|
||||||
|
filename = sys.argv[1]
|
||||||
|
|
||||||
|
with open(filename, 'rb') as f:
|
||||||
|
buf = f.read()
|
||||||
|
|
||||||
|
parse_class(memoryview(buf))
|
245
gen_decoder.py
Normal file
245
gen_decoder.py
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
import io
|
||||||
|
import sys
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import csv
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Argument:
|
||||||
|
signed: bool
|
||||||
|
size: int
|
||||||
|
name: str
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Instruction:
|
||||||
|
code: int
|
||||||
|
mnemonic: str
|
||||||
|
arguments_size: int
|
||||||
|
arguments: list[Argument]
|
||||||
|
|
||||||
|
def parse_arguments(types, arguments):
|
||||||
|
types = types.strip()
|
||||||
|
arguments = arguments.strip()
|
||||||
|
if not types:
|
||||||
|
assert not arguments
|
||||||
|
return 0, []
|
||||||
|
|
||||||
|
types = types.split(',')
|
||||||
|
arguments = arguments.split(',')
|
||||||
|
assert len(types) == len(arguments), (types, arguments)
|
||||||
|
|
||||||
|
total_size = 0
|
||||||
|
l = list(zip(types, arguments))
|
||||||
|
args = []
|
||||||
|
for i, (type, name) in enumerate(l):
|
||||||
|
signed = None
|
||||||
|
if type.startswith('v'):
|
||||||
|
assert i == (len(l) - 1), l
|
||||||
|
assert name == '_', name
|
||||||
|
total_size += int(type.removeprefix('v'))
|
||||||
|
continue
|
||||||
|
elif type.startswith('s'):
|
||||||
|
size = int(type.removeprefix('s'))
|
||||||
|
signed = True
|
||||||
|
elif type.startswith('u'):
|
||||||
|
size = int(type.removeprefix('u'))
|
||||||
|
signed = False
|
||||||
|
else:
|
||||||
|
assert False, (type, argument)
|
||||||
|
|
||||||
|
assert size in {1, 2, 4}
|
||||||
|
assert signed is not None
|
||||||
|
if name in {"byte", "branch"}:
|
||||||
|
assert signed == True, name
|
||||||
|
else:
|
||||||
|
assert signed == False, name
|
||||||
|
|
||||||
|
total_size += size
|
||||||
|
args.append(Argument(
|
||||||
|
signed=signed,
|
||||||
|
size=size,
|
||||||
|
name=name,
|
||||||
|
))
|
||||||
|
|
||||||
|
return total_size, args
|
||||||
|
|
||||||
|
def parse_row(row):
|
||||||
|
code, _, mnemonic, types, arguments = row
|
||||||
|
|
||||||
|
if types.strip() == '-1':
|
||||||
|
arguments_size = -1;
|
||||||
|
arguments = arguments.split(',')
|
||||||
|
else:
|
||||||
|
arguments_size, arguments = parse_arguments(types, arguments)
|
||||||
|
|
||||||
|
yield Instruction(
|
||||||
|
code=int(code),
|
||||||
|
mnemonic=mnemonic,
|
||||||
|
arguments_size=arguments_size,
|
||||||
|
arguments=arguments
|
||||||
|
)
|
||||||
|
|
||||||
|
def parse_opcode_table():
|
||||||
|
with open('opcodes.csv', 'r') as f:
|
||||||
|
reader = csv.reader(f, delimiter=",", quotechar='"')
|
||||||
|
instructions = [instruction for row in reader for instruction in parse_row(row)]
|
||||||
|
return instructions
|
||||||
|
|
||||||
|
opcode_table = list(sorted(parse_opcode_table(), key=lambda i: i.code))
|
||||||
|
|
||||||
|
sign_type_table = {
|
||||||
|
(True, 4): "_s4",
|
||||||
|
(True, 2): "_s2",
|
||||||
|
(True, 1): "_s1",
|
||||||
|
(False, 4): "_u4",
|
||||||
|
(False, 2): "_u2",
|
||||||
|
(False, 1): "_u1",
|
||||||
|
}
|
||||||
|
|
||||||
|
def generate_print_fixed_width_instruction(instruction):
|
||||||
|
offset = 1
|
||||||
|
for argument in instruction.arguments:
|
||||||
|
c_type = "int32_t" if argument.signed else "uint32_t"
|
||||||
|
conversion = sign_type_table[(argument.signed, argument.size)]
|
||||||
|
yield f"{c_type} {argument.name} = {conversion}(&code[pc + {offset}]);"
|
||||||
|
offset += argument.size
|
||||||
|
|
||||||
|
argument_format = ", ".join(
|
||||||
|
f"%{'d' if argument.signed else 'u'}"
|
||||||
|
for argument in instruction.arguments
|
||||||
|
)
|
||||||
|
argument_values = ", ".join(
|
||||||
|
argument.name
|
||||||
|
for argument in instruction.arguments
|
||||||
|
)
|
||||||
|
if argument_values:
|
||||||
|
argument_values = ", " + argument_values
|
||||||
|
mnemonic = instruction.mnemonic.ljust(13)
|
||||||
|
yield f'printf("%4d: {mnemonic} {argument_format}\\n", pc{argument_values});'
|
||||||
|
yield f"return pc + {1 + instruction.arguments_size};"
|
||||||
|
|
||||||
|
def generate_print_variable_width_instruction(instruction):
|
||||||
|
mnemonic = instruction.mnemonic.ljust(13)
|
||||||
|
yield f"{instruction.mnemonic.upper()}_ARGS;"
|
||||||
|
yield f'printf("%4d: {mnemonic} {{\\n", pc);'
|
||||||
|
yield f"{instruction.mnemonic.upper()}_PRINT_ARGS();"
|
||||||
|
yield 'printf("}\\n");'
|
||||||
|
yield f"return {instruction.mnemonic.upper()}_NEXT_PC;"
|
||||||
|
|
||||||
|
def generate_print_decoder():
|
||||||
|
yield "uint32_t decode_print_instruction(const uint8_t * code, uint32_t pc)"
|
||||||
|
yield "{"
|
||||||
|
yield "switch (code[pc]) {"
|
||||||
|
for instruction in opcode_table:
|
||||||
|
yield f"case {instruction.code}: // {instruction.mnemonic}"
|
||||||
|
yield "{"
|
||||||
|
|
||||||
|
if instruction.arguments_size == -1:
|
||||||
|
yield from generate_print_variable_width_instruction(instruction)
|
||||||
|
else:
|
||||||
|
yield from generate_print_fixed_width_instruction(instruction)
|
||||||
|
|
||||||
|
yield "}"
|
||||||
|
|
||||||
|
yield "default:"
|
||||||
|
yield "{"
|
||||||
|
yield "assert(false);"
|
||||||
|
yield "return pc;"
|
||||||
|
yield "}"
|
||||||
|
yield "}"
|
||||||
|
yield "}"
|
||||||
|
|
||||||
|
def generate_execute_fixed_width_instruction(instruction):
|
||||||
|
offset = 1
|
||||||
|
for argument in instruction.arguments:
|
||||||
|
c_type = "int32_t" if argument.signed else "uint32_t"
|
||||||
|
conversion = sign_type_table[(argument.signed, argument.size)]
|
||||||
|
yield f"{c_type} {argument.name} = {conversion}(&code[pc + {offset}]);"
|
||||||
|
offset += argument.size
|
||||||
|
argument_values = ", ".join(
|
||||||
|
argument.name
|
||||||
|
for argument in instruction.arguments
|
||||||
|
)
|
||||||
|
if argument_values:
|
||||||
|
argument_values = ", " + argument_values
|
||||||
|
yield f"op_{instruction.mnemonic}(vm{argument_values});"
|
||||||
|
yield f"return pc + {1 + instruction.arguments_size};"
|
||||||
|
|
||||||
|
def generate_execute_variable_width_instruction(instruction):
|
||||||
|
yield f"{instruction.mnemonic.upper()}_ARGS;"
|
||||||
|
argument_values = ", ".join(
|
||||||
|
argument
|
||||||
|
for argument in instruction.arguments
|
||||||
|
)
|
||||||
|
if argument_values:
|
||||||
|
argument_values = ", " + argument_values
|
||||||
|
yield f"op_{instruction.mnemonic}(vm{argument_values});"
|
||||||
|
yield f"return {instruction.mnemonic.upper()}_NEXT_PC;"
|
||||||
|
|
||||||
|
def generate_execute_decoder():
|
||||||
|
yield "uint32_t decode_execute_instruction(struct vm * vm, const uint8_t * code, uint32_t pc)"
|
||||||
|
yield "{"
|
||||||
|
yield "switch (code[pc]) {"
|
||||||
|
for instruction in opcode_table:
|
||||||
|
yield f"case {instruction.code}: // {instruction.mnemonic}"
|
||||||
|
yield "{"
|
||||||
|
|
||||||
|
if instruction.arguments_size == -1:
|
||||||
|
yield from generate_execute_variable_width_instruction(instruction)
|
||||||
|
else:
|
||||||
|
yield from generate_execute_fixed_width_instruction(instruction)
|
||||||
|
|
||||||
|
yield "}"
|
||||||
|
yield "default:"
|
||||||
|
yield "{"
|
||||||
|
yield "assert(false);"
|
||||||
|
yield "return pc;"
|
||||||
|
yield "}"
|
||||||
|
yield "}"
|
||||||
|
yield "}"
|
||||||
|
|
||||||
|
def should_autonewline(line):
|
||||||
|
return (
|
||||||
|
"static_assert" not in line
|
||||||
|
and "extern" not in line
|
||||||
|
and (len(line.split()) < 2 or line.split()[1] != '=') # hacky; meh
|
||||||
|
)
|
||||||
|
|
||||||
|
def _render(out, lines):
|
||||||
|
indent = " "
|
||||||
|
level = 0
|
||||||
|
namespace = 0
|
||||||
|
for l in lines:
|
||||||
|
if l and (l[0] == "}" or l[0] == ")"):
|
||||||
|
level -= 2
|
||||||
|
if level < 0:
|
||||||
|
assert namespace >= 0
|
||||||
|
namespace -= 1
|
||||||
|
level = 0
|
||||||
|
|
||||||
|
if len(l) == 0:
|
||||||
|
out.write("\n")
|
||||||
|
else:
|
||||||
|
out.write(indent * level + l + "\n")
|
||||||
|
|
||||||
|
if l and (l[-1] == "{" or l[-1] == "("):
|
||||||
|
if l.startswith("namespace"):
|
||||||
|
namespace += 1
|
||||||
|
else:
|
||||||
|
level += 2
|
||||||
|
|
||||||
|
if level == 0 and l and l[-1] == ";":
|
||||||
|
if should_autonewline(l):
|
||||||
|
out.write("\n")
|
||||||
|
return out
|
||||||
|
|
||||||
|
def renderer():
|
||||||
|
out = io.StringIO()
|
||||||
|
def render(lines):
|
||||||
|
return _render(out, lines)
|
||||||
|
return render, out
|
||||||
|
|
||||||
|
render, out = renderer()
|
||||||
|
render(generate_print_decoder())
|
||||||
|
render(generate_execute_decoder())
|
||||||
|
sys.stdout.write(out.getvalue())
|
BIN
opcodes.ods
Normal file
BIN
opcodes.ods
Normal file
Binary file not shown.
63
p/AdventDay1.java
Normal file
63
p/AdventDay1.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package p;
|
||||||
|
|
||||||
|
class AdventDay1 {
|
||||||
|
public static int part1() {
|
||||||
|
int[] input = {3, 4,
|
||||||
|
4, 3,
|
||||||
|
2, 5,
|
||||||
|
1, 3,
|
||||||
|
3, 9,
|
||||||
|
3, 3};
|
||||||
|
|
||||||
|
int[] left = new int[input.length / 2];
|
||||||
|
int[] right = new int[input.length / 2];
|
||||||
|
|
||||||
|
for (int i = 0; i < input.length / 2; i++) {
|
||||||
|
left[i] = input[i * 2];
|
||||||
|
right[i] = input[i * 2 + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// bubble sort left
|
||||||
|
while (true) {
|
||||||
|
boolean swapped = false;
|
||||||
|
for (int i = 1; i < left.length; i++) {
|
||||||
|
if (left[i - 1] > left[i]) {
|
||||||
|
int tmp = left[i - 1];
|
||||||
|
left[i - 1] = left[i];
|
||||||
|
left[i] = tmp;
|
||||||
|
swapped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!swapped)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bubble sort right
|
||||||
|
while (true) {
|
||||||
|
boolean swapped = false;
|
||||||
|
for (int i = 1; i < right.length; i++) {
|
||||||
|
if (right[i - 1] > right[i]) {
|
||||||
|
int tmp = right[i - 1];
|
||||||
|
right[i - 1] = right[i];
|
||||||
|
right[i] = tmp;
|
||||||
|
swapped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!swapped)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sum = 0;
|
||||||
|
for (int i = 0; i < left.length; i++) {
|
||||||
|
int dist = left[i] - right[i];
|
||||||
|
if (dist < 0)
|
||||||
|
dist = -dist;
|
||||||
|
sum += dist;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println(part1());
|
||||||
|
}
|
||||||
|
}
|
84
p/AdventDay1_String.java
Normal file
84
p/AdventDay1_String.java
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package p;
|
||||||
|
|
||||||
|
class AdventOfCode2024Day1 {
|
||||||
|
public static int part1() {
|
||||||
|
char[] string_input = {
|
||||||
|
'3', ' ', ' ', ' ', '4', '\n',
|
||||||
|
'4', ' ', ' ', ' ', '3', '\n',
|
||||||
|
'2', ' ', ' ', ' ', '5', '\n',
|
||||||
|
'1', ' ', ' ', ' ', '3', '\n',
|
||||||
|
'3', ' ', ' ', ' ', '9', '\n',
|
||||||
|
'3', ' ', ' ', ' ', '3', '\n',
|
||||||
|
};
|
||||||
|
|
||||||
|
int[] input = new int[100];
|
||||||
|
int nums = 0;
|
||||||
|
for (int i = 0; i < string_input.length; i++) {
|
||||||
|
switch (string_input[i]) {
|
||||||
|
case '0': input[nums++] = 0; break;
|
||||||
|
case '1': input[nums++] = 1; break;
|
||||||
|
case '2': input[nums++] = 2; break;
|
||||||
|
case '3': input[nums++] = 3; break;
|
||||||
|
case '4': input[nums++] = 4; break;
|
||||||
|
case '5': input[nums++] = 5; break;
|
||||||
|
case '6': input[nums++] = 6; break;
|
||||||
|
case '7': input[nums++] = 7; break;
|
||||||
|
case '8': input[nums++] = 8; break;
|
||||||
|
case '9': input[nums++] = 9; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int[] left = new int[nums / 2];
|
||||||
|
int[] right = new int[nums / 2];
|
||||||
|
|
||||||
|
for (int i = 0; i < nums / 2; i++) {
|
||||||
|
left[i] = input[i * 2];
|
||||||
|
right[i] = input[i * 2 + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// bubble sort left
|
||||||
|
while (true) {
|
||||||
|
boolean swapped = false;
|
||||||
|
for (int i = 1; i < left.length; i++) {
|
||||||
|
if (left[i - 1] > left[i]) {
|
||||||
|
int tmp = left[i - 1];
|
||||||
|
left[i - 1] = left[i];
|
||||||
|
left[i] = tmp;
|
||||||
|
swapped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!swapped)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bubble sort right
|
||||||
|
while (true) {
|
||||||
|
boolean swapped = false;
|
||||||
|
for (int i = 1; i < right.length; i++) {
|
||||||
|
if (right[i - 1] > right[i]) {
|
||||||
|
int tmp = right[i - 1];
|
||||||
|
right[i - 1] = right[i];
|
||||||
|
right[i] = tmp;
|
||||||
|
swapped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!swapped)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sum = 0;
|
||||||
|
for (int i = 0; i < left.length; i++) {
|
||||||
|
int dist = left[i] - right[i];
|
||||||
|
if (dist < 0)
|
||||||
|
dist = -dist;
|
||||||
|
sum += dist;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println(part1());
|
||||||
|
}
|
||||||
|
}
|
5
p/Data.java
Normal file
5
p/Data.java
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package p;
|
||||||
|
|
||||||
|
class Data {
|
||||||
|
static int[] num = {10, 20, 30, 40};
|
||||||
|
}
|
9
p/Java23.java
Normal file
9
p/Java23.java
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package p;
|
||||||
|
|
||||||
|
class Java23 {
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
java.io.IO.println("Hello world!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
p/Main.java
Normal file
29
p/Main.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package p;
|
||||||
|
|
||||||
|
class Main {
|
||||||
|
public static int test2() {
|
||||||
|
int[] num = {10, 20, 30, 40};
|
||||||
|
int sum = 0;
|
||||||
|
for (int i = 0; i < num.length; i++) {
|
||||||
|
sum += num[i];
|
||||||
|
}
|
||||||
|
return sum + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int test1() {
|
||||||
|
int a = 6;
|
||||||
|
int b = 7;
|
||||||
|
int c = a * b; // 42
|
||||||
|
int d = c << 4; // 672
|
||||||
|
int e = d + 11; // 683
|
||||||
|
int f = e - 3; // 680
|
||||||
|
int g = f / 3; // 226
|
||||||
|
int h = g ^ 0x0fffffff; // 268435229
|
||||||
|
char i = (char)h; // 65309
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println(test2());
|
||||||
|
}
|
||||||
|
}
|
9
p/Nest.java
Normal file
9
p/Nest.java
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
public class Nest {
|
||||||
|
private String secret = "Hidden message";
|
||||||
|
public class InnerClass {
|
||||||
|
public void revealSecret() {
|
||||||
|
// Direct access to private member of OuterClass
|
||||||
|
System.out.println(secret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
p/Rem.java
Normal file
7
p/Rem.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package p;
|
||||||
|
|
||||||
|
class Rem {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println(-42 % 9);
|
||||||
|
}
|
||||||
|
}
|
36
p/Test.java
Normal file
36
p/Test.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package p;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class Test {
|
||||||
|
public int foo(int a) {
|
||||||
|
return a * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String test = "asdf";
|
||||||
|
public final int eggs = 1234;
|
||||||
|
public final float bacon = 12.34f;
|
||||||
|
|
||||||
|
public int bar = foo(eggs);
|
||||||
|
|
||||||
|
public static float mul(float a, float b) {
|
||||||
|
return a * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int comp(int a, int b) {
|
||||||
|
if (a < b) {
|
||||||
|
return a * 2;
|
||||||
|
} else {
|
||||||
|
return a * 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
ArrayList<Integer> numbers = new ArrayList<Integer>();
|
||||||
|
numbers.add(5);
|
||||||
|
numbers.add(9);
|
||||||
|
numbers.add(8);
|
||||||
|
numbers.add(1);
|
||||||
|
numbers.forEach( (n) -> { System.out.println(n); } );
|
||||||
|
}
|
||||||
|
}
|
9
p/Test2.java
Normal file
9
p/Test2.java
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package p;
|
||||||
|
|
||||||
|
import p.Test;
|
||||||
|
|
||||||
|
class Test2 {
|
||||||
|
public static float fmul(float a, float b, float c) {
|
||||||
|
return Test.mul(a, b) + c;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user