Files
wasmtime/crates/api/src/callable.rs
Dan Gohman d55a9967b1 Remove some obsolete re-exports in wasmtime-jit. (#992)
These were from when wasmtime-jit was trying to present a different API;
now they're not needed.
2020-02-26 05:13:28 -08:00

226 lines
6.8 KiB
Rust

use crate::runtime::Store;
use crate::trampoline::generate_func_export;
use crate::trap::Trap;
use crate::types::FuncType;
use crate::values::Val;
use std::ptr;
use std::rc::Rc;
use wasmtime_environ::ir;
use wasmtime_runtime::{Export, InstanceHandle};
/// A trait representing a function that can be imported and called from inside
/// WebAssembly.
/// # Example
/// ```
/// use wasmtime::Val;
///
/// struct TimesTwo;
///
/// impl wasmtime::Callable for TimesTwo {
/// fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), wasmtime::Trap> {
/// let mut value = params[0].unwrap_i32();
/// value *= 2;
/// results[0] = value.into();
///
/// Ok(())
/// }
/// }
///
/// # fn main () -> Result<(), Box<dyn std::error::Error>> {
/// // Simple module that imports our host function ("times_two") and re-exports
/// // it as "run".
/// let wat = r#"
/// (module
/// (func $times_two (import "" "times_two") (param i32) (result i32))
/// (func
/// (export "run")
/// (param i32)
/// (result i32)
/// (local.get 0)
/// (call $times_two))
/// )
/// "#;
///
/// // Initialise environment and our module.
/// let store = wasmtime::Store::default();
/// let module = wasmtime::Module::new(&store, wat)?;
///
/// // Define the type of the function we're going to call.
/// let times_two_type = wasmtime::FuncType::new(
/// // Parameters
/// Box::new([wasmtime::ValType::I32]),
/// // Results
/// Box::new([wasmtime::ValType::I32])
/// );
///
/// // Build a reference to the "times_two" function that can be used.
/// let times_two_function =
/// wasmtime::Func::new(&store, times_two_type, std::rc::Rc::new(TimesTwo));
///
/// // Create module instance that imports our function
/// let instance = wasmtime::Instance::new(
/// &module,
/// &[times_two_function.into()]
/// )?;
///
/// // Get "run" function from the exports.
/// let run_function = instance.exports()[0].func().unwrap();
///
/// // Borrow and call "run". Returning any error message from Wasm as a string.
/// let original = 5i32;
/// let results = run_function
/// .call(&[original.into()])
/// .map_err(|trap| trap.to_string())?;
///
/// // Compare that the results returned matches what we expect.
/// assert_eq!(original * 2, results[0].unwrap_i32());
/// # Ok(())
/// # }
/// ```
pub trait Callable {
/// What is called when the function is invoked in WebAssembly.
/// `params` is an immutable list of parameters provided to the function.
/// `results` is mutable list of results to be potentially set by your
/// function. Produces a `Trap` if the function encounters any errors.
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap>;
}
pub(crate) trait WrappedCallable {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap>;
fn signature(&self) -> &ir::Signature {
match self.wasmtime_export() {
Export::Function { signature, .. } => signature,
_ => panic!("unexpected export type in Callable"),
}
}
fn wasmtime_handle(&self) -> &InstanceHandle;
fn wasmtime_export(&self) -> &Export;
}
pub(crate) struct WasmtimeFn {
store: Store,
instance: InstanceHandle,
export: Export,
}
impl WasmtimeFn {
pub fn new(store: &Store, instance: InstanceHandle, export: Export) -> WasmtimeFn {
WasmtimeFn {
store: store.clone(),
instance,
export,
}
}
}
impl WrappedCallable for WasmtimeFn {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap> {
use std::cmp::max;
use std::mem;
let (vmctx, body, signature) = match self.wasmtime_export() {
Export::Function {
vmctx,
address,
signature,
} => (*vmctx, *address, signature.clone()),
_ => panic!("unexpected export type in Callable"),
};
if signature.params.len() - 2 != params.len() {
return Err(Trap::new(format!(
"expected {} arguments, got {}",
signature.params.len() - 2,
params.len()
)));
}
if signature.returns.len() != results.len() {
return Err(Trap::new(format!(
"expected {} results, got {}",
signature.returns.len(),
results.len()
)));
}
let value_size = mem::size_of::<u128>();
let mut values_vec = vec![0; max(params.len(), results.len())];
// Store the argument values into `values_vec`.
let param_tys = signature.params.iter().skip(2);
for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) {
if arg.ty().get_wasmtime_type() != Some(ty.value_type) {
return Err(Trap::new("argument type mismatch"));
}
unsafe {
arg.write_value_to(slot);
}
}
// Get the trampoline to call for this function.
let exec_code_buf = self
.store
.compiler_mut()
.get_published_trampoline(&signature, value_size)
.map_err(|e| Trap::new(format!("trampoline error: {:?}", e)))?;
// Call the trampoline.
if let Err(error) = unsafe {
wasmtime_runtime::wasmtime_call_trampoline(
vmctx,
ptr::null_mut(),
exec_code_buf,
body,
values_vec.as_mut_ptr() as *mut u8,
)
} {
return Err(Trap::from_jit(error));
}
// Load the return values out of `values_vec`.
for (index, abi_param) in signature.returns.iter().enumerate() {
unsafe {
let ptr = values_vec.as_ptr().add(index);
results[index] = Val::read_value_from(ptr, abi_param.value_type);
}
}
Ok(())
}
fn wasmtime_handle(&self) -> &InstanceHandle {
&self.instance
}
fn wasmtime_export(&self) -> &Export {
&self.export
}
}
pub struct NativeCallable {
callable: Rc<dyn Callable + 'static>,
instance: InstanceHandle,
export: Export,
}
impl NativeCallable {
pub(crate) fn new(callable: Rc<dyn Callable + 'static>, ft: &FuncType, store: &Store) -> Self {
let (instance, export) =
generate_func_export(ft, &callable, store).expect("generated func");
NativeCallable {
callable,
instance,
export,
}
}
}
impl WrappedCallable for NativeCallable {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap> {
self.callable.call(params, results)
}
fn wasmtime_handle(&self) -> &InstanceHandle {
&self.instance
}
fn wasmtime_export(&self) -> &Export {
&self.export
}
}