native_java_io_printstream_write

This commit is contained in:
Zack Buhman 2024-12-26 05:16:55 -06:00
parent a476201cc1
commit 08f1fe0aa6
25 changed files with 300 additions and 110 deletions

1
.gitignore vendored
View File

@ -5,6 +5,7 @@
*.csv
*.class
*.out
*.elf
main
print_class
__pycache__

View File

@ -5,7 +5,9 @@ include java.mk
CC ?= gcc
ARCH = -m32
CFLAGS ?= -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -fstack-protector -std=c2x -DDEBUG -g
CFLAGS += -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -fstack-protector -std=c2x -g
CFLAGS += -DDEBUG
#CFLAGS += -DDEBUG_PRINT
OPT ?= -O0
DEPFLAGS = -MMD -MP

View File

@ -4,7 +4,9 @@ OPT = -O0
MAKEFILE_PATH := $(patsubst %/,%,$(dir $(abspath $(firstword $(MAKEFILE_LIST)))))
LIB ?= $(MAKEFILE_PATH)/dreamcast
CFLAGS += -D__dreamcast__ -DDEBUG
CFLAGS += -D__dreamcast__
CFLAGS += -DDEBUG
#CFLAGS += -DDEBUG_PRINT
CFLAGS += -I$(MAKEFILE_PATH)
CFLAGS += -I$(MAKEFILE_PATH)/dreamcast/
CARCH = -m4-single -ml
@ -45,7 +47,11 @@ LIBGCC_OBJ = \
libgcc/_div_table.o
CLASS_FILES = \
p/Multiply.class.o
p/Native.class.o \
java/lang/String.class.o \
java/lang/System.class.o \
java/io/PrintStream.class.o \
java/lang/Object.class.o
main.elf: LDSCRIPT = $(LIB)/main.lds
main.elf: $(START_OBJ) $(OBJ) $(MAIN_OBJ) $(MAIN_DREAMCAST_OBJ) $(LIBGCC_OBJ) $(CLASS_FILES)

View File

