java.nio: partial Path/Files/FileSystem/InputStream implementation

This commit is contained in:
Zack Buhman 2025-01-19 22:54:54 -06:00
parent e83cf7f092
commit 86e18a0944
48 changed files with 517 additions and 112 deletions

View File

@ -10,7 +10,7 @@ CFLAGS += -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -fstack-protec
CFLAGS += -I$(MAKEFILE_PATH)/
CFLAGS += -I$(MAKEFILE_PATH)/c
CFLAGS += -DDEBUG
CFLAGS += -DDEBUG_PRINT
#CFLAGS += -DDEBUG_PRINT
LDFLAGS = -lm
OPT ?= -O0
DEPFLAGS = -MMD -MP

View File

@ -1,7 +1,7 @@
#pragma once
#include "native_types.h"
#include "frame.h"
#include "vm.h"
struct backtrace_entry {
struct class_file * class_file;

View File

@ -13,7 +13,6 @@
#include "fatal.h"
#include "parse_type.h"
#include "find_attribute.h"
#include "frame.h"
#include "native_types_allocate.h"
#include "vm_instance.h"

View File

@ -5,53 +5,7 @@
#include "class_file.h"
#include "hash_table.h"
#include "native_types.h"
enum initialization_state {
CLASS_UNINITIALIZED,
CLASS_INITIALIZING,
CLASS_INITIALIZED,
};
struct method_entry {
struct class_entry * class_entry;
struct method_info * method_info;
struct Code_attribute * code_attribute;
};
union attribute_entry {
struct class_entry * class_entry;
struct method_entry * method_entry;
struct field_entry * field_entry;
struct objectref * string_objectref;
};
struct field_entry {
struct class_entry * class_entry;
struct field_info * field_info;
union {
int32_t instance_index;
int32_t static_index;
};
};
struct class_entry {
struct class_file * class_file;
enum initialization_state initialization_state;
union attribute_entry * attribute_entry;
int32_t static_fields_count;
int32_t * static_fields;
int32_t instance_fields_count;
struct {
int length;
struct hash_table_entry * entry;
} fields;
struct {
int length;
struct hash_table_entry * entry;
} methods;
};
#include "vm.h"
struct hash_table_entry * class_resolver_load_from_buffers(const uint8_t ** buffers,
int length,
@ -94,3 +48,7 @@ bool class_resolver_instanceof(int class_hash_table_length,
struct class_entry * origin_class_entry,
const int class_index,
struct objectref * objectref);
struct objectref * class_resolver_lookup_string(struct vm * vm,
struct class_entry * class_entry,
const int string_index);

View File

@ -1,6 +1,6 @@
#pragma once
#include "frame.h"
#include "vm.h"
uint32_t decode_print_instruction(const uint8_t * code, uint32_t pc);
void decode_execute_instruction(struct vm * vm, const uint8_t * code, uint32_t pc);

View File

@ -2,7 +2,7 @@
#include <stdint.h>
#include "frame.h"
#include "vm.h"
void op_aaload(struct vm * vm);
void op_aastore(struct vm * vm);

View File

@ -1,8 +1,3 @@
// declared here due to circular frame.h include in class_resolver.h
struct objectref * class_resolver_lookup_string(struct vm * vm,
struct class_entry * class_entry,
const int string_index);
static inline struct field_entry * field_entry_from_constant_index(int class_hash_table_length,
struct hash_table_entry * class_hash_table,
struct class_entry * origin_class_entry,

View File

@ -1,9 +1,56 @@
#pragma once
#include <stdint.h>
#include "vm.h"
#include "assert.h"
#include "class_file.h"
#include "class_resolver.h"
#include "native_types.h"
enum initialization_state {
CLASS_UNINITIALIZED,
CLASS_INITIALIZING,
CLASS_INITIALIZED,
};
struct method_entry {
struct class_entry * class_entry;
struct method_info * method_info;
struct Code_attribute * code_attribute;
};
union attribute_entry {
struct class_entry * class_entry;
struct method_entry * method_entry;
struct field_entry * field_entry;
struct objectref * string_objectref;
};
struct field_entry {
struct class_entry * class_entry;
struct field_info * field_info;
union {
int32_t instance_index;
int32_t static_index;
};
};
struct class_entry {
struct class_file * class_file;
enum initialization_state initialization_state;
union attribute_entry * attribute_entry;
int32_t static_fields_count;
int32_t * static_fields;
int32_t instance_fields_count;
struct {
int length;
struct hash_table_entry * entry;
} fields;
struct {
int length;
struct hash_table_entry * entry;
} methods;
};
struct frame {
struct class_entry * class_entry;
@ -27,20 +74,6 @@ struct stack {
int32_t capacity;
};
struct vm {
struct stack frame_stack;
struct stack data_stack;
struct frame * current_frame;
struct {
int length;
struct hash_table_entry * entry;
} class_hash_table;
struct {
int length;
struct hash_table_entry * entry;
} native_hash_table;
};
static inline struct frame * stack_push_frame(struct stack * stack, int num_frames)
{
struct frame * frame = &stack->frame[stack->ix];
@ -182,17 +215,3 @@ static inline double operand_stack_pop_f64(struct frame * frame)
double f = *((double *)&value);
return f;
}
bool vm_initialize_class(struct vm * vm, struct class_entry * class_entry);
void vm_special_method_call(struct vm * vm, struct class_entry * class_entry, struct method_entry * method_entry);
void vm_static_method_call(struct vm * vm, struct class_entry * class_entry, struct method_entry * method_entry);
void vm_method_return(struct vm * vm);
void vm_execute(struct vm * vm);
struct vm * vm_start(int class_hash_table_length,
struct hash_table_entry * class_hash_table,
int native_hash_table_length,
struct hash_table_entry * native_hash_table,
const uint8_t * main_class_name,
int main_class_name_length);
int descriptor_nargs(struct constant * descriptor_constant, uint8_t * return_type);
void vm_exception(struct vm * vm, struct objectref * objectref);

4
c/gc.c
View File

@ -1,6 +1,8 @@
#include "frame.h"
#include "gc.h"
#include "memory_allocator.h"
#include "printf.h"
#include "hash_table.h"
#include "native_types.h"
static void walk_address(void * address);

2
c/gc.h
View File

@ -1,6 +1,6 @@
#pragma once
#include "frame.h"
#include "vm.h"
void gc_mark(struct vm * vm);
void gc_sweep();

View File

@ -4,6 +4,7 @@
#include "printf.h"
#include "native.h"
#include "native/class.h"
#include "native/libcinputstream.h"
#include "native/loader.h"
#include "native/math.h"
#include "native/memory.h"
@ -160,6 +161,26 @@ const static struct native_method native_method[] = {
.method_descriptor = "(Ljava/lang/Object;)I",
.func = native_java_lang_system_hashcode_1,
},
#if !defined(__dreamcast__)
{
.class_name = "jvm/internal/LibcInputStream",
.method_name = "_open",
.method_descriptor = "([B)I",
.func = native_jvm_internal_libcinputstream_open_1,
},
{
.class_name = "jvm/internal/LibcInputStream",
.method_name = "_close",
.method_descriptor = "(I)V",
.func = native_jvm_internal_libcinputstream_close_1,
},
{
.class_name = "jvm/internal/LibcInputStream",
.method_name = "_read",
.method_descriptor = "(I)I",
.func = native_jvm_internal_libcinputstream_read_1,
},
#endif
};
struct hash_table_entry * native_init_hash_table(int * hash_table_length)

View File

@ -1,6 +1,6 @@
#pragma once
#include "frame.h"
#include "vm.h"
void native_method_call(struct vm * vm,
struct constant * class_name_constant,

View File

@ -1,6 +1,7 @@
#include "class.h"
#include "printf.h"
#include "vm_instance.h"
#include "class_resolver.h"
void native_java_lang_class_getname_1(struct vm * vm, uint32_t * args)
{

View File

@ -2,7 +2,7 @@
#include <stdint.h>
#include "frame.h"
#include "vm.h"
void native_java_lang_class_getname_1(struct vm * vm, uint32_t * args);
void native_java_lang_class_getsuperclass_1(struct vm * vm, uint32_t * args);

View File

@ -0,0 +1,38 @@
#include <stdio.h>
#include "libcinputstream.h"
#include "assert.h"
#include "native_types.h"
void native_jvm_internal_libcinputstream_open_1(struct vm * vm, uint32_t * args)
{
struct arrayref * arrayref = (struct arrayref *)args[0];
assert(arrayref != nullptr);
assert(arrayref->length > 0);
assert(arrayref->u8[arrayref->length - 1] == 0);
FILE * file = fopen((const char *)arrayref->u8, "rb");
assert(file != nullptr);
operand_stack_push_u32(vm->current_frame, (uint32_t)file);
}
void native_jvm_internal_libcinputstream_close_1(struct vm * vm, uint32_t * args)
{
FILE * file = (FILE *)args[0];
assert(file != nullptr);
int ret = fclose(file);
assert(ret == 0);
}
void native_jvm_internal_libcinputstream_read_1(struct vm * vm, uint32_t * args)
{
FILE * file = (FILE *)args[0];
assert(file != nullptr);
uint8_t buf[1];
int32_t size = fread(buf, 1, 1, file);
if (size == 0)
operand_stack_push_u32(vm->current_frame, (uint32_t)-1);
else
operand_stack_push_u32(vm->current_frame, (uint32_t)buf[0]);
}

View File

@ -0,0 +1,9 @@
#pragma once
#include <stdint.h>
#include "vm.h"
void native_jvm_internal_libcinputstream_open_1(struct vm * vm, uint32_t * args);
void native_jvm_internal_libcinputstream_close_1(struct vm * vm, uint32_t * args);
void native_jvm_internal_libcinputstream_read_1(struct vm * vm, uint32_t * args);

View File

@ -4,6 +4,7 @@
#include "malloc.h"
#include "loader.h"
#include "native.h"
#include "class_resolver.h"
static uint8_t loader_buffer[0x100000] __attribute__ ((aligned (32)));

View File

@ -2,7 +2,7 @@
#include <stdint.h>
#include "frame.h"
#include "vm.h"
void native_jvm_internal_loader_getbuffer_0(struct vm * vm, uint32_t * args);
void native_jvm_internal_loader_load_2(struct vm * vm, uint32_t * args);

View File

@ -2,7 +2,7 @@
#include <stdint.h>
#include "frame.h"
#include "vm.h"
void native_java_lang_math_sin_1(struct vm * vm, uint32_t * args);
void native_java_lang_math_cos_1(struct vm * vm, uint32_t * args);

View File

@ -2,7 +2,7 @@
#include <stdint.h>
#include "frame.h"
#include "vm.h"
void native_java_misc_memory_putU4_2(struct vm * vm, uint32_t * args);
void native_java_misc_memory_putU2_2(struct vm * vm, uint32_t * args);

View File

@ -1,5 +1,6 @@
#include "object.h"
#include "vm_instance.h"
#include "native_types.h"
void native_java_lang_object_getclass_1(struct vm * vm, uint32_t * args)
{

View File

@ -2,6 +2,6 @@
#include <stdint.h>
#include "frame.h"
#include "vm.h"
void native_java_lang_object_getclass_1(struct vm * vm, uint32_t * args);

View File

@ -5,18 +5,22 @@
void native_java_io_printstream_write_ba_1(struct vm * vm, uint32_t * args)
{
struct arrayref * arrayref = (struct arrayref *)args[0];
assert(arrayref != nullptr);
print_bytes(arrayref->u8, arrayref->length);
}
void native_java_io_printstream_write_ca_1(struct vm * vm, uint32_t * args)
{
struct arrayref * arrayref = (struct arrayref *)args[0];
assert(arrayref != nullptr);
print_chars(arrayref->u16, arrayref->length);
}
void native_java_io_printstream_write_s_1(struct vm * vm, uint32_t * args)
{
struct objectref * objectref = (struct objectref *)args[0];
assert(objectref != nullptr);
struct arrayref * arrayref = objectref->aref[0];
assert(arrayref != nullptr);
print_chars(arrayref->u16, arrayref->length);
}

View File

@ -2,7 +2,7 @@
#include <stdint.h>
#include "frame.h"
#include "vm.h"
void native_java_io_printstream_write_ba_1(struct vm * vm, uint32_t * args);
void native_java_io_printstream_write_ca_1(struct vm * vm, uint32_t * args);

View File

@ -2,7 +2,7 @@
#include <stdint.h>
#include "frame.h"
#include "vm.h"
void native_java_lang_runtime_freememory_0(struct vm * vm, uint32_t * args);
void native_java_lang_runtime_gc_0(struct vm * vm, uint32_t * args);

View File

@ -2,6 +2,6 @@
#include <stdint.h>
#include "frame.h"
#include "vm.h"
void native_java_lang_system_hashcode_1(struct vm * vm, uint32_t * args);

View File

@ -3,7 +3,6 @@
#include <stdint.h>
#include "assert.h"
#include "class_resolver.h"
#include "memory_allocator.h"
enum tag_type {

View File

@ -1,8 +1,9 @@
#pragma once
#include "native_types.h"
#include "frame.h"
#include "gc.h"
#include "vm.h"
#include "memory_allocator.h"
static inline void * gc_memory_allocate(struct vm * vm, int size)
{

View File

@ -4,7 +4,6 @@
#include "class_file.h"
#include "bytes.h"
#include "decode.h"
#include "frame.h"
#include "class_resolver.h"
#include "printf.h"
#include "string.h"
@ -104,20 +103,26 @@ bool vm_initialize_class(struct vm * vm, struct class_entry * class_entry)
for (int j = 0; j < field_info->attributes_count; j++) {
if (field_info->attributes[j].attribute_name_index == constantvalue_name_index) {
struct attribute_info * attribute = &field_info->attributes[j];
struct constant * constantvalue = &class_file->constant_pool[attribute->constantvalue->constantvalue_index - 1];
assert(constantvalue->tag == CONSTANT_Integer); // also need to support CONSTANT_String
struct constant * name_constant = &class_file->constant_pool[field_info->name_index - 1];
assert(name_constant->tag == CONSTANT_Utf8);
struct field_entry * field_entry = class_resolver_lookup_field(class_entry->fields.length,
class_entry->fields.entry,
name_constant->utf8.bytes,
name_constant->utf8.length);
assert(field_entry != nullptr);
class_entry->static_fields[field_entry->static_index] = constantvalue->integer.bytes;
debugf(" constantvalue: %d\n", constantvalue->integer.bytes);
struct attribute_info * attribute = &field_info->attributes[j];
struct constant * constantvalue = &class_file->constant_pool[attribute->constantvalue->constantvalue_index - 1];
if (constantvalue->tag == CONSTANT_Integer) {
class_entry->static_fields[field_entry->static_index] = constantvalue->integer.bytes;
} else if (constantvalue->tag == CONSTANT_String) {
struct objectref * objectref = class_resolver_lookup_string(vm,
class_entry,
attribute->constantvalue->constantvalue_index);
class_entry->static_fields[field_entry->static_index] = (int32_t)objectref;
} else {
assert(!"invalid constantvalue tag");
}
break;
}

32
c/vm.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include "frame_stack.h"
#include "class_file.h"
struct vm {
struct stack frame_stack;
struct stack data_stack;
struct frame * current_frame;
struct {
int length;
struct hash_table_entry * entry;
} class_hash_table;
struct {
int length;
struct hash_table_entry * entry;
} native_hash_table;
};
bool vm_initialize_class(struct vm * vm, struct class_entry * class_entry);
void vm_special_method_call(struct vm * vm, struct class_entry * class_entry, struct method_entry * method_entry);
void vm_static_method_call(struct vm * vm, struct class_entry * class_entry, struct method_entry * method_entry);
void vm_method_return(struct vm * vm);
void vm_execute(struct vm * vm);
struct vm * vm_start(int class_hash_table_length,
struct hash_table_entry * class_hash_table,
int native_hash_table_length,
struct hash_table_entry * native_hash_table,
const uint8_t * main_class_name,
int main_class_name_length);
int descriptor_nargs(struct constant * descriptor_constant, uint8_t * return_type);
void vm_exception(struct vm * vm, struct objectref * objectref);

View File

@ -1,6 +1,7 @@
#include "string.h"
#include "native_types_allocate.h"
#include "vm_instance.h"
#include "class_resolver.h"
struct objectref * vm_instance_create(struct vm * vm, const char * class_name)
{

View File

@ -1,6 +1,6 @@
#pragma once
#include "frame.h"
#include "vm.h"
struct objectref * vm_instance_create(struct vm * vm, const char * class_name);
struct objectref * vm_instance_string_from_constant(struct vm * vm, struct constant * constant);

View File

@ -0,0 +1,7 @@
package java.io;
public interface Closeable
extends AutoCloseable {
void close() throws IOException;
}

View File

@ -0,0 +1,4 @@
package java.io;
public class IOException extends Exception {
}

View File

@ -0,0 +1,72 @@
package java.io;
public abstract class InputStream
implements Closeable {
public InputStream() {
}
public int available() throws IOException {
return 0;
}
public void close() throws IOException {
}
public void mark(int readlimit) {
}
public abstract int read() throws IOException;
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public int read(byte[] b,
int off,
int len) throws IOException {
if (off < 0 || len < 0 || len > b.length - off)
throw new IndexOutOfBoundsException();
if (b == null)
throw new NullPointerException();
for (int i = 0; i < len; i++) {
try {
int c = read();
boolean endOfStream = c == -1;
if (endOfStream) {
if (i == 0)
return -1;
else
return i;
}
b[off + i] = (byte)c;
} catch (IOException e) {
if (i == 0)
throw e;
else
return i;
}
}
return len;
}
public void reset() throws IOException {
throw new IOException();
}
public long skip(long n)
throws IOException {
long ni = n;
while (ni > 0L) {
int b = read();
if (b < 0)
break;
ni -= 1;
}
return n - ni;
}
}

View File

@ -1,7 +1,7 @@
package java.lang;
public final class Class<T> {
private Object klass; // struct class_entry *
private int class_entry; // is actually struct class_entry *
private String name;
private Class() {

View File

@ -0,0 +1,26 @@
package java.nio.file;
import java.io.Closeable;
import java.io.IOException;
import java.nio.file.spi.FileSystemProvider;
public abstract class FileSystem
implements Closeable {
protected FileSystem() {
}
public abstract void close()
throws IOException;
public abstract Path getPath(String first,
String... more);
public abstract String getSeparator();
public abstract boolean isOpen();
public abstract boolean isReadOnly();
public abstract FileSystemProvider provider();
}

View File

@ -0,0 +1,12 @@
package java.nio.file;
import jvm.internal.LibcFileSystem;
public final class FileSystems {
private FileSystems() {
}
public static FileSystem getDefault() {
return LibcFileSystem.getLibcFileSystem();
}
}

View File

@ -0,0 +1,15 @@
package java.nio.file;
import java.io.IOException;
import java.io.InputStream;
public final class Files {
public static InputStream newInputStream(Path path,
OpenOption... options)
throws IOException {
return path.getFileSystem().provider().newInputStream(path, options);
}
}

View File

@ -0,0 +1,4 @@
package java.nio.file;
public interface OpenOption {
}

View File

@ -0,0 +1,7 @@
package java.nio.file;
public interface Path
// extends Comparable<Path>, Iterable<Path>
{
FileSystem getFileSystem();
}

View File

@ -0,0 +1,18 @@
package java.nio.file.spi;
import java.nio.file.Path;
import java.nio.file.OpenOption;
import java.io.InputStream;
import java.io.IOException;
public abstract class FileSystemProvider {
protected FileSystemProvider() {
}
public InputStream newInputStream(Path path,
OpenOption... options)
throws IOException {
throw new IOException();
}
}

View File

@ -0,0 +1,43 @@
package jvm.internal;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.nio.file.spi.FileSystemProvider;
public class LibcFileSystem extends FileSystem {
private static final String separator = "/";
private static LibcFileSystem libcFileSystem = new LibcFileSystem();
public static LibcFileSystem getLibcFileSystem() {
return libcFileSystem;
}
private LibcFileSystem() {
super();
}
public void close() {
// do nothing
}
public Path getPath(String first, String ... more) {
return LibcPath.fromString(first, more);
}
public String getSeparator() {
return LibcFileSystem.separator;
}
public boolean isOpen() {
return true;
}
public boolean isReadOnly() {
return false;
}
public FileSystemProvider provider() {
return LibcFileSystemProvider.getLibcFileSystemProvider();
}
}

View File

@ -0,0 +1,26 @@
package jvm.internal;
import java.nio.file.Path;
import java.nio.file.OpenOption;
import java.nio.file.FileSystem;
import java.nio.file.spi.FileSystemProvider;
import java.io.InputStream;
import java.io.IOException;
public class LibcFileSystemProvider extends FileSystemProvider {
private static LibcFileSystemProvider libcFileSystemProvider = new LibcFileSystemProvider();
private LibcFileSystemProvider() {
super();
}
public static LibcFileSystemProvider getLibcFileSystemProvider() {
return libcFileSystemProvider;
}
public InputStream newInputStream(Path path,
OpenOption... options)
throws IOException {
return new LibcInputStream((LibcPath)path);
}
}

View File

@ -0,0 +1,25 @@
package jvm.internal;
import java.io.InputStream;
public class LibcInputStream extends InputStream {
private int file; // is actually FILE *
private static native int _open(byte[] path);
public LibcInputStream(LibcPath path) {
file = _open(path.path);
}
private static native void _close(int file);
public void close() {
_close(file);
}
private static native int _read(int file);
public int read() {
return _read(file);
}
}

View File

@ -0,0 +1,43 @@
package jvm.internal;
import java.nio.file.Path;
import java.nio.file.FileSystem;
public class LibcPath implements Path {
public byte[] path;
public LibcPath(byte[] path) {
this.path = path;
}
private static int copyInto(byte[] dest, int offset, String s) {
int length = s.length();
for (int i = 0; i < length; i++) {
dest[offset + i] = (byte)s.charAt(i);
}
return offset + length;
}
public static LibcPath fromString(String first, String... more) {
int length = first.length();
for (int i = 0; i < more.length; i++) {
length += more[i].length();
}
length += more.length + 1; // including C string null terminator
byte[] path = new byte[length];
int offset = 0;
offset = copyInto(path, offset, first);
byte sep = (byte)'/';
for (int i = 0; i < more.length; i++) {
path[offset++] = sep;
offset = copyInto(path, offset, more[i]);
}
return new LibcPath(path);
}
public FileSystem getFileSystem() {
return LibcFileSystem.getLibcFileSystem();
}
}

View File

@ -0,0 +1,16 @@
package test;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.InputStream;
import java.io.IOException;
class TestInputStream {
public static void main() throws IOException {
Path path = FileSystems.getDefault().getPath("/home/bilbo/source.txt");
InputStream is = Files.newInputStream(path);
char c = (char)is.read();
System.out.println(c);
}
}

View File

@ -15,7 +15,7 @@ OBJ = \
c/execute.o \
c/fatal.o \
c/find_attribute.o \
c/frame.o \
c/vm.o \
c/gc.o \
c/hash_table.o \
c/malloc.o \
@ -42,6 +42,7 @@ MAIN_DREAMCAST_OBJ = \
MAIN_HOSTED_OBJ = \
c/file.o \
c/native/libcinputstream.o \
c/main_hosted.o
PRINT_CLASS_OBJ = \