From 7c700e3937b4ea306bde70375cf9f580f9be7c69 Mon Sep 17 00:00:00 2001 From: Inseok Lee Date: Sun, 5 Apr 2026 08:52:06 +0900 Subject: [PATCH 1/3] Add FileDescriptorId type and update Runtime trait to use fd abstraction --- java_runtime/src/lib.rs | 2 +- java_runtime/src/runtime.rs | 12 +++++++----- java_runtime/src/runtime/io.rs | 13 +++++++++++++ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/java_runtime/src/lib.rs b/java_runtime/src/lib.rs index 4e6a3f4a..f8bcfe3a 100644 --- a/java_runtime/src/lib.rs +++ b/java_runtime/src/lib.rs @@ -7,7 +7,7 @@ mod runtime; pub use self::{ loader::{get_bootstrap_class_loader, get_runtime_class_proto}, - runtime::{File, FileSize, FileStat, FileType, IOError, IOResult, Runtime, SpawnCallback}, + runtime::{File, FileDescriptorId, FileSize, FileStat, FileType, IOError, IOResult, Runtime, SpawnCallback}, }; pub type RuntimeContext = dyn runtime::Runtime; diff --git a/java_runtime/src/runtime.rs b/java_runtime/src/runtime.rs index 032365a5..ae12d782 100644 --- a/java_runtime/src/runtime.rs +++ b/java_runtime/src/runtime.rs @@ -7,7 +7,7 @@ use dyn_clone::{DynClone, clone_trait_object}; use jvm::{ClassDefinition, Jvm, Result as JvmResult}; -pub use io::{File, FileSize, FileStat, FileType, IOError, IOResult}; +pub use io::{File, FileDescriptorId, FileSize, FileStat, FileType, IOError, IOResult}; #[async_trait::async_trait] pub trait SpawnCallback: Sync + Send { @@ -23,11 +23,13 @@ pub trait Runtime: Sync + Send + DynClone { fn now(&self) -> u64; // unix time in millis fn current_task_id(&self) -> u64; - fn stdin(&self) -> IOResult>; - fn stdout(&self) -> IOResult>; - fn stderr(&self) -> IOResult>; + fn stdin(&self) -> IOResult; + fn stdout(&self) -> IOResult; + fn stderr(&self) -> IOResult; - async fn open(&self, path: &str, write: bool) -> IOResult>; + async fn open(&self, path: &str, write: bool) -> IOResult; + fn get_file(&self, fd: FileDescriptorId) -> IOResult>; + fn close_file(&self, fd: FileDescriptorId); async fn unlink(&self, path: &str) -> IOResult<()>; async fn metadata(&self, path: &str) -> IOResult; diff --git a/java_runtime/src/runtime/io.rs b/java_runtime/src/runtime/io.rs index cbb5cec2..9d7ffd24 100644 --- a/java_runtime/src/runtime/io.rs +++ b/java_runtime/src/runtime/io.rs @@ -2,6 +2,19 @@ use alloc::boxed::Box; use dyn_clone::{DynClone, clone_trait_object}; +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct FileDescriptorId(u32); + +impl FileDescriptorId { + pub fn new(id: u32) -> Self { + Self(id) + } + + pub fn id(&self) -> u32 { + self.0 + } +} + #[derive(Debug)] pub enum IOError { Unsupported, From 0084673b1001015d3d2e03bf7ff4e3dfa2553e21 Mon Sep 17 00:00:00 2001 From: Inseok Lee Date: Sun, 5 Apr 2026 08:52:14 +0900 Subject: [PATCH 2/3] Update FileDescriptor and I/O stream classes to use fd-based file access --- .../src/classes/java/io/file_descriptor.rs | 46 +++++++++++++------ .../src/classes/java/io/file_input_stream.rs | 26 ++++++----- .../src/classes/java/io/file_output_stream.rs | 19 ++++---- .../src/classes/java/io/random_access_file.rs | 36 ++++++++------- 4 files changed, 75 insertions(+), 52 deletions(-) diff --git a/java_runtime/src/classes/java/io/file_descriptor.rs b/java_runtime/src/classes/java/io/file_descriptor.rs index 9283e0e3..be94706a 100644 --- a/java_runtime/src/classes/java/io/file_descriptor.rs +++ b/java_runtime/src/classes/java/io/file_descriptor.rs @@ -4,7 +4,7 @@ use java_class_proto::{JavaFieldProto, JavaMethodProto}; use java_constants::{FieldAccessFlags, MethodAccessFlags}; use jvm::{ClassInstanceRef, Jvm, Result}; -use crate::{File, RuntimeClassProto, RuntimeContext}; +use crate::{RuntimeClassProto, RuntimeContext}; // class java.io.FileDescriptor pub struct FileDescriptor; @@ -20,7 +20,7 @@ impl FileDescriptor { JavaMethodProto::new("", "()V", Self::cl_init, MethodAccessFlags::STATIC), ], fields: vec![ - JavaFieldProto::new("raw", "[B", Default::default()), + JavaFieldProto::new("fd", "I", Default::default()), JavaFieldProto::new("err", "Ljava/io/FileDescriptor;", FieldAccessFlags::STATIC), JavaFieldProto::new("in", "Ljava/io/FileDescriptor;", FieldAccessFlags::STATIC), JavaFieldProto::new("out", "Ljava/io/FileDescriptor;", FieldAccessFlags::STATIC), @@ -32,28 +32,28 @@ impl FileDescriptor { async fn cl_init(jvm: &Jvm, runtime: &mut RuntimeContext) -> Result<()> { tracing::debug!("java.io.FileDescriptor::()"); - let stderr_file = runtime.stderr(); - if let Ok(stderr_file) = stderr_file { + let stderr_fd = runtime.stderr(); + if let Ok(stderr_fd) = stderr_fd { let mut stderr = jvm.new_class("java/io/FileDescriptor", "()V", []).await?; - jvm.put_rust_object_field(&mut stderr, "raw", stderr_file).await?; + jvm.put_field(&mut stderr, "fd", "I", stderr_fd.id() as i32).await?; jvm.put_static_field("java/io/FileDescriptor", "err", "Ljava/io/FileDescriptor;", stderr) .await?; } - let stdin_file = runtime.stdin(); - if let Ok(stdin_file) = stdin_file { + let stdin_fd = runtime.stdin(); + if let Ok(stdin_fd) = stdin_fd { let mut stdin = jvm.new_class("java/io/FileDescriptor", "()V", []).await?; - jvm.put_rust_object_field(&mut stdin, "raw", stdin_file).await?; + jvm.put_field(&mut stdin, "fd", "I", stdin_fd.id() as i32).await?; jvm.put_static_field("java/io/FileDescriptor", "in", "Ljava/io/FileDescriptor;", stdin) .await?; } - let stdout_file = runtime.stdout(); - if let Ok(stdout_file) = stdout_file { + let stdout_fd = runtime.stdout(); + if let Ok(stdout_fd) = stdout_fd { let mut stdout = jvm.new_class("java/io/FileDescriptor", "()V", []).await?; - jvm.put_rust_object_field(&mut stdout, "raw", stdout_file).await?; + jvm.put_field(&mut stdout, "fd", "I", stdout_fd.id() as i32).await?; jvm.put_static_field("java/io/FileDescriptor", "out", "Ljava/io/FileDescriptor;", stdout) .await?; @@ -70,14 +70,30 @@ impl FileDescriptor { Ok(()) } - pub async fn from_file(jvm: &Jvm, file: Box) -> Result> { + pub async fn from_fd(jvm: &Jvm, fd: crate::FileDescriptorId) -> Result> { let mut this = jvm.new_class("java/io/FileDescriptor", "()V", []).await?; - jvm.put_rust_object_field(&mut this, "raw", file).await?; + jvm.put_field(&mut this, "fd", "I", fd.id() as i32).await?; Ok(this.into()) } - pub async fn file(jvm: &Jvm, this: ClassInstanceRef) -> Result> { - jvm.get_rust_object_field(&this, "raw").await + pub async fn file(jvm: &Jvm, runtime: &RuntimeContext, this: ClassInstanceRef) -> Result> { + let fd: i32 = jvm.get_field(&this, "fd", "I").await?; + if fd <= 0 { + return Err(jvm.exception("java/io/IOException", "Invalid file descriptor").await); + } + let fd = crate::FileDescriptorId::new(fd as u32); + match runtime.get_file(fd) { + Ok(file) => Ok(file), + Err(_) => Err(jvm.exception("java/io/IOException", "Invalid file descriptor").await), + } + } + + pub async fn close(jvm: &Jvm, runtime: &RuntimeContext, this: ClassInstanceRef) -> Result<()> { + let fd: i32 = jvm.get_field(&this, "fd", "I").await?; + if fd > 0 { + runtime.close_file(crate::FileDescriptorId::new(fd as u32)); + } + Ok(()) } } 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 2b532288..b68cba42 100644 --- a/java_runtime/src/classes/java/io/file_input_stream.rs +++ b/java_runtime/src/classes/java/io/file_input_stream.rs @@ -43,13 +43,12 @@ impl FileInputStream { let path = jvm.invoke_virtual(&file, "getPath", "()Ljava/lang/String;", ()).await?; let path = JavaLangString::to_rust_string(jvm, &path).await?; - let rust_file = context.open(&path, false).await; - if rust_file.is_err() { - // TODO correct error handling + let fd = context.open(&path, false).await; + if fd.is_err() { return Err(jvm.exception("java/io/FileNotFoundException", "File not found").await); } - let fd = FileDescriptor::from_file(jvm, rust_file.unwrap()).await?; + let fd = FileDescriptor::from_fd(jvm, fd.unwrap()).await?; let _: () = jvm .invoke_special(&this, "java/io/FileInputStream", "", "(Ljava/io/FileDescriptor;)V", (fd,)) @@ -73,11 +72,11 @@ impl FileInputStream { Ok(()) } - async fn available(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef) -> Result { + async fn available(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef) -> Result { tracing::debug!("java.io.FileInputStream::available({:?})", &this); let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; - let rust_file = FileDescriptor::file(jvm, fd).await?; + let rust_file = FileDescriptor::file(jvm, context, fd).await?; // TODO get os buffer size let stat = rust_file.metadata().await.unwrap(); @@ -90,7 +89,7 @@ impl FileInputStream { async fn read_array( jvm: &Jvm, - _: &mut RuntimeContext, + context: &mut RuntimeContext, this: ClassInstanceRef, mut buf: ClassInstanceRef>, offset: i32, @@ -99,7 +98,7 @@ impl FileInputStream { tracing::debug!("java.io.FileInputStream::read({this:?}, {buf:?}, {offset:?}, {length:?})"); let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; - let mut rust_file = FileDescriptor::file(jvm, fd).await?; + 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(); @@ -112,11 +111,11 @@ impl FileInputStream { Ok(read as _) } - async fn read_byte(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef) -> Result { + async fn read_byte(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef) -> Result { tracing::debug!("java.io.FileInputStream::read({:?})", &this); let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; - let mut rust_file = FileDescriptor::file(jvm, fd).await?; + let mut rust_file = FileDescriptor::file(jvm, context, fd).await?; let mut buf = [0; 1]; let read = rust_file.read(&mut buf).await.unwrap(); @@ -127,8 +126,11 @@ impl FileInputStream { Ok(buf[0] as i32) } - async fn close(_jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef) -> Result<()> { - tracing::debug!("stub java.io.FileInputStream::close({:?})", &this); + async fn close(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef) -> Result<()> { + tracing::debug!("java.io.FileInputStream::close({:?})", &this); + + let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; + FileDescriptor::close(jvm, context, fd).await?; Ok(()) } 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 309afdae..1066dfc6 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,8 @@ impl FileOutputStream { let path = jvm.invoke_virtual(&file, "getPath", "()Ljava/lang/String;", ()).await?; let path = JavaLangString::to_rust_string(jvm, &path).await?; - let file = context.open(&path, true).await.unwrap(); - let fd = FileDescriptor::from_file(jvm, file).await?; + let fd = context.open(&path, true).await.unwrap(); + let fd = FileDescriptor::from_fd(jvm, fd).await?; let _: () = jvm .invoke_special(&this, "java/io/FileOutputStream", "", "(Ljava/io/FileDescriptor;)V", (fd,)) @@ -69,7 +69,7 @@ impl FileOutputStream { async fn write_bytes_offset( jvm: &Jvm, - _: &mut RuntimeContext, + context: &mut RuntimeContext, this: ClassInstanceRef, buffer: ClassInstanceRef>, offset: i32, @@ -84,7 +84,7 @@ impl FileOutputStream { ); let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; - let mut file = FileDescriptor::file(jvm, fd).await?; + 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(); @@ -94,19 +94,22 @@ impl FileOutputStream { Ok(()) } - async fn write(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef, byte: i32) -> Result<()> { + async fn write(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef, byte: i32) -> Result<()> { tracing::debug!("java.io.FileOutputStream::write({:?}, {:?})", &this, &byte); let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; - let mut file = FileDescriptor::file(jvm, fd).await?; + let mut file = FileDescriptor::file(jvm, context, fd).await?; file.write(&[byte as u8]).await.unwrap(); Ok(()) } - async fn close(_jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef) -> Result<()> { - tracing::debug!("stub java.io.FileInputStream::close({:?})", &this); + async fn close(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef) -> Result<()> { + tracing::debug!("java.io.FileOutputStream::close({:?})", &this); + + let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; + FileDescriptor::close(jvm, context, fd).await?; Ok(()) } 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 28cbae35..7bd646e7 100644 --- a/java_runtime/src/classes/java/io/random_access_file.rs +++ b/java_runtime/src/classes/java/io/random_access_file.rs @@ -57,12 +57,11 @@ impl RandomAccessFile { let write = mode.contains('w'); - let rust_file = context.open(&name, write).await; - if rust_file.is_err() { - // TODO correct error handling + let fd_id = context.open(&name, write).await; + if fd_id.is_err() { return Err(jvm.exception("java/io/FileNotFoundException", "File not found").await); } - let fd = FileDescriptor::from_file(jvm, rust_file.unwrap()).await?; + let fd = FileDescriptor::from_fd(jvm, fd_id.unwrap()).await?; jvm.put_field(&mut this, "fd", "Ljava/io/FileDescriptor;", fd).await?; Ok(()) @@ -103,7 +102,7 @@ impl RandomAccessFile { async fn read_offset_length( jvm: &Jvm, - _: &mut RuntimeContext, + context: &mut RuntimeContext, this: ClassInstanceRef, mut buf: ClassInstanceRef>, offset: i32, @@ -112,7 +111,7 @@ impl RandomAccessFile { tracing::debug!("java.io.RandomAccessFile::read({:?}, {:?}, {:?}, {:?})", &this, &buf, &offset, &length); let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; - let mut rust_file = FileDescriptor::file(jvm, fd).await?; + 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(); @@ -133,7 +132,7 @@ impl RandomAccessFile { async fn write_offset_length( jvm: &Jvm, - _: &mut RuntimeContext, + context: &mut RuntimeContext, this: ClassInstanceRef, buf: ClassInstanceRef>, offset: i32, @@ -142,7 +141,7 @@ impl RandomAccessFile { tracing::debug!("java.io.RandomAccessFile::write({:?}, {:?}, {:?}, {:?})", &this, &buf, &offset, &length); let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; - let mut rust_file = FileDescriptor::file(jvm, fd).await?; + 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(); @@ -151,44 +150,44 @@ impl RandomAccessFile { Ok(()) } - async fn seek(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef, pos: i64) -> Result<()> { + async fn seek(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef, pos: i64) -> Result<()> { tracing::debug!("java.io.RandomAccessFile::seek({:?}, {:?})", &this, &pos); let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; - let mut rust_file = FileDescriptor::file(jvm, fd).await?; + let mut rust_file = FileDescriptor::file(jvm, context, fd).await?; rust_file.seek(pos as _).await.unwrap(); Ok(()) } - async fn set_length(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef, new_length: i64) -> Result<()> { + async fn set_length(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef, new_length: i64) -> Result<()> { tracing::debug!("java.io.RandomAccessFile::setLength({:?}, {:?})", &this, &new_length); let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; - let mut rust_file = FileDescriptor::file(jvm, fd).await?; + let mut rust_file = FileDescriptor::file(jvm, context, fd).await?; rust_file.set_len(new_length as _).await.unwrap(); Ok(()) } - async fn length(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef) -> Result { + async fn length(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef) -> Result { tracing::debug!("java.io.RandomAccessFile::length({:?})", &this); let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; - let rust_file = FileDescriptor::file(jvm, fd).await?; + let rust_file = FileDescriptor::file(jvm, context, fd).await?; let len = rust_file.metadata().await.unwrap().size; Ok(len as i64) } - async fn get_file_pointer(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef) -> Result { + async fn get_file_pointer(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef) -> Result { tracing::debug!("java.io.RandomAccessFile::getFilePointer({:?})", &this); let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; - let rust_file = FileDescriptor::file(jvm, fd).await?; + let rust_file = FileDescriptor::file(jvm, context, fd).await?; let pos = rust_file.tell().await.unwrap(); @@ -203,9 +202,12 @@ impl RandomAccessFile { Ok(fd) } - async fn close(_jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef) -> Result<()> { + async fn close(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef) -> Result<()> { tracing::debug!("java.io.RandomAccessFile::close({:?})", &this); + let fd = jvm.get_field(&this, "fd", "Ljava/io/FileDescriptor;").await?; + FileDescriptor::close(jvm, context, fd).await?; + Ok(()) } } From 9ce4e648496d2ba5bab8b546768122361a4b22c1 Mon Sep 17 00:00:00 2001 From: Inseok Lee Date: Sun, 5 Apr 2026 08:52:18 +0900 Subject: [PATCH 3/3] Add fd table to RuntimeImpl and TestRuntime --- src/runtime.rs | 50 ++++++++++++++++++++++++++++---------- test_utils/src/lib.rs | 56 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 83 insertions(+), 23 deletions(-) diff --git a/src/runtime.rs b/src/runtime.rs index 919683e5..649fd6e7 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,8 +1,8 @@ mod io; -use alloc::sync::Arc; +use alloc::{collections::BTreeMap, sync::Arc}; use core::{ - sync::atomic::{AtomicU64, Ordering}, + sync::atomic::{AtomicU32, AtomicU64, Ordering}, time::Duration, }; use std::{ @@ -11,7 +11,7 @@ use std::{ sync::Mutex, }; -use java_runtime::{File, FileStat, FileType, IOError, IOResult, RT_RUSTJAR, Runtime, SpawnCallback, get_runtime_class_proto}; +use java_runtime::{File, FileDescriptorId, FileStat, FileType, IOError, IOResult, RT_RUSTJAR, Runtime, SpawnCallback, get_runtime_class_proto}; use jvm::{ClassDefinition, Jvm}; use jvm_rust::{ArrayClassDefinitionImpl, ClassDefinitionImpl}; @@ -57,6 +57,8 @@ where T: Sync + Send + Write + 'static, { stdout: WriteWrapper, + file_table: Arc>>>, + next_fd: Arc, } impl RuntimeImpl @@ -68,8 +70,16 @@ where stdout: WriteWrapper { write: Arc::new(Mutex::new(stdout)), }, + file_table: Arc::new(Mutex::new(BTreeMap::new())), + next_fd: Arc::new(AtomicU32::new(1)), } } + + fn register_file(&self, file: Box) -> FileDescriptorId { + let fd = self.next_fd.fetch_add(1, Ordering::SeqCst); + self.file_table.lock().unwrap().insert(fd, file); + FileDescriptorId::new(fd) + } } #[async_trait::async_trait] @@ -104,20 +114,32 @@ where TASK_ID.try_with(|x| *x).unwrap_or(0) } - fn stdin(&self) -> IOResult> { - Ok(Box::new(InputStreamFile::new(stdin()))) + fn stdin(&self) -> IOResult { + let file = Box::new(InputStreamFile::new(stdin())); + Ok(self.register_file(file)) + } + + fn stdout(&self) -> IOResult { + let file = Box::new(WriteStreamFile::new(self.stdout.clone())); + Ok(self.register_file(file)) } - fn stdout(&self) -> IOResult> { - Ok(Box::new(WriteStreamFile::new(self.stdout.clone()))) + fn stderr(&self) -> IOResult { + let file = Box::new(WriteStreamFile::new(stderr())); + Ok(self.register_file(file)) } - fn stderr(&self) -> IOResult> { - Ok(Box::new(WriteStreamFile::new(stderr()))) + async fn open(&self, path: &str, write: bool) -> IOResult { + let file = Box::new(FileImpl::new(path, write)); + Ok(self.register_file(file)) } - async fn open(&self, path: &str, write: bool) -> IOResult> { - Ok(Box::new(FileImpl::new(path, write))) + fn get_file(&self, fd: FileDescriptorId) -> IOResult> { + self.file_table.lock().unwrap().get(&fd.id()).cloned().ok_or(IOError::NotFound) + } + + fn close_file(&self, fd: FileDescriptorId) { + self.file_table.lock().unwrap().remove(&fd.id()); } async fn unlink(&self, path: &str) -> IOResult<()> { @@ -166,6 +188,10 @@ where T: Sync + Send + Write + 'static, { fn clone(&self) -> Self { - Self { stdout: self.stdout.clone() } + Self { + stdout: self.stdout.clone(), + file_table: self.file_table.clone(), + next_fd: self.next_fd.clone(), + } } } diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs index 0ae15e4c..9f92d1cb 100644 --- a/test_utils/src/lib.rs +++ b/test_utils/src/lib.rs @@ -1,28 +1,53 @@ extern crate alloc; -use alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec}; +use alloc::{boxed::Box, collections::BTreeMap, string::String, sync::Arc, vec::Vec}; use core::{ cmp::min, - sync::atomic::{AtomicU64, Ordering}, + sync::atomic::{AtomicU32, AtomicU64, Ordering}, time::Duration, }; -use std::time::{SystemTime, UNIX_EPOCH}; +use std::{ + sync::Mutex, + time::{SystemTime, UNIX_EPOCH}, +}; use jvm::{ClassDefinition, Jvm, Result}; use jvm_rust::{ArrayClassDefinitionImpl, ClassDefinitionImpl}; use java_runtime::{ - File, FileSize, FileStat, FileType, IOError, IOResult, RT_RUSTJAR, Runtime, SpawnCallback, get_bootstrap_class_loader, get_runtime_class_proto, + File, FileDescriptorId, FileSize, FileStat, FileType, IOError, IOResult, RT_RUSTJAR, Runtime, SpawnCallback, get_bootstrap_class_loader, + get_runtime_class_proto, }; -#[derive(Clone)] pub struct TestRuntime { filesystem: BTreeMap>, + file_table: Arc>>>, + next_fd: Arc, +} + +impl Clone for TestRuntime { + fn clone(&self) -> Self { + Self { + filesystem: self.filesystem.clone(), + file_table: self.file_table.clone(), + next_fd: self.next_fd.clone(), + } + } } impl TestRuntime { pub fn new(filesystem: BTreeMap>) -> Self { - Self { filesystem } + Self { + filesystem, + file_table: Arc::new(Mutex::new(BTreeMap::new())), + next_fd: Arc::new(AtomicU32::new(1)), + } + } + + fn register_file(&self, file: Box) -> FileDescriptorId { + let fd = self.next_fd.fetch_add(1, Ordering::SeqCst); + self.file_table.lock().unwrap().insert(fd, file); + FileDescriptorId::new(fd) } } @@ -61,27 +86,36 @@ impl Runtime for TestRuntime { TASK_ID.try_with(|x| *x).unwrap_or(0) } - fn stdin(&self) -> IOResult> { + fn stdin(&self) -> IOResult { Err(IOError::NotFound) } - fn stdout(&self) -> IOResult> { + fn stdout(&self) -> IOResult { Err(IOError::NotFound) } - fn stderr(&self) -> IOResult> { + fn stderr(&self) -> IOResult { Err(IOError::NotFound) } - async fn open(&self, path: &str, _write: bool) -> IOResult> { + async fn open(&self, path: &str, _write: bool) -> IOResult { let entry = self.filesystem.get(path); if let Some(data) = entry { - Ok(Box::new(DummyFile::new(data.clone())) as Box<_>) + let file = Box::new(DummyFile::new(data.clone())) as Box; + Ok(self.register_file(file)) } else { Err(IOError::NotFound) } } + fn get_file(&self, fd: FileDescriptorId) -> IOResult> { + self.file_table.lock().unwrap().get(&fd.id()).cloned().ok_or(IOError::NotFound) + } + + fn close_file(&self, fd: FileDescriptorId) { + self.file_table.lock().unwrap().remove(&fd.id()); + } + async fn unlink(&self, _path: &str) -> IOResult<()> { Err(IOError::NotFound) }