@ -147,9 +147,7 @@ static void class_resolver_allocate_attribute_entry(struct class_entry * class_e
class_entry->attribute_entry = attribute_entry;
}
struct hash_table_entry * class_resolver_load_from_buffers(const uint8_t ** class_names,
const int * class_names_length,
const uint8_t ** buffers,
struct hash_table_entry * class_resolver_load_from_buffers(const uint8_t ** buffers,
int length,
int * hash_table_length)
{
@ -167,10 +165,15 @@ struct hash_table_entry * class_resolver_load_from_buffers(const uint8_t ** clas
class_entry[i].class_file = class_file;
class_entry[i].initialization_state = CLASS_UNINITIALIZED;
struct constant * class_constant = &class_file->constant_pool[class_file->this_class - 1];
assert(class_constant->tag == CONSTANT_Class);
struct constant * class_name_constant = &class_file->constant_pool[class_constant->class.name_index - 1];
assert(class_name_constant->tag == CONSTANT_Utf8);
hash_table_add(class_hash_table_length,
class_hash_table,
class_names[i],
class_names_length[i],
class_name_constant->utf8.bytes,
class_name_constant->utf8.length,
&class_entry[i]);
// make hash table for interfaces

View File

@ -48,9 +48,7 @@ struct class_entry {
} methods;
};
struct hash_table_entry * class_resolver_load_from_buffers(const uint8_t ** class_names,
const int * class_names_length,
const uint8_t ** buffers,
struct hash_table_entry * class_resolver_load_from_buffers(const uint8_t ** buffers,
int length,
int * hash_table_length);
struct class_entry * class_resolver_lookup_class(int class_hash_table_length,

View File

@ -94,4 +94,7 @@ static inline int32_t aligned_s4(const void * buf)
#define WIDE_NEXT_PC 0
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
#include "decode.inc.c"
#pragma GCC diagnostic pop

118
c/frame.c
View File

@ -9,6 +9,7 @@
#include "class_resolver.h"
#include "printf.h"
#include "string.h"
#include "native.h"
struct Code_attribute * get_code_attribute(int code_name_index,
int attributes_count,
@ -194,18 +195,49 @@ bool vm_initialize_class(struct vm * vm, struct class_entry * class_entry)
return true;
}
void vm_special_method_call(struct vm * vm, struct class_entry * class_entry, struct method_info * method_info)
void vm_native_method_call(struct vm * vm, struct class_entry * class_entry, struct method_info * method_info, int nargs, uint8_t return_type)
{
/* If the method is not native, the nargs argument values are popped from the
operand stack. A new frame is created on the Java Virtual Machine stack for
the method being invoked. The nargs argument values are consecutively made
the values of local variables of the new frame, with arg1 in local variable
0 (or, if arg1 is of type long or double, in local variables 0 and 1) and
so on. The new frame is then made current, and the Java Virtual Machine pc
is set to the opcode of the first instruction of the method to be
invoked. Execution continues with the first instruction of the method.
*/
debugf("vm_static_native_method_call: nargs %d\n", nargs);
uint32_t args[nargs];
for (int i = 0; i < nargs; i++) {
uint32_t value = operand_stack_pop_u32(vm->current_frame);
debugf("args[%d] = %x\n", nargs - i - 1, value);
args[nargs - i - 1] = value;
}
debugf("native:\n ");
struct constant * class_constant = &class_entry->class_file->constant_pool[class_entry->class_file->this_class - 1];
struct constant * class_name_constant = &class_entry->class_file->constant_pool[class_constant->class.name_index - 1];
print_constant(class_name_constant);
debugf(" ");
struct constant * method_name_constant = &class_entry->class_file->constant_pool[method_info->name_index - 1];
print_constant(method_name_constant);
int java_io_printstream_length = 19;
bool java_io_printstream =
class_name_constant->utf8.length == java_io_printstream_length &&
hash_table_key_equal(class_name_constant->utf8.bytes, (const uint8_t *)"java/io/PrintStream", class_name_constant->utf8.length);
if (java_io_printstream) {
int write_length = 5;
bool write =
method_name_constant->utf8.length == write_length &&
hash_table_key_equal(method_name_constant->utf8.bytes, (const uint8_t *)"write", method_name_constant->utf8.length);
if (write) {
if (nargs == 1) {
native_java_io_printstream_write_1(args);
return;
} else if (nargs == 2) {
native_java_io_printstream_write_2(args);
return;
}
}
}
assert(false);
}
void vm_method_call(struct vm * vm, struct class_entry * class_entry, struct method_info * method_info, int nargs, uint8_t return_type)
{
int code_name_index = find_code_name_index(class_entry->class_file);
assert(code_name_index > 0);
@ -222,11 +254,8 @@ void vm_special_method_call(struct vm * vm, struct class_entry * class_entry, st
vm->current_frame->operand_stack = stack_push_data(&vm->data_stack, code->max_stack);
vm->current_frame->operand_stack_ix = 0;
vm->current_frame->initialization_frame = 0;
vm->current_frame->return_type = return_type;
struct constant * descriptor_constant = &class_entry->class_file->constant_pool[method_info->descriptor_index - 1];
int nargs = descriptor_nargs(descriptor_constant, &vm->current_frame->return_type);
nargs += 1;
debugf("nargs+1: %d\n", nargs);
for (int i = 0; i < nargs; i++) {
uint32_t value = operand_stack_pop_u32(old_frame);
debugf("local[%d] = %x\n", nargs - i - 1, value);
@ -240,6 +269,31 @@ void vm_special_method_call(struct vm * vm, struct class_entry * class_entry, st
debugf("operand_stack_ix: %d\n", vm->current_frame->operand_stack_ix);
}
void vm_special_method_call(struct vm * vm, struct class_entry * class_entry, struct method_info * method_info)
{
/* If the method is not native, the nargs argument values are popped from the
operand stack. A new frame is created on the Java Virtual Machine stack for
the method being invoked. The nargs argument values are consecutively made
the values of local variables of the new frame, with arg1 in local variable
0 (or, if arg1 is of type long or double, in local variables 0 and 1) and
so on. The new frame is then made current, and the Java Virtual Machine pc
is set to the opcode of the first instruction of the method to be
invoked. Execution continues with the first instruction of the method.
*/
uint8_t return_type;
struct constant * descriptor_constant = &class_entry->class_file->constant_pool[method_info->descriptor_index - 1];
int nargs = descriptor_nargs(descriptor_constant, &return_type);
nargs += 1;
debugf("nargs+1: %d\n", nargs);
if (method_info->access_flags & METHOD_ACC_NATIVE) {
vm_native_method_call(vm, class_entry, method_info, nargs, return_type);
} else {
vm_method_call(vm, class_entry, method_info, nargs, return_type);
}
}
void vm_static_method_call(struct vm * vm, struct class_entry * class_entry, struct method_info * method_info)
{
/* If the method is not native, the nargs argument values are popped from the
@ -252,38 +306,16 @@ void vm_static_method_call(struct vm * vm, struct class_entry * class_entry, str
invoked. Execution continues with the first instruction of the method.
*/
int code_name_index = find_code_name_index(class_entry->class_file);
assert(code_name_index > 0);
struct Code_attribute * code = get_code_attribute(code_name_index,
method_info->attributes_count,
method_info->attributes);
assert(code != nullptr);
struct frame * old_frame = vm->current_frame;
vm->current_frame = stack_push_frame(&vm->frame_stack, 1);
vm->current_frame->code = code;
vm->current_frame->local_variable = stack_push_data(&vm->data_stack, code->max_locals);
vm->current_frame->operand_stack = stack_push_data(&vm->data_stack, code->max_stack);
vm->current_frame->operand_stack_ix = 0;
vm->current_frame->initialization_frame = 0;
uint8_t return_type;
struct constant * descriptor_constant = &class_entry->class_file->constant_pool[method_info->descriptor_index - 1];
int nargs = descriptor_nargs(descriptor_constant, &vm->current_frame->return_type);
int nargs = descriptor_nargs(descriptor_constant, &return_type);
debugf("nargs %d\n", nargs);
for (int i = 0; i < nargs; i++) {
uint32_t value = operand_stack_pop_u32(old_frame);
debugf("local[%d] = %x\n", nargs - i - 1, value);
vm->current_frame->local_variable[nargs - i - 1] = value;
if (method_info->access_flags & METHOD_ACC_NATIVE) {
vm_native_method_call(vm, class_entry, method_info, nargs, return_type);
} else {
vm_method_call(vm, class_entry, method_info, nargs, return_type);
}
;
vm->current_frame->pc = 0;
vm->current_frame->class_entry = class_entry;
vm->current_frame->method = method_info;
debugf("operand_stack_ix: %d\n", vm->current_frame->operand_stack_ix);
}
void vm_method_return(struct vm * vm)

View File

@ -77,15 +77,6 @@ void hash_table_add(int hash_table_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;
}
struct hash_table_entry * hash_table_find(int hash_table_length,
struct hash_table_entry * entry,
const uint8_t * key,
@ -99,7 +90,7 @@ struct hash_table_entry * hash_table_find(int hash_table_length,
while (e != nullptr && e->key != nullptr) {
//debugf("key find: %p ", e->key);
//print_key(e->key, e->key_length);
if (e->key_length == key_length && key_equal(key, e->key, e->key_length)) {
if (e->key_length == key_length && hash_table_key_equal(key, e->key, e->key_length)) {
return e;
}
e = e->next;

View File

@ -40,13 +40,11 @@ struct hash_table_entry * hash_table_find2(int hash_table_length,
const uint8_t * key2,
int key2_length);
/*
void hash_table_add_int(int hash_table_length,
struct hash_table_entry * entry,
int key,
void * value);
struct hash_table_entry * hash_table_find_int(int hash_table_length,
struct hash_table_entry * entry,
int key);
*/
static inline bool hash_table_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;
}

View File

@ -7,31 +7,31 @@
#include "sh7091_scif.h"
#include "p/Multiply.class.h"
#include "p/Native.class.h"
#include "java/lang/String.class.h"
#include "java/lang/System.class.h"
#include "java/io/PrintStream.class.h"
#include "java/lang/Object.class.h"
void main()
{
scif_init(0);
const uint8_t * class_names[1];
int class_names_length[1];
const uint8_t * class_file_buffers[] = {
(const uint8_t *)&_binary_p_Native_class_start,
(const uint8_t *)&_binary_java_lang_String_class_start,
(const uint8_t *)&_binary_java_lang_System_class_start,
(const uint8_t *)&_binary_java_io_PrintStream_class_start,
(const uint8_t *)&_binary_java_lang_Object_class_start,
};
int class_file_buffers_length = (sizeof (class_file_buffers)) / (sizeof (class_file_buffers[0]));
class_names[0] = (const uint8_t *)"p/Multiply";
class_names_length[0] = string_length((const char *)class_names[0]);
const uint8_t * buffers[1];
buffers[0] = (const uint8_t *)&_binary_p_Multiply_class_start;
const uint8_t * main_class = class_names[0];
int main_class_length = class_names_length[0];
int length = (sizeof (class_names)) / (sizeof (class_names[0]));
const uint8_t * main_class = (const uint8_t *)"p/Native";
int main_class_length = string_length((const char *)main_class);
int class_hash_table_length;
struct hash_table_entry * class_hash_table = class_resolver_load_from_buffers(class_names,
class_names_length,
buffers,
length,
struct hash_table_entry * class_hash_table = class_resolver_load_from_buffers(class_file_buffers,
class_file_buffers_length,
&class_hash_table_length);
vm_start(class_hash_table_length,

View File

@ -9,29 +9,15 @@
static struct hash_table_entry * load_from_filenames(const char * filenames[], int length, int * hash_table_length)
{
const uint8_t ** class_names = (const uint8_t **)filenames;
int class_names_length[length];
uint8_t * buffers[length];
size_t file_size[length];
for (int i = 0; i < length; i++) {
uint32_t filename_length = string_length(filenames[i]);
const char * suffix = ".class";
uint32_t suffix_length = string_length(suffix);
const char * filename_suffix = &filenames[i][filename_length - suffix_length];
if (filename_length <= suffix_length || !string_equal(suffix, filename_suffix)) {
printf("invalid class filename: %s\n", filenames[i]);
continue;
}
class_names_length[i] = filename_length - suffix_length;
printf("load class: %s\n", filenames[i]);
buffers[i] = file_read(filenames[i], &file_size[i]);
}
struct hash_table_entry * class_hash_table = class_resolver_load_from_buffers(class_names,
class_names_length,
(const uint8_t **)buffers,
struct hash_table_entry * class_hash_table = class_resolver_load_from_buffers((const uint8_t **)buffers,
length,
hash_table_length);

22
c/native.c Normal file
View File

@ -0,0 +1,22 @@
#include "native.h"
#include "printf.h"
void native_java_io_printstream_write(uint32_t * arrayref)
{
uint32_t length = arrayref[0];
char * buf = (char *)&arrayref[1];
print_string(buf, length);
}
void native_java_io_printstream_write_1(uint32_t * args)
{
uint32_t * arrayref = (uint32_t *)args[0];
native_java_io_printstream_write(arrayref);
}
void native_java_io_printstream_write_2(uint32_t * args)
{
//uint32_t this = args[0];
uint32_t * arrayref = (uint32_t *)args[1];
native_java_io_printstream_write(arrayref);
}

7
c/native.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
void native_java_io_printstream_write(uint32_t * arrayref);
void native_java_io_printstream_write_1(uint32_t * args);
void native_java_io_printstream_write_2(uint32_t * args);

View File

@ -25,9 +25,16 @@ void print_cstring(const char * s);
void _printf(const char * format, ...);
#define printf(...) _printf(__VA_ARGS__)
#if defined(DEBUG_PRINT)
#define debugf(...) _printf(__VA_ARGS__)
#define debugc(c) print_char(c)
#define debugs(s) print_cstring(s)
#else
#define debugf(...)
#define debugc(c)
#define debugs(c)
#endif
#ifdef __cplusplus
}

View File

@ -1,7 +1,7 @@
%.class: %.java
javac $<
java/lang/%.class: java/lang/%.java
java/%.class: java/%.java
javac --source 8 --target 8 --boot-class-path . $<
OBJ = \
@ -16,7 +16,8 @@ OBJ = \
c/frame.o \
c/printf.o \
c/parse.o \
c/unparse.o
c/unparse.o \
c/native.o
MAIN_DREAMCAST_OBJ = \
c/sh7091_scif.o \

View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t _binary_java_io_PrintStream_class_start __asm("_binary_java_io_PrintStream_class_start");
extern uint32_t _binary_java_io_PrintStream_class_end __asm("_binary_java_io_PrintStream_class_end");
extern uint32_t _binary_java_io_PrintStream_class_size __asm("_binary_java_io_PrintStream_class_size");
#ifdef __cplusplus
}
#endif

35
java/io/PrintStream.java Normal file
View File

@ -0,0 +1,35 @@
package java.io;
import java.lang.String;
public class PrintStream
{
public PrintStream() {
}
private final byte[] newline = {'\n'};
public static native void write(byte[] buf);
public void println() {
write(newline);
}
public void println(byte[] buf) {
write(buf);
write(newline);
}
public void println(String s) {
write(s.getBytes());
write(newline);
}
public void print(byte[] buf) {
write(buf);
}
public void print(String s) {
write(s.getBytes());
}
}

15
java/lang/Object.class.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t _binary_java_lang_Object_class_start __asm("_binary_java_lang_Object_class_start");
extern uint32_t _binary_java_lang_Object_class_end __asm("_binary_java_lang_Object_class_end");
extern uint32_t _binary_java_lang_Object_class_size __asm("_binary_java_lang_Object_class_size");
#ifdef __cplusplus
}
#endif

View File

@ -1,5 +1,5 @@
package java.lang;
class Object {
public class Object {
public Object() {}
}

15
java/lang/String.class.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t _binary_java_lang_String_class_start __asm("_binary_java_lang_String_class_start");
extern uint32_t _binary_java_lang_String_class_end __asm("_binary_java_lang_String_class_end");
extern uint32_t _binary_java_lang_String_class_size __asm("_binary_java_lang_String_class_size");
#ifdef __cplusplus
}
#endif

View File

@ -1,6 +1,6 @@
package java.lang;
class String {
public class String {
private final byte[] value;
public String() {

15
java/lang/System.class.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t _binary_java_lang_System_class_start __asm("_binary_java_lang_System_class_start");
extern uint32_t _binary_java_lang_System_class_end __asm("_binary_java_lang_System_class_end");
extern uint32_t _binary_java_lang_System_class_size __asm("_binary_java_lang_System_class_size");
#ifdef __cplusplus
}
#endif

13
java/lang/System.java Normal file
View File

@ -0,0 +1,13 @@
package java.lang;
import java.io.PrintStream;
public final class System {
public static PrintStream out = null;
private System() {
}
static {
out = new PrintStream();
}
}

15
p/Native.class.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t _binary_p_Native_class_start __asm("_binary_p_Native_class_start");
extern uint32_t _binary_p_Native_class_end __asm("_binary_p_Native_class_end");
extern uint32_t _binary_p_Native_class_size __asm("_binary_p_Native_class_size");
#ifdef __cplusplus
}
#endif

10
p/Native.java Normal file
View File

@ -0,0 +1,10 @@
package p;
import java.io.IOException;
class Native {
public static void main() {
String foo = "hello Dreamcast";
System.out.println(foo);
}
}