770 lines
20 KiB
Rust
770 lines
20 KiB
Rust
use anyhow::Result;
|
|
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
|
use wasmtime::*;
|
|
use wasmtime_wasi::{sync::WasiCtxBuilder, Wasi};
|
|
|
|
#[test]
|
|
fn async_required() {
|
|
let mut config = Config::default();
|
|
config.define_host_func_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();
|
|
|
|
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 });
|
|
|
|
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 {} });
|
|
}
|
|
|
|
#[test]
|
|
fn drop_func() -> Result<()> {
|
|
static HITS: AtomicUsize = AtomicUsize::new(0);
|
|
struct A;
|
|
|
|
impl Drop for A {
|
|
fn drop(&mut self) {
|
|
HITS.fetch_add(1, SeqCst);
|
|
}
|
|
}
|
|
|
|
let mut config = Config::default();
|
|
|
|
let a = A;
|
|
config.wrap_host_func("", "", 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 || {
|
|
drop(&a);
|
|
});
|
|
|
|
assert_eq!(HITS.load(SeqCst), 1);
|
|
|
|
drop(config);
|
|
|
|
assert_eq!(HITS.load(SeqCst), 2);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn drop_delayed() -> Result<()> {
|
|
static HITS: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
struct A;
|
|
|
|
impl Drop for A {
|
|
fn drop(&mut self) {
|
|
HITS.fetch_add(1, SeqCst);
|
|
}
|
|
}
|
|
|
|
let mut config = Config::default();
|
|
|
|
let a = A;
|
|
config.wrap_host_func("", "", 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()],
|
|
)?;
|
|
|
|
drop((instance, 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()],
|
|
)?;
|
|
|
|
drop((instance, store, engine, module));
|
|
|
|
assert_eq!(HITS.load(SeqCst), 0);
|
|
|
|
drop(config);
|
|
|
|
assert_eq!(HITS.load(SeqCst), 1);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn signatures_match() -> Result<()> {
|
|
let mut config = Config::default();
|
|
|
|
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(
|
|
"",
|
|
"f6",
|
|
|_: f32, _: f64, _: i32, _: i64, _: i32, _: Option<ExternRef>, _: Option<Func>| -> f64 {
|
|
loop {}
|
|
},
|
|
);
|
|
|
|
let engine = Engine::new(&config)?;
|
|
let store = Store::new(&engine);
|
|
|
|
let f = store
|
|
.get_host_func("", "f1")
|
|
.expect("func should be defined");
|
|
|
|
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 = store
|
|
.get_host_func("", "f2")
|
|
.expect("func should be defined");
|
|
|
|
assert_eq!(f.ty().params().collect::<Vec<_>>(), &[]);
|
|
assert_eq!(f.ty().results().collect::<Vec<_>>(), &[ValType::I32]);
|
|
|
|
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");
|
|
|
|
assert_eq!(
|
|
f.ty().params().collect::<Vec<_>>(),
|
|
&[
|
|
ValType::F32,
|
|
ValType::F64,
|
|
ValType::I32,
|
|
ValType::I64,
|
|
ValType::I32,
|
|
ValType::ExternRef,
|
|
ValType::FuncRef,
|
|
]
|
|
);
|
|
assert_eq!(f.ty().results().collect::<Vec<_>>(), &[ValType::F64]);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn import_works() -> Result<()> {
|
|
static HITS: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
let wasm = wat::parse_str(
|
|
r#"
|
|
(import "" "f1" (func))
|
|
(import "" "f2" (func (param i32) (result i32)))
|
|
(import "" "f3" (func (param i32) (param i64)))
|
|
(import "" "f4" (func (param i32 i64 i32 f32 f64 externref funcref)))
|
|
|
|
(func (export "run") (param externref funcref)
|
|
call 0
|
|
i32.const 0
|
|
call 1
|
|
i32.const 1
|
|
i32.add
|
|
i64.const 3
|
|
call 2
|
|
|
|
i32.const 100
|
|
i64.const 200
|
|
i32.const 300
|
|
f32.const 400
|
|
f64.const 500
|
|
local.get 0
|
|
local.get 1
|
|
call 3
|
|
)
|
|
"#,
|
|
)?;
|
|
|
|
let mut config = Config::new();
|
|
config.wasm_reference_types(true);
|
|
|
|
config.wrap_host_func("", "f1", || {
|
|
assert_eq!(HITS.fetch_add(1, SeqCst), 0);
|
|
});
|
|
|
|
config.wrap_host_func("", "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| {
|
|
assert_eq!(x, 2);
|
|
assert_eq!(y, 3);
|
|
assert_eq!(HITS.fetch_add(1, SeqCst), 2);
|
|
});
|
|
|
|
config.wrap_host_func(
|
|
"",
|
|
"f4",
|
|
|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);
|
|
},
|
|
);
|
|
|
|
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 }))),
|
|
])?;
|
|
|
|
assert_eq!(HITS.load(SeqCst), 4);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn call_import_many_args() -> Result<()> {
|
|
let wasm = wat::parse_str(
|
|
r#"
|
|
(import "" "host" (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32)))
|
|
(func (export "run")
|
|
i32.const 1
|
|
i32.const 2
|
|
i32.const 3
|
|
i32.const 4
|
|
i32.const 5
|
|
i32.const 6
|
|
i32.const 7
|
|
i32.const 8
|
|
i32.const 9
|
|
i32.const 10
|
|
call 0
|
|
)
|
|
"#,
|
|
)?;
|
|
|
|
let mut config = Config::new();
|
|
|
|
config.wrap_host_func(
|
|
"",
|
|
"host",
|
|
|x1: i32,
|
|
x2: i32,
|
|
x3: i32,
|
|
x4: i32,
|
|
x5: i32,
|
|
x6: i32,
|
|
x7: i32,
|
|
x8: i32,
|
|
x9: i32,
|
|
x10: i32| {
|
|
assert_eq!(x1, 1);
|
|
assert_eq!(x2, 2);
|
|
assert_eq!(x3, 3);
|
|
assert_eq!(x4, 4);
|
|
assert_eq!(x5, 5);
|
|
assert_eq!(x6, 6);
|
|
assert_eq!(x7, 7);
|
|
assert_eq!(x8, 8);
|
|
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(&[])?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn call_wasm_many_args() -> Result<()> {
|
|
let wasm = wat::parse_str(
|
|
r#"
|
|
(func (export "run") (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32)
|
|
i32.const 1
|
|
get_local 0
|
|
i32.ne
|
|
if
|
|
unreachable
|
|
end
|
|
|
|
i32.const 10
|
|
get_local 9
|
|
i32.ne
|
|
if
|
|
unreachable
|
|
end
|
|
)
|
|
|
|
(func (export "test")
|
|
i32.const 1
|
|
i32.const 2
|
|
i32.const 3
|
|
i32.const 4
|
|
i32.const 5
|
|
i32.const 6
|
|
i32.const 7
|
|
i32.const 8
|
|
i32.const 9
|
|
i32.const 10
|
|
call 0
|
|
)
|
|
"#,
|
|
)?;
|
|
|
|
let config = Config::new();
|
|
let engine = Engine::new(&config)?;
|
|
let module = Module::new(&engine, &wasm)?;
|
|
|
|
let store = Store::new(&engine);
|
|
let instance = Instance::new(&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 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 test = instance.get_func("test").unwrap();
|
|
test.call(&[])?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn trap_smoke() -> Result<()> {
|
|
let mut config = Config::default();
|
|
config.wrap_host_func("", "", || -> Result<(), Trap> { Err(Trap::new("test")) });
|
|
|
|
let engine = Engine::new(&config)?;
|
|
let store = Store::new(&engine);
|
|
|
|
let f = store.get_host_func("", "").expect("should be defined");
|
|
|
|
let err = f.call(&[]).unwrap_err().downcast::<Trap>()?;
|
|
|
|
assert!(err.to_string().contains("test"));
|
|
assert!(err.i32_exit_status().is_none());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn trap_import() -> Result<()> {
|
|
let wasm = wat::parse_str(
|
|
r#"
|
|
(import "" "" (func))
|
|
(start 0)
|
|
"#,
|
|
)?;
|
|
|
|
let mut config = Config::default();
|
|
config.wrap_host_func("", "", || -> Result<(), Trap> { Err(Trap::new("foo")) });
|
|
|
|
let engine = Engine::new(&config)?;
|
|
let module = Module::new(&engine, &wasm)?;
|
|
let store = Store::new(&engine);
|
|
|
|
let trap = Instance::new(
|
|
&store,
|
|
&module,
|
|
&[store.get_host_func("", "").expect("defined").into()],
|
|
)
|
|
.err()
|
|
.unwrap()
|
|
.downcast::<Trap>()?;
|
|
|
|
assert!(trap.to_string().contains("foo"));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn new_from_signature() -> Result<()> {
|
|
let mut config = Config::default();
|
|
|
|
let ty = FuncType::new(None, None);
|
|
config.define_host_func("", "f1", ty, |_, _, _| panic!());
|
|
|
|
let ty = FuncType::new(Some(ValType::I32), Some(ValType::F64));
|
|
config.define_host_func("", "f2", ty, |_, _, _| panic!());
|
|
|
|
let engine = Engine::new(&config)?;
|
|
let 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 = 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());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn call_wrapped_func() -> Result<()> {
|
|
let mut config = Config::default();
|
|
|
|
config.wrap_host_func("", "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);
|
|
|
|
config.wrap_host_func("", "f3", || 2i64);
|
|
|
|
config.wrap_host_func("", "f4", || 3.0f32);
|
|
|
|
config.wrap_host_func("", "f5", || 4.0f64);
|
|
|
|
let engine = Engine::new(&config)?;
|
|
let 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 = store.get_host_func("", "f2").expect("func defined");
|
|
let results = f.call(&[])?;
|
|
assert_eq!(results.len(), 1);
|
|
assert_eq!(results[0].unwrap_i32(), 1);
|
|
assert_eq!(f.typed::<(), i32>()?.call(())?, 1);
|
|
|
|
let f = store.get_host_func("", "f3").expect("func defined");
|
|
let results = f.call(&[])?;
|
|
assert_eq!(results.len(), 1);
|
|
assert_eq!(results[0].unwrap_i64(), 2);
|
|
assert_eq!(f.typed::<(), i64>()?.call(())?, 2);
|
|
|
|
let f = store.get_host_func("", "f4").expect("func defined");
|
|
let results = f.call(&[])?;
|
|
assert_eq!(results.len(), 1);
|
|
assert_eq!(results[0].unwrap_f32(), 3.0);
|
|
assert_eq!(f.typed::<(), f32>()?.call(())?, 3.0);
|
|
|
|
let f = store.get_host_func("", "f5").expect("func defined");
|
|
let results = f.call(&[])?;
|
|
assert_eq!(results.len(), 1);
|
|
assert_eq!(results[0].unwrap_f64(), 4.0);
|
|
assert_eq!(f.typed::<(), f64>()?.call(())?, 4.0);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn func_return_nothing() -> Result<()> {
|
|
let mut config = Config::default();
|
|
let ty = FuncType::new(None, Some(ValType::I32));
|
|
|
|
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>()?;
|
|
assert!(err
|
|
.to_string()
|
|
.contains("function attempted to return an incompatible value"));
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn call_via_funcref() -> Result<()> {
|
|
static HITS: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
struct A;
|
|
|
|
impl Drop for A {
|
|
fn drop(&mut self) {
|
|
HITS.fetch_add(1, SeqCst);
|
|
}
|
|
}
|
|
|
|
let wasm = wat::parse_str(
|
|
r#"
|
|
(table $t 1 funcref)
|
|
(type $add (func (param i32 i32) (result i32)))
|
|
(func (export "call") (param funcref) (result i32 funcref)
|
|
(table.set $t (i32.const 0) (local.get 0))
|
|
(call_indirect (type $add) (i32.const 3) (i32.const 4) (i32.const 0))
|
|
(local.get 0)
|
|
)
|
|
"#,
|
|
)?;
|
|
|
|
let mut config = Config::default();
|
|
let a = A;
|
|
config.wrap_host_func("", "", 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 results = instance
|
|
.get_func("call")
|
|
.unwrap()
|
|
.call(&[store.get_host_func("", "").expect("func defined").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()])?;
|
|
assert_eq!(results.len(), 1);
|
|
assert_eq!(results[0].unwrap_i32(), 3);
|
|
}
|
|
|
|
assert_eq!(HITS.load(SeqCst), 0);
|
|
|
|
drop((results, instance, store, module, engine));
|
|
|
|
assert_eq!(HITS.load(SeqCst), 0);
|
|
|
|
drop(config);
|
|
|
|
assert_eq!(HITS.load(SeqCst), 1);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn store_with_context() -> Result<()> {
|
|
struct Ctx {
|
|
called: std::cell::Cell<bool>,
|
|
}
|
|
|
|
let mut config = Config::default();
|
|
|
|
config.wrap_host_func("", "", |caller: Caller| {
|
|
let ctx = caller
|
|
.store()
|
|
.get::<Ctx>()
|
|
.expect("store should have context");
|
|
ctx.called.set(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 f = store.get_host_func("", "").expect("func defined");
|
|
f.call(&[])?;
|
|
|
|
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());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn wasi_imports() -> 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);
|
|
assert!(Wasi::set_context(&store, WasiCtxBuilder::new().build()?).is_ok());
|
|
let linker = Linker::new(&store);
|
|
let instance = linker.instantiate(&module)?;
|
|
|
|
let start = instance.get_typed_func::<(), ()>("_start")?;
|
|
let trap = start.call(()).unwrap_err();
|
|
assert_eq!(trap.i32_exit_status(), Some(123));
|
|
|
|
Ok(())
|
|
}
|