From f9e8c8ee03a6ab8fa5ebbaa0a66c3d0b7a9e61d9 Mon Sep 17 00:00:00 2001
From: Pat Hickey <pat@moreproductive.org>
Date: Fri, 28 May 2021 16:41:57 -0700
Subject: [PATCH] add hooks for entering and exiting native code to Store

---
 crates/wasmtime/src/func.rs  | 15 ++++++++++---
 crates/wasmtime/src/store.rs | 43 +++++++++++++++++++++++++++++++++++-
 2 files changed, 54 insertions(+), 4 deletions(-)

diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs
index 8e6cafac959b..aec521d7267e 100644
--- a/crates/wasmtime/src/func.rs
+++ b/crates/wasmtime/src/func.rs
@@ -843,6 +843,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()?;
         // We have a dynamic guarantee that `values_vec` has the right
         // number of arguments and the right types of arguments. As a result
         // we should be able to safely run through them all and read them.
@@ -883,6 +884,7 @@ impl Func {
             }
         }
 
+        caller.store.0.exiting_native_hook()?;
         Ok(())
     }
 
@@ -1173,7 +1175,7 @@ pub unsafe trait WasmRet {
     // explicitly, used when wrapping async functions which always bottom-out
     // in a function that returns a trap because futures can be cancelled.
     #[doc(hidden)]
-    type Fallible: WasmRet;
+    type Fallible: WasmRet<Abi = Self::Abi, Retptr = Self::Retptr>;
     #[doc(hidden)]
     fn into_fallible(self) -> Self::Fallible;
     #[doc(hidden)]
@@ -1689,12 +1691,19 @@ macro_rules! impl_into_func {
 
                         let ret = {
                             panic::catch_unwind(AssertUnwindSafe(|| {
+                                if let Err(trap) = caller.store.0.entering_native_hook() {
+                                    return R::fallible_from_trap(trap);
+                                }
                                 let mut _store = caller.sub_caller().store.opaque();
                                 $(let $args = $args::from_abi($args, &mut _store);)*
-                                func(
+                                let r = func(
                                     caller.sub_caller(),
                                     $( $args, )*
-                                )
+                                );
+                                if let Err(trap) = caller.store.0.exiting_native_hook() {
+                                    return R::fallible_from_trap(trap);
+                                }
+                                r.into_fallible()
                             }))
                         };
 
diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs
index 5520d463c713..367aba0d19e8 100644
--- a/crates/wasmtime/src/store.rs
+++ b/crates/wasmtime/src/store.rs
@@ -104,6 +104,8 @@ pub struct StoreInner<T> {
     _marker: marker::PhantomPinned,
     inner: StoreInnermost,
     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>>,
     // for comments about `ManuallyDrop`, see `Store::into_data`
     data: ManuallyDrop<T>,
 }
@@ -239,7 +241,8 @@ impl<T> Store<T> {
                 default_callee,
             },
             limiter: None,
-            default_callee,
+            entering_native_hook: None,
+            exiting_native_hook: None,
             data: ManuallyDrop::new(data),
         });
 
@@ -322,6 +325,28 @@ impl<T> Store<T> {
         inner.limiter = Some(Box::new(limiter));
     }
 
+    /// Configure a function that runs each time WebAssembly code running on this [`Store`] calls
+    /// into native code.
+    ///
+    /// This function may return a [`Trap`], which terminates execution.
+    pub fn entering_native_code_hook(
+        &mut self,
+        hook: impl FnMut(&mut T) -> Result<(), Trap> + Send + Sync + 'static,
+    ) {
+        self.inner.entering_native_hook = Some(Box::new(hook));
+    }
+
+    /// Configure a function that runs before native code running on this [`Store`] returns to
+    /// WebAssembly code.
+    ///
+    /// This function may return a [`Trap`], which terminates execution.
+    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));
+    }
+
     /// Returns the [`Engine`] that this store is associated with.
     pub fn engine(&self) -> &Engine {
         self.inner.engine()
@@ -621,6 +646,22 @@ impl<T> StoreInner<T> {
         let accessor = self.limiter.as_mut()?;
         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)
+        } else {
+            Ok(())
+        }
+    }
 }
 
 impl StoreInnermost {