Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Wasmtime-specific C API functions to return errors #1467

Merged
merged 8 commits into from
Apr 6, 2020
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,29 @@

--------------------------------------------------------------------------------

## 0.16.0

Unreleased

### Added

* The C API has a number of new `wasmtime_*` functions which return error
objects to get detailed error information when an API fails.
[#TODO](https://github.com/bytecodealliance/wasmtime/pull/TODO)

### Changed

* The `Func::call` API has changed its error type from `Trap` to `anyhow::Error`
to distinguish between wasm traps and runtiem violations (like the wrong
number of parameters).
[#TODO](https://github.com/bytecodealliance/wasmtime/pull/TODO)

* A number of `wasmtime_linker_*` and `wasmtime_config_*` C APIs have new type
signatures which reflect returning errors.
[#TODO](https://github.com/bytecodealliance/wasmtime/pull/TODO)

--------------------------------------------------------------------------------

## 0.15.0

Unreleased
Expand Down
16 changes: 7 additions & 9 deletions crates/api/src/func.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{Extern, FuncType, Memory, Store, Trap, Val, ValType};
use anyhow::{ensure, Context as _};
use anyhow::{bail, ensure, Context as _, Result};
use std::cmp::max;
use std::fmt;
use std::mem;
Expand Down Expand Up @@ -493,18 +493,18 @@ impl Func {
///
/// This function should not panic unless the underlying function itself
/// initiates a panic.
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, Trap> {
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>> {
// We need to perform a dynamic check that the arguments given to us
// match the signature of this function and are appropriate to pass to
// this function. This involves checking to make sure we have the right
// number and types of arguments as well as making sure everything is
// from the same `Store`.
if self.ty.params().len() != params.len() {
return Err(Trap::new(format!(
bail!(
"expected {} arguments, got {}",
self.ty.params().len(),
params.len()
)));
);
}

let mut values_vec = vec![0; max(params.len(), self.ty.results().len())];
Expand All @@ -513,12 +513,10 @@ impl Func {
let param_tys = self.ty.params().iter();
for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) {
if arg.ty() != *ty {
return Err(Trap::new("argument type mismatch"));
bail!("argument type mismatch");
}
if !arg.comes_from_same_store(&self.store) {
return Err(Trap::new(
"cross-`Store` values are not currently supported",
));
bail!("cross-`Store` values are not currently supported");
}
unsafe {
arg.write_value_to(slot);
Expand All @@ -536,7 +534,7 @@ impl Func {
)
})
} {
return Err(Trap::from_jit(error));
return Err(Trap::from_jit(error).into());
}

// Load the return values out of `values_vec`.
Expand Down
7 changes: 4 additions & 3 deletions crates/api/tests/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,12 @@ fn import_works() -> Result<()> {
}

#[test]
fn trap_smoke() {
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();
let err = f.call(&[]).unwrap_err().downcast::<Trap>()?;
assert_eq!(err.message(), "test");
Ok(())
}

#[test]
Expand Down Expand Up @@ -391,7 +392,7 @@ 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();
let err = f.call(&[]).unwrap_err().downcast::<Trap>()?;
assert_eq!(
err.message(),
"function attempted to return an incompatible value"
Expand Down
14 changes: 9 additions & 5 deletions crates/api/tests/import_calling_export.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use anyhow::Result;
use std::cell::RefCell;
use std::rc::Rc;
use wasmtime::*;
Expand Down Expand Up @@ -57,7 +58,7 @@ fn test_import_calling_export() {
}

#[test]
fn test_returns_incorrect_type() {
fn test_returns_incorrect_type() -> Result<()> {
const WAT: &str = r#"
(module
(import "env" "evil" (func $evil (result i32)))
Expand All @@ -68,7 +69,7 @@ fn test_returns_incorrect_type() {
"#;

let store = Store::default();
let module = Module::new(&store, WAT).expect("failed to create module");
let module = Module::new(&store, WAT)?;

let callback_func = Func::new(
&store,
Expand All @@ -81,8 +82,7 @@ fn test_returns_incorrect_type() {
);

let imports = vec![callback_func.into()];
let instance =
Instance::new(&module, imports.as_slice()).expect("failed to instantiate module");
let instance = Instance::new(&module, imports.as_slice())?;

let exports = instance.exports();
assert!(!exports.is_empty());
Expand All @@ -91,9 +91,13 @@ fn test_returns_incorrect_type() {
.func()
.expect("expected a run func in the module");

let trap = run_func.call(&[]).expect_err("the execution should fail");
let trap = run_func
.call(&[])
.expect_err("the execution should fail")
.downcast::<Trap>()?;
assert_eq!(
trap.message(),
"function attempted to return an incompatible value"
);
Ok(())
}
30 changes: 23 additions & 7 deletions crates/api/tests/traps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ fn test_trap_return() -> Result<()> {
.func()
.expect("expected function export");

let e = run_func.call(&[]).err().expect("error calling function");
let e = run_func
.call(&[])
.err()
.expect("error calling function")
.downcast::<Trap>()?;

assert_eq!(e.message(), "test 123");

Expand All @@ -44,7 +48,11 @@ fn test_trap_trace() -> Result<()> {
.func()
.expect("expected function export");

let e = run_func.call(&[]).err().expect("error calling function");
let e = run_func
.call(&[])
.err()
.expect("error calling function")
.downcast::<Trap>()?;

let trace = e.trace();
assert_eq!(trace.len(), 2);
Expand Down Expand Up @@ -83,7 +91,11 @@ fn test_trap_trace_cb() -> Result<()> {
.func()
.expect("expected function export");

let e = run_func.call(&[]).err().expect("error calling function");
let e = run_func
.call(&[])
.err()
.expect("error calling function")
.downcast::<Trap>()?;

let trace = e.trace();
assert_eq!(trace.len(), 2);
Expand Down Expand Up @@ -111,7 +123,11 @@ fn test_trap_stack_overflow() -> Result<()> {
.func()
.expect("expected function export");

let e = run_func.call(&[]).err().expect("error calling function");
let e = run_func
.call(&[])
.err()
.expect("error calling function")
.downcast::<Trap>()?;

let trace = e.trace();
assert!(trace.len() >= 32);
Expand Down Expand Up @@ -315,17 +331,17 @@ fn mismatched_arguments() -> Result<()> {
let instance = Instance::new(&module, &[])?;
let func = instance.exports()[0].func().unwrap().clone();
assert_eq!(
func.call(&[]).unwrap_err().message(),
func.call(&[]).unwrap_err().to_string(),
"expected 1 arguments, got 0"
);
assert_eq!(
func.call(&[Val::F32(0)]).unwrap_err().message(),
func.call(&[Val::F32(0)]).unwrap_err().to_string(),
"argument type mismatch",
);
assert_eq!(
func.call(&[Val::I32(0), Val::I32(1)])
.unwrap_err()
.message(),
.to_string(),
"expected 1 arguments, got 2"
);
Ok(())
Expand Down
Loading