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:
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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: {}",
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
568
tests/all/gc.rs
568
tests/all/gc.rs
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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: {}",
|
||||
|
||||
@@ -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(_) => (),
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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
22
tests/all/store.rs
Normal 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);
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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());
|
||||
});
|
||||
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user