Skip to content

Commit af4f6f8

Browse files
authored
Add CLDC 1.1 core API compatibility (#177)
* Add CLDC 1.1 core API compatibility * Fix CI lint and improve CLDC coverage * Fix array assignability and reader progress
1 parent 1f356ae commit af4f6f8

62 files changed

Lines changed: 3245 additions & 366 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

java_runtime/src/classes/java/io.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,28 @@ mod filter_input_stream;
1515
mod filter_output_stream;
1616
mod input_stream;
1717
mod input_stream_reader;
18+
mod interrupted_io_exception;
1819
mod io_exception;
1920
mod output_stream;
21+
mod output_stream_writer;
2022
mod print_stream;
2123
mod print_writer;
2224
mod random_access_file;
2325
mod reader;
2426
mod serializable;
2527
mod string_writer;
28+
mod unsupported_encoding_exception;
29+
mod utf_data_format_exception;
2630
mod writer;
2731

2832
pub use self::{
2933
buffered_reader::BufferedReader, byte_array_input_stream::ByteArrayInputStream, byte_array_output_stream::ByteArrayOutputStream,
3034
data_input::DataInput, data_input_stream::DataInputStream, data_output::DataOutput, data_output_stream::DataOutputStream,
3135
eof_exception::EOFException, file::File, file_descriptor::FileDescriptor, file_input_stream::FileInputStream,
3236
file_not_found_exception::FileNotFoundException, file_output_stream::FileOutputStream, filter_input_stream::FilterInputStream,
33-
filter_output_stream::FilterOutputStream, input_stream::InputStream, input_stream_reader::InputStreamReader, io_exception::IOException,
34-
output_stream::OutputStream, print_stream::PrintStream, print_writer::PrintWriter, random_access_file::RandomAccessFile, reader::Reader,
35-
serializable::Serializable, string_writer::StringWriter, writer::Writer,
37+
filter_output_stream::FilterOutputStream, input_stream::InputStream, input_stream_reader::InputStreamReader,
38+
interrupted_io_exception::InterruptedIOException, io_exception::IOException, output_stream::OutputStream,
39+
output_stream_writer::OutputStreamWriter, print_stream::PrintStream, print_writer::PrintWriter, random_access_file::RandomAccessFile,
40+
reader::Reader, serializable::Serializable, string_writer::StringWriter, unsupported_encoding_exception::UnsupportedEncodingException,
41+
utf_data_format_exception::UTFDataFormatException, writer::Writer,
3642
};

java_runtime/src/classes/java/io/byte_array_input_stream.rs

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ impl ByteArrayInputStream {
2424
JavaMethodProto::new("skip", "(J)J", Self::skip, Default::default()),
2525
JavaMethodProto::new("mark", "(I)V", Self::mark, Default::default()),
2626
JavaMethodProto::new("reset", "()V", Self::reset, Default::default()),
27+
JavaMethodProto::new("markSupported", "()Z", Self::mark_supported, Default::default()),
2728
],
2829
fields: vec![
2930
JavaFieldProto::new("buf", "[B", Default::default()),
@@ -57,11 +58,17 @@ impl ByteArrayInputStream {
5758
) -> Result<()> {
5859
tracing::debug!("java.io.ByteArrayInputStream::<init>({this:?}, {data:?}, {offset}, {length})");
5960

61+
let data_length = jvm.array_length(&data).await? as i32;
62+
if offset < 0 || length < 0 || offset > data_length {
63+
return Err(jvm.exception("java/lang/IndexOutOfBoundsException", "Invalid offset or length").await);
64+
}
65+
6066
let _: () = jvm.invoke_special(&this, "java/io/InputStream", "<init>", "()V", ()).await?;
6167

6268
jvm.put_field(&mut this, "buf", "[B", data).await?;
6369
jvm.put_field(&mut this, "pos", "I", offset).await?;
64-
jvm.put_field(&mut this, "count", "I", length).await?;
70+
jvm.put_field(&mut this, "count", "I", (offset + length).min(data_length)).await?;
71+
jvm.put_field(&mut this, "mark", "I", offset).await?;
6572

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

88-
let buf = jvm.get_field(&this, "buf", "[B").await?;
89-
let buf_length = jvm.array_length(&buf).await?;
95+
let buf: ClassInstanceRef<Array<i8>> = jvm.get_field(&this, "buf", "[B").await?;
96+
let count: i32 = jvm.get_field(&this, "count", "I").await?;
9097
let pos: i32 = jvm.get_field(&this, "pos", "I").await?;
9198

92-
let available = (buf_length as i32 - pos) as _;
93-
let len_to_read = if len > available { available } else { len };
94-
if len_to_read == 0 {
99+
let target_length = jvm.array_length(&b).await? as i32;
100+
if off < 0 || len < 0 || off > target_length - len {
101+
return Err(jvm.exception("java/lang/IndexOutOfBoundsException", "Invalid offset or length").await);
102+
}
103+
if len == 0 {
104+
return Ok(0);
105+
}
106+
107+
let available = count - pos;
108+
if available <= 0 {
95109
return Ok(-1);
96110
}
111+
let len_to_read = if len > available { available } else { len };
97112

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

115130
let buf = jvm.get_field(&this, "buf", "[B").await?;
116-
let buf_length = jvm.array_length(&buf).await?;
131+
let count: i32 = jvm.get_field(&this, "count", "I").await?;
117132
let pos: i32 = jvm.get_field(&this, "pos", "I").await?;
118133

119-
if pos as usize >= buf_length {
134+
if pos >= count {
120135
return Ok(-1);
121136
}
122137

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

139-
let buf = jvm.get_field(&this, "buf", "[B").await?;
140-
let buf_length = jvm.array_length(&buf).await?;
154+
let count: i32 = jvm.get_field(&this, "count", "I").await?;
141155
let pos: i32 = jvm.get_field(&this, "pos", "I").await?;
142156

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

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

@@ -165,4 +179,9 @@ impl ByteArrayInputStream {
165179

166180
Ok(())
167181
}
182+
183+
async fn mark_supported(_: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<bool> {
184+
tracing::debug!("java.io.ByteArrayInputStream::markSupported({this:?})");
185+
Ok(true)
186+
}
168187
}

java_runtime/src/classes/java/io/byte_array_output_stream.rs

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
use alloc::vec;
1+
use alloc::{vec, vec::Vec};
22

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

6-
use crate::{RuntimeClassProto, RuntimeContext};
6+
use crate::{RuntimeClassProto, RuntimeContext, classes::java::lang::String};
77

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

49+
if size < 0 {
50+
return Err(jvm.exception("java/lang/IllegalArgumentException", "Negative initial size").await);
51+
}
52+
4753
let _: () = jvm.invoke_special(&this, "java/io/OutputStream", "<init>", "()V", ()).await?;
4854

49-
let array = jvm.instantiate_array("B", 1024).await?;
55+
let array = jvm.instantiate_array("B", size as usize).await?;
5056

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

5460
Ok(())
5561
}
5662

63+
async fn write_bytes(
64+
jvm: &Jvm,
65+
_: &mut RuntimeContext,
66+
mut this: ClassInstanceRef<Self>,
67+
bytes: ClassInstanceRef<Array<i8>>,
68+
off: i32,
69+
len: i32,
70+
) -> Result<()> {
71+
tracing::debug!("java.io.ByteArrayOutputStream::write({this:?}, {bytes:?}, {off}, {len})");
72+
73+
let length = jvm.array_length(&bytes).await? as i32;
74+
if off < 0 || len < 0 || off > length - len {
75+
return Err(jvm.exception("java/lang/IndexOutOfBoundsException", "Invalid offset or length").await);
76+
}
77+
78+
let pos: i32 = jvm.get_field(&this, "pos", "I").await?;
79+
Self::ensure_capacity(jvm, &mut this, (pos + len) as usize).await?;
80+
let mut buf = jvm.get_field(&this, "buf", "[B").await?;
81+
let values: Vec<i8> = jvm.load_array(&bytes, off as usize, len as usize).await?;
82+
jvm.store_array(&mut buf, pos as usize, values).await?;
83+
jvm.put_field(&mut this, "pos", "I", pos + len).await
84+
}
85+
5786
async fn write(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef<Self>, b: i32) -> Result<()> {
5887
tracing::debug!("java.io.ByteArrayOutputStream::write({this:?}, {b:?})");
5988

@@ -95,6 +124,22 @@ impl ByteArrayOutputStream {
95124
Ok(pos)
96125
}
97126

127+
async fn to_string(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<ClassInstanceRef<String>> {
128+
tracing::debug!("java.io.ByteArrayOutputStream::toString({this:?})");
129+
let buf: ClassInstanceRef<Array<i8>> = jvm.get_field(&this, "buf", "[B").await?;
130+
let pos: i32 = jvm.get_field(&this, "pos", "I").await?;
131+
let bytes = jvm.instantiate_array("B", pos as usize).await?;
132+
let _: () = jvm
133+
.invoke_static(
134+
"java/lang/System",
135+
"arraycopy",
136+
"(Ljava/lang/Object;ILjava/lang/Object;II)V",
137+
(buf, 0, bytes.clone(), 0, pos),
138+
)
139+
.await?;
140+
Ok(jvm.new_class("java/lang/String", "([B)V", (bytes,)).await?.into())
141+
}
142+
98143
async fn reset(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef<Self>) -> Result<()> {
99144
tracing::debug!("java.io.ByteArrayOutputStream::reset({this:?})");
100145

java_runtime/src/classes/java/io/data_input.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ impl DataInput {
2424
JavaMethodProto::new_abstract("readInt", "()I", Default::default()),
2525
JavaMethodProto::new_abstract("readLong", "()J", Default::default()),
2626
JavaMethodProto::new_abstract("readShort", "()S", Default::default()),
27+
JavaMethodProto::new_abstract("readUnsignedByte", "()I", Default::default()),
2728
JavaMethodProto::new_abstract("readUnsignedShort", "()I", Default::default()),
2829
JavaMethodProto::new_abstract("readUTF", "()Ljava/lang/String;", Default::default()),
2930
JavaMethodProto::new_abstract("skipBytes", "(I)I", Default::default()),

0 commit comments

Comments
 (0)