This commit is contained in:
Zack Buhman 2024-12-23 14:41:20 -06:00
commit 39f53e8ee4
37 changed files with 6044 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.~*
*.o
*.d
*.gch
*.csv
*.class
main
print_class

51
Makefile Normal file
View 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.%

BIN
c/a.out Executable file

Binary file not shown.

7
c/bswap.h Normal file
View 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
View 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
View 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
View 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
View File

278
c/debug_class_file.c Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

1217
c/execute.c Normal file

File diff suppressed because it is too large Load Diff

211
c/execute.h Normal file
View 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
View 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
View File

@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
uint8_t * file_read(const char * path);

145
c/frame.c Normal file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
#pragma once
void * malloc_class_arena(uint32_t size);

113
c/memory_allocator.c Normal file
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

63
p/AdventDay1.java Normal file
View 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
View 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
View File

@ -0,0 +1,5 @@
package p;
class Data {
static int[] num = {10, 20, 30, 40};
}

9
p/Java23.java Normal file
View File

@ -0,0 +1,9 @@
package p;
class Java23 {
void main() {
java.io.IO.println("Hello world!");
}
}

29
p/Main.java Normal file
View 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
View 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
View 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
View 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
View 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;
}
}