Implement RFC 11: Redesigning Wasmtime's APIs (#2897)

Implement Wasmtime's new API as designed by RFC 11. This is quite a large commit which has had lots of discussion externally, so for more information it's best to read the RFC thread and the PR thread.
This commit is contained in:
Alex Crichton
2021-06-03 09:10:53 -05:00
committed by GitHub
parent a5a28b1c5b
commit 7a1b7cdf92
233 changed files with 13349 additions and 11997 deletions

View File

@@ -1,114 +1,105 @@
use std::cell::Cell;
use std::cell::RefCell;
use anyhow::Result;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use wasmtime::*;
fn async_store() -> Store {
Store::new(&Engine::new(Config::new().async_support(true)).unwrap())
fn async_store() -> Store<()> {
Store::new(&Engine::new(Config::new().async_support(true)).unwrap(), ())
}
fn run_smoke_test(func: &Func) {
run(func.call_async(&[])).unwrap();
run(func.call_async(&[])).unwrap();
let future1 = func.call_async(&[]);
let future2 = func.call_async(&[]);
run(future2).unwrap();
run(future1).unwrap();
fn run_smoke_test(store: &mut Store<()>, func: Func) {
run(func.call_async(&mut *store, &[])).unwrap();
run(func.call_async(&mut *store, &[])).unwrap();
}
fn run_smoke_typed_test(func: &Func) {
let func = func.typed::<(), ()>().unwrap();
run(func.call_async(())).unwrap();
run(func.call_async(())).unwrap();
let future1 = func.call_async(());
let future2 = func.call_async(());
run(future2).unwrap();
run(future1).unwrap();
fn run_smoke_typed_test(store: &mut Store<()>, func: Func) {
let func = func.typed::<(), (), _>(&store).unwrap();
run(func.call_async(&mut *store, ())).unwrap();
run(func.call_async(&mut *store, ())).unwrap();
}
#[test]
fn smoke() {
let store = async_store();
let mut store = async_store();
let func = Func::new_async(
&store,
&mut store,
FuncType::new(None, None),
(),
move |_caller, _state, _params, _results| Box::new(async { Ok(()) }),
move |_caller, _params, _results| Box::new(async { Ok(()) }),
);
run_smoke_test(&func);
run_smoke_typed_test(&func);
run_smoke_test(&mut store, func);
run_smoke_typed_test(&mut store, func);
let func = Func::wrap0_async(&store, (), move |_caller: Caller<'_>, _state| {
Box::new(async { Ok(()) })
});
run_smoke_test(&func);
run_smoke_typed_test(&func);
let func = Func::wrap0_async(&mut store, move |_caller| Box::new(async { Ok(()) }));
run_smoke_test(&mut store, func);
run_smoke_typed_test(&mut store, func);
}
#[test]
fn smoke_host_func() {
let mut config = Config::new();
config.async_support(true);
config.define_host_func_async(
fn smoke_host_func() -> Result<()> {
let mut store = async_store();
let mut linker = Linker::new(store.engine());
linker.func_new_async(
"",
"first",
FuncType::new(None, None),
move |_caller, _params, _results| Box::new(async { Ok(()) }),
);
config.wrap0_host_func_async("", "second", move |_caller: Caller<'_>| {
Box::new(async { Ok(()) })
});
)?;
let store = Store::new(&Engine::new(&config).unwrap());
linker.func_wrap0_async("", "second", move |_caller| Box::new(async { Ok(()) }))?;
let func = store
.get_host_func("", "first")
.expect("expected host function");
run_smoke_test(&func);
run_smoke_typed_test(&func);
let func = linker
.get(&mut store, "", Some("first"))
.unwrap()
.into_func()
.unwrap();
run_smoke_test(&mut store, func);
run_smoke_typed_test(&mut store, func);
let func = store
.get_host_func("", "second")
.expect("expected host function");
run_smoke_test(&func);
run_smoke_typed_test(&func);
let func = linker
.get(&mut store, "", Some("second"))
.unwrap()
.into_func()
.unwrap();
run_smoke_test(&mut store, func);
run_smoke_typed_test(&mut store, func);
Ok(())
}
#[test]
fn smoke_with_suspension() {
let store = async_store();
let mut store = async_store();
let func = Func::new_async(
&store,
&mut store,
FuncType::new(None, None),
(),
move |_caller, _state, _params, _results| {
move |_caller, _params, _results| {
Box::new(async {
PendingOnce::default().await;
Ok(())
})
},
);
run_smoke_test(&func);
run_smoke_typed_test(&func);
run_smoke_test(&mut store, func);
run_smoke_typed_test(&mut store, func);
let func = Func::wrap0_async(&store, (), move |_caller: Caller<'_>, _state| {
let func = Func::wrap0_async(&mut store, move |_caller| {
Box::new(async {
PendingOnce::default().await;
Ok(())
})
});
run_smoke_test(&func);
run_smoke_typed_test(&func);
run_smoke_test(&mut store, func);
run_smoke_typed_test(&mut store, func);
}
#[test]
fn smoke_host_func_with_suspension() {
let mut config = Config::new();
config.async_support(true);
config.define_host_func_async(
fn smoke_host_func_with_suspension() -> Result<()> {
let mut store = async_store();
let mut linker = Linker::new(store.engine());
linker.func_new_async(
"",
"first",
FuncType::new(None, None),
@@ -118,56 +109,57 @@ fn smoke_host_func_with_suspension() {
Ok(())
})
},
);
config.wrap0_host_func_async("", "second", move |_caller: Caller<'_>| {
)?;
linker.func_wrap0_async("", "second", move |_caller| {
Box::new(async {
PendingOnce::default().await;
Ok(())
})
});
})?;
let store = Store::new(&Engine::new(&config).unwrap());
let func = linker
.get(&mut store, "", Some("first"))
.unwrap()
.into_func()
.unwrap();
run_smoke_test(&mut store, func);
run_smoke_typed_test(&mut store, func);
let func = store
.get_host_func("", "first")
.expect("expected host function");
run_smoke_test(&func);
run_smoke_typed_test(&func);
let func = linker
.get(&mut store, "", Some("second"))
.unwrap()
.into_func()
.unwrap();
run_smoke_test(&mut store, func);
run_smoke_typed_test(&mut store, func);
let func = store
.get_host_func("", "second")
.expect("expected host function");
run_smoke_test(&func);
run_smoke_typed_test(&func);
Ok(())
}
#[test]
fn recursive_call() {
let store = async_store();
let async_wasm_func = Rc::new(Func::new_async(
&store,
let mut store = async_store();
let async_wasm_func = Func::new_async(
&mut store,
FuncType::new(None, None),
(),
|_caller, _state, _params, _results| {
|_caller, _params, _results| {
Box::new(async {
PendingOnce::default().await;
Ok(())
})
},
));
let weak = Rc::downgrade(&async_wasm_func);
);
// Create an imported function which recursively invokes another wasm
// function asynchronously, although this one is just our own host function
// which suffices for this test.
let func2 = Func::new_async(
&store,
&mut store,
FuncType::new(None, None),
(),
move |_caller, _state, _params, _results| {
let async_wasm_func = weak.upgrade().unwrap();
move |mut caller, _params, _results| {
Box::new(async move {
async_wasm_func.call_async(&[]).await?;
async_wasm_func.call_async(&mut caller, &[]).await?;
Ok(())
})
},
@@ -190,16 +182,16 @@ fn recursive_call() {
.unwrap();
run(async {
let instance = Instance::new_async(&store, &module, &[func2.into()]).await?;
let func = instance.get_func("").unwrap();
func.call_async(&[]).await
let instance = Instance::new_async(&mut store, &module, &[func2.into()]).await?;
let func = instance.get_func(&mut store, "").unwrap();
func.call_async(&mut store, &[]).await
})
.unwrap();
}
#[test]
fn suspend_while_suspending() {
let store = async_store();
let mut store = async_store();
// Create a synchronous function which calls our asynchronous function and
// runs it locally. This shouldn't generally happen but we know everything
@@ -208,19 +200,16 @@ fn suspend_while_suspending() {
// The purpose of this test is intended to stress various cases in how
// we manage pointers in ways that are not necessarily common but are still
// possible in safe code.
let async_thunk = Rc::new(Func::new_async(
&store,
let async_thunk = Func::new_async(
&mut store,
FuncType::new(None, None),
(),
|_caller, _state, _params, _results| Box::new(async { Ok(()) }),
));
let weak = Rc::downgrade(&async_thunk);
|_caller, _params, _results| Box::new(async { Ok(()) }),
);
let sync_call_async_thunk = Func::new(
&store,
&mut store,
FuncType::new(None, None),
move |_caller, _params, _results| {
let async_thunk = weak.upgrade().unwrap();
run(async_thunk.call_async(&[]))?;
move |mut caller, _params, _results| {
run(async_thunk.call_async(&mut caller, &[]))?;
Ok(())
},
);
@@ -228,10 +217,9 @@ fn suspend_while_suspending() {
// A small async function that simply awaits once to pump the loops and
// then finishes.
let async_import = Func::new_async(
&store,
&mut store,
FuncType::new(None, None),
(),
move |_caller, _state, _params, _results| {
move |_caller, _params, _results| {
Box::new(async move {
PendingOnce::default().await;
Ok(())
@@ -255,31 +243,28 @@ fn suspend_while_suspending() {
.unwrap();
run(async {
let instance = Instance::new_async(
&store,
&mut store,
&module,
&[sync_call_async_thunk.into(), async_import.into()],
)
.await?;
let func = instance.get_func("").unwrap();
func.call_async(&[]).await
let func = instance.get_func(&mut store, "").unwrap();
func.call_async(&mut store, &[]).await
})
.unwrap();
}
#[test]
fn cancel_during_run() {
let store = async_store();
let state = Rc::new(Cell::new(0));
let state2 = state.clone();
let mut store = Store::new(&Engine::new(Config::new().async_support(true)).unwrap(), 0);
let async_thunk = Func::new_async(
&store,
&mut store,
FuncType::new(None, None),
(),
move |_caller, _state, _params, _results| {
assert_eq!(state2.get(), 0);
state2.set(1);
let dtor = SetOnDrop(state2.clone());
move |mut caller, _params, _results| {
assert_eq!(*caller.data(), 0);
*caller.data_mut() = 1;
let dtor = SetOnDrop(caller);
Box::new(async move {
drop(&dtor);
PendingOnce::default().await;
@@ -288,12 +273,11 @@ fn cancel_during_run() {
},
);
// Shouldn't have called anything yet...
assert_eq!(state.get(), 0);
assert_eq!(*store.data(), 0);
// Create our future, but as per async conventions this still doesn't
// actually do anything. No wasm or host function has been called yet.
let mut future = Pin::from(Box::new(async_thunk.call_async(&[])));
assert_eq!(state.get(), 0);
let mut future = Pin::from(Box::new(async_thunk.call_async(&mut store, &[])));
// Push the future forward one tick, which actually runs the host code in
// our async func. Our future is designed to be pending once, however.
@@ -301,19 +285,18 @@ fn cancel_during_run() {
.as_mut()
.poll(&mut Context::from_waker(&dummy_waker()));
assert!(poll.is_pending());
assert_eq!(state.get(), 1);
// Now that our future is running (on a separate, now-suspended fiber), drop
// the future and that should deallocate all the Rust bits as well.
drop(future);
assert_eq!(state.get(), 2);
assert_eq!(*store.data(), 2);
struct SetOnDrop(Rc<Cell<u32>>);
struct SetOnDrop<'a>(Caller<'a, usize>);
impl Drop for SetOnDrop {
impl Drop for SetOnDrop<'_> {
fn drop(&mut self) {
assert_eq!(self.0.get(), 1);
self.0.set(2);
assert_eq!(*self.0.data(), 1);
*self.0.data_mut() = 2;
}
}
}
@@ -374,7 +357,7 @@ fn dummy_waker() -> Waker {
#[test]
fn iloop_with_fuel() {
let engine = Engine::new(Config::new().async_support(true).consume_fuel(true)).unwrap();
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
store.out_of_fuel_async_yield(1_000, 10);
let module = Module::new(
&engine,
@@ -386,7 +369,7 @@ fn iloop_with_fuel() {
",
)
.unwrap();
let instance = Instance::new_async(&store, &module, &[]);
let instance = Instance::new_async(&mut store, &module, &[]);
let mut f = Pin::from(Box::new(instance));
let waker = dummy_waker();
let mut cx = Context::from_waker(&waker);
@@ -408,7 +391,7 @@ fn iloop_with_fuel() {
#[test]
fn fuel_eventually_finishes() {
let engine = Engine::new(Config::new().async_support(true).consume_fuel(true)).unwrap();
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
store.out_of_fuel_async_yield(u32::max_value(), 10);
let module = Module::new(
&engine,
@@ -430,7 +413,7 @@ fn fuel_eventually_finishes() {
",
)
.unwrap();
let instance = Instance::new_async(&store, &module, &[]);
let instance = Instance::new_async(&mut store, &module, &[]);
run(instance).unwrap();
}
@@ -452,20 +435,19 @@ fn async_with_pooling_stacks() {
});
let engine = Engine::new(&config).unwrap();
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let func = Func::new_async(
&store,
&mut store,
FuncType::new(None, None),
(),
move |_caller, _state, _params, _results| Box::new(async { Ok(()) }),
move |_caller, _params, _results| Box::new(async { Ok(()) }),
);
run_smoke_test(&func);
run_smoke_typed_test(&func);
run_smoke_test(&mut store, func);
run_smoke_typed_test(&mut store, func);
}
#[test]
fn async_host_func_with_pooling_stacks() {
fn async_host_func_with_pooling_stacks() -> Result<()> {
let mut config = Config::new();
config.async_support(true);
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
@@ -481,32 +463,27 @@ fn async_host_func_with_pooling_stacks() {
},
});
config.define_host_func_async(
let mut store = Store::new(&Engine::new(&config)?, ());
let mut linker = Linker::new(store.engine());
linker.func_new_async(
"",
"",
FuncType::new(None, None),
move |_caller, _params, _results| Box::new(async { Ok(()) }),
);
)?;
let store = Store::new(&Engine::new(&config).unwrap());
let func = store.get_host_func("", "").expect("expected host function");
run_smoke_test(&func);
run_smoke_typed_test(&func);
let func = linker
.get(&mut store, "", Some(""))
.unwrap()
.into_func()
.unwrap();
run_smoke_test(&mut store, func);
run_smoke_typed_test(&mut store, func);
Ok(())
}
fn execute_across_threads<F: Future + 'static>(future: F) {
struct UnsafeSend<T>(T);
unsafe impl<T> Send for UnsafeSend<T> {}
impl<T: Future> Future for UnsafeSend<T> {
type Output = T::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T::Output> {
unsafe { self.map_unchecked_mut(|p| &mut p.0).poll(cx) }
}
}
let mut future = Pin::from(Box::new(UnsafeSend(future)));
fn execute_across_threads<F: Future + Send + 'static>(future: F) {
let mut future = Pin::from(Box::new(future));
let poll = future
.as_mut()
.poll(&mut Context::from_waker(&dummy_waker()));
@@ -527,7 +504,7 @@ fn resume_separate_thread() {
// This test will poll the following future on two threads. Simulating a
// trap requires accessing TLS info, so that should be preserved correctly.
execute_across_threads(async {
let store = async_store();
let mut store = async_store();
let module = Module::new(
store.engine(),
"
@@ -538,13 +515,13 @@ fn resume_separate_thread() {
",
)
.unwrap();
let func = Func::wrap0_async(&store, (), |_, _| {
let func = Func::wrap0_async(&mut store, |_| {
Box::new(async {
PendingOnce::default().await;
Err::<(), _>(wasmtime::Trap::new("test"))
})
});
let result = Instance::new_async(&store, &module, &[func.into()]).await;
let result = Instance::new_async(&mut store, &module, &[func.into()]).await;
assert!(result.is_err());
});
}
@@ -555,7 +532,7 @@ fn resume_separate_thread2() {
// signal requires looking up TLS information to determine whether it's a
// trap to handle or not, so that must be preserved correctly across threads.
execute_across_threads(async {
let store = async_store();
let mut store = async_store();
let module = Module::new(
store.engine(),
"
@@ -569,10 +546,10 @@ fn resume_separate_thread2() {
",
)
.unwrap();
let func = Func::wrap0_async(&store, (), |_, _| {
let func = Func::wrap0_async(&mut store, |_| {
Box::new(async { PendingOnce::default().await })
});
let result = Instance::new_async(&store, &module, &[func.into()]).await;
let result = Instance::new_async(&mut store, &module, &[func.into()]).await;
assert!(result.is_err());
});
}
@@ -587,30 +564,28 @@ fn resume_separate_thread3() {
// "enter into wasm" semantics since it's just calling a trampoline. In this
// situation we'll set up the TLS info so it's in place while the body of
// the function executes...
let store = Store::default();
let storage = Rc::new(RefCell::new(None));
let storage2 = storage.clone();
let f = Func::wrap(&store, move || {
let mut store = Store::new(&Engine::default(), None);
let f = Func::wrap(&mut store, move |mut caller: Caller<'_, _>| {
// ... and the execution of this host-defined function (while the TLS
// info is initialized), will set up a recursive call into wasm. This
// recursive call will be done asynchronously so we can suspend it
// halfway through.
let f = async {
let store = async_store();
let mut store = async_store();
let module = Module::new(
store.engine(),
"
(module
(import \"\" \"\" (func))
(start 0)
)
",
(module
(import \"\" \"\" (func))
(start 0)
)
",
)
.unwrap();
let func = Func::wrap0_async(&store, (), |_, _| {
let func = Func::wrap0_async(&mut store, |_| {
Box::new(async { PendingOnce::default().await })
});
drop(Instance::new_async(&store, &module, &[func.into()]).await);
drop(Instance::new_async(&mut store, &module, &[func.into()]).await);
unreachable!()
};
let mut future = Pin::from(Box::new(f));
@@ -626,12 +601,12 @@ fn resume_separate_thread3() {
// here then we would reenter the future's suspended stack to clean it
// up, which would do more alterations of TLS information we're not
// testing here.
*storage2.borrow_mut() = Some(future);
*caller.data_mut() = Some(future);
// ... all in all this function will need access to the original TLS
// information to raise the trap. This TLS information should be
// restored even though the asynchronous execution is suspended.
Err::<(), _>(wasmtime::Trap::new(""))
});
assert!(f.call(&[]).is_err());
assert!(f.call(&mut store, &[]).is_err());
}

View File

@@ -1,8 +1,8 @@
#[cfg(target_os = "linux")]
mod tests {
use anyhow::Result;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use wasmtime::unix::StoreExt;
use wasmtime::*;
@@ -41,16 +41,18 @@ mod tests {
)
"#;
fn invoke_export(instance: &Instance, func_name: &str) -> Result<i32> {
let ret = instance.get_typed_func::<(), i32>(func_name)?.call(())?;
fn invoke_export(store: &mut Store<()>, instance: Instance, func_name: &str) -> Result<i32> {
let ret = instance
.get_typed_func::<(), i32, _>(&mut *store, func_name)?
.call(store, ())?;
Ok(ret)
}
// Locate "memory" export, get base address and size and set memory protection to PROT_NONE
fn set_up_memory(instance: &Instance) -> (*mut u8, usize) {
let mem_export = instance.get_memory("memory").unwrap();
let base = mem_export.data_ptr();
let length = mem_export.data_size();
fn set_up_memory(store: &mut Store<()>, instance: Instance) -> (usize, usize) {
let mem_export = instance.get_memory(&mut *store, "memory").unwrap();
let base = mem_export.data_ptr(&store);
let length = mem_export.data_size(&store);
// So we can later trigger SIGSEGV by performing a read
unsafe {
@@ -59,11 +61,11 @@ mod tests {
println!("memory: base={:?}, length={}", base, length);
(base, length)
(base as usize, length)
}
fn handle_sigsegv(
base: *mut u8,
base: usize,
length: usize,
signum: libc::c_int,
siginfo: *const libc::siginfo_t,
@@ -90,15 +92,15 @@ mod tests {
}
}
fn make_externs(store: &Store, module: &Module) -> Vec<Extern> {
fn make_externs(store: &mut Store<()>, module: &Module) -> Vec<Extern> {
module
.imports()
.map(|import| {
assert_eq!(Some("hostcall_read"), import.name());
let func = Func::wrap(&store, {
move |caller: Caller<'_>| {
let func = Func::wrap(&mut *store, {
move |mut caller: Caller<'_, _>| {
let mem = caller.get_export("memory").unwrap().into_memory().unwrap();
let memory = unsafe { mem.data_unchecked_mut() };
let memory = mem.data(&caller);
use std::convert::TryInto;
i32::from_le_bytes(memory[0..4].try_into().unwrap())
}
@@ -112,20 +114,21 @@ mod tests {
// hostcall can be handled.
#[test]
fn test_custom_signal_handler_single_instance_hostcall() -> Result<()> {
let engine = Engine::new(&Config::default())?;
let store = Store::new(&engine);
let engine = Engine::default();
let mut store = Store::new(&engine, ());
let module = Module::new(&engine, WAT1)?;
let instance = Instance::new(&store, &module, &make_externs(&store, &module))?;
let externs = make_externs(&mut store, &module);
let instance = Instance::new(&mut store, &module, &externs)?;
let (base, length) = set_up_memory(&instance);
let (base, length) = set_up_memory(&mut store, instance);
unsafe {
store.set_signal_handler(move |signum, siginfo, _| {
handle_sigsegv(base, length, signum, siginfo)
});
}
println!("calling hostcall_read...");
let result = invoke_export(&instance, "hostcall_read").unwrap();
let result = invoke_export(&mut store, instance, "hostcall_read").unwrap();
assert_eq!(123, result);
Ok(())
}
@@ -133,12 +136,13 @@ mod tests {
#[test]
fn test_custom_signal_handler_single_instance() -> Result<()> {
let engine = Engine::new(&Config::default())?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let module = Module::new(&engine, WAT1)?;
let instance = Instance::new(&store, &module, &make_externs(&store, &module))?;
let externs = make_externs(&mut store, &module);
let instance = Instance::new(&mut store, &module, &externs)?;
let (base, length) = set_up_memory(&instance);
let (base, length) = set_up_memory(&mut store, instance);
unsafe {
store.set_signal_handler(move |signum, siginfo, _| {
handle_sigsegv(base, length, signum, siginfo)
@@ -148,13 +152,13 @@ mod tests {
// these invoke wasmtime_call_trampoline from action.rs
{
println!("calling read...");
let result = invoke_export(&instance, "read").expect("read succeeded");
let result = invoke_export(&mut store, instance, "read").expect("read succeeded");
assert_eq!(123, result);
}
{
println!("calling read_out_of_bounds...");
let trap = invoke_export(&instance, "read_out_of_bounds")
let trap = invoke_export(&mut store, instance, "read_out_of_bounds")
.unwrap_err()
.downcast::<Trap>()?;
assert!(
@@ -167,17 +171,19 @@ mod tests {
// these invoke wasmtime_call_trampoline from callable.rs
{
let read_func = instance.get_typed_func::<(), i32>("read")?;
let read_func = instance.get_typed_func::<(), i32, _>(&mut store, "read")?;
println!("calling read...");
let result = read_func.call(()).expect("expected function not to trap");
let result = read_func
.call(&mut store, ())
.expect("expected function not to trap");
assert_eq!(123i32, result);
}
{
let read_out_of_bounds_func =
instance.get_typed_func::<(), i32>("read_out_of_bounds")?;
instance.get_typed_func::<(), i32, _>(&mut store, "read_out_of_bounds")?;
println!("calling read_out_of_bounds...");
let trap = read_out_of_bounds_func.call(()).unwrap_err();
let trap = read_out_of_bounds_func.call(&mut store, ()).unwrap_err();
assert!(trap
.to_string()
.contains("wasm trap: out of bounds memory access"));
@@ -187,17 +193,18 @@ mod tests {
#[test]
fn test_custom_signal_handler_multiple_instances() -> Result<()> {
let engine = Engine::new(&Config::default())?;
let store = Store::new(&engine);
let engine = Engine::default();
let mut store = Store::new(&engine, ());
let module = Module::new(&engine, WAT1)?;
// Set up multiple instances
let instance1 = Instance::new(&store, &module, &make_externs(&store, &module))?;
let instance1_handler_triggered = Rc::new(AtomicBool::new(false));
let externs = make_externs(&mut store, &module);
let instance1 = Instance::new(&mut store, &module, &externs)?;
let instance1_handler_triggered = Arc::new(AtomicBool::new(false));
unsafe {
let (base1, length1) = set_up_memory(&instance1);
let (base1, length1) = set_up_memory(&mut store, instance1);
store.set_signal_handler({
let instance1_handler_triggered = instance1_handler_triggered.clone();
@@ -222,11 +229,12 @@ mod tests {
// First instance1
{
let mut exports1 = instance1.exports();
let mut exports1 = instance1.exports(&mut store);
assert!(exports1.next().is_some());
drop(exports1);
println!("calling instance1.read...");
let result = invoke_export(&instance1, "read").expect("read succeeded");
let result = invoke_export(&mut store, instance1, "read").expect("read succeeded");
assert_eq!(123, result);
assert_eq!(
instance1_handler_triggered.load(Ordering::SeqCst),
@@ -235,12 +243,13 @@ mod tests {
);
}
let instance2 = Instance::new(&store, &module, &make_externs(&store, &module))
.expect("failed to instantiate module");
let instance2_handler_triggered = Rc::new(AtomicBool::new(false));
let externs = make_externs(&mut store, &module);
let instance2 =
Instance::new(&mut store, &module, &externs).expect("failed to instantiate module");
let instance2_handler_triggered = Arc::new(AtomicBool::new(false));
unsafe {
let (base2, length2) = set_up_memory(&instance2);
let (base2, length2) = set_up_memory(&mut store, instance2);
store.set_signal_handler({
let instance2_handler_triggered = instance2_handler_triggered.clone();
@@ -263,11 +272,12 @@ mod tests {
// And then instance2
{
let mut exports2 = instance2.exports();
let mut exports2 = instance2.exports(&mut store);
assert!(exports2.next().is_some());
drop(exports2);
println!("calling instance2.read...");
let result = invoke_export(&instance2, "read").expect("read succeeded");
let result = invoke_export(&mut store, instance2, "read").expect("read succeeded");
assert_eq!(123, result);
assert_eq!(
instance2_handler_triggered.load(Ordering::SeqCst),
@@ -280,13 +290,14 @@ mod tests {
#[test]
fn test_custom_signal_handler_instance_calling_another_instance() -> Result<()> {
let engine = Engine::new(&Config::default())?;
let store = Store::new(&engine);
let engine = Engine::default();
let mut store = Store::new(&engine, ());
// instance1 which defines 'read'
let module1 = Module::new(&engine, WAT1)?;
let instance1 = Instance::new(&store, &module1, &make_externs(&store, &module1))?;
let (base1, length1) = set_up_memory(&instance1);
let externs = make_externs(&mut store, &module1);
let instance1 = Instance::new(&mut store, &module1, &externs)?;
let (base1, length1) = set_up_memory(&mut store, instance1);
unsafe {
store.set_signal_handler(move |signum, siginfo, _| {
println!("instance1");
@@ -294,12 +305,13 @@ mod tests {
});
}
let mut instance1_exports = instance1.exports();
let instance1_read = instance1_exports.next().unwrap();
let mut instance1_exports = instance1.exports(&mut store);
let instance1_read = instance1_exports.next().unwrap().clone().into_extern();
drop(instance1_exports);
// instance2 which calls 'instance1.read'
let module2 = Module::new(&engine, WAT2)?;
let instance2 = Instance::new(&store, &module2, &[instance1_read.into_extern()])?;
let instance2 = Instance::new(&mut store, &module2, &[instance1_read])?;
// since 'instance2.run' calls 'instance1.read' we need to set up the signal handler to handle
// SIGSEGV originating from within the memory of instance1
unsafe {
@@ -309,7 +321,7 @@ mod tests {
}
println!("calling instance2.run");
let result = invoke_export(&instance2, "run")?;
let result = invoke_export(&mut store, instance2, "run")?;
assert_eq!(123, result);
Ok(())
}

View File

@@ -2,55 +2,58 @@ use wasmtime::*;
#[test]
fn bad_globals() {
let mut store = Store::<()>::default();
let ty = GlobalType::new(ValType::I32, Mutability::Var);
assert!(Global::new(&Store::default(), ty.clone(), Val::I64(0)).is_err());
assert!(Global::new(&Store::default(), ty.clone(), Val::F32(0)).is_err());
assert!(Global::new(&Store::default(), ty.clone(), Val::F64(0)).is_err());
assert!(Global::new(&mut store, ty.clone(), Val::I64(0)).is_err());
assert!(Global::new(&mut store, ty.clone(), Val::F32(0)).is_err());
assert!(Global::new(&mut store, ty.clone(), Val::F64(0)).is_err());
let ty = GlobalType::new(ValType::I32, Mutability::Const);
let g = Global::new(&Store::default(), ty.clone(), Val::I32(0)).unwrap();
assert!(g.set(Val::I32(1)).is_err());
let g = Global::new(&mut store, ty.clone(), Val::I32(0)).unwrap();
assert!(g.set(&mut store, Val::I32(1)).is_err());
let ty = GlobalType::new(ValType::I32, Mutability::Var);
let g = Global::new(&Store::default(), ty.clone(), Val::I32(0)).unwrap();
assert!(g.set(Val::I64(0)).is_err());
let g = Global::new(&mut store, ty.clone(), Val::I32(0)).unwrap();
assert!(g.set(&mut store, Val::I64(0)).is_err());
}
#[test]
fn bad_tables() {
let mut store = Store::<()>::default();
// i32 not supported yet
let ty = TableType::new(ValType::I32, Limits::new(0, Some(1)));
assert!(Table::new(&Store::default(), ty.clone(), Val::I32(0)).is_err());
assert!(Table::new(&mut store, ty.clone(), Val::I32(0)).is_err());
// mismatched initializer
let ty = TableType::new(ValType::FuncRef, Limits::new(0, Some(1)));
assert!(Table::new(&Store::default(), ty.clone(), Val::I32(0)).is_err());
assert!(Table::new(&mut store, ty.clone(), Val::I32(0)).is_err());
// get out of bounds
let ty = TableType::new(ValType::FuncRef, Limits::new(0, Some(1)));
let t = Table::new(&Store::default(), ty.clone(), Val::FuncRef(None)).unwrap();
assert!(t.get(0).is_none());
assert!(t.get(u32::max_value()).is_none());
let t = Table::new(&mut store, ty.clone(), Val::FuncRef(None)).unwrap();
assert!(t.get(&mut store, 0).is_none());
assert!(t.get(&mut store, u32::max_value()).is_none());
// set out of bounds or wrong type
let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(1)));
let t = Table::new(&Store::default(), ty.clone(), Val::FuncRef(None)).unwrap();
assert!(t.set(0, Val::I32(0)).is_err());
assert!(t.set(0, Val::FuncRef(None)).is_ok());
assert!(t.set(1, Val::FuncRef(None)).is_err());
let t = Table::new(&mut store, ty.clone(), Val::FuncRef(None)).unwrap();
assert!(t.set(&mut store, 0, Val::I32(0)).is_err());
assert!(t.set(&mut store, 0, Val::FuncRef(None)).is_ok());
assert!(t.set(&mut store, 1, Val::FuncRef(None)).is_err());
// grow beyond max
let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(1)));
let t = Table::new(&Store::default(), ty.clone(), Val::FuncRef(None)).unwrap();
assert!(t.grow(0, Val::FuncRef(None)).is_ok());
assert!(t.grow(1, Val::FuncRef(None)).is_err());
assert_eq!(t.size(), 1);
let t = Table::new(&mut store, ty.clone(), Val::FuncRef(None)).unwrap();
assert!(t.grow(&mut store, 0, Val::FuncRef(None)).is_ok());
assert!(t.grow(&mut store, 1, Val::FuncRef(None)).is_err());
assert_eq!(t.size(&store), 1);
// grow wrong type
let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(2)));
let t = Table::new(&Store::default(), ty.clone(), Val::FuncRef(None)).unwrap();
assert!(t.grow(1, Val::I32(0)).is_err());
assert_eq!(t.size(), 1);
let t = Table::new(&mut store, ty.clone(), Val::FuncRef(None)).unwrap();
assert!(t.grow(&mut store, 1, Val::I32(0)).is_err());
assert_eq!(t.size(&store), 1);
}
#[test]
@@ -58,77 +61,79 @@ fn cross_store() -> anyhow::Result<()> {
let mut cfg = Config::new();
cfg.wasm_reference_types(true);
let engine = Engine::new(&cfg)?;
let store1 = Store::new(&engine);
let store2 = Store::new(&engine);
let mut store1 = Store::new(&engine, ());
let mut store2 = Store::new(&engine, ());
// ============ Cross-store instantiation ==============
let func = Func::wrap(&store2, || {});
let func = Func::wrap(&mut store2, || {});
let ty = GlobalType::new(ValType::I32, Mutability::Const);
let global = Global::new(&store2, ty, Val::I32(0))?;
let global = Global::new(&mut store2, ty, Val::I32(0))?;
let ty = MemoryType::new(Limits::new(1, None));
let memory = Memory::new(&store2, ty)?;
let memory = Memory::new(&mut store2, ty)?;
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
let table = Table::new(&store2, ty, Val::FuncRef(None))?;
let table = Table::new(&mut store2, ty, Val::FuncRef(None))?;
let need_func = Module::new(&engine, r#"(module (import "" "" (func)))"#)?;
assert!(Instance::new(&store1, &need_func, &[func.into()]).is_err());
assert!(Instance::new(&mut store1, &need_func, &[func.into()]).is_err());
let need_global = Module::new(&engine, r#"(module (import "" "" (global i32)))"#)?;
assert!(Instance::new(&store1, &need_global, &[global.into()]).is_err());
assert!(Instance::new(&mut store1, &need_global, &[global.into()]).is_err());
let need_table = Module::new(&engine, r#"(module (import "" "" (table 1 funcref)))"#)?;
assert!(Instance::new(&store1, &need_table, &[table.into()]).is_err());
assert!(Instance::new(&mut store1, &need_table, &[table.into()]).is_err());
let need_memory = Module::new(&engine, r#"(module (import "" "" (memory 1)))"#)?;
assert!(Instance::new(&store1, &need_memory, &[memory.into()]).is_err());
assert!(Instance::new(&mut store1, &need_memory, &[memory.into()]).is_err());
// ============ Cross-store globals ==============
let store1val = Val::FuncRef(Some(Func::wrap(&store1, || {})));
let store2val = Val::FuncRef(Some(Func::wrap(&store2, || {})));
let store1val = Val::FuncRef(Some(Func::wrap(&mut store1, || {})));
let store2val = Val::FuncRef(Some(Func::wrap(&mut store2, || {})));
let ty = GlobalType::new(ValType::FuncRef, Mutability::Var);
assert!(Global::new(&store2, ty.clone(), store1val.clone()).is_err());
if let Ok(g) = Global::new(&store2, ty.clone(), store2val.clone()) {
assert!(g.set(store1val.clone()).is_err());
assert!(Global::new(&mut store2, ty.clone(), store1val.clone()).is_err());
if let Ok(g) = Global::new(&mut store2, ty.clone(), store2val.clone()) {
assert!(g.set(&mut store2, store1val.clone()).is_err());
}
// ============ Cross-store tables ==============
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
assert!(Table::new(&store2, ty.clone(), store1val.clone()).is_err());
let t1 = Table::new(&store2, ty.clone(), store2val.clone())?;
assert!(t1.set(0, store1val.clone()).is_err());
assert!(t1.grow(0, store1val.clone()).is_err());
assert!(t1.fill(0, store1val.clone(), 1).is_err());
let t2 = Table::new(&store1, ty.clone(), store1val.clone())?;
assert!(Table::copy(&t1, 0, &t2, 0, 0).is_err());
assert!(Table::new(&mut store2, ty.clone(), store1val.clone()).is_err());
let t1 = Table::new(&mut store2, ty.clone(), store2val.clone())?;
assert!(t1.set(&mut store2, 0, store1val.clone()).is_err());
assert!(t1.grow(&mut store2, 0, store1val.clone()).is_err());
assert!(t1.fill(&mut store2, 0, store1val.clone(), 1).is_err());
// ============ Cross-store funcs ==============
let module = Module::new(&engine, r#"(module (func (export "f") (param funcref)))"#)?;
let s1_inst = Instance::new(&store1, &module, &[])?;
let s2_inst = Instance::new(&store2, &module, &[])?;
let s1_f = s1_inst.get_func("f").unwrap();
let s2_f = s2_inst.get_func("f").unwrap();
let s1_inst = Instance::new(&mut store1, &module, &[])?;
let s2_inst = Instance::new(&mut store2, &module, &[])?;
let s1_f = s1_inst.get_func(&mut store1, "f").unwrap();
let s2_f = s2_inst.get_func(&mut store2, "f").unwrap();
assert!(s1_f.call(&[Val::FuncRef(None)]).is_ok());
assert!(s2_f.call(&[Val::FuncRef(None)]).is_ok());
assert!(s1_f.call(&[Val::FuncRef(Some(s1_f.clone()))]).is_ok());
assert!(s1_f.call(&[Val::FuncRef(Some(s2_f.clone()))]).is_err());
assert!(s2_f.call(&[Val::FuncRef(Some(s1_f.clone()))]).is_err());
assert!(s2_f.call(&[Val::FuncRef(Some(s2_f.clone()))]).is_ok());
assert!(s1_f.call(&mut store1, &[Val::FuncRef(None)]).is_ok());
assert!(s2_f.call(&mut store2, &[Val::FuncRef(None)]).is_ok());
assert!(s1_f.call(&mut store1, &[Some(s1_f.clone()).into()]).is_ok());
assert!(s1_f
.call(&mut store1, &[Some(s2_f.clone()).into()])
.is_err());
assert!(s2_f
.call(&mut store2, &[Some(s1_f.clone()).into()])
.is_err());
assert!(s2_f.call(&mut store2, &[Some(s2_f.clone()).into()]).is_ok());
let s1_f_t = s1_f.typed::<Option<Func>, ()>()?;
let s2_f_t = s2_f.typed::<Option<Func>, ()>()?;
let s1_f_t = s1_f.typed::<Option<Func>, (), _>(&store1)?;
let s2_f_t = s2_f.typed::<Option<Func>, (), _>(&store2)?;
assert!(s1_f_t.call(None).is_ok());
assert!(s2_f_t.call(None).is_ok());
assert!(s1_f_t.call(Some(s1_f.clone())).is_ok());
assert!(s1_f_t.call(Some(s2_f.clone())).is_err());
assert!(s2_f_t.call(Some(s1_f.clone())).is_err());
assert!(s2_f_t.call(Some(s2_f.clone())).is_ok());
assert!(s1_f_t.call(&mut store1, None).is_ok());
assert!(s2_f_t.call(&mut store2, None).is_ok());
assert!(s1_f_t.call(&mut store1, Some(s1_f.clone())).is_ok());
assert!(s1_f_t.call(&mut store1, Some(s2_f.clone())).is_err());
assert!(s2_f_t.call(&mut store2, Some(s1_f.clone())).is_err());
assert!(s2_f_t.call(&mut store2, Some(s2_f.clone())).is_ok());
Ok(())
}
@@ -138,30 +143,33 @@ fn get_set_externref_globals_via_api() -> anyhow::Result<()> {
let mut cfg = Config::new();
cfg.wasm_reference_types(true);
let engine = Engine::new(&cfg)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
// Initialize with a null externref.
let global = Global::new(
&store,
&mut store,
GlobalType::new(ValType::ExternRef, Mutability::Var),
Val::ExternRef(None),
)?;
assert!(global.get().unwrap_externref().is_none());
assert!(global.get(&mut store).unwrap_externref().is_none());
global.set(Val::ExternRef(Some(ExternRef::new("hello".to_string()))))?;
let r = global.get().unwrap_externref().unwrap();
global.set(
&mut store,
Val::ExternRef(Some(ExternRef::new("hello".to_string()))),
)?;
let r = global.get(&mut store).unwrap_externref().unwrap();
assert!(r.data().is::<String>());
assert_eq!(r.data().downcast_ref::<String>().unwrap(), "hello");
// Initialize with a non-null externref.
let global = Global::new(
&store,
&mut store,
GlobalType::new(ValType::ExternRef, Mutability::Const),
Val::ExternRef(Some(ExternRef::new(42_i32))),
)?;
let r = global.get().unwrap_externref().unwrap();
let r = global.get(&mut store).unwrap_externref().unwrap();
assert!(r.data().is::<i32>());
assert_eq!(r.data().downcast_ref::<i32>().copied().unwrap(), 42);
@@ -173,32 +181,32 @@ fn get_set_funcref_globals_via_api() -> anyhow::Result<()> {
let mut cfg = Config::new();
cfg.wasm_reference_types(true);
let engine = Engine::new(&cfg)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let f = Func::wrap(&store, || {});
let f = Func::wrap(&mut store, || {});
// Initialize with a null funcref.
let global = Global::new(
&store,
&mut store,
GlobalType::new(ValType::FuncRef, Mutability::Var),
Val::FuncRef(None),
)?;
assert!(global.get().unwrap_funcref().is_none());
assert!(global.get(&mut store).unwrap_funcref().is_none());
global.set(Val::FuncRef(Some(f.clone())))?;
let f2 = global.get().unwrap_funcref().cloned().unwrap();
assert_eq!(f.ty(), f2.ty());
global.set(&mut store, Val::FuncRef(Some(f.clone())))?;
let f2 = global.get(&mut store).unwrap_funcref().cloned().unwrap();
assert_eq!(f.ty(&store), f2.ty(&store));
// Initialize with a non-null funcref.
let global = Global::new(
&store,
&mut store,
GlobalType::new(ValType::FuncRef, Mutability::Var),
Val::FuncRef(Some(f.clone())),
)?;
let f2 = global.get().unwrap_funcref().cloned().unwrap();
assert_eq!(f.ty(), f2.ty());
let f2 = global.get(&mut store).unwrap_funcref().cloned().unwrap();
assert_eq!(f.ty(&store), f2.ty(&store));
Ok(())
}
@@ -208,18 +216,15 @@ fn create_get_set_funcref_tables_via_api() -> anyhow::Result<()> {
let mut cfg = Config::new();
cfg.wasm_reference_types(true);
let engine = Engine::new(&cfg)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let table_ty = TableType::new(ValType::FuncRef, Limits::at_least(10));
let table = Table::new(
&store,
table_ty,
Val::FuncRef(Some(Func::wrap(&store, || {}))),
)?;
let init = Val::FuncRef(Some(Func::wrap(&mut store, || {})));
let table = Table::new(&mut store, table_ty, init)?;
assert!(table.get(5).unwrap().unwrap_funcref().is_some());
table.set(5, Val::FuncRef(None))?;
assert!(table.get(5).unwrap().unwrap_funcref().is_none());
assert!(table.get(&mut store, 5).unwrap().unwrap_funcref().is_some());
table.set(&mut store, 5, Val::FuncRef(None))?;
assert!(table.get(&mut store, 5).unwrap().unwrap_funcref().is_none());
Ok(())
}
@@ -229,22 +234,23 @@ fn fill_funcref_tables_via_api() -> anyhow::Result<()> {
let mut cfg = Config::new();
cfg.wasm_reference_types(true);
let engine = Engine::new(&cfg)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let table_ty = TableType::new(ValType::FuncRef, Limits::at_least(10));
let table = Table::new(&store, table_ty, Val::FuncRef(None))?;
let table = Table::new(&mut store, table_ty, Val::FuncRef(None))?;
for i in 0..10 {
assert!(table.get(i).unwrap().unwrap_funcref().is_none());
assert!(table.get(&mut store, i).unwrap().unwrap_funcref().is_none());
}
table.fill(2, Val::FuncRef(Some(Func::wrap(&store, || {}))), 4)?;
let fill = Val::FuncRef(Some(Func::wrap(&mut store, || {})));
table.fill(&mut store, 2, fill, 4)?;
for i in (0..2).chain(7..10) {
assert!(table.get(i).unwrap().unwrap_funcref().is_none());
assert!(table.get(&mut store, i).unwrap().unwrap_funcref().is_none());
}
for i in 2..6 {
assert!(table.get(i).unwrap().unwrap_funcref().is_some());
assert!(table.get(&mut store, i).unwrap().unwrap_funcref().is_some());
}
Ok(())
@@ -255,14 +261,14 @@ fn grow_funcref_tables_via_api() -> anyhow::Result<()> {
let mut cfg = Config::new();
cfg.wasm_reference_types(true);
let engine = Engine::new(&cfg)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let table_ty = TableType::new(ValType::FuncRef, Limits::at_least(10));
let table = Table::new(&store, table_ty, Val::FuncRef(None))?;
let table = Table::new(&mut store, table_ty, Val::FuncRef(None))?;
assert_eq!(table.size(), 10);
table.grow(3, Val::FuncRef(None))?;
assert_eq!(table.size(), 13);
assert_eq!(table.size(&store), 10);
table.grow(&mut store, 3, Val::FuncRef(None))?;
assert_eq!(table.size(&store), 13);
Ok(())
}
@@ -272,18 +278,18 @@ fn create_get_set_externref_tables_via_api() -> anyhow::Result<()> {
let mut cfg = Config::new();
cfg.wasm_reference_types(true);
let engine = Engine::new(&cfg)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let table_ty = TableType::new(ValType::ExternRef, Limits::at_least(10));
let table = Table::new(
&store,
&mut store,
table_ty,
Val::ExternRef(Some(ExternRef::new(42_usize))),
)?;
assert_eq!(
*table
.get(5)
.get(&mut store, 5)
.unwrap()
.unwrap_externref()
.unwrap()
@@ -292,8 +298,12 @@ fn create_get_set_externref_tables_via_api() -> anyhow::Result<()> {
.unwrap(),
42
);
table.set(5, Val::ExternRef(None))?;
assert!(table.get(5).unwrap().unwrap_externref().is_none());
table.set(&mut store, 5, Val::ExternRef(None))?;
assert!(table
.get(&mut store, 5)
.unwrap()
.unwrap_externref()
.is_none());
Ok(())
}
@@ -303,24 +313,37 @@ fn fill_externref_tables_via_api() -> anyhow::Result<()> {
let mut cfg = Config::new();
cfg.wasm_reference_types(true);
let engine = Engine::new(&cfg)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let table_ty = TableType::new(ValType::ExternRef, Limits::at_least(10));
let table = Table::new(&store, table_ty, Val::ExternRef(None))?;
let table = Table::new(&mut store, table_ty, Val::ExternRef(None))?;
for i in 0..10 {
assert!(table.get(i).unwrap().unwrap_externref().is_none());
assert!(table
.get(&mut store, i)
.unwrap()
.unwrap_externref()
.is_none());
}
table.fill(2, Val::ExternRef(Some(ExternRef::new(42_usize))), 4)?;
table.fill(
&mut store,
2,
Val::ExternRef(Some(ExternRef::new(42_usize))),
4,
)?;
for i in (0..2).chain(7..10) {
assert!(table.get(i).unwrap().unwrap_externref().is_none());
assert!(table
.get(&mut store, i)
.unwrap()
.unwrap_externref()
.is_none());
}
for i in 2..6 {
assert_eq!(
*table
.get(i)
.get(&mut store, i)
.unwrap()
.unwrap_externref()
.unwrap()
@@ -339,14 +362,14 @@ fn grow_externref_tables_via_api() -> anyhow::Result<()> {
let mut cfg = Config::new();
cfg.wasm_reference_types(true);
let engine = Engine::new(&cfg)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let table_ty = TableType::new(ValType::ExternRef, Limits::at_least(10));
let table = Table::new(&store, table_ty, Val::ExternRef(None))?;
let table = Table::new(&mut store, table_ty, Val::ExternRef(None))?;
assert_eq!(table.size(), 10);
table.grow(3, Val::ExternRef(None))?;
assert_eq!(table.size(), 13);
assert_eq!(table.size(&store), 10);
table.grow(&mut store, 3, Val::ExternRef(None))?;
assert_eq!(table.size(&store), 13);
Ok(())
}
@@ -354,16 +377,17 @@ fn grow_externref_tables_via_api() -> anyhow::Result<()> {
#[test]
fn read_write_memory_via_api() {
let cfg = Config::new();
let store = Store::new(&Engine::new(&cfg).unwrap());
let mut store = Store::new(&Engine::new(&cfg).unwrap(), ());
let ty = MemoryType::new(Limits::new(1, None));
let mem = Memory::new(&store, ty).unwrap();
mem.grow(1).unwrap();
let mem = Memory::new(&mut store, ty).unwrap();
mem.grow(&mut store, 1).unwrap();
let value = b"hello wasm";
mem.write(mem.data_size() - value.len(), value).unwrap();
let size = mem.data_size(&store);
mem.write(&mut store, size - value.len(), value).unwrap();
let mut buffer = [0u8; 10];
mem.read(mem.data_size() - buffer.len(), &mut buffer)
mem.read(&store, mem.data_size(&store) - buffer.len(), &mut buffer)
.unwrap();
assert_eq!(value, &buffer);
@@ -371,10 +395,11 @@ fn read_write_memory_via_api() {
// Out of bounds write.
let res = mem.write(mem.data_size() - value.len() + 1, value);
let size = mem.data_size(&store);
let res = mem.write(&mut store, size - value.len() + 1, value);
assert!(res.is_err());
assert_ne!(
unsafe { mem.data_unchecked()[mem.data_size() - value.len() + 1] },
mem.data(&store)[mem.data_size(&store) - value.len() + 1],
value[0],
"no data is written",
);
@@ -382,15 +407,19 @@ fn read_write_memory_via_api() {
// Out of bounds read.
buffer[0] = 0x42;
let res = mem.read(mem.data_size() - buffer.len() + 1, &mut buffer);
let res = mem.read(
&store,
mem.data_size(&store) - buffer.len() + 1,
&mut buffer,
);
assert!(res.is_err());
assert_eq!(buffer[0], 0x42, "no data is read");
// Read offset overflow.
let res = mem.read(usize::MAX, &mut buffer);
let res = mem.read(&store, usize::MAX, &mut buffer);
assert!(res.is_err());
// Write offset overflow.
let res = mem.write(usize::MAX, &mut buffer);
let res = mem.write(&mut store, usize::MAX, &mut buffer);
assert!(res.is_err());
}

View File

@@ -50,9 +50,9 @@ fn fuel_consumed(wasm: &[u8]) -> u64 {
config.consume_fuel(true);
let engine = Engine::new(&config).unwrap();
let module = Module::new(&engine, wasm).unwrap();
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
store.add_fuel(u64::max_value()).unwrap();
drop(Instance::new(&store, &module, &[]));
drop(Instance::new(&mut store, &module, &[]));
store.fuel_consumed().unwrap()
}
@@ -112,9 +112,9 @@ fn iloop() {
config.consume_fuel(true);
let engine = Engine::new(&config).unwrap();
let module = Module::new(&engine, wat).unwrap();
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
store.add_fuel(10_000).unwrap();
let error = Instance::new(&store, &module, &[]).err().unwrap();
let error = Instance::new(&mut store, &module, &[]).err().unwrap();
assert!(
error.to_string().contains("all fuel consumed"),
"bad error: {}",

View File

@@ -1,30 +1,31 @@
use anyhow::Result;
use std::cell::Cell;
use std::rc::Rc;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst};
use std::sync::Arc;
use wasmtime::*;
#[test]
fn func_constructors() {
let store = Store::default();
Func::wrap(&store, || {});
Func::wrap(&store, |_: i32| {});
Func::wrap(&store, |_: i32, _: i64| {});
Func::wrap(&store, |_: f32, _: f64| {});
Func::wrap(&store, || -> i32 { 0 });
Func::wrap(&store, || -> i64 { 0 });
Func::wrap(&store, || -> f32 { 0.0 });
Func::wrap(&store, || -> f64 { 0.0 });
Func::wrap(&store, || -> Option<ExternRef> { None });
Func::wrap(&store, || -> Option<Func> { None });
let mut store = Store::<()>::default();
Func::wrap(&mut store, || {});
Func::wrap(&mut store, |_: i32| {});
Func::wrap(&mut store, |_: i32, _: i64| {});
Func::wrap(&mut store, |_: f32, _: f64| {});
Func::wrap(&mut store, || -> i32 { 0 });
Func::wrap(&mut store, || -> i64 { 0 });
Func::wrap(&mut store, || -> f32 { 0.0 });
Func::wrap(&mut store, || -> f64 { 0.0 });
Func::wrap(&mut store, || -> Option<ExternRef> { None });
Func::wrap(&mut store, || -> Option<Func> { None });
Func::wrap(&store, || -> Result<(), Trap> { loop {} });
Func::wrap(&store, || -> Result<i32, Trap> { loop {} });
Func::wrap(&store, || -> Result<i64, Trap> { loop {} });
Func::wrap(&store, || -> Result<f32, Trap> { loop {} });
Func::wrap(&store, || -> Result<f64, Trap> { loop {} });
Func::wrap(&store, || -> Result<Option<ExternRef>, Trap> { loop {} });
Func::wrap(&store, || -> Result<Option<Func>, Trap> { loop {} });
Func::wrap(&mut store, || -> Result<(), Trap> { loop {} });
Func::wrap(&mut store, || -> Result<i32, Trap> { loop {} });
Func::wrap(&mut store, || -> Result<i64, Trap> { loop {} });
Func::wrap(&mut store, || -> Result<f32, Trap> { loop {} });
Func::wrap(&mut store, || -> Result<f64, Trap> { loop {} });
Func::wrap(&mut store, || -> Result<Option<ExternRef>, Trap> {
loop {}
});
Func::wrap(&mut store, || -> Result<Option<Func>, Trap> { loop {} });
}
#[test]
@@ -39,10 +40,10 @@ fn dtor_runs() {
}
}
let store = Store::default();
let mut store = Store::<()>::default();
let a = A;
assert_eq!(HITS.load(SeqCst), 0);
Func::wrap(&store, move || {
Func::wrap(&mut store, move || {
drop(&a);
});
drop(store);
@@ -61,54 +62,52 @@ fn dtor_delayed() -> Result<()> {
}
}
let store = Store::default();
let mut store = Store::<()>::default();
let a = A;
let func = Func::wrap(&store, move || drop(&a));
let func = Func::wrap(&mut store, move || drop(&a));
assert_eq!(HITS.load(SeqCst), 0);
let wasm = wat::parse_str(r#"(import "" "" (func))"#)?;
let module = Module::new(store.engine(), &wasm)?;
let instance = Instance::new(&store, &module, &[func.into()])?;
let _instance = Instance::new(&mut store, &module, &[func.into()])?;
assert_eq!(HITS.load(SeqCst), 0);
drop((instance, module, store));
drop(store);
assert_eq!(HITS.load(SeqCst), 1);
Ok(())
}
#[test]
fn signatures_match() {
let store = Store::default();
let mut store = Store::<()>::default();
let f = Func::wrap(&store, || {});
assert_eq!(f.ty().params().collect::<Vec<_>>(), &[]);
assert_eq!(f.param_arity(), 0);
assert_eq!(f.ty().results().collect::<Vec<_>>(), &[]);
assert_eq!(f.result_arity(), 0);
let f = Func::wrap(&mut store, || {});
assert_eq!(f.ty(&store).params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[]);
let f = Func::wrap(&store, || -> i32 { loop {} });
assert_eq!(f.ty().params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty().results().collect::<Vec<_>>(), &[ValType::I32]);
let f = Func::wrap(&mut store, || -> i32 { loop {} });
assert_eq!(f.ty(&store).params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[ValType::I32]);
let f = Func::wrap(&store, || -> i64 { loop {} });
assert_eq!(f.ty().params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty().results().collect::<Vec<_>>(), &[ValType::I64]);
let f = Func::wrap(&mut store, || -> i64 { loop {} });
assert_eq!(f.ty(&store).params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[ValType::I64]);
let f = Func::wrap(&store, || -> f32 { loop {} });
assert_eq!(f.ty().params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty().results().collect::<Vec<_>>(), &[ValType::F32]);
let f = Func::wrap(&mut store, || -> f32 { loop {} });
assert_eq!(f.ty(&store).params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[ValType::F32]);
let f = Func::wrap(&store, || -> f64 { loop {} });
assert_eq!(f.ty().params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty().results().collect::<Vec<_>>(), &[ValType::F64]);
let f = Func::wrap(&mut store, || -> f64 { loop {} });
assert_eq!(f.ty(&store).params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[ValType::F64]);
let f = Func::wrap(
&store,
&mut store,
|_: f32, _: f64, _: i32, _: i64, _: i32, _: Option<ExternRef>, _: Option<Func>| -> f64 {
loop {}
},
);
assert_eq!(
f.ty().params().collect::<Vec<_>>(),
f.ty(&store).params().collect::<Vec<_>>(),
&[
ValType::F32,
ValType::F64,
@@ -119,7 +118,7 @@ fn signatures_match() {
ValType::FuncRef,
]
);
assert_eq!(f.ty().results().collect::<Vec<_>>(), &[ValType::F64]);
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[ValType::F64]);
}
#[test]
@@ -156,61 +155,74 @@ fn import_works() -> Result<()> {
let mut config = Config::new();
config.wasm_reference_types(true);
let engine = Engine::new(&config)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let module = Module::new(&engine, &wasm)?;
let instance = Instance::new(
&store,
&module,
let imports = [
Func::wrap(&mut store, || {
assert_eq!(HITS.fetch_add(1, SeqCst), 0);
})
.into(),
Func::wrap(&mut store, |x: i32| -> i32 {
assert_eq!(x, 0);
assert_eq!(HITS.fetch_add(1, SeqCst), 1);
1
})
.into(),
Func::wrap(&mut store, |x: i32, y: i64| {
assert_eq!(x, 2);
assert_eq!(y, 3);
assert_eq!(HITS.fetch_add(1, SeqCst), 2);
})
.into(),
Func::wrap(
&mut store,
|mut caller: Caller<'_, ()>,
a: i32,
b: i64,
c: i32,
d: f32,
e: f64,
f: Option<ExternRef>,
g: Option<Func>| {
assert_eq!(a, 100);
assert_eq!(b, 200);
assert_eq!(c, 300);
assert_eq!(d, 400.0);
assert_eq!(e, 500.0);
assert_eq!(
f.as_ref().unwrap().data().downcast_ref::<String>().unwrap(),
"hello"
);
assert_eq!(
g.as_ref().unwrap().call(&mut caller, &[]).unwrap()[0].unwrap_i32(),
42
);
assert_eq!(HITS.fetch_add(1, SeqCst), 3);
},
)
.into(),
];
let instance = Instance::new(&mut store, &module, &imports)?;
let run = instance.get_func(&mut store, "run").unwrap();
let funcref = Val::FuncRef(Some(Func::wrap(&mut store, || -> i32 { 42 })));
run.call(
&mut store,
&[
Func::wrap(&store, || {
assert_eq!(HITS.fetch_add(1, SeqCst), 0);
})
.into(),
Func::wrap(&store, |x: i32| -> i32 {
assert_eq!(x, 0);
assert_eq!(HITS.fetch_add(1, SeqCst), 1);
1
})
.into(),
Func::wrap(&store, |x: i32, y: i64| {
assert_eq!(x, 2);
assert_eq!(y, 3);
assert_eq!(HITS.fetch_add(1, SeqCst), 2);
})
.into(),
Func::wrap(
&store,
|a: i32, b: i64, c: i32, d: f32, e: f64, f: Option<ExternRef>, g: Option<Func>| {
assert_eq!(a, 100);
assert_eq!(b, 200);
assert_eq!(c, 300);
assert_eq!(d, 400.0);
assert_eq!(e, 500.0);
assert_eq!(
f.as_ref().unwrap().data().downcast_ref::<String>().unwrap(),
"hello"
);
assert_eq!(g.as_ref().unwrap().call(&[]).unwrap()[0].unwrap_i32(), 42);
assert_eq!(HITS.fetch_add(1, SeqCst), 3);
},
)
.into(),
Val::ExternRef(Some(ExternRef::new("hello".to_string()))),
funcref,
],
)?;
let run = instance.get_func("run").unwrap();
run.call(&[
Val::ExternRef(Some(ExternRef::new("hello".to_string()))),
Val::FuncRef(Some(Func::wrap(&store, || -> i32 { 42 }))),
])?;
assert_eq!(HITS.load(SeqCst), 4);
Ok(())
}
#[test]
fn trap_smoke() -> Result<()> {
let store = Store::default();
let f = Func::wrap(&store, || -> Result<(), Trap> { Err(Trap::new("test")) });
let err = f.call(&[]).unwrap_err().downcast::<Trap>()?;
let mut store = Store::<()>::default();
let f = Func::wrap(&mut store, || -> Result<(), Trap> {
Err(Trap::new("test"))
});
let err = f.call(&mut store, &[]).unwrap_err().downcast::<Trap>()?;
assert!(err.to_string().contains("test"));
assert!(err.i32_exit_status().is_none());
Ok(())
@@ -224,80 +236,77 @@ fn trap_import() -> Result<()> {
(start 0)
"#,
)?;
let store = Store::default();
let mut store = Store::<()>::default();
let module = Module::new(store.engine(), &wasm)?;
let trap = Instance::new(
&store,
&module,
&[Func::wrap(&store, || -> Result<(), Trap> { Err(Trap::new("foo")) }).into()],
)
.err()
.unwrap()
.downcast::<Trap>()?;
let import = Func::wrap(&mut store, || -> Result<(), Trap> { Err(Trap::new("foo")) });
let trap = Instance::new(&mut store, &module, &[import.into()])
.err()
.unwrap()
.downcast::<Trap>()?;
assert!(trap.to_string().contains("foo"));
Ok(())
}
#[test]
fn get_from_wrapper() {
let store = Store::default();
let f = Func::wrap(&store, || {});
assert!(f.typed::<(), ()>().is_ok());
assert!(f.typed::<(), i32>().is_err());
assert!(f.typed::<(), ()>().is_ok());
assert!(f.typed::<i32, ()>().is_err());
assert!(f.typed::<i32, i32>().is_err());
assert!(f.typed::<(i32, i32), ()>().is_err());
assert!(f.typed::<(i32, i32), i32>().is_err());
let mut store = Store::<()>::default();
let f = Func::wrap(&mut store, || {});
assert!(f.typed::<(), (), _>(&store).is_ok());
assert!(f.typed::<(), i32, _>(&store).is_err());
assert!(f.typed::<(), (), _>(&store).is_ok());
assert!(f.typed::<i32, (), _>(&store).is_err());
assert!(f.typed::<i32, i32, _>(&store).is_err());
assert!(f.typed::<(i32, i32), (), _>(&store).is_err());
assert!(f.typed::<(i32, i32), i32, _>(&store).is_err());
let f = Func::wrap(&store, || -> i32 { loop {} });
assert!(f.typed::<(), i32>().is_ok());
let f = Func::wrap(&store, || -> f32 { loop {} });
assert!(f.typed::<(), f32>().is_ok());
let f = Func::wrap(&store, || -> f64 { loop {} });
assert!(f.typed::<(), f64>().is_ok());
let f = Func::wrap(&store, || -> Option<ExternRef> { loop {} });
assert!(f.typed::<(), Option<ExternRef>>().is_ok());
let f = Func::wrap(&store, || -> Option<Func> { loop {} });
assert!(f.typed::<(), Option<Func>>().is_ok());
let f = Func::wrap(&mut store, || -> i32 { loop {} });
assert!(f.typed::<(), i32, _>(&store).is_ok());
let f = Func::wrap(&mut store, || -> f32 { loop {} });
assert!(f.typed::<(), f32, _>(&store).is_ok());
let f = Func::wrap(&mut store, || -> f64 { loop {} });
assert!(f.typed::<(), f64, _>(&store).is_ok());
let f = Func::wrap(&mut store, || -> Option<ExternRef> { loop {} });
assert!(f.typed::<(), Option<ExternRef>, _>(&store).is_ok());
let f = Func::wrap(&mut store, || -> Option<Func> { loop {} });
assert!(f.typed::<(), Option<Func>, _>(&store).is_ok());
let f = Func::wrap(&store, |_: i32| {});
assert!(f.typed::<i32, ()>().is_ok());
assert!(f.typed::<i64, ()>().is_err());
assert!(f.typed::<f32, ()>().is_err());
assert!(f.typed::<f64, ()>().is_err());
let f = Func::wrap(&store, |_: i64| {});
assert!(f.typed::<i64, ()>().is_ok());
let f = Func::wrap(&store, |_: f32| {});
assert!(f.typed::<f32, ()>().is_ok());
let f = Func::wrap(&store, |_: f64| {});
assert!(f.typed::<f64, ()>().is_ok());
let f = Func::wrap(&store, |_: Option<ExternRef>| {});
assert!(f.typed::<Option<ExternRef>, ()>().is_ok());
let f = Func::wrap(&store, |_: Option<Func>| {});
assert!(f.typed::<Option<Func>, ()>().is_ok());
let f = Func::wrap(&mut store, |_: i32| {});
assert!(f.typed::<i32, (), _>(&store).is_ok());
assert!(f.typed::<i64, (), _>(&store).is_err());
assert!(f.typed::<f32, (), _>(&store).is_err());
assert!(f.typed::<f64, (), _>(&store).is_err());
let f = Func::wrap(&mut store, |_: i64| {});
assert!(f.typed::<i64, (), _>(&store).is_ok());
let f = Func::wrap(&mut store, |_: f32| {});
assert!(f.typed::<f32, (), _>(&store).is_ok());
let f = Func::wrap(&mut store, |_: f64| {});
assert!(f.typed::<f64, (), _>(&store).is_ok());
let f = Func::wrap(&mut store, |_: Option<ExternRef>| {});
assert!(f.typed::<Option<ExternRef>, (), _>(&store).is_ok());
let f = Func::wrap(&mut store, |_: Option<Func>| {});
assert!(f.typed::<Option<Func>, (), _>(&store).is_ok());
}
#[test]
fn get_from_signature() {
let store = Store::default();
let mut store = Store::<()>::default();
let ty = FuncType::new(None, None);
let f = Func::new(&store, ty, |_, _, _| panic!());
assert!(f.typed::<(), ()>().is_ok());
assert!(f.typed::<(), i32>().is_err());
assert!(f.typed::<i32, ()>().is_err());
let f = Func::new(&mut store, ty, |_, _, _| panic!());
assert!(f.typed::<(), (), _>(&store).is_ok());
assert!(f.typed::<(), i32, _>(&store).is_err());
assert!(f.typed::<i32, (), _>(&store).is_err());
let ty = FuncType::new(Some(ValType::I32), Some(ValType::F64));
let f = Func::new(&store, ty, |_, _, _| panic!());
assert!(f.typed::<(), ()>().is_err());
assert!(f.typed::<(), i32>().is_err());
assert!(f.typed::<i32, ()>().is_err());
assert!(f.typed::<i32, f64>().is_ok());
let f = Func::new(&mut store, ty, |_, _, _| panic!());
assert!(f.typed::<(), (), _>(&store).is_err());
assert!(f.typed::<(), i32, _>(&store).is_err());
assert!(f.typed::<i32, (), _>(&store).is_err());
assert!(f.typed::<i32, f64, _>(&store).is_ok());
}
#[test]
fn get_from_module() -> anyhow::Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let module = Module::new(
store.engine(),
r#"
@@ -310,72 +319,75 @@ fn get_from_module() -> anyhow::Result<()> {
"#,
)?;
let instance = Instance::new(&store, &module, &[])?;
let f0 = instance.get_func("f0").unwrap();
assert!(f0.typed::<(), ()>().is_ok());
assert!(f0.typed::<(), i32>().is_err());
let f1 = instance.get_func("f1").unwrap();
assert!(f1.typed::<(), ()>().is_err());
assert!(f1.typed::<i32, ()>().is_ok());
assert!(f1.typed::<i32, f32>().is_err());
let f2 = instance.get_func("f2").unwrap();
assert!(f2.typed::<(), ()>().is_err());
assert!(f2.typed::<(), i32>().is_ok());
assert!(f2.typed::<i32, ()>().is_err());
assert!(f2.typed::<i32, f32>().is_err());
let instance = Instance::new(&mut store, &module, &[])?;
let f0 = instance.get_func(&mut store, "f0").unwrap();
assert!(f0.typed::<(), (), _>(&store).is_ok());
assert!(f0.typed::<(), i32, _>(&store).is_err());
let f1 = instance.get_func(&mut store, "f1").unwrap();
assert!(f1.typed::<(), (), _>(&store).is_err());
assert!(f1.typed::<i32, (), _>(&store).is_ok());
assert!(f1.typed::<i32, f32, _>(&store).is_err());
let f2 = instance.get_func(&mut store, "f2").unwrap();
assert!(f2.typed::<(), (), _>(&store).is_err());
assert!(f2.typed::<(), i32, _>(&store).is_ok());
assert!(f2.typed::<i32, (), _>(&store).is_err());
assert!(f2.typed::<i32, f32, _>(&store).is_err());
Ok(())
}
#[test]
fn call_wrapped_func() -> Result<()> {
let store = Store::default();
let f = Func::wrap(&store, |a: i32, b: i64, c: f32, d: f64| {
let mut store = Store::<()>::default();
let f = Func::wrap(&mut store, |a: i32, b: i64, c: f32, d: f64| {
assert_eq!(a, 1);
assert_eq!(b, 2);
assert_eq!(c, 3.0);
assert_eq!(d, 4.0);
});
f.call(&[Val::I32(1), Val::I64(2), 3.0f32.into(), 4.0f64.into()])?;
f.typed::<(i32, i64, f32, f64), ()>()?
.call((1, 2, 3.0, 4.0))?;
f.call(
&mut store,
&[Val::I32(1), Val::I64(2), 3.0f32.into(), 4.0f64.into()],
)?;
f.typed::<(i32, i64, f32, f64), (), _>(&store)?
.call(&mut store, (1, 2, 3.0, 4.0))?;
let f = Func::wrap(&store, || 1i32);
let results = f.call(&[])?;
let f = Func::wrap(&mut store, || 1i32);
let results = f.call(&mut store, &[])?;
assert_eq!(results.len(), 1);
assert_eq!(results[0].unwrap_i32(), 1);
assert_eq!(f.typed::<(), i32>()?.call(())?, 1);
assert_eq!(f.typed::<(), i32, _>(&store)?.call(&mut store, ())?, 1);
let f = Func::wrap(&store, || 2i64);
let results = f.call(&[])?;
let f = Func::wrap(&mut store, || 2i64);
let results = f.call(&mut store, &[])?;
assert_eq!(results.len(), 1);
assert_eq!(results[0].unwrap_i64(), 2);
assert_eq!(f.typed::<(), i64>()?.call(())?, 2);
assert_eq!(f.typed::<(), i64, _>(&store)?.call(&mut store, ())?, 2);
let f = Func::wrap(&store, || 3.0f32);
let results = f.call(&[])?;
let f = Func::wrap(&mut store, || 3.0f32);
let results = f.call(&mut store, &[])?;
assert_eq!(results.len(), 1);
assert_eq!(results[0].unwrap_f32(), 3.0);
assert_eq!(f.typed::<(), f32>()?.call(())?, 3.0);
assert_eq!(f.typed::<(), f32, _>(&store)?.call(&mut store, ())?, 3.0);
let f = Func::wrap(&store, || 4.0f64);
let results = f.call(&[])?;
let f = Func::wrap(&mut store, || 4.0f64);
let results = f.call(&mut store, &[])?;
assert_eq!(results.len(), 1);
assert_eq!(results[0].unwrap_f64(), 4.0);
assert_eq!(f.typed::<(), f64>()?.call(())?, 4.0);
assert_eq!(f.typed::<(), f64, _>(&store)?.call(&mut store, ())?, 4.0);
Ok(())
}
#[test]
fn caller_memory() -> anyhow::Result<()> {
let store = Store::default();
let f = Func::wrap(&store, |c: Caller<'_>| {
let mut store = Store::<()>::default();
let f = Func::wrap(&mut store, |mut c: Caller<'_, ()>| {
assert!(c.get_export("x").is_none());
assert!(c.get_export("y").is_none());
assert!(c.get_export("z").is_none());
});
f.call(&[])?;
f.call(&mut store, &[])?;
let f = Func::wrap(&store, |c: Caller<'_>| {
let f = Func::wrap(&mut store, |mut c: Caller<'_, ()>| {
assert!(c.get_export("x").is_none());
});
let module = Module::new(
@@ -388,9 +400,9 @@ fn caller_memory() -> anyhow::Result<()> {
"#,
)?;
Instance::new(&store, &module, &[f.into()])?;
Instance::new(&mut store, &module, &[f.into()])?;
let f = Func::wrap(&store, |c: Caller<'_>| {
let f = Func::wrap(&mut store, |mut c: Caller<'_, ()>| {
assert!(c.get_export("memory").is_some());
});
let module = Module::new(
@@ -404,9 +416,9 @@ fn caller_memory() -> anyhow::Result<()> {
"#,
)?;
Instance::new(&store, &module, &[f.into()])?;
Instance::new(&mut store, &module, &[f.into()])?;
let f = Func::wrap(&store, |c: Caller<'_>| {
let f = Func::wrap(&mut store, |mut c: Caller<'_, ()>| {
assert!(c.get_export("m").is_some());
assert!(c.get_export("f").is_some());
assert!(c.get_export("g").is_none());
@@ -426,16 +438,16 @@ fn caller_memory() -> anyhow::Result<()> {
"#,
)?;
Instance::new(&store, &module, &[f.into()])?;
Instance::new(&mut store, &module, &[f.into()])?;
Ok(())
}
#[test]
fn func_write_nothing() -> anyhow::Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let ty = FuncType::new(None, Some(ValType::I32));
let f = Func::new(&store, ty, |_, _, _| Ok(()));
let err = f.call(&[]).unwrap_err().downcast::<Trap>()?;
let f = Func::new(&mut store, ty, |_, _, _| Ok(()));
let err = f.call(&mut store, &[]).unwrap_err().downcast::<Trap>()?;
assert!(err
.to_string()
.contains("function attempted to return an incompatible value"));
@@ -461,16 +473,16 @@ fn return_cross_store_value() -> anyhow::Result<()> {
let engine = Engine::new(&config)?;
let module = Module::new(&engine, &wasm)?;
let store1 = Store::new(&engine);
let store2 = Store::new(&engine);
let mut store1 = Store::new(&engine, ());
let mut store2 = Store::new(&engine, ());
let store2_func = Func::wrap(&store2, || {});
let return_cross_store_func = Func::wrap(&store1, move || Some(store2_func.clone()));
let store2_func = Func::wrap(&mut store2, || {});
let return_cross_store_func = Func::wrap(&mut store1, move || Some(store2_func.clone()));
let instance = Instance::new(&store1, &module, &[return_cross_store_func.into()])?;
let instance = Instance::new(&mut store1, &module, &[return_cross_store_func.into()])?;
let run = instance.get_func("run").unwrap();
let result = run.call(&[]);
let run = instance.get_func(&mut store1, "run").unwrap();
let result = run.call(&mut store1, &[]);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("cross-`Store`"));
@@ -486,21 +498,21 @@ fn pass_cross_store_arg() -> anyhow::Result<()> {
config.wasm_reference_types(true);
let engine = Engine::new(&config)?;
let store1 = Store::new(&engine);
let store2 = Store::new(&engine);
let mut store1 = Store::new(&engine, ());
let mut store2 = Store::new(&engine, ());
let store1_func = Func::wrap(&store1, |_: Option<Func>| {});
let store2_func = Func::wrap(&store2, || {});
let store1_func = Func::wrap(&mut store1, |_: Option<Func>| {});
let store2_func = Func::wrap(&mut store2, || {});
// Using regular `.call` fails with cross-Store arguments.
assert!(store1_func
.call(&[Val::FuncRef(Some(store2_func.clone()))])
.call(&mut store1, &[Val::FuncRef(Some(store2_func.clone()))])
.is_err());
// And using `.get` followed by a function call also fails with cross-Store
// arguments.
let f = store1_func.typed::<Option<Func>, ()>()?;
let result = f.call(Some(store2_func));
let f = store1_func.typed::<Option<Func>, (), _>(&store1)?;
let result = f.call(&mut store1, Some(store2_func));
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("cross-`Store`"));
@@ -509,10 +521,12 @@ fn pass_cross_store_arg() -> anyhow::Result<()> {
#[test]
fn externref_signature_no_reference_types() -> anyhow::Result<()> {
let store = Store::default();
Func::wrap(&store, |_: Option<Func>| {});
let mut config = Config::new();
config.wasm_reference_types(false);
let mut store = Store::new(&Engine::new(&config)?, ());
Func::wrap(&mut store, |_: Option<Func>| {});
Func::new(
&store,
&mut store,
FuncType::new(
[ValType::FuncRef, ValType::ExternRef].iter().cloned(),
[ValType::FuncRef, ValType::ExternRef].iter().cloned(),
@@ -524,35 +538,34 @@ fn externref_signature_no_reference_types() -> anyhow::Result<()> {
#[test]
fn trampolines_always_valid() -> anyhow::Result<()> {
let func = {
// Compile two modules up front
let store = Store::default();
let module1 = Module::new(store.engine(), "(module (import \"\" \"\" (func)))")?;
let module2 = Module::new(store.engine(), "(module (func (export \"\")))")?;
// Start instantiating the first module, but this will fail.
// Historically this registered the module's trampolines with `Store`
// before the failure, but then after the failure the `Store` didn't
// hold onto the trampoline.
drop(Instance::new(&store, &module1, &[]));
drop(module1);
// Compile two modules up front
let mut store = Store::<()>::default();
let module1 = Module::new(store.engine(), "(module (import \"\" \"\" (func)))")?;
let module2 = Module::new(store.engine(), "(module (func (export \"\")))")?;
// Start instantiating the first module, but this will fail.
// Historically this registered the module's trampolines with `Store`
// before the failure, but then after the failure the `Store` didn't
// hold onto the trampoline.
drop(Instance::new(&mut store, &module1, &[]));
drop(module1);
// Then instantiate another module which has the same function type (no
// parameters or results) which tries to use the trampoline defined in
// the previous module. Then we extract the function and, in another
// scope where everything is dropped, we call the func.
let i = Instance::new(&store, &module2, &[])?;
i.get_func("").unwrap()
};
// Then instantiate another module which has the same function type (no
// parameters or results) which tries to use the trampoline defined in
// the previous module. Then we extract the function and, after we drop the
// module's reference, we call the func.
let i = Instance::new(&mut store, &module2, &[])?;
let func = i.get_func(&mut store, "").unwrap();
drop(module2);
// ... and no segfaults! right? right? ...
func.call(&[])?;
func.call(&mut store, &[])?;
Ok(())
}
#[test]
#[cfg(not(feature = "old-x86-backend"))]
fn typed_multiple_results() -> anyhow::Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let module = Module::new(
store.engine(),
r#"
@@ -567,16 +580,21 @@ fn typed_multiple_results() -> anyhow::Result<()> {
"#,
)?;
let instance = Instance::new(&store, &module, &[])?;
let f0 = instance.get_func("f0").unwrap();
assert!(f0.typed::<(), ()>().is_err());
assert!(f0.typed::<(), (i32, f32)>().is_err());
assert!(f0.typed::<(), i32>().is_err());
assert_eq!(f0.typed::<(), (i32, i64)>()?.call(())?, (0, 1));
let f1 = instance.get_func("f1").unwrap();
let instance = Instance::new(&mut store, &module, &[])?;
let f0 = instance.get_func(&mut store, "f0").unwrap();
assert!(f0.typed::<(), (), _>(&store).is_err());
assert!(f0.typed::<(), (i32, f32), _>(&store).is_err());
assert!(f0.typed::<(), i32, _>(&store).is_err());
assert_eq!(
f1.typed::<(i32, i32, i32), (f32, f64)>()?.call((1, 2, 3))?,
f0.typed::<(), (i32, i64), _>(&store)?
.call(&mut store, ())?,
(0, 1)
);
let f1 = instance.get_func(&mut store, "f1").unwrap();
assert_eq!(
f1.typed::<(i32, i32, i32), (f32, f64), _>(&store)?
.call(&mut store, (1, 2, 3))?,
(2., 3.)
);
Ok(())
@@ -584,108 +602,117 @@ fn typed_multiple_results() -> anyhow::Result<()> {
#[test]
fn trap_doesnt_leak() -> anyhow::Result<()> {
struct Canary(Rc<Cell<bool>>);
#[derive(Default)]
struct Canary(Arc<AtomicBool>);
impl Drop for Canary {
fn drop(&mut self) {
self.0.set(true);
self.0.store(true, SeqCst);
}
}
let store = Store::default();
let mut store = Store::<()>::default();
// test that `Func::wrap` is correct
let canary1 = Canary(Rc::new(Cell::new(false)));
let canary1 = Canary::default();
let dtor1_run = canary1.0.clone();
let f1 = Func::wrap(&store, move || -> Result<(), Trap> {
let f1 = Func::wrap(&mut store, move || -> Result<(), Trap> {
drop(&canary1);
Err(Trap::new(""))
});
assert!(f1.typed::<(), ()>()?.call(()).is_err());
assert!(f1.call(&[]).is_err());
assert!(f1.typed::<(), (), _>(&store)?.call(&mut store, ()).is_err());
assert!(f1.call(&mut store, &[]).is_err());
// test that `Func::new` is correct
let canary2 = Canary(Rc::new(Cell::new(false)));
let canary2 = Canary::default();
let dtor2_run = canary2.0.clone();
let f2 = Func::new(&store, FuncType::new(None, None), move |_, _, _| {
let f2 = Func::new(&mut store, FuncType::new(None, None), move |_, _, _| {
drop(&canary2);
Err(Trap::new(""))
});
assert!(f2.typed::<(), ()>()?.call(()).is_err());
assert!(f2.call(&[]).is_err());
assert!(f2.typed::<(), (), _>(&store)?.call(&mut store, ()).is_err());
assert!(f2.call(&mut store, &[]).is_err());
// drop everything and ensure dtors are run
drop((store, f1, f2));
assert!(dtor1_run.get());
assert!(dtor2_run.get());
drop(store);
assert!(dtor1_run.load(SeqCst));
assert!(dtor2_run.load(SeqCst));
Ok(())
}
#[test]
#[cfg(not(feature = "old-x86-backend"))]
fn wrap_multiple_results() -> anyhow::Result<()> {
fn test<T>(store: &Store, t: T) -> anyhow::Result<()>
fn test<T>(store: &mut Store<()>, t: T) -> anyhow::Result<()>
where
T: WasmRet + WasmResults + PartialEq + Copy + std::fmt::Debug + EqualToValues + 'static,
T: WasmRet
+ WasmResults
+ PartialEq
+ Copy
+ std::fmt::Debug
+ EqualToValues
+ 'static
+ Send
+ Sync,
{
let f = Func::wrap(store, move || t);
assert_eq!(f.typed::<(), T>()?.call(())?, t);
assert!(t.eq_values(&f.call(&[])?));
let f = Func::wrap(&mut *store, move || t);
assert_eq!(f.typed::<(), T, _>(&store,)?.call(&mut *store, ())?, t);
assert!(t.eq_values(&f.call(&mut *store, &[])?));
let module = Module::new(store.engine(), &T::gen_wasm())?;
let instance = Instance::new(store, &module, &[f.into()])?;
let f = instance.get_func("foo").unwrap();
let instance = Instance::new(&mut *store, &module, &[f.into()])?;
let f = instance.get_func(&mut *store, "foo").unwrap();
assert_eq!(f.typed::<(), T>()?.call(())?, t);
assert!(t.eq_values(&f.call(&[])?));
assert_eq!(f.typed::<(), T, _>(&store)?.call(&mut *store, ())?, t);
assert!(t.eq_values(&f.call(&mut *store, &[])?));
Ok(())
}
let store = Store::default();
let mut store = Store::default();
// 0 element
test(&store, ())?;
test(&mut store, ())?;
// 1 element
test(&store, (1i32,))?;
test(&store, (2u32,))?;
test(&store, (3i64,))?;
test(&store, (4u64,))?;
test(&store, (5.0f32,))?;
test(&store, (6.0f64,))?;
test(&mut store, (1i32,))?;
test(&mut store, (2u32,))?;
test(&mut store, (3i64,))?;
test(&mut store, (4u64,))?;
test(&mut store, (5.0f32,))?;
test(&mut store, (6.0f64,))?;
// 2 element ...
test(&store, (7i32, 8i32))?;
test(&store, (7i32, 8i64))?;
test(&store, (7i32, 8f32))?;
test(&store, (7i32, 8f64))?;
test(&mut store, (7i32, 8i32))?;
test(&mut store, (7i32, 8i64))?;
test(&mut store, (7i32, 8f32))?;
test(&mut store, (7i32, 8f64))?;
test(&store, (7i64, 8i32))?;
test(&store, (7i64, 8i64))?;
test(&store, (7i64, 8f32))?;
test(&store, (7i64, 8f64))?;
test(&mut store, (7i64, 8i32))?;
test(&mut store, (7i64, 8i64))?;
test(&mut store, (7i64, 8f32))?;
test(&mut store, (7i64, 8f64))?;
test(&store, (7f32, 8i32))?;
test(&store, (7f32, 8i64))?;
test(&store, (7f32, 8f32))?;
test(&store, (7f32, 8f64))?;
test(&mut store, (7f32, 8i32))?;
test(&mut store, (7f32, 8i64))?;
test(&mut store, (7f32, 8f32))?;
test(&mut store, (7f32, 8f64))?;
test(&store, (7f64, 8i32))?;
test(&store, (7f64, 8i64))?;
test(&store, (7f64, 8f32))?;
test(&store, (7f64, 8f64))?;
test(&mut store, (7f64, 8i32))?;
test(&mut store, (7f64, 8i64))?;
test(&mut store, (7f64, 8f32))?;
test(&mut store, (7f64, 8f64))?;
// and beyond...
test(&store, (1i32, 2i32, 3i32))?;
test(&store, (1i32, 2f32, 3i32))?;
test(&store, (1f64, 2f32, 3i32))?;
test(&store, (1f64, 2i64, 3i32))?;
test(&store, (1f32, 2f32, 3i64, 4f64))?;
test(&store, (1f64, 2i64, 3i32, 4i64, 5f32))?;
test(&store, (1i32, 2f64, 3i64, 4f64, 5f64, 6f32))?;
test(&store, (1i64, 2i32, 3i64, 4f32, 5f32, 6i32, 7u64))?;
test(&store, (1u32, 2f32, 3u64, 4f64, 5i32, 6f32, 7u64, 8u32))?;
test(&mut store, (1i32, 2i32, 3i32))?;
test(&mut store, (1i32, 2f32, 3i32))?;
test(&mut store, (1f64, 2f32, 3i32))?;
test(&mut store, (1f64, 2i64, 3i32))?;
test(&mut store, (1f32, 2f32, 3i64, 4f64))?;
test(&mut store, (1f64, 2i64, 3i32, 4i64, 5f32))?;
test(&mut store, (1i32, 2f64, 3i64, 4f64, 5f64, 6f32))?;
test(&mut store, (1i64, 2i32, 3i64, 4f32, 5f32, 6i32, 7u64))?;
test(&mut store, (1u32, 2f32, 3u64, 4f64, 5i32, 6f32, 7u64, 8u32))?;
test(
&store,
&mut store,
(1f32, 2f64, 3f32, 4i32, 5u32, 6i64, 7f32, 8i32, 9u64),
)?;
return Ok(());
@@ -792,12 +819,68 @@ fn trampoline_for_declared_elem() -> anyhow::Result<()> {
"#,
)?;
let store = Store::new(&engine);
let instance = Instance::new(&store, &module, &[])?;
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let g = instance.get_typed_func::<(), Option<Func>>("g")?;
let g = instance.get_typed_func::<(), Option<Func>, _>(&mut store, "g")?;
let func = g.call(())?;
func.unwrap().call(&[])?;
let func = g.call(&mut store, ())?;
func.unwrap().call(&mut store, &[])?;
Ok(())
}
#[test]
fn wasm_ty_roundtrip() -> Result<(), anyhow::Error> {
let mut store = Store::<()>::default();
let debug = Func::wrap(
&mut store,
|a: i32, b: u32, c: f32, d: i64, e: u64, f: f64| {
assert_eq!(a, -1);
assert_eq!(b, 1);
assert_eq!(c, 2.0);
assert_eq!(d, -3);
assert_eq!(e, 3);
assert_eq!(f, 4.0);
},
);
let module = Module::new(
store.engine(),
r#"
(module
(import "" "" (func $debug (param i32 i32 f32 i64 i64 f64)))
(func (export "foo") (param i32 i32 f32 i64 i64 f64)
(if (i32.ne (local.get 0) (i32.const -1))
(then unreachable)
)
(if (i32.ne (local.get 1) (i32.const 1))
(then unreachable)
)
(if (f32.ne (local.get 2) (f32.const 2))
(then unreachable)
)
(if (i64.ne (local.get 3) (i64.const -3))
(then unreachable)
)
(if (i64.ne (local.get 4) (i64.const 3))
(then unreachable)
)
(if (f64.ne (local.get 5) (f64.const 4))
(then unreachable)
)
local.get 0
local.get 1
local.get 2
local.get 3
local.get 4
local.get 5
call $debug
)
)
"#,
)?;
let instance = Instance::new(&mut store, &module, &[debug.into()])?;
let foo =
instance.get_typed_func::<(i32, u32, f32, i64, u64, f64), (), _>(&mut store, "foo")?;
foo.call(&mut store, (-1, 1, 2.0, -3, 3, 4.0))?;
Ok(())
}

View File

@@ -1,11 +1,11 @@
use super::ref_types_module;
use std::cell::Cell;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
use std::sync::Arc;
use wasmtime::*;
#[test]
fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> {
let (store, module) = ref_types_module(
let (mut store, module) = ref_types_module(
r#"
(module
(func (export "func") (param funcref) (result funcref)
@@ -15,22 +15,22 @@ fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> {
"#,
)?;
let instance = Instance::new(&store, &module, &[])?;
let func = instance.get_func("func").unwrap();
let instance = Instance::new(&mut store, &module, &[])?;
let func = instance.get_func(&mut store, "func").unwrap();
// Pass in a non-null funcref.
{
let results = func.call(&[Val::FuncRef(Some(func.clone()))])?;
let results = func.call(&mut store, &[Val::FuncRef(Some(func.clone()))])?;
assert_eq!(results.len(), 1);
// Can't compare `Func` for equality, so this is the best we can do here.
let result_func = results[0].unwrap_funcref().unwrap();
assert_eq!(func.ty(), result_func.ty());
assert_eq!(func.ty(&store), result_func.ty(&store));
}
// Pass in a null funcref.
{
let results = func.call(&[Val::FuncRef(None)])?;
let results = func.call(&mut store, &[Val::FuncRef(None)])?;
assert_eq!(results.len(), 1);
let result_func = results[0].unwrap_funcref();
@@ -39,24 +39,29 @@ fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> {
// Pass in a `funcref` from another instance.
{
let other_instance = Instance::new(&store, &module, &[])?;
let other_instance_func = other_instance.get_func("func").unwrap();
let other_instance = Instance::new(&mut store, &module, &[])?;
let other_instance_func = other_instance.get_func(&mut store, "func").unwrap();
let results = func.call(&[Val::FuncRef(Some(other_instance_func.clone()))])?;
let results = func.call(
&mut store,
&[Val::FuncRef(Some(other_instance_func.clone()))],
)?;
assert_eq!(results.len(), 1);
// Can't compare `Func` for equality, so this is the best we can do here.
let result_func = results[0].unwrap_funcref().unwrap();
assert_eq!(other_instance_func.ty(), result_func.ty());
assert_eq!(other_instance_func.ty(&store), result_func.ty(&store));
}
// Passing in a `funcref` from another store fails.
{
let (other_store, other_module) = ref_types_module(r#"(module (func (export "f")))"#)?;
let other_store_instance = Instance::new(&other_store, &other_module, &[])?;
let f = other_store_instance.get_func("f").unwrap();
let (mut other_store, other_module) = ref_types_module(r#"(module (func (export "f")))"#)?;
let other_store_instance = Instance::new(&mut other_store, &other_module, &[])?;
let f = other_store_instance
.get_func(&mut other_store, "f")
.unwrap();
assert!(func.call(&[Val::FuncRef(Some(f))]).is_err());
assert!(func.call(&mut store, &[Val::FuncRef(Some(f))]).is_err());
}
Ok(())
@@ -64,7 +69,7 @@ fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> {
#[test]
fn receive_null_funcref_from_wasm() -> anyhow::Result<()> {
let (store, module) = ref_types_module(
let (mut store, module) = ref_types_module(
r#"
(module
(func (export "get-null") (result funcref)
@@ -74,10 +79,10 @@ fn receive_null_funcref_from_wasm() -> anyhow::Result<()> {
"#,
)?;
let instance = Instance::new(&store, &module, &[])?;
let get_null = instance.get_func("get-null").unwrap();
let instance = Instance::new(&mut store, &module, &[])?;
let get_null = instance.get_func(&mut store, "get-null").unwrap();
let results = get_null.call(&[])?;
let results = get_null.call(&mut store, &[])?;
assert_eq!(results.len(), 1);
let result_func = results[0].unwrap_funcref();
@@ -88,57 +93,57 @@ fn receive_null_funcref_from_wasm() -> anyhow::Result<()> {
#[test]
fn wrong_store() -> anyhow::Result<()> {
let dropped = Rc::new(Cell::new(false));
let dropped = Arc::new(AtomicBool::new(false));
{
let store1 = Store::default();
let store2 = Store::default();
let mut store1 = Store::<()>::default();
let mut store2 = Store::<()>::default();
let set = SetOnDrop(dropped.clone());
let f1 = Func::wrap(&store1, move || drop(&set));
let f2 = Func::wrap(&store2, move || Some(f1.clone()));
assert!(f2.call(&[]).is_err());
let f1 = Func::wrap(&mut store1, move || drop(&set));
let f2 = Func::wrap(&mut store2, move || Some(f1.clone()));
assert!(f2.call(&mut store2, &[]).is_err());
}
assert!(dropped.get());
assert!(dropped.load(SeqCst));
return Ok(());
struct SetOnDrop(Rc<Cell<bool>>);
struct SetOnDrop(Arc<AtomicBool>);
impl Drop for SetOnDrop {
fn drop(&mut self) {
self.0.set(true);
self.0.store(true, SeqCst);
}
}
}
#[test]
fn func_new_returns_wrong_store() -> anyhow::Result<()> {
let dropped = Rc::new(Cell::new(false));
let dropped = Arc::new(AtomicBool::new(false));
{
let store1 = Store::default();
let store2 = Store::default();
let mut store1 = Store::<()>::default();
let mut store2 = Store::<()>::default();
let set = SetOnDrop(dropped.clone());
let f1 = Func::wrap(&store1, move || drop(&set));
let f1 = Func::wrap(&mut store1, move || drop(&set));
let f2 = Func::new(
&store2,
&mut store2,
FuncType::new(None, Some(ValType::FuncRef)),
move |_, _, results| {
results[0] = f1.clone().into();
Ok(())
},
);
assert!(f2.call(&[]).is_err());
assert!(f2.call(&mut store2, &[]).is_err());
}
assert!(dropped.get());
assert!(dropped.load(SeqCst));
return Ok(());
struct SetOnDrop(Rc<Cell<bool>>);
struct SetOnDrop(Arc<AtomicBool>);
impl Drop for SetOnDrop {
fn drop(&mut self) {
self.0.set(true);
self.0.store(true, SeqCst);
}
}
}

View File

@@ -1,31 +1,19 @@
use super::ref_types_module;
use std::cell::Cell;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst};
use std::sync::Arc;
use wasmtime::*;
struct SetFlagOnDrop(Rc<Cell<bool>>);
struct SetFlagOnDrop(Arc<AtomicBool>);
impl Drop for SetFlagOnDrop {
fn drop(&mut self) {
self.0.set(true);
}
}
struct GcOnDrop {
store: Store,
gc_count: Rc<Cell<usize>>,
}
impl Drop for GcOnDrop {
fn drop(&mut self) {
self.store.gc();
self.gc_count.set(self.gc_count.get() + 1);
self.0.store(true, SeqCst);
}
}
#[test]
fn smoke_test_gc() -> anyhow::Result<()> {
let (store, module) = ref_types_module(
let (mut store, module) = ref_types_module(
r#"
(module
(import "" "" (func $do_gc))
@@ -47,18 +35,18 @@ fn smoke_test_gc() -> anyhow::Result<()> {
"#,
)?;
let do_gc = Func::wrap(&store, |caller: Caller| {
let do_gc = Func::wrap(&mut store, |mut caller: Caller<'_, _>| {
// Do a GC with `externref`s on the stack in Wasm frames.
caller.store().gc();
caller.gc();
});
let instance = Instance::new(&store, &module, &[do_gc.into()])?;
let func = instance.get_func("func").unwrap();
let instance = Instance::new(&mut store, &module, &[do_gc.into()])?;
let func = instance.get_func(&mut store, "func").unwrap();
let inner_dropped = Rc::new(Cell::new(false));
let inner_dropped = Arc::new(AtomicBool::new(false));
let r = ExternRef::new(SetFlagOnDrop(inner_dropped.clone()));
{
let args = [Val::I32(5), Val::ExternRef(Some(r.clone()))];
func.call(&args)?;
func.call(&mut store, &args)?;
}
// Still held alive by the `VMExternRefActivationsTable` (potentially in
@@ -72,14 +60,14 @@ fn smoke_test_gc() -> anyhow::Result<()> {
// Dropping `r` should drop the inner `SetFlagOnDrop` value.
drop(r);
assert!(inner_dropped.get());
assert!(inner_dropped.load(SeqCst));
Ok(())
}
#[test]
fn wasm_dropping_refs() -> anyhow::Result<()> {
let (store, module) = ref_types_module(
let (mut store, module) = ref_types_module(
r#"
(module
(func (export "drop_ref") (param externref)
@@ -89,32 +77,32 @@ fn wasm_dropping_refs() -> anyhow::Result<()> {
"#,
)?;
let instance = Instance::new(&store, &module, &[])?;
let drop_ref = instance.get_func("drop_ref").unwrap();
let instance = Instance::new(&mut store, &module, &[])?;
let drop_ref = instance.get_func(&mut store, "drop_ref").unwrap();
let num_refs_dropped = Rc::new(Cell::new(0));
let num_refs_dropped = Arc::new(AtomicUsize::new(0));
// NB: 4096 is greater than the initial `VMExternRefActivationsTable`
// capacity, so this will trigger at least one GC.
for _ in 0..4096 {
let r = ExternRef::new(CountDrops(num_refs_dropped.clone()));
let args = [Val::ExternRef(Some(r))];
drop_ref.call(&args)?;
drop_ref.call(&mut store, &args)?;
}
assert!(num_refs_dropped.get() > 0);
assert!(num_refs_dropped.load(SeqCst) > 0);
// And after doing a final GC, all the refs should have been dropped.
store.gc();
assert_eq!(num_refs_dropped.get(), 4096);
assert_eq!(num_refs_dropped.load(SeqCst), 4096);
return Ok(());
struct CountDrops(Rc<Cell<usize>>);
struct CountDrops(Arc<AtomicUsize>);
impl Drop for CountDrops {
fn drop(&mut self) {
self.0.set(self.0.get() + 1);
self.0.fetch_add(1, SeqCst);
}
}
}
@@ -156,54 +144,53 @@ fn many_live_refs() -> anyhow::Result<()> {
",
);
let (store, module) = ref_types_module(&wat)?;
let (mut store, module) = ref_types_module(&wat)?;
let live_refs = Rc::new(Cell::new(0));
let live_refs = Arc::new(AtomicUsize::new(0));
let make_ref = Func::wrap(&store, {
let make_ref = Func::wrap(&mut store, {
let live_refs = live_refs.clone();
move || Some(ExternRef::new(CountLiveRefs::new(live_refs.clone())))
});
let observe_ref = Func::wrap(&store, |r: Option<ExternRef>| {
let observe_ref = Func::wrap(&mut store, |r: Option<ExternRef>| {
let r = r.unwrap();
let r = r.data().downcast_ref::<CountLiveRefs>().unwrap();
assert!(r.live_refs.get() > 0);
assert!(r.live_refs.load(SeqCst) > 0);
});
let instance = Instance::new(&store, &module, &[make_ref.into(), observe_ref.into()])?;
let many_live_refs = instance.get_func("many_live_refs").unwrap();
let instance = Instance::new(&mut store, &module, &[make_ref.into(), observe_ref.into()])?;
let many_live_refs = instance.get_func(&mut store, "many_live_refs").unwrap();
many_live_refs.call(&[])?;
many_live_refs.call(&mut store, &[])?;
store.gc();
assert_eq!(live_refs.get(), 0);
assert_eq!(live_refs.load(SeqCst), 0);
return Ok(());
struct CountLiveRefs {
live_refs: Rc<Cell<usize>>,
live_refs: Arc<AtomicUsize>,
}
impl CountLiveRefs {
fn new(live_refs: Rc<Cell<usize>>) -> Self {
let live = live_refs.get();
live_refs.set(live + 1);
fn new(live_refs: Arc<AtomicUsize>) -> Self {
live_refs.fetch_add(1, SeqCst);
Self { live_refs }
}
}
impl Drop for CountLiveRefs {
fn drop(&mut self) {
let live = self.live_refs.get();
self.live_refs.set(live - 1);
self.live_refs.fetch_sub(1, SeqCst);
}
}
}
#[test]
#[cfg(not(feature = "old-x86-backend"))] // uses atomic instrs not implemented here
fn drop_externref_via_table_set() -> anyhow::Result<()> {
let (store, module) = ref_types_module(
let (mut store, module) = ref_types_module(
r#"
(module
(table $t 1 externref)
@@ -215,373 +202,218 @@ fn drop_externref_via_table_set() -> anyhow::Result<()> {
"#,
)?;
let instance = Instance::new(&store, &module, &[])?;
let table_set = instance.get_func("table-set").unwrap();
let instance = Instance::new(&mut store, &module, &[])?;
let table_set = instance.get_func(&mut store, "table-set").unwrap();
let foo_is_dropped = Rc::new(Cell::new(false));
let bar_is_dropped = Rc::new(Cell::new(false));
let foo_is_dropped = Arc::new(AtomicBool::new(false));
let bar_is_dropped = Arc::new(AtomicBool::new(false));
let foo = ExternRef::new(SetFlagOnDrop(foo_is_dropped.clone()));
let bar = ExternRef::new(SetFlagOnDrop(bar_is_dropped.clone()));
{
let args = vec![Val::ExternRef(Some(foo))];
table_set.call(&args)?;
table_set.call(&mut store, &args)?;
}
store.gc();
assert!(!foo_is_dropped.get());
assert!(!bar_is_dropped.get());
assert!(!foo_is_dropped.load(SeqCst));
assert!(!bar_is_dropped.load(SeqCst));
{
let args = vec![Val::ExternRef(Some(bar))];
table_set.call(&args)?;
table_set.call(&mut store, &args)?;
}
store.gc();
assert!(foo_is_dropped.get());
assert!(!bar_is_dropped.get());
assert!(foo_is_dropped.load(SeqCst));
assert!(!bar_is_dropped.load(SeqCst));
table_set.call(&[Val::ExternRef(None)])?;
assert!(foo_is_dropped.get());
assert!(bar_is_dropped.get());
table_set.call(&mut store, &[Val::ExternRef(None)])?;
assert!(foo_is_dropped.load(SeqCst));
assert!(bar_is_dropped.load(SeqCst));
Ok(())
}
#[test]
fn gc_in_externref_dtor() -> anyhow::Result<()> {
let (store, module) = ref_types_module(
r#"
(module
(table $t 1 externref)
(func (export "table-set") (param externref)
(table.set $t (i32.const 0) (local.get 0))
)
)
"#,
)?;
let instance = Instance::new(&store, &module, &[])?;
let table_set = instance.get_func("table-set").unwrap();
let gc_count = Rc::new(Cell::new(0));
// Put a `GcOnDrop` into the table.
{
let args = vec![Val::ExternRef(Some(ExternRef::new(GcOnDrop {
store: store.clone(),
gc_count: gc_count.clone(),
})))];
table_set.call(&args)?;
}
// Remove the `GcOnDrop` from the `VMExternRefActivationsTable`.
store.gc();
// Overwrite the `GcOnDrop` table element, causing it to be dropped, and
// triggering a GC.
table_set.call(&[Val::ExternRef(None)])?;
assert_eq!(gc_count.get(), 1);
Ok(())
}
#[test]
fn touch_own_table_element_in_externref_dtor() -> anyhow::Result<()> {
let (store, module) = ref_types_module(
r#"
(module
(table $t (export "table") 1 externref)
(func (export "table-set") (param externref)
(table.set $t (i32.const 0) (local.get 0))
)
)
"#,
)?;
let instance = Instance::new(&store, &module, &[])?;
let table = instance.get_table("table").unwrap();
let table_set = instance.get_func("table-set").unwrap();
let touched = Rc::new(Cell::new(false));
{
let args = vec![Val::ExternRef(Some(ExternRef::new(TouchTableOnDrop {
table,
touched: touched.clone(),
})))];
table_set.call(&args)?;
}
// Remove the `TouchTableOnDrop` from the `VMExternRefActivationsTable`.
store.gc();
table_set.call(&[Val::ExternRef(Some(ExternRef::new("hello".to_string())))])?;
assert!(touched.get());
fn global_drops_externref() -> anyhow::Result<()> {
test_engine(&Engine::default())?;
test_engine(&Engine::new(
Config::new().allocation_strategy(InstanceAllocationStrategy::pooling()),
)?)?;
return Ok(());
struct TouchTableOnDrop {
table: Table,
touched: Rc<Cell<bool>>,
}
fn test_engine(engine: &Engine) -> anyhow::Result<()> {
let mut store = Store::new(&engine, ());
let flag = Arc::new(AtomicBool::new(false));
let externref = ExternRef::new(SetFlagOnDrop(flag.clone()));
Global::new(
&mut store,
GlobalType::new(ValType::ExternRef, Mutability::Const),
externref.into(),
)?;
drop(store);
assert!(flag.load(SeqCst));
impl Drop for TouchTableOnDrop {
fn drop(&mut self) {
// From the `Drop` implementation, we see the new table element, not
// `self`.
let elem = self.table.get(0).unwrap().unwrap_externref().unwrap();
assert!(elem.data().is::<String>());
assert_eq!(elem.data().downcast_ref::<String>().unwrap(), "hello");
self.touched.set(true);
}
}
}
let mut store = Store::new(&engine, ());
let module = Module::new(
&engine,
r#"
(module
(global (mut externref) (ref.null extern))
#[test]
fn gc_during_gc_when_passing_refs_into_wasm() -> anyhow::Result<()> {
let (store, module) = ref_types_module(
r#"
(module
(table $t 1 externref)
(func (export "f") (param externref)
(table.set $t (i32.const 0) (local.get 0))
)
)
"#,
)?;
let instance = Instance::new(&store, &module, &[])?;
let f = instance.get_func("f").unwrap();
let gc_count = Rc::new(Cell::new(0));
for _ in 0..1024 {
let args = vec![Val::ExternRef(Some(ExternRef::new(GcOnDrop {
store: store.clone(),
gc_count: gc_count.clone(),
})))];
f.call(&args)?;
}
f.call(&[Val::ExternRef(None)])?;
store.gc();
assert_eq!(gc_count.get(), 1024);
Ok(())
}
#[test]
fn gc_during_gc_from_many_table_gets() -> anyhow::Result<()> {
let (store, module) = ref_types_module(
r#"
(module
(import "" "" (func $observe_ref (param externref)))
(table $t 1 externref)
(func (export "init") (param externref)
(table.set $t (i32.const 0) (local.get 0))
)
(func (export "run") (param i32)
(loop $continue
(if (i32.eqz (local.get 0)) (return))
(call $observe_ref (table.get $t (i32.const 0)))
(local.set 0 (i32.sub (local.get 0) (i32.const 1)))
(br $continue)
(func (export "run") (param externref)
local.get 0
global.set 0
)
)
"#,
)?;
let instance = Instance::new(&mut store, &module, &[])?;
let run = instance.get_typed_func::<Option<ExternRef>, (), _>(&mut store, "run")?;
let flag = Arc::new(AtomicBool::new(false));
let externref = ExternRef::new(SetFlagOnDrop(flag.clone()));
run.call(&mut store, Some(externref))?;
drop(store);
assert!(flag.load(SeqCst));
Ok(())
}
}
#[test]
#[cfg(not(feature = "old-x86-backend"))] // uses atomic instrs not implemented here
fn table_drops_externref() -> anyhow::Result<()> {
test_engine(&Engine::default())?;
test_engine(&Engine::new(
Config::new().allocation_strategy(InstanceAllocationStrategy::pooling()),
)?)?;
return Ok(());
fn test_engine(engine: &Engine) -> anyhow::Result<()> {
let mut store = Store::new(&engine, ());
let flag = Arc::new(AtomicBool::new(false));
let externref = ExternRef::new(SetFlagOnDrop(flag.clone()));
Table::new(
&mut store,
TableType::new(ValType::ExternRef, Limits::new(1, None)),
externref.into(),
)?;
drop(store);
assert!(flag.load(SeqCst));
let mut store = Store::new(&engine, ());
let module = Module::new(
&engine,
r#"
(module
(table 1 externref)
(func (export "run") (param externref)
i32.const 0
local.get 0
table.set 0
)
)
"#,
)?;
let instance = Instance::new(&mut store, &module, &[])?;
let run = instance.get_typed_func::<Option<ExternRef>, (), _>(&mut store, "run")?;
let flag = Arc::new(AtomicBool::new(false));
let externref = ExternRef::new(SetFlagOnDrop(flag.clone()));
run.call(&mut store, Some(externref))?;
drop(store);
assert!(flag.load(SeqCst));
Ok(())
}
}
#[test]
#[cfg(not(feature = "old-x86-backend"))] // uses atomic instrs not implemented here
fn gee_i_sure_hope_refcounting_is_atomic() -> anyhow::Result<()> {
let mut config = Config::new();
config.wasm_reference_types(true);
config.interruptable(true);
let engine = Engine::new(&config)?;
let mut store = Store::new(&engine, ());
let module = Module::new(
&engine,
r#"
(module
(global (mut externref) (ref.null extern))
(table 1 externref)
(func (export "run") (param externref)
local.get 0
global.set 0
i32.const 0
local.get 0
table.set 0
loop
global.get 0
global.set 0
i32.const 0
i32.const 0
table.get
table.set
local.get 0
call $f
br 0
end
)
(func $f (param externref))
)
"#,
)?;
let observe_ref = Func::wrap(&store, |_: Option<ExternRef>| {});
let instance = Instance::new(&mut store, &module, &[])?;
let run = instance.get_typed_func::<Option<ExternRef>, (), _>(&mut store, "run")?;
let instance = Instance::new(&store, &module, &[observe_ref.into()])?;
let init = instance.get_func("init").unwrap();
let run = instance.get_func("run").unwrap();
let flag = Arc::new(AtomicBool::new(false));
let externref = ExternRef::new(SetFlagOnDrop(flag.clone()));
let externref2 = externref.clone();
let handle = store.interrupt_handle()?;
let gc_count = Rc::new(Cell::new(0));
let child = std::thread::spawn(move || run.call(&mut store, Some(externref2)));
// Initialize the table element with a `GcOnDrop`. This also puts it in the
// `VMExternRefActivationsTable`.
{
let args = vec![Val::ExternRef(Some(ExternRef::new(GcOnDrop {
store: store.clone(),
gc_count: gc_count.clone(),
})))];
init.call(&args)?;
for _ in 0..10000 {
drop(externref.clone());
}
handle.interrupt();
// Overwrite the `GcOnDrop` with another reference. The `GcOnDrop` is still
// in the `VMExternRefActivationsTable`.
{
let args = vec![Val::ExternRef(Some(ExternRef::new(String::from("hello"))))];
init.call(&args)?;
}
// Now call `run`, which does a bunch of `table.get`s, filling up the
// `VMExternRefActivationsTable`'s bump region, and eventually triggering a
// GC that will deallocate our `GcOnDrop` which will also trigger a nested
// GC.
run.call(&[Val::I32(1024)])?;
// We should have done our nested GC.
assert_eq!(gc_count.get(), 1);
assert!(child.join().unwrap().is_err());
assert!(!flag.load(SeqCst));
assert_eq!(externref.strong_count(), 1);
drop(externref);
assert!(flag.load(SeqCst));
Ok(())
}
#[test]
fn pass_externref_into_wasm_during_destructor_in_gc() -> anyhow::Result<()> {
let (store, module) = ref_types_module(
fn global_init_no_leak() -> anyhow::Result<()> {
let (mut store, module) = ref_types_module(
r#"
(module
(table $t 1 externref)
(func (export "f") (param externref)
nop
)
(import "" "" (global externref))
(global externref (global.get 0))
)
"#,
)?;
let instance = Instance::new(&store, &module, &[])?;
let f = instance.get_func("f").unwrap();
let r = ExternRef::new("hello");
let did_call = Rc::new(Cell::new(false));
// Put a `CallOnDrop` into the `VMExternRefActivationsTable`.
{
let args = vec![Val::ExternRef(Some(ExternRef::new(CallOnDrop(
f.clone(),
r.clone(),
did_call.clone(),
))))];
f.call(&args)?;
}
// One ref count for `r`, one for the `CallOnDrop`.
assert_eq!(r.strong_count(), 2);
// Do a GC, which will see that the only reference holding the `CallOnDrop`
// is the `VMExternRefActivationsTable`, and will drop it. Dropping it will
// cause it to call into `f` again.
store.gc();
assert!(did_call.get());
// The `CallOnDrop` is no longer holding onto `r`, but the
// `VMExternRefActivationsTable` is.
assert_eq!(r.strong_count(), 2);
// GC again to empty the `VMExternRefActivationsTable`. Now `r` is the only
// thing holding its `externref` alive.
store.gc();
assert_eq!(r.strong_count(), 1);
return Ok(());
struct CallOnDrop(Func, ExternRef, Rc<Cell<bool>>);
impl Drop for CallOnDrop {
fn drop(&mut self) {
self.0
.call(&[Val::ExternRef(Some(self.1.clone()))])
.unwrap();
self.2.set(true);
}
}
}
#[test]
fn gc_on_drop_in_mutable_externref_global() -> anyhow::Result<()> {
let (store, module) = ref_types_module(
r#"
(module
(global $g (mut externref) (ref.null extern))
(func (export "set-g") (param externref)
(global.set $g (local.get 0))
)
)
"#,
let externref = ExternRef::new(());
let global = Global::new(
&mut store,
GlobalType::new(ValType::ExternRef, Mutability::Const),
externref.clone().into(),
)?;
let instance = Instance::new(&store, &module, &[])?;
let set_g = instance.get_func("set-g").unwrap();
let gc_count = Rc::new(Cell::new(0));
// Put a `GcOnDrop` into the global.
{
let args = vec![Val::ExternRef(Some(ExternRef::new(GcOnDrop {
store: store.clone(),
gc_count: gc_count.clone(),
})))];
set_g.call(&args)?;
}
// Remove the `GcOnDrop` from the `VMExternRefActivationsTable`.
store.gc();
// Overwrite the `GcOnDrop` global value, causing it to be dropped, and
// triggering a GC.
assert_eq!(gc_count.get(), 0);
set_g.call(&[Val::ExternRef(None)])?;
assert_eq!(gc_count.get(), 1);
Instance::new(&mut store, &module, &[global.into()])?;
drop(store);
assert_eq!(externref.strong_count(), 1);
Ok(())
}
#[test]
fn touch_own_externref_global_on_drop() -> anyhow::Result<()> {
let (store, module) = ref_types_module(
r#"
(module
(global $g (export "g") (mut externref) (ref.null extern))
(func (export "set-g") (param externref)
(global.set $g (local.get 0))
)
)
"#,
)?;
let instance = Instance::new(&store, &module, &[])?;
let g = instance.get_global("g").unwrap();
let set_g = instance.get_func("set-g").unwrap();
let touched = Rc::new(Cell::new(false));
{
let args = vec![Val::ExternRef(Some(ExternRef::new(TouchGlobalOnDrop {
g,
touched: touched.clone(),
})))];
set_g.call(&args)?;
}
// Remove the `TouchGlobalOnDrop` from the `VMExternRefActivationsTable`.
store.gc();
assert!(!touched.get());
set_g.call(&[Val::ExternRef(Some(ExternRef::new("hello".to_string())))])?;
assert!(touched.get());
return Ok(());
struct TouchGlobalOnDrop {
g: Global,
touched: Rc<Cell<bool>>,
}
impl Drop for TouchGlobalOnDrop {
fn drop(&mut self) {
// From the `Drop` implementation, we see the new global value, not
// `self`.
let r = self.g.get().unwrap_externref().unwrap();
assert!(r.data().is::<String>());
assert_eq!(r.data().downcast_ref::<String>().unwrap(), "hello");
self.touched.set(true);
}
}
}

View File

@@ -2,56 +2,56 @@ use wasmtime::*;
#[test]
fn smoke() -> anyhow::Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let g = Global::new(
&store,
&mut store,
GlobalType::new(ValType::I32, Mutability::Const),
0.into(),
)?;
assert_eq!(g.get().i32(), Some(0));
assert!(g.set(0.into()).is_err());
assert_eq!(g.get(&mut store).i32(), Some(0));
assert!(g.set(&mut store, 0.into()).is_err());
let g = Global::new(
&store,
&mut store,
GlobalType::new(ValType::I32, Mutability::Const),
1i32.into(),
)?;
assert_eq!(g.get().i32(), Some(1));
assert_eq!(g.get(&mut store).i32(), Some(1));
let g = Global::new(
&store,
&mut store,
GlobalType::new(ValType::I64, Mutability::Const),
2i64.into(),
)?;
assert_eq!(g.get().i64(), Some(2));
assert_eq!(g.get(&mut store).i64(), Some(2));
let g = Global::new(
&store,
&mut store,
GlobalType::new(ValType::F32, Mutability::Const),
3.0f32.into(),
)?;
assert_eq!(g.get().f32(), Some(3.0));
assert_eq!(g.get(&mut store).f32(), Some(3.0));
let g = Global::new(
&store,
&mut store,
GlobalType::new(ValType::F64, Mutability::Const),
4.0f64.into(),
)?;
assert_eq!(g.get().f64(), Some(4.0));
assert_eq!(g.get(&mut store).f64(), Some(4.0));
Ok(())
}
#[test]
fn mutability() -> anyhow::Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let g = Global::new(
&store,
&mut store,
GlobalType::new(ValType::I32, Mutability::Var),
0.into(),
)?;
assert_eq!(g.get().i32(), Some(0));
g.set(1.into())?;
assert_eq!(g.get().i32(), Some(1));
assert_eq!(g.get(&mut store).i32(), Some(0));
g.set(&mut store, 1.into())?;
assert_eq!(g.get(&mut store).i32(), Some(1));
Ok(())
}
@@ -61,7 +61,7 @@ fn mutability() -> anyhow::Result<()> {
// least some cases of heap corruption.
#[test]
fn use_after_drop() -> anyhow::Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let module = Module::new(
store.engine(),
r#"
@@ -69,18 +69,16 @@ fn use_after_drop() -> anyhow::Result<()> {
(global (export "foo") (mut i32) (i32.const 100)))
"#,
)?;
let instance = Instance::new(&store, &module, &[])?;
let g = instance.get_global("foo").unwrap();
assert_eq!(g.get().i32(), Some(100));
g.set(101.into())?;
let instance = Instance::new(&mut store, &module, &[])?;
let g = instance.get_global(&mut store, "foo").unwrap();
assert_eq!(g.get(&mut store).i32(), Some(100));
g.set(&mut store, 101.into())?;
drop(instance);
assert_eq!(g.get().i32(), Some(101));
Instance::new(&store, &module, &[])?;
assert_eq!(g.get().i32(), Some(101));
assert_eq!(g.get(&mut store).i32(), Some(101));
Instance::new(&mut store, &module, &[])?;
assert_eq!(g.get(&mut store).i32(), Some(101));
drop(module);
assert_eq!(g.get().i32(), Some(101));
drop(store);
assert_eq!(g.get().i32(), Some(101));
assert_eq!(g.get(&mut store).i32(), Some(101));
// spray some heap values
let mut x = Vec::new();
@@ -88,6 +86,6 @@ fn use_after_drop() -> anyhow::Result<()> {
x.push("xy".to_string());
}
drop(x);
assert_eq!(g.get().i32(), Some(101));
assert_eq!(g.get(&mut store).i32(), Some(101));
Ok(())
}

View File

@@ -1,49 +1,46 @@
use anyhow::Result;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use wasmtime::*;
use wasmtime_wasi::{sync::WasiCtxBuilder, Wasi};
use wasmtime_wasi::sync::WasiCtxBuilder;
#[test]
#[should_panic = "cannot use `func_new_async` without enabling async support"]
fn async_required() {
let mut config = Config::default();
config.define_host_func_async(
let engine = Engine::default();
let mut linker = Linker::<()>::new(&engine);
drop(linker.func_new_async(
"",
"",
FuncType::new(None, None),
move |_caller, _params, _results| Box::new(async { Ok(()) }),
);
assert_eq!(
Engine::new(&config)
.map_err(|e| e.to_string())
.err()
.unwrap(),
"an async host function cannot be defined without async support enabled in the config"
);
));
}
#[test]
fn wrap_func() {
let mut config = Config::default();
fn wrap_func() -> Result<()> {
let engine = Engine::default();
let mut linker = Linker::<()>::new(&engine);
linker.allow_shadowing(true);
config.wrap_host_func("", "", || {});
config.wrap_host_func("m", "f", |_: i32| {});
config.wrap_host_func("m", "f2", |_: i32, _: i64| {});
config.wrap_host_func("m2", "f", |_: f32, _: f64| {});
config.wrap_host_func("m2", "f2", || -> i32 { 0 });
config.wrap_host_func("", "", || -> i64 { 0 });
config.wrap_host_func("m", "f", || -> f32 { 0.0 });
config.wrap_host_func("m2", "f", || -> f64 { 0.0 });
config.wrap_host_func("m3", "", || -> Option<ExternRef> { None });
config.wrap_host_func("m3", "f", || -> Option<Func> { None });
linker.func_wrap("", "", || {})?;
linker.func_wrap("m", "f", |_: i32| {})?;
linker.func_wrap("m", "f2", |_: i32, _: i64| {})?;
linker.func_wrap("m2", "f", |_: f32, _: f64| {})?;
linker.func_wrap("m2", "f2", || -> i32 { 0 })?;
linker.func_wrap("", "", || -> i64 { 0 })?;
linker.func_wrap("m", "f", || -> f32 { 0.0 })?;
linker.func_wrap("m2", "f", || -> f64 { 0.0 })?;
linker.func_wrap("m3", "", || -> Option<ExternRef> { None })?;
linker.func_wrap("m3", "f", || -> Option<Func> { None })?;
config.wrap_host_func("", "f1", || -> Result<(), Trap> { loop {} });
config.wrap_host_func("", "f2", || -> Result<i32, Trap> { loop {} });
config.wrap_host_func("", "f3", || -> Result<i64, Trap> { loop {} });
config.wrap_host_func("", "f4", || -> Result<f32, Trap> { loop {} });
config.wrap_host_func("", "f5", || -> Result<f64, Trap> { loop {} });
config.wrap_host_func("", "f6", || -> Result<Option<ExternRef>, Trap> { loop {} });
config.wrap_host_func("", "f7", || -> Result<Option<Func>, Trap> { loop {} });
linker.func_wrap("", "f1", || -> Result<(), Trap> { loop {} })?;
linker.func_wrap("", "f2", || -> Result<i32, Trap> { loop {} })?;
linker.func_wrap("", "f3", || -> Result<i64, Trap> { loop {} })?;
linker.func_wrap("", "f4", || -> Result<f32, Trap> { loop {} })?;
linker.func_wrap("", "f5", || -> Result<f64, Trap> { loop {} })?;
linker.func_wrap("", "f6", || -> Result<Option<ExternRef>, Trap> { loop {} })?;
linker.func_wrap("", "f7", || -> Result<Option<Func>, Trap> { loop {} })?;
Ok(())
}
#[test]
@@ -57,25 +54,27 @@ fn drop_func() -> Result<()> {
}
}
let mut config = Config::default();
let engine = Engine::default();
let mut linker = Linker::<()>::new(&engine);
linker.allow_shadowing(true);
let a = A;
config.wrap_host_func("", "", move || {
linker.func_wrap("", "", move || {
drop(&a);
});
})?;
assert_eq!(HITS.load(SeqCst), 0);
// Define the function again to ensure redefined functions are dropped
let a = A;
config.wrap_host_func("", "", move || {
linker.func_wrap("", "", move || {
drop(&a);
});
})?;
assert_eq!(HITS.load(SeqCst), 1);
drop(config);
drop(linker);
assert_eq!(HITS.load(SeqCst), 2);
@@ -94,45 +93,33 @@ fn drop_delayed() -> Result<()> {
}
}
let mut config = Config::default();
let engine = Engine::default();
let mut linker = Linker::<()>::new(&engine);
let a = A;
config.wrap_host_func("", "", move || drop(&a));
linker.func_wrap("", "", move || drop(&a))?;
assert_eq!(HITS.load(SeqCst), 0);
let engine = Engine::new(&config)?;
let module = Module::new(&engine, &wat::parse_str(r#"(import "" "" (func))"#)?)?;
let store = Store::new(&engine);
let instance = Instance::new(
&store,
&module,
&[store
.get_host_func("", "")
.expect("function should be defined")
.into()],
)?;
let mut store = Store::new(&engine, ());
let func = linker.get(&mut store, "", Some("")).unwrap();
Instance::new(&mut store, &module, &[func])?;
drop((instance, store));
drop(store);
assert_eq!(HITS.load(SeqCst), 0);
let store = Store::new(&engine);
let instance = Instance::new(
&store,
&module,
&[store
.get_host_func("", "")
.expect("function should be defined")
.into()],
)?;
let mut store = Store::new(&engine, ());
let func = linker.get(&mut store, "", Some("")).unwrap();
Instance::new(&mut store, &module, &[func])?;
drop((instance, store, engine, module));
drop(store);
assert_eq!(HITS.load(SeqCst), 0);
drop(config);
drop(linker);
assert_eq!(HITS.load(SeqCst), 1);
@@ -141,67 +128,71 @@ fn drop_delayed() -> Result<()> {
#[test]
fn signatures_match() -> Result<()> {
let mut config = Config::default();
let engine = Engine::default();
let mut linker = Linker::<()>::new(&engine);
config.wrap_host_func("", "f1", || {});
config.wrap_host_func("", "f2", || -> i32 { loop {} });
config.wrap_host_func("", "f3", || -> i64 { loop {} });
config.wrap_host_func("", "f4", || -> f32 { loop {} });
config.wrap_host_func("", "f5", || -> f64 { loop {} });
config.wrap_host_func(
linker.func_wrap("", "f1", || {})?;
linker.func_wrap("", "f2", || -> i32 { loop {} })?;
linker.func_wrap("", "f3", || -> i64 { loop {} })?;
linker.func_wrap("", "f4", || -> f32 { loop {} })?;
linker.func_wrap("", "f5", || -> f64 { loop {} })?;
linker.func_wrap(
"",
"f6",
|_: f32, _: f64, _: i32, _: i64, _: i32, _: Option<ExternRef>, _: Option<Func>| -> f64 {
loop {}
},
);
)?;
let engine = Engine::new(&config)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let f = store
.get_host_func("", "f1")
.expect("func should be defined");
let f = linker
.get(&mut store, "", Some("f1"))
.unwrap()
.into_func()
.unwrap();
assert_eq!(f.ty(&store).params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty().params().collect::<Vec<_>>(), &[]);
assert_eq!(f.param_arity(), 0);
assert_eq!(f.ty().results().collect::<Vec<_>>(), &[]);
assert_eq!(f.result_arity(), 0);
let f = linker
.get(&mut store, "", Some("f2"))
.unwrap()
.into_func()
.unwrap();
assert_eq!(f.ty(&store).params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[ValType::I32]);
let f = store
.get_host_func("", "f2")
.expect("func should be defined");
let f = linker
.get(&mut store, "", Some("f3"))
.unwrap()
.into_func()
.unwrap();
assert_eq!(f.ty(&store).params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[ValType::I64]);
assert_eq!(f.ty().params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty().results().collect::<Vec<_>>(), &[ValType::I32]);
let f = linker
.get(&mut store, "", Some("f4"))
.unwrap()
.into_func()
.unwrap();
assert_eq!(f.ty(&store).params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[ValType::F32]);
let f = store
.get_host_func("", "f3")
.expect("func should be defined");
assert_eq!(f.ty().params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty().results().collect::<Vec<_>>(), &[ValType::I64]);
let f = store
.get_host_func("", "f4")
.expect("func should be defined");
assert_eq!(f.ty().params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty().results().collect::<Vec<_>>(), &[ValType::F32]);
let f = store
.get_host_func("", "f5")
.expect("func should be defined");
assert_eq!(f.ty().params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty().results().collect::<Vec<_>>(), &[ValType::F64]);
let f = store
.get_host_func("", "f6")
.expect("func should be defined");
let f = linker
.get(&mut store, "", Some("f5"))
.unwrap()
.into_func()
.unwrap();
assert_eq!(f.ty(&store).params().collect::<Vec<_>>(), &[]);
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[ValType::F64]);
let f = linker
.get(&mut store, "", Some("f6"))
.unwrap()
.into_func()
.unwrap();
assert_eq!(
f.ty().params().collect::<Vec<_>>(),
f.ty(&store).params().collect::<Vec<_>>(),
&[
ValType::F32,
ValType::F64,
@@ -212,7 +203,7 @@ fn signatures_match() -> Result<()> {
ValType::FuncRef,
]
);
assert_eq!(f.ty().results().collect::<Vec<_>>(), &[ValType::F64]);
assert_eq!(f.ty(&store).results().collect::<Vec<_>>(), &[ValType::F64]);
Ok(())
}
@@ -249,29 +240,36 @@ fn import_works() -> Result<()> {
"#,
)?;
let mut config = Config::new();
config.wasm_reference_types(true);
let engine = Engine::default();
let mut linker = Linker::<()>::new(&engine);
config.wrap_host_func("", "f1", || {
linker.func_wrap("", "f1", || {
assert_eq!(HITS.fetch_add(1, SeqCst), 0);
});
})?;
config.wrap_host_func("", "f2", |x: i32| -> i32 {
linker.func_wrap("", "f2", |x: i32| -> i32 {
assert_eq!(x, 0);
assert_eq!(HITS.fetch_add(1, SeqCst), 1);
1
});
})?;
config.wrap_host_func("", "f3", |x: i32, y: i64| {
linker.func_wrap("", "f3", |x: i32, y: i64| {
assert_eq!(x, 2);
assert_eq!(y, 3);
assert_eq!(HITS.fetch_add(1, SeqCst), 2);
});
})?;
config.wrap_host_func(
linker.func_wrap(
"",
"f4",
|a: i32, b: i64, c: i32, d: f32, e: f64, f: Option<ExternRef>, g: Option<Func>| {
|mut caller: Caller<'_, _>,
a: i32,
b: i64,
c: i32,
d: f32,
e: f64,
f: Option<ExternRef>,
g: Option<Func>| {
assert_eq!(a, 100);
assert_eq!(b, 200);
assert_eq!(c, 300);
@@ -281,43 +279,27 @@ fn import_works() -> Result<()> {
f.as_ref().unwrap().data().downcast_ref::<String>().unwrap(),
"hello"
);
assert_eq!(g.as_ref().unwrap().call(&[]).unwrap()[0].unwrap_i32(), 42);
assert_eq!(
g.as_ref().unwrap().call(&mut caller, &[]).unwrap()[0].unwrap_i32(),
42
);
assert_eq!(HITS.fetch_add(1, SeqCst), 3);
},
);
let engine = Engine::new(&config)?;
let module = Module::new(&engine, &wasm)?;
let store = Store::new(&engine);
let instance = Instance::new(
&store,
&module,
&[
store
.get_host_func("", "f1")
.expect("should be defined")
.into(),
store
.get_host_func("", "f2")
.expect("should be defined")
.into(),
store
.get_host_func("", "f3")
.expect("should be defined")
.into(),
store
.get_host_func("", "f4")
.expect("should be defined")
.into(),
],
)?;
let run = instance.get_func("run").unwrap();
run.call(&[
Val::ExternRef(Some(ExternRef::new("hello".to_string()))),
Val::FuncRef(Some(Func::wrap(&store, || -> i32 { 42 }))),
])?;
let module = Module::new(&engine, &wasm)?;
let mut store = Store::new(&engine, ());
let instance = linker.instantiate(&mut store, &module)?;
let run = instance.get_func(&mut store, "run").unwrap();
let funcref = Val::FuncRef(Some(Func::wrap(&mut store, || -> i32 { 42 })));
run.call(
&mut store,
&[
Val::ExternRef(Some(ExternRef::new("hello".to_string()))),
funcref,
],
)?;
assert_eq!(HITS.load(SeqCst), 4);
@@ -345,9 +327,10 @@ fn call_import_many_args() -> Result<()> {
"#,
)?;
let mut config = Config::new();
let engine = Engine::default();
let mut linker = Linker::<()>::new(&engine);
config.wrap_host_func(
linker.func_wrap(
"",
"host",
|x1: i32,
@@ -371,23 +354,13 @@ fn call_import_many_args() -> Result<()> {
assert_eq!(x9, 9);
assert_eq!(x10, 10);
},
);
let engine = Engine::new(&config)?;
let module = Module::new(&engine, &wasm)?;
let store = Store::new(&engine);
let instance = Instance::new(
&store,
&module,
&[store
.get_host_func("", "host")
.expect("should be defined")
.into()],
)?;
let run = instance.get_func("run").unwrap();
run.call(&[])?;
let module = Module::new(&engine, &wasm)?;
let mut store = Store::new(&engine, ());
let instance = linker.instantiate(&mut store, &module)?;
let run = instance.get_func(&mut store, "run").unwrap();
run.call(&mut store, &[])?;
Ok(())
}
@@ -428,48 +401,56 @@ fn call_wasm_many_args() -> Result<()> {
"#,
)?;
let config = Config::new();
let engine = Engine::new(&config)?;
let engine = Engine::default();
let module = Module::new(&engine, &wasm)?;
let store = Store::new(&engine);
let instance = Instance::new(&store, &module, &[])?;
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let run = instance.get_func("run").unwrap();
run.call(&[
1.into(),
2.into(),
3.into(),
4.into(),
5.into(),
6.into(),
7.into(),
8.into(),
9.into(),
10.into(),
])?;
let run = instance.get_func(&mut store, "run").unwrap();
run.call(
&mut store,
&[
1.into(),
2.into(),
3.into(),
4.into(),
5.into(),
6.into(),
7.into(),
8.into(),
9.into(),
10.into(),
],
)?;
let typed_run =
instance.get_typed_func::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32), ()>("run")?;
typed_run.call((1, 2, 3, 4, 5, 6, 7, 8, 9, 10))?;
let typed_run = instance
.get_typed_func::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32), (), _>(
&mut store, "run",
)?;
typed_run.call(&mut store, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10))?;
let test = instance.get_func("test").unwrap();
test.call(&[])?;
let test = instance.get_func(&mut store, "test").unwrap();
test.call(&mut store, &[])?;
Ok(())
}
#[test]
fn trap_smoke() -> Result<()> {
let mut config = Config::default();
config.wrap_host_func("", "", || -> Result<(), Trap> { Err(Trap::new("test")) });
let engine = Engine::default();
let mut linker = Linker::<()>::new(&engine);
linker.func_wrap("", "", || -> Result<(), Trap> { Err(Trap::new("test")) })?;
let engine = Engine::new(&config)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let f = store.get_host_func("", "").expect("should be defined");
let f = linker
.get(&mut store, "", Some(""))
.unwrap()
.into_func()
.unwrap();
let err = f.call(&[]).unwrap_err().downcast::<Trap>()?;
let err = f.call(&mut store, &[]).unwrap_err().downcast::<Trap>()?;
assert!(err.to_string().contains("test"));
assert!(err.i32_exit_status().is_none());
@@ -486,21 +467,18 @@ fn trap_import() -> Result<()> {
"#,
)?;
let mut config = Config::default();
config.wrap_host_func("", "", || -> Result<(), Trap> { Err(Trap::new("foo")) });
let engine = Engine::default();
let mut linker = Linker::new(&engine);
linker.func_wrap("", "", || -> Result<(), Trap> { Err(Trap::new("foo")) })?;
let engine = Engine::new(&config)?;
let module = Module::new(&engine, &wasm)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let trap = Instance::new(
&store,
&module,
&[store.get_host_func("", "").expect("defined").into()],
)
.err()
.unwrap()
.downcast::<Trap>()?;
let trap = linker
.instantiate(&mut store, &module)
.err()
.unwrap()
.downcast::<Trap>()?;
assert!(trap.to_string().contains("foo"));
@@ -509,96 +487,130 @@ fn trap_import() -> Result<()> {
#[test]
fn new_from_signature() -> Result<()> {
let mut config = Config::default();
let engine = Engine::default();
let mut linker = Linker::new(&engine);
let ty = FuncType::new(None, None);
config.define_host_func("", "f1", ty, |_, _, _| panic!());
linker.func_new("", "f1", ty, |_, _, _| panic!())?;
let ty = FuncType::new(Some(ValType::I32), Some(ValType::F64));
config.define_host_func("", "f2", ty, |_, _, _| panic!());
linker.func_new("", "f2", ty, |_, _, _| panic!())?;
let engine = Engine::new(&config)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let f = store.get_host_func("", "f1").expect("func defined");
assert!(f.typed::<(), ()>().is_ok());
assert!(f.typed::<(), i32>().is_err());
assert!(f.typed::<i32, ()>().is_err());
let f = linker
.get(&mut store, "", Some("f1"))
.unwrap()
.into_func()
.unwrap();
assert!(f.typed::<(), (), _>(&store).is_ok());
assert!(f.typed::<(), i32, _>(&store).is_err());
assert!(f.typed::<i32, (), _>(&store).is_err());
let f = store.get_host_func("", "f2").expect("func defined");
assert!(f.typed::<(), ()>().is_err());
assert!(f.typed::<(), i32>().is_err());
assert!(f.typed::<i32, ()>().is_err());
assert!(f.typed::<i32, f64>().is_ok());
let f = linker
.get(&mut store, "", Some("f2"))
.unwrap()
.into_func()
.unwrap();
assert!(f.typed::<(), (), _>(&store).is_err());
assert!(f.typed::<(), i32, _>(&store).is_err());
assert!(f.typed::<i32, (), _>(&store).is_err());
assert!(f.typed::<i32, f64, _>(&store).is_ok());
Ok(())
}
#[test]
fn call_wrapped_func() -> Result<()> {
let mut config = Config::default();
let engine = Engine::default();
let mut linker = Linker::new(&engine);
config.wrap_host_func("", "f1", |a: i32, b: i64, c: f32, d: f64| {
linker.func_wrap("", "f1", |a: i32, b: i64, c: f32, d: f64| {
assert_eq!(a, 1);
assert_eq!(b, 2);
assert_eq!(c, 3.0);
assert_eq!(d, 4.0);
});
})?;
config.wrap_host_func("", "f2", || 1i32);
linker.func_wrap("", "f2", || 1i32)?;
config.wrap_host_func("", "f3", || 2i64);
linker.func_wrap("", "f3", || 2i64)?;
config.wrap_host_func("", "f4", || 3.0f32);
linker.func_wrap("", "f4", || 3.0f32)?;
config.wrap_host_func("", "f5", || 4.0f64);
linker.func_wrap("", "f5", || 4.0f64)?;
let engine = Engine::new(&config)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let f = store.get_host_func("", "f1").expect("func defined");
f.call(&[Val::I32(1), Val::I64(2), 3.0f32.into(), 4.0f64.into()])?;
f.typed::<(i32, i64, f32, f64), ()>()?
.call((1, 2, 3.0, 4.0))?;
let f = linker
.get(&mut store, "", Some("f1"))
.unwrap()
.into_func()
.unwrap();
f.call(
&mut store,
&[Val::I32(1), Val::I64(2), 3.0f32.into(), 4.0f64.into()],
)?;
f.typed::<(i32, i64, f32, f64), (), _>(&store)?
.call(&mut store, (1, 2, 3.0, 4.0))?;
let f = store.get_host_func("", "f2").expect("func defined");
let results = f.call(&[])?;
let f = linker
.get(&mut store, "", Some("f2"))
.unwrap()
.into_func()
.unwrap();
let results = f.call(&mut store, &[])?;
assert_eq!(results.len(), 1);
assert_eq!(results[0].unwrap_i32(), 1);
assert_eq!(f.typed::<(), i32>()?.call(())?, 1);
assert_eq!(f.typed::<(), i32, _>(&store)?.call(&mut store, ())?, 1);
let f = store.get_host_func("", "f3").expect("func defined");
let results = f.call(&[])?;
let f = linker
.get(&mut store, "", Some("f3"))
.unwrap()
.into_func()
.unwrap();
let results = f.call(&mut store, &[])?;
assert_eq!(results.len(), 1);
assert_eq!(results[0].unwrap_i64(), 2);
assert_eq!(f.typed::<(), i64>()?.call(())?, 2);
assert_eq!(f.typed::<(), i64, _>(&store)?.call(&mut store, ())?, 2);
let f = store.get_host_func("", "f4").expect("func defined");
let results = f.call(&[])?;
let f = linker
.get(&mut store, "", Some("f4"))
.unwrap()
.into_func()
.unwrap();
let results = f.call(&mut store, &[])?;
assert_eq!(results.len(), 1);
assert_eq!(results[0].unwrap_f32(), 3.0);
assert_eq!(f.typed::<(), f32>()?.call(())?, 3.0);
assert_eq!(f.typed::<(), f32, _>(&store)?.call(&mut store, ())?, 3.0);
let f = store.get_host_func("", "f5").expect("func defined");
let results = f.call(&[])?;
let f = linker
.get(&mut store, "", Some("f5"))
.unwrap()
.into_func()
.unwrap();
let results = f.call(&mut store, &[])?;
assert_eq!(results.len(), 1);
assert_eq!(results[0].unwrap_f64(), 4.0);
assert_eq!(f.typed::<(), f64>()?.call(())?, 4.0);
assert_eq!(f.typed::<(), f64, _>(&store)?.call(&mut store, ())?, 4.0);
Ok(())
}
#[test]
fn func_return_nothing() -> Result<()> {
let mut config = Config::default();
let engine = Engine::default();
let mut linker = Linker::new(&engine);
let ty = FuncType::new(None, Some(ValType::I32));
linker.func_new("", "", ty, |_, _, _| Ok(()))?;
config.define_host_func("", "", ty, |_, _, _| Ok(()));
let engine = Engine::new(&config)?;
let store = Store::new(&engine);
let f = store.get_host_func("", "").expect("func defined");
let err = f.call(&[]).unwrap_err().downcast::<Trap>()?;
let mut store = Store::new(&engine, ());
let f = linker
.get(&mut store, "", Some(""))
.unwrap()
.into_func()
.unwrap();
let err = f.call(&mut store, &[]).unwrap_err().downcast::<Trap>()?;
assert!(err
.to_string()
.contains("function attempted to return an incompatible value"));
@@ -629,40 +641,45 @@ fn call_via_funcref() -> Result<()> {
"#,
)?;
let mut config = Config::default();
let engine = Engine::default();
let mut linker = Linker::new(&engine);
let a = A;
config.wrap_host_func("", "", move |x: i32, y: i32| {
linker.func_wrap("", "", move |x: i32, y: i32| {
drop(&a);
x + y
});
})?;
let engine = Engine::new(&config)?;
let module = Module::new(&engine, &wasm)?;
let store = Store::new(&engine);
let instance = Instance::new(&store, &module, &[])?;
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let results = instance
.get_func("call")
let f = linker
.get(&mut store, "", Some(""))
.unwrap()
.call(&[store.get_host_func("", "").expect("func defined").into()])?;
.into_func()
.unwrap();
let results = instance
.get_func(&mut store, "call")
.unwrap()
.call(&mut store, &[f.into()])?;
assert_eq!(results.len(), 2);
assert_eq!(results[0].unwrap_i32(), 7);
{
let f = results[1].unwrap_funcref().unwrap();
let results = f.call(&[1.into(), 2.into()])?;
let results = f.call(&mut store, &[1.into(), 2.into()])?;
assert_eq!(results.len(), 1);
assert_eq!(results[0].unwrap_i32(), 3);
}
assert_eq!(HITS.load(SeqCst), 0);
drop((results, instance, store, module, engine));
drop(store);
assert_eq!(HITS.load(SeqCst), 0);
drop(config);
drop(linker);
assert_eq!(HITS.load(SeqCst), 1);
@@ -672,77 +689,35 @@ fn call_via_funcref() -> Result<()> {
#[test]
fn store_with_context() -> Result<()> {
struct Ctx {
called: std::cell::Cell<bool>,
called: bool,
}
let mut config = Config::default();
let engine = Engine::default();
let mut linker = Linker::new(&engine);
config.wrap_host_func("", "", |caller: Caller| {
let ctx = caller
.store()
.get::<Ctx>()
.expect("store should have context");
ctx.called.set(true);
});
linker.func_wrap("", "", |mut caller: Caller<'_, Ctx>| {
caller.data_mut().called = true;
})?;
let engine = Engine::new(&config)?;
let store = Store::new(&engine);
assert!(store.get::<Ctx>().is_none());
assert!(store
.set(Ctx {
called: std::cell::Cell::new(false)
})
.is_ok());
assert!(store
.set(Ctx {
called: std::cell::Cell::new(false)
})
.is_err());
assert!(!store.get::<Ctx>().unwrap().called.get());
let mut store = Store::new(&engine, Ctx { called: false });
let f = store.get_host_func("", "").expect("func defined");
f.call(&[])?;
let f = linker
.get(&mut store, "", Some(""))
.unwrap()
.into_func()
.unwrap();
f.call(&mut store, &[])?;
assert!(store.get::<Ctx>().unwrap().called.get());
Ok(())
}
#[test]
fn wasi_imports_missing_context() -> Result<()> {
let mut config = Config::default();
Wasi::add_to_config(&mut config);
let wasm = wat::parse_str(
r#"
(import "wasi_snapshot_preview1" "proc_exit" (func $__wasi_proc_exit (param i32)))
(memory (export "memory") 0)
(func (export "_start")
(call $__wasi_proc_exit (i32.const 123))
)
"#,
)?;
let engine = Engine::new(&config)?;
let module = Module::new(&engine, wasm)?;
let store = Store::new(&engine);
let linker = Linker::new(&store);
let instance = linker.instantiate(&module)?;
let start = instance.get_typed_func::<(), ()>("_start")?;
let trap = start.call(()).unwrap_err();
assert!(trap.to_string().contains("context is missing in the store"));
assert!(trap.i32_exit_status().is_none());
assert!(store.data().called);
Ok(())
}
#[test]
fn wasi_imports() -> Result<()> {
let mut config = Config::default();
Wasi::add_to_config(&mut config);
let engine = Engine::default();
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |s| s)?;
let wasm = wat::parse_str(
r#"
@@ -754,15 +729,12 @@ fn wasi_imports() -> Result<()> {
"#,
)?;
let engine = Engine::new(&config)?;
let module = Module::new(&engine, wasm)?;
let store = Store::new(&engine);
assert!(Wasi::set_context(&store, WasiCtxBuilder::new().build()).is_ok());
let linker = Linker::new(&store);
let instance = linker.instantiate(&module)?;
let mut store = Store::new(&engine, WasiCtxBuilder::new().build());
let instance = linker.instantiate(&mut store, &module)?;
let start = instance.get_typed_func::<(), ()>("_start")?;
let trap = start.call(()).unwrap_err();
let start = instance.get_typed_func::<(), (), _>(&mut store, "_start")?;
let trap = start.call(&mut store, ()).unwrap_err();
assert_eq!(trap.i32_exit_status(), Some(123));
Ok(())

View File

@@ -1,12 +1,12 @@
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use wasmtime::*;
fn interruptable_store() -> Store {
fn interruptable_store() -> Store<()> {
let engine = Engine::new(Config::new().interruptable(true)).unwrap();
Store::new(&engine)
Store::new(&engine, ())
}
fn hugely_recursive_module(store: &Store) -> anyhow::Result<Module> {
fn hugely_recursive_module(engine: &Engine) -> anyhow::Result<Module> {
let mut wat = String::new();
wat.push_str(
r#"
@@ -19,30 +19,30 @@ fn hugely_recursive_module(store: &Store) -> anyhow::Result<Module> {
}
wat.push_str("(func call 0)\n");
Module::new(store.engine(), &wat)
Module::new(engine, &wat)
}
#[test]
fn loops_interruptable() -> anyhow::Result<()> {
let store = interruptable_store();
let mut store = interruptable_store();
let module = Module::new(store.engine(), r#"(func (export "loop") (loop br 0))"#)?;
let instance = Instance::new(&store, &module, &[])?;
let iloop = instance.get_typed_func::<(), ()>("loop")?;
let instance = Instance::new(&mut store, &module, &[])?;
let iloop = instance.get_typed_func::<(), (), _>(&mut store, "loop")?;
store.interrupt_handle()?.interrupt();
let trap = iloop.call(()).unwrap_err();
let trap = iloop.call(&mut store, ()).unwrap_err();
assert!(trap.to_string().contains("wasm trap: interrupt"));
Ok(())
}
#[test]
fn functions_interruptable() -> anyhow::Result<()> {
let store = interruptable_store();
let module = hugely_recursive_module(&store)?;
let func = Func::wrap(&store, || {});
let instance = Instance::new(&store, &module, &[func.into()])?;
let iloop = instance.get_typed_func::<(), ()>("loop")?;
let mut store = interruptable_store();
let module = hugely_recursive_module(store.engine())?;
let func = Func::wrap(&mut store, || {});
let instance = Instance::new(&mut store, &module, &[func.into()])?;
let iloop = instance.get_typed_func::<(), (), _>(&mut store, "loop")?;
store.interrupt_handle()?.interrupt();
let trap = iloop.call(()).unwrap_err();
let trap = iloop.call(&mut store, ()).unwrap_err();
assert!(
trap.to_string().contains("wasm trap: interrupt"),
"{}",
@@ -57,7 +57,7 @@ fn loop_interrupt_from_afar() -> anyhow::Result<()> {
// the loop so we can count the number of loop iterations we've executed so
// far.
static HITS: AtomicUsize = AtomicUsize::new(0);
let store = interruptable_store();
let mut store = interruptable_store();
let module = Module::new(
store.engine(),
r#"
@@ -70,10 +70,10 @@ fn loop_interrupt_from_afar() -> anyhow::Result<()> {
)
"#,
)?;
let func = Func::wrap(&store, || {
let func = Func::wrap(&mut store, || {
HITS.fetch_add(1, SeqCst);
});
let instance = Instance::new(&store, &module, &[func.into()])?;
let instance = Instance::new(&mut store, &module, &[func.into()])?;
// Use the instance's interrupt handle to wait for it to enter the loop long
// enough and then we signal an interrupt happens.
@@ -88,8 +88,8 @@ fn loop_interrupt_from_afar() -> anyhow::Result<()> {
// Enter the infinitely looping function and assert that our interrupt
// handle does indeed actually interrupt the function.
let iloop = instance.get_typed_func::<(), ()>("loop")?;
let trap = iloop.call(()).unwrap_err();
let iloop = instance.get_typed_func::<(), (), _>(&mut store, "loop")?;
let trap = iloop.call(&mut store, ()).unwrap_err();
thread.join().unwrap();
assert!(
trap.to_string().contains("wasm trap: interrupt"),
@@ -105,12 +105,12 @@ fn function_interrupt_from_afar() -> anyhow::Result<()> {
// the loop so we can count the number of loop iterations we've executed so
// far.
static HITS: AtomicUsize = AtomicUsize::new(0);
let store = interruptable_store();
let module = hugely_recursive_module(&store)?;
let func = Func::wrap(&store, || {
let mut store = interruptable_store();
let module = hugely_recursive_module(store.engine())?;
let func = Func::wrap(&mut store, || {
HITS.fetch_add(1, SeqCst);
});
let instance = Instance::new(&store, &module, &[func.into()])?;
let instance = Instance::new(&mut store, &module, &[func.into()])?;
// Use the instance's interrupt handle to wait for it to enter the loop long
// enough and then we signal an interrupt happens.
@@ -124,8 +124,8 @@ fn function_interrupt_from_afar() -> anyhow::Result<()> {
// Enter the infinitely looping function and assert that our interrupt
// handle does indeed actually interrupt the function.
let iloop = instance.get_typed_func::<(), ()>("loop")?;
let trap = iloop.call(()).unwrap_err();
let iloop = instance.get_typed_func::<(), (), _>(&mut store, "loop")?;
let trap = iloop.call(&mut store, ()).unwrap_err();
thread.join().unwrap();
assert!(
trap.to_string().contains("wasm trap: interrupt"),

View File

@@ -1,6 +1,4 @@
use anyhow::Result;
use std::cell::RefCell;
use std::rc::Rc;
use wasmtime::*;
#[test]
@@ -16,39 +14,38 @@ fn test_import_calling_export() {
)
"#;
let store = Store::default();
let mut store = Store::<Option<Func>>::default();
let module = Module::new(store.engine(), WAT).expect("failed to create module");
let other = Rc::new(RefCell::new(None::<Func>));
let other2 = Rc::downgrade(&other);
let callback_func = Func::new(&store, FuncType::new(None, None), move |_, _, _| {
other2
.upgrade()
.unwrap()
.borrow()
.as_ref()
.expect("expected a function ref")
.call(&[])
.expect("expected function not to trap");
Ok(())
});
let imports = vec![callback_func.into()];
let instance =
Instance::new(&store, &module, imports.as_slice()).expect("failed to instantiate module");
let run_func = instance
.get_func("run")
.expect("expected a run func in the module");
*other.borrow_mut() = Some(
instance
.get_func("other")
.expect("expected an other func in the module"),
let callback_func = Func::new(
&mut store,
FuncType::new(None, None),
move |mut caller, _, _| {
caller
.data()
.unwrap()
.call(&mut caller, &[])
.expect("expected function not to trap");
Ok(())
},
);
run_func.call(&[]).expect("expected function not to trap");
let imports = vec![callback_func.into()];
let instance = Instance::new(&mut store, &module, imports.as_slice())
.expect("failed to instantiate module");
let run_func = instance
.get_func(&mut store, "run")
.expect("expected a run func in the module");
let other_func = instance
.get_func(&mut store, "other")
.expect("expected an other func in the module");
*store.data_mut() = Some(other_func);
run_func
.call(&mut store, &[])
.expect("expected function not to trap");
}
#[test]
@@ -62,11 +59,11 @@ fn test_returns_incorrect_type() -> Result<()> {
)
"#;
let store = Store::default();
let mut store = Store::<()>::default();
let module = Module::new(store.engine(), WAT)?;
let callback_func = Func::new(
&store,
&mut store,
FuncType::new(None, Some(ValType::I32)),
|_, _, results| {
// Evil! Returns I64 here instead of promised in the signature I32.
@@ -76,14 +73,14 @@ fn test_returns_incorrect_type() -> Result<()> {
);
let imports = vec![callback_func.into()];
let instance = Instance::new(&store, &module, imports.as_slice())?;
let instance = Instance::new(&mut store, &module, imports.as_slice())?;
let run_func = instance
.get_func("run")
.get_func(&mut store, "run")
.expect("expected a run func in the module");
let trap = run_func
.call(&[])
.call(&mut store, &[])
.expect_err("the execution should fail")
.downcast::<Trap>()?;
assert!(trap

View File

@@ -14,12 +14,12 @@ fn same_import_names_still_distinct() -> anyhow::Result<()> {
)
"#;
let store = Store::default();
let mut store = Store::<()>::default();
let module = Module::new(store.engine(), WAT)?;
let imports = [
Func::new(
&store,
&mut store,
FuncType::new(None, Some(ValType::I32)),
|_, params, results| {
assert!(params.is_empty());
@@ -30,7 +30,7 @@ fn same_import_names_still_distinct() -> anyhow::Result<()> {
)
.into(),
Func::new(
&store,
&mut store,
FuncType::new(None, Some(ValType::F32)),
|_, params, results| {
assert!(params.is_empty());
@@ -41,10 +41,10 @@ fn same_import_names_still_distinct() -> anyhow::Result<()> {
)
.into(),
];
let instance = Instance::new(&store, &module, &imports)?;
let instance = Instance::new(&mut store, &module, &imports)?;
let func = instance.get_typed_func::<(), i32>("foo")?;
let result = func.call(())?;
let func = instance.get_typed_func::<(), i32, _>(&mut store, "foo")?;
let result = func.call(&mut store, ())?;
assert_eq!(result, 3);
Ok(())
}

View File

@@ -3,12 +3,12 @@ use wasmtime::*;
#[test]
fn wrong_import_numbers() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let module = Module::new(store.engine(), r#"(module (import "" "" (func)))"#)?;
assert!(Instance::new(&store, &module, &[]).is_err());
let func = Func::wrap(&store, || {});
assert!(Instance::new(&store, &module, &[func.clone().into(), func.into()]).is_err());
assert!(Instance::new(&mut store, &module, &[]).is_err());
let func = Func::wrap(&mut store, || {});
assert!(Instance::new(&mut store, &module, &[func.clone().into(), func.into()]).is_err());
Ok(())
}
@@ -22,12 +22,54 @@ fn initializes_linear_memory() -> Result<()> {
)"#;
let module = Module::new(&Engine::default(), wat)?;
let store = Store::new(module.engine());
let instance = Instance::new(&store, &module, &[])?;
let memory = instance.get_memory("memory").unwrap();
let mut store = Store::new(module.engine(), ());
let instance = Instance::new(&mut store, &module, &[])?;
let memory = instance.get_memory(&mut store, "memory").unwrap();
let mut bytes = [0; 12];
memory.read(0, &mut bytes)?;
memory.read(&store, 0, &mut bytes)?;
assert_eq!(bytes, "Hello World!".as_bytes());
Ok(())
}
#[test]
fn linear_memory_limits() -> Result<()> {
// this test will allocate 4GB of virtual memory space, and may not work in
// situations like CI QEMU emulation where it triggers SIGKILL.
if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() {
return Ok(());
}
test(&Engine::default())?;
test(&Engine::new(Config::new().allocation_strategy(
InstanceAllocationStrategy::Pooling {
strategy: PoolingAllocationStrategy::NextAvailable,
module_limits: ModuleLimits {
memory_pages: 65536,
..ModuleLimits::default()
},
instance_limits: InstanceLimits::default(),
},
))?)?;
return Ok(());
fn test(engine: &Engine) -> Result<()> {
let wat = r#"
(module
(memory 65535)
(func (export "foo") (result i32)
i32.const 1
memory.grow)
)
"#;
let module = Module::new(engine, wat)?;
let mut store = Store::new(engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let foo = instance.get_typed_func::<(), i32, _>(&mut store, "foo")?;
assert_eq!(foo.call(&mut store, ())?, 65535);
assert_eq!(foo.call(&mut store, ())?, -1);
Ok(())
}
}

View File

@@ -3,7 +3,7 @@ use wasmtime::*;
#[test]
fn test_invoke_func_via_table() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let wat = r#"
(module
@@ -14,18 +14,19 @@ fn test_invoke_func_via_table() -> Result<()> {
)
"#;
let module = Module::new(store.engine(), wat).context("> Error compiling module!")?;
let instance = Instance::new(&store, &module, &[]).context("> Error instantiating module!")?;
let instance =
Instance::new(&mut store, &module, &[]).context("> Error instantiating module!")?;
let f = instance
.get_table("table")
.get_table(&mut store, "table")
.unwrap()
.get(0)
.get(&mut store, 0)
.unwrap()
.funcref()
.unwrap()
.unwrap()
.clone();
let result = f.call(&[]).unwrap();
let result = f.call(&mut store, &[]).unwrap();
assert_eq!(result[0].unwrap_i64(), 42);
Ok(())
}

View File

@@ -1,6 +1,6 @@
use anyhow::Result;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst};
use std::sync::Arc;
use wasmtime::*;
#[test]
@@ -11,47 +11,50 @@ fn test_limits() -> Result<()> {
r#"(module (memory (export "m") 0) (table (export "t") 0 anyfunc))"#,
)?;
let store = Store::new_with_limits(
&engine,
let mut store = Store::new(&engine, ());
store.limiter(
StoreLimitsBuilder::new()
.memory_pages(10)
.table_elements(5)
.build(),
);
let instance = Instance::new(&store, &module, &[])?;
let instance = Instance::new(&mut store, &module, &[])?;
// Test instance exports and host objects hitting the limit
for memory in std::array::IntoIter::new([
instance.get_memory("m").unwrap(),
Memory::new(&store, MemoryType::new(Limits::new(0, None)))?,
instance.get_memory(&mut store, "m").unwrap(),
Memory::new(&mut store, MemoryType::new(Limits::new(0, None)))?,
]) {
memory.grow(3)?;
memory.grow(5)?;
memory.grow(2)?;
memory.grow(&mut store, 3)?;
memory.grow(&mut store, 5)?;
memory.grow(&mut store, 2)?;
assert_eq!(
memory.grow(1).map_err(|e| e.to_string()).unwrap_err(),
memory
.grow(&mut store, 1)
.map_err(|e| e.to_string())
.unwrap_err(),
"failed to grow memory by `1`"
);
}
// Test instance exports and host objects hitting the limit
for table in std::array::IntoIter::new([
instance.get_table("t").unwrap(),
instance.get_table(&mut store, "t").unwrap(),
Table::new(
&store,
&mut store,
TableType::new(ValType::FuncRef, Limits::new(0, None)),
Val::FuncRef(None),
)?,
]) {
table.grow(2, Val::FuncRef(None))?;
table.grow(1, Val::FuncRef(None))?;
table.grow(2, Val::FuncRef(None))?;
table.grow(&mut store, 2, Val::FuncRef(None))?;
table.grow(&mut store, 1, Val::FuncRef(None))?;
table.grow(&mut store, 2, Val::FuncRef(None))?;
assert_eq!(
table
.grow(1, Val::FuncRef(None))
.grow(&mut store, 1, Val::FuncRef(None))
.map_err(|e| e.to_string())
.unwrap_err(),
"failed to grow table by `1`"
@@ -69,38 +72,42 @@ fn test_limits_memory_only() -> Result<()> {
r#"(module (memory (export "m") 0) (table (export "t") 0 anyfunc))"#,
)?;
let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().memory_pages(10).build());
let mut store = Store::new(&engine, ());
store.limiter(StoreLimitsBuilder::new().memory_pages(10).build());
let instance = Instance::new(&store, &module, &[])?;
let instance = Instance::new(&mut store, &module, &[])?;
// Test instance exports and host objects hitting the limit
for memory in std::array::IntoIter::new([
instance.get_memory("m").unwrap(),
Memory::new(&store, MemoryType::new(Limits::new(0, None)))?,
instance.get_memory(&mut store, "m").unwrap(),
Memory::new(&mut store, MemoryType::new(Limits::new(0, None)))?,
]) {
memory.grow(3)?;
memory.grow(5)?;
memory.grow(2)?;
memory.grow(&mut store, 3)?;
memory.grow(&mut store, 5)?;
memory.grow(&mut store, 2)?;
assert_eq!(
memory.grow(1).map_err(|e| e.to_string()).unwrap_err(),
memory
.grow(&mut store, 1)
.map_err(|e| e.to_string())
.unwrap_err(),
"failed to grow memory by `1`"
);
}
// Test instance exports and host objects *not* hitting the limit
for table in std::array::IntoIter::new([
instance.get_table("t").unwrap(),
instance.get_table(&mut store, "t").unwrap(),
Table::new(
&store,
&mut store,
TableType::new(ValType::FuncRef, Limits::new(0, None)),
Val::FuncRef(None),
)?,
]) {
table.grow(2, Val::FuncRef(None))?;
table.grow(1, Val::FuncRef(None))?;
table.grow(2, Val::FuncRef(None))?;
table.grow(1, Val::FuncRef(None))?;
table.grow(&mut store, 2, Val::FuncRef(None))?;
table.grow(&mut store, 1, Val::FuncRef(None))?;
table.grow(&mut store, 2, Val::FuncRef(None))?;
table.grow(&mut store, 1, Val::FuncRef(None))?;
}
Ok(())
@@ -111,9 +118,10 @@ fn test_initial_memory_limits_exceeded() -> Result<()> {
let engine = Engine::default();
let module = Module::new(&engine, r#"(module (memory (export "m") 11))"#)?;
let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().memory_pages(10).build());
let mut store = Store::new(&engine, ());
store.limiter(StoreLimitsBuilder::new().memory_pages(10).build());
match Instance::new(&store, &module, &[]) {
match Instance::new(&mut store, &module, &[]) {
Ok(_) => unreachable!(),
Err(e) => assert_eq!(
e.to_string(),
@@ -121,7 +129,7 @@ fn test_initial_memory_limits_exceeded() -> Result<()> {
),
}
match Memory::new(&store, MemoryType::new(Limits::new(25, None))) {
match Memory::new(&mut store, MemoryType::new(Limits::new(25, None))) {
Ok(_) => unreachable!(),
Err(e) => assert_eq!(
e.to_string(),
@@ -140,38 +148,38 @@ fn test_limits_table_only() -> Result<()> {
r#"(module (memory (export "m") 0) (table (export "t") 0 anyfunc))"#,
)?;
let store =
Store::new_with_limits(&engine, StoreLimitsBuilder::new().table_elements(5).build());
let mut store = Store::new(&engine, ());
store.limiter(StoreLimitsBuilder::new().table_elements(5).build());
let instance = Instance::new(&store, &module, &[])?;
let instance = Instance::new(&mut store, &module, &[])?;
// Test instance exports and host objects *not* hitting the limit
for memory in std::array::IntoIter::new([
instance.get_memory("m").unwrap(),
Memory::new(&store, MemoryType::new(Limits::new(0, None)))?,
instance.get_memory(&mut store, "m").unwrap(),
Memory::new(&mut store, MemoryType::new(Limits::new(0, None)))?,
]) {
memory.grow(3)?;
memory.grow(5)?;
memory.grow(2)?;
memory.grow(1)?;
memory.grow(&mut store, 3)?;
memory.grow(&mut store, 5)?;
memory.grow(&mut store, 2)?;
memory.grow(&mut store, 1)?;
}
// Test instance exports and host objects hitting the limit
for table in std::array::IntoIter::new([
instance.get_table("t").unwrap(),
instance.get_table(&mut store, "t").unwrap(),
Table::new(
&store,
&mut store,
TableType::new(ValType::FuncRef, Limits::new(0, None)),
Val::FuncRef(None),
)?,
]) {
table.grow(2, Val::FuncRef(None))?;
table.grow(1, Val::FuncRef(None))?;
table.grow(2, Val::FuncRef(None))?;
table.grow(&mut store, 2, Val::FuncRef(None))?;
table.grow(&mut store, 1, Val::FuncRef(None))?;
table.grow(&mut store, 2, Val::FuncRef(None))?;
assert_eq!(
table
.grow(1, Val::FuncRef(None))
.grow(&mut store, 1, Val::FuncRef(None))
.map_err(|e| e.to_string())
.unwrap_err(),
"failed to grow table by `1`"
@@ -186,10 +194,10 @@ fn test_initial_table_limits_exceeded() -> Result<()> {
let engine = Engine::default();
let module = Module::new(&engine, r#"(module (table (export "t") 23 anyfunc))"#)?;
let store =
Store::new_with_limits(&engine, StoreLimitsBuilder::new().table_elements(4).build());
let mut store = Store::new(&engine, ());
store.limiter(StoreLimitsBuilder::new().table_elements(4).build());
match Instance::new(&store, &module, &[]) {
match Instance::new(&mut store, &module, &[]) {
Ok(_) => unreachable!(),
Err(e) => assert_eq!(
e.to_string(),
@@ -198,7 +206,7 @@ fn test_initial_table_limits_exceeded() -> Result<()> {
}
match Table::new(
&store,
&mut store,
TableType::new(ValType::FuncRef, Limits::new(99, None)),
Val::FuncRef(None),
) {
@@ -234,9 +242,10 @@ fn test_pooling_allocator_initial_limits_exceeded() -> Result<()> {
r#"(module (memory (export "m1") 2) (memory (export "m2") 5))"#,
)?;
let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().memory_pages(3).build());
let mut store = Store::new(&engine, ());
store.limiter(StoreLimitsBuilder::new().memory_pages(3).build());
match Instance::new(&store, &module, &[]) {
match Instance::new(&mut store, &module, &[]) {
Ok(_) => unreachable!(),
Err(e) => assert_eq!(
e.to_string(),
@@ -247,135 +256,136 @@ fn test_pooling_allocator_initial_limits_exceeded() -> Result<()> {
// An instance should still be able to be created after the failure above
let module = Module::new(&engine, r#"(module (memory (export "m") 2))"#)?;
Instance::new(&store, &module, &[])?;
Instance::new(&mut store, &module, &[])?;
Ok(())
}
struct MemoryContext {
host_memory_used: usize,
wasm_memory_used: usize,
host_memory_used: AtomicUsize,
wasm_memory_used: AtomicUsize,
memory_limit: usize,
limit_exceeded: bool,
limiter_dropped: bool,
limit_exceeded: AtomicBool,
limiter_dropped: AtomicBool,
}
struct HostMemoryLimiter(Rc<RefCell<MemoryContext>>);
struct HostMemoryLimiter(Arc<MemoryContext>);
impl ResourceLimiter for HostMemoryLimiter {
fn memory_growing(&self, current: u32, desired: u32, maximum: Option<u32>) -> bool {
let mut ctx = self.0.borrow_mut();
fn memory_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> bool {
// Check if the desired exceeds a maximum (either from Wasm or from the host)
if desired > maximum.unwrap_or(u32::MAX) {
ctx.limit_exceeded = true;
self.0.limit_exceeded.store(true, SeqCst);
return false;
}
assert_eq!(current as usize * 0x10000, ctx.wasm_memory_used);
assert_eq!(
current as usize * 0x10000,
self.0.wasm_memory_used.load(SeqCst)
);
let desired = desired as usize * 0x10000;
if desired + ctx.host_memory_used > ctx.memory_limit {
ctx.limit_exceeded = true;
if desired + self.0.host_memory_used.load(SeqCst) > self.0.memory_limit {
self.0.limit_exceeded.store(true, SeqCst);
return false;
}
ctx.wasm_memory_used = desired;
self.0.wasm_memory_used.store(desired, SeqCst);
true
}
fn table_growing(&self, _current: u32, _desired: u32, _maximum: Option<u32>) -> bool {
fn table_growing(&mut self, _current: u32, _desired: u32, _maximum: Option<u32>) -> bool {
true
}
}
impl Drop for HostMemoryLimiter {
fn drop(&mut self) {
self.0.borrow_mut().limiter_dropped = true;
self.0.limiter_dropped.store(true, SeqCst);
}
}
#[test]
fn test_custom_limiter() -> Result<()> {
let mut config = Config::default();
let engine = Engine::default();
let mut linker = Linker::new(&engine);
// This approximates a function that would "allocate" resources that the host tracks.
// Here this is a simple function that increments the current host memory "used".
config.wrap_host_func("", "alloc", |caller: Caller, size: u32| -> u32 {
if let Some(ctx) = caller.store().get::<Rc<RefCell<MemoryContext>>>() {
let mut ctx = ctx.borrow_mut();
linker.func_wrap(
"",
"alloc",
|caller: Caller<'_, Arc<MemoryContext>>, size: u32| -> u32 {
let ctx = caller.data();
let size = size as usize;
if size + ctx.host_memory_used + ctx.wasm_memory_used <= ctx.memory_limit {
ctx.host_memory_used += size;
if size + ctx.host_memory_used.load(SeqCst) + ctx.wasm_memory_used.load(SeqCst)
<= ctx.memory_limit
{
ctx.host_memory_used.fetch_add(size, SeqCst);
return 1;
}
ctx.limit_exceeded = true;
}
ctx.limit_exceeded.store(true, SeqCst);
0
});
0
},
)?;
let engine = Engine::new(&config)?;
let module = Module::new(
&engine,
r#"(module (import "" "alloc" (func $alloc (param i32) (result i32))) (memory (export "m") 0) (func (export "f") (param i32) (result i32) local.get 0 call $alloc))"#,
)?;
let context = Rc::new(RefCell::new(MemoryContext {
host_memory_used: 0,
wasm_memory_used: 0,
let context = Arc::new(MemoryContext {
host_memory_used: AtomicUsize::new(0),
wasm_memory_used: AtomicUsize::new(0),
memory_limit: 1 << 20, // 16 wasm pages is the limit for both wasm + host memory
limit_exceeded: false,
limiter_dropped: false,
}));
limit_exceeded: AtomicBool::new(false),
limiter_dropped: AtomicBool::new(false),
});
let store = Store::new_with_limits(&engine, HostMemoryLimiter(context.clone()));
assert!(store.set(context.clone()).is_ok());
let linker = Linker::new(&store);
let instance = linker.instantiate(&module)?;
let memory = instance.get_memory("m").unwrap();
let mut store = Store::new(&engine, context.clone());
store.limiter(HostMemoryLimiter(context.clone()));
let instance = linker.instantiate(&mut store, &module)?;
let memory = instance.get_memory(&mut store, "m").unwrap();
// Grow the memory by 640 KiB
memory.grow(3)?;
memory.grow(5)?;
memory.grow(2)?;
memory.grow(&mut store, 3)?;
memory.grow(&mut store, 5)?;
memory.grow(&mut store, 2)?;
assert!(!context.borrow().limit_exceeded);
assert!(!context.limit_exceeded.load(SeqCst));
// Grow the host "memory" by 384 KiB
let f = instance.get_typed_func::<u32, u32>("f")?;
let f = instance.get_typed_func::<u32, u32, _>(&mut store, "f")?;
assert_eq!(f.call(1 * 0x10000).unwrap(), 1);
assert_eq!(f.call(3 * 0x10000).unwrap(), 1);
assert_eq!(f.call(2 * 0x10000).unwrap(), 1);
assert_eq!(f.call(&mut store, 1 * 0x10000)?, 1);
assert_eq!(f.call(&mut store, 3 * 0x10000)?, 1);
assert_eq!(f.call(&mut store, 2 * 0x10000)?, 1);
// Memory is at the maximum, but the limit hasn't been exceeded
assert!(!context.borrow().limit_exceeded);
assert!(!context.limit_exceeded.load(SeqCst));
// Try to grow the memory again
assert_eq!(
memory.grow(1).map_err(|e| e.to_string()).unwrap_err(),
memory
.grow(&mut store, 1)
.map_err(|e| e.to_string())
.unwrap_err(),
"failed to grow memory by `1`"
);
assert!(context.borrow().limit_exceeded);
assert!(context.limit_exceeded.load(SeqCst));
// Try to grow the host "memory" again
assert_eq!(f.call(1).unwrap(), 0);
assert_eq!(f.call(&mut store, 1)?, 0);
assert!(context.borrow().limit_exceeded);
assert!(context.limit_exceeded.load(SeqCst));
drop(f);
drop(memory);
drop(instance);
drop(linker);
drop(store);
assert!(context.borrow().limiter_dropped);
assert!(context.limiter_dropped.load(SeqCst));
Ok(())
}

View File

@@ -1,89 +1,91 @@
use anyhow::Result;
use std::cell::Cell;
use std::rc::Rc;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use std::sync::Arc;
use wasmtime::*;
#[test]
fn link_undefined() -> Result<()> {
let store = Store::default();
let linker = Linker::new(&store);
let mut store = Store::<()>::default();
let linker = Linker::new(store.engine());
let module = Module::new(store.engine(), r#"(module (import "" "" (func)))"#)?;
assert!(linker.instantiate(&module).is_err());
assert!(linker.instantiate(&mut store, &module).is_err());
let module = Module::new(store.engine(), r#"(module (import "" "" (global i32)))"#)?;
assert!(linker.instantiate(&module).is_err());
assert!(linker.instantiate(&mut store, &module).is_err());
let module = Module::new(store.engine(), r#"(module (import "" "" (memory 1)))"#)?;
assert!(linker.instantiate(&module).is_err());
assert!(linker.instantiate(&mut store, &module).is_err());
let module = Module::new(
store.engine(),
r#"(module (import "" "" (table 1 funcref)))"#,
)?;
assert!(linker.instantiate(&module).is_err());
assert!(linker.instantiate(&mut store, &module).is_err());
Ok(())
}
#[test]
fn link_twice_bad() -> Result<()> {
let store = Store::default();
let mut linker = Linker::new(&store);
let mut store = Store::<()>::default();
let mut linker = Linker::<()>::new(store.engine());
// functions
linker.func("f", "", || {})?;
assert!(linker.func("f", "", || {}).is_err());
linker.func_wrap("f", "", || {})?;
assert!(linker.func_wrap("f", "", || {}).is_err());
assert!(linker
.func("f", "", || -> Result<(), Trap> { loop {} })
.func_wrap("f", "", || -> Result<(), Trap> { loop {} })
.is_err());
// globals
let ty = GlobalType::new(ValType::I32, Mutability::Const);
let global = Global::new(&store, ty, Val::I32(0))?;
let global = Global::new(&mut store, ty, Val::I32(0))?;
linker.define("g", "1", global.clone())?;
assert!(linker.define("g", "1", global.clone()).is_err());
let ty = GlobalType::new(ValType::I32, Mutability::Var);
let global = Global::new(&store, ty, Val::I32(0))?;
let global = Global::new(&mut store, ty, Val::I32(0))?;
linker.define("g", "2", global.clone())?;
assert!(linker.define("g", "2", global.clone()).is_err());
let ty = GlobalType::new(ValType::I64, Mutability::Const);
let global = Global::new(&store, ty, Val::I64(0))?;
let global = Global::new(&mut store, ty, Val::I64(0))?;
linker.define("g", "3", global.clone())?;
assert!(linker.define("g", "3", global.clone()).is_err());
// memories
let ty = MemoryType::new(Limits::new(1, None));
let memory = Memory::new(&store, ty)?;
let memory = Memory::new(&mut store, ty)?;
linker.define("m", "", memory.clone())?;
assert!(linker.define("m", "", memory.clone()).is_err());
let ty = MemoryType::new(Limits::new(2, None));
let memory = Memory::new(&store, ty)?;
let memory = Memory::new(&mut store, ty)?;
assert!(linker.define("m", "", memory.clone()).is_err());
// tables
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
let table = Table::new(&store, ty, Val::FuncRef(None))?;
let table = Table::new(&mut store, ty, Val::FuncRef(None))?;
linker.define("t", "", table.clone())?;
assert!(linker.define("t", "", table.clone()).is_err());
let ty = TableType::new(ValType::FuncRef, Limits::new(2, None));
let table = Table::new(&store, ty, Val::FuncRef(None))?;
let table = Table::new(&mut store, ty, Val::FuncRef(None))?;
assert!(linker.define("t", "", table.clone()).is_err());
Ok(())
}
#[test]
fn function_interposition() -> Result<()> {
let store = Store::default();
let mut linker = Linker::new(&store);
let mut store = Store::<()>::default();
let mut linker = Linker::new(store.engine());
linker.allow_shadowing(true);
let mut module = Module::new(
store.engine(),
r#"(module (func (export "green") (result i32) (i32.const 7)))"#,
)?;
for _ in 0..4 {
let instance = linker.instantiate(&module)?;
let instance = linker.instantiate(&mut store, &module)?;
linker.define(
"red",
"green",
instance.get_export("green").unwrap().clone(),
instance.get_export(&mut store, "green").unwrap().clone(),
)?;
module = Module::new(
store.engine(),
@@ -93,10 +95,14 @@ fn function_interposition() -> Result<()> {
)"#,
)?;
}
let instance = linker.instantiate(&module)?;
let func = instance.get_export("green").unwrap().into_func().unwrap();
let func = func.typed::<(), i32>()?;
assert_eq!(func.call(())?, 112);
let instance = linker.instantiate(&mut store, &module)?;
let func = instance
.get_export(&mut store, "green")
.unwrap()
.into_func()
.unwrap();
let func = func.typed::<(), i32, _>(&store)?;
assert_eq!(func.call(&mut store, ())?, 112);
Ok(())
}
@@ -104,19 +110,19 @@ fn function_interposition() -> Result<()> {
// differs from the module's name.
#[test]
fn function_interposition_renamed() -> Result<()> {
let store = Store::default();
let mut linker = Linker::new(&store);
let mut store = Store::<()>::default();
let mut linker = Linker::new(store.engine());
linker.allow_shadowing(true);
let mut module = Module::new(
store.engine(),
r#"(module (func (export "export") (result i32) (i32.const 7)))"#,
)?;
for _ in 0..4 {
let instance = linker.instantiate(&module)?;
let instance = linker.instantiate(&mut store, &module)?;
linker.define(
"red",
"green",
instance.get_export("export").unwrap().clone(),
instance.get_export(&mut store, "export").unwrap().clone(),
)?;
module = Module::new(
store.engine(),
@@ -126,10 +132,10 @@ fn function_interposition_renamed() -> Result<()> {
)"#,
)?;
}
let instance = linker.instantiate(&module)?;
let func = instance.get_func("export").unwrap();
let func = func.typed::<(), i32>()?;
assert_eq!(func.call(())?, 112);
let instance = linker.instantiate(&mut store, &module)?;
let func = instance.get_func(&mut store, "export").unwrap();
let func = func.typed::<(), i32, _>(&store)?;
assert_eq!(func.call(&mut store, ())?, 112);
Ok(())
}
@@ -137,16 +143,16 @@ fn function_interposition_renamed() -> Result<()> {
// `Linker::define`.
#[test]
fn module_interposition() -> Result<()> {
let store = Store::default();
let mut linker = Linker::new(&store);
let mut store = Store::<()>::default();
let mut linker = Linker::new(store.engine());
linker.allow_shadowing(true);
let mut module = Module::new(
store.engine(),
r#"(module (func (export "export") (result i32) (i32.const 7)))"#,
)?;
for _ in 0..4 {
let instance = linker.instantiate(&module)?;
linker.instance("instance", &instance)?;
let instance = linker.instantiate(&mut store, &module)?;
linker.instance(&mut store, "instance", instance)?;
module = Module::new(
store.engine(),
r#"(module
@@ -155,27 +161,31 @@ fn module_interposition() -> Result<()> {
)"#,
)?;
}
let instance = linker.instantiate(&module)?;
let func = instance.get_export("export").unwrap().into_func().unwrap();
let func = func.typed::<(), i32>()?;
assert_eq!(func.call(())?, 112);
let instance = linker.instantiate(&mut store, &module)?;
let func = instance
.get_export(&mut store, "export")
.unwrap()
.into_func()
.unwrap();
let func = func.typed::<(), i32, _>(&store)?;
assert_eq!(func.call(&mut store, ())?, 112);
Ok(())
}
#[test]
fn allow_unknown_exports() -> Result<()> {
let store = Store::default();
let mut linker = Linker::new(&store);
let mut store = Store::<()>::default();
let mut linker = Linker::new(store.engine());
let module = Module::new(
store.engine(),
r#"(module (func (export "_start")) (global (export "g") i32 (i32.const 0)))"#,
)?;
assert!(linker.module("module", &module).is_err());
assert!(linker.module(&mut store, "module", &module).is_err());
let mut linker = Linker::new(&store);
let mut linker = Linker::new(store.engine());
linker.allow_unknown_exports(true);
linker.module("module", &module)?;
linker.module(&mut store, "module", &module)?;
Ok(())
}
@@ -192,10 +202,8 @@ fn no_leak() -> Result<()> {
let flag = Rc::new(Cell::new(false));
{
let store = Store::default();
let mut linker = Linker::new(&store);
let drop_me = DropMe(flag.clone());
linker.func("", "", move || drop(&drop_me))?;
let mut store = Store::new(&Engine::default(), DropMe(flag.clone()));
let mut linker = Linker::new(store.engine());
let module = Module::new(
store.engine(),
r#"
@@ -204,7 +212,7 @@ fn no_leak() -> Result<()> {
)
"#,
)?;
linker.module("a", &module)?;
linker.module(&mut store, "a", &module)?;
}
assert!(flag.get(), "store was leaked");
Ok(())
@@ -212,20 +220,20 @@ fn no_leak() -> Result<()> {
#[test]
fn no_leak_with_imports() -> Result<()> {
struct DropMe(Rc<Cell<bool>>);
struct DropMe(Arc<AtomicUsize>);
impl Drop for DropMe {
fn drop(&mut self) {
self.0.set(true);
self.0.fetch_add(1, SeqCst);
}
}
let flag = Rc::new(Cell::new(false));
let flag = Arc::new(AtomicUsize::new(0));
{
let store = Store::default();
let mut linker = Linker::new(&store);
let mut store = Store::new(&Engine::default(), DropMe(flag.clone()));
let mut linker = Linker::new(store.engine());
let drop_me = DropMe(flag.clone());
linker.func("", "", move || drop(&drop_me))?;
linker.func_wrap("", "", move || drop(&drop_me))?;
let module = Module::new(
store.engine(),
r#"
@@ -235,45 +243,68 @@ fn no_leak_with_imports() -> Result<()> {
)
"#,
)?;
linker.module("a", &module)?;
linker.module(&mut store, "a", &module)?;
}
assert!(flag.get(), "store was leaked");
assert!(flag.load(SeqCst) == 2, "something was leaked");
Ok(())
}
#[test]
fn get_host_function() -> Result<()> {
let mut config = Config::default();
config.wrap_host_func("mod", "f1", || {});
let engine = Engine::new(&config)?;
let engine = Engine::default();
let module = Module::new(&engine, r#"(module (import "mod" "f1" (func)))"#)?;
let store = Store::new(&engine);
let linker = Linker::new(&store);
assert!(linker.get(&module.imports().nth(0).unwrap()).is_some());
let mut linker = Linker::new(&engine);
linker.func_wrap("mod", "f1", || {})?;
let mut store = Store::<()>::default();
assert!(linker
.get_by_import(&mut store, &module.imports().nth(0).unwrap())
.is_some());
Ok(())
}
#[test]
fn shadowing_host_function() -> Result<()> {
let mut config = Config::default();
config.wrap_host_func("mod", "f1", || {});
fn funcs_live_on_to_fight_another_day() -> Result<()> {
struct DropMe(Arc<AtomicUsize>);
let engine = Engine::new(&config)?;
let store = Store::new(&engine);
impl Drop for DropMe {
fn drop(&mut self) {
self.0.fetch_add(1, SeqCst);
}
}
let mut linker = Linker::new(&store);
assert!(linker
.define("mod", "f1", Func::wrap(&store, || {}))
.is_err());
linker.define("mod", "f2", Func::wrap(&store, || {}))?;
let flag = Arc::new(AtomicUsize::new(0));
let engine = Engine::default();
let mut linker = Linker::new(&engine);
let drop_me = DropMe(flag.clone());
linker.func_wrap("", "", move || drop(&drop_me))?;
assert_eq!(flag.load(SeqCst), 0);
let mut linker = Linker::new(&store);
linker.allow_shadowing(true);
linker.define("mod", "f1", Func::wrap(&store, || {}))?;
linker.define("mod", "f2", Func::wrap(&store, || {}))?;
let get_and_call = || -> Result<()> {
assert_eq!(flag.load(SeqCst), 0);
let mut store = Store::new(&engine, ());
let func = linker.get(&mut store, "", Some("")).unwrap();
func.into_func().unwrap().call(&mut store, &[])?;
assert_eq!(flag.load(SeqCst), 0);
Ok(())
};
get_and_call()?;
get_and_call()?;
drop(linker);
assert_eq!(flag.load(SeqCst), 1);
Ok(())
}
#[test]
fn alias_one() -> Result<()> {
let mut store = Store::<()>::default();
let mut linker = Linker::new(store.engine());
assert!(linker.alias("a", "b", "c", "d").is_err());
linker.func_wrap("a", "b", || {})?;
assert!(linker.alias("a", "b", "c", "d").is_ok());
assert!(linker.get(&mut store, "a", Some("b")).is_some());
assert!(linker.get(&mut store, "c", Some("d")).is_some());
Ok(())
}

View File

@@ -22,9 +22,9 @@ mod module_serialize;
mod name;
mod pooling_allocator;
mod stack_overflow;
mod store;
mod table;
mod traps;
mod use_after_drop;
mod wast;
// TODO(#1886): Cranelift only supports reference types on x64.
@@ -37,7 +37,7 @@ mod gc;
#[cfg(target_arch = "x86_64")]
pub(crate) fn ref_types_module(
source: &str,
) -> anyhow::Result<(wasmtime::Store, wasmtime::Module)> {
) -> anyhow::Result<(wasmtime::Store<()>, wasmtime::Module)> {
use wasmtime::*;
let _ = env_logger::try_init();
@@ -46,7 +46,7 @@ pub(crate) fn ref_types_module(
config.wasm_reference_types(true);
let engine = Engine::new(&config)?;
let store = Store::new(&engine);
let store = Store::new(&engine, ());
let module = Module::new(&engine, source)?;

View File

@@ -3,22 +3,20 @@ mod not_for_windows {
use wasmtime::*;
use wasmtime_environ::{WASM_MAX_PAGES, WASM_PAGE_SIZE};
use libc::c_void;
use libc::MAP_FAILED;
use libc::{mmap, mprotect, munmap};
use libc::{sysconf, _SC_PAGESIZE};
use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
use std::cell::RefCell;
use std::io::Error;
use std::ptr::null_mut;
use std::sync::{Arc, Mutex};
struct CustomMemory {
mem: *mut c_void,
mem: usize,
size: usize,
guard_size: usize,
used_wasm_pages: RefCell<u32>,
used_wasm_pages: u32,
glob_page_counter: Arc<Mutex<u64>>,
}
@@ -42,10 +40,10 @@ mod not_for_windows {
*glob_counter.lock().unwrap() += num_wasm_pages as u64;
Self {
mem,
mem: mem as usize,
size,
guard_size,
used_wasm_pages: RefCell::new(num_wasm_pages),
used_wasm_pages: num_wasm_pages,
glob_page_counter: glob_counter,
}
}
@@ -53,26 +51,26 @@ mod not_for_windows {
impl Drop for CustomMemory {
fn drop(&mut self) {
let n = *self.used_wasm_pages.borrow() as u64;
let n = self.used_wasm_pages as u64;
*self.glob_page_counter.lock().unwrap() -= n;
let r = unsafe { munmap(self.mem, self.size) };
let r = unsafe { munmap(self.mem as *mut _, self.size) };
assert_eq!(r, 0, "munmap failed: {}", Error::last_os_error());
}
}
unsafe impl LinearMemory for CustomMemory {
fn size(&self) -> u32 {
*self.used_wasm_pages.borrow()
self.used_wasm_pages
}
fn maximum(&self) -> Option<u32> {
Some((self.size as u32 - self.guard_size as u32) / WASM_PAGE_SIZE)
}
fn grow(&self, delta: u32) -> Option<u32> {
fn grow(&mut self, delta: u32) -> Option<u32> {
let delta_size = (delta as usize).checked_mul(WASM_PAGE_SIZE as usize)?;
let prev_pages = *self.used_wasm_pages.borrow();
let prev_pages = self.used_wasm_pages;
let prev_size = (prev_pages as usize).checked_mul(WASM_PAGE_SIZE as usize)?;
let new_pages = prev_pages.checked_add(delta)?;
@@ -87,7 +85,7 @@ mod not_for_windows {
}
*self.glob_page_counter.lock().unwrap() += delta as u64;
*self.used_wasm_pages.borrow_mut() = new_pages;
self.used_wasm_pages = new_pages;
Some(prev_pages)
}
@@ -132,19 +130,19 @@ mod not_for_windows {
}
}
fn config() -> (Store, Arc<CustomMemoryCreator>) {
fn config() -> (Store<()>, Arc<CustomMemoryCreator>) {
let mem_creator = Arc::new(CustomMemoryCreator::new());
let mut config = Config::new();
config
.with_host_memory(mem_creator.clone())
.static_memory_maximum_size(0)
.dynamic_memory_guard_size(0);
(Store::new(&Engine::new(&config).unwrap()), mem_creator)
(Store::new(&Engine::new(&config).unwrap(), ()), mem_creator)
}
#[test]
fn host_memory() -> anyhow::Result<()> {
let (store, mem_creator) = config();
let (mut store, mem_creator) = config();
let module = Module::new(
store.engine(),
r#"
@@ -153,7 +151,7 @@ mod not_for_windows {
)
"#,
)?;
Instance::new(&store, &module, &[])?;
Instance::new(&mut store, &module, &[])?;
assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 1);
@@ -162,7 +160,7 @@ mod not_for_windows {
#[test]
fn host_memory_grow() -> anyhow::Result<()> {
let (store, mem_creator) = config();
let (mut store, mem_creator) = config();
let module = Module::new(
store.engine(),
r#"
@@ -174,18 +172,24 @@ mod not_for_windows {
"#,
)?;
let instance1 = Instance::new(&store, &module, &[])?;
let instance2 = Instance::new(&store, &module, &[])?;
Instance::new(&mut store, &module, &[])?;
let instance2 = Instance::new(&mut store, &module, &[])?;
assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 2);
assert_eq!(instance2.get_memory("memory").unwrap().size(), 2);
assert_eq!(
instance2
.get_memory(&mut store, "memory")
.unwrap()
.size(&store),
2
);
// we take the lock outside the assert, so it won't get poisoned on assert failure
let tot_pages = *mem_creator.num_total_pages.lock().unwrap();
assert_eq!(tot_pages, 4);
drop((instance1, instance2, store, module));
drop(store);
let tot_pages = *mem_creator.num_total_pages.lock().unwrap();
assert_eq!(tot_pages, 0);

View File

@@ -28,7 +28,7 @@ fn caches_across_engines() {
.unwrap();
unsafe {
let res = Module::deserialize(&Engine::new(&Config::new()).unwrap(), &bytes);
let res = Module::deserialize(&Engine::default(), &bytes);
assert!(res.is_ok());
// differ in shared cranelift flags
@@ -70,11 +70,11 @@ fn aot_compiles() -> Result<()> {
let module = unsafe { Module::deserialize(&engine, &bytes)? };
let store = Store::new(&engine);
let instance = Instance::new(&store, &module, &[])?;
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let f = instance.get_typed_func::<i32, i32>("f")?;
assert_eq!(f.call(101).unwrap(), 101);
let f = instance.get_typed_func::<i32, i32, _>(&mut store, "f")?;
assert_eq!(f.call(&mut store, 101)?, 101);
Ok(())
}

View File

@@ -217,8 +217,9 @@ fn limit_instances() -> Result<()> {
)
"#,
)?;
let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().instances(10).build());
let err = Instance::new(&store, &module, &[]).err().unwrap();
let mut store = Store::new(&engine, ());
store.limiter(StoreLimitsBuilder::new().instances(10).build());
let err = Instance::new(&mut store, &module, &[]).err().unwrap();
assert!(
err.to_string().contains("resource limit exceeded"),
"bad error: {}",
@@ -252,8 +253,9 @@ fn limit_memories() -> Result<()> {
)
"#,
)?;
let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().memories(10).build());
let err = Instance::new(&store, &module, &[]).err().unwrap();
let mut store = Store::new(&engine, ());
store.limiter(StoreLimitsBuilder::new().memories(10).build());
let err = Instance::new(&mut store, &module, &[]).err().unwrap();
assert!(
err.to_string().contains("resource limit exceeded"),
"bad error: {}",
@@ -286,8 +288,9 @@ fn limit_tables() -> Result<()> {
)
"#,
)?;
let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().tables(10).build());
let err = Instance::new(&store, &module, &[]).err().unwrap();
let mut store = Store::new(&engine, ());
store.limiter(StoreLimitsBuilder::new().tables(10).build());
let err = Instance::new(&mut store, &module, &[]).err().unwrap();
assert!(
err.to_string().contains("resource limit exceeded"),
"bad error: {}",

View File

@@ -6,9 +6,9 @@ fn serialize(engine: &Engine, wat: &'static str) -> Result<Vec<u8>> {
Ok(module.serialize()?)
}
unsafe fn deserialize_and_instantiate(store: &Store, buffer: &[u8]) -> Result<Instance> {
unsafe fn deserialize_and_instantiate(store: &mut Store<()>, buffer: &[u8]) -> Result<Instance> {
let module = Module::deserialize(store.engine(), buffer)?;
Ok(Instance::new(&store, &module, &[])?)
Ok(Instance::new(store, &module, &[])?)
}
#[test]
@@ -34,10 +34,10 @@ fn test_module_serialize_simple() -> Result<()> {
"(module (func (export \"run\") (result i32) i32.const 42))",
)?;
let store = Store::default();
let instance = unsafe { deserialize_and_instantiate(&store, &buffer)? };
let run = instance.get_typed_func::<(), i32>("run")?;
let result = run.call(())?;
let mut store = Store::default();
let instance = unsafe { deserialize_and_instantiate(&mut store, &buffer)? };
let run = instance.get_typed_func::<(), i32, _>(&mut store, "run")?;
let result = run.call(&mut store, ())?;
assert_eq!(42, result);
Ok(())
@@ -52,8 +52,8 @@ fn test_module_serialize_fail() -> Result<()> {
let mut config = Config::new();
config.cranelift_opt_level(OptLevel::None);
let store = Store::new(&Engine::new(&config)?);
match unsafe { deserialize_and_instantiate(&store, &buffer) } {
let mut store = Store::new(&Engine::new(&config)?, ());
match unsafe { deserialize_and_instantiate(&mut store, &buffer) } {
Ok(_) => bail!("expected failure at deserialization"),
Err(_) => (),
}

View File

@@ -21,8 +21,8 @@ fn successful_instantiation() -> Result<()> {
let module = Module::new(&engine, r#"(module (memory 1) (table 10 funcref))"#)?;
// Module should instantiate
let store = Store::new(&engine);
Instance::new(&store, &module, &[])?;
let mut store = Store::new(&engine, ());
Instance::new(&mut store, &module, &[])?;
Ok(())
}
@@ -61,30 +61,36 @@ fn memory_limit() -> Result<()> {
// Instantiate the module and grow the memory via the `f` function
{
let store = Store::new(&engine);
let instance = Instance::new(&store, &module, &[])?;
let f = instance.get_typed_func::<(), i32>("f")?;
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let f = instance.get_typed_func::<(), i32, _>(&mut store, "f")?;
assert_eq!(f.call(()).expect("function should not trap"), 0);
assert_eq!(f.call(()).expect("function should not trap"), 1);
assert_eq!(f.call(()).expect("function should not trap"), 2);
assert_eq!(f.call(()).expect("function should not trap"), -1);
assert_eq!(f.call(()).expect("function should not trap"), -1);
assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 0);
assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 1);
assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 2);
assert_eq!(
f.call(&mut store, ()).expect("function should not trap"),
-1
);
assert_eq!(
f.call(&mut store, ()).expect("function should not trap"),
-1
);
}
// Instantiate the module and grow the memory via the Wasmtime API
let store = Store::new(&engine);
let instance = Instance::new(&store, &module, &[])?;
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let memory = instance.get_memory("m").unwrap();
assert_eq!(memory.size(), 0);
assert_eq!(memory.grow(1).expect("memory should grow"), 0);
assert_eq!(memory.size(), 1);
assert_eq!(memory.grow(1).expect("memory should grow"), 1);
assert_eq!(memory.size(), 2);
assert_eq!(memory.grow(1).expect("memory should grow"), 2);
assert_eq!(memory.size(), 3);
assert!(memory.grow(1).is_err());
let memory = instance.get_memory(&mut store, "m").unwrap();
assert_eq!(memory.size(&store), 0);
assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 0);
assert_eq!(memory.size(&store), 1);
assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 1);
assert_eq!(memory.size(&store), 2);
assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 2);
assert_eq!(memory.size(&store), 3);
assert!(memory.grow(&mut store, 1).is_err());
Ok(())
}
@@ -112,17 +118,15 @@ fn memory_init() -> Result<()> {
r#"(module (memory (export "m") 2) (data (i32.const 65530) "this data spans multiple pages") (data (i32.const 10) "hello world"))"#,
)?;
let store = Store::new(&engine);
let instance = Instance::new(&store, &module, &[])?;
let memory = instance.get_memory("m").unwrap();
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let memory = instance.get_memory(&mut store, "m").unwrap();
unsafe {
assert_eq!(
&memory.data_unchecked()[65530..65560],
b"this data spans multiple pages"
);
assert_eq!(&memory.data_unchecked()[10..21], b"hello world");
}
assert_eq!(
&memory.data(&store)[65530..65560],
b"this data spans multiple pages"
);
assert_eq!(&memory.data(&store)[10..21], b"hello world");
Ok(())
}
@@ -152,30 +156,31 @@ fn memory_guard_page_trap() -> Result<()> {
// Instantiate the module and check for out of bounds trap
for _ in 0..10 {
let store = Store::new(&engine);
let instance = Instance::new(&store, &module, &[])?;
let m = instance.get_memory("m").unwrap();
let f = instance.get_typed_func::<i32, ()>("f")?;
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let m = instance.get_memory(&mut store, "m").unwrap();
let f = instance.get_typed_func::<i32, (), _>(&mut store, "f")?;
let trap = f.call(0).expect_err("function should trap");
let trap = f.call(&mut store, 0).expect_err("function should trap");
assert!(trap.to_string().contains("out of bounds"));
let trap = f.call(1).expect_err("function should trap");
let trap = f.call(&mut store, 1).expect_err("function should trap");
assert!(trap.to_string().contains("out of bounds"));
m.grow(1).expect("memory should grow");
f.call(0).expect("function should not trap");
m.grow(&mut store, 1).expect("memory should grow");
f.call(&mut store, 0).expect("function should not trap");
let trap = f.call(65536).expect_err("function should trap");
let trap = f.call(&mut store, 65536).expect_err("function should trap");
assert!(trap.to_string().contains("out of bounds"));
let trap = f.call(65537).expect_err("function should trap");
let trap = f.call(&mut store, 65537).expect_err("function should trap");
assert!(trap.to_string().contains("out of bounds"));
m.grow(1).expect("memory should grow");
f.call(65536).expect("function should not trap");
m.grow(&mut store, 1).expect("memory should grow");
f.call(&mut store, 65536).expect("function should not trap");
m.grow(1).expect_err("memory should be at the limit");
m.grow(&mut store, 1)
.expect_err("memory should be at the limit");
}
Ok(())
@@ -204,20 +209,20 @@ fn memory_zeroed() -> Result<()> {
// Instantiate the module repeatedly after writing data to the entire memory
for _ in 0..10 {
let store = Store::new(&engine);
let instance = Instance::new(&store, &module, &[])?;
let memory = instance.get_memory("m").unwrap();
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let memory = instance.get_memory(&mut store, "m").unwrap();
assert_eq!(memory.size(), 1);
assert_eq!(memory.data_size(), 65536);
assert_eq!(memory.size(&store,), 1);
assert_eq!(memory.data_size(&store), 65536);
let ptr = memory.data_ptr();
let ptr = memory.data_mut(&mut store).as_mut_ptr();
unsafe {
for i in 0..8192 {
assert_eq!(*ptr.cast::<u64>().offset(i), 0);
}
std::ptr::write_bytes(ptr, 0xFE, memory.data_size());
std::ptr::write_bytes(ptr, 0xFE, memory.data_size(&store));
}
}
@@ -259,36 +264,45 @@ fn table_limit() -> Result<()> {
// Instantiate the module and grow the table via the `f` function
{
let store = Store::new(&engine);
let instance = Instance::new(&store, &module, &[])?;
let f = instance.get_typed_func::<(), i32>("f")?;
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let f = instance.get_typed_func::<(), i32, _>(&mut store, "f")?;
for i in 0..TABLE_ELEMENTS {
assert_eq!(f.call(()).expect("function should not trap"), i as i32);
assert_eq!(
f.call(&mut store, ()).expect("function should not trap"),
i as i32
);
}
assert_eq!(f.call(()).expect("function should not trap"), -1);
assert_eq!(f.call(()).expect("function should not trap"), -1);
assert_eq!(
f.call(&mut store, ()).expect("function should not trap"),
-1
);
assert_eq!(
f.call(&mut store, ()).expect("function should not trap"),
-1
);
}
// Instantiate the module and grow the table via the Wasmtime API
let store = Store::new(&engine);
let instance = Instance::new(&store, &module, &[])?;
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let table = instance.get_table("t").unwrap();
let table = instance.get_table(&mut store, "t").unwrap();
for i in 0..TABLE_ELEMENTS {
assert_eq!(table.size(), i);
assert_eq!(table.size(&store), i);
assert_eq!(
table
.grow(1, Val::FuncRef(None))
.grow(&mut store, 1, Val::FuncRef(None))
.expect("table should grow"),
i
);
}
assert_eq!(table.size(), TABLE_ELEMENTS);
assert!(table.grow(1, Val::FuncRef(None)).is_err());
assert_eq!(table.size(&store), TABLE_ELEMENTS);
assert!(table.grow(&mut store, 1, Val::FuncRef(None)).is_err());
Ok(())
}
@@ -316,22 +330,22 @@ fn table_init() -> Result<()> {
r#"(module (table (export "t") 6 funcref) (elem (i32.const 1) 1 2 3 4) (elem (i32.const 0) 0) (func) (func (param i32)) (func (param i32 i32)) (func (param i32 i32 i32)) (func (param i32 i32 i32 i32)))"#,
)?;
let store = Store::new(&engine);
let instance = Instance::new(&store, &module, &[])?;
let table = instance.get_table("t").unwrap();
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let table = instance.get_table(&mut store, "t").unwrap();
for i in 0..5 {
let v = table.get(i).expect("table should have entry");
let v = table.get(&mut store, i).expect("table should have entry");
let f = v
.funcref()
.expect("expected funcref")
.expect("expected non-null value");
assert_eq!(f.ty().params().len(), i as usize);
assert_eq!(f.ty(&store).params().len(), i as usize);
}
assert!(
table
.get(5)
.get(&mut store, 5)
.expect("table should have entry")
.funcref()
.expect("expected funcref")
@@ -365,19 +379,21 @@ fn table_zeroed() -> Result<()> {
// Instantiate the module repeatedly after filling table elements
for _ in 0..10 {
let store = Store::new(&engine);
let instance = Instance::new(&store, &module, &[])?;
let table = instance.get_table("t").unwrap();
let f = Func::wrap(&store, || {});
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let table = instance.get_table(&mut store, "t").unwrap();
let f = Func::wrap(&mut store, || {});
assert_eq!(table.size(), 10);
assert_eq!(table.size(&store), 10);
for i in 0..10 {
match table.get(i).unwrap() {
match table.get(&mut store, i).unwrap() {
Val::FuncRef(r) => assert!(r.is_none()),
_ => panic!("expected a funcref"),
}
table.set(i, Val::FuncRef(Some(f.clone()))).unwrap();
table
.set(&mut store, i, Val::FuncRef(Some(f.clone())))
.unwrap();
}
}
@@ -406,13 +422,13 @@ fn instantiation_limit() -> Result<()> {
// Instantiate to the limit
{
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
for _ in 0..INSTANCE_LIMIT {
Instance::new(&store, &module, &[])?;
Instance::new(&mut store, &module, &[])?;
}
match Instance::new(&store, &module, &[]) {
match Instance::new(&mut store, &module, &[]) {
Ok(_) => panic!("instantiation should fail"),
Err(e) => assert_eq!(
e.to_string(),
@@ -426,10 +442,10 @@ fn instantiation_limit() -> Result<()> {
// With the above store dropped, ensure instantiations can be made
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
for _ in 0..INSTANCE_LIMIT {
Instance::new(&store, &module, &[])?;
Instance::new(&mut store, &module, &[])?;
}
Ok(())

View File

@@ -7,7 +7,7 @@ fn host_always_has_some_stack() -> anyhow::Result<()> {
// assume hosts always have at least 512k of stack
const HOST_STACK: usize = 512 * 1024;
let store = Store::default();
let mut store = Store::<()>::default();
// Create a module that's infinitely recursive, but calls the host on each
// level of wasm stack to always test how much host stack we have left.
@@ -22,13 +22,13 @@ fn host_always_has_some_stack() -> anyhow::Result<()> {
)
"#,
)?;
let func = Func::wrap(&store, test_host_stack);
let instance = Instance::new(&store, &module, &[func.into()])?;
let foo = instance.get_typed_func::<(), ()>("foo")?;
let func = Func::wrap(&mut store, test_host_stack);
let instance = Instance::new(&mut store, &module, &[func.into()])?;
let foo = instance.get_typed_func::<(), (), _>(&mut store, "foo")?;
// Make sure that our function traps and the trap says that the call stack
// has been exhausted.
let trap = foo.call(()).unwrap_err();
let trap = foo.call(&mut store, ()).unwrap_err();
assert!(
trap.to_string().contains("call stack exhausted"),
"{}",

22
tests/all/store.rs Normal file
View File

@@ -0,0 +1,22 @@
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use wasmtime::{Engine, Store};
#[test]
fn into_inner() {
static HITS: AtomicUsize = AtomicUsize::new(0);
struct A;
impl Drop for A {
fn drop(&mut self) {
HITS.fetch_add(1, SeqCst);
}
}
let engine = Engine::default();
assert_eq!(HITS.load(SeqCst), 0);
drop(Store::new(&engine, A));
assert_eq!(HITS.load(SeqCst), 1);
Store::new(&engine, A).into_data();
assert_eq!(HITS.load(SeqCst), 2);
}

View File

@@ -2,49 +2,49 @@ use wasmtime::*;
#[test]
fn get_none() {
let store = Store::default();
let mut store = Store::<()>::default();
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
let table = Table::new(&store, ty, Val::FuncRef(None)).unwrap();
match table.get(0) {
let table = Table::new(&mut store, ty, Val::FuncRef(None)).unwrap();
match table.get(&mut store, 0) {
Some(Val::FuncRef(None)) => {}
_ => panic!(),
}
assert!(table.get(1).is_none());
assert!(table.get(&mut store, 1).is_none());
}
#[test]
fn fill_wrong() {
let store = Store::default();
let mut store = Store::<()>::default();
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
let table = Table::new(&store, ty, Val::FuncRef(None)).unwrap();
let table = Table::new(&mut store, ty, Val::FuncRef(None)).unwrap();
assert_eq!(
table
.fill(0, Val::ExternRef(None), 1)
.fill(&mut store, 0, Val::ExternRef(None), 1)
.map_err(|e| e.to_string())
.unwrap_err(),
"mismatched element fill type"
"value does not match table element type"
);
let ty = TableType::new(ValType::ExternRef, Limits::new(1, None));
let table = Table::new(&store, ty, Val::ExternRef(None)).unwrap();
let table = Table::new(&mut store, ty, Val::ExternRef(None)).unwrap();
assert_eq!(
table
.fill(0, Val::FuncRef(None), 1)
.fill(&mut store, 0, Val::FuncRef(None), 1)
.map_err(|e| e.to_string())
.unwrap_err(),
"mismatched element fill type"
"value does not match table element type"
);
}
#[test]
fn copy_wrong() {
let store = Store::default();
let mut store = Store::<()>::default();
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
let table1 = Table::new(&store, ty, Val::FuncRef(None)).unwrap();
let table1 = Table::new(&mut store, ty, Val::FuncRef(None)).unwrap();
let ty = TableType::new(ValType::ExternRef, Limits::new(1, None));
let table2 = Table::new(&store, ty, Val::ExternRef(None)).unwrap();
let table2 = Table::new(&mut store, ty, Val::ExternRef(None)).unwrap();
assert_eq!(
Table::copy(&table1, 0, &table2, 0, 1)
Table::copy(&mut store, &table1, 0, &table2, 0, 1)
.map_err(|e| e.to_string())
.unwrap_err(),
"tables do not have the same element type"

View File

@@ -5,7 +5,7 @@ use wasmtime::*;
#[test]
fn test_trap_return() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let wat = r#"
(module
(func $hello (import "" "hello"))
@@ -15,12 +15,15 @@ fn test_trap_return() -> Result<()> {
let module = Module::new(store.engine(), wat)?;
let hello_type = FuncType::new(None, None);
let hello_func = Func::new(&store, hello_type, |_, _, _| Err(Trap::new("test 123")));
let hello_func = Func::new(&mut store, hello_type, |_, _, _| Err(Trap::new("test 123")));
let instance = Instance::new(&store, &module, &[hello_func.into()])?;
let run_func = instance.get_typed_func::<(), ()>("run")?;
let instance = Instance::new(&mut store, &module, &[hello_func.into()])?;
let run_func = instance.get_typed_func::<(), (), _>(&mut store, "run")?;
let e = run_func.call(()).err().expect("error calling function");
let e = run_func
.call(&mut store, ())
.err()
.expect("error calling function");
assert!(e.to_string().contains("test 123"));
Ok(())
@@ -29,7 +32,7 @@ fn test_trap_return() -> Result<()> {
#[test]
#[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64
fn test_trap_trace() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let wat = r#"
(module $hello_mod
(func (export "run") (call $hello))
@@ -38,10 +41,13 @@ fn test_trap_trace() -> Result<()> {
"#;
let module = Module::new(store.engine(), wat)?;
let instance = Instance::new(&store, &module, &[])?;
let run_func = instance.get_typed_func::<(), ()>("run")?;
let instance = Instance::new(&mut store, &module, &[])?;
let run_func = instance.get_typed_func::<(), (), _>(&mut store, "run")?;
let e = run_func.call(()).err().expect("error calling function");
let e = run_func
.call(&mut store, ())
.err()
.expect("error calling function");
let trace = e.trace();
assert_eq!(trace.len(), 2);
@@ -67,7 +73,7 @@ fn test_trap_trace() -> Result<()> {
#[test]
#[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64
fn test_trap_trace_cb() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let wat = r#"
(module $hello_mod
(import "" "throw" (func $throw))
@@ -77,13 +83,16 @@ fn test_trap_trace_cb() -> Result<()> {
"#;
let fn_type = FuncType::new(None, None);
let fn_func = Func::new(&store, fn_type, |_, _, _| Err(Trap::new("cb throw")));
let fn_func = Func::new(&mut store, fn_type, |_, _, _| Err(Trap::new("cb throw")));
let module = Module::new(store.engine(), wat)?;
let instance = Instance::new(&store, &module, &[fn_func.into()])?;
let run_func = instance.get_typed_func::<(), ()>("run")?;
let instance = Instance::new(&mut store, &module, &[fn_func.into()])?;
let run_func = instance.get_typed_func::<(), (), _>(&mut store, "run")?;
let e = run_func.call(()).err().expect("error calling function");
let e = run_func
.call(&mut store, ())
.err()
.expect("error calling function");
let trace = e.trace();
assert_eq!(trace.len(), 2);
@@ -99,7 +108,7 @@ fn test_trap_trace_cb() -> Result<()> {
#[test]
#[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64
fn test_trap_stack_overflow() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let wat = r#"
(module $rec_mod
(func $run (export "run") (call $run))
@@ -107,10 +116,13 @@ fn test_trap_stack_overflow() -> Result<()> {
"#;
let module = Module::new(store.engine(), wat)?;
let instance = Instance::new(&store, &module, &[])?;
let run_func = instance.get_typed_func::<(), ()>("run")?;
let instance = Instance::new(&mut store, &module, &[])?;
let run_func = instance.get_typed_func::<(), (), _>(&mut store, "run")?;
let e = run_func.call(()).err().expect("error calling function");
let e = run_func
.call(&mut store, ())
.err()
.expect("error calling function");
let trace = e.trace();
assert!(trace.len() >= 32);
@@ -127,7 +139,7 @@ fn test_trap_stack_overflow() -> Result<()> {
#[test]
#[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64
fn trap_display_pretty() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let wat = r#"
(module $m
(func $die unreachable)
@@ -138,10 +150,13 @@ fn trap_display_pretty() -> Result<()> {
"#;
let module = Module::new(store.engine(), wat)?;
let instance = Instance::new(&store, &module, &[])?;
let run_func = instance.get_typed_func::<(), ()>("bar")?;
let instance = Instance::new(&mut store, &module, &[])?;
let run_func = instance.get_typed_func::<(), (), _>(&mut store, "bar")?;
let e = run_func.call(()).err().expect("error calling function");
let e = run_func
.call(&mut store, ())
.err()
.expect("error calling function");
assert_eq!(
e.to_string(),
"\
@@ -159,7 +174,7 @@ wasm backtrace:
#[test]
#[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64
fn trap_display_multi_module() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let wat = r#"
(module $a
(func $die unreachable)
@@ -170,8 +185,8 @@ fn trap_display_multi_module() -> Result<()> {
"#;
let module = Module::new(store.engine(), wat)?;
let instance = Instance::new(&store, &module, &[])?;
let bar = instance.get_export("bar").unwrap();
let instance = Instance::new(&mut store, &module, &[])?;
let bar = instance.get_export(&mut store, "bar").unwrap();
let wat = r#"
(module $b
@@ -181,10 +196,13 @@ fn trap_display_multi_module() -> Result<()> {
)
"#;
let module = Module::new(store.engine(), wat)?;
let instance = Instance::new(&store, &module, &[bar])?;
let bar2 = instance.get_typed_func::<(), ()>("bar2")?;
let instance = Instance::new(&mut store, &module, &[bar])?;
let bar2 = instance.get_typed_func::<(), (), _>(&mut store, "bar2")?;
let e = bar2.call(()).err().expect("error calling function");
let e = bar2
.call(&mut store, ())
.err()
.expect("error calling function");
assert_eq!(
e.to_string(),
"\
@@ -203,7 +221,7 @@ wasm backtrace:
#[test]
fn trap_start_function_import() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let binary = wat::parse_str(
r#"
(module $a
@@ -215,8 +233,8 @@ fn trap_start_function_import() -> Result<()> {
let module = Module::new(store.engine(), &binary)?;
let sig = FuncType::new(None, None);
let func = Func::new(&store, sig, |_, _, _| Err(Trap::new("user trap")));
let err = Instance::new(&store, &module, &[func.into()])
let func = Func::new(&mut store, sig, |_, _, _| Err(Trap::new("user trap")));
let err = Instance::new(&mut store, &module, &[func.into()])
.err()
.unwrap();
assert!(err
@@ -229,7 +247,7 @@ fn trap_start_function_import() -> Result<()> {
#[test]
fn rust_panic_import() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let binary = wat::parse_str(
r#"
(module $a
@@ -243,22 +261,17 @@ fn rust_panic_import() -> Result<()> {
let module = Module::new(store.engine(), &binary)?;
let sig = FuncType::new(None, None);
let func = Func::new(&store, sig, |_, _, _| panic!("this is a panic"));
let instance = Instance::new(
&store,
&module,
&[
func.into(),
Func::wrap(&store, || panic!("this is another panic")).into(),
],
)?;
let func = instance.get_typed_func::<(), ()>("foo")?;
let err = panic::catch_unwind(AssertUnwindSafe(|| drop(func.call(())))).unwrap_err();
let func = Func::new(&mut store, sig, |_, _, _| panic!("this is a panic"));
let func2 = Func::wrap(&mut store, || panic!("this is another panic"));
let instance = Instance::new(&mut store, &module, &[func.into(), func2.into()])?;
let func = instance.get_typed_func::<(), (), _>(&mut store, "foo")?;
let err =
panic::catch_unwind(AssertUnwindSafe(|| drop(func.call(&mut store, ())))).unwrap_err();
assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic"));
let func = instance.get_typed_func::<(), ()>("bar")?;
let func = instance.get_typed_func::<(), (), _>(&mut store, "bar")?;
let err = panic::catch_unwind(AssertUnwindSafe(|| {
drop(func.call(()));
drop(func.call(&mut store, ()));
}))
.unwrap_err();
assert_eq!(
@@ -270,7 +283,7 @@ fn rust_panic_import() -> Result<()> {
#[test]
fn rust_panic_start_function() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let binary = wat::parse_str(
r#"
(module $a
@@ -282,16 +295,16 @@ fn rust_panic_start_function() -> Result<()> {
let module = Module::new(store.engine(), &binary)?;
let sig = FuncType::new(None, None);
let func = Func::new(&store, sig, |_, _, _| panic!("this is a panic"));
let func = Func::new(&mut store, sig, |_, _, _| panic!("this is a panic"));
let err = panic::catch_unwind(AssertUnwindSafe(|| {
drop(Instance::new(&store, &module, &[func.into()]));
drop(Instance::new(&mut store, &module, &[func.into()]));
}))
.unwrap_err();
assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic"));
let func = Func::wrap(&store, || panic!("this is another panic"));
let func = Func::wrap(&mut store, || panic!("this is another panic"));
let err = panic::catch_unwind(AssertUnwindSafe(|| {
drop(Instance::new(&store, &module, &[func.into()]));
drop(Instance::new(&mut store, &module, &[func.into()]));
}))
.unwrap_err();
assert_eq!(
@@ -303,7 +316,7 @@ fn rust_panic_start_function() -> Result<()> {
#[test]
fn mismatched_arguments() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let binary = wat::parse_str(
r#"
(module $a
@@ -313,18 +326,20 @@ fn mismatched_arguments() -> Result<()> {
)?;
let module = Module::new(store.engine(), &binary)?;
let instance = Instance::new(&store, &module, &[])?;
let func = instance.get_func("foo").unwrap();
let instance = Instance::new(&mut store, &module, &[])?;
let func = instance.get_func(&mut store, "foo").unwrap();
assert_eq!(
func.call(&[]).unwrap_err().to_string(),
func.call(&mut store, &[]).unwrap_err().to_string(),
"expected 1 arguments, got 0"
);
assert_eq!(
func.call(&[Val::F32(0)]).unwrap_err().to_string(),
func.call(&mut store, &[Val::F32(0)])
.unwrap_err()
.to_string(),
"argument type mismatch: found f32 but expected i32",
);
assert_eq!(
func.call(&[Val::I32(0), Val::I32(1)])
func.call(&mut store, &[Val::I32(0), Val::I32(1)])
.unwrap_err()
.to_string(),
"expected 1 arguments, got 2"
@@ -334,7 +349,7 @@ fn mismatched_arguments() -> Result<()> {
#[test]
fn call_signature_mismatch() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let binary = wat::parse_str(
r#"
(module $a
@@ -351,7 +366,7 @@ fn call_signature_mismatch() -> Result<()> {
)?;
let module = Module::new(store.engine(), &binary)?;
let err = Instance::new(&store, &module, &[])
let err = Instance::new(&mut store, &module, &[])
.err()
.unwrap()
.downcast::<Trap>()
@@ -365,7 +380,7 @@ fn call_signature_mismatch() -> Result<()> {
#[test]
#[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64
fn start_trap_pretty() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let wat = r#"
(module $m
(func $die unreachable)
@@ -377,7 +392,7 @@ fn start_trap_pretty() -> Result<()> {
"#;
let module = Module::new(store.engine(), wat)?;
let e = match Instance::new(&store, &module, &[]) {
let e = match Instance::new(&mut store, &module, &[]) {
Ok(_) => panic!("expected failure"),
Err(e) => e.downcast::<Trap>()?,
};
@@ -399,17 +414,17 @@ wasm backtrace:
#[test]
#[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64
fn present_after_module_drop() -> Result<()> {
let store = Store::default();
let mut store = Store::<()>::default();
let module = Module::new(store.engine(), r#"(func (export "foo") unreachable)"#)?;
let instance = Instance::new(&store, &module, &[])?;
let func = instance.get_typed_func::<(), ()>("foo")?;
let instance = Instance::new(&mut store, &module, &[])?;
let func = instance.get_typed_func::<(), (), _>(&mut store, "foo")?;
println!("asserting before we drop modules");
assert_trap(func.call(()).unwrap_err());
assert_trap(func.call(&mut store, ()).unwrap_err());
drop((instance, module));
println!("asserting after drop");
assert_trap(func.call(()).unwrap_err());
assert_trap(func.call(&mut store, ()).unwrap_err());
return Ok(());
fn assert_trap(t: Trap) {
@@ -420,10 +435,10 @@ fn present_after_module_drop() -> Result<()> {
}
fn assert_trap_code(wat: &str, code: wasmtime::TrapCode) {
let store = Store::default();
let mut store = Store::<()>::default();
let module = Module::new(store.engine(), wat).unwrap();
let err = match Instance::new(&store, &module, &[]) {
let err = match Instance::new(&mut store, &module, &[]) {
Ok(_) => unreachable!(),
Err(e) => e,
};
@@ -493,19 +508,18 @@ fn parse_dwarf_info() -> Result<()> {
let mut config = Config::new();
config.wasm_backtrace_details(WasmBacktraceDetails::Enable);
let engine = Engine::new(&config)?;
let store = Store::new(&engine);
let module = Module::new(&engine, &wasm)?;
let mut linker = Linker::new(&store);
wasmtime_wasi::Wasi::new(
&store,
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |s| s)?;
let mut store = Store::new(
&engine,
wasmtime_wasi::sync::WasiCtxBuilder::new()
.inherit_stdio()
.build(),
)
.add_to_linker(&mut linker)?;
linker.module("", &module)?;
let run = linker.get_default("")?;
let trap = run.call(&[]).unwrap_err().downcast::<Trap>()?;
);
linker.module(&mut store, "", &module)?;
let run = linker.get_default(&mut store, "")?;
let trap = run.call(&mut store, &[]).unwrap_err().downcast::<Trap>()?;
let mut found = false;
for frame in trap.trace() {
@@ -529,7 +543,7 @@ fn no_hint_even_with_dwarf_info() -> Result<()> {
let mut config = Config::new();
config.wasm_backtrace_details(WasmBacktraceDetails::Disable);
let engine = Engine::new(&config)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let module = Module::new(
&engine,
r#"
@@ -541,7 +555,7 @@ fn no_hint_even_with_dwarf_info() -> Result<()> {
)
"#,
)?;
let trap = Instance::new(&store, &module, &[])
let trap = Instance::new(&mut store, &module, &[])
.err()
.unwrap()
.downcast::<Trap>()?;
@@ -564,7 +578,7 @@ fn hint_with_dwarf_info() -> Result<()> {
if std::env::var("WASMTIME_BACKTRACE_DETAILS").is_ok() {
return Ok(());
}
let store = Store::default();
let mut store = Store::<()>::default();
let module = Module::new(
store.engine(),
r#"
@@ -576,7 +590,7 @@ fn hint_with_dwarf_info() -> Result<()> {
)
"#,
)?;
let trap = Instance::new(&store, &module, &[])
let trap = Instance::new(&mut store, &module, &[])
.err()
.unwrap()
.downcast::<Trap>()?;
@@ -596,30 +610,23 @@ note: run with `WASMTIME_BACKTRACE_DETAILS=1` environment variable to display mo
fn multithreaded_traps() -> Result<()> {
// Compile and run unreachable on a thread, then moves over the whole store to another thread,
// and make sure traps are still correctly caught after notifying the store of the move.
let instance = {
let store = Store::default();
let module = Module::new(
store.engine(),
r#"(module (func (export "run") unreachable))"#,
)?;
Instance::new(&store, &module, &[])?
};
let mut store = Store::<()>::default();
let module = Module::new(
store.engine(),
r#"(module (func (export "run") unreachable))"#,
)?;
let instance = Instance::new(&mut store, &module, &[])?;
assert!(instance.get_typed_func::<(), ()>("run")?.call(()).is_err());
struct SendInstance {
inner: Instance,
}
unsafe impl Send for SendInstance {}
let instance = SendInstance { inner: instance };
assert!(instance
.get_typed_func::<(), (), _>(&mut store, "run")?
.call(&mut store, ())
.is_err());
let handle = std::thread::spawn(move || {
let instance = instance.inner;
assert!(instance
.get_typed_func::<(), ()>("run")
.get_typed_func::<(), (), _>(&mut store, "run")
.unwrap()
.call(())
.call(&mut store, ())
.is_err());
});

View File

@@ -1,21 +0,0 @@
use anyhow::Result;
use wasmtime::*;
#[test]
fn use_func_after_drop() -> Result<()> {
let table;
{
let store = Store::default();
let closed_over_data = String::from("abcd");
let func = Func::wrap(&store, move || {
assert_eq!(closed_over_data, "abcd");
});
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
table = Table::new(&store, ty, Val::FuncRef(None))?;
table.set(0, func.into())?;
}
let func = table.get(0).unwrap().funcref().unwrap().unwrap().clone();
let func = func.typed::<(), ()>()?;
func.call(())?;
Ok(())
}

View File

@@ -71,7 +71,7 @@ fn run_wast(wast: &str, strategy: Strategy, pooling: bool) -> anyhow::Result<()>
});
}
let store = Store::new(&Engine::new(&cfg)?);
let store = Store::new(&Engine::new(&cfg)?, ());
let mut wast_context = WastContext::new(store);
wast_context.register_spectest()?;
wast_context.run_file(wast)?;

View File

@@ -97,9 +97,9 @@ fn main() {
"make instance then segfault",
|| {
let engine = Engine::default();
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let module = Module::new(&engine, "(module)").unwrap();
let _instance = Instance::new(&store, &module, &[]).unwrap();
let _instance = Instance::new(&mut store, &module, &[]).unwrap();
segfault();
},
false,
@@ -108,9 +108,9 @@ fn main() {
"make instance then overrun the stack",
|| {
let engine = Engine::default();
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let module = Module::new(&engine, "(module)").unwrap();
let _instance = Instance::new(&store, &module, &[]).unwrap();
let _instance = Instance::new(&mut store, &module, &[]).unwrap();
overrun_the_stack();
},
true,
@@ -119,10 +119,10 @@ fn main() {
"segfault in a host function",
|| {
let engine = Engine::default();
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let module = Module::new(&engine, r#"(import "" "" (func)) (start 0)"#).unwrap();
let segfault = Func::wrap(&store, || segfault());
Instance::new(&store, &module, &[segfault.into()]).unwrap();
let segfault = Func::wrap(&mut store, || segfault());
Instance::new(&mut store, &module, &[segfault.into()]).unwrap();
unreachable!();
},
false,
@@ -133,13 +133,13 @@ fn main() {
let mut config = Config::default();
config.async_support(true);
let engine = Engine::new(&config).unwrap();
let store = Store::new(&engine);
let f = Func::wrap0_async(&store, (), |_, _| {
let mut store = Store::new(&engine, ());
let f = Func::wrap0_async(&mut store, |_| {
Box::new(async {
overrun_the_stack();
})
});
run_future(f.call_async(&[])).unwrap();
run_future(f.call_async(&mut store, &[])).unwrap();
unreachable!();
},
true,
@@ -151,13 +151,13 @@ fn main() {
config.async_support(true);
config.allocation_strategy(InstanceAllocationStrategy::pooling());
let engine = Engine::new(&config).unwrap();
let store = Store::new(&engine);
let f = Func::wrap0_async(&store, (), |_, _| {
let mut store = Store::new(&engine, ());
let f = Func::wrap0_async(&mut store, |_| {
Box::new(async {
overrun_the_stack();
})
});
run_future(f.call_async(&[])).unwrap();
run_future(f.call_async(&mut store, &[])).unwrap();
unreachable!();
},
true,