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
12 changes: 9 additions & 3 deletions java_runtime/src/classes/java/io.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,22 +15,28 @@ mod filter_input_stream;
mod filter_output_stream;
mod input_stream;
mod input_stream_reader;
mod interrupted_io_exception;
mod io_exception;
mod output_stream;
mod output_stream_writer;
mod print_stream;
mod print_writer;
mod random_access_file;
mod reader;
mod serializable;
mod string_writer;
mod unsupported_encoding_exception;
mod utf_data_format_exception;
mod writer;

pub use self::{
buffered_reader::BufferedReader, byte_array_input_stream::ByteArrayInputStream, byte_array_output_stream::ByteArrayOutputStream,
data_input::DataInput, data_input_stream::DataInputStream, data_output::DataOutput, data_output_stream::DataOutputStream,
eof_exception::EOFException, file::File, file_descriptor::FileDescriptor, file_input_stream::FileInputStream,
file_not_found_exception::FileNotFoundException, file_output_stream::FileOutputStream, filter_input_stream::FilterInputStream,
filter_output_stream::FilterOutputStream, input_stream::InputStream, input_stream_reader::InputStreamReader, io_exception::IOException,
output_stream::OutputStream, print_stream::PrintStream, print_writer::PrintWriter, random_access_file::RandomAccessFile, reader::Reader,
serializable::Serializable, string_writer::StringWriter, writer::Writer,
filter_output_stream::FilterOutputStream, input_stream::InputStream, input_stream_reader::InputStreamReader,
interrupted_io_exception::InterruptedIOException, io_exception::IOException, output_stream::OutputStream,
output_stream_writer::OutputStreamWriter, print_stream::PrintStream, print_writer::PrintWriter, random_access_file::RandomAccessFile,
reader::Reader, serializable::Serializable, string_writer::StringWriter, unsupported_encoding_exception::UnsupportedEncodingException,
utf_data_format_exception::UTFDataFormatException, writer::Writer,
};
43 changes: 31 additions & 12 deletions java_runtime/src/classes/java/io/byte_array_input_stream.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@ impl ByteArrayInputStream {
JavaMethodProto::new("skip", "(J)J", Self::skip, Default::default()),
JavaMethodProto::new("mark", "(I)V", Self::mark, Default::default()),
JavaMethodProto::new("reset", "()V", Self::reset, Default::default()),
JavaMethodProto::new("markSupported", "()Z", Self::mark_supported, Default::default()),
],
fields: vec![
JavaFieldProto::new("buf", "[B", Default::default()),
Expand DownExpand Up@@ -57,11 +58,17 @@ impl ByteArrayInputStream {
) -> Result<()> {
tracing::debug!("java.io.ByteArrayInputStream::<init>({this:?}, {data:?}, {offset}, {length})");

let data_length = jvm.array_length(&data).await? as i32;
if offset < 0 || length < 0 || offset > data_length {
return Err(jvm.exception("java/lang/IndexOutOfBoundsException", "Invalid offset or length").await);
}

let _: () = jvm.invoke_special(&this, "java/io/InputStream", "<init>", "()V", ()).await?;

jvm.put_field(&mut this, "buf", "[B", data).await?;
jvm.put_field(&mut this, "pos", "I", offset).await?;
jvm.put_field(&mut this, "count", "I", length).await?;
jvm.put_field(&mut this, "count", "I", (offset + length).min(data_length)).await?;
jvm.put_field(&mut this, "mark", "I", offset).await?;

Ok(())
}
Expand All@@ -85,15 +92,23 @@ impl ByteArrayInputStream {
) -> Result<i32> {
tracing::debug!("java.io.ByteArrayInputStream::read({this:?}, {b:?}, {off}, {len})");

let buf = jvm.get_field(&this, "buf", "[B").await?;
let buf_length = jvm.array_length(&buf).await?;
let buf: ClassInstanceRef<Array<i8>> = jvm.get_field(&this, "buf", "[B").await?;
let count: i32 = jvm.get_field(&this, "count", "I").await?;
let pos: i32 = jvm.get_field(&this, "pos", "I").await?;

let available = (buf_length as i32 - pos) as _;
let len_to_read = if len > available { available } else { len };
if len_to_read == 0 {
let target_length = jvm.array_length(&b).await? as i32;
if off < 0 || len < 0 || off > target_length - len {
return Err(jvm.exception("java/lang/IndexOutOfBoundsException", "Invalid offset or length").await);
}
if len == 0 {
return Ok(0);
}

let available = count - pos;
if available <= 0 {
return Ok(-1);
}
let len_to_read = if len > available { available } else { len };

let _: () = jvm
.invoke_static(
Expand All@@ -113,10 +128,10 @@ impl ByteArrayInputStream {
tracing::debug!("java.io.ByteArrayInputStream::readByte({this:?})");

let buf = jvm.get_field(&this, "buf", "[B").await?;
let buf_length = jvm.array_length(&buf).await?;
let count: i32 = jvm.get_field(&this, "count", "I").await?;
let pos: i32 = jvm.get_field(&this, "pos", "I").await?;

if pos as usize >= buf_length {
if pos >= count {
return Ok(-1);
}

Expand All@@ -136,12 +151,11 @@ impl ByteArrayInputStream {
async fn skip(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef<Self>, n: i64) -> Result<i64> {
tracing::debug!("java.io.ByteArrayInputStream::skip({this:?}, {n:?})");

let buf = jvm.get_field(&this, "buf", "[B").await?;
let buf_length = jvm.array_length(&buf).await?;
let count: i32 = jvm.get_field(&this, "count", "I").await?;
let pos: i32 = jvm.get_field(&this, "pos", "I").await?;

let available = (buf_length as i32 - pos) as i64;
let len_to_skip = if n > available { available } else { n };
let available = (count - pos) as i64;
let len_to_skip = n.max(0).min(available);

jvm.put_field(&mut this, "pos", "I", pos + len_to_skip as i32).await?;

Expand All@@ -165,4 +179,9 @@ impl ByteArrayInputStream {

Ok(())
}

async fn mark_supported(_: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<bool> {
tracing::debug!("java.io.ByteArrayInputStream::markSupported({this:?})");
Ok(true)
}
}
51 changes: 48 additions & 3 deletions java_runtime/src/classes/java/io/byte_array_output_stream.rs
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
use alloc::vec;
use alloc::{vec, vec::Vec};

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

use crate::{RuntimeClassProto, RuntimeContext};
use crate::{RuntimeClassProto, RuntimeContext, classes::java::lang::String};

// class java.io.ByteArrayOutputStream
pub struct ByteArrayOutputStream;
Expand All@@ -18,7 +18,9 @@ impl ByteArrayOutputStream {
JavaMethodProto::new("<init>", "()V", Self::init, Default::default()),
JavaMethodProto::new("<init>", "(I)V", Self::init_with_size, Default::default()),
JavaMethodProto::new("write", "(I)V", Self::write, Default::default()),
JavaMethodProto::new("write", "([BII)V", Self::write_bytes, Default::default()),
JavaMethodProto::new("toByteArray", "()[B", Self::to_byte_array, Default::default()),
JavaMethodProto::new("toString", "()Ljava/lang/String;", Self::to_string, Default::default()),
JavaMethodProto::new("size", "()I", Self::size, Default::default()),
JavaMethodProto::new("reset", "()V", Self::reset, Default::default()),
JavaMethodProto::new("close", "()V", Self::close, Default::default()),
Expand All@@ -44,16 +46,43 @@ impl ByteArrayOutputStream {
async fn init_with_size(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef<Self>, size: i32) -> Result<()> {
tracing::debug!("java.io.ByteArrayOutputStream::<init>({this:?}, {size:?})");

if size < 0 {
return Err(jvm.exception("java/lang/IllegalArgumentException", "Negative initial size").await);
}

let _: () = jvm.invoke_special(&this, "java/io/OutputStream", "<init>", "()V", ()).await?;

let array = jvm.instantiate_array("B", 1024).await?;
let array = jvm.instantiate_array("B", size as usize).await?;

jvm.put_field(&mut this, "buf", "[B", array).await?;
jvm.put_field(&mut this, "pos", "I", 0).await?;

Ok(())
}

async fn write_bytes(
jvm: &Jvm,
_: &mut RuntimeContext,
mut this: ClassInstanceRef<Self>,
bytes: ClassInstanceRef<Array<i8>>,
off: i32,
len: i32,
) -> Result<()> {
tracing::debug!("java.io.ByteArrayOutputStream::write({this:?}, {bytes:?}, {off}, {len})");

let length = jvm.array_length(&bytes).await? as i32;
if off < 0 || len < 0 || off > length - len {
return Err(jvm.exception("java/lang/IndexOutOfBoundsException", "Invalid offset or length").await);
}

let pos: i32 = jvm.get_field(&this, "pos", "I").await?;
Self::ensure_capacity(jvm, &mut this, (pos + len) as usize).await?;
let mut buf = jvm.get_field(&this, "buf", "[B").await?;
let values: Vec<i8> = jvm.load_array(&bytes, off as usize, len as usize).await?;
jvm.store_array(&mut buf, pos as usize, values).await?;
jvm.put_field(&mut this, "pos", "I", pos + len).await
}

async fn write(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef<Self>, b: i32) -> Result<()> {
tracing::debug!("java.io.ByteArrayOutputStream::write({this:?}, {b:?})");

Expand DownExpand Up@@ -95,6 +124,22 @@ impl ByteArrayOutputStream {
Ok(pos)
}

async fn to_string(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<ClassInstanceRef<String>> {
tracing::debug!("java.io.ByteArrayOutputStream::toString({this:?})");
let buf: ClassInstanceRef<Array<i8>> = jvm.get_field(&this, "buf", "[B").await?;
let pos: i32 = jvm.get_field(&this, "pos", "I").await?;
let bytes = jvm.instantiate_array("B", pos as usize).await?;
let _: () = jvm
.invoke_static(
"java/lang/System",
"arraycopy",
"(Ljava/lang/Object;ILjava/lang/Object;II)V",
(buf, 0, bytes.clone(), 0, pos),
)
.await?;
Ok(jvm.new_class("java/lang/String", "([B)V", (bytes,)).await?.into())
}

async fn reset(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef<Self>) -> Result<()> {
tracing::debug!("java.io.ByteArrayOutputStream::reset({this:?})");

Expand Down
1 change: 1 addition & 0 deletions java_runtime/src/classes/java/io/data_input.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@ impl DataInput {
JavaMethodProto::new_abstract("readInt", "()I", Default::default()),
JavaMethodProto::new_abstract("readLong", "()J", Default::default()),
JavaMethodProto::new_abstract("readShort", "()S", Default::default()),
JavaMethodProto::new_abstract("readUnsignedByte", "()I", Default::default()),
JavaMethodProto::new_abstract("readUnsignedShort", "()I", Default::default()),
JavaMethodProto::new_abstract("readUTF", "()Ljava/lang/String;", Default::default()),
JavaMethodProto::new_abstract("skipBytes", "(I)I", Default::default()),
Expand Down
Loading