You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

404 lines
11 KiB

use anyhow::Result;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
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, || -> 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 {} });
}
#[test]
fn dtor_runs() {
static HITS: AtomicUsize = AtomicUsize::new(0);
struct A;
impl Drop for A {
fn drop(&mut self) {
HITS.fetch_add(1, SeqCst);
}
}
let store = Store::default();
let a = A;
assert_eq!(HITS.load(SeqCst), 0);
Func::wrap(&store, move || {
drop(&a);
});
drop(store);
assert_eq!(HITS.load(SeqCst), 1);
}
#[test]
fn dtor_delayed() -> Result<()> {
static HITS: AtomicUsize = AtomicUsize::new(0);
struct A;
impl Drop for A {
fn drop(&mut self) {
HITS.fetch_add(1, SeqCst);
}
}
let store = Store::default();
let a = A;
let func = Func::wrap(&store, move || drop(&a));
assert_eq!(HITS.load(SeqCst), 0);
let wasm = wat::parse_str(r#"(import "" "" (func))"#)?;
let module = Module::new(&store, &wasm)?;
let instance = Instance::new(&module, &[func.into()])?;
assert_eq!(HITS.load(SeqCst), 0);
drop((instance, module, store));
assert_eq!(HITS.load(SeqCst), 1);
Ok(())
}
#[test]
fn signatures_match() {
let store = Store::default();
let f = Func::wrap(&store, || {});
assert_eq!(f.ty().params(), &[]);
assert_eq!(f.param_arity(), 0);
assert_eq!(f.ty().results(), &[]);
assert_eq!(f.result_arity(), 0);
let f = Func::wrap(&store, || -> i32 { loop {} });
assert_eq!(f.ty().params(), &[]);
assert_eq!(f.ty().results(), &[ValType::I32]);
let f = Func::wrap(&store, || -> i64 { loop {} });
assert_eq!(f.ty().params(), &[]);
assert_eq!(f.ty().results(), &[ValType::I64]);
let f = Func::wrap(&store, || -> f32 { loop {} });
assert_eq!(f.ty().params(), &[]);
assert_eq!(f.ty().results(), &[ValType::F32]);
let f = Func::wrap(&store, || -> f64 { loop {} });
assert_eq!(f.ty().params(), &[]);
assert_eq!(f.ty().results(), &[ValType::F64]);
let f = Func::wrap(&store, |_: f32, _: f64, _: i32, _: i64, _: i32| -> f64 {
loop {}
});
assert_eq!(
f.ty().params(),
&[
ValType::F32,
ValType::F64,
ValType::I32,
ValType::I64,
ValType::I32
]
);
assert_eq!(f.ty().results(), &[ValType::F64]);
}
#[test]
fn import_works() -> Result<()> {
static HITS: AtomicUsize = AtomicUsize::new(0);
let wasm = wat::parse_str(
r#"
(import "" "" (func))
(import "" "" (func (param i32) (result i32)))
(import "" "" (func (param i32) (param i64)))
(import "" "" (func (param i32 i64 i32 f32 f64)))
(func $foo
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
call 3
)
(start $foo)
"#,
)?;
let store = Store::default();
let module = Module::new(&store, &wasm)?;
Instance::new(
&module,
&[
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| {
assert_eq!(a, 100);
assert_eq!(b, 200);
assert_eq!(c, 300);
assert_eq!(d, 400.0);
assert_eq!(e, 500.0);
assert_eq!(HITS.fetch_add(1, SeqCst), 3);
})
.into(),
],
)?;
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>()?;
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 store = Store::default();
let module = Module::new(&store, &wasm)?;
let trap = Instance::new(
&module,
&[Func::wrap(&store, || -> Result<(), Trap> { Err(Trap::new("foo")) }).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.get0::<()>().is_ok());
assert!(f.get0::<i32>().is_err());
assert!(f.get1::<(), ()>().is_ok());
assert!(f.get1::<i32, ()>().is_err());
assert!(f.get1::<i32, i32>().is_err());
assert!(f.get2::<(), (), ()>().is_ok());
assert!(f.get2::<i32, i32, ()>().is_err());
assert!(f.get2::<i32, i32, i32>().is_err());
let f = Func::wrap(&store, || -> i32 { loop {} });
assert!(f.get0::<i32>().is_ok());
let f = Func::wrap(&store, || -> f32 { loop {} });
assert!(f.get0::<f32>().is_ok());
let f = Func::wrap(&store, || -> f64 { loop {} });
assert!(f.get0::<f64>().is_ok());
let f = Func::wrap(&store, |_: i32| {});
assert!(f.get1::<i32, ()>().is_ok());
assert!(f.get1::<i64, ()>().is_err());
assert!(f.get1::<f32, ()>().is_err());
assert!(f.get1::<f64, ()>().is_err());
let f = Func::wrap(&store, |_: i64| {});
assert!(f.get1::<i64, ()>().is_ok());
let f = Func::wrap(&store, |_: f32| {});
assert!(f.get1::<f32, ()>().is_ok());
let f = Func::wrap(&store, |_: f64| {});
assert!(f.get1::<f64, ()>().is_ok());
}
#[test]
fn get_from_signature() {
let store = Store::default();
let ty = FuncType::new(Box::new([]), Box::new([]));
let f = Func::new(&store, ty, |_, _, _| panic!());
assert!(f.get0::<()>().is_ok());
assert!(f.get0::<i32>().is_err());
assert!(f.get1::<i32, ()>().is_err());
let ty = FuncType::new(Box::new([ValType::I32]), Box::new([ValType::F64]));
let f = Func::new(&store, ty, |_, _, _| panic!());
assert!(f.get0::<()>().is_err());
assert!(f.get0::<i32>().is_err());
assert!(f.get1::<i32, ()>().is_err());
assert!(f.get1::<i32, f64>().is_ok());
}
#[test]
fn get_from_module() -> anyhow::Result<()> {
let store = Store::default();
let module = Module::new(
&store,
r#"
(module
(func (export "f0"))
(func (export "f1") (param i32))
(func (export "f2") (result i32)
i32.const 0)
)
"#,
)?;
let instance = Instance::new(&module, &[])?;
let f0 = instance.get_func("f0").unwrap();
assert!(f0.get0::<()>().is_ok());
assert!(f0.get0::<i32>().is_err());
let f1 = instance.get_func("f1").unwrap();
assert!(f1.get0::<()>().is_err());
assert!(f1.get1::<i32, ()>().is_ok());
assert!(f1.get1::<i32, f32>().is_err());
let f2 = instance.get_func("f2").unwrap();
assert!(f2.get0::<()>().is_err());
assert!(f2.get0::<i32>().is_ok());
assert!(f2.get1::<i32, ()>().is_err());
assert!(f2.get1::<i32, f32>().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| {
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.get4::<i32, i64, f32, f64, ()>()?(1, 2, 3.0, 4.0)?;
let f = Func::wrap(&store, || 1i32);
let results = f.call(&[])?;
assert_eq!(results.len(), 1);
assert_eq!(results[0].unwrap_i32(), 1);
assert_eq!(f.get0::<i32>()?()?, 1);
let f = Func::wrap(&store, || 2i64);
let results = f.call(&[])?;
assert_eq!(results.len(), 1);
assert_eq!(results[0].unwrap_i64(), 2);
assert_eq!(f.get0::<i64>()?()?, 2);
let f = Func::wrap(&store, || 3.0f32);
let results = f.call(&[])?;
assert_eq!(results.len(), 1);
assert_eq!(results[0].unwrap_f32(), 3.0);
assert_eq!(f.get0::<f32>()?()?, 3.0);
let f = Func::wrap(&store, || 4.0f64);
let results = f.call(&[])?;
assert_eq!(results.len(), 1);
assert_eq!(results[0].unwrap_f64(), 4.0);
assert_eq!(f.get0::<f64>()?()?, 4.0);
Ok(())
}
#[test]
fn caller_memory() -> anyhow::Result<()> {
let store = Store::default();
let f = Func::wrap(&store, |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(&[])?;
let f = Func::wrap(&store, |c: Caller<'_>| {
assert!(c.get_export("x").is_none());
});
let module = Module::new(
&store,
r#"
(module
(import "" "" (func $f))
(start $f)
)
"#,
)?;
Instance::new(&module, &[f.into()])?;
let f = Func::wrap(&store, |c: Caller<'_>| {
assert!(c.get_export("memory").is_some());
});
let module = Module::new(
&store,
r#"
(module
(import "" "" (func $f))
(memory (export "memory") 1)
(start $f)
)
"#,
)?;
Instance::new(&module, &[f.into()])?;
let f = Func::wrap(&store, |c: Caller<'_>| {
assert!(c.get_export("m").is_some());
assert!(c.get_export("f").is_none());
assert!(c.get_export("g").is_none());
assert!(c.get_export("t").is_none());
});
let module = Module::new(
&store,
r#"
(module
(import "" "" (func $f))
(memory (export "m") 1)
(func (export "f"))
(global (export "g") i32 (i32.const 0))
(table (export "t") 1 funcref)
(start $f)
)
"#,
)?;
Instance::new(&module, &[f.into()])?;
Ok(())
}
#[test]
fn func_write_nothing() -> anyhow::Result<()> {
let store = Store::default();
let ty = FuncType::new(Box::new([]), Box::new([ValType::I32]));
let f = Func::new(&store, ty, |_, _, _| Ok(()));
let err = f.call(&[]).unwrap_err().downcast::<Trap>()?;
assert!(err
.to_string()
.contains("function attempted to return an incompatible value"));
Ok(())
}