diff --git a/java_runtime/src/classes/java/io/file.rs b/java_runtime/src/classes/java/io/file.rs index af938fc2..d3343d8a 100644 --- a/java_runtime/src/classes/java/io/file.rs +++ b/java_runtime/src/classes/java/io/file.rs @@ -59,12 +59,7 @@ impl File { let path = jvm.invoke_virtual(&this, "getPath", "()Ljava/lang/String;", ()).await?; let path = JavaLangString::to_rust_string(jvm, &path).await?; - let stat = context.metadata(&path).await; - if stat.is_err() { - return Ok(false); - } - - Ok(stat.unwrap().r#type == FileType::Directory) + Ok(context.metadata(&path).await.is_ok_and(|x| x.r#type == FileType::Directory)) } async fn is_file(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef) -> Result { @@ -73,12 +68,7 @@ impl File { let path = jvm.invoke_virtual(&this, "getPath", "()Ljava/lang/String;", ()).await?; let path = JavaLangString::to_rust_string(jvm, &path).await?; - let stat = context.metadata(&path).await; - if stat.is_err() { - return Ok(false); - } - - Ok(stat.unwrap().r#type == FileType::File) + Ok(context.metadata(&path).await.is_ok_and(|x| x.r#type == FileType::File)) } async fn delete(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef) -> Result { @@ -96,8 +86,7 @@ impl File { let path = jvm.invoke_virtual(&this, "getPath", "()Ljava/lang/String;", ()).await?; let path = JavaLangString::to_rust_string(jvm, &path).await?; - let stat = context.metadata(&path).await.unwrap(); - - Ok(stat.size as _) + // File.length() is 0 when the file does not exist + Ok(context.metadata(&path).await.map_or(0, |x| x.size as _)) } } diff --git a/java_runtime/src/classes/java/io/file_input_stream.rs b/java_runtime/src/classes/java/io/file_input_stream.rs index 9a111c88..8b923f07 100644 --- a/java_runtime/src/classes/java/io/file_input_stream.rs +++ b/java_runtime/src/classes/java/io/file_input_stream.rs @@ -79,8 +79,12 @@ impl FileInputStream { let rust_file = FileDescriptor::file(jvm, context, fd).await?; // TODO get os buffer size - let stat = rust_file.metadata().await.unwrap(); - let tell = rust_file.tell().await.unwrap(); + let Ok(stat) = rust_file.metadata().await else { + return Err(jvm.exception("java/io/IOException", "I/O error").await); + }; + let Ok(tell) = rust_file.tell().await else { + return Err(jvm.exception("java/io/IOException", "I/O error").await); + }; let available = stat.size - tell; @@ -101,7 +105,9 @@ impl FileInputStream { let mut rust_file = FileDescriptor::file(jvm, context, fd).await?; let mut rust_buf = vec![0; length as _]; - let read = rust_file.read(&mut rust_buf).await.unwrap(); + let Ok(read) = rust_file.read(&mut rust_buf).await else { + return Err(jvm.exception("java/io/IOException", "I/O error").await); + }; if read == 0 { return Ok(-1); } @@ -118,7 +124,9 @@ impl FileInputStream { let mut rust_file = FileDescriptor::file(jvm, context, fd).await?; let mut buf = [0; 1]; - let read = rust_file.read(&mut buf).await.unwrap(); + let Ok(read) = rust_file.read(&mut buf).await else { + return Err(jvm.exception("java/io/IOException", "I/O error").await); + }; if read == 0 { return Ok(-1); } diff --git a/java_runtime/src/classes/java/io/file_output_stream.rs b/java_runtime/src/classes/java/io/file_output_stream.rs index 89045226..02b5fcc3 100644 --- a/java_runtime/src/classes/java/io/file_output_stream.rs +++ b/java_runtime/src/classes/java/io/file_output_stream.rs @@ -42,8 +42,12 @@ impl FileOutputStream { let path = jvm.invoke_virtual(&file, "getPath", "()Ljava/lang/String;", ()).await?; let path = JavaLangString::to_rust_string(jvm, &path).await?; - let fd = context.open(&path, true).await.unwrap(); - let fd = FileDescriptor::from_fd(jvm, fd).await?; + let fd = context.open(&path, true).await; + if fd.is_err() { + return Err(jvm.exception("java/io/FileNotFoundException", "File not found").await); + } + + let fd = FileDescriptor::from_fd(jvm, fd.unwrap()).await?; let _: () = jvm .invoke_special(&this, "java/io/FileOutputStream", "", "(Ljava/io/FileDescriptor;)V", (fd,)) @@ -81,9 +85,11 @@ impl FileOutputStream { let mut file = FileDescriptor::file(jvm, context, fd).await?; let mut buf = vec![0; length as _]; - jvm.array_raw_buffer(&buffer).await?.read(offset as _, &mut buf).unwrap(); + jvm.array_raw_buffer(&buffer).await?.read(offset as _, &mut buf)?; - file.write(cast_slice(&buf)).await.unwrap(); + if file.write(cast_slice(&buf)).await.is_err() { + return Err(jvm.exception("java/io/IOException", "I/O error").await); + } Ok(()) } @@ -94,7 +100,9 @@ impl FileOutputStream { let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; let mut file = FileDescriptor::file(jvm, context, fd).await?; - file.write(&[byte as u8]).await.unwrap(); + if file.write(&[byte as u8]).await.is_err() { + return Err(jvm.exception("java/io/IOException", "I/O error").await); + } Ok(()) } diff --git a/java_runtime/src/classes/java/io/input_stream_reader.rs b/java_runtime/src/classes/java/io/input_stream_reader.rs index 09c9763a..d210b9f1 100644 --- a/java_runtime/src/classes/java/io/input_stream_reader.rs +++ b/java_runtime/src/classes/java/io/input_stream_reader.rs @@ -103,7 +103,7 @@ impl InputStreamReader { let read_buf_size: i32 = jvm.get_field(&this, "readBufSize", "I").await?; 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(); + jvm.array_raw_buffer(&read_buf).await?.read(0, &mut read_buf_data)?; let charset_ref = jvm.get_field(&this, "charset", "Ljava/lang/String;").await?; let charset = JavaLangString::to_rust_string(jvm, &charset_ref).await?; diff --git a/java_runtime/src/classes/java/io/print_stream.rs b/java_runtime/src/classes/java/io/print_stream.rs index f611cbbb..aa0b5a29 100644 --- a/java_runtime/src/classes/java/io/print_stream.rs +++ b/java_runtime/src/classes/java/io/print_stream.rs @@ -110,7 +110,8 @@ impl PrintStream { async fn println_char(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef, char: JavaChar) -> Result<()> { tracing::debug!("java.io.PrintStream::println({this:?}, {char:?})"); - let char = char::from_u32(char as _).unwrap(); + // an unpaired surrogate is not a valid char; the JDK charset encoder replaces it with '?' + let char = char::from_u32(char as _).unwrap_or('?'); let java_string = JavaLangString::from_rust_string(jvm, &char.to_string()).await?; diff --git a/java_runtime/src/classes/java/io/random_access_file.rs b/java_runtime/src/classes/java/io/random_access_file.rs index 62b0291d..2defa325 100644 --- a/java_runtime/src/classes/java/io/random_access_file.rs +++ b/java_runtime/src/classes/java/io/random_access_file.rs @@ -114,7 +114,9 @@ impl RandomAccessFile { let mut rust_file = FileDescriptor::file(jvm, context, fd).await?; let mut rust_buf = vec![0; length as usize]; - let read = rust_file.read(&mut rust_buf).await.unwrap(); + let Ok(read) = rust_file.read(&mut rust_buf).await else { + return Err(jvm.exception("java/io/IOException", "I/O error").await); + }; jvm.array_raw_buffer_mut(&mut buf).await?.write(offset as _, &rust_buf)?; @@ -144,8 +146,10 @@ impl RandomAccessFile { let mut rust_file = FileDescriptor::file(jvm, context, fd).await?; let mut rust_buf = vec![0; length as usize]; - jvm.array_raw_buffer(&buf).await?.read(offset as _, &mut rust_buf).unwrap(); - rust_file.write(&cast_vec(rust_buf)).await.unwrap(); + jvm.array_raw_buffer(&buf).await?.read(offset as _, &mut rust_buf)?; + if rust_file.write(&cast_vec(rust_buf)).await.is_err() { + return Err(jvm.exception("java/io/IOException", "I/O error").await); + } Ok(()) } @@ -156,7 +160,9 @@ impl RandomAccessFile { let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; let mut rust_file = FileDescriptor::file(jvm, context, fd).await?; - rust_file.seek(pos as _).await.unwrap(); + if rust_file.seek(pos as _).await.is_err() { + return Err(jvm.exception("java/io/IOException", "I/O error").await); + } Ok(()) } @@ -167,7 +173,9 @@ impl RandomAccessFile { let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; let mut rust_file = FileDescriptor::file(jvm, context, fd).await?; - rust_file.set_len(new_length as _).await.unwrap(); + if rust_file.set_len(new_length as _).await.is_err() { + return Err(jvm.exception("java/io/IOException", "I/O error").await); + } Ok(()) } @@ -178,7 +186,10 @@ impl RandomAccessFile { let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; let rust_file = FileDescriptor::file(jvm, context, fd).await?; - let len = rust_file.metadata().await.unwrap().size; + let Ok(metadata) = rust_file.metadata().await else { + return Err(jvm.exception("java/io/IOException", "I/O error").await); + }; + let len = metadata.size; Ok(len as i64) } @@ -189,7 +200,9 @@ impl RandomAccessFile { let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; let rust_file = FileDescriptor::file(jvm, context, fd).await?; - let pos = rust_file.tell().await.unwrap(); + let Ok(pos) = rust_file.tell().await else { + return Err(jvm.exception("java/io/IOException", "I/O error").await); + }; Ok(pos as i64) } diff --git a/java_runtime/src/classes/java/lang.rs b/java_runtime/src/classes/java/lang.rs index 67ae16d2..416ca554 100644 --- a/java_runtime/src/classes/java/lang.rs +++ b/java_runtime/src/classes/java/lang.rs @@ -5,6 +5,7 @@ mod array_store_exception; mod class; mod class_cast_exception; mod class_loader; +mod class_not_found_exception; mod clone_not_supported_exception; mod cloneable; mod comparable; @@ -41,14 +42,14 @@ mod unsupported_operation_exception; pub use self::{ abstract_method_error::AbstractMethodError, arithmetic_exception::ArithmeticException, array_index_out_of_bounds_exception::ArrayIndexOutOfBoundsException, array_store_exception::ArrayStoreException, class::Class, - class_cast_exception::ClassCastException, class_loader::ClassLoader, clone_not_supported_exception::CloneNotSupportedException, - cloneable::Cloneable, comparable::Comparable, error::Error, exception::Exception, exception_in_initializer_error::ExceptionInInitializerError, - illegal_argument_exception::IllegalArgumentException, incompatible_class_change_error::IncompatibleClassChangeError, - index_out_of_bounds_exception::IndexOutOfBoundsException, instantiation_error::InstantiationError, integer::Integer, - interrupted_exception::InterruptedException, linkage_error::LinkageError, math::Math, negative_array_size_exception::NegativeArraySizeException, - no_class_def_found_error::NoClassDefFoundError, no_such_field_error::NoSuchFieldError, no_such_method_error::NoSuchMethodError, - null_pointer_exception::NullPointerException, number_format_exception::NumberFormatException, object::Object, runnable::Runnable, - runtime::Runtime, runtime_exception::RuntimeException, security_exception::SecurityException, string::String, string_buffer::StringBuffer, - string_index_out_of_bounds_exception::StringIndexOutOfBoundsException, system::System, thread::Thread, throwable::Throwable, - unsupported_operation_exception::UnsupportedOperationException, + class_cast_exception::ClassCastException, class_loader::ClassLoader, class_not_found_exception::ClassNotFoundException, + clone_not_supported_exception::CloneNotSupportedException, cloneable::Cloneable, comparable::Comparable, error::Error, exception::Exception, + exception_in_initializer_error::ExceptionInInitializerError, illegal_argument_exception::IllegalArgumentException, + incompatible_class_change_error::IncompatibleClassChangeError, index_out_of_bounds_exception::IndexOutOfBoundsException, + instantiation_error::InstantiationError, integer::Integer, interrupted_exception::InterruptedException, linkage_error::LinkageError, math::Math, + negative_array_size_exception::NegativeArraySizeException, no_class_def_found_error::NoClassDefFoundError, no_such_field_error::NoSuchFieldError, + no_such_method_error::NoSuchMethodError, null_pointer_exception::NullPointerException, number_format_exception::NumberFormatException, + object::Object, runnable::Runnable, runtime::Runtime, runtime_exception::RuntimeException, security_exception::SecurityException, string::String, + string_buffer::StringBuffer, string_index_out_of_bounds_exception::StringIndexOutOfBoundsException, system::System, thread::Thread, + throwable::Throwable, unsupported_operation_exception::UnsupportedOperationException, }; diff --git a/java_runtime/src/classes/java/lang/class.rs b/java_runtime/src/classes/java/lang/class.rs index a8c8e682..a2f62d50 100644 --- a/java_runtime/src/classes/java/lang/class.rs +++ b/java_runtime/src/classes/java/lang/class.rs @@ -103,8 +103,10 @@ impl Class { let rust_name = JavaLangString::to_rust_string(jvm, &name).await?; let qualified_name = rust_name.replace('.', "/"); - let class = jvm.get_class(&qualified_name).unwrap().java_class(); - Ok(class.into()) + match jvm.resolve_class(&qualified_name).await { + Ok(class) => Ok(class.java_class().into()), + Err(_) => Err(jvm.exception("java/lang/ClassNotFoundException", &rust_name).await), + } } } diff --git a/java_runtime/src/classes/java/lang/class_not_found_exception.rs b/java_runtime/src/classes/java/lang/class_not_found_exception.rs new file mode 100644 index 00000000..f4f48d30 --- /dev/null +++ b/java_runtime/src/classes/java/lang/class_not_found_exception.rs @@ -0,0 +1,43 @@ +use alloc::vec; + +use java_class_proto::JavaMethodProto; +use jvm::{ClassInstanceRef, Jvm, Result}; + +use crate::{RuntimeClassProto, RuntimeContext, classes::java::lang::String}; + +// class java.lang.ClassNotFoundException +pub struct ClassNotFoundException; + +impl ClassNotFoundException { + pub fn as_proto() -> RuntimeClassProto { + RuntimeClassProto { + name: "java/lang/ClassNotFoundException", + parent_class: Some("java/lang/Exception"), + interfaces: vec![], + methods: vec![ + JavaMethodProto::new("", "()V", Self::init, Default::default()), + JavaMethodProto::new("", "(Ljava/lang/String;)V", Self::init_with_message, Default::default()), + ], + fields: vec![], + access_flags: Default::default(), + } + } + + async fn init(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef) -> Result<()> { + tracing::debug!("java.lang.ClassNotFoundException::({this:?})"); + + let _: () = jvm.invoke_special(&this, "java/lang/Exception", "", "()V", ()).await?; + + Ok(()) + } + + async fn init_with_message(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef, message: ClassInstanceRef) -> Result<()> { + tracing::debug!("java.lang.ClassNotFoundException::({this:?}, {message:?})"); + + let _: () = jvm + .invoke_special(&this, "java/lang/Exception", "", "(Ljava/lang/String;)V", (message,)) + .await?; + + Ok(()) + } +} diff --git a/java_runtime/src/classes/java/lang/string.rs b/java_runtime/src/classes/java/lang/string.rs index 18fb9d0f..6c51f0cd 100644 --- a/java_runtime/src/classes/java/lang/string.rs +++ b/java_runtime/src/classes/java/lang/string.rs @@ -379,9 +379,11 @@ impl String { async fn value_of_char(jvm: &Jvm, _: &mut RuntimeContext, value: JavaChar) -> Result> { tracing::debug!("java.lang.String::valueOf({value})"); - let string = RustString::from_utf16(&[value]).unwrap(); + // build through [C so an unpaired surrogate is preserved + let mut chars = jvm.instantiate_array("C", 1).await?; + jvm.store_array(&mut chars, 0, [value]).await?; - Ok(JavaLangString::from_rust_string(jvm, &string).await?.into()) + Ok(jvm.new_class("java/lang/String", "([C)V", (chars,)).await?.into()) } async fn value_of_integer(jvm: &Jvm, _: &mut RuntimeContext, value: i32) -> Result> { diff --git a/java_runtime/src/classes/java/lang/string_buffer.rs b/java_runtime/src/classes/java/lang/string_buffer.rs index 960f3a2c..23efd99d 100644 --- a/java_runtime/src/classes/java/lang/string_buffer.rs +++ b/java_runtime/src/classes/java/lang/string_buffer.rs @@ -1,8 +1,4 @@ -use alloc::{ - string::{String as RustString, ToString}, - vec, - vec::Vec, -}; +use alloc::{string::ToString, vec, vec::Vec}; use java_class_proto::{JavaFieldProto, JavaMethodProto}; use jvm::{Array, ClassInstanceRef, JavaChar, Jvm, Result, runtime::JavaLangString}; @@ -162,9 +158,7 @@ impl StringBuffer { async fn append_character(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef, value: u16) -> Result> { tracing::debug!("java.lang.StringBuffer::append({this:?}, {value:?})"); - let value = RustString::from_utf16(&[value]).unwrap(); - - Self::append(jvm, &mut this, &value).await?; + Self::append_utf16(jvm, &mut this, vec![value]).await?; Ok(this) } @@ -180,9 +174,8 @@ impl StringBuffer { tracing::debug!("java.lang.StringBuffer::append({this:?}, {array:?}, {offset:?}, {length:?})"); let value: Vec = jvm.load_array(&array, offset as _, length as _).await?; - let string = RustString::from_utf16(&value).unwrap(); - Self::append(jvm, &mut this, &string).await?; + Self::append_utf16(jvm, &mut this, value).await?; Ok(this) } @@ -237,9 +230,12 @@ impl StringBuffer { } async fn append(jvm: &Jvm, this: &mut ClassInstanceRef, string: &str) -> Result<()> { + Self::append_utf16(jvm, this, string.encode_utf16().collect()).await + } + + async fn append_utf16(jvm: &Jvm, this: &mut ClassInstanceRef, value_to_add: Vec) -> Result<()> { let current_count: i32 = jvm.get_field(this, "count", "I").await?; - let value_to_add = string.encode_utf16().collect::>(); let count_to_add = value_to_add.len() as i32; StringBuffer::ensure_capacity(jvm, this, (current_count + count_to_add) as _).await?; diff --git a/java_runtime/src/classes/java/util/zip.rs b/java_runtime/src/classes/java/util/zip.rs index 1b44f678..0f442a04 100644 --- a/java_runtime/src/classes/java/util/zip.rs +++ b/java_runtime/src/classes/java/util/zip.rs @@ -1,5 +1,6 @@ mod zip_entry; +mod zip_exception; mod zip_file; mod zip_file_entries; -pub use {zip_entry::ZipEntry, zip_file::ZipFile, zip_file_entries::ZipFileEntries}; +pub use {zip_entry::ZipEntry, zip_exception::ZipException, zip_file::ZipFile, zip_file_entries::ZipFileEntries}; diff --git a/java_runtime/src/classes/java/util/zip/zip_exception.rs b/java_runtime/src/classes/java/util/zip/zip_exception.rs new file mode 100644 index 00000000..fffde9f5 --- /dev/null +++ b/java_runtime/src/classes/java/util/zip/zip_exception.rs @@ -0,0 +1,43 @@ +use alloc::vec; + +use java_class_proto::JavaMethodProto; +use jvm::{ClassInstanceRef, Jvm, Result}; + +use crate::{RuntimeClassProto, RuntimeContext, classes::java::lang::String}; + +// class java.util.zip.ZipException +pub struct ZipException; + +impl ZipException { + pub fn as_proto() -> RuntimeClassProto { + RuntimeClassProto { + name: "java/util/zip/ZipException", + parent_class: Some("java/io/IOException"), + interfaces: vec![], + methods: vec![ + JavaMethodProto::new("", "()V", Self::init, Default::default()), + JavaMethodProto::new("", "(Ljava/lang/String;)V", Self::init_with_message, Default::default()), + ], + fields: vec![], + access_flags: Default::default(), + } + } + + async fn init(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef) -> Result<()> { + tracing::debug!("java.util.zip.ZipException::({this:?})"); + + let _: () = jvm.invoke_special(&this, "java/io/IOException", "", "()V", ()).await?; + + Ok(()) + } + + async fn init_with_message(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef, message: ClassInstanceRef) -> Result<()> { + tracing::debug!("java.util.zip.ZipException::({this:?}, {message:?})"); + + let _: () = jvm + .invoke_special(&this, "java/io/IOException", "", "(Ljava/lang/String;)V", (message,)) + .await?; + + Ok(()) + } +} diff --git a/java_runtime/src/classes/java/util/zip/zip_file.rs b/java_runtime/src/classes/java/util/zip/zip_file.rs index 36afd213..23498a4d 100644 --- a/java_runtime/src/classes/java/util/zip/zip_file.rs +++ b/java_runtime/src/classes/java/util/zip/zip_file.rs @@ -53,8 +53,12 @@ impl ZipFile { let zip_data: ClassInstanceRef> = 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()) + jvm.array_raw_buffer(&zip_data).await?.read(0, &mut buf)?; + + match ZipArchive::new(Cursor::new(buf)) { + Ok(x) => Ok(x), + Err(err) => Err(jvm.exception("java/util/zip/ZipException", &err.to_string()).await), + } } async fn init(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef, file: ClassInstanceRef) -> Result<()> { @@ -70,6 +74,9 @@ impl ZipFile { jvm.put_field(&mut this, "zipData", "[B", buf).await?; + // the constructor throws ZipException for a malformed archive + let _ = Self::get_zip_archive(jvm, &this).await?; + Ok(()) } @@ -132,17 +139,22 @@ impl ZipFile { let data = { let mut zip = Self::get_zip_archive(jvm, &this).await?; - let mut file = zip.by_name(&entry_name).unwrap(); + let file = zip.by_name(&entry_name); + let Ok(mut file) = file else { + // getInputStream returns null when the entry is not in this zip + return Ok(None.into()); + }; let mut buf = Vec::new(); - file.read_to_end(&mut buf).unwrap(); - - buf + file.read_to_end(&mut buf).map(|_| buf) + }; + let Ok(data) = data else { + return Err(jvm.exception("java/util/zip/ZipException", "invalid entry data").await); }; // TODO do we have to use InflaterInputStream? let mut java_buf = jvm.instantiate_array("B", data.len() as _).await?; - jvm.array_raw_buffer_mut(&mut java_buf).await?.write(0, &data).unwrap(); + jvm.array_raw_buffer_mut(&mut java_buf).await?.write(0, &data)?; let input_stream = jvm.new_class("java/io/ByteArrayInputStream", "([B)V", (java_buf,)).await?; diff --git a/java_runtime/src/loader.rs b/java_runtime/src/loader.rs index c11457a8..cbd2fced 100644 --- a/java_runtime/src/loader.rs +++ b/java_runtime/src/loader.rs @@ -39,6 +39,7 @@ pub fn get_runtime_class_proto(name: &str) -> Option { crate::classes::java::lang::Class::as_proto(), crate::classes::java::lang::ClassCastException::as_proto(), crate::classes::java::lang::ClassLoader::as_proto(), + crate::classes::java::lang::ClassNotFoundException::as_proto(), crate::classes::java::lang::Cloneable::as_proto(), crate::classes::java::lang::CloneNotSupportedException::as_proto(), crate::classes::java::lang::Comparable::as_proto(), @@ -130,6 +131,7 @@ pub fn get_runtime_class_proto(name: &str) -> Option { crate::classes::java::util::jar::JarFileEntries::as_proto(), crate::classes::java::util::jar::Manifest::as_proto(), crate::classes::java::util::zip::ZipEntry::as_proto(), + crate::classes::java::util::zip::ZipException::as_proto(), crate::classes::java::util::zip::ZipFile::as_proto(), crate::classes::java::util::zip::ZipFileEntries::as_proto(), crate::classes::org::rustjava::net::FileURLConnection::as_proto(), diff --git a/java_runtime/src/runtime/io.rs b/java_runtime/src/runtime/io.rs index 9d7ffd24..9b460876 100644 --- a/java_runtime/src/runtime/io.rs +++ b/java_runtime/src/runtime/io.rs @@ -19,6 +19,7 @@ impl FileDescriptorId { pub enum IOError { Unsupported, NotFound, + Io, } pub type IOResult = Result; diff --git a/src/runtime.rs b/src/runtime.rs index 649fd6e7..921baa4c 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -130,8 +130,8 @@ where } async fn open(&self, path: &str, write: bool) -> IOResult { - let file = Box::new(FileImpl::new(path, write)); - Ok(self.register_file(file)) + let file = FileImpl::new(path, write).map_err(|_| IOError::NotFound)?; + Ok(self.register_file(Box::new(file))) } fn get_file(&self, fd: FileDescriptorId) -> IOResult> { diff --git a/src/runtime/io.rs b/src/runtime/io.rs index 6567c092..61243625 100644 --- a/src/runtime/io.rs +++ b/src/runtime/io.rs @@ -35,7 +35,7 @@ where } async fn write(&mut self, buf: &[u8]) -> IOResult { - let written = self.write.lock().unwrap().write(buf).unwrap(); + let written = self.write.lock().unwrap().write(buf).map_err(|_| IOError::Io)?; Ok(written) } @@ -90,7 +90,7 @@ where R: Read + Send + Sync + 'static, { async fn read(&mut self, buf: &mut [u8]) -> IOResult { - let read = self.read.lock().unwrap().read(buf).unwrap(); + let read = self.read.lock().unwrap().read(buf).map_err(|_| IOError::Io)?; Ok(read) } @@ -131,50 +131,50 @@ pub struct FileImpl { } impl FileImpl { - pub fn new(path: &str, write: bool) -> Self { + pub fn new(path: &str, write: bool) -> io::Result { let mut options = OpenOptions::new(); - let file = options.read(true).write(write).create(write).open(path).unwrap(); + let file = options.read(true).write(write).create(write).open(path)?; - Self { + Ok(Self { file: Arc::new(Mutex::new(file)), - } + }) } } #[async_trait::async_trait] impl File for FileImpl { async fn read(&mut self, buf: &mut [u8]) -> Result { - let read = self.file.lock().unwrap().read(buf).unwrap(); + let read = self.file.lock().unwrap().read(buf).map_err(|_| IOError::Io)?; Ok(read) } async fn write(&mut self, buf: &[u8]) -> Result { - let write = self.file.lock().unwrap().write(buf).unwrap(); + let write = self.file.lock().unwrap().write(buf).map_err(|_| IOError::Io)?; Ok(write) } async fn seek(&mut self, pos: FileSize) -> Result<(), IOError> { - self.file.lock().unwrap().seek(io::SeekFrom::Start(pos)).unwrap(); + self.file.lock().unwrap().seek(io::SeekFrom::Start(pos)).map_err(|_| IOError::Io)?; Ok(()) } async fn tell(&self) -> Result { - let pos = self.file.lock().unwrap().seek(io::SeekFrom::Current(0)).unwrap(); + let pos = self.file.lock().unwrap().seek(io::SeekFrom::Current(0)).map_err(|_| IOError::Io)?; Ok(pos as FileSize) } async fn set_len(&mut self, len: FileSize) -> Result<(), IOError> { - self.file.lock().unwrap().set_len(len).unwrap(); + self.file.lock().unwrap().set_len(len).map_err(|_| IOError::Io)?; Ok(()) } async fn metadata(&self) -> Result { - let metadata = self.file.lock().unwrap().metadata().unwrap(); + let metadata = self.file.lock().unwrap().metadata().map_err(|_| IOError::Io)?; let size = metadata.len(); Ok(FileStat { diff --git a/test_data/FileErrors.class b/test_data/FileErrors.class new file mode 100644 index 00000000..b137b9e2 Binary files /dev/null and b/test_data/FileErrors.class differ diff --git a/test_data/FileErrors.txt b/test_data/FileErrors.txt new file mode 100644 index 00000000..1ef96a68 --- /dev/null +++ b/test_data/FileErrors.txt @@ -0,0 +1,3 @@ +fnf-in +fnf-out +fnf-raf diff --git a/test_data/FileLength.class b/test_data/FileLength.class new file mode 100644 index 00000000..5b0cd01b Binary files /dev/null and b/test_data/FileLength.class differ diff --git a/test_data/FileLength.txt b/test_data/FileLength.txt new file mode 100644 index 00000000..e05b13f5 --- /dev/null +++ b/test_data/FileLength.txt @@ -0,0 +1,2 @@ +false +0 diff --git a/test_data/ForName.class b/test_data/ForName.class new file mode 100644 index 00000000..86295c2f Binary files /dev/null and b/test_data/ForName.class differ diff --git a/test_data/ForName.txt b/test_data/ForName.txt new file mode 100644 index 00000000..f4c76f60 --- /dev/null +++ b/test_data/ForName.txt @@ -0,0 +1,2 @@ +java.util.Vector +cnfe diff --git a/test_data/SurrogateChars.class b/test_data/SurrogateChars.class new file mode 100644 index 00000000..4d82419f Binary files /dev/null and b/test_data/SurrogateChars.class differ diff --git a/test_data/SurrogateChars.txt b/test_data/SurrogateChars.txt new file mode 100644 index 00000000..17e48d32 --- /dev/null +++ b/test_data/SurrogateChars.txt @@ -0,0 +1,6 @@ +2 +😀 +1 +1 +? +2 diff --git a/test_data/ZipCorrupt.class b/test_data/ZipCorrupt.class new file mode 100644 index 00000000..37b7442a Binary files /dev/null and b/test_data/ZipCorrupt.class differ diff --git a/test_data/ZipCorrupt.txt b/test_data/ZipCorrupt.txt new file mode 100644 index 00000000..ad53f2c8 --- /dev/null +++ b/test_data/ZipCorrupt.txt @@ -0,0 +1 @@ +zipex diff --git a/test_data/src/FileErrors.java b/test_data/src/FileErrors.java new file mode 100644 index 00000000..c063027e --- /dev/null +++ b/test_data/src/FileErrors.java @@ -0,0 +1,28 @@ +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.RandomAccessFile; + +public class FileErrors { + public static void main(String[] args) { + try { + new FileInputStream(new File("definitely-missing-file")); + System.out.println("no-ex"); + } catch (FileNotFoundException e) { + System.out.println("fnf-in"); + } + try { + new FileOutputStream(new File("definitely-missing-dir/x")); + System.out.println("no-ex"); + } catch (FileNotFoundException e) { + System.out.println("fnf-out"); + } + try { + new RandomAccessFile("definitely-missing-file", "r"); + System.out.println("no-ex"); + } catch (FileNotFoundException e) { + System.out.println("fnf-raf"); + } + } +} diff --git a/test_data/src/FileLength.java b/test_data/src/FileLength.java new file mode 100644 index 00000000..b395ba62 --- /dev/null +++ b/test_data/src/FileLength.java @@ -0,0 +1,9 @@ +import java.io.File; + +public class FileLength { + public static void main(String[] args) { + File missing = new File("definitely-missing-file"); + System.out.println(missing.exists()); + System.out.println(missing.length()); + } +} diff --git a/test_data/src/ForName.java b/test_data/src/ForName.java new file mode 100644 index 00000000..b1627fac --- /dev/null +++ b/test_data/src/ForName.java @@ -0,0 +1,11 @@ +public class ForName { + public static void main(String[] args) throws Exception { + System.out.println(Class.forName("java.util.Vector").getName()); + try { + Class.forName("no.such.Clazz"); + System.out.println("no-ex"); + } catch (ClassNotFoundException e) { + System.out.println("cnfe"); + } + } +} diff --git a/test_data/src/SurrogateChars.java b/test_data/src/SurrogateChars.java new file mode 100644 index 00000000..54e941a6 --- /dev/null +++ b/test_data/src/SurrogateChars.java @@ -0,0 +1,20 @@ +public class SurrogateChars { + public static void main(String[] args) { + StringBuffer sb = new StringBuffer(); + sb.append('\uD83D').append('\uDE00'); + System.out.println(sb.length()); + System.out.println(sb.toString()); + + StringBuffer lone = new StringBuffer(); + lone.append('\uD800'); + System.out.println(lone.length()); + + System.out.println(String.valueOf('\uD800').length()); + System.out.println('\uD800'); + + char[] arr = { '\uD83D', '\uDE00' }; + StringBuffer sb2 = new StringBuffer(); + sb2.append(arr, 0, 2); + System.out.println(sb2.length()); + } +} diff --git a/test_data/src/ZipCorrupt.java b/test_data/src/ZipCorrupt.java new file mode 100644 index 00000000..e84a23ad --- /dev/null +++ b/test_data/src/ZipCorrupt.java @@ -0,0 +1,14 @@ +import java.io.File; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +public class ZipCorrupt { + public static void main(String[] args) throws Exception { + try { + new ZipFile(new File("test_data/FileLength.txt")); + System.out.println("no-ex"); + } catch (ZipException e) { + System.out.println("zipex"); + } + } +}