From 0a1a71fd6ef4f426f67b1145b064326eb825d76e Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Mon, 21 Nov 2022 11:09:19 -0800 Subject: [PATCH] Revert #3145 This reverst buggy commit e1e08f44b73564fa3e2debf5f4485990c4e6b4fb --- Cargo.lock | 4 +- examples/wasi_pipes.rs | 6 +- lib/api/tests/externals.rs | 46 +- lib/api/tests/reference_types.rs | 10 +- lib/c-api/src/wasm_c_api/wasi/mod.rs | 948 +----------------- lib/c-api/tests/wasm-c-api/example/stdio.wasm | Bin 66338 -> 0 bytes .../tests/wasm-c-api/example/testrust.wasm | Bin 70088 -> 0 bytes lib/vm/src/memory.rs | 8 +- lib/wasi/src/lib.rs | 5 +- lib/wasi/src/state/pipe.rs | 304 +----- lib/wasi/src/state/types.rs | 85 +- lib/wasi/src/syscalls/mod.rs | 6 +- lib/wasi/tests/stdio.rs | 31 +- tests/integration/cli/tests/create_exe.rs | 4 +- tests/integration/ios/tests/dylib.rs | 3 +- tests/lib/wast/src/wasi_wast.rs | 6 +- 16 files changed, 197 insertions(+), 1269 deletions(-) delete mode 100755 lib/c-api/tests/wasm-c-api/example/stdio.wasm delete mode 100755 lib/c-api/tests/wasm-c-api/example/testrust.wasm diff --git a/Cargo.lock b/Cargo.lock index 265672374a1..0089c595e53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4195,9 +4195,9 @@ dependencies = [ [[package]] name = "wasmer-inline-c" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c4e7a2a3363ceeb2ee60371af9460748f2bf53569b58627f1f640284ab07778" +checksum = "e2405c99de49dc05338e5ed2eb397fe70b7128340d960507d0ba716f7d29a91a" dependencies = [ "assert_cmd", "cc", diff --git a/examples/wasi_pipes.rs b/examples/wasi_pipes.rs index e4319b71688..ed7ef03a075 100644 --- a/examples/wasi_pipes.rs +++ b/examples/wasi_pipes.rs @@ -14,7 +14,7 @@ use std::io::{Read, Write}; use wasmer::{Instance, Module, Store}; use wasmer_compiler_cranelift::Cranelift; -use wasmer_wasi::{WasiBidirectionalSharedPipePair, WasiState}; +use wasmer_wasi::{Pipe, WasiState}; fn main() -> Result<(), Box> { let wasm_path = concat!( @@ -36,8 +36,8 @@ fn main() -> Result<(), Box> { println!("Creating `WasiEnv`..."); // First, we create the `WasiEnv` with the stdio pipes - let mut input = WasiBidirectionalSharedPipePair::new().with_blocking(false); - let mut output = WasiBidirectionalSharedPipePair::new().with_blocking(false); + let mut input = Pipe::new(); + let mut output = Pipe::new(); let wasi_env = WasiState::new("hello") .stdin(Box::new(input.clone())) .stdout(Box::new(output.clone())) diff --git a/lib/api/tests/externals.rs b/lib/api/tests/externals.rs index f593c763b79..5c17579a4ab 100644 --- a/lib/api/tests/externals.rs +++ b/lib/api/tests/externals.rs @@ -210,25 +210,28 @@ fn memory_grow() -> Result<(), String> { fn function_new() -> Result<(), String> { let mut store = Store::default(); let function = Function::new_typed(&mut store, || {}); - assert_eq!(function.ty(&mut store), FunctionType::new(vec![], vec![])); + assert_eq!( + function.ty(&mut store).clone(), + FunctionType::new(vec![], vec![]) + ); let function = Function::new_typed(&mut store, |_a: i32| {}); assert_eq!( - function.ty(&mut store), + function.ty(&mut store).clone(), FunctionType::new(vec![Type::I32], vec![]) ); let function = Function::new_typed(&mut store, |_a: i32, _b: i64, _c: f32, _d: f64| {}); assert_eq!( - function.ty(&mut store), + function.ty(&mut store).clone(), FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]) ); let function = Function::new_typed(&mut store, || -> i32 { 1 }); assert_eq!( - function.ty(&mut store), + function.ty(&mut store).clone(), FunctionType::new(vec![], vec![Type::I32]) ); let function = Function::new_typed(&mut store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) }); assert_eq!( - function.ty(&mut store), + function.ty(&mut store).clone(), FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]) ); Ok(()) @@ -243,11 +246,14 @@ fn function_new_env() -> Result<(), String> { let my_env = MyEnv {}; let env = FunctionEnv::new(&mut store, my_env); let function = Function::new_typed_with_env(&mut store, &env, |_env: FunctionEnvMut| {}); - assert_eq!(function.ty(&mut store), FunctionType::new(vec![], vec![])); + assert_eq!( + function.ty(&mut store).clone(), + FunctionType::new(vec![], vec![]) + ); let function = Function::new_typed_with_env(&mut store, &env, |_env: FunctionEnvMut, _a: i32| {}); assert_eq!( - function.ty(&mut store), + function.ty(&mut store).clone(), FunctionType::new(vec![Type::I32], vec![]) ); let function = Function::new_typed_with_env( @@ -256,13 +262,13 @@ fn function_new_env() -> Result<(), String> { |_env: FunctionEnvMut, _a: i32, _b: i64, _c: f32, _d: f64| {}, ); assert_eq!( - function.ty(&mut store), + function.ty(&mut store).clone(), FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]) ); let function = Function::new_typed_with_env(&mut store, &env, |_env: FunctionEnvMut| -> i32 { 1 }); assert_eq!( - function.ty(&mut store), + function.ty(&mut store).clone(), FunctionType::new(vec![], vec![Type::I32]) ); let function = Function::new_typed_with_env( @@ -271,7 +277,7 @@ fn function_new_env() -> Result<(), String> { |_env: FunctionEnvMut| -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) }, ); assert_eq!( - function.ty(&mut store), + function.ty(&mut store).clone(), FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]) ); Ok(()) @@ -288,35 +294,35 @@ fn function_new_dynamic() -> Result<(), String> { &function_type, |_values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store), function_type); + assert_eq!(function.ty(&mut store).clone(), function_type); let function_type = FunctionType::new(vec![Type::I32], vec![]); let function = Function::new( &mut store, &function_type, |_values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store), function_type); + assert_eq!(function.ty(&mut store).clone(), function_type); let function_type = FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]); let function = Function::new( &mut store, &function_type, |_values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store), function_type); + assert_eq!(function.ty(&mut store).clone(), function_type); let function_type = FunctionType::new(vec![], vec![Type::I32]); let function = Function::new( &mut store, &function_type, |_values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store), function_type); + assert_eq!(function.ty(&mut store).clone(), function_type); let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]); let function = Function::new( &mut store, &function_type, |_values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store), function_type); + assert_eq!(function.ty(&mut store).clone(), function_type); // Using array signature let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]); @@ -350,7 +356,7 @@ fn function_new_dynamic_env() -> Result<(), String> { &function_type, |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store), function_type); + assert_eq!(function.ty(&mut store).clone(), function_type); let function_type = FunctionType::new(vec![Type::I32], vec![]); let function = Function::new_with_env( &mut store, @@ -358,7 +364,7 @@ fn function_new_dynamic_env() -> Result<(), String> { &function_type, |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store), function_type); + assert_eq!(function.ty(&mut store).clone(), function_type); let function_type = FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]); let function = Function::new_with_env( &mut store, @@ -366,7 +372,7 @@ fn function_new_dynamic_env() -> Result<(), String> { &function_type, |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store), function_type); + assert_eq!(function.ty(&mut store).clone(), function_type); let function_type = FunctionType::new(vec![], vec![Type::I32]); let function = Function::new_with_env( &mut store, @@ -374,7 +380,7 @@ fn function_new_dynamic_env() -> Result<(), String> { &function_type, |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store), function_type); + assert_eq!(function.ty(&mut store).clone(), function_type); let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]); let function = Function::new_with_env( &mut store, @@ -382,7 +388,7 @@ fn function_new_dynamic_env() -> Result<(), String> { &function_type, |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store), function_type); + assert_eq!(function.ty(&mut store).clone(), function_type); // Using array signature let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]); diff --git a/lib/api/tests/reference_types.rs b/lib/api/tests/reference_types.rs index 8746936f06f..aecd46d48ec 100644 --- a/lib/api/tests/reference_types.rs +++ b/lib/api/tests/reference_types.rs @@ -367,7 +367,7 @@ pub mod reference_types { let global: &Global = instance.exports.get_global("global")?; { let er = ExternRef::new(&mut store, 3usize); - global.set(&mut store, Value::ExternRef(Some(er)))?; + global.set(&mut store, Value::ExternRef(Some(er.clone())))?; } let get_from_global: TypedFunction<(), Option> = instance .exports @@ -398,7 +398,7 @@ pub mod reference_types { let er = ExternRef::new(&mut store, 3usize); - let result = pass_extern_ref.call(&mut store, Some(er)); + let result = pass_extern_ref.call(&mut store, Some(er.clone())); assert!(result.is_err()); Ok(()) @@ -442,7 +442,7 @@ pub mod reference_types { let result = grow_table_with_ref.call(&mut store, Some(er1.clone()), 10_000)?; assert_eq!(result, -1); - let result = grow_table_with_ref.call(&mut store, Some(er1), 8)?; + let result = grow_table_with_ref.call(&mut store, Some(er1.clone()), 8)?; assert_eq!(result, 2); for i in 2..10 { @@ -454,7 +454,7 @@ pub mod reference_types { } { - fill_table_with_ref.call(&mut store, Some(er2), 0, 2)?; + fill_table_with_ref.call(&mut store, Some(er2.clone()), 0, 2)?; } { @@ -462,7 +462,7 @@ pub mod reference_types { table2.set(&mut store, 1, Value::ExternRef(Some(er3.clone())))?; table2.set(&mut store, 2, Value::ExternRef(Some(er3.clone())))?; table2.set(&mut store, 3, Value::ExternRef(Some(er3.clone())))?; - table2.set(&mut store, 4, Value::ExternRef(Some(er3)))?; + table2.set(&mut store, 4, Value::ExternRef(Some(er3.clone())))?; } { diff --git a/lib/c-api/src/wasm_c_api/wasi/mod.rs b/lib/c-api/src/wasm_c_api/wasi/mod.rs index 0d5f9aa7d76..3882d664b96 100644 --- a/lib/c-api/src/wasm_c_api/wasi/mod.rs +++ b/lib/c-api/src/wasm_c_api/wasi/mod.rs @@ -11,647 +11,22 @@ use super::{ types::wasm_byte_vec_t, }; use crate::error::update_last_error; -use std::convert::TryInto; +use std::convert::TryFrom; use std::ffi::CStr; use std::os::raw::c_char; use std::slice; -use std::sync::{Arc, Mutex}; -use std::{ - convert::TryFrom, - ffi::c_void, - fmt, - io::{self, SeekFrom}, - sync::MutexGuard, +use wasmer_wasi::{ + get_wasi_version, Pipe, WasiFile, WasiFunctionEnv, WasiState, WasiStateBuilder, WasiVersion, }; #[cfg(feature = "webc_runner")] use wasmer_api::{AsStoreMut, Imports, Module}; -use wasmer_wasi::{ - get_wasi_version, FsError, VirtualFile, WasiBidirectionalPipePair, WasiFile, WasiFunctionEnv, - WasiPipe, WasiState, WasiStateBuilder, WasiVersion, -}; - -/// Function callback that takes: -/// -/// - a *mut to the environment data (passed in on creation), -/// - the length of the environment data -/// - a *const to the bytes to write -/// - the length of the bytes to write -pub type WasiConsoleIoReadCallback = unsafe extern "C" fn(*const c_void, *mut c_char, usize) -> i64; -pub type WasiConsoleIoWriteCallback = - unsafe extern "C" fn(*const c_void, *const c_char, usize, bool) -> i64; -pub type WasiConsoleIoSeekCallback = unsafe extern "C" fn(*const c_void, c_char, i64) -> i64; -pub type WasiConsoleIoEnvDestructor = unsafe extern "C" fn(*const c_void) -> i64; - -/// The console override is a custom context consisting of callback pointers -/// (which are activated whenever some console I/O occurs) and a "context", which -/// can be owned or referenced from C. This struct can be used in `wasi_config_overwrite_stdin`, -/// `wasi_config_overwrite_stdout` or `wasi_config_overwrite_stderr` to redirect the output or -/// insert input into the console I/O log. -/// -/// Internally the stdout / stdin is synchronized, so the console is usable across threads -/// (only one thread can read / write / seek from the console I/O) -#[allow(non_camel_case_types)] -#[allow(clippy::box_collection, clippy::redundant_allocation)] -#[repr(C)] -#[derive(Clone)] -pub struct wasi_pipe_t { - read: WasiConsoleIoReadCallback, - write: WasiConsoleIoWriteCallback, - seek: WasiConsoleIoSeekCallback, - data: Option>>>, -} - -#[derive(Debug)] -struct WasiPipeDataWithDestructor { - data: Vec, - // Buffer of already-read data that is being read into, - // then the result is returned - temp_buffer: Vec, - destructor: WasiConsoleIoEnvDestructor, -} - -impl WasiPipeDataWithDestructor { - fn read_buffer( - &mut self, - read_cb: WasiConsoleIoReadCallback, - max_read: Option, - ) -> io::Result { - const BLOCK_SIZE: usize = 1024; - - let mut final_buf = Vec::new(); - - let max_to_read = max_read.unwrap_or(usize::MAX); - let max_read = max_to_read.saturating_sub(self.temp_buffer.len()); - if max_read == 0 { - // there are n bytes being available to read in the temp_buffer - return Ok(max_to_read); - } - let mut cur_read = 0; - - // Read bytes until either EOF is reached or max_read bytes are reached - loop { - if cur_read >= max_read { - break; - } - - let mut temp_buffer = if cur_read + BLOCK_SIZE > max_read { - vec![0; max_read - cur_read] - } else { - vec![0; BLOCK_SIZE] - }; - - let result = unsafe { - let ptr = temp_buffer.as_mut_ptr() as *mut c_char; - (read_cb)( - self.data.as_mut_ptr() as *const c_void, - ptr, - temp_buffer.len(), - ) - }; - - if result < 0 { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("could not read from wasi_pipe_t: {result}"), - )); - } - - let result = result as usize; - if result == 0 || result > temp_buffer.len() { - break; // EOF - } - - cur_read += result; - final_buf.extend_from_slice(&temp_buffer[..result]); - } - - let final_buf_len = final_buf.len(); - - // store the bytes in temp_buffer - self.temp_buffer.extend_from_slice(&final_buf); - - // temp_buffer.len() can be smaller than max_read in case we - // encounter EOF earlier than expected - assert!(self.temp_buffer.len() <= max_read); - - // return how many bytes were just read - // - // caller has to clear temp_buffer to advance actual reading - Ok(final_buf_len) - } -} - -impl Drop for WasiPipeDataWithDestructor { - fn drop(&mut self) { - let error = unsafe { (self.destructor)(self.data.as_mut_ptr() as *const c_void) }; - if error < 0 { - panic!("error dropping wasi_pipe_t: {}", error); - } - } -} - -impl wasi_pipe_t { - fn get_data_mut( - &self, - op_id: &'static str, - ) -> io::Result> { - self.data - .as_ref() - .ok_or_else(|| { - io::Error::new( - io::ErrorKind::Other, - format!("could not lock mutex ({op_id}) on wasi_pipe_t: no mutex"), - ) - })? - .lock() - .map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("could not lock mutex ({op_id}) on wasi_pipe_t: {e}"), - ) - }) - } -} - -impl fmt::Debug for wasi_pipe_t { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "wasi_pipe_t") - } -} - -impl io::Read for wasi_pipe_t { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let self_read = self.read; - let mut data = self.get_data_mut("read")?; - let _ = data.read_buffer(self_read, Some(buf.len()))?; - let bytes_to_read = buf.len().min(data.temp_buffer.len()); - let bytes_read = data.temp_buffer.drain(..bytes_to_read).collect::>(); - buf[..bytes_read.len()].clone_from_slice(&bytes_read); - Ok(bytes_to_read) - } -} - -impl io::Write for wasi_pipe_t { - fn write(&mut self, buf: &[u8]) -> io::Result { - let self_write = self.write; - let mut data = self.get_data_mut("write")?; - let result = unsafe { - (self_write)( - data.data.as_mut_ptr() as *const c_void, - buf.as_ptr() as *const c_char, - buf.len(), - false, - ) - }; - if result >= 0 { - Ok(result.try_into().unwrap_or(0)) - } else { - Err(std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "could not write {} bytes to wasi_pipe_t: {result}", - buf.len() - ), - )) - } - } - fn flush(&mut self) -> io::Result<()> { - let self_write = self.write; - let mut data = self.get_data_mut("flush")?; - let bytes_to_write = &[]; - let result: i64 = unsafe { - (self_write)( - data.data.as_mut_ptr() as *const c_void, - bytes_to_write.as_ptr(), - 0, - true, - ) - }; - if result >= 0 { - Ok(()) - } else { - Err(std::io::Error::new( - std::io::ErrorKind::Other, - format!("could not flush wasi_pipe_t: {result}"), - )) - } - } -} - -impl io::Seek for wasi_pipe_t { - fn seek(&mut self, pos: SeekFrom) -> io::Result { - let self_seek = self.seek; - let mut data = self.get_data_mut("seek")?; - let (id, pos) = match pos { - SeekFrom::Start(s) => (0, s as i64), - SeekFrom::End(s) => (1, s), - SeekFrom::Current(s) => (2, s), - }; - let result = unsafe { (self_seek)(data.data.as_mut_ptr() as *const c_void, id, pos) }; - if result >= 0 { - Ok(result.try_into().unwrap_or(0)) - } else { - Err(std::io::Error::new( - std::io::ErrorKind::Other, - format!("could not seek to {pos:?} wasi_pipe_t: {result}"), - )) - } - } -} - -impl VirtualFile for wasi_pipe_t { - fn last_accessed(&self) -> u64 { - 0 - } - fn last_modified(&self) -> u64 { - 0 - } - fn created_time(&self) -> u64 { - 0 - } - fn size(&self) -> u64 { - 0 - } - fn set_len(&mut self, _: u64) -> Result<(), FsError> { - Ok(()) - } - fn unlink(&mut self) -> Result<(), FsError> { - Ok(()) - } - fn bytes_available(&self) -> Result { - Ok(self.bytes_available_read()?.unwrap_or(0usize) - + self.bytes_available_write()?.unwrap_or(0usize)) - } - fn bytes_available_read(&self) -> Result, FsError> { - let self_read = self.read; - let mut data = self.get_data_mut("bytes_available_read")?; - let _ = data.read_buffer(self_read, None)?; - Ok(Some(data.temp_buffer.len())) - } - fn bytes_available_write(&self) -> Result, FsError> { - Ok(None) - } -} - -#[no_mangle] -pub unsafe extern "C" fn wasi_pipe_new_internal( - read: WasiConsoleIoReadCallback, - write: WasiConsoleIoWriteCallback, - seek: WasiConsoleIoSeekCallback, - destructor: WasiConsoleIoEnvDestructor, - env_data: *const c_void, - env_data_len: usize, -) -> *mut wasi_pipe_t { - let data_vec: Vec = - std::slice::from_raw_parts(env_data as *const c_char, env_data_len).to_vec(); - - Box::leak(Box::new(wasi_pipe_t { - read, - write, - seek, - data: Some(Box::new(Arc::new(Mutex::new(WasiPipeDataWithDestructor { - data: data_vec, - temp_buffer: Vec::new(), - destructor, - })))), - })) -} - -/// Creates a `wasi_pipe_t` callback object that does nothing -/// and redirects stdout / stderr to /dev/null -#[no_mangle] -pub unsafe extern "C" fn wasi_pipe_new_null() -> *mut wasi_pipe_t { - let mut data = Vec::new(); - wasi_pipe_new_internal( - wasi_pipe_read_null, - wasi_pipe_write_null, - wasi_pipe_seek_null, - wasi_pipe_delete_null, - data.as_mut_ptr(), - data.len(), - ) -} - -extern "C" fn wasi_pipe_read_null(_: *const c_void, _: *mut c_char, _: usize) -> i64 { - 0 -} - -extern "C" fn wasi_pipe_write_null(_: *const c_void, _: *const c_char, _: usize, _: bool) -> i64 { - 0 -} - -extern "C" fn wasi_pipe_seek_null(_: *const c_void, _: c_char, _: i64) -> i64 { - 0 -} - -extern "C" fn wasi_pipe_delete_null(_: *const c_void) -> i64 { - 0 -} - -unsafe extern "C" fn wasi_pipe_read_memory_2( - ptr: *const c_void, /* = *WasiPipe */ - byte_ptr: *mut c_char, /* &[u8] bytes to read */ - max_bytes: usize, /* max bytes to read */ -) -> i64 { - use std::io::Read; - let ptr = ptr as *mut WasiPipe; - let ptr = &mut *ptr; - let slice = std::slice::from_raw_parts_mut(byte_ptr as *mut u8, max_bytes); - match ptr.read(slice) { - Ok(o) => o as i64, - Err(_) => -1, - } -} - -unsafe extern "C" fn wasi_pipe_write_memory_2( - ptr: *const c_void, /* = *WasiPipe */ - byte_ptr: *const c_char, - byte_len: usize, - flush: bool, -) -> i64 { - use std::io::Write; - - let ptr = ptr as *mut WasiPipe; - let ptr = &mut *ptr; - - if flush { - match ptr.flush() { - Ok(()) => 0, - Err(_) => -1, - } - } else { - let slice = std::slice::from_raw_parts(byte_ptr as *const u8, byte_len); - match ptr.write(slice) { - Ok(o) => o as i64, - Err(_) => -1, - } - } -} - -unsafe extern "C" fn wasi_pipe_seek_memory_2( - ptr: *const c_void, /* = *WasiPipe */ - direction: c_char, - seek_to: i64, -) -> i64 { - use std::io::Seek; - - let ptr = ptr as *mut WasiPipe; - let ptr = &mut *ptr; - - let seek_from = match direction { - 0 => std::io::SeekFrom::Start(seek_to.max(0) as u64), - 1 => std::io::SeekFrom::End(seek_to), - 2 => std::io::SeekFrom::Current(seek_to), - _ => { - return -1; - } - }; - - match ptr.seek(seek_from) { - Ok(o) => o as i64, - Err(_) => -1, - } -} - -#[no_mangle] -unsafe extern "C" fn wasi_pipe_delete_memory_2(ptr: *const c_void /* = *WasiPipe */) -> i64 { - let ptr = ptr as *const WasiPipe; - let mut pipe: WasiPipe = std::mem::transmute_copy(&*ptr); // dropped here, destructors run here - pipe.close(); - 0 -} - -/// Creates a new `wasi_pipe_t` which uses a memory buffer -/// for backing stdin / stdout / stderr -#[no_mangle] -pub unsafe extern "C" fn wasi_pipe_new(ptr_user: &mut *mut wasi_pipe_t) -> *mut wasi_pipe_t { - wasi_pipe_new_internal_memory(ptr_user, false) -} - -/// Same as `wasi_pipe_new`, but the pipe will block to wait for stdin input -#[no_mangle] -pub unsafe extern "C" fn wasi_pipe_new_blocking( - ptr_user: &mut *mut wasi_pipe_t, -) -> *mut wasi_pipe_t { - wasi_pipe_new_internal_memory(ptr_user, true) -} - -unsafe fn wasi_pipe_new_internal_memory( - ptr_user: &mut *mut wasi_pipe_t, - blocking: bool, -) -> *mut wasi_pipe_t { - use std::mem::ManuallyDrop; - - let mut pair = WasiBidirectionalPipePair::new(); - pair.send.set_blocking(blocking); - pair.recv.set_blocking(blocking); - - let mut data1 = ManuallyDrop::new(pair.send); - let ptr1: &mut WasiPipe = &mut data1; - - *ptr_user = wasi_pipe_new_internal( - wasi_pipe_read_memory_2, - wasi_pipe_write_memory_2, - wasi_pipe_seek_memory_2, - wasi_pipe_delete_memory_2, - ptr1 as *mut _ as *mut c_void, - std::mem::size_of::(), - ); - - let mut data2 = ManuallyDrop::new(pair.recv); - let ptr2: &mut WasiPipe = &mut data2; - wasi_pipe_new_internal( - wasi_pipe_read_memory_2, - wasi_pipe_write_memory_2, - wasi_pipe_seek_memory_2, - wasi_pipe_delete_memory_2, - ptr2 as *mut _ as *mut c_void, - std::mem::size_of::(), - ) -} - -#[no_mangle] -pub unsafe extern "C" fn wasi_pipe_delete(ptr: *mut wasi_pipe_t) -> bool { - let _ = Box::from_raw(ptr); - true -} - -#[no_mangle] -pub unsafe extern "C" fn wasi_pipe_write_bytes( - ptr: *mut wasi_pipe_t, - buf: *const c_char, - len: usize, -) -> i64 { - use std::io::Write; - let buf = buf as *const u8; - let ptr = &mut *ptr; - let read_slice = std::slice::from_raw_parts(buf, len); - match ptr.write(read_slice) { - Ok(o) => o as i64, - Err(_) => -1, - } -} - -#[no_mangle] -pub unsafe extern "C" fn wasi_pipe_write_str(ptr: *const wasi_pipe_t, buf: *const c_char) -> i64 { - use std::io::Write; - let c_str = std::ffi::CStr::from_ptr(buf); - let as_bytes_with_nul = c_str.to_bytes(); - let ptr = &mut *(ptr as *mut wasi_pipe_t); - match ptr.write(as_bytes_with_nul) { - Ok(o) => o as i64, - Err(_) => -1, - } -} - -#[no_mangle] -pub unsafe extern "C" fn wasi_pipe_flush(ptr: *mut wasi_pipe_t) -> i64 { - use std::io::Write; - let ptr = &mut *ptr; - match ptr.flush() { - Ok(_) => 0, - Err(_) => -1, - } -} - -#[test] -fn test_wasi_pipe_with_destructor() { - let mut wasi_pipe_t_ptr = std::ptr::null_mut(); - let second_wasi_pipe_t_ptr = unsafe { wasi_pipe_new(&mut wasi_pipe_t_ptr) }; - let wasi_pipe_t_ptr = unsafe { &mut *wasi_pipe_t_ptr }; - let second_wasi_pipe_t_ptr = unsafe { &mut *second_wasi_pipe_t_ptr }; - - let data = b"hello".iter().map(|v| *v as i8).collect::>(); - let result = unsafe { wasi_pipe_write_bytes(wasi_pipe_t_ptr, data.as_ptr(), data.len()) }; - assert_eq!(result, 5); - - let bytes_avail = wasi_pipe_t_ptr.bytes_available_read(); - assert_eq!(bytes_avail, Ok(Some(0))); - - let bytes_avail2 = second_wasi_pipe_t_ptr.bytes_available_read(); - assert_eq!(bytes_avail2, Ok(Some(5))); - - let mut read_str_ptr = std::ptr::null_mut(); - let result = unsafe { wasi_pipe_read_str(second_wasi_pipe_t_ptr, &mut read_str_ptr) }; - assert_eq!(result, 6); // hello\0 - let buf_slice = unsafe { std::slice::from_raw_parts_mut(read_str_ptr, result as usize) }; - assert_eq!(buf_slice[..5], data); - - unsafe { - wasi_pipe_delete_str(read_str_ptr); - } - unsafe { wasi_pipe_delete(wasi_pipe_t_ptr) }; - unsafe { wasi_pipe_delete(second_wasi_pipe_t_ptr) }; -} - -#[no_mangle] -pub unsafe extern "C" fn wasi_pipe_read_bytes( - ptr: *const wasi_pipe_t, - buf: *const c_char, - read: usize, -) -> i64 { - use std::io::Read; - let ptr = &mut *(ptr as *mut wasi_pipe_t); - let buf = buf as *mut u8; - let slice = std::slice::from_raw_parts_mut(buf, read); - match ptr.read(slice) { - Ok(o) => o as i64, - Err(_) => -1, - } -} - -#[no_mangle] -pub unsafe extern "C" fn wasi_pipe_delete_str(buf: *mut c_char) { - use std::ffi::CString; - let _ = CString::from_raw(buf); -} - -unsafe fn wasi_pipe_read_bytes_internal(ptr: *const wasi_pipe_t, buf: &mut Vec) -> i64 { - use std::io::Read; - - const BLOCK_SIZE: usize = 1024; - - let ptr = &mut *(ptr as *mut wasi_pipe_t); - let mut target = Vec::new(); - - loop { - let mut v = vec![0; BLOCK_SIZE]; - // read n bytes, maximum of 1024 - match ptr.read(&mut v) { - Ok(0) => { - break; - } - Ok(n) => { - target.extend_from_slice(&v[..n]); - } - Err(_) => { - return -1; - } - } - } - - let len = target.len() as i64; - *buf = target; - len -} - -#[no_mangle] -pub unsafe extern "C" fn wasi_pipe_read_str(ptr: *const wasi_pipe_t, buf: &mut *mut c_char) -> i64 { - use std::ffi::CString; - - let mut target = Vec::new(); - let read_result = wasi_pipe_read_bytes_internal(ptr, &mut target); - if read_result < 0 { - return read_result; - } - - target.push(0); - let len = target.len(); - let c_string = match CString::from_vec_with_nul(target.clone()) { - Ok(o) => o, - Err(_) => { - return -1; - } - }; - - *buf = CString::into_raw(c_string); - len as i64 -} - -#[no_mangle] -pub unsafe extern "C" fn wasi_pipe_seek( - ptr: *mut wasi_pipe_t, - // 0 = from start - // 1 = from end - // 2 = from current position - seek_dir: c_char, - seek: i64, -) -> i64 { - use std::io::Seek; - - let seek_pos = match seek_dir { - 0 => SeekFrom::Start(seek as u64), - 1 => SeekFrom::End(seek), - 2 => SeekFrom::Current(seek), - _ => { - return -1; - } - }; - - let ptr = &mut *ptr; - - ptr.seek(seek_pos) - .ok() - .and_then(|p| p.try_into().ok()) - .unwrap_or(-1) -} #[derive(Debug)] #[allow(non_camel_case_types)] pub struct wasi_config_t { - stdout: Option>, - stderr: Option>, - stdin: Option>, + inherit_stdout: bool, + inherit_stderr: bool, + inherit_stdin: bool, state_builder: WasiStateBuilder, } @@ -665,16 +40,16 @@ pub unsafe extern "C" fn wasi_config_new( let prog_name = c_try!(name_c_str.to_str()); Some(Box::new(wasi_config_t { - stdout: None, - stderr: None, - stdin: None, + inherit_stdout: true, + inherit_stderr: true, + inherit_stdin: true, state_builder: WasiState::new(prog_name), })) } #[no_mangle] pub unsafe extern "C" fn wasi_config_env( - wasi_config: &mut wasi_config_t, + config: &mut wasi_config_t, key: *const c_char, value: *const c_char, ) { @@ -686,22 +61,22 @@ pub unsafe extern "C" fn wasi_config_env( let value_cstr = CStr::from_ptr(value); let value_bytes = value_cstr.to_bytes(); - wasi_config.state_builder.env(key_bytes, value_bytes); + config.state_builder.env(key_bytes, value_bytes); } #[no_mangle] -pub unsafe extern "C" fn wasi_config_arg(wasi_config: &mut wasi_config_t, arg: *const c_char) { +pub unsafe extern "C" fn wasi_config_arg(config: &mut wasi_config_t, arg: *const c_char) { debug_assert!(!arg.is_null()); let arg_cstr = CStr::from_ptr(arg); let arg_bytes = arg_cstr.to_bytes(); - wasi_config.state_builder.arg(arg_bytes); + config.state_builder.arg(arg_bytes); } #[no_mangle] pub unsafe extern "C" fn wasi_config_preopen_dir( - wasi_config: &mut wasi_config_t, + config: &mut wasi_config_t, dir: *const c_char, ) -> bool { let dir_cstr = CStr::from_ptr(dir); @@ -714,7 +89,7 @@ pub unsafe extern "C" fn wasi_config_preopen_dir( } }; - if let Err(e) = wasi_config.state_builder.preopen_dir(dir_str) { + if let Err(e) = config.state_builder.preopen_dir(dir_str) { update_last_error(e); return false; } @@ -724,7 +99,7 @@ pub unsafe extern "C" fn wasi_config_preopen_dir( #[no_mangle] pub unsafe extern "C" fn wasi_config_mapdir( - wasi_config: &mut wasi_config_t, + config: &mut wasi_config_t, alias: *const c_char, dir: *const c_char, ) -> bool { @@ -748,7 +123,7 @@ pub unsafe extern "C" fn wasi_config_mapdir( } }; - if let Err(e) = wasi_config.state_builder.map_dir(alias_str, dir_str) { + if let Err(e) = config.state_builder.map_dir(alias_str, dir_str) { update_last_error(e); return false; } @@ -757,63 +132,33 @@ pub unsafe extern "C" fn wasi_config_mapdir( } #[no_mangle] -pub extern "C" fn wasi_config_capture_stdout(wasi_config: &mut wasi_config_t) { - wasi_config.stdout = Some(unsafe { Box::from_raw(wasi_pipe_new_null()) }); -} - -#[no_mangle] -pub extern "C" fn wasi_config_inherit_stdout(wasi_config: &mut wasi_config_t) { - wasi_config.stdout = None; +pub extern "C" fn wasi_config_capture_stdout(config: &mut wasi_config_t) { + config.inherit_stdout = false; } #[no_mangle] -pub extern "C" fn wasi_config_capture_stderr(wasi_config: &mut wasi_config_t) { - wasi_config.stderr = Some(unsafe { Box::from_raw(wasi_pipe_new_null()) }); +pub extern "C" fn wasi_config_inherit_stdout(config: &mut wasi_config_t) { + config.inherit_stdout = true; } #[no_mangle] -pub extern "C" fn wasi_config_inherit_stderr(wasi_config: &mut wasi_config_t) { - wasi_config.stderr = None; +pub extern "C" fn wasi_config_capture_stderr(config: &mut wasi_config_t) { + config.inherit_stderr = false; } #[no_mangle] -pub extern "C" fn wasi_config_capture_stdin(wasi_config: &mut wasi_config_t) { - wasi_config.stdin = Some(unsafe { Box::from_raw(wasi_pipe_new_null()) }); +pub extern "C" fn wasi_config_inherit_stderr(config: &mut wasi_config_t) { + config.inherit_stderr = true; } -#[no_mangle] -pub extern "C" fn wasi_config_inherit_stdin(wasi_config: &mut wasi_config_t) { - wasi_config.stdin = None; -} +//#[no_mangle] +//pub extern "C" fn wasi_config_capture_stdin(config: &mut wasi_config_t) { +// config.inherit_stdin = false; +//} #[no_mangle] -pub unsafe extern "C" fn wasi_config_overwrite_stdin( - config_overwrite: &mut wasi_config_t, - stdin_overwrite: *mut wasi_pipe_t, -) { - config_overwrite - .state_builder - .stdin(Box::from_raw(stdin_overwrite)); -} - -#[no_mangle] -pub unsafe extern "C" fn wasi_config_overwrite_stdout( - config_overwrite: &mut wasi_config_t, - stdout_overwrite: *mut wasi_pipe_t, -) { - config_overwrite - .state_builder - .stdout(Box::from_raw(stdout_overwrite)); -} - -#[no_mangle] -pub unsafe extern "C" fn wasi_config_overwrite_stderr( - config_overwrite: &mut wasi_config_t, - stderr_overwrite: *mut wasi_pipe_t, -) { - config_overwrite - .state_builder - .stderr(Box::from_raw(stderr_overwrite)); +pub extern "C" fn wasi_config_inherit_stdin(config: &mut wasi_config_t) { + config.inherit_stdin = true; } #[repr(C)] @@ -957,24 +302,21 @@ pub struct wasi_env_t { #[no_mangle] pub unsafe extern "C" fn wasi_env_new( store: Option<&mut wasm_store_t>, - mut wasi_config: Box, + mut config: Box, ) -> Option> { let store = &mut store?.inner; let mut store_mut = store.store_mut(); - - if let Some(stdout) = wasi_config.stdout { - wasi_config.state_builder.stdout(stdout); + if !config.inherit_stdout { + config.state_builder.stdout(Box::new(Pipe::new())); } - if let Some(stderr) = wasi_config.stderr { - wasi_config.state_builder.stderr(stderr); + if !config.inherit_stderr { + config.state_builder.stderr(Box::new(Pipe::new())); } - if let Some(stdin) = wasi_config.stdin { - wasi_config.state_builder.stdin(stdin); - } + // TODO: impl capturer for stdin - let wasi_state = c_try!(wasi_config.state_builder.finalize(&mut store_mut)); + let wasi_state = c_try!(config.state_builder.finalize(&mut store_mut)); Some(Box::new(wasi_env_t { inner: wasi_state, @@ -1305,218 +647,4 @@ mod tests { }) .success(); } - - #[test] - fn test_wasi_stdin_set() { - (assert_c! { - #include "tests/wasmer.h" - #include "string.h" - #include "stdio.h" - - int main() { - wasi_pipe_t* override_stdout_1 = NULL; - wasi_pipe_t* override_stdout_2 = wasi_pipe_new(&override_stdout_1); - - assert(override_stdout_1); - assert(override_stdout_2); - - // write to override_stdout_1, then close override_stdout_1 - wasi_pipe_write_str(override_stdout_1, "test"); - wasi_pipe_delete(override_stdout_1); - - // read from override_stdout_2, after override_stdout_1 has been closed so it doesn't block - char* out; - wasi_pipe_read_str(override_stdout_2, &out); - assert(strcmp(out, "test") == 0); - wasi_pipe_delete_str(out); - - // cleanup - wasi_pipe_delete(override_stdout_2); - return 0; - } - }) - .success(); - } - - #[test] - fn test_wasi_stdin_set_2() { - (assert_c! { - #include "tests/wasmer.h" - #include "string.h" - #include "stdio.h" - - int main() { - - wasm_engine_t* engine = wasm_engine_new(); - wasm_store_t* store = wasm_store_new(engine); - wasi_config_t* config = wasi_config_new("example_program"); - - wasi_pipe_t* override_stdout_1 = NULL; - wasi_pipe_t* override_stdout_2 = wasi_pipe_new(&override_stdout_1); - assert(override_stdout_1); - assert(override_stdout_2); - - wasi_pipe_t* override_stderr_1 = NULL; - wasi_pipe_t* override_stderr_2 = wasi_pipe_new(&override_stderr_1); - assert(override_stderr_1); - assert(override_stderr_2); - - wasi_pipe_t* override_stdin_1 = NULL; - wasi_pipe_t* override_stdin_2 = wasi_pipe_new(&override_stdin_1); - assert(override_stdin_1); - assert(override_stdin_2); - - // The override_stdin ownership is moved to the config - wasi_config_overwrite_stdin(config, override_stdin_1); - wasi_config_overwrite_stdout(config, override_stdout_1); - wasi_config_overwrite_stderr(config, override_stderr_1); - - // write to stdin, then close all senders in order - // not to block during execution - wasi_pipe_write_str(override_stdin_2, "hello"); - wasi_pipe_delete(override_stdin_2); - - /* - // testrust.wasm: - - use std::io::{self, Write}; - - fn main() -> io::Result<()> { - - let mut input = String::new(); - io::stdin().read_line(&mut input)?; - - io::stdout().write_all(format!("stdout: {input}").as_bytes())?; - io::stderr().write_all(format!("stderr: {input}").as_bytes())?; - - Ok(()) - } - */ - - // Load binary. - FILE* file = fopen("tests/wasm-c-api/example/testrust.wasm", "rb"); - if (!file) { - printf("> Error loading module!\n"); - return 1; - } - - fseek(file, 0L, SEEK_END); - size_t file_size = ftell(file); - fseek(file, 0L, SEEK_SET); - - wasm_byte_vec_t binary; - wasm_byte_vec_new_uninitialized(&binary, file_size); - - if (fread(binary.data, file_size, 1, file) != 1) { - printf("> Error loading module!\n"); - return 1; - } - - fclose(file); - - wasm_module_t* module = wasm_module_new(store, &binary); - if (!module) { - printf("> Error compiling module!\n"); - return 1; - } - - // The env now has ownership of the config (using the custom stdout / stdin channels) - wasi_env_t *wasi_env = wasi_env_new(store, config); - if (!wasi_env) { - printf("> Error building WASI env!\n"); - return 1; - } - - wasm_importtype_vec_t import_types; - wasm_module_imports(module, &import_types); - - wasm_extern_vec_t imports; - wasm_extern_vec_new_uninitialized(&imports, import_types.size); - wasm_importtype_vec_delete(&import_types); - - bool get_imports_result = wasi_get_imports(store, wasi_env, module, &imports); - - if (!get_imports_result) { - printf("Error getting WASI imports!\n"); - return 1; - } - - // The program should wait for a stdin, then print "stdout: $1" to stdout - // and "stderr: $1" to stderr and exit. - - // Instantiate the module - wasm_instance_t *instance = wasm_instance_new(store, module, &imports, NULL); - if (!instance) { - printf("> Error instantiating module!\n"); - return -1; - } - - // Read the exports. - wasm_extern_vec_t exports; - wasm_instance_exports(instance, &exports); - wasm_memory_t* mem = NULL; - for (size_t i = 0; i < exports.size; i++) { - mem = wasm_extern_as_memory(exports.data[i]); - if (mem) { - break; - } - } - - if (!mem) { - printf("Failed to create instance: Could not find memory in exports\n"); - return -1; - } - wasi_env_set_memory(wasi_env, mem); - - // Get the _start function - wasm_func_t* run_func = wasi_get_start_function(instance); - if (run_func == NULL) { - printf("> Error accessing export!\n"); - return 1; - } - - // Run the _start function - // Running the program should trigger the stdin to write "hello" to the stdin - wasm_val_vec_t args = WASM_EMPTY_VEC; - wasm_val_vec_t res = WASM_EMPTY_VEC; - if (wasm_func_call(run_func, &args, &res)) { - printf("> Error calling function!\n"); - return 1; - } - - // Verify that the stdout / stderr worked as expected - char* out; - wasi_pipe_read_str(override_stdout_2, &out); - assert(strcmp(out, "stdout: hello") == 0); - wasi_pipe_delete_str(out); - - char* out2; - wasi_pipe_read_str(override_stdout_2, &out2); - assert(strcmp(out2, "") == 0); - wasi_pipe_delete_str(out2); - - char* out3; - wasi_pipe_read_str(override_stderr_2, &out3); - assert(strcmp(out3, "stderr: hello") == 0); - wasi_pipe_delete_str(out3); - - char* out4; - wasi_pipe_read_str(override_stderr_2, &out4); - assert(strcmp(out4, "") == 0); - wasi_pipe_delete_str(out4); - - wasi_pipe_delete(override_stdout_2); - wasi_pipe_delete(override_stderr_2); - wasm_byte_vec_delete(&binary); - wasm_module_delete(module); - wasm_func_delete(run_func); - wasi_env_delete(wasi_env); - wasm_store_delete(store); - wasm_engine_delete(engine); - - return 0; - } - }) - .success(); - } } diff --git a/lib/c-api/tests/wasm-c-api/example/stdio.wasm b/lib/c-api/tests/wasm-c-api/example/stdio.wasm deleted file mode 100755 index 9ed5b3d4709f9f57860742a50a42601d84f030df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66338 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 R=s)~@^QN-Ee>wdA{{V3ZoEiWC diff --git a/lib/c-api/tests/wasm-c-api/example/testrust.wasm b/lib/c-api/tests/wasm-c-api/example/testrust.wasm deleted file mode 100755 index 92a691fc6bb65016f2560464b2863c2a07b60967..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70088 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

