Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 16 additions & 17 deletions java_runtime/src/classes/java/io/input_stream_reader.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use core::cmp::min;

use alloc::{sync::Arc, vec};
use alloc::vec;

use bytemuck::{cast_slice, cast_vec};
use encoding_rs::{Decoder, EUC_KR, UTF_8};
use parking_lot::Mutex;
use encoding_rs::{EUC_KR, UTF_8};

use java_class_proto::{JavaFieldProto, JavaMethodProto};
use jvm::{Array, ClassInstanceRef, JavaChar, Jvm, Result};
use jvm::{Array, ClassInstanceRef, JavaChar, Jvm, Result, runtime::JavaLangString};

use crate::{
RuntimeClassProto, RuntimeContext,
Expand Down Expand Up @@ -36,7 +35,7 @@ impl InputStreamReader {
JavaFieldProto::new("readBufSize", "I", Default::default()),
JavaFieldProto::new("writeBuf", "[C", Default::default()),
JavaFieldProto::new("writeBufSize", "I", Default::default()),
JavaFieldProto::new("decoder", "[B", Default::default()),
JavaFieldProto::new("charset", "Ljava/lang/String;", Default::default()),
],
access_flags: Default::default(),
}
Expand All @@ -48,16 +47,8 @@ impl InputStreamReader {
let _: () = jvm.invoke_special(&this, "java/io/Reader", "<init>", "()V", ()).await?;

let charset = System::get_charset(jvm).await?;

let decoder = if charset == "UTF-8" {
UTF_8.new_decoder()
} else if charset == "EUC-KR" {
EUC_KR.new_decoder()
} else {
unimplemented!("unsupported charset: {}", charset)
};

jvm.put_rust_object_field(&mut this, "decoder", Arc::new(Mutex::new(decoder))).await?;
let charset_java = JavaLangString::from_rust_string(jvm, &charset).await?;
jvm.put_field(&mut this, "charset", "Ljava/lang/String;", charset_java).await?;

let read_buf = jvm.instantiate_array("B", BUF_SIZE).await?;
jvm.put_field(&mut this, "readBuf", "[B", read_buf).await?;
Expand Down Expand Up @@ -114,10 +105,18 @@ impl InputStreamReader {
let mut read_buf_data = vec![0; read_buf_size as _];
jvm.array_raw_buffer(&read_buf).await?.read(0, &mut read_buf_data).unwrap();

let decoder: Arc<Mutex<Decoder>> = jvm.get_rust_object_field(&this, "decoder").await?;
let charset_ref = jvm.get_field(&this, "charset", "Ljava/lang/String;").await?;
let charset = JavaLangString::to_rust_string(jvm, &charset_ref).await?;
let mut decoder = if charset == "UTF-8" {
UTF_8.new_decoder_without_bom_handling()
} else if charset == "EUC-KR" {
EUC_KR.new_decoder_without_bom_handling()
} else {
unimplemented!("unsupported charset: {}", charset)
};

let mut decoded = vec![0; BUF_SIZE * 3];
let (_, read, wrote, _) = decoder.lock().decode_to_utf16(&cast_vec(read_buf_data), &mut decoded, false);
let (_, read, wrote, _) = decoder.decode_to_utf16(&cast_vec(read_buf_data), &mut decoded, false);

// advance readBuf
let _: () = jvm
Expand Down
4 changes: 3 additions & 1 deletion java_runtime/src/classes/java/lang/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ impl Class {
),
],
fields: vec![
JavaFieldProto::new("raw", "[B", Default::default()), // raw rust pointer of Box<dyn Class>
// Stored as raw bytes instead of java/lang/String to avoid circular dependency:
// from_rust_class -> JavaLangString::from_rust_string -> new_class("java/lang/String") -> from_rust_class -> stack overflow
JavaFieldProto::new("nameBytes", "[B", Default::default()),
JavaFieldProto::new("classLoader", "Ljava/lang/ClassLoader;", Default::default()),
],
access_flags: Default::default(),
Expand Down
31 changes: 20 additions & 11 deletions java_runtime/src/classes/java/lang/throwable.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use alloc::{boxed::Box, format, string::String as RustString, sync::Arc, vec, vec::Vec};
use alloc::{boxed::Box, format, vec, vec::Vec};

use java_class_proto::{JavaFieldProto, JavaMethodProto};
use jvm::{ClassInstance, ClassInstanceRef, Jvm, Result, runtime::JavaLangString};
use jvm::{Array, ClassInstance, ClassInstanceRef, Jvm, Result, runtime::JavaLangString};

use crate::{
RuntimeClassProto, RuntimeContext,
Expand Down Expand Up @@ -46,7 +46,7 @@ impl Throwable {
],
fields: vec![
JavaFieldProto::new("detailMessage", "Ljava/lang/String;", Default::default()),
JavaFieldProto::new("stackTrace", "[B", Default::default()),
JavaFieldProto::new("stackTrace", "[Ljava/lang/String;", Default::default()),
],
access_flags: Default::default(),
}
Expand Down Expand Up @@ -77,9 +77,13 @@ impl Throwable {
async fn fill_in_stack_trace(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef<Self>) -> Result<ClassInstanceRef<Self>> {
tracing::debug!("java.lang.Throwable::fillInStackTrace({:?})", &this);

let stack_trace = Arc::new(jvm.stack_trace());

jvm.put_rust_object_field(&mut this, "stackTrace", stack_trace).await?;
let stack_trace = jvm.stack_trace();
let mut stack_trace_array = jvm.instantiate_array("Ljava/lang/String;", stack_trace.len()).await?;
for (i, line) in stack_trace.iter().enumerate() {
let java_line = JavaLangString::from_rust_string(jvm, line).await?;
jvm.store_array(&mut stack_trace_array, i, core::iter::once(java_line)).await?;
}
jvm.put_field(&mut this, "stackTrace", "[Ljava/lang/String;", stack_trace_array).await?;

Ok(this)
}
Expand Down Expand Up @@ -146,18 +150,23 @@ impl Throwable {
}

async fn do_print_stack_trace(jvm: &Jvm, this: ClassInstanceRef<Self>, stream_or_writer: Box<dyn ClassInstance>) -> Result<()> {
let stack_trace: Arc<Vec<RustString>> = jvm.get_rust_object_field(&this, "stackTrace").await?;
let stack_trace: ClassInstanceRef<Array<ClassInstanceRef<String>>> = jvm.get_field(&this, "stackTrace", "[Ljava/lang/String;").await?;

// TODO we can call println(Ljava/lang/Object;)V
let string: ClassInstanceRef<String> = jvm.invoke_virtual(&this, "toString", "()Ljava/lang/String;", ()).await?;
let _: () = jvm
.invoke_virtual(&stream_or_writer, "println", "(Ljava/lang/String;)V", (string,))
.await?;

for line in stack_trace.iter() {
let line = format!("\tat {line}");
let line = JavaLangString::from_rust_string(jvm, &line).await?;
let _: () = jvm.invoke_virtual(&stream_or_writer, "println", "(Ljava/lang/String;)V", (line,)).await?;
if !stack_trace.is_null() {
let length = jvm.array_length(&stack_trace).await?;
let lines: Vec<ClassInstanceRef<String>> = jvm.load_array(&stack_trace, 0, length).await?;
for line_ref in lines {
let line = JavaLangString::to_rust_string(jvm, &line_ref).await?;
let line = format!("\tat {line}");
let line = JavaLangString::from_rust_string(jvm, &line).await?;
let _: () = jvm.invoke_virtual(&stream_or_writer, "println", "(Ljava/lang/String;)V", (line,)).await?;
}
}

Ok(())
Expand Down
34 changes: 17 additions & 17 deletions java_runtime/src/classes/java/util/zip/zip_file.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use alloc::{string::ToString, sync::Arc, vec, vec::Vec};
use alloc::{string::ToString, vec, vec::Vec};
use core::iter;

// XXX for zip..
extern crate std;
use std::io::{Cursor, Read};

use parking_lot::Mutex;
use zip::ZipArchive;

use java_class_proto::{JavaFieldProto, JavaMethodProto};
use jvm::{ClassInstanceRef, Jvm, Result, runtime::JavaLangString};
use jvm::{Array, ClassInstanceRef, Jvm, Result, runtime::JavaLangString};

use crate::{
RuntimeClassProto, RuntimeContext,
Expand All @@ -20,8 +19,6 @@ use crate::{
},
};

type JavaZipArchive = Arc<Mutex<ZipArchive<Cursor<Vec<u8>>>>>;

// class java.util.zip.ZipFile
pub struct ZipFile;

Expand All @@ -47,11 +44,19 @@ impl ZipFile {
),
JavaMethodProto::new("entries", "()Ljava/util/Enumeration;", Self::entries, Default::default()),
],
fields: vec![JavaFieldProto::new("zip", "[B", Default::default())],
fields: vec![JavaFieldProto::new("zipData", "[B", Default::default())],
access_flags: Default::default(),
}
}

async fn get_zip_archive(jvm: &Jvm, this: &ClassInstanceRef<Self>) -> Result<ZipArchive<Cursor<Vec<u8>>>> {
let zip_data: ClassInstanceRef<Array<i8>> = jvm.get_field(this, "zipData", "[B").await?;
let length = jvm.array_length(&zip_data).await?;
let mut buf = vec![0u8; length];
jvm.array_raw_buffer(&zip_data).await?.read(0, &mut buf).unwrap();
Ok(ZipArchive::new(Cursor::new(buf)).unwrap())
}

async fn init(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef<Self>, file: ClassInstanceRef<File>) -> Result<()> {
tracing::debug!("java.util.zip.ZipFile::<init>({:?}, {:?})", &this, &file,);

Expand All @@ -63,10 +68,7 @@ impl ZipFile {
let buf = jvm.instantiate_array("B", length as _).await?;
let _: i32 = jvm.invoke_virtual(&is, "read", "([B)I", (buf.clone(),)).await?;

let mut rust_buf = vec![0; length as _];
jvm.array_raw_buffer(&buf).await?.read(0, &mut rust_buf).unwrap();
let zip = Arc::new(Mutex::new(ZipArchive::new(Cursor::new(rust_buf)).unwrap()));
jvm.put_rust_object_field(&mut this, "zip", zip).await?;
jvm.put_field(&mut this, "zipData", "[B", buf).await?;

Ok(())
}
Expand All @@ -82,8 +84,8 @@ impl ZipFile {
let entry = jvm.new_class("java/util/zip/ZipEntry", "(Ljava/lang/String;)V", (name.clone(),)).await?;
let name = JavaLangString::to_rust_string(jvm, &name).await?;

let zip: JavaZipArchive = jvm.get_rust_object_field(&this, "zip").await?;
let file_size = zip.lock().by_name(&name).map(|x| x.size());
let mut zip = Self::get_zip_archive(jvm, &this).await?;
let file_size = zip.by_name(&name).map(|x| x.size());

if let Ok(x) = file_size {
let _: () = jvm.invoke_virtual(&entry, "setSize", "(J)V", (x as i64,)).await?;
Expand All @@ -97,8 +99,8 @@ impl ZipFile {
async fn entries(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<ClassInstanceRef<Enumeration>> {
tracing::debug!("java.util.zip.ZipFile::entries({:?})", &this);

let zip: JavaZipArchive = jvm.get_rust_object_field(&this, "zip").await?;
let names = zip.lock().file_names().map(|x| x.to_string()).collect::<Vec<_>>();
let zip = Self::get_zip_archive(jvm, &this).await?;
let names = zip.file_names().map(|x| x.to_string()).collect::<Vec<_>>();

let mut name_array = jvm.instantiate_array("Ljava/lang/String;", names.len() as _).await?;
for (i, name) in names.iter().enumerate() {
Expand Down Expand Up @@ -129,9 +131,7 @@ impl ZipFile {
let entry_name = JavaLangString::to_rust_string(jvm, &entry_name).await?;

let data = {
let zip: JavaZipArchive = jvm.get_rust_object_field(&this, "zip").await?;

let mut zip = zip.lock();
let mut zip = Self::get_zip_archive(jvm, &this).await?;
let mut file = zip.by_name(&entry_name).unwrap();

let mut buf = Vec::new();
Expand Down
25 changes: 19 additions & 6 deletions jvm/src/runtime/java_lang_class.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
use alloc::boxed::Box;
use alloc::{boxed::Box, string::String, vec::Vec};

use crate::{Result, class_definition::ClassDefinition, class_instance::ClassInstance, jvm::Jvm};
use bytemuck::cast_vec;

use crate::{Array, ClassInstanceRef, Result, class_definition::ClassDefinition, class_instance::ClassInstance, jvm::Jvm};

pub struct JavaLangClass;

impl JavaLangClass {
#[allow(clippy::borrowed_box)]
pub async fn to_rust_class(jvm: &Jvm, this: &Box<dyn ClassInstance>) -> Result<Box<dyn ClassDefinition>> {
let rust_class = jvm.get_rust_object_field(this, "raw").await?;

Ok(rust_class)
let name_bytes: ClassInstanceRef<Array<i8>> = jvm.get_field(this, "nameBytes", "[B").await?;
let len = jvm.array_length(&name_bytes).await?;
let name_bytes_vec: Vec<i8> = jvm.load_array(&name_bytes, 0, len).await?;
let class_name = String::from_utf8(cast_vec(name_bytes_vec)).unwrap_or_default();
if let Some(class) = jvm.get_class(&class_name) {
Ok(class.definition)
} else {
Err(jvm.exception("java/lang/NoClassDefFoundError", &class_name).await)
}
}

pub async fn from_rust_class(
Expand All @@ -19,7 +27,12 @@ impl JavaLangClass {
) -> Result<Box<dyn ClassInstance>> {
let mut java_class = jvm.new_class("java/lang/Class", "()V", ()).await?;

jvm.put_rust_object_field(&mut java_class, "raw", rust_class).await?;
let class_name = rust_class.name();
let mut name_bytes = jvm.instantiate_array("B", class_name.len()).await?;
let bytes: Vec<i8> = cast_vec(class_name.into_bytes());
jvm.store_array(&mut name_bytes, 0, bytes).await?;
jvm.put_field(&mut java_class, "nameBytes", "[B", name_bytes).await?;

jvm.put_field(&mut java_class, "classLoader", "Ljava/lang/ClassLoader;", class_loader)
.await?;

Expand Down
Loading