diff --git a/crates/c-api/src/func.rs b/crates/c-api/src/func.rs index 7e6a2e8c2d..fe875d951f 100644 --- a/crates/c-api/src/func.rs +++ b/crates/c-api/src/func.rs @@ -3,7 +3,6 @@ use crate::{ wasm_extern_t, wasm_functype_t, wasm_store_t, wasm_val_t, wasm_val_vec_t, wasmtime_error_t, wasmtime_extern_t, wasmtime_val_t, wasmtime_val_union, CStoreContext, CStoreContextMut, }; -use anyhow::anyhow; use std::ffi::c_void; use std::mem::{self, MaybeUninit}; use std::panic::{self, AssertUnwindSafe}; @@ -109,6 +108,22 @@ pub unsafe extern "C" fn wasm_func_new_with_env( }) } +/// Places the `args` into `dst` and additionally reserves space in `dst` for `results_size` +/// returns. The params/results slices are then returned separately. +fn translate_args<'a>( + dst: &'a mut Vec, + args: impl ExactSizeIterator, + results_size: usize, +) -> (&'a [Val], &'a mut [Val]) { + debug_assert!(dst.is_empty()); + let num_args = args.len(); + dst.reserve(args.len() + results_size); + dst.extend(args); + dst.extend((0..results_size).map(|_| Val::null())); + let (a, b) = dst.split_at_mut(num_args); + (a, b) +} + #[no_mangle] pub unsafe extern "C" fn wasm_func_call( func: &mut wasm_func_t, @@ -118,23 +133,20 @@ pub unsafe extern "C" fn wasm_func_call( let f = func.func(); let results = (*results).as_uninit_slice(); let args = (*args).as_slice(); - if results.len() != f.ty(func.ext.store.context()).results().len() { - return Box::into_raw(Box::new(wasm_trap_t::new( - anyhow!("wrong number of results provided").into(), - ))); - } - let params = args.iter().map(|i| i.val()).collect::>(); + let mut dst = Vec::new(); + let (wt_params, wt_results) = + translate_args(&mut dst, args.iter().map(|i| i.val()), results.len()); // We're calling arbitrary code here most of the time, and we in general // want to try to insulate callers against bugs in wasmtime/wasi/etc if we // can. As a result we catch panics here and transform them to traps to // allow the caller to have any insulation possible against Rust panics. let result = panic::catch_unwind(AssertUnwindSafe(|| { - f.call(func.ext.store.context_mut(), ¶ms) + f.call(func.ext.store.context_mut(), wt_params, wt_results) })); match result { - Ok(Ok(out)) => { - for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) { + Ok(Ok(())) => { + for (slot, val) in results.iter_mut().zip(wt_results.iter().cloned()) { crate::initialize(slot, wasm_val_t::from_val(val)); } ptr::null_mut() @@ -261,7 +273,7 @@ pub(crate) unsafe fn c_callback_to_rust_fn( #[no_mangle] pub unsafe extern "C" fn wasmtime_func_call( - store: CStoreContextMut<'_>, + mut store: CStoreContextMut<'_>, func: &Func, args: *const wasmtime_val_t, nargs: usize, @@ -269,27 +281,31 @@ pub unsafe extern "C" fn wasmtime_func_call( nresults: usize, trap_ret: &mut *mut wasm_trap_t, ) -> Option> { - if nresults != func.ty(&store).results().len() { - return Some(Box::new(wasmtime_error_t::from(anyhow!( - "wrong number of results provided" - )))); - } - let params = crate::slice_from_raw_parts(args, nargs) - .iter() - .map(|i| i.to_val()) - .collect::>(); + let mut store = store.as_context_mut(); + let mut params = mem::take(&mut store.data_mut().wasm_val_storage); + let (wt_params, wt_results) = translate_args( + &mut params, + crate::slice_from_raw_parts(args, nargs) + .iter() + .map(|i| i.to_val()), + nresults, + ); // We're calling arbitrary code here most of the time, and we in general // want to try to insulate callers against bugs in wasmtime/wasi/etc if we // can. As a result we catch panics here and transform them to traps to // allow the caller to have any insulation possible against Rust panics. - let result = panic::catch_unwind(AssertUnwindSafe(|| func.call(store, ¶ms))); + let result = panic::catch_unwind(AssertUnwindSafe(|| { + func.call(&mut store, wt_params, wt_results) + })); match result { - Ok(Ok(out)) => { + Ok(Ok(())) => { let results = crate::slice_from_raw_parts_mut(results, nresults); - for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) { - crate::initialize(slot, wasmtime_val_t::from_val(val)); + for (slot, val) in results.iter_mut().zip(wt_results.iter()) { + crate::initialize(slot, wasmtime_val_t::from_val(val.clone())); } + params.truncate(0); + store.data_mut().wasm_val_storage = params; None } Ok(Err(trap)) => match trap.downcast::() { diff --git a/crates/c-api/src/store.rs b/crates/c-api/src/store.rs index ea8994ddc9..cdee5969ba 100644 --- a/crates/c-api/src/store.rs +++ b/crates/c-api/src/store.rs @@ -2,7 +2,9 @@ use crate::{wasm_engine_t, wasmtime_error_t, wasmtime_val_t, ForeignData}; use std::cell::UnsafeCell; use std::ffi::c_void; use std::sync::Arc; -use wasmtime::{AsContext, AsContextMut, InterruptHandle, Store, StoreContext, StoreContextMut}; +use wasmtime::{ + AsContext, AsContextMut, InterruptHandle, Store, StoreContext, StoreContextMut, Val, +}; /// This representation of a `Store` is used to implement the `wasm.h` API. /// @@ -71,6 +73,10 @@ pub struct StoreData { /// Temporary storage for usage during a wasm->host call to store values /// in a slice we pass to the C API. pub hostcall_val_storage: Vec, + + /// Temporary storage for usage during host->wasm calls, same as above but + /// for a different direction. + pub wasm_val_storage: Vec, } #[no_mangle] @@ -90,6 +96,7 @@ pub extern "C" fn wasmtime_store_new( #[cfg(feature = "wasi")] wasi: None, hostcall_val_storage: Vec::new(), + wasm_val_storage: Vec::new(), }, ), }) diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs index ff517e9782..467aa6955e 100644 --- a/crates/fuzzing/src/oracles.rs +++ b/crates/fuzzing/src/oracles.rs @@ -297,8 +297,10 @@ pub fn differential_execution( let ty = f.ty(&store); let params = dummy::dummy_values(ty.params()); + let mut results = vec![Val::I32(0); ty.results().len()]; let this_result = f - .call(&mut store, ¶ms) + .call(&mut store, ¶ms, &mut results) + .map(|()| results.into()) .map_err(|e| e.downcast::().unwrap()); let existing_result = export_func_results @@ -312,7 +314,7 @@ pub fn differential_execution( match instance.get_export(&mut *store, "hangLimitInitializer") { None => return, Some(Extern::Func(f)) => { - f.call(store, &[]) + f.call(store, &[], &mut []) .expect("initializing the hang limit should not fail"); } Some(_) => panic!("unexpected hangLimitInitializer export"), @@ -478,7 +480,8 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) { let f = &funcs[nth]; let ty = f.ty(&store); let params = dummy::dummy_values(ty.params()); - let _ = f.call(store, ¶ms); + let mut results = vec![Val::I32(0); ty.results().len()]; + let _ = f.call(store, ¶ms, &mut results); } } } @@ -636,7 +639,7 @@ pub fn table_ops( let args: Vec<_> = (0..ops.num_params()) .map(|_| Val::ExternRef(Some(ExternRef::new(CountDrops(num_dropped.clone()))))) .collect(); - let _ = run.call(&mut store, &args); + let _ = run.call(&mut store, &args, &mut []); } assert_eq!(num_dropped.load(SeqCst), expected_drops.load(SeqCst)); @@ -740,7 +743,7 @@ pub fn differential_wasmi_execution(wasm: &[u8], config: &crate::generators::Con // Introspect wasmtime module to find name of an exported function and of an // exported memory. - let (func_name, _ty) = first_exported_function(&wasmtime_module)?; + let (func_name, ty) = first_exported_function(&wasmtime_module)?; let memory_name = first_exported_memory(&wasmtime_module)?; let wasmi_mem_export = wasmi_instance.export_by_name(memory_name).unwrap(); @@ -755,8 +758,10 @@ pub fn differential_wasmi_execution(wasm: &[u8], config: &crate::generators::Con let wasmtime_main = wasmtime_instance .get_func(&mut wasmtime_store, func_name) .expect("function export is present"); - let wasmtime_vals = wasmtime_main.call(&mut wasmtime_store, &[]); - let wasmtime_val = wasmtime_vals.map(|v| v.iter().next().cloned()); + let mut wasmtime_results = vec![Val::I32(0); ty.results().len()]; + let wasmtime_val = wasmtime_main + .call(&mut wasmtime_store, &[], &mut wasmtime_results) + .map(|()| wasmtime_results.get(0).cloned()); debug!( "Successful execution: wasmi returned {:?}, wasmtime returned {:?}", @@ -918,15 +923,17 @@ fn run_in_wasmtime( .context("Wasmtime cannot instantiate module")?; // Find the first exported function. - let (func_name, _ty) = + let (func_name, ty) = first_exported_function(&wasmtime_module).context("Cannot find exported function")?; let wasmtime_main = wasmtime_instance .get_func(&mut wasmtime_store, &func_name[..]) .expect("function export is present"); // Execute the function and return the values. - let wasmtime_vals = wasmtime_main.call(&mut wasmtime_store, params); - wasmtime_vals.map(|v| v.to_vec()) + let mut results = vec![Val::I32(0); ty.results().len()]; + wasmtime_main + .call(&mut wasmtime_store, params, &mut results) + .map(|()| results) } // Introspect wasmtime module to find the name of the first exported function. diff --git a/crates/fuzzing/src/oracles/v8.rs b/crates/fuzzing/src/oracles/v8.rs index 0e66c38cb9..ff16757c00 100644 --- a/crates/fuzzing/src/oracles/v8.rs +++ b/crates/fuzzing/src/oracles/v8.rs @@ -93,7 +93,9 @@ pub fn differential_v8_execution(wasm: &[u8], config: &crate::generators::Config let wasmtime_main = wasmtime_instance .get_func(&mut wasmtime_store, func) .expect("function export is present"); - let wasmtime_vals = wasmtime_main.call(&mut wasmtime_store, &wasmtime_params); + let mut wasmtime_vals = vec![Val::I32(0); ty.results().len()]; + let wasmtime_result = + wasmtime_main.call(&mut wasmtime_store, &wasmtime_params, &mut wasmtime_vals); log::trace!("finished wasmtime invocation"); // V8: call the first exported func @@ -112,15 +114,15 @@ pub fn differential_v8_execution(wasm: &[u8], config: &crate::generators::Config log::trace!("finished v8 invocation"); // Verify V8 and wasmtime match - match (wasmtime_vals, v8_vals) { - (Ok(wasmtime), Ok(v8)) => { + match (wasmtime_result, v8_vals) { + (Ok(()), Ok(v8)) => { log::trace!("both executed successfully"); - match wasmtime.len() { + match wasmtime_vals.len() { 0 => assert!(v8.is_undefined()), - 1 => assert_val_match(&wasmtime[0], &v8, &mut scope), + 1 => assert_val_match(&wasmtime_vals[0], &v8, &mut scope), _ => { let array = v8::Local::<'_, v8::Array>::try_from(v8).unwrap(); - for (i, wasmtime) in wasmtime.iter().enumerate() { + for (i, wasmtime) in wasmtime_vals.iter().enumerate() { let v8 = array.get_index(&mut scope, i as u32).unwrap(); assert_val_match(wasmtime, &v8, &mut scope); // .. @@ -128,7 +130,7 @@ pub fn differential_v8_execution(wasm: &[u8], config: &crate::generators::Config } } } - (Ok(_), Err(msg)) => { + (Ok(()), Err(msg)) => { panic!("wasmtime succeeded at invocation, v8 failed: {}", msg) } (Err(err), Ok(_)) => { diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index dbc8feba6c..abadbdb341 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -4,7 +4,6 @@ use crate::{ StoreContext, StoreContextMut, Trap, Val, ValType, }; use anyhow::{bail, Context as _, Result}; -use std::cmp::max; use std::error::Error; use std::fmt; use std::future::Future; @@ -90,13 +89,13 @@ use wasmtime_runtime::{ /// /// // Work with `foo` as a `Func` at this point, such as calling it /// // dynamically... -/// match foo.call(&mut store, &[]) { -/// Ok(result) => { /* ... */ } +/// match foo.call(&mut store, &[], &mut []) { +/// Ok(()) => { /* ... */ } /// Err(trap) => { /// panic!("execution of `foo` resulted in a wasm trap: {}", trap); /// } /// } -/// foo.call(&mut store, &[])?; +/// foo.call(&mut store, &[], &mut [])?; /// /// // ... or we can make a static assertion about its signature and call it. /// // Our first call here can fail if the signatures don't match, and then the @@ -184,9 +183,14 @@ use wasmtime_runtime::{ #[repr(transparent)] // here for the C API pub struct Func(Stored); +pub(crate) struct FuncData { + kind: FuncKind, + ty: FuncType, +} + /// The three ways that a function can be created and referenced from within a /// store. -pub(crate) enum FuncData { +enum FuncKind { /// A function already owned by the store via some other means. This is /// used, for example, when creating a `Func` from an instance's exported /// function. The instance's `InstanceHandle` is already owned by the store @@ -647,42 +651,37 @@ impl Func { /// /// Panics if `store` does not own this function. pub fn ty(&self, store: impl AsContext) -> FuncType { - // Signatures should always be registered in the engine's registry of - // shared signatures, so we should be able to unwrap safely here. - let store = store.as_context(); - let sig_index = unsafe { store[self.0].export().anyfunc.as_ref().type_index }; - FuncType::from_wasm_func_type( - store - .engine() - .signatures() - .lookup_type(sig_index) - .expect("signature should be registered"), - ) + store.as_context()[self.0].ty.clone() } pub(crate) fn sig_index(&self, data: &StoreData) -> VMSharedSignatureIndex { unsafe { data[self.0].export().anyfunc.as_ref().type_index } } - /// Invokes this function with the `params` given, returning the results and - /// any trap, if one occurs. + /// Invokes this function with the `params` given and writes returned values + /// to `results`. /// /// The `params` here must match the type signature of this `Func`, or a /// trap will occur. If a trap occurs while executing this function, then a - /// trap will also be returned. + /// trap will also be returned. Additionally `results` must have the same + /// length as the number of results for this function. /// /// # Panics /// /// This function will panic if called on a function belonging to an async /// store. Asynchronous stores must always use `call_async`. /// initiates a panic. Also panics if `store` does not own this function. - pub fn call(&self, mut store: impl AsContextMut, params: &[Val]) -> Result> { + pub fn call( + &self, + mut store: impl AsContextMut, + params: &[Val], + results: &mut [Val], + ) -> Result<()> { assert!( !store.as_context().async_support(), "must use `call_async` when async support is enabled on the config", ); - let my_ty = self.ty(&store); - self.call_impl(&mut store.as_context_mut(), my_ty, params) + self.call_impl(&mut store.as_context_mut(), params, results) } /// Invokes this function with the `params` given, returning the results @@ -713,7 +712,8 @@ impl Func { &self, mut store: impl AsContextMut, params: &[Val], - ) -> Result> + results: &mut [Val], + ) -> Result<()> where T: Send, { @@ -722,9 +722,8 @@ impl Func { store.0.async_support(), "cannot use `call_async` without enabling async support in the config", ); - let my_ty = self.ty(&store); let result = store - .on_fiber(|store| self.call_impl(store, my_ty, params)) + .on_fiber(|store| self.call_impl(store, params, results)) .await??; Ok(result) } @@ -732,10 +731,43 @@ impl Func { fn call_impl( &self, store: &mut StoreContextMut<'_, T>, - my_ty: FuncType, params: &[Val], - ) -> Result> { - let mut values_vec = write_params(store.0, &my_ty, params)?; + results: &mut [Val], + ) -> Result<()> { + // 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`. + let ty = &store[self.0].ty; + if ty.params().len() != params.len() { + bail!( + "expected {} arguments, got {}", + ty.params().len(), + params.len() + ); + } + if ty.results().len() != results.len() { + bail!( + "expected {} results, got {}", + ty.results().len(), + results.len() + ); + } + for (ty, arg) in ty.params().zip(params) { + if arg.ty() != ty { + bail!( + "argument type mismatch: found {} but expected {}", + arg.ty(), + ty + ); + } + if !arg.comes_from_same_store(store.0) { + bail!("cross-`Store` values are not currently supported"); + } + } + + let mut values_vec = write_params(store.0, params, results)?; // Call the trampoline. unsafe { @@ -752,27 +784,19 @@ impl Func { })?; } - return Ok(read_results(store.0, &my_ty, &values_vec)); + read_results(store.0, self, values_vec, results); + return Ok(()); fn write_params( store: &mut StoreOpaque, - ty: &FuncType, params: &[Val], + results: &mut [Val], ) -> Result> { - // 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 ty.params().len() != params.len() { - bail!( - "expected {} arguments, got {}", - ty.params().len(), - params.len() - ); - } + let values_vec_size = params.len().max(results.len()); - let mut values_vec = vec![0; max(params.len(), ty.results().len())]; + let mut values_vec = store.take_wasm_u128_storage(); + debug_assert!(values_vec.is_empty()); + values_vec.extend((0..values_vec_size).map(|_| 0)); // Whenever we pass `externref`s from host code to Wasm code, they // go into the `VMExternRefActivationsTable`. But the table might be @@ -790,18 +814,7 @@ impl Func { } // Store the argument values into `values_vec`. - let param_tys = ty.params(); - for ((arg, slot), ty) in params.iter().cloned().zip(&mut values_vec).zip(param_tys) { - if arg.ty() != ty { - bail!( - "argument type mismatch: found {} but expected {}", - arg.ty(), - ty - ); - } - if !arg.comes_from_same_store(store) { - bail!("cross-`Store` values are not currently supported"); - } + for (arg, slot) in params.iter().zip(&mut values_vec) { unsafe { arg.write_value_without_gc(store, slot); } @@ -810,15 +823,20 @@ impl Func { Ok(values_vec) } - fn read_results(store: &mut StoreOpaque, ty: &FuncType, values_vec: &[u128]) -> Box<[Val]> { - let mut results = Vec::with_capacity(ty.results().len()); - for (index, ty) in ty.results().enumerate() { + fn read_results( + store: &mut StoreOpaque, + func: &Func, + mut values_vec: Vec, + results: &mut [Val], + ) { + for (i, (ptr, dst)) in values_vec.iter().zip(results).enumerate() { + let ty = store[func.0].ty.results().nth(i).unwrap(); unsafe { - let ptr = &values_vec[index]; - results.push(Val::read_value_from(store, ptr, ty)); + *dst = Val::read_value_from(store, ptr, ty); } } - results.into() + values_vec.truncate(0); + store.save_wasm_u128_storage(values_vec); } } @@ -836,8 +854,21 @@ impl Func { ) -> Self { let anyfunc = export.anyfunc.as_ref(); let trampoline = store.lookup_trampoline(&*anyfunc); - let data = FuncData::StoreOwned { trampoline, export }; - Func(store.store_data_mut().insert(data)) + Func::from_func_kind(FuncKind::StoreOwned { trampoline, export }, store) + } + + fn from_func_kind(kind: FuncKind, store: &mut StoreOpaque) -> Self { + // Signatures should always be registered in the engine's registry of + // shared signatures, so we should be able to unwrap safely here. + let ty = unsafe { kind.export().anyfunc.as_ref().type_index }; + let ty = FuncType::from_wasm_func_type( + store + .engine() + .signatures() + .lookup_type(ty) + .expect("signature should be registered"), + ); + Func(store.store_data_mut().insert(FuncData { kind, ty })) } pub(crate) fn vmimport(&self, store: &mut StoreOpaque) -> VMFunctionImport { @@ -1972,13 +2003,13 @@ impl HostFunc { pub unsafe fn to_func(self: &Arc, store: &mut StoreOpaque) -> Func { self.register_trampoline(store); let me = self.clone(); - Func(store.store_data_mut().insert(FuncData::SharedHost(me))) + Func::from_func_kind(FuncKind::SharedHost(me), store) } /// Same as [`HostFunc::to_func`], different ownership. unsafe fn into_func(self, store: &mut StoreOpaque) -> Func { self.register_trampoline(store); - Func(store.store_data_mut().insert(FuncData::Host(self))) + Func::from_func_kind(FuncKind::Host(self), store) } unsafe fn register_trampoline(&self, store: &mut StoreOpaque) { @@ -2014,20 +2045,28 @@ impl Drop for HostFunc { } impl FuncData { + #[inline] fn trampoline(&self) -> VMTrampoline { - match self { - FuncData::StoreOwned { trampoline, .. } => *trampoline, - FuncData::SharedHost(host) => host.trampoline, - FuncData::Host(host) => host.trampoline, + match &self.kind { + FuncKind::StoreOwned { trampoline, .. } => *trampoline, + FuncKind::SharedHost(host) => host.trampoline, + FuncKind::Host(host) => host.trampoline, } } + #[inline] + fn export(&self) -> &ExportFunction { + self.kind.export() + } +} + +impl FuncKind { #[inline] fn export(&self) -> &ExportFunction { match self { - FuncData::StoreOwned { export, .. } => export, - FuncData::SharedHost(host) => &host.export, - FuncData::Host(host) => &host.export, + FuncKind::StoreOwned { export, .. } => export, + FuncKind::SharedHost(host) => &host.export, + FuncKind::Host(host) => &host.export, } } } diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs index 64b2bf6d53..7b6a096868 100644 --- a/crates/wasmtime/src/lib.rs +++ b/crates/wasmtime/src/lib.rs @@ -440,7 +440,7 @@ fn _assert_send_sync() { #[cfg(feature = "async")] fn _call_async(s: &mut Store<()>, f: Func) { - _assert_send(f.call_async(&mut *s, &[])) + _assert_send(f.call_async(&mut *s, &[], &mut [])) } #[cfg(feature = "async")] fn _typed_call_async(s: &mut Store<()>, f: TypedFunc<(), ()>) { diff --git a/crates/wasmtime/src/linker.rs b/crates/wasmtime/src/linker.rs index c3d8940c31..a84b497959 100644 --- a/crates/wasmtime/src/linker.rs +++ b/crates/wasmtime/src/linker.rs @@ -645,21 +645,14 @@ impl Linker { // `unwrap()` everything here because we know the instance contains a // function export with the given name and signature because we're // iterating over the module it was instantiated from. - let command_results = instance + instance .get_export(&mut caller, &export_name) .unwrap() .into_func() .unwrap() - .call(&mut caller, params) + .call(&mut caller, params, results) .map_err(|error| error.downcast::().unwrap())?; - // Copy the return values into the output slice. - for (result, command_result) in - results.iter_mut().zip(command_results.into_vec()) - { - *result = command_result; - } - Ok(()) }, ) @@ -718,20 +711,14 @@ impl Linker { let (instance_pre, export_name) = &*upvars; let instance = instance_pre.instantiate_async(&mut caller).await?; - let command_results = instance + instance .get_export(&mut caller, &export_name) .unwrap() .into_func() .unwrap() - .call_async(&mut caller, params) + .call_async(&mut caller, params, results) .await .map_err(|error| error.downcast::().unwrap())?; - - for (result, command_result) in - results.iter_mut().zip(command_results.into_vec()) - { - *result = command_result; - } Ok(()) }) }, diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index 015eb29c5c..e797984bc7 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -274,6 +274,9 @@ pub struct StoreOpaque { /// `Func::new` to avoid allocating a new vector each time a function is /// called. hostcall_val_storage: Vec, + /// Same as `hostcall_val_storage`, but for the direction of the host + /// calling wasm. + wasm_u128_storage: Vec, } #[cfg(feature = "async")] @@ -430,6 +433,7 @@ impl Store { store_data: StoreData::new(), default_callee, hostcall_val_storage: Vec::new(), + wasm_u128_storage: Vec::new(), }, limiter: None, call_hook: None, @@ -1160,6 +1164,7 @@ impl StoreOpaque { /// Takes the cached `Vec` stored internally across hostcalls to get /// used as part of calling the host in a `Func::new` method invocation. + #[inline] pub fn take_hostcall_val_storage(&mut self) -> Vec { mem::take(&mut self.hostcall_val_storage) } @@ -1167,11 +1172,28 @@ impl StoreOpaque { /// Restores the vector previously taken by `take_hostcall_val_storage` /// above back into the store, allowing it to be used in the future for the /// next wasm->host call. + #[inline] pub fn save_hostcall_val_storage(&mut self, storage: Vec) { if storage.capacity() > self.hostcall_val_storage.capacity() { self.hostcall_val_storage = storage; } } + + /// Same as `take_hostcall_val_storage`, but for the direction of the host + /// calling wasm. + #[inline] + pub fn take_wasm_u128_storage(&mut self) -> Vec { + mem::take(&mut self.wasm_u128_storage) + } + + /// Same as `save_hostcall_val_storage`, but for the direction of the host + /// calling wasm. + #[inline] + pub fn save_wasm_u128_storage(&mut self, storage: Vec) { + if storage.capacity() > self.wasm_u128_storage.capacity() { + self.wasm_u128_storage = storage; + } + } } impl StoreContextMut<'_, T> { diff --git a/crates/wasmtime/src/types.rs b/crates/wasmtime/src/types.rs index 479187825c..738cc1c7dd 100644 --- a/crates/wasmtime/src/types.rs +++ b/crates/wasmtime/src/types.rs @@ -238,11 +238,13 @@ impl FuncType { } /// Returns the list of parameter types for this function. + #[inline] pub fn params(&self) -> impl ExactSizeIterator + '_ { self.sig.params().iter().map(ValType::from_wasm_type) } /// Returns the list of result types for this function. + #[inline] pub fn results(&self) -> impl ExactSizeIterator + '_ { self.sig.returns().iter().map(ValType::from_wasm_type) } diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs index 0308f3f611..c351f0ae0c 100644 --- a/crates/wast/src/wast.rs +++ b/crates/wast/src/wast.rs @@ -163,8 +163,10 @@ impl WastContext { .get_export(instance_name, field)? .into_func() .ok_or_else(|| anyhow!("no function named `{}`", field))?; - Ok(match func.call(&mut self.store, args) { - Ok(result) => Outcome::Ok(result.into()), + + let mut results = vec![Val::null(); func.ty(&self.store).results().len()]; + Ok(match func.call(&mut self.store, args, &mut results) { + Ok(()) => Outcome::Ok(results.into()), Err(e) => Outcome::Trap(e.downcast()?), }) } diff --git a/crates/wiggle/tests/wasmtime_async.rs b/crates/wiggle/tests/wasmtime_async.rs index 51a0e4aa82..19209f9fc2 100644 --- a/crates/wiggle/tests/wasmtime_async.rs +++ b/crates/wiggle/tests/wasmtime_async.rs @@ -1,4 +1,4 @@ -use wasmtime::{Config, Engine, Linker, Module, Store}; +use wasmtime::{Config, Engine, Linker, Module, Store, Val}; wiggle::from_witx!({ witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], @@ -42,14 +42,14 @@ async fn test_sync_host_func() { .await .unwrap(); - let results = shim_inst + let mut results = [Val::I32(0)]; + shim_inst .get_func(&mut store, "int_float_args_shim") .unwrap() - .call_async(&mut store, &[0i32.into(), 123.45f32.into()]) + .call_async(&mut store, &[0i32.into(), 123.45f32.into()], &mut results) .await .unwrap(); - assert_eq!(results.len(), 1, "one return value"); assert_eq!( results[0].unwrap_i32(), types::Errno::Ok as i32, @@ -72,14 +72,18 @@ async fn test_async_host_func() { let input: i32 = 123; let result_location: i32 = 0; - let results = shim_inst + let mut results = [Val::I32(0)]; + shim_inst .get_func(&mut store, "double_int_return_float_shim") .unwrap() - .call_async(&mut store, &[input.into(), result_location.into()]) + .call_async( + &mut store, + &[input.into(), result_location.into()], + &mut results, + ) .await .unwrap(); - assert_eq!(results.len(), 1, "one return value"); assert_eq!( results[0].unwrap_i32(), types::Errno::Ok as i32, diff --git a/crates/wiggle/tests/wasmtime_integration.rs b/crates/wiggle/tests/wasmtime_integration.rs index 756d8ca8f6..8617c6d757 100644 --- a/crates/wiggle/tests/wasmtime_integration.rs +++ b/crates/wiggle/tests/wasmtime_integration.rs @@ -1,4 +1,4 @@ -use wasmtime::{Engine, Linker, Module, Store}; +use wasmtime::{Engine, Linker, Module, Store, Val}; // from_witx invocation says the func is async. This context doesn't support async! wiggle::from_witx!({ @@ -49,13 +49,13 @@ fn test_sync_host_func() { let shim_mod = shim_module(&engine); let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap(); - let results = shim_inst + let mut results = [Val::I32(0)]; + shim_inst .get_func(&mut store, "int_float_args_shim") .unwrap() - .call(&mut store, &[0i32.into(), 123.45f32.into()]) + .call(&mut store, &[0i32.into(), 123.45f32.into()], &mut results) .unwrap(); - assert_eq!(results.len(), 1, "one return value"); assert_eq!( results[0].unwrap_i32(), types::Errno::Ok as i32, @@ -76,13 +76,17 @@ fn test_async_host_func() { let input: i32 = 123; let result_location: i32 = 0; - let results = shim_inst + let mut results = [Val::I32(0)]; + shim_inst .get_func(&mut store, "double_int_return_float_shim") .unwrap() - .call(&mut store, &[input.into(), result_location.into()]) + .call( + &mut store, + &[input.into(), result_location.into()], + &mut results, + ) .unwrap(); - assert_eq!(results.len(), 1, "one return value"); assert_eq!( results[0].unwrap_i32(), types::Errno::Ok as i32, diff --git a/crates/wiggle/tests/wasmtime_sync.rs b/crates/wiggle/tests/wasmtime_sync.rs index 8332b19a55..3410e530ea 100644 --- a/crates/wiggle/tests/wasmtime_sync.rs +++ b/crates/wiggle/tests/wasmtime_sync.rs @@ -1,4 +1,4 @@ -use wasmtime::{Engine, Linker, Module, Store}; +use wasmtime::{Engine, Linker, Module, Store, Val}; wiggle::from_witx!({ witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], @@ -55,13 +55,13 @@ fn test_sync_host_func() { let shim_mod = shim_module(&engine); let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap(); - let results = shim_inst + let mut results = [Val::I32(0)]; + shim_inst .get_func(&mut store, "int_float_args_shim") .unwrap() - .call(&mut store, &[0i32.into(), 123.45f32.into()]) + .call(&mut store, &[0i32.into(), 123.45f32.into()], &mut results) .unwrap(); - assert_eq!(results.len(), 1, "one return value"); assert_eq!( results[0].unwrap_i32(), types::Errno::Ok as i32, @@ -82,13 +82,17 @@ fn test_async_host_func() { let input: i32 = 123; let result_location: i32 = 0; - let results = shim_inst + let mut results = [Val::I32(0)]; + shim_inst .get_func(&mut store, "double_int_return_float_shim") .unwrap() - .call(&mut store, &[input.into(), result_location.into()]) + .call( + &mut store, + &[input.into(), result_location.into()], + &mut results, + ) .unwrap(); - assert_eq!(results.len(), 1, "one return value"); assert_eq!( results[0].unwrap_i32(), types::Errno::Ok as i32, @@ -121,7 +125,11 @@ fn test_async_host_func_pending() { let trap = shim_inst .get_func(&mut store, "double_int_return_float_shim") .unwrap() - .call(&mut store, &[input.into(), result_location.into()]) + .call( + &mut store, + &[input.into(), result_location.into()], + &mut [Val::I32(0)], + ) .unwrap_err(); assert!( format!("{}", trap).contains("Cannot wait on pending future"), diff --git a/src/commands/run.rs b/src/commands/run.rs index ab01807cc5..c7877d10d1 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -343,7 +343,8 @@ impl RunCommand { // Invoke the function and then afterwards print all the results that came // out, if there are any. - let results = func.call(store, &values).with_context(|| { + let mut results = vec![Val::null(); ty.results().len()]; + func.call(store, &values, &mut results).with_context(|| { if let Some(name) = name { format!("failed to invoke `{}`", name) } else { @@ -357,7 +358,7 @@ impl RunCommand { ); } - for result in results.into_vec() { + for result in results { match result { Val::I32(i) => println!("{}", i), Val::I64(i) => println!("{}", i), diff --git a/tests/all/async_functions.rs b/tests/all/async_functions.rs index 20349c5a58..584501ded3 100644 --- a/tests/all/async_functions.rs +++ b/tests/all/async_functions.rs @@ -9,8 +9,8 @@ fn async_store() -> Store<()> { } fn run_smoke_test(store: &mut Store<()>, func: Func) { - run(func.call_async(&mut *store, &[])).unwrap(); - run(func.call_async(&mut *store, &[])).unwrap(); + run(func.call_async(&mut *store, &[], &mut [])).unwrap(); + run(func.call_async(&mut *store, &[], &mut [])).unwrap(); } fn run_smoke_typed_test(store: &mut Store<()>, func: Func) { @@ -159,7 +159,9 @@ fn recursive_call() { FuncType::new(None, None), move |mut caller, _params, _results| { Box::new(async move { - async_wasm_func.call_async(&mut caller, &[]).await?; + async_wasm_func + .call_async(&mut caller, &[], &mut []) + .await?; Ok(()) }) }, @@ -184,7 +186,7 @@ fn recursive_call() { run(async { let instance = Instance::new_async(&mut store, &module, &[func2.into()]).await?; let func = instance.get_func(&mut store, "").unwrap(); - func.call_async(&mut store, &[]).await + func.call_async(&mut store, &[], &mut []).await }) .unwrap(); } @@ -209,7 +211,7 @@ fn suspend_while_suspending() { &mut store, FuncType::new(None, None), move |mut caller, _params, _results| { - run(async_thunk.call_async(&mut caller, &[]))?; + run(async_thunk.call_async(&mut caller, &[], &mut []))?; Ok(()) }, ); @@ -249,7 +251,7 @@ fn suspend_while_suspending() { ) .await?; let func = instance.get_func(&mut store, "").unwrap(); - func.call_async(&mut store, &[]).await + func.call_async(&mut store, &[], &mut []).await }) .unwrap(); } @@ -277,7 +279,7 @@ fn cancel_during_run() { // Create our future, but as per async conventions this still doesn't // actually do anything. No wasm or host function has been called yet. - let mut future = Pin::from(Box::new(async_thunk.call_async(&mut store, &[]))); + let mut future = Pin::from(Box::new(async_thunk.call_async(&mut store, &[], &mut []))); // Push the future forward one tick, which actually runs the host code in // our async func. Our future is designed to be pending once, however. @@ -608,7 +610,7 @@ fn resume_separate_thread3() { // restored even though the asynchronous execution is suspended. Err::<(), _>(wasmtime::Trap::new("")) }); - assert!(f.call(&mut store, &[]).is_err()); + assert!(f.call(&mut store, &[], &mut []).is_err()); } #[test] @@ -636,7 +638,7 @@ fn recursive_async() -> Result<()> { Ok(()) }) }); - run(f2.call_async(&mut store, &[]))?; + run(f2.call_async(&mut store, &[], &mut []))?; Ok(()) } diff --git a/tests/all/call_hook.rs b/tests/all/call_hook.rs index bb63b611d1..e253519f80 100644 --- a/tests/all/call_hook.rs +++ b/tests/all/call_hook.rs @@ -31,6 +31,7 @@ fn call_wrapped_func() -> Result<(), Error> { f.call( &mut store, &[Val::I32(1), Val::I64(2), 3.0f32.into(), 4.0f64.into()], + &mut [], )?; // One switch from vm to host to call f, another in return from f. @@ -85,6 +86,7 @@ async fn call_wrapped_async_func() -> Result<(), Error> { f.call_async( &mut store, &[Val::I32(1), Val::I64(2), 3.0f32.into(), 4.0f64.into()], + &mut [], ) .await?; @@ -154,7 +156,7 @@ fn call_linked_func() -> Result<(), Error> { .into_func() .expect("export is func"); - export.call(&mut store, &[])?; + export.call(&mut store, &[], &mut [])?; // One switch from vm to host to call f, another in return from f. assert_eq!(store.data().calls_into_host, 1); @@ -225,7 +227,7 @@ async fn call_linked_func_async() -> Result<(), Error> { .into_func() .expect("export is func"); - export.call_async(&mut store, &[]).await?; + export.call_async(&mut store, &[], &mut []).await?; // One switch from vm to host to call f, another in return from f. assert_eq!(store.data().calls_into_host, 1); @@ -333,7 +335,7 @@ fn recursion() -> Result<(), Error> { // Recursion depth: let n: usize = 10; - export.call(&mut store, &[Val::I32(n as i32)])?; + export.call(&mut store, &[Val::I32(n as i32)], &mut [])?; // Recurse down to 0: n+1 calls assert_eq!(store.data().calls_into_host, n + 1); @@ -423,6 +425,7 @@ fn trapping() -> Result<(), Error> { let r = export.call( &mut store, &[Val::I32(action), Val::I32(if recur { 1 } else { 0 })], + &mut [], ); (store.into_data(), r.err()) }; diff --git a/tests/all/externals.rs b/tests/all/externals.rs index 847c44f7e7..04742fa506 100644 --- a/tests/all/externals.rs +++ b/tests/all/externals.rs @@ -114,16 +114,24 @@ fn cross_store() -> anyhow::Result<()> { let s1_f = s1_inst.get_func(&mut store1, "f").unwrap(); let s2_f = s2_inst.get_func(&mut store2, "f").unwrap(); - assert!(s1_f.call(&mut store1, &[Val::FuncRef(None)]).is_ok()); - assert!(s2_f.call(&mut store2, &[Val::FuncRef(None)]).is_ok()); - assert!(s1_f.call(&mut store1, &[Some(s1_f.clone()).into()]).is_ok()); assert!(s1_f - .call(&mut store1, &[Some(s2_f.clone()).into()]) + .call(&mut store1, &[Val::FuncRef(None)], &mut []) + .is_ok()); + assert!(s2_f + .call(&mut store2, &[Val::FuncRef(None)], &mut []) + .is_ok()); + assert!(s1_f + .call(&mut store1, &[Some(s1_f.clone()).into()], &mut []) + .is_ok()); + assert!(s1_f + .call(&mut store1, &[Some(s2_f.clone()).into()], &mut []) .is_err()); assert!(s2_f - .call(&mut store2, &[Some(s1_f.clone()).into()]) + .call(&mut store2, &[Some(s1_f.clone()).into()], &mut []) .is_err()); - assert!(s2_f.call(&mut store2, &[Some(s2_f.clone()).into()]).is_ok()); + assert!(s2_f + .call(&mut store2, &[Some(s2_f.clone()).into()], &mut []) + .is_ok()); let s1_f_t = s1_f.typed::, (), _>(&store1)?; let s2_f_t = s2_f.typed::, (), _>(&store2)?; diff --git a/tests/all/func.rs b/tests/all/func.rs index 64d0b3e63e..da4c630fa4 100644 --- a/tests/all/func.rs +++ b/tests/all/func.rs @@ -193,10 +193,12 @@ fn import_works() -> Result<()> { f.as_ref().unwrap().data().downcast_ref::().unwrap(), "hello" ); - assert_eq!( - g.as_ref().unwrap().call(&mut caller, &[]).unwrap()[0].unwrap_i32(), - 42 - ); + let mut results = [Val::I32(0)]; + g.as_ref() + .unwrap() + .call(&mut caller, &[], &mut results) + .unwrap(); + assert_eq!(results[0].unwrap_i32(), 42); assert_eq!(HITS.fetch_add(1, SeqCst), 3); }, ) @@ -211,6 +213,7 @@ fn import_works() -> Result<()> { Val::ExternRef(Some(ExternRef::new("hello".to_string()))), funcref, ], + &mut [], )?; assert_eq!(HITS.load(SeqCst), 4); Ok(()) @@ -222,7 +225,10 @@ fn trap_smoke() -> Result<()> { let f = Func::wrap(&mut store, || -> Result<(), Trap> { Err(Trap::new("test")) }); - let err = f.call(&mut store, &[]).unwrap_err().downcast::()?; + let err = f + .call(&mut store, &[], &mut []) + .unwrap_err() + .downcast::()?; assert!(err.to_string().contains("test")); assert!(err.i32_exit_status().is_none()); Ok(()) @@ -347,31 +353,29 @@ fn call_wrapped_func() -> Result<()> { f.call( &mut store, &[Val::I32(1), Val::I64(2), 3.0f32.into(), 4.0f64.into()], + &mut [], )?; f.typed::<(i32, i64, f32, f64), (), _>(&store)? .call(&mut store, (1, 2, 3.0, 4.0))?; + let mut results = [Val::I32(0)]; let f = Func::wrap(&mut store, || 1i32); - let results = f.call(&mut store, &[])?; - assert_eq!(results.len(), 1); + f.call(&mut store, &[], &mut results)?; assert_eq!(results[0].unwrap_i32(), 1); assert_eq!(f.typed::<(), i32, _>(&store)?.call(&mut store, ())?, 1); let f = Func::wrap(&mut store, || 2i64); - let results = f.call(&mut store, &[])?; - assert_eq!(results.len(), 1); + f.call(&mut store, &[], &mut results)?; assert_eq!(results[0].unwrap_i64(), 2); assert_eq!(f.typed::<(), i64, _>(&store)?.call(&mut store, ())?, 2); let f = Func::wrap(&mut store, || 3.0f32); - let results = f.call(&mut store, &[])?; - assert_eq!(results.len(), 1); + f.call(&mut store, &[], &mut results)?; assert_eq!(results[0].unwrap_f32(), 3.0); assert_eq!(f.typed::<(), f32, _>(&store)?.call(&mut store, ())?, 3.0); let f = Func::wrap(&mut store, || 4.0f64); - let results = f.call(&mut store, &[])?; - assert_eq!(results.len(), 1); + f.call(&mut store, &[], &mut results)?; assert_eq!(results[0].unwrap_f64(), 4.0); assert_eq!(f.typed::<(), f64, _>(&store)?.call(&mut store, ())?, 4.0); Ok(()) @@ -385,7 +389,7 @@ fn caller_memory() -> anyhow::Result<()> { assert!(c.get_export("y").is_none()); assert!(c.get_export("z").is_none()); }); - f.call(&mut store, &[])?; + f.call(&mut store, &[], &mut [])?; let f = Func::wrap(&mut store, |mut c: Caller<'_, ()>| { assert!(c.get_export("x").is_none()); @@ -447,7 +451,10 @@ fn func_write_nothing() -> anyhow::Result<()> { let mut store = Store::<()>::default(); let ty = FuncType::new(None, Some(ValType::I32)); let f = Func::new(&mut store, ty, |_, _, _| Ok(())); - let err = f.call(&mut store, &[]).unwrap_err().downcast::()?; + let err = f + .call(&mut store, &[], &mut [Val::I32(0)]) + .unwrap_err() + .downcast::()?; assert!(err .to_string() .contains("function attempted to return an incompatible value")); @@ -479,7 +486,7 @@ fn return_cross_store_value() -> anyhow::Result<()> { let instance = Instance::new(&mut store1, &module, &[return_cross_store_func.into()])?; let run = instance.get_func(&mut store1, "run").unwrap(); - let result = run.call(&mut store1, &[]); + let result = run.call(&mut store1, &[], &mut [Val::I32(0)]); assert!(result.is_err()); assert!(result.unwrap_err().to_string().contains("cross-`Store`")); @@ -500,7 +507,11 @@ fn pass_cross_store_arg() -> anyhow::Result<()> { // Using regular `.call` fails with cross-Store arguments. assert!(store1_func - .call(&mut store1, &[Val::FuncRef(Some(store2_func.clone()))]) + .call( + &mut store1, + &[Val::FuncRef(Some(store2_func.clone()))], + &mut [] + ) .is_err()); // And using `.get` followed by a function call also fails with cross-Store @@ -553,7 +564,7 @@ fn trampolines_always_valid() -> anyhow::Result<()> { drop(module2); // ... and no segfaults! right? right? ... - func.call(&mut store, &[])?; + func.call(&mut store, &[], &mut [])?; Ok(()) } @@ -616,7 +627,7 @@ fn trap_doesnt_leak() -> anyhow::Result<()> { Err(Trap::new("")) }); assert!(f1.typed::<(), (), _>(&store)?.call(&mut store, ()).is_err()); - assert!(f1.call(&mut store, &[]).is_err()); + assert!(f1.call(&mut store, &[], &mut []).is_err()); // test that `Func::new` is correct let canary2 = Canary::default(); @@ -626,7 +637,7 @@ fn trap_doesnt_leak() -> anyhow::Result<()> { Err(Trap::new("")) }); assert!(f2.typed::<(), (), _>(&store)?.call(&mut store, ()).is_err()); - assert!(f2.call(&mut store, &[]).is_err()); + assert!(f2.call(&mut store, &[], &mut []).is_err()); // drop everything and ensure dtors are run drop(store); @@ -651,15 +662,18 @@ fn wrap_multiple_results() -> anyhow::Result<()> { + Sync, { let f = Func::wrap(&mut *store, move || t); - assert_eq!(f.typed::<(), T, _>(&store,)?.call(&mut *store, ())?, t); - assert!(t.eq_values(&f.call(&mut *store, &[])?)); + let mut results = vec![Val::I32(0); f.ty(&store).results().len()]; + assert_eq!(f.typed::<(), T, _>(&store)?.call(&mut *store, ())?, t); + f.call(&mut *store, &[], &mut results)?; + assert!(t.eq_values(&results)); let module = Module::new(store.engine(), &T::gen_wasm())?; let instance = Instance::new(&mut *store, &module, &[f.into()])?; let f = instance.get_func(&mut *store, "foo").unwrap(); assert_eq!(f.typed::<(), T, _>(&store)?.call(&mut *store, ())?, t); - assert!(t.eq_values(&f.call(&mut *store, &[])?)); + f.call(&mut *store, &[], &mut results)?; + assert!(t.eq_values(&results)); Ok(()) } @@ -820,7 +834,7 @@ fn trampoline_for_declared_elem() -> anyhow::Result<()> { let g = instance.get_typed_func::<(), Option, _>(&mut store, "g")?; let func = g.call(&mut store, ())?; - func.unwrap().call(&mut store, &[])?; + func.unwrap().call(&mut store, &[], &mut [])?; Ok(()) } diff --git a/tests/all/funcref.rs b/tests/all/funcref.rs index 676fd255e8..73dd21319d 100644 --- a/tests/all/funcref.rs +++ b/tests/all/funcref.rs @@ -20,8 +20,12 @@ fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> { // Pass in a non-null funcref. { - let results = func.call(&mut store, &[Val::FuncRef(Some(func.clone()))])?; - assert_eq!(results.len(), 1); + let mut results = [Val::I32(0)]; + func.call( + &mut store, + &[Val::FuncRef(Some(func.clone()))], + &mut results, + )?; // Can't compare `Func` for equality, so this is the best we can do here. let result_func = results[0].unwrap_funcref().unwrap(); @@ -30,9 +34,8 @@ fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> { // Pass in a null funcref. { - let results = func.call(&mut store, &[Val::FuncRef(None)])?; - assert_eq!(results.len(), 1); - + let mut results = [Val::I32(0)]; + func.call(&mut store, &[Val::FuncRef(None)], &mut results)?; let result_func = results[0].unwrap_funcref(); assert!(result_func.is_none()); } @@ -42,9 +45,11 @@ fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> { let other_instance = Instance::new(&mut store, &module, &[])?; let other_instance_func = other_instance.get_func(&mut store, "func").unwrap(); - let results = func.call( + let mut results = [Val::I32(0)]; + func.call( &mut store, &[Val::FuncRef(Some(other_instance_func.clone()))], + &mut results, )?; assert_eq!(results.len(), 1); @@ -61,7 +66,9 @@ fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> { .get_func(&mut other_store, "f") .unwrap(); - assert!(func.call(&mut store, &[Val::FuncRef(Some(f))]).is_err()); + assert!(func + .call(&mut store, &[Val::FuncRef(Some(f))], &mut [Val::I32(0)]) + .is_err()); } Ok(()) @@ -82,9 +89,8 @@ fn receive_null_funcref_from_wasm() -> anyhow::Result<()> { let instance = Instance::new(&mut store, &module, &[])?; let get_null = instance.get_func(&mut store, "get-null").unwrap(); - let results = get_null.call(&mut store, &[])?; - assert_eq!(results.len(), 1); - + let mut results = [Val::I32(0)]; + get_null.call(&mut store, &[], &mut results)?; let result_func = results[0].unwrap_funcref(); assert!(result_func.is_none()); @@ -101,7 +107,7 @@ fn wrong_store() -> anyhow::Result<()> { let set = SetOnDrop(dropped.clone()); let f1 = Func::wrap(&mut store1, move || drop(&set)); let f2 = Func::wrap(&mut store2, move || Some(f1.clone())); - assert!(f2.call(&mut store2, &[]).is_err()); + assert!(f2.call(&mut store2, &[], &mut []).is_err()); } assert!(dropped.load(SeqCst)); @@ -133,7 +139,7 @@ fn func_new_returns_wrong_store() -> anyhow::Result<()> { Ok(()) }, ); - assert!(f2.call(&mut store2, &[]).is_err()); + assert!(f2.call(&mut store2, &[], &mut [Val::I32(0)]).is_err()); } assert!(dropped.load(SeqCst)); diff --git a/tests/all/gc.rs b/tests/all/gc.rs index 8092613eb5..73ffe03ece 100644 --- a/tests/all/gc.rs +++ b/tests/all/gc.rs @@ -47,7 +47,7 @@ fn smoke_test_gc() -> anyhow::Result<()> { let r = ExternRef::new(SetFlagOnDrop(inner_dropped.clone())); { let args = [Val::I32(5), Val::ExternRef(Some(r.clone()))]; - func.call(&mut store, &args)?; + func.call(&mut store, &args, &mut [Val::I32(0)])?; } // Still held alive by the `VMExternRefActivationsTable` (potentially in @@ -88,7 +88,7 @@ fn wasm_dropping_refs() -> anyhow::Result<()> { for _ in 0..4096 { let r = ExternRef::new(CountDrops(num_refs_dropped.clone())); let args = [Val::ExternRef(Some(r))]; - drop_ref.call(&mut store, &args)?; + drop_ref.call(&mut store, &args, &mut [])?; } assert!(num_refs_dropped.load(SeqCst) > 0); @@ -163,7 +163,7 @@ fn many_live_refs() -> anyhow::Result<()> { let instance = Instance::new(&mut store, &module, &[make_ref.into(), observe_ref.into()])?; let many_live_refs = instance.get_func(&mut store, "many_live_refs").unwrap(); - many_live_refs.call(&mut store, &[])?; + many_live_refs.call(&mut store, &[], &mut [])?; store.gc(); assert_eq!(live_refs.load(SeqCst), 0); @@ -214,7 +214,7 @@ fn drop_externref_via_table_set() -> anyhow::Result<()> { { let args = vec![Val::ExternRef(Some(foo))]; - table_set.call(&mut store, &args)?; + table_set.call(&mut store, &args, &mut [])?; } store.gc(); assert!(!foo_is_dropped.load(SeqCst)); @@ -222,13 +222,13 @@ fn drop_externref_via_table_set() -> anyhow::Result<()> { { let args = vec![Val::ExternRef(Some(bar))]; - table_set.call(&mut store, &args)?; + table_set.call(&mut store, &args, &mut [])?; } store.gc(); assert!(foo_is_dropped.load(SeqCst)); assert!(!bar_is_dropped.load(SeqCst)); - table_set.call(&mut store, &[Val::ExternRef(None)])?; + table_set.call(&mut store, &[Val::ExternRef(None)], &mut [])?; assert!(foo_is_dropped.load(SeqCst)); assert!(bar_is_dropped.load(SeqCst)); diff --git a/tests/all/host_funcs.rs b/tests/all/host_funcs.rs index d88faab143..3af5aa92d6 100644 --- a/tests/all/host_funcs.rs +++ b/tests/all/host_funcs.rs @@ -279,10 +279,12 @@ fn import_works() -> Result<()> { f.as_ref().unwrap().data().downcast_ref::().unwrap(), "hello" ); - assert_eq!( - g.as_ref().unwrap().call(&mut caller, &[]).unwrap()[0].unwrap_i32(), - 42 - ); + let mut results = [Val::I32(0)]; + g.as_ref() + .unwrap() + .call(&mut caller, &[], &mut results) + .unwrap(); + assert_eq!(results[0].unwrap_i32(), 42); assert_eq!(HITS.fetch_add(1, SeqCst), 3); }, )?; @@ -299,6 +301,7 @@ fn import_works() -> Result<()> { Val::ExternRef(Some(ExternRef::new("hello".to_string()))), funcref, ], + &mut [], )?; assert_eq!(HITS.load(SeqCst), 4); @@ -360,7 +363,7 @@ fn call_import_many_args() -> Result<()> { let mut store = Store::new(&engine, ()); let instance = linker.instantiate(&mut store, &module)?; let run = instance.get_func(&mut store, "run").unwrap(); - run.call(&mut store, &[])?; + run.call(&mut store, &[], &mut [])?; Ok(()) } @@ -422,6 +425,7 @@ fn call_wasm_many_args() -> Result<()> { 9.into(), 10.into(), ], + &mut [], )?; let typed_run = instance @@ -431,7 +435,7 @@ fn call_wasm_many_args() -> Result<()> { typed_run.call(&mut store, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10))?; let test = instance.get_func(&mut store, "test").unwrap(); - test.call(&mut store, &[])?; + test.call(&mut store, &[], &mut [])?; Ok(()) } @@ -450,7 +454,10 @@ fn trap_smoke() -> Result<()> { .into_func() .unwrap(); - let err = f.call(&mut store, &[]).unwrap_err().downcast::()?; + let err = f + .call(&mut store, &[], &mut []) + .unwrap_err() + .downcast::()?; assert!(err.to_string().contains("test")); assert!(err.i32_exit_status().is_none()); @@ -524,6 +531,7 @@ fn new_from_signature() -> Result<()> { fn call_wrapped_func() -> Result<()> { let engine = Engine::default(); let mut linker = Linker::new(&engine); + let mut results = [Val::I32(0)]; linker.func_wrap("", "f1", |a: i32, b: i64, c: f32, d: f64| { assert_eq!(a, 1); @@ -550,6 +558,7 @@ fn call_wrapped_func() -> Result<()> { f.call( &mut store, &[Val::I32(1), Val::I64(2), 3.0f32.into(), 4.0f64.into()], + &mut [], )?; f.typed::<(i32, i64, f32, f64), (), _>(&store)? .call(&mut store, (1, 2, 3.0, 4.0))?; @@ -559,8 +568,7 @@ fn call_wrapped_func() -> Result<()> { .unwrap() .into_func() .unwrap(); - let results = f.call(&mut store, &[])?; - assert_eq!(results.len(), 1); + f.call(&mut store, &[], &mut results)?; assert_eq!(results[0].unwrap_i32(), 1); assert_eq!(f.typed::<(), i32, _>(&store)?.call(&mut store, ())?, 1); @@ -569,8 +577,7 @@ fn call_wrapped_func() -> Result<()> { .unwrap() .into_func() .unwrap(); - let results = f.call(&mut store, &[])?; - assert_eq!(results.len(), 1); + f.call(&mut store, &[], &mut results)?; assert_eq!(results[0].unwrap_i64(), 2); assert_eq!(f.typed::<(), i64, _>(&store)?.call(&mut store, ())?, 2); @@ -579,8 +586,7 @@ fn call_wrapped_func() -> Result<()> { .unwrap() .into_func() .unwrap(); - let results = f.call(&mut store, &[])?; - assert_eq!(results.len(), 1); + f.call(&mut store, &[], &mut results)?; assert_eq!(results[0].unwrap_f32(), 3.0); assert_eq!(f.typed::<(), f32, _>(&store)?.call(&mut store, ())?, 3.0); @@ -589,8 +595,7 @@ fn call_wrapped_func() -> Result<()> { .unwrap() .into_func() .unwrap(); - let results = f.call(&mut store, &[])?; - assert_eq!(results.len(), 1); + f.call(&mut store, &[], &mut results)?; assert_eq!(results[0].unwrap_f64(), 4.0); assert_eq!(f.typed::<(), f64, _>(&store)?.call(&mut store, ())?, 4.0); @@ -610,7 +615,10 @@ fn func_return_nothing() -> Result<()> { .unwrap() .into_func() .unwrap(); - let err = f.call(&mut store, &[]).unwrap_err().downcast::()?; + let err = f + .call(&mut store, &[], &mut [Val::I32(0)]) + .unwrap_err() + .downcast::()?; assert!(err .to_string() .contains("function attempted to return an incompatible value")); @@ -658,18 +666,17 @@ fn call_via_funcref() -> Result<()> { .unwrap() .into_func() .unwrap(); - let results = instance + let mut results = [Val::I32(0), Val::I32(0)]; + instance .get_func(&mut store, "call") .unwrap() - .call(&mut store, &[f.into()])?; - - assert_eq!(results.len(), 2); + .call(&mut store, &[f.into()], &mut results)?; assert_eq!(results[0].unwrap_i32(), 7); { let f = results[1].unwrap_funcref().unwrap(); - let results = f.call(&mut store, &[1.into(), 2.into()])?; - assert_eq!(results.len(), 1); + let mut results = [Val::I32(0)]; + f.call(&mut store, &[1.into(), 2.into()], &mut results)?; assert_eq!(results[0].unwrap_i32(), 3); } @@ -706,7 +713,7 @@ fn store_with_context() -> Result<()> { .unwrap() .into_func() .unwrap(); - f.call(&mut store, &[])?; + f.call(&mut store, &[], &mut [])?; assert!(store.data().called); diff --git a/tests/all/import_calling_export.rs b/tests/all/import_calling_export.rs index e26e545aa6..dd7342a054 100644 --- a/tests/all/import_calling_export.rs +++ b/tests/all/import_calling_export.rs @@ -24,7 +24,7 @@ fn test_import_calling_export() { caller .data() .unwrap() - .call(&mut caller, &[]) + .call(&mut caller, &[], &mut []) .expect("expected function not to trap"); Ok(()) }, @@ -44,7 +44,7 @@ fn test_import_calling_export() { *store.data_mut() = Some(other_func); run_func - .call(&mut store, &[]) + .call(&mut store, &[], &mut []) .expect("expected function not to trap"); } @@ -79,8 +79,9 @@ fn test_returns_incorrect_type() -> Result<()> { .get_func(&mut store, "run") .expect("expected a run func in the module"); + let mut result = [Val::I32(0)]; let trap = run_func - .call(&mut store, &[]) + .call(&mut store, &[], &mut result) .expect_err("the execution should fail") .downcast::()?; assert!(trap diff --git a/tests/all/invoke_func_via_table.rs b/tests/all/invoke_func_via_table.rs index ada0271881..a50d30ceee 100644 --- a/tests/all/invoke_func_via_table.rs +++ b/tests/all/invoke_func_via_table.rs @@ -26,7 +26,8 @@ fn test_invoke_func_via_table() -> Result<()> { .unwrap() .unwrap() .clone(); - let result = f.call(&mut store, &[]).unwrap(); - assert_eq!(result[0].unwrap_i64(), 42); + let mut results = [Val::I32(0)]; + f.call(&mut store, &[], &mut results).unwrap(); + assert_eq!(results[0].unwrap_i64(), 42); Ok(()) } diff --git a/tests/all/linker.rs b/tests/all/linker.rs index a5c3471278..f89adb0301 100644 --- a/tests/all/linker.rs +++ b/tests/all/linker.rs @@ -285,7 +285,7 @@ fn funcs_live_on_to_fight_another_day() -> Result<()> { assert_eq!(flag.load(SeqCst), 0); let mut store = Store::new(&engine, ()); let func = linker.get(&mut store, "", Some("")).unwrap(); - func.into_func().unwrap().call(&mut store, &[])?; + func.into_func().unwrap().call(&mut store, &[], &mut [])?; assert_eq!(flag.load(SeqCst), 0); Ok(()) }; diff --git a/tests/all/traps.rs b/tests/all/traps.rs index d2e707b13a..857c870359 100644 --- a/tests/all/traps.rs +++ b/tests/all/traps.rs @@ -329,17 +329,17 @@ fn mismatched_arguments() -> Result<()> { let instance = Instance::new(&mut store, &module, &[])?; let func = instance.get_func(&mut store, "foo").unwrap(); assert_eq!( - func.call(&mut store, &[]).unwrap_err().to_string(), + func.call(&mut store, &[], &mut []).unwrap_err().to_string(), "expected 1 arguments, got 0" ); assert_eq!( - func.call(&mut store, &[Val::F32(0)]) + func.call(&mut store, &[Val::F32(0)], &mut []) .unwrap_err() .to_string(), "argument type mismatch: found f32 but expected i32", ); assert_eq!( - func.call(&mut store, &[Val::I32(0), Val::I32(1)]) + func.call(&mut store, &[Val::I32(0), Val::I32(1)], &mut []) .unwrap_err() .to_string(), "expected 1 arguments, got 2" @@ -519,7 +519,10 @@ fn parse_dwarf_info() -> Result<()> { ); linker.module(&mut store, "", &module)?; let run = linker.get_default(&mut store, "")?; - let trap = run.call(&mut store, &[]).unwrap_err().downcast::()?; + let trap = run + .call(&mut store, &[], &mut []) + .unwrap_err() + .downcast::()?; let mut found = false; for frame in trap.trace() { diff --git a/tests/host_segfault.rs b/tests/host_segfault.rs index 57edb07bac..7a761678b8 100644 --- a/tests/host_segfault.rs +++ b/tests/host_segfault.rs @@ -139,7 +139,7 @@ fn main() { overrun_the_stack(); }) }); - run_future(f.call_async(&mut store, &[])).unwrap(); + run_future(f.call_async(&mut store, &[], &mut [])).unwrap(); unreachable!(); }, true, @@ -157,7 +157,7 @@ fn main() { overrun_the_stack(); }) }); - run_future(f.call_async(&mut store, &[])).unwrap(); + run_future(f.call_async(&mut store, &[], &mut [])).unwrap(); unreachable!(); }, true,