R%{_%tN1nA@;}Vf1$_-{lmr9u#McP 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 Result { + pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result { Ok(Self(Box::new(VMOwnedMemory::new(memory, style)?))) } @@ -377,7 +377,7 @@ impl VMMemory { memory: &MemoryType, style: &MemoryStyle, vm_memory_location: NonNull, - ) -> Result { + ) -> Result { Ok(Self(Box::new(VMOwnedMemory::from_definition( memory, style, @@ -389,9 +389,9 @@ impl VMMemory { /// are natively supported /// - VMOwnedMemory -> VMMemory /// - Box -> VMMemory - pub fn from_custom(memory: IntoVMMemory) -> Self + pub fn from_custom(memory: IntoVMMemory) -> VMMemory where - IntoVMMemory: Into, + IntoVMMemory: Into, { memory.into() } diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 81a77fc7fcd..d5cac8ce9a7 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -47,9 +47,8 @@ pub mod runners; use crate::syscalls::*; pub use crate::state::{ - Fd, Pipe, Stderr, Stdin, Stdout, WasiBidirectionalPipePair, WasiBidirectionalSharedPipePair, - WasiFs, WasiInodes, WasiPipe, WasiState, WasiStateBuilder, WasiStateCreationError, ALL_RIGHTS, - VIRTUAL_ROOT_FD, + Fd, Pipe, Stderr, Stdin, Stdout, WasiFs, WasiInodes, WasiState, WasiStateBuilder, + WasiStateCreationError, ALL_RIGHTS, VIRTUAL_ROOT_FD, }; pub use crate::syscalls::types; #[cfg(feature = "wasix")] diff --git a/lib/wasi/src/state/pipe.rs b/lib/wasi/src/state/pipe.rs index 60754a0cb0f..4781ab0a02f 100644 --- a/lib/wasi/src/state/pipe.rs +++ b/lib/wasi/src/state/pipe.rs @@ -2,14 +2,12 @@ use crate::syscalls::types::*; use crate::syscalls::{read_bytes, write_bytes}; use bytes::{Buf, Bytes}; use std::convert::TryInto; -use std::io::{self, Read, Seek, SeekFrom, Write}; +use std::io::{self, Read}; use std::ops::DerefMut; use std::sync::mpsc; -use std::sync::Arc; use std::sync::Mutex; use wasmer::WasmSlice; use wasmer::{MemorySize, MemoryView}; -use wasmer_vfs::{FsError, VirtualFile}; use wasmer_wasi_types::wasi::Errno; #[derive(Debug)] @@ -20,70 +18,10 @@ pub struct WasiPipe { rx: Mutex>>, /// Buffers the last read message from the pipe while its being consumed read_buffer: Option, - /// Whether the pipe should block or not block to wait for stdin reads - block: bool, } -/// Pipe pair of (a, b) WasiPipes that are connected together -#[derive(Debug)] -pub struct WasiBidirectionalPipePair { - pub send: WasiPipe, - pub recv: WasiPipe, -} - -impl Write for WasiBidirectionalPipePair { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.send.write(buf) - } - fn flush(&mut self) -> io::Result<()> { - self.send.flush() - } -} - -impl Seek for WasiBidirectionalPipePair { - fn seek(&mut self, _: SeekFrom) -> io::Result { - Ok(0) - } -} - -impl Read for WasiBidirectionalPipePair { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - self.recv.read(buf) - } -} - -impl VirtualFile for WasiBidirectionalPipePair { - fn last_accessed(&self) -> u64 { - self.recv.last_accessed() - } - fn last_modified(&self) -> u64 { - self.recv.last_modified() - } - fn created_time(&self) -> u64 { - self.recv.created_time() - } - fn size(&self) -> u64 { - self.recv.size() - } - fn set_len(&mut self, i: u64) -> Result<(), FsError> { - self.recv.set_len(i) - } - fn unlink(&mut self) -> Result<(), FsError> { - self.recv.unlink() - } - fn bytes_available_read(&self) -> Result, FsError> { - self.recv.bytes_available_read() - } -} - -impl Default for WasiBidirectionalPipePair { - fn default() -> Self { - Self::new() - } -} - -impl WasiBidirectionalPipePair { - pub fn new() -> WasiBidirectionalPipePair { +impl WasiPipe { + pub fn new() -> (WasiPipe, WasiPipe) { let (tx1, rx1) = mpsc::channel(); let (tx2, rx2) = mpsc::channel(); @@ -91,142 +29,15 @@ impl WasiBidirectionalPipePair { tx: Mutex::new(tx1), rx: Mutex::new(rx2), read_buffer: None, - block: true, }; let pipe2 = WasiPipe { tx: Mutex::new(tx2), rx: Mutex::new(rx1), read_buffer: None, - block: true, }; - WasiBidirectionalPipePair { - send: pipe1, - recv: pipe2, - } - } - - #[allow(dead_code)] - pub fn with_blocking(mut self, block: bool) -> Self { - self.set_blocking(block); - self - } - - /// Whether to block on reads (ususally for waiting for stdin keyboard input). Default: `true` - #[allow(dead_code)] - pub fn set_blocking(&mut self, block: bool) { - self.send.set_blocking(block); - self.recv.set_blocking(block); - } -} - -/// Shared version of WasiBidirectionalPipePair for situations where you need -/// to emulate the old behaviour of `Pipe` (both send and recv on one channel). -#[derive(Debug, Clone)] -pub struct WasiBidirectionalSharedPipePair { - inner: Arc>, -} - -impl Default for WasiBidirectionalSharedPipePair { - fn default() -> Self { - Self::new() - } -} - -impl WasiBidirectionalSharedPipePair { - pub fn new() -> Self { - Self { - inner: Arc::new(Mutex::new(WasiBidirectionalPipePair::new())), - } - } - - #[allow(dead_code)] - pub fn with_blocking(mut self, block: bool) -> Self { - self.set_blocking(block); - self - } - - /// Whether to block on reads (ususally for waiting for stdin keyboard input). Default: `true` - #[allow(dead_code)] - pub fn set_blocking(&mut self, block: bool) { - self.inner.lock().unwrap().set_blocking(block); - } -} - -impl Write for WasiBidirectionalSharedPipePair { - fn write(&mut self, buf: &[u8]) -> io::Result { - match self.inner.lock().as_mut().map(|l| l.write(buf)) { - Ok(r) => r, - Err(_) => Ok(0), - } - } - fn flush(&mut self) -> io::Result<()> { - match self.inner.lock().as_mut().map(|l| l.flush()) { - Ok(r) => r, - Err(_) => Ok(()), - } - } -} - -impl Seek for WasiBidirectionalSharedPipePair { - fn seek(&mut self, _: SeekFrom) -> io::Result { - Ok(0) - } -} - -impl Read for WasiBidirectionalSharedPipePair { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - match self.inner.lock().as_mut().map(|l| l.read(buf)) { - Ok(r) => r, - Err(_) => Ok(0), - } - } -} - -impl VirtualFile for WasiBidirectionalSharedPipePair { - fn last_accessed(&self) -> u64 { - self.inner.lock().map(|l| l.last_accessed()).unwrap_or(0) - } - fn last_modified(&self) -> u64 { - self.inner.lock().map(|l| l.last_modified()).unwrap_or(0) - } - fn created_time(&self) -> u64 { - self.inner.lock().map(|l| l.created_time()).unwrap_or(0) - } - fn size(&self) -> u64 { - self.inner.lock().map(|l| l.size()).unwrap_or(0) - } - fn set_len(&mut self, i: u64) -> Result<(), FsError> { - match self.inner.lock().as_mut().map(|l| l.set_len(i)) { - Ok(r) => r, - Err(_) => Err(FsError::Lock), - } - } - fn unlink(&mut self) -> Result<(), FsError> { - match self.inner.lock().as_mut().map(|l| l.unlink()) { - Ok(r) => r, - Err(_) => Err(FsError::Lock), - } - } - fn bytes_available_read(&self) -> Result, FsError> { - self.inner - .lock() - .map(|l| l.bytes_available_read()) - .unwrap_or(Ok(None)) - } -} - -impl WasiPipe { - /// Same as `set_blocking`, but as a builder method - pub fn with_blocking(mut self, block: bool) -> Self { - self.set_blocking(block); - self - } - - /// Whether to block on reads (ususally for waiting for stdin keyboard input). Default: `true` - pub fn set_blocking(&mut self, block: bool) { - self.block = block; + (pipe1, pipe2) } pub fn recv( @@ -283,113 +94,26 @@ impl WasiPipe { } } -impl Write for WasiPipe { - fn write(&mut self, buf: &[u8]) -> io::Result { - let buf_len = buf.len(); - let tx = self.tx.lock().unwrap(); - tx.send(buf.to_vec()) - .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{e}")))?; - Ok(buf_len) - } - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl Seek for WasiPipe { - fn seek(&mut self, _: SeekFrom) -> io::Result { - Ok(0) - } -} - impl Read for WasiPipe { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { loop { if let Some(inner_buf) = self.read_buffer.as_mut() { let buf_len = inner_buf.len(); if buf_len > 0 { - if inner_buf.len() > buf.len() { - let mut reader = inner_buf.as_ref(); - let read = reader.read_exact(buf).map(|_| buf.len())?; - inner_buf.advance(read); - return Ok(read); - } else { - let mut reader = inner_buf.as_ref(); - let read = reader.read(buf).map(|_| buf_len as usize)?; - inner_buf.advance(read); - return Ok(read); - } + let mut reader = inner_buf.as_ref(); + let read = reader.read(buf).map(|_| buf_len as usize)?; + inner_buf.advance(read); + return Ok(read); } } let rx = self.rx.lock().unwrap(); - - // We need to figure out whether we need to block here. - // The problem is that in cases of multiple buffered reads like: - // - // println!("abc"); - // println!("def"); - // - // get_stdout() // would only return "abc\n" instead of "abc\ndef\n" - - let data = match rx.try_recv() { - Ok(mut s) => { - s.append(&mut rx.try_iter().flat_map(|f| f.into_iter()).collect()); - s - } - Err(_) => { - if !self.block { - // If self.block is explicitly set to false, never block - Vec::new() - } else { - // could not immediately receive bytes, so we need to block - match rx.recv() { - Ok(o) => o, - // Errors can happen if the sender has been dropped already - // In this case, just return 0 to indicate that we can't read any - // bytes anymore - Err(_) => { - return Ok(0); - } - } - } - } - }; - if data.is_empty() && self.read_buffer.as_ref().map(|s| s.len()).unwrap_or(0) == 0 { - return Ok(0); - } + let data = rx.recv().map_err(|_| { + io::Error::new( + io::ErrorKind::BrokenPipe, + "the wasi pipe is not connected".to_string(), + ) + })?; self.read_buffer.replace(Bytes::from(data)); } } } - -impl VirtualFile for WasiPipe { - fn last_accessed(&self) -> u64 { - 0 - } - fn last_modified(&self) -> u64 { - 0 - } - fn created_time(&self) -> u64 { - 0 - } - fn size(&self) -> u64 { - self.read_buffer - .as_ref() - .map(|s| s.len() as u64) - .unwrap_or_default() - } - fn set_len(&mut self, _: u64) -> Result<(), FsError> { - Ok(()) - } - fn unlink(&mut self) -> Result<(), FsError> { - Ok(()) - } - fn bytes_available_read(&self) -> Result, FsError> { - Ok(Some( - self.read_buffer - .as_ref() - .map(|s| s.len()) - .unwrap_or_default(), - )) - } -} diff --git a/lib/wasi/src/state/types.rs b/lib/wasi/src/state/types.rs index 08b448409bd..f4aca85ab3f 100644 --- a/lib/wasi/src/state/types.rs +++ b/lib/wasi/src/state/types.rs @@ -3,7 +3,12 @@ use serde::{Deserialize, Serialize}; #[cfg(all(unix, feature = "sys-poll"))] use std::convert::TryInto; -use std::time::Duration; +use std::{ + collections::VecDeque, + io::{self, Read, Seek, Write}, + sync::{Arc, Mutex}, + time::Duration, +}; use wasmer_vbus::BusError; use wasmer_wasi_types::wasi::{BusErrno, Errno}; @@ -371,11 +376,79 @@ pub(crate) fn poll( pub trait WasiPath {} -#[deprecated( - since = "3.0.0-beta.2", - note = "Moved to `wasmer_wasi::pipe::WasiBidirectionalSharedPipePair`, `Pipe` is only a transitional reexport" -)] -pub use crate::state::WasiBidirectionalSharedPipePair as Pipe; +/// For piping stdio. Stores all output / input in a byte-vector. +#[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub struct Pipe { + buffer: Arc>>, +} + +impl Pipe { + pub fn new() -> Self { + Self::default() + } +} + +impl Read for Pipe { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let mut buffer = self.buffer.lock().unwrap(); + let amt = std::cmp::min(buf.len(), buffer.len()); + let buf_iter = buffer.drain(..amt).enumerate(); + for (i, byte) in buf_iter { + buf[i] = byte; + } + Ok(amt) + } +} + +impl Write for Pipe { + fn write(&mut self, buf: &[u8]) -> io::Result { + let mut buffer = self.buffer.lock().unwrap(); + buffer.extend(buf); + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Seek for Pipe { + fn seek(&mut self, _pos: io::SeekFrom) -> io::Result { + Err(io::Error::new( + io::ErrorKind::Other, + "can not seek in a pipe", + )) + } +} + +//#[cfg_attr(feature = "enable-serde", typetag::serde)] +impl VirtualFile for Pipe { + fn last_accessed(&self) -> u64 { + 0 + } + fn last_modified(&self) -> u64 { + 0 + } + fn created_time(&self) -> u64 { + 0 + } + fn size(&self) -> u64 { + let buffer = self.buffer.lock().unwrap(); + buffer.len() as u64 + } + fn set_len(&mut self, len: u64) -> Result<(), FsError> { + let mut buffer = self.buffer.lock().unwrap(); + buffer.resize(len as usize, 0); + Ok(()) + } + fn unlink(&mut self) -> Result<(), FsError> { + Ok(()) + } + fn bytes_available_read(&self) -> Result, FsError> { + let buffer = self.buffer.lock().unwrap(); + Ok(Some(buffer.len())) + } +} /* TODO: Think about using this diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index f92a1625e33..b6476fb5c9b 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -43,7 +43,7 @@ use crate::{ state::{ self, fs_error_into_wasi_err, iterate_poll_events, net_error_into_wasi_err, poll, virtual_file_type_to_wasi_file_type, Inode, InodeSocket, InodeSocketKind, InodeVal, Kind, - PollEvent, PollEventBuilder, WasiBidirectionalPipePair, WasiState, MAX_SYMLINKS, + PollEvent, PollEventBuilder, WasiPipe, WasiState, MAX_SYMLINKS, }, Fd, WasiEnv, WasiError, WasiThread, WasiThreadId, }; @@ -1804,9 +1804,7 @@ pub fn fd_pipe( let env = ctx.data(); let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0); - let pipes = WasiBidirectionalPipePair::new(); - let pipe1 = pipes.send; - let pipe2 = pipes.recv; + let (pipe1, pipe2) = WasiPipe::new(); let inode1 = state.fs.create_inode_with_default_stat( inodes.deref_mut(), diff --git a/lib/wasi/tests/stdio.rs b/lib/wasi/tests/stdio.rs index fc29889ebc1..78b93520e1a 100644 --- a/lib/wasi/tests/stdio.rs +++ b/lib/wasi/tests/stdio.rs @@ -1,7 +1,7 @@ use std::io::{Read, Write}; use wasmer::{Instance, Module, Store}; -use wasmer_wasi::{WasiBidirectionalSharedPipePair, WasiState}; +use wasmer_wasi::{Pipe, WasiState}; mod sys { #[test] @@ -73,10 +73,10 @@ fn test_stdout() { "#).unwrap(); // Create the `WasiEnv`. - let mut pipe = WasiBidirectionalSharedPipePair::new().with_blocking(false); + let mut stdout = Pipe::default(); let wasi_env = WasiState::new("command-name") .args(&["Gordon"]) - .stdout(Box::new(pipe.clone())) + .stdout(Box::new(stdout.clone())) .finalize(&mut store) .unwrap(); @@ -93,7 +93,7 @@ fn test_stdout() { start.call(&mut store, &[]).unwrap(); let mut stdout_str = String::new(); - pipe.read_to_string(&mut stdout_str).unwrap(); + stdout.read_to_string(&mut stdout_str).unwrap(); let stdout_as_str = stdout_str.as_str(); assert_eq!(stdout_as_str, "hello world\n"); } @@ -110,7 +110,7 @@ fn test_env() { }); // Create the `WasiEnv`. - let mut pipe = WasiBidirectionalSharedPipePair::new().with_blocking(false); + let mut stdout = Pipe::new(); let mut wasi_state_builder = WasiState::new("command-name"); wasi_state_builder .args(&["Gordon"]) @@ -119,7 +119,7 @@ fn test_env() { .env("TEST2", "VALUE2"); // panic!("envs: {:?}", wasi_state_builder.envs); let wasi_env = wasi_state_builder - .stdout(Box::new(pipe.clone())) + .stdout(Box::new(stdout.clone())) .finalize(&mut store) .unwrap(); @@ -136,7 +136,7 @@ fn test_env() { start.call(&mut store, &[]).unwrap(); let mut stdout_str = String::new(); - pipe.read_to_string(&mut stdout_str).unwrap(); + stdout.read_to_string(&mut stdout_str).unwrap(); let stdout_as_str = stdout_str.as_str(); assert_eq!(stdout_as_str, "Env vars:\nDOG=X\nTEST2=VALUE2\nTEST=VALUE\nDOG Ok(\"X\")\nDOG_TYPE Err(NotPresent)\nSET VAR Ok(\"HELLO\")\n"); } @@ -146,17 +146,16 @@ fn test_stdin() { let module = Module::new(&store, include_bytes!("stdin-hello.wasm")).unwrap(); // Create the `WasiEnv`. - let mut pipe = WasiBidirectionalSharedPipePair::new().with_blocking(false); - - // Write to STDIN - let buf = "Hello, stdin!\n".as_bytes().to_owned(); - pipe.write(&buf[..]).unwrap(); - + let mut stdin = Pipe::new(); let wasi_env = WasiState::new("command-name") - .stdin(Box::new(pipe.clone())) + .stdin(Box::new(stdin.clone())) .finalize(&mut store) .unwrap(); + // Write to STDIN + let buf = "Hello, stdin!\n".as_bytes().to_owned(); + stdin.write(&buf[..]).unwrap(); + // Generate an `ImportObject`. let import_object = wasi_env.import_object(&mut store, &module).unwrap(); @@ -168,10 +167,10 @@ fn test_stdin() { // Let's call the `_start` function, which is our `main` function in Rust. let start = instance.exports.get_function("_start").unwrap(); let result = start.call(&mut store, &[]); - assert!(result.is_ok()); + assert!(!result.is_err()); // We assure stdin is now empty let mut buf = Vec::new(); - pipe.read_to_end(&mut buf).unwrap(); + stdin.read_to_end(&mut buf).unwrap(); assert_eq!(buf.len(), 0); } diff --git a/tests/integration/cli/tests/create_exe.rs b/tests/integration/cli/tests/create_exe.rs index 866bef6a5ae..45969f7c69e 100644 --- a/tests/integration/cli/tests/create_exe.rs +++ b/tests/integration/cli/tests/create_exe.rs @@ -277,7 +277,7 @@ fn create_obj(args: Vec<&'static str>, keyword_needle: &str, keyword: &str) -> a let object_path = operating_dir.join("wasm.obj"); let output: Vec = WasmerCreateObj { - current_dir: operating_dir, + current_dir: operating_dir.clone(), wasm_path, output_object_path: object_path.clone(), compiler: Compiler::Cranelift, @@ -292,7 +292,7 @@ fn create_obj(args: Vec<&'static str>, keyword_needle: &str, keyword: &str) -> a "create-obj successfully completed but object output file `{}` missing", object_path.display() ); - let mut object_header_path = object_path; + let mut object_header_path = object_path.clone(); object_header_path.set_extension("h"); assert!( object_header_path.exists(), diff --git a/tests/integration/ios/tests/dylib.rs b/tests/integration/ios/tests/dylib.rs index db452fffb60..7660a62e4aa 100644 --- a/tests/integration/ios/tests/dylib.rs +++ b/tests/integration/ios/tests/dylib.rs @@ -40,8 +40,9 @@ mod tests { */ let command_success = command.status.success(); let test_success = !stderr.contains("** TEST FAILED **"); + let success = command_success && test_success; - command_success && test_success + success } fn remove_existing_artificats() -> Output { diff --git a/tests/lib/wast/src/wasi_wast.rs b/tests/lib/wast/src/wasi_wast.rs index 08195b64bd6..e31993deb0f 100644 --- a/tests/lib/wast/src/wasi_wast.rs +++ b/tests/lib/wast/src/wasi_wast.rs @@ -7,8 +7,8 @@ use wasmer::{FunctionEnv, Imports, Instance, Module, Store}; use wasmer_vfs::{host_fs, mem_fs, FileSystem}; use wasmer_wasi::types::wasi::{Filesize, Timestamp}; use wasmer_wasi::{ - generate_import_object_from_env, get_wasi_version, FsError, VirtualFile, - WasiBidirectionalPipePair, WasiEnv, WasiFunctionEnv, WasiState, WasiVersion, + generate_import_object_from_env, get_wasi_version, FsError, Pipe, VirtualFile, WasiEnv, + WasiFunctionEnv, WasiState, WasiVersion, }; use wast::parser::{self, Parse, ParseBuffer, Parser}; @@ -142,7 +142,7 @@ impl<'a> WasiTest<'a> { )> { let mut builder = WasiState::new(self.wasm_path); - let stdin_pipe = WasiBidirectionalPipePair::new().with_blocking(false); + let stdin_pipe = Pipe::new(); builder.stdin(Box::new(stdin_pipe)); for (name, value) in &self.envs {