mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-08 05:38:19 +00:00
Make capi unit test functional, finalize C API
This commit is contained in:
@@ -10,36 +10,43 @@ use super::{
|
||||
store::{wasm_store_t, StoreRef},
|
||||
};
|
||||
use crate::error::update_last_error;
|
||||
use std::{io::{self, SeekFrom}, fmt, convert::TryFrom, sync::MutexGuard};
|
||||
use std::ffi::CStr;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::{Mutex, Arc};
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
use std::slice;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
fmt,
|
||||
io::{self, SeekFrom},
|
||||
sync::MutexGuard,
|
||||
};
|
||||
use wasmer_wasi::{
|
||||
get_wasi_version, VirtualFile, FsError,
|
||||
WasiFile, WasiFunctionEnv, WasiState,
|
||||
WasiStateBuilder, WasiVersion,
|
||||
get_wasi_version, FsError, VirtualFile, WasiFile, WasiFunctionEnv, WasiState, WasiStateBuilder,
|
||||
WasiVersion,
|
||||
};
|
||||
|
||||
/// Function callback that takes:
|
||||
///
|
||||
/// - a *mut to the environment data (passed in on creation),
|
||||
///
|
||||
/// - 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(*mut c_char, usize, *mut c_char, usize) -> i64;
|
||||
pub type WasiConsoleIoWriteCallback = unsafe extern "C" fn(*mut c_char, usize, *const c_char, usize, bool) -> i64;
|
||||
pub type WasiConsoleIoSeekCallback = unsafe extern "C" fn(*mut c_char, usize, c_char, i64) -> i64;
|
||||
pub type WasiConsoleIoEnvDestructor = unsafe extern "C" fn (*mut c_char, usize) -> i64;
|
||||
/// - the length of the bytes to write
|
||||
pub type WasiConsoleIoReadCallback =
|
||||
unsafe extern "C" fn(*mut c_char, usize, usize, *mut c_char, usize) -> i64;
|
||||
pub type WasiConsoleIoWriteCallback =
|
||||
unsafe extern "C" fn(*mut c_char, usize, usize, *const c_char, usize, bool) -> i64;
|
||||
pub type WasiConsoleIoSeekCallback =
|
||||
unsafe extern "C" fn(*mut c_char, usize, usize, c_char, i64) -> i64;
|
||||
pub type WasiConsoleIoEnvDestructor = unsafe extern "C" fn(*mut c_char, usize, usize) -> i64;
|
||||
|
||||
/// The console override is a custom context consisting of callback pointers
|
||||
/// 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`,
|
||||
/// 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
|
||||
///
|
||||
/// 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)]
|
||||
#[repr(C)]
|
||||
@@ -49,47 +56,63 @@ pub struct wasi_console_io_override_t {
|
||||
write: WasiConsoleIoWriteCallback,
|
||||
seek: WasiConsoleIoSeekCallback,
|
||||
destructor: WasiConsoleIoEnvDestructor,
|
||||
data: Option<Arc<Mutex<Vec<c_char>>>>,
|
||||
data: Option<Box<Arc<Mutex<Vec<c_char>>>>>,
|
||||
align: usize,
|
||||
dropped: bool,
|
||||
}
|
||||
|
||||
impl wasi_console_io_override_t {
|
||||
fn get_data_mut(&mut self, op_id: &'static str) -> io::Result<MutexGuard<Vec<c_char>>> {
|
||||
self.data
|
||||
.as_mut()
|
||||
.ok_or({
|
||||
io::Error::new(io::ErrorKind::Other, format!("could not lock mutex ({op_id}) on wasi_console_io_override_t: no mutex"))
|
||||
})?
|
||||
.lock()
|
||||
.map_err(|e| {
|
||||
io::Error::new(io::ErrorKind::Other, format!("could not lock mutex ({op_id}) on wasi_console_io_override_t: {e}"))
|
||||
})
|
||||
.as_mut()
|
||||
.ok_or({
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"could not lock mutex ({op_id}) on wasi_console_io_override_t: no mutex"
|
||||
),
|
||||
)
|
||||
})?
|
||||
.lock()
|
||||
.map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("could not lock mutex ({op_id}) on wasi_console_io_override_t: {e}"),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for wasi_console_io_override_t {
|
||||
fn drop(&mut self) {
|
||||
|
||||
let align = self.align;
|
||||
let data = match self.data.take() {
|
||||
Some(s) => s,
|
||||
None => { return; },
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let value = match Arc::try_unwrap(data) {
|
||||
let value = match Arc::try_unwrap(*data) {
|
||||
Ok(o) => o,
|
||||
Err(_) => { return; },
|
||||
Err(_) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut inner_value = match value.into_inner() {
|
||||
Ok(o) => o,
|
||||
Err(_) => { return; },
|
||||
Err(_) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if self.dropped {
|
||||
return;
|
||||
}
|
||||
|
||||
let error = unsafe { (self.destructor)(inner_value.as_mut_ptr(), inner_value.len()) };
|
||||
let error =
|
||||
unsafe { (self.destructor)(inner_value.as_mut_ptr(), inner_value.len(), align) };
|
||||
if error <= 0 {
|
||||
println!("error dropping wasi_console_io_override_t: {error}");
|
||||
}
|
||||
@@ -107,12 +130,24 @@ impl fmt::Debug for wasi_console_io_override_t {
|
||||
impl io::Read for wasi_console_io_override_t {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let self_read = self.read.clone();
|
||||
let self_align = self.align;
|
||||
let mut data = self.get_data_mut("read")?;
|
||||
let result = unsafe { (self_read)(data.as_mut_ptr(), data.len(), buf.as_mut_ptr() as *mut c_char, buf.len()) };
|
||||
let result = unsafe {
|
||||
(self_read)(
|
||||
data.as_mut_ptr(),
|
||||
data.len(),
|
||||
self_align,
|
||||
buf.as_mut_ptr() as *mut c_char,
|
||||
buf.len(),
|
||||
)
|
||||
};
|
||||
if result >= 0 {
|
||||
Ok(result as usize)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, format!("could not read from wasi_console_io_override_t: {result}")))
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("could not read from wasi_console_io_override_t: {result}"),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,23 +155,52 @@ impl io::Read for wasi_console_io_override_t {
|
||||
impl io::Write for wasi_console_io_override_t {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let self_write = self.write.clone();
|
||||
let self_align = self.align;
|
||||
let mut data = self.get_data_mut("write")?;
|
||||
let result = unsafe { (self_write)(data.as_mut_ptr(), data.len(), buf.as_ptr() as *const c_char, buf.len(), false) };
|
||||
let result = unsafe {
|
||||
(self_write)(
|
||||
data.as_mut_ptr(),
|
||||
data.len(),
|
||||
self_align,
|
||||
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_console_io_override_t: {result}", buf.len())))
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"could not write {} bytes to wasi_console_io_override_t: {result}",
|
||||
buf.len()
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
let self_write = self.write.clone();
|
||||
let self_align = self.align;
|
||||
let mut data = self.get_data_mut("flush")?;
|
||||
let bytes_to_write = &[];
|
||||
let result: i64 = unsafe { (self_write)(data.as_mut_ptr(), data.len(), bytes_to_write.as_ptr(), 0, true) };
|
||||
let result: i64 = unsafe {
|
||||
(self_write)(
|
||||
data.as_mut_ptr(),
|
||||
data.len(),
|
||||
self_align,
|
||||
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_console_io_override_t: {result}")))
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("could not flush wasi_console_io_override_t: {result}"),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,28 +208,44 @@ impl io::Write for wasi_console_io_override_t {
|
||||
impl io::Seek for wasi_console_io_override_t {
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
let self_seek = self.seek.clone();
|
||||
let self_align = self.align;
|
||||
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.as_mut_ptr(), data.len(), id, pos) };
|
||||
let result = unsafe { (self_seek)(data.as_mut_ptr(), data.len(), self_align, 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_console_io_override_t: {result}")))
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("could not seek to {pos:?} wasi_console_io_override_t: {result}"),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualFile for wasi_console_io_override_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 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(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new callback object that is being
|
||||
@@ -177,9 +257,9 @@ pub unsafe extern "C" fn wasi_console_io_override_new(
|
||||
destructor: WasiConsoleIoEnvDestructor,
|
||||
env_data: *mut c_char,
|
||||
env_data_len: usize,
|
||||
env_data_align: usize,
|
||||
transfer_ownership: bool,
|
||||
) -> *mut wasi_console_io_override_t {
|
||||
|
||||
let data_vec = if transfer_ownership {
|
||||
std::slice::from_raw_parts(env_data, env_data_len).to_vec()
|
||||
} else {
|
||||
@@ -191,14 +271,209 @@ pub unsafe extern "C" fn wasi_console_io_override_new(
|
||||
write,
|
||||
seek,
|
||||
destructor,
|
||||
data: Some(Arc::new(Mutex::new(data_vec))),
|
||||
data: Some(Box::new(Arc::new(Mutex::new(data_vec)))),
|
||||
align: env_data_align,
|
||||
dropped: false,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Creates a `wasi_console_io_override_t` callback object that does nothing
|
||||
/// and redirects stdout / stderr to /dev/null
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_console_override_new_null() -> *mut wasi_console_io_override_t {
|
||||
let mut data = Vec::new();
|
||||
wasi_console_io_override_new(
|
||||
wasi_console_io_override_read_null,
|
||||
wasi_console_io_override_write_null,
|
||||
wasi_console_io_override_seek_null,
|
||||
wasi_console_io_override_delete_null,
|
||||
data.as_mut_ptr(),
|
||||
data.len(),
|
||||
std::mem::align_of_val(&data),
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
extern "C" fn wasi_console_io_override_read_null(
|
||||
_: *mut c_char,
|
||||
_: usize,
|
||||
_: usize,
|
||||
_: *mut c_char,
|
||||
_: usize,
|
||||
) -> i64 {
|
||||
0
|
||||
}
|
||||
extern "C" fn wasi_console_io_override_write_null(
|
||||
_: *mut c_char,
|
||||
_: usize,
|
||||
_: usize,
|
||||
_: *const c_char,
|
||||
_: usize,
|
||||
_: bool,
|
||||
) -> i64 {
|
||||
0
|
||||
}
|
||||
extern "C" fn wasi_console_io_override_seek_null(
|
||||
_: *mut c_char,
|
||||
_: usize,
|
||||
_: usize,
|
||||
_: c_char,
|
||||
_: i64,
|
||||
) -> i64 {
|
||||
0
|
||||
}
|
||||
extern "C" fn wasi_console_io_override_delete_null(_: *mut c_char, _: usize, _: usize) -> i64 {
|
||||
0
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct WasiConsoleMemoryOverride {
|
||||
backed: Vec<u8>,
|
||||
cursor: usize,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn wasi_console_io_override_read_memory(
|
||||
ptr: *mut c_char, /* = *WasiConsoleMemoryOverride */
|
||||
sizeof: usize, /* = sizeof(WasiConsoleMemoryOverride) */
|
||||
alignof: usize, /* = alignof(WasiConsoleMemoryOverride) */
|
||||
byte_ptr: *mut c_char, /* &[u8] bytes to read */
|
||||
max_bytes: usize, /* max bytes to read */
|
||||
) -> i64 {
|
||||
if sizeof != std::mem::size_of::<WasiConsoleMemoryOverride>()
|
||||
|| alignof != std::mem::align_of::<WasiConsoleMemoryOverride>()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
let ptr = ptr as *mut WasiConsoleMemoryOverride;
|
||||
let ptr = &mut *ptr;
|
||||
let read_slice = &ptr.backed[ptr.cursor..];
|
||||
let byte_ptr = byte_ptr as *mut u8;
|
||||
let write_slice = std::slice::from_raw_parts_mut(byte_ptr, max_bytes);
|
||||
let read = read_slice.len().min(write_slice.len());
|
||||
for (source, target) in read_slice.iter().zip(write_slice.iter_mut()) {
|
||||
*target = *source;
|
||||
}
|
||||
ptr.cursor += read;
|
||||
read as i64
|
||||
}
|
||||
|
||||
unsafe extern "C" fn wasi_console_io_override_write_memory(
|
||||
ptr: *mut c_char, /* = *WasiConsoleMemoryOverride */
|
||||
sizeof: usize, /* = sizeof(WasiConsoleMemoryOverride) */
|
||||
alignof: usize, /* = alignof(WasiConsoleMemoryOverride) */
|
||||
byte_ptr: *const c_char,
|
||||
byte_len: usize,
|
||||
flush: bool,
|
||||
) -> i64 {
|
||||
if sizeof != std::mem::size_of::<WasiConsoleMemoryOverride>()
|
||||
|| alignof != std::mem::align_of::<WasiConsoleMemoryOverride>()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if flush {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let ptr = ptr as *mut WasiConsoleMemoryOverride;
|
||||
let ptr = &mut *ptr;
|
||||
let byte_ptr = byte_ptr as *const u8;
|
||||
let read_slice = std::slice::from_raw_parts(byte_ptr, byte_len);
|
||||
let bytes_to_extend = ptr.cursor + read_slice.len();
|
||||
if bytes_to_extend > ptr.backed.len() {
|
||||
ptr.backed
|
||||
.append(&mut vec![0; bytes_to_extend - ptr.backed.len()]);
|
||||
}
|
||||
let write_slice = &mut ptr.backed[ptr.cursor..(ptr.cursor + read_slice.len())];
|
||||
assert_eq!(write_slice.len(), read_slice.len());
|
||||
let written = read_slice.len();
|
||||
for (source, target) in read_slice.iter().zip(write_slice.iter_mut()) {
|
||||
*target = *source;
|
||||
}
|
||||
written as i64
|
||||
}
|
||||
|
||||
unsafe extern "C" fn wasi_console_io_override_seek_memory(
|
||||
ptr: *mut c_char, /* = *WasiConsoleMemoryOverride */
|
||||
sizeof: usize, /* = sizeof(WasiConsoleMemoryOverride) */
|
||||
alignof: usize, /* = alignof(WasiConsoleMemoryOverride) */
|
||||
direction: c_char,
|
||||
seek_to: i64,
|
||||
) -> i64 {
|
||||
if sizeof != std::mem::size_of::<WasiConsoleMemoryOverride>()
|
||||
|| alignof != std::mem::align_of::<WasiConsoleMemoryOverride>()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
let ptr = ptr as *mut WasiConsoleMemoryOverride;
|
||||
let ptr = &mut *ptr;
|
||||
|
||||
if direction == 0 {
|
||||
// seek from start
|
||||
let seek_to = (seek_to.max(0_i64) as usize).min(ptr.backed.len());
|
||||
let diff = ptr.cursor as i64 - seek_to as i64;
|
||||
ptr.cursor = seek_to;
|
||||
diff
|
||||
} else if direction == 1 {
|
||||
// seek from end
|
||||
let seek_to = ptr.backed.len() as i64 + seek_to;
|
||||
let seek_to = (seek_to.max(0_i64) as usize).min(ptr.backed.len());
|
||||
let diff = ptr.cursor as i64 - seek_to as i64;
|
||||
ptr.cursor = seek_to;
|
||||
diff
|
||||
} else if direction == 2 {
|
||||
// seek from cursor
|
||||
let seek_to = ptr.cursor as i64 + seek_to;
|
||||
let seek_to = (seek_to.max(0_i64) as usize).min(ptr.backed.len());
|
||||
let diff = ptr.cursor as i64 - seek_to as i64;
|
||||
ptr.cursor = seek_to;
|
||||
diff
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn wasi_console_io_override_delete_memory(
|
||||
ptr: *mut c_char, /* = *WasiConsoleMemoryOverride */
|
||||
sizeof: usize, /* = sizeof(WasiConsoleMemoryOverride) */
|
||||
alignof: usize, /* = alignof(WasiConsoleMemoryOverride) */
|
||||
) -> i64 {
|
||||
if sizeof != std::mem::size_of::<WasiConsoleMemoryOverride>()
|
||||
|| alignof != std::mem::align_of::<WasiConsoleMemoryOverride>()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
let ptr = ptr as *mut WasiConsoleMemoryOverride;
|
||||
let ptr = &*ptr;
|
||||
let _: WasiConsoleMemoryOverride = std::mem::transmute_copy(&ptr);
|
||||
// dropped here, destructors run here
|
||||
0
|
||||
}
|
||||
|
||||
/// Creates a new `wasi_console_io_override_t` which uses a memory buffer
|
||||
/// for backing stdin / stdout / stderr
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_console_io_override_new_memory() -> *mut wasi_console_io_override_t {
|
||||
use std::mem::ManuallyDrop;
|
||||
|
||||
let data = WasiConsoleMemoryOverride::default();
|
||||
let mut data = ManuallyDrop::new(data);
|
||||
|
||||
wasi_console_io_override_new(
|
||||
wasi_console_io_override_read_memory,
|
||||
wasi_console_io_override_write_memory,
|
||||
wasi_console_io_override_seek_memory,
|
||||
wasi_console_io_override_delete_memory,
|
||||
&mut data as *mut _ as *mut i8,
|
||||
std::mem::size_of::<WasiConsoleMemoryOverride>(),
|
||||
std::mem::align_of::<WasiConsoleMemoryOverride>(),
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_console_io_override_delete(
|
||||
ptr: *mut wasi_console_io_override_t
|
||||
ptr: *mut wasi_console_io_override_t,
|
||||
) -> bool {
|
||||
let _ = Box::from_raw(ptr);
|
||||
true
|
||||
@@ -236,7 +511,7 @@ pub unsafe extern "C" fn wasi_console_io_override_write_str(
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_console_io_override_flush(
|
||||
ptr: *mut wasi_console_io_override_t
|
||||
ptr: *mut wasi_console_io_override_t,
|
||||
) -> i64 {
|
||||
use std::io::Write;
|
||||
let ptr = &mut *ptr;
|
||||
@@ -263,9 +538,7 @@ pub unsafe extern "C" fn wasi_console_io_override_read_bytes(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_console_io_override_delete_str(
|
||||
buf: *mut c_char,
|
||||
) {
|
||||
pub unsafe extern "C" fn wasi_console_io_override_delete_str(buf: *mut c_char) {
|
||||
use std::ffi::CString;
|
||||
let _ = CString::from_raw(buf);
|
||||
}
|
||||
@@ -277,19 +550,25 @@ pub unsafe extern "C" fn wasi_console_io_override_read_str(
|
||||
) -> i64 {
|
||||
use std::ffi::CString;
|
||||
use std::io::Read;
|
||||
|
||||
|
||||
const BLOCK_SIZE: usize = 1024;
|
||||
|
||||
|
||||
let mut target = Vec::new();
|
||||
let ptr = &mut *ptr;
|
||||
|
||||
|
||||
loop {
|
||||
let mut v = vec![0;BLOCK_SIZE];
|
||||
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; }
|
||||
Ok(0) => {
|
||||
break;
|
||||
}
|
||||
Ok(n) => {
|
||||
target.extend_from_slice(&v[..n]);
|
||||
}
|
||||
Err(_) => {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,7 +576,9 @@ pub unsafe extern "C" fn wasi_console_io_override_read_str(
|
||||
let len = target.len();
|
||||
let c_string = match CString::from_vec_with_nul(target) {
|
||||
Ok(o) => o,
|
||||
Err(_) => { return -1; },
|
||||
Err(_) => {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
*buf = CString::into_raw(c_string);
|
||||
@@ -313,52 +594,32 @@ pub unsafe extern "C" fn wasi_console_io_override_seek(
|
||||
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; },
|
||||
_ => {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
let ptr = &mut *ptr;
|
||||
|
||||
ptr
|
||||
.seek(seek_pos).ok()
|
||||
.and_then(|p| p.try_into().ok())
|
||||
.unwrap_or(-1)
|
||||
|
||||
ptr.seek(seek_pos)
|
||||
.ok()
|
||||
.and_then(|p| p.try_into().ok())
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_console_io_override_clone(
|
||||
ptr: *const wasi_console_io_override_t
|
||||
ptr: *const wasi_console_io_override_t,
|
||||
) -> *mut wasi_console_io_override_t {
|
||||
Box::leak(Box::new((&*ptr).clone()))
|
||||
}
|
||||
|
||||
/// Creates a `wasi_console_io_override_t` callback object that does nothing
|
||||
/// and redirects stdout / stderr to /dev/null
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_console_override_new_null() -> *mut wasi_console_io_override_t {
|
||||
let mut data = Vec::new();
|
||||
wasi_console_io_override_new(
|
||||
wasi_console_io_override_read_default,
|
||||
wasi_console_io_override_write_default,
|
||||
wasi_console_io_override_seek_default,
|
||||
wasi_console_io_override_delete_default,
|
||||
data.as_mut_ptr(),
|
||||
data.len(),
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
extern "C" fn wasi_console_io_override_read_default(_: *mut c_char, _:usize, _:*mut c_char, _: usize) -> i64 { 0 }
|
||||
extern "C" fn wasi_console_io_override_write_default(_: *mut c_char, _: usize, _: *const c_char, _: usize, _: bool) -> i64 { 0 }
|
||||
extern "C" fn wasi_console_io_override_seek_default(_: *mut c_char, _: usize, _: c_char, _: i64) -> i64 { 0 }
|
||||
extern "C" fn wasi_console_io_override_delete_default(_: *mut c_char, _: usize) -> i64 { 0 }
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct wasi_config_t {
|
||||
@@ -501,24 +762,24 @@ pub extern "C" fn wasi_config_inherit_stdin(config: &mut wasi_config_t) {
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_config_overwrite_stdin(
|
||||
config: &mut wasi_config_t,
|
||||
stdin: *mut wasi_console_io_override_t
|
||||
config: &mut wasi_config_t,
|
||||
stdin: *mut wasi_console_io_override_t,
|
||||
) {
|
||||
config.state_builder.stdin(Box::from_raw(stdin));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_config_overwrite_stdout(
|
||||
config: &mut wasi_config_t,
|
||||
stdout: *mut wasi_console_io_override_t
|
||||
config: &mut wasi_config_t,
|
||||
stdout: *mut wasi_console_io_override_t,
|
||||
) {
|
||||
config.state_builder.stdout(Box::from_raw(stdout));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_config_overwrite_stderr(
|
||||
config: &mut wasi_config_t,
|
||||
stderr: *mut wasi_console_io_override_t
|
||||
config: &mut wasi_config_t,
|
||||
stderr: *mut wasi_console_io_override_t,
|
||||
) {
|
||||
config.state_builder.stderr(Box::from_raw(stderr));
|
||||
}
|
||||
@@ -777,55 +1038,92 @@ mod tests {
|
||||
fn test_wasi_get_wasi_version_snapshot0() {
|
||||
(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);
|
||||
wasmer_funcenv_t* env = wasmer_funcenv_new(store, 0);
|
||||
wasi_config_t* config = wasi_config_new("example_program");
|
||||
|
||||
wasm_byte_vec_t wat;
|
||||
wasmer_byte_vec_new_from_string(&wat, "(module (import \"wasi_unstable\" \"args_get\" (func (param i32 i32) (result i32))))");
|
||||
wasm_byte_vec_t wasm;
|
||||
wat2wasm(&wat, &wasm);
|
||||
|
||||
wasi_console_io_override_t* override_stdin = wasi_console_io_override_new(
|
||||
|
||||
);
|
||||
wasi_console_io_override_t* override_stdout = wasi_console_io_override_new(
|
||||
|
||||
);
|
||||
wasi_console_io_override_t* override_stdin = wasi_console_io_override_new(
|
||||
|
||||
);
|
||||
wasi_console_io_override_t* override_stdin = wasi_console_io_override_new_memory();
|
||||
wasi_console_io_override_t* override_stdout = wasi_console_io_override_new_memory();
|
||||
wasi_console_io_override_t* override_stderr = wasi_console_io_override_new_memory();
|
||||
|
||||
// Cloning the `wasi_console_io_override_t` does not deep-clone the
|
||||
// internal stream, since that is locked behind an Arc<Mutex<T>>.
|
||||
wasi_console_io_override_t* stdin_sender = wasi_console_io_override_clone(&override_stdin);
|
||||
wasi_console_io_override_t* stdout_receiver = wasi_console_io_override_clone(&override_stdout);
|
||||
wasi_console_io_override_t* stderr_receiver = wasi_console_io_override_clone(&override_stderr);
|
||||
wasi_console_io_override_t* stdin_sender = wasi_console_io_override_clone(override_stdin);
|
||||
wasi_console_io_override_t* stdout_receiver = wasi_console_io_override_clone(override_stdout);
|
||||
wasi_console_io_override_t* stderr_receiver = wasi_console_io_override_clone(override_stderr);
|
||||
|
||||
wasm_module_t* module = wasm_module_new(store, &wasm);
|
||||
// The override_stdin ownership is moved to the config
|
||||
wasi_config_overwrite_stdin(config, override_stdin);
|
||||
wasi_config_overwrite_stdout(config, override_stdout);
|
||||
wasi_config_overwrite_stderr(config, override_stderr);
|
||||
|
||||
// The env now has ownership of the config (using the custom stdout / stdin channels)
|
||||
wasmer_funcenv_t* env = wasmer_funcenv_new(store, config);
|
||||
|
||||
FILE *fileptr;
|
||||
char *buffer;
|
||||
long filelen;
|
||||
|
||||
fileptr = fopen("tests/wasm-c-api/example/stdio.wasm", "rb");
|
||||
if (!fileptr) {
|
||||
assert(false); // file not found
|
||||
}
|
||||
fseek(fileptr, 0, SEEK_END);
|
||||
filelen = ftell(fileptr);
|
||||
rewind(fileptr);
|
||||
|
||||
buffer = (char *)malloc(filelen * sizeof(char));
|
||||
if (!fread(buffer, filelen, 1, fileptr)) {
|
||||
assert(false);
|
||||
}
|
||||
fclose(fileptr);
|
||||
|
||||
// convert to wasm_byte_vec_t
|
||||
wasm_byte_vec_t* wasm = 0;
|
||||
wasm_byte_vec_new(wasm, filelen, buffer);
|
||||
|
||||
wasm_module_t* module = wasm_module_new(store, wasm);
|
||||
assert(module);
|
||||
|
||||
assert(wasi_get_wasi_version(module) == SNAPSHOT0);
|
||||
|
||||
|
||||
// The program should wait for a stdin, then print "stdout: $1" to stdout
|
||||
// and "stderr: $1" to stderr and exit.
|
||||
wasi_console_io_override_write_str(stdin_sender, "hello\0");
|
||||
|
||||
assert(wasi_console_io_override_read_str(stdout_receiver), "stdout: hello\0");
|
||||
assert(wasi_console_io_override_read_str(stdout_receiver), "\0");
|
||||
wasi_console_io_override_write_str(stdin_sender, "hello");
|
||||
|
||||
assert(wasi_console_io_override_read_str(stderr_receiver), "stderr: hello\0");
|
||||
assert(wasi_console_io_override_read_str(stderr_receiver), "\0");
|
||||
char* out;
|
||||
|
||||
wasi_console_io_override_read_str(stdout_receiver, &out);
|
||||
printf("stdout 1: \"%s\"\n", out);
|
||||
assert(strcmp(out, "stdout: hello") == 0);
|
||||
wasi_console_io_override_delete_str(out);
|
||||
|
||||
wasi_console_io_override_read_str(stdout_receiver, &out);
|
||||
printf("stdout 2: \"%s\"\n", out);
|
||||
assert(strcmp(out, ""));
|
||||
wasi_console_io_override_delete_str(out);
|
||||
|
||||
wasi_console_io_override_read_str(stderr_receiver, &out);
|
||||
printf("stdout 3: \"%s\"\n", out);
|
||||
assert(strcmp(out, "stderr: hello"));
|
||||
wasi_console_io_override_delete_str(out);
|
||||
|
||||
wasi_console_io_override_read_str(stderr_receiver, &out);
|
||||
printf("stdout 4: \"%s\"\n", out);
|
||||
assert(strcmp(out, ""));
|
||||
wasi_console_io_override_delete_str(out);
|
||||
|
||||
wasi_console_io_override_delete(stdin_sender);
|
||||
wasi_console_io_override_delete(stdout_receiver);
|
||||
wasi_console_io_override_delete(stderr_receiver);
|
||||
|
||||
free(buffer);
|
||||
wasm_module_delete(module);
|
||||
wasm_byte_vec_delete(&wasm);
|
||||
wasm_byte_vec_delete(&wat);
|
||||
wasm_byte_vec_delete(wasm);
|
||||
wasmer_funcenv_delete(env);
|
||||
wasm_store_delete(store);
|
||||
wasm_engine_delete(engine);
|
||||
@@ -901,42 +1199,4 @@ mod tests {
|
||||
})
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wasi_stdin_set() {
|
||||
(assert_c! {
|
||||
#include "tests/wasmer.h"
|
||||
|
||||
struct MyCustomStdin { }
|
||||
|
||||
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_config_capture_stdout(config);
|
||||
wasi_config_overwrite_stdin(config);
|
||||
|
||||
wasm_byte_vec_t wat;
|
||||
wasmer_byte_vec_new_from_string(&wat, "(module (import \"wasi_unstable\" \"args_get\" (func (param i32 i32) (result i32))))");
|
||||
wasm_byte_vec_t wasm;
|
||||
wat2wasm(&wat, &wasm);
|
||||
|
||||
wasm_module_t* module = wasm_module_new(store, &wasm);
|
||||
assert(module);
|
||||
|
||||
// TODO FIXME
|
||||
//
|
||||
// Test captured stdin
|
||||
|
||||
wasm_module_delete(module);
|
||||
wasm_byte_vec_delete(&wasm);
|
||||
wasm_byte_vec_delete(&wat);
|
||||
wasm_store_delete(store);
|
||||
wasm_engine_delete(engine);
|
||||
|
||||
return 0;
|
||||
}
|
||||
})
|
||||
.success();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user