rewrite Store::{entering,exiting}_native_code_hook into Store::call_hook (#3313)
which provides additional detail on what state transition is being made
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use crate::store::{StoreData, StoreOpaque, Stored};
|
||||
use crate::{
|
||||
AsContext, AsContextMut, Engine, Extern, FuncType, Instance, InterruptHandle, StoreContext,
|
||||
StoreContextMut, Trap, Val, ValType,
|
||||
AsContext, AsContextMut, CallHook, Engine, Extern, FuncType, Instance, InterruptHandle,
|
||||
StoreContext, StoreContextMut, Trap, Val, ValType,
|
||||
};
|
||||
use anyhow::{bail, Context as _, Result};
|
||||
use std::cmp::max;
|
||||
@@ -845,7 +845,7 @@ impl Func {
|
||||
values_vec: *mut u128,
|
||||
func: &dyn Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<(), Trap>,
|
||||
) -> Result<(), Trap> {
|
||||
caller.store.0.entering_native_hook()?;
|
||||
caller.store.0.call_hook(CallHook::CallingHost)?;
|
||||
|
||||
// Translate the raw JIT arguments in `values_vec` into a `Val` which
|
||||
// we'll be passing as a slice. The storage for our slice-of-`Val` we'll
|
||||
@@ -895,7 +895,7 @@ impl Func {
|
||||
// hostcall to reuse our own storage.
|
||||
val_vec.truncate(0);
|
||||
caller.store.0.save_hostcall_val_storage(val_vec);
|
||||
caller.store.0.exiting_native_hook()?;
|
||||
caller.store.0.call_hook(CallHook::ReturningFromHost)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1044,7 +1044,7 @@ pub(crate) fn invoke_wasm_and_catch_traps<T>(
|
||||
unsafe {
|
||||
let exit = enter_wasm(store)?;
|
||||
|
||||
if let Err(trap) = store.0.exiting_native_hook() {
|
||||
if let Err(trap) = store.0.call_hook(CallHook::CallingWasm) {
|
||||
exit_wasm(store, exit);
|
||||
return Err(trap);
|
||||
}
|
||||
@@ -1055,7 +1055,7 @@ pub(crate) fn invoke_wasm_and_catch_traps<T>(
|
||||
closure,
|
||||
);
|
||||
exit_wasm(store, exit);
|
||||
store.0.entering_native_hook()?;
|
||||
store.0.call_hook(CallHook::ReturningFromWasm)?;
|
||||
result.map_err(Trap::from_runtime_box)
|
||||
}
|
||||
}
|
||||
@@ -1741,7 +1741,7 @@ macro_rules! impl_into_func {
|
||||
|
||||
let ret = {
|
||||
panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
if let Err(trap) = caller.store.0.entering_native_hook() {
|
||||
if let Err(trap) = caller.store.0.call_hook(CallHook::CallingHost) {
|
||||
return R::fallible_from_trap(trap);
|
||||
}
|
||||
$(let $args = $args::from_abi($args, caller.store.0);)*
|
||||
@@ -1749,7 +1749,7 @@ macro_rules! impl_into_func {
|
||||
caller.sub_caller(),
|
||||
$( $args, )*
|
||||
);
|
||||
if let Err(trap) = caller.store.0.exiting_native_hook() {
|
||||
if let Err(trap) = caller.store.0.call_hook(CallHook::ReturningFromHost) {
|
||||
return R::fallible_from_trap(trap);
|
||||
}
|
||||
r.into_fallible()
|
||||
|
||||
@@ -401,7 +401,7 @@ pub use crate::memory::*;
|
||||
pub use crate::module::{FrameInfo, FrameSymbol, Module};
|
||||
pub use crate::r#ref::ExternRef;
|
||||
pub use crate::store::{
|
||||
AsContext, AsContextMut, InterruptHandle, Store, StoreContext, StoreContextMut,
|
||||
AsContext, AsContextMut, CallHook, InterruptHandle, Store, StoreContext, StoreContextMut,
|
||||
};
|
||||
pub use crate::trap::*;
|
||||
pub use crate::types::*;
|
||||
|
||||
@@ -157,6 +157,37 @@ pub struct Store<T> {
|
||||
inner: ManuallyDrop<Box<StoreInner<T>>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
/// Passed to the argument of [`Store::call_hook`] to indicate a state transition in
|
||||
/// the WebAssembly VM.
|
||||
pub enum CallHook {
|
||||
/// Indicates the VM is calling a WebAssembly function, from the host.
|
||||
CallingWasm,
|
||||
/// Indicates the VM is returning from a WebAssembly function, to the host.
|
||||
ReturningFromWasm,
|
||||
/// Indicates the VM is calling a host function, from WebAssembly.
|
||||
CallingHost,
|
||||
/// Indicates the VM is returning from a host function, to WebAssembly.
|
||||
ReturningFromHost,
|
||||
}
|
||||
|
||||
impl CallHook {
|
||||
/// Indicates the VM is entering host code (exiting WebAssembly code)
|
||||
pub fn entering_host(&self) -> bool {
|
||||
match self {
|
||||
CallHook::ReturningFromWasm | CallHook::CallingHost => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
/// Indicates the VM is exiting host code (entering WebAssembly code)
|
||||
pub fn exiting_host(&self) -> bool {
|
||||
match self {
|
||||
CallHook::ReturningFromHost | CallHook::CallingWasm => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal contents of a `Store<T>` that live on the heap.
|
||||
///
|
||||
/// The members of this struct are those that need to be generic over `T`, the
|
||||
@@ -167,8 +198,7 @@ pub struct StoreInner<T> {
|
||||
inner: StoreOpaque,
|
||||
|
||||
limiter: Option<Box<dyn FnMut(&mut T) -> &mut (dyn crate::ResourceLimiter) + Send + Sync>>,
|
||||
entering_native_hook: Option<Box<dyn FnMut(&mut T) -> Result<(), crate::Trap> + Send + Sync>>,
|
||||
exiting_native_hook: Option<Box<dyn FnMut(&mut T) -> Result<(), crate::Trap> + Send + Sync>>,
|
||||
call_hook: Option<Box<dyn FnMut(&mut T, CallHook) -> Result<(), crate::Trap> + Send + Sync>>,
|
||||
// for comments about `ManuallyDrop`, see `Store::into_data`
|
||||
data: ManuallyDrop<T>,
|
||||
}
|
||||
@@ -340,8 +370,7 @@ impl<T> Store<T> {
|
||||
hostcall_val_storage: Vec::new(),
|
||||
},
|
||||
limiter: None,
|
||||
entering_native_hook: None,
|
||||
exiting_native_hook: None,
|
||||
call_hook: None,
|
||||
data: ManuallyDrop::new(data),
|
||||
});
|
||||
|
||||
@@ -433,56 +462,25 @@ impl<T> Store<T> {
|
||||
inner.limiter = Some(Box::new(limiter));
|
||||
}
|
||||
|
||||
/// Configure a function that runs each time the host resumes execution from
|
||||
/// WebAssembly.
|
||||
/// Configure a function that runs on calls and returns between WebAssembly
|
||||
/// and host code.
|
||||
///
|
||||
/// This hook is called in two circumstances:
|
||||
///
|
||||
/// * When WebAssembly calls a function defined by the host, this hook is
|
||||
/// called before other host code runs.
|
||||
/// * When WebAssembly returns back to the host after being called, this
|
||||
/// hook is called.
|
||||
///
|
||||
/// This method can be used with [`Store::exiting_native_code_hook`] to track
|
||||
/// execution time of WebAssembly, for example, by starting/stopping timers
|
||||
/// in the enter/exit hooks.
|
||||
/// The function is passed a [`CallHook`] argument, which indicates which
|
||||
/// state transition the VM is making.
|
||||
///
|
||||
/// This function may return a [`Trap`]. If a trap is returned when an
|
||||
/// import was called, it is immediately raised as-if the host import had
|
||||
/// returned the trap. If a trap is returned after wasm returns to the host
|
||||
/// then the wasm function's result is ignored and this trap is returned
|
||||
/// instead.
|
||||
pub fn entering_native_code_hook(
|
||||
///
|
||||
/// After this function returns a trap, it may be called for subsequent returns
|
||||
/// to host or wasm code as the trap propogates to the root call.
|
||||
pub fn call_hook(
|
||||
&mut self,
|
||||
hook: impl FnMut(&mut T) -> Result<(), Trap> + Send + Sync + 'static,
|
||||
hook: impl FnMut(&mut T, CallHook) -> Result<(), Trap> + Send + Sync + 'static,
|
||||
) {
|
||||
self.inner.entering_native_hook = Some(Box::new(hook));
|
||||
}
|
||||
|
||||
/// Configure a function that runs just before WebAssembly code starts
|
||||
/// executing.
|
||||
///
|
||||
/// The closure provided is called in two circumstances:
|
||||
///
|
||||
/// * When the host calls a WebAssembly function, the hook is called just
|
||||
/// before WebAssembly starts executing.
|
||||
/// * When a host function returns back to WebAssembly this hook is called
|
||||
/// just before the return.
|
||||
///
|
||||
/// This method can be used with [`Store::entering_native_code_hook`] to track
|
||||
/// execution time of WebAssembly, for example, by starting/stopping timers
|
||||
/// in the enter/exit hooks.
|
||||
///
|
||||
/// This function may return a [`Trap`]. If a trap is returned when an
|
||||
/// imported host function is returning, then the imported host function's
|
||||
/// result is ignored and the trap is raised. If a trap is returned when
|
||||
/// the host is about to start executing WebAssembly, then no WebAssembly
|
||||
/// code is run and the trap is returned instead.
|
||||
pub fn exiting_native_code_hook(
|
||||
&mut self,
|
||||
hook: impl FnMut(&mut T) -> Result<(), Trap> + Send + Sync + 'static,
|
||||
) {
|
||||
self.inner.exiting_native_hook = Some(Box::new(hook));
|
||||
self.inner.call_hook = Some(Box::new(hook));
|
||||
}
|
||||
|
||||
/// Returns the [`Engine`] that this store is associated with.
|
||||
@@ -783,17 +781,9 @@ impl<T> StoreInner<T> {
|
||||
Some(accessor(&mut self.data))
|
||||
}
|
||||
|
||||
pub fn entering_native_hook(&mut self) -> Result<(), Trap> {
|
||||
if let Some(hook) = &mut self.entering_native_hook {
|
||||
hook(&mut self.data)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exiting_native_hook(&mut self) -> Result<(), Trap> {
|
||||
if let Some(hook) = &mut self.exiting_native_hook {
|
||||
hook(&mut self.data)
|
||||
pub fn call_hook(&mut self, s: CallHook) -> Result<(), Trap> {
|
||||
if let Some(hook) = &mut self.call_hook {
|
||||
hook(&mut self.data, s)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user