Skip to content

Commit ad52cbc

Browse files
committed
Fix array assignability and reader progress
1 parent 1292ea8 commit ad52cbc

5 files changed

Lines changed: 146 additions & 15 deletions

File tree

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,9 @@ impl InputStreamReader {
120120
return Ok(0);
121121
}
122122

123-
let write_buf_size: i32 = jvm.get_field(&this, "writeBufSize", "I").await?;
123+
let mut write_buf_size: i32 = jvm.get_field(&this, "writeBufSize", "I").await?;
124124

125-
if write_buf_size < length {
125+
while write_buf_size < length {
126126
let read_buf: ClassInstanceRef<Array<i8>> = jvm.get_field(&this, "readBuf", "[B").await?;
127127
let read_buf_size: i32 = jvm.get_field(&this, "readBufSize", "I").await?;
128128

@@ -202,14 +202,18 @@ impl InputStreamReader {
202202

203203
// add to writeBuf
204204
let mut write_buf = jvm.get_field(&this, "writeBuf", "[C").await?;
205-
let write_buf_size: i32 = jvm.get_field(&this, "writeBufSize", "I").await?;
205+
let buffered_chars: i32 = jvm.get_field(&this, "writeBufSize", "I").await?;
206206
jvm.store_array(
207207
&mut write_buf,
208-
write_buf_size as _,
208+
buffered_chars as _,
209209
cast_slice::<u16, JavaChar>(&decoded[..wrote]).to_vec(),
210210
)
211211
.await?;
212-
jvm.put_field(&mut this, "writeBufSize", "I", write_buf_size + wrote as i32).await?;
212+
write_buf_size = buffered_chars + wrote as i32;
213+
jvm.put_field(&mut this, "writeBufSize", "I", write_buf_size).await?;
214+
if write_buf_size > 0 {
215+
break;
216+
}
213217
}
214218

215219
let write_buf: ClassInstanceRef<Array<JavaChar>> = jvm.get_field(&this, "writeBuf", "[C").await?;

java_runtime/src/classes/java/lang/class.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use alloc::vec;
33
use java_class_proto::{JavaFieldProto, JavaMethodProto};
44
use java_constants::{ClassAccessFlags, MethodAccessFlags};
55
use jvm::{
6-
ClassInstanceRef, Jvm, Result,
6+
ClassInstanceRef, JavaType, Jvm, Result,
77
runtime::{JavaLangClass, JavaLangClassLoader, JavaLangString},
88
};
99

@@ -170,10 +170,7 @@ impl Class {
170170
return Ok(class_name == other_name);
171171
}
172172

173-
let rust_class = JavaLangClass::to_rust_class(jvm, &this).await?;
174-
let other_rust_class = JavaLangClass::to_rust_class(jvm, &other).await?;
175-
176-
Ok(jvm.is_inherited_from(&*other_rust_class, &rust_class.name()))
173+
Ok(jvm.is_type_assignable(&JavaType::from_class_name(&other_name), &JavaType::from_class_name(&class_name)))
177174
}
178175

179176
async fn get_resource_as_stream(

java_runtime/tests/classes/java/io/test_input_stream_reader.rs

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,76 @@
1-
use alloc::{vec, vec::Vec};
1+
use alloc::{boxed::Box, collections::BTreeMap, vec, vec::Vec};
22

3+
use java_class_proto::{JavaFieldProto, JavaMethodProto};
34
use java_runtime::classes::java::lang::Object;
4-
use jvm::{ClassInstanceRef, JavaChar, JavaError, Result, runtime::JavaLangString};
5-
6-
use test_utils::test_jvm;
5+
use java_runtime::{RuntimeClassProto, RuntimeContext};
6+
use jvm::{Array, ClassInstanceRef, JavaChar, JavaError, Jvm, Result, runtime::JavaLangString};
7+
use jvm_rust::ClassDefinitionImpl;
8+
9+
use test_utils::{TestRuntime, create_test_jvm, test_jvm};
10+
11+
struct OneByteInputStream;
12+
13+
impl OneByteInputStream {
14+
fn as_proto() -> RuntimeClassProto {
15+
RuntimeClassProto {
16+
name: "OneByteInputStream",
17+
parent_class: Some("java/io/InputStream"),
18+
interfaces: vec![],
19+
methods: vec![
20+
JavaMethodProto::new("<init>", "([B)V", Self::init, Default::default()),
21+
JavaMethodProto::new("read", "()I", Self::read, Default::default()),
22+
JavaMethodProto::new("read", "([BII)I", Self::read_offset_length, Default::default()),
23+
],
24+
fields: vec![
25+
JavaFieldProto::new("data", "[B", Default::default()),
26+
JavaFieldProto::new("position", "I", Default::default()),
27+
],
28+
access_flags: Default::default(),
29+
}
30+
}
31+
32+
async fn init(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef<Self>, data: ClassInstanceRef<Array<i8>>) -> Result<()> {
33+
let _: () = jvm.invoke_special(&this, "java/io/InputStream", "<init>", "()V", ()).await?;
34+
jvm.put_field(&mut this, "data", "[B", data).await?;
35+
jvm.put_field(&mut this, "position", "I", 0).await?;
36+
37+
Ok(())
38+
}
39+
40+
async fn read(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef<Self>) -> Result<i32> {
41+
let data: ClassInstanceRef<Array<i8>> = jvm.get_field(&this, "data", "[B").await?;
42+
let position: i32 = jvm.get_field(&this, "position", "I").await?;
43+
if position == jvm.array_length(&data).await? as i32 {
44+
return Ok(-1);
45+
}
46+
47+
let value = jvm.load_array::<i8>(&data, position as usize, 1).await?[0];
48+
jvm.put_field(&mut this, "position", "I", position + 1).await?;
49+
50+
Ok(value as u8 as i32)
51+
}
52+
53+
async fn read_offset_length(
54+
jvm: &Jvm,
55+
_: &mut RuntimeContext,
56+
this: ClassInstanceRef<Self>,
57+
mut target: ClassInstanceRef<Array<i8>>,
58+
offset: i32,
59+
length: i32,
60+
) -> Result<i32> {
61+
if length == 0 {
62+
return Ok(0);
63+
}
64+
65+
let value: i32 = jvm.invoke_virtual(&this, "read", "()I", ()).await?;
66+
if value == -1 {
67+
return Ok(-1);
68+
}
69+
70+
jvm.store_array(&mut target, offset as usize, [value as i8]).await?;
71+
Ok(1)
72+
}
73+
}
774

875
#[tokio::test]
976
async fn test_isr() -> Result<()> {
@@ -55,6 +122,33 @@ async fn test_input_stream_reader_preserves_split_multibyte_and_buffered_eof() -
55122
Ok(())
56123
}
57124

125+
#[tokio::test]
126+
async fn test_input_stream_reader_does_not_return_zero_for_split_multibyte_input() -> Result<()> {
127+
let runtime = TestRuntime::new(BTreeMap::new());
128+
let jvm = create_test_jvm(runtime.clone()).await?;
129+
jvm.register_class(
130+
Box::new(ClassDefinitionImpl::from_class_proto(
131+
OneByteInputStream::as_proto(),
132+
Box::new(runtime) as Box<_>,
133+
)),
134+
None,
135+
)
136+
.await?;
137+
138+
let value = "한";
139+
let mut bytes = jvm.instantiate_array("B", value.len()).await?;
140+
jvm.store_array(&mut bytes, 0, value.as_bytes().iter().map(|byte| *byte as i8)).await?;
141+
let input = jvm.new_class("OneByteInputStream", "([B)V", (bytes,)).await?;
142+
let reader = jvm.new_class("java/io/InputStreamReader", "(Ljava/io/InputStream;)V", (input,)).await?;
143+
let chars = jvm.instantiate_array("C", 1).await?;
144+
145+
assert_eq!(jvm.invoke_virtual::<_, i32>(&reader, "read", "([CII)I", (chars.clone(), 0, 1)).await?, 1);
146+
assert_eq!(jvm.load_array::<JavaChar>(&chars, 0, 1).await?, ['한' as JavaChar]);
147+
assert_eq!(jvm.invoke_virtual::<_, i32>(&reader, "read", "([CII)I", (chars, 0, 1)).await?, -1);
148+
149+
Ok(())
150+
}
151+
58152
#[tokio::test]
59153
async fn test_input_stream_reader_rejects_unknown_encoding() -> Result<()> {
60154
let jvm = test_jvm().await?;

java_runtime/tests/classes/java/lang/test_class.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,42 @@ async fn test_is_assignable_from() -> Result<()> {
4141
.await?;
4242
assert!(!result);
4343

44+
let string_array_class = jvm.resolve_class("[Ljava/lang/String;").await?.java_class();
45+
let object_array_class = jvm.resolve_class("[Ljava/lang/Object;").await?.java_class();
46+
let cloneable_class = jvm.resolve_class("java/lang/Cloneable").await?.java_class();
47+
let serializable_class = jvm.resolve_class("java/io/Serializable").await?.java_class();
48+
49+
assert!(
50+
jvm.invoke_virtual::<_, bool>(
51+
&object_array_class,
52+
"isAssignableFrom",
53+
"(Ljava/lang/Class;)Z",
54+
(string_array_class.clone(),),
55+
)
56+
.await?
57+
);
58+
assert!(
59+
!jvm.invoke_virtual::<_, bool>(&string_array_class, "isAssignableFrom", "(Ljava/lang/Class;)Z", (object_array_class,),)
60+
.await?
61+
);
62+
assert!(
63+
jvm.invoke_virtual::<_, bool>(&object_class, "isAssignableFrom", "(Ljava/lang/Class;)Z", (string_array_class.clone(),),)
64+
.await?
65+
);
66+
assert!(
67+
jvm.invoke_virtual::<_, bool>(
68+
&cloneable_class,
69+
"isAssignableFrom",
70+
"(Ljava/lang/Class;)Z",
71+
(string_array_class.clone(),),
72+
)
73+
.await?
74+
);
75+
assert!(
76+
jvm.invoke_virtual::<_, bool>(&serializable_class, "isAssignableFrom", "(Ljava/lang/Class;)Z", (string_array_class,),)
77+
.await?
78+
);
79+
4480
Ok(())
4581
}
4682

jvm/src/jvm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ impl Jvm {
631631
}
632632

633633
// JVMS 4.10.3 subtyping, including array covariance
634-
fn is_type_assignable(&self, source: &JavaType, target: &JavaType) -> bool {
634+
pub fn is_type_assignable(&self, source: &JavaType, target: &JavaType) -> bool {
635635
if source == target {
636636
return true;
637637
}

0 commit comments

Comments
 (0)