Skip to content

Commit

Permalink
test resumable calls for both engine backends
Browse files Browse the repository at this point in the history
  • Loading branch information
Robbepop committed Nov 30, 2023
1 parent a76aa21 commit 900f043
Showing 1 changed file with 160 additions and 127 deletions.
287 changes: 160 additions & 127 deletions crates/wasmi/tests/e2e/v1/resumable_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use wasmi::{
Caller,
Config,
Engine,
EngineBackend,
Error,
Extern,
Func,
Expand All @@ -21,9 +22,10 @@ use wasmi::{
};
use wasmi_core::{Trap, TrapCode, ValueType};

fn test_setup(remaining: u32) -> (Store<TestData>, Linker<TestData>) {
fn test_setup(remaining: u32, backend: EngineBackend) -> (Store<TestData>, Linker<TestData>) {
let mut config = Config::default();
config.wasm_tail_call(true);
config.set_engine_backend(backend);
let engine = Engine::new(&config);
let store = Store::new(
&engine,
Expand All @@ -41,8 +43,11 @@ pub struct TestData {
_remaining: u32,
}

fn resumable_call_smoldot_common(wasm: &str) -> (Store<TestData>, TypedFunc<(), i32>) {
let (mut store, mut linker) = test_setup(0);
fn resumable_call_smoldot_common(
backend: EngineBackend,
wasm: &str,
) -> (Store<TestData>, TypedFunc<(), i32>) {
let (mut store, mut linker) = test_setup(0, backend);
// The important part about this test is that this
// host function has more results than parameters.
linker
Expand Down Expand Up @@ -85,166 +90,194 @@ impl<Results> UnwrapResumable for Result<TypedResumableCall<Results>, Trap> {

#[test]
fn resumable_call_smoldot_01() {
let (mut store, wasm_fn) = resumable_call_smoldot_common(
r#"
(module
(import "env" "host_fn" (func $host_fn (result i32)))
(func (export "test") (result i32)
(call $host_fn)
fn test_with(backend: EngineBackend) {
let (mut store, wasm_fn) = resumable_call_smoldot_common(
backend,
r#"
(module
(import "env" "host_fn" (func $host_fn (result i32)))
(func (export "test") (result i32)
(call $host_fn)
)
)
)
"#,
);
let invocation = wasm_fn.call_resumable(&mut store, ()).unwrap_resumable();
match invocation.resume(&mut store, &[Value::I32(42)]).unwrap() {
TypedResumableCall::Finished(result) => assert_eq!(result, 42),
TypedResumableCall::Resumable(_) => panic!("expected TypeResumableCall::Finished"),
"#,
);
let invocation = wasm_fn.call_resumable(&mut store, ()).unwrap_resumable();
match invocation.resume(&mut store, &[Value::I32(42)]).unwrap() {
TypedResumableCall::Finished(result) => assert_eq!(result, 42),
TypedResumableCall::Resumable(_) => panic!("expected TypeResumableCall::Finished"),
}
}
test_with(EngineBackend::StackMachine);
test_with(EngineBackend::RegisterMachine);
}

#[test]
fn resumable_call_smoldot_tail_01() {
let (mut store, wasm_fn) = resumable_call_smoldot_common(
r#"
(module
(import "env" "host_fn" (func $host_fn (result i32)))
(func (export "test") (result i32)
(return_call $host_fn)
fn test_with(backend: EngineBackend) {
let (mut store, wasm_fn) = resumable_call_smoldot_common(
backend,
r#"
(module
(import "env" "host_fn" (func $host_fn (result i32)))
(func (export "test") (result i32)
(return_call $host_fn)
)
)
)
"#,
);
assert_eq!(
wasm_fn
.call_resumable(&mut store, ())
.unwrap_err()
.i32_exit_status(),
Some(100),
);
"#,
);
assert_eq!(
wasm_fn
.call_resumable(&mut store, ())
.unwrap_err()
.i32_exit_status(),
Some(100),
);
}
test_with(EngineBackend::StackMachine);
test_with(EngineBackend::RegisterMachine);
}

#[test]
fn resumable_call_smoldot_tail_02() {
let (mut store, wasm_fn) = resumable_call_smoldot_common(
r#"
(module
(import "env" "host_fn" (func $host (result i32)))
(func $wasm (result i32)
(return_call $host)
)
(func (export "test") (result i32)
(call $wasm)
fn test_with(backend: EngineBackend) {
let (mut store, wasm_fn) = resumable_call_smoldot_common(
backend,
r#"
(module
(import "env" "host_fn" (func $host (result i32)))
(func $wasm (result i32)
(return_call $host)
)
(func (export "test") (result i32)
(call $wasm)
)
)
)
"#,
);
let invocation = wasm_fn.call_resumable(&mut store, ()).unwrap_resumable();
match invocation.resume(&mut store, &[Value::I32(42)]).unwrap() {
TypedResumableCall::Finished(result) => assert_eq!(result, 42),
TypedResumableCall::Resumable(_) => panic!("expected TypeResumableCall::Finished"),
"#,
);
let invocation = wasm_fn.call_resumable(&mut store, ()).unwrap_resumable();
match invocation.resume(&mut store, &[Value::I32(42)]).unwrap() {
TypedResumableCall::Finished(result) => assert_eq!(result, 42),
TypedResumableCall::Resumable(_) => panic!("expected TypeResumableCall::Finished"),
}
}
test_with(EngineBackend::StackMachine);
test_with(EngineBackend::RegisterMachine);
}

#[test]
fn resumable_call_smoldot_02() {
let (mut store, wasm_fn) = resumable_call_smoldot_common(
r#"
(module
(import "env" "host_fn" (func $host_fn (result i32)))
(func (export "test") (result i32)
(if (result i32) (i32.ne (call $host_fn) (i32.const 0))
(then
(i32.const 11) ;; EXPECTED
)
(else
(i32.const 22) ;; FAILURE
fn test_with(backend: EngineBackend) {
let (mut store, wasm_fn) = resumable_call_smoldot_common(
backend,
r#"
(module
(import "env" "host_fn" (func $host_fn (result i32)))
(func (export "test") (result i32)
(if (result i32) (i32.ne (call $host_fn) (i32.const 0))
(then
(i32.const 11) ;; EXPECTED
)
(else
(i32.const 22) ;; FAILURE
)
)
)
)
)
"#,
);
let invocation = wasm_fn.call_resumable(&mut store, ()).unwrap_resumable();
match invocation.resume(&mut store, &[Value::I32(42)]).unwrap() {
TypedResumableCall::Finished(result) => assert_eq!(result, 11),
TypedResumableCall::Resumable(_) => panic!("expected TypeResumableCall::Finished"),
"#,
);
let invocation = wasm_fn.call_resumable(&mut store, ()).unwrap_resumable();
match invocation.resume(&mut store, &[Value::I32(42)]).unwrap() {
TypedResumableCall::Finished(result) => assert_eq!(result, 11),
TypedResumableCall::Resumable(_) => panic!("expected TypeResumableCall::Finished"),
}
}
test_with(EngineBackend::StackMachine);
test_with(EngineBackend::RegisterMachine);
}

#[test]
fn resumable_call_host() {
let (mut store, _linker) = test_setup(0);
let host_fn = Func::wrap(&mut store, || -> Result<(), Trap> {
Err(Trap::i32_exit(100))
});
// Even though the called host function traps we expect a normal error
// since the host function is the root function of the call and therefore
// it would not make sense to resume it.
let error = host_fn
.call_resumable(&mut store, &[], &mut [])
.unwrap_err();
match error {
Error::Trap(trap) => {
assert_eq!(trap.i32_exit_status(), Some(100));
fn test_with(backend: EngineBackend) {
let (mut store, _linker) = test_setup(0, backend);
let host_fn = Func::wrap(&mut store, || -> Result<(), Trap> {
Err(Trap::i32_exit(100))
});
// Even though the called host function traps we expect a normal error
// since the host function is the root function of the call and therefore
// it would not make sense to resume it.
let error = host_fn
.call_resumable(&mut store, &[], &mut [])
.unwrap_err();
match error {
Error::Trap(trap) => {
assert_eq!(trap.i32_exit_status(), Some(100));
}
_ => panic!("expected Wasm trap"),
}
_ => panic!("expected Wasm trap"),
// The same test for `TypedFunc`:
let trap = host_fn
.typed::<(), ()>(&store)
.unwrap()
.call_resumable(&mut store, ())
.unwrap_err();
assert_eq!(trap.i32_exit_status(), Some(100));
}
// The same test for `TypedFunc`:
let trap = host_fn
.typed::<(), ()>(&store)
.unwrap()
.call_resumable(&mut store, ())
.unwrap_err();
assert_eq!(trap.i32_exit_status(), Some(100));
test_with(EngineBackend::StackMachine);
test_with(EngineBackend::RegisterMachine);
}

#[test]
fn resumable_call() {
let (mut store, mut linker) = test_setup(0);
let host_fn = Func::wrap(&mut store, |input: i32| -> Result<i32, Trap> {
match input {
1 => Err(Trap::i32_exit(10)),
2 => Err(Trap::i32_exit(20)),
n => Ok(n + 1),
}
});
linker.define("env", "host_fn", host_fn).unwrap();
let wasm = wat::parse_str(
r#"
(module
(import "env" "host_fn" (func $host_fn (param i32) (result i32)))
(func (export "wasm_fn") (param $wasm_trap i32) (result i32)
(local $i i32)
(local.set $i (i32.const 0))
(local.set $i (call $host_fn (local.get $i))) ;; Ok
(local.set $i (call $host_fn (local.get $i))) ;; Trap::i32_exit(1)
(local.set $i (call $host_fn (local.get $i))) ;; Trap::i32_exit(2)
(local.set $i (call $host_fn (local.get $i))) ;; Ok
(if (i32.eq (local.get $wasm_trap) (i32.const 1))
(then unreachable) ;; trap in Wasm if $wasm_trap == 1
fn test_with(backend: EngineBackend) {
let (mut store, mut linker) = test_setup(0, backend);
let host_fn = Func::wrap(&mut store, |input: i32| -> Result<i32, Trap> {
match input {
1 => Err(Trap::i32_exit(10)),
2 => Err(Trap::i32_exit(20)),
n => Ok(n + 1),
}
});
linker.define("env", "host_fn", host_fn).unwrap();
let wasm = wat::parse_str(
r#"
(module
(import "env" "host_fn" (func $host_fn (param i32) (result i32)))
(func (export "wasm_fn") (param $wasm_trap i32) (result i32)
(local $i i32)
(local.set $i (i32.const 0))
(local.set $i (call $host_fn (local.get $i))) ;; Ok
(local.set $i (call $host_fn (local.get $i))) ;; Trap::i32_exit(1)
(local.set $i (call $host_fn (local.get $i))) ;; Trap::i32_exit(2)
(local.set $i (call $host_fn (local.get $i))) ;; Ok
(if (i32.eq (local.get $wasm_trap) (i32.const 1))
(then unreachable) ;; trap in Wasm if $wasm_trap == 1
)
(local.get $i) ;; return i == 4
)
(local.get $i) ;; return i == 4
)
"#,
)
"#,
)
.unwrap();

let module = Module::new(store.engine(), &mut &wasm[..]).unwrap();
let instance = linker
.instantiate(&mut store, &module)
.unwrap()
.start(&mut store)
.unwrap();
let wasm_fn = instance
.get_export(&store, "wasm_fn")
.and_then(Extern::into_func)
.unwrap();

run_test(wasm_fn, &mut store, false);
run_test(wasm_fn, &mut store, true);
run_test_typed(wasm_fn, &mut store, false);
run_test_typed(wasm_fn, &mut store, true);
let module = Module::new(store.engine(), &mut &wasm[..]).unwrap();
let instance = linker
.instantiate(&mut store, &module)
.unwrap()
.start(&mut store)
.unwrap();
let wasm_fn = instance
.get_export(&store, "wasm_fn")
.and_then(Extern::into_func)
.unwrap();

run_test(wasm_fn, &mut store, false);
run_test(wasm_fn, &mut store, true);
run_test_typed(wasm_fn, &mut store, false);
run_test_typed(wasm_fn, &mut store, true);
}
test_with(EngineBackend::StackMachine);
test_with(EngineBackend::RegisterMachine);
}

trait AssertResumable {
Expand Down

0 comments on commit 900f043

Please sign in to comment.