|
1 | | -use alloc::{vec, vec::Vec}; |
| 1 | +use alloc::{boxed::Box, collections::BTreeMap, vec, vec::Vec}; |
2 | 2 |
|
| 3 | +use java_class_proto::{JavaFieldProto, JavaMethodProto}; |
3 | 4 | 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 | +} |
7 | 74 |
|
8 | 75 | #[tokio::test] |
9 | 76 | async fn test_isr() -> Result<()> { |
@@ -55,6 +122,33 @@ async fn test_input_stream_reader_preserves_split_multibyte_and_buffered_eof() - |
55 | 122 | Ok(()) |
56 | 123 | } |
57 | 124 |
|
| 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 | + |
58 | 152 | #[tokio::test] |
59 | 153 | async fn test_input_stream_reader_rejects_unknown_encoding() -> Result<()> { |
60 | 154 | let jvm = test_jvm().await?; |
|
0 commit comments