make ResourceLimiter operate on Store data; add hooks for entering and exiting native code (#2952)

* wasmtime_runtime: move ResourceLimiter defaults into this crate

In preparation of changing wasmtime::ResourceLimiter to be a re-export
of this definition, because translating between two traits was causing
problems elsewhere.

* wasmtime: make ResourceLimiter a re-export of wasmtime_runtime::ResourceLimiter

* refactor Store internals to support ResourceLimiter as part of store's data

* add hooks for entering and exiting native code to Store

* wasmtime-wast, fuzz: changes to adapt ResourceLimiter API

* fix tests

* wrap calls into wasm with entering/exiting exit hooks as well

* the most trivial test found a bug, lets write some more

* store: mark some methods as #[inline] on Store, StoreInner, StoreInnerMost

Co-authored-By: Alex Crichton <alex@alexcrichton.com>

* improve tests for the entering/exiting native hooks

Co-authored-by: Alex Crichton <alex@alexcrichton.com>
This commit is contained in:
Pat Hickey
2021-06-08 07:37:00 -07:00
committed by GitHub
parent ffb92d9109
commit 8b4bdf92e2
17 changed files with 550 additions and 283 deletions

View File

@@ -566,11 +566,11 @@ impl Table {
/// Panics if `store` does not own this table.
pub fn grow(&self, mut store: impl AsContextMut, delta: u32, init: Val) -> Result<u32> {
let ty = self.ty(&store).element().clone();
let mut store = store.as_context_mut().opaque();
let init = init.into_table_element(&mut store, ty)?;
let table = self.wasmtime_table(&mut store);
let init = init.into_table_element(&mut store.as_context_mut().opaque(), ty)?;
let table = self.wasmtime_table(&mut store.as_context_mut().opaque());
let store = store.as_context_mut();
unsafe {
match (*table).grow(delta, init, store.limiter()) {
match (*table).grow(delta, init, store.0.limiter()) {
Some(size) => {
let vm = (*table).vmtable();
*store[self.0].definition = vm;

View File

@@ -681,7 +681,10 @@ impl Func {
"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().opaque(), my_ty, params)
store.as_context_mut().0.exiting_native_hook()?;
let r = self.call_impl(&mut store.as_context_mut().opaque(), my_ty, params);
store.as_context_mut().0.entering_native_hook()?;
r
}
/// Invokes this function with the `params` given, returning the results
@@ -717,8 +720,12 @@ impl Func {
T: Send,
{
let my_ty = self.ty(&store);
self._call_async(store.as_context_mut().opaque_send(), my_ty, params)
.await
store.as_context_mut().0.exiting_native_hook()?;
let r = self
._call_async(store.as_context_mut().opaque_send(), my_ty, params)
.await;
store.as_context_mut().0.entering_native_hook()?;
r
}
#[cfg(feature = "async")]
@@ -843,6 +850,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 +891,7 @@ impl Func {
}
}
caller.store.0.exiting_native_hook()?;
Ok(())
}
@@ -1173,7 +1182,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 +1698,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()
}))
};

View File

@@ -71,12 +71,15 @@ where
/// This function will panic if it is called when the underlying [`Func`] is
/// connected to an asynchronous store.
pub fn call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results, Trap> {
let mut store = store.as_context_mut().opaque();
store.as_context_mut().0.exiting_native_hook()?;
let mut store_opaque = store.as_context_mut().opaque();
assert!(
!store.async_support(),
!store_opaque.async_support(),
"must use `call_async` with async stores"
);
unsafe { self._call(&mut store, params) }
let r = unsafe { self._call(&mut store_opaque, params) };
store.as_context_mut().0.entering_native_hook()?;
r
}
/// Invokes this WebAssembly function with the specified parameters.
@@ -100,14 +103,17 @@ where
where
T: Send,
{
let mut store = store.as_context_mut().opaque_send();
store.as_context_mut().0.exiting_native_hook()?;
let mut store_opaque = store.as_context_mut().opaque_send();
assert!(
store.async_support(),
store_opaque.async_support(),
"must use `call` with non-async stores"
);
store
let r = store_opaque
.on_fiber(|store| unsafe { self._call(store, params) })
.await?
.await?;
store.as_context_mut().0.entering_native_hook()?;
r
}
unsafe fn _call(&self, store: &mut StoreOpaque<'_>, params: Params) -> Result<Results, Trap> {

View File

@@ -1,97 +1,4 @@
pub(crate) const DEFAULT_INSTANCE_LIMIT: usize = 10000;
pub(crate) const DEFAULT_TABLE_LIMIT: usize = 10000;
pub(crate) const DEFAULT_MEMORY_LIMIT: usize = 10000;
/// Used by hosts to limit resource consumption of instances at runtime.
///
/// [`Store::limiter`](crate::Store::limiter) can be used
/// with a resource limiter to take into account non-WebAssembly resource
/// usage to determine if a linear memory or table should be grown.
pub trait ResourceLimiter: Send + Sync + 'static {
/// Notifies the resource limiter that an instance's linear memory has been requested to grow.
///
/// * `current` is the current size of the linear memory in WebAssembly page units.
/// * `desired` is the desired size of the linear memory in WebAssembly page units.
/// * `maximum` is either the linear memory's maximum or a maximum from an instance allocator,
/// also in WebAssembly page units. A value of `None` indicates that the linear memory is
/// unbounded.
///
/// This function should return `true` to indicate that the growing operation is permitted or
/// `false` if not permitted.
///
/// Note that this function will be called even when the desired count exceeds the given maximum.
///
/// Returning `true` when a maximum has been exceeded will have no effect as the linear memory
/// will not be grown.
fn memory_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> bool;
/// Notifies the resource limiter that an instance's table has been requested to grow.
///
/// * `current` is the current number of elements in the table.
/// * `desired` is the desired number of elements in the table.
/// * `maximum` is either the table's maximum or a maximum from an instance allocator,
/// A value of `None` indicates that the table is unbounded.
///
/// This function should return `true` to indicate that the growing operation is permitted or
/// `false` if not permitted.
///
/// Note that this function will be called even when the desired count exceeds the given maximum.
///
/// Returning `true` when a maximum has been exceeded will have no effect as the table will
/// not be grown.
fn table_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> bool;
/// The maximum number of instances that can be created for a [`Store`](crate::Store).
///
/// Module instantiation will fail if this limit is exceeded.
///
/// This value defaults to 10,000.
fn instances(&self) -> usize {
DEFAULT_INSTANCE_LIMIT
}
/// The maximum number of tables that can be created for a [`Store`](crate::Store).
///
/// Module instantiation will fail if this limit is exceeded.
///
/// This value defaults to 10,000.
fn tables(&self) -> usize {
DEFAULT_TABLE_LIMIT
}
/// The maximum number of linear memories that can be created for a [`Store`](crate::Store).
///
/// Instantiation will fail with an error if this limit is exceeded.
///
/// This value defaults to 10,000.
fn memories(&self) -> usize {
DEFAULT_MEMORY_LIMIT
}
}
pub(crate) struct ResourceLimiterProxy<T>(pub T);
impl<T: ResourceLimiter> wasmtime_runtime::ResourceLimiter for ResourceLimiterProxy<T> {
fn memory_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> bool {
self.0.memory_growing(current, desired, maximum)
}
fn table_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> bool {
self.0.table_growing(current, desired, maximum)
}
fn instances(&self) -> usize {
self.0.instances()
}
fn tables(&self) -> usize {
self.0.tables()
}
fn memories(&self) -> usize {
self.0.memories()
}
}
pub use wasmtime_runtime::ResourceLimiter;
/// Used to build [`StoreLimits`].
pub struct StoreLimitsBuilder(StoreLimits);
@@ -172,9 +79,9 @@ impl Default for StoreLimits {
Self {
memory_pages: None,
table_elements: None,
instances: DEFAULT_INSTANCE_LIMIT,
tables: DEFAULT_TABLE_LIMIT,
memories: DEFAULT_MEMORY_LIMIT,
instances: wasmtime_runtime::DEFAULT_INSTANCE_LIMIT,
tables: wasmtime_runtime::DEFAULT_TABLE_LIMIT,
memories: wasmtime_runtime::DEFAULT_MEMORY_LIMIT,
}
}
}

View File

@@ -416,10 +416,10 @@ impl Memory {
/// # }
/// ```
pub fn grow(&self, mut store: impl AsContextMut, delta: u32) -> Result<u32> {
let mut store = store.as_context_mut().opaque();
let mem = self.wasmtime_memory(&mut store);
let mem = self.wasmtime_memory(&mut store.as_context_mut().opaque());
let store = store.as_context_mut();
unsafe {
match (*mem).grow(delta, store.limiter()) {
match (*mem).grow(delta, store.0.limiter()) {
Some(size) => {
let vm = (*mem).vmmemory();
*store[self.0].definition = vm;

View File

@@ -8,6 +8,7 @@ use std::fmt;
use std::future::Future;
use std::marker;
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::ptr;
use std::sync::Arc;
@@ -78,7 +79,7 @@ pub struct Store<T> {
inner: ManuallyDrop<Box<StoreInner<T>>>,
}
pub struct StoreInner<T: ?Sized> {
pub struct StoreInner<T> {
// This `StoreInner<T>` structure has references to itself. These aren't
// immediately evident, however, so we need to tell the compiler that it
// contains self-references. This notably suppresses `noalias` annotations
@@ -101,7 +102,30 @@ pub struct StoreInner<T: ?Sized> {
// least telling the compiler something about all the aliasing happening
// within a `Store`.
_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>,
}
impl<T> Deref for StoreInner<T> {
type Target = StoreInnermost;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> DerefMut for StoreInner<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
// I apologize for the convoluted structure and the terrible naming of this struct.
// This exists so that most of wasmtime can be monomorphic on StoreInnermost, without
// having to care about the generic in StoreInner<T>.
pub struct StoreInnermost {
engine: Engine,
interrupts: Arc<VMInterrupts>,
instances: Vec<StoreInstance>,
@@ -109,10 +133,13 @@ pub struct StoreInner<T: ?Sized> {
externref_activations_table: VMExternRefActivationsTable,
modules: ModuleRegistry,
host_trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
// Numbers of resources instantiated in this store.
// Numbers of resources instantiated in this store, and their limits
instance_count: usize,
instance_limit: usize,
memory_count: usize,
memory_limit: usize,
table_count: usize,
table_limit: usize,
/// An adjustment to add to the fuel consumed value in `interrupts` above
/// to get the true amount of fuel consumed.
fuel_adj: i64,
@@ -120,10 +147,7 @@ pub struct StoreInner<T: ?Sized> {
async_state: AsyncState,
out_of_gas_behavior: OutOfGas,
store_data: StoreData,
limiter: Option<Box<dyn wasmtime_runtime::ResourceLimiter>>,
default_callee: InstanceHandle,
// for comments about `ManuallyDrop`, see `Store::into_data`
data: ManuallyDrop<T>,
}
#[cfg(feature = "async")]
@@ -192,26 +216,33 @@ impl<T> Store<T> {
};
let mut inner = Box::new(StoreInner {
_marker: marker::PhantomPinned,
engine: engine.clone(),
interrupts: Default::default(),
instances: Vec::new(),
signal_handler: None,
externref_activations_table: VMExternRefActivationsTable::new(),
modules: ModuleRegistry::default(),
host_trampolines: HashMap::default(),
instance_count: 0,
memory_count: 0,
table_count: 0,
fuel_adj: 0,
#[cfg(feature = "async")]
async_state: AsyncState {
current_suspend: UnsafeCell::new(ptr::null()),
current_poll_cx: UnsafeCell::new(ptr::null_mut()),
inner: StoreInnermost {
engine: engine.clone(),
interrupts: Default::default(),
instances: Vec::new(),
signal_handler: None,
externref_activations_table: VMExternRefActivationsTable::new(),
modules: ModuleRegistry::default(),
host_trampolines: HashMap::default(),
instance_count: 0,
instance_limit: wasmtime_runtime::DEFAULT_INSTANCE_LIMIT,
memory_count: 0,
memory_limit: wasmtime_runtime::DEFAULT_MEMORY_LIMIT,
table_count: 0,
table_limit: wasmtime_runtime::DEFAULT_TABLE_LIMIT,
fuel_adj: 0,
#[cfg(feature = "async")]
async_state: AsyncState {
current_suspend: UnsafeCell::new(ptr::null()),
current_poll_cx: UnsafeCell::new(ptr::null_mut()),
},
out_of_gas_behavior: OutOfGas::Trap,
store_data: StoreData::new(),
default_callee,
},
out_of_gas_behavior: OutOfGas::Trap,
store_data: StoreData::new(),
limiter: None,
default_callee,
entering_native_hook: None,
exiting_native_hook: None,
data: ManuallyDrop::new(data),
});
@@ -227,11 +258,13 @@ impl<T> Store<T> {
}
/// Access the underlying data owned by this `Store`.
#[inline]
pub fn data(&self) -> &T {
self.inner.data()
}
/// Access the underlying data owned by this `Store`.
#[inline]
pub fn data_mut(&mut self) -> &mut T {
self.inner.data_mut()
}
@@ -275,8 +308,45 @@ impl<T> Store<T> {
/// Note that this limiter is only used to limit the creation/growth of
/// resources in the future, this does not retroactively attempt to apply
/// limits to the [`Store`].
pub fn limiter(&mut self, limiter: impl crate::ResourceLimiter) {
self.inner.limiter = Some(Box::new(crate::limits::ResourceLimiterProxy(limiter)));
pub fn limiter(
&mut self,
mut limiter: impl FnMut(&mut T) -> &mut (dyn crate::ResourceLimiter) + Send + Sync + 'static,
) {
// Apply the limits on instances, tables, and memory given by the limiter:
let inner = &mut self.inner;
let (instance_limit, table_limit, memory_limit) = {
let l = limiter(&mut inner.data);
(l.instances(), l.tables(), l.memories())
};
let innermost = &mut inner.inner;
innermost.instance_limit = instance_limit;
innermost.table_limit = table_limit;
innermost.memory_limit = memory_limit;
// Save the limiter accessor function:
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.
@@ -565,27 +635,79 @@ impl<'a, T> StoreContextMut<'a, T> {
}
}
impl<T: ?Sized> StoreInner<T> {
impl<T> StoreInner<T> {
#[inline]
fn data(&self) -> &T {
&self.data
}
#[inline]
fn data_mut(&mut self) -> &mut T {
&mut self.data
}
pub fn limiter(&mut self) -> Option<&mut dyn crate::limits::ResourceLimiter> {
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 {
pub fn bump_resource_counts(&mut self, module: &Module) -> Result<()> {
fn bump(slot: &mut usize, max: usize, amt: usize, desc: &str) -> Result<()> {
let new = slot.saturating_add(amt);
if new > max {
bail!(
"resource limit exceeded: {} count too high at {}",
desc,
new
);
}
*slot = new;
Ok(())
}
let module = module.env_module();
let memories = module.memory_plans.len() - module.num_imported_memories;
let tables = module.table_plans.len() - module.num_imported_tables;
bump(&mut self.instance_count, self.instance_limit, 1, "instance")?;
bump(
&mut self.memory_count,
self.memory_limit,
memories,
"memory",
)?;
bump(&mut self.table_count, self.table_limit, tables, "table")?;
Ok(())
}
#[inline]
pub fn async_support(&self) -> bool {
cfg!(feature = "async") && self.engine().config().async_support
}
#[inline]
pub fn engine(&self) -> &Engine {
&self.engine
}
pub fn limiter(&mut self) -> Option<&mut dyn wasmtime_runtime::ResourceLimiter> {
self.limiter.as_mut().map(|l| &mut **l)
}
pub fn store_data(&self) -> &StoreData {
&self.store_data
}
@@ -654,43 +776,6 @@ impl<T: ?Sized> StoreInner<T> {
unsafe { wasmtime_runtime::gc(&self.modules, &mut self.externref_activations_table) }
}
pub fn bump_resource_counts(&mut self, module: &Module) -> Result<()> {
fn bump(slot: &mut usize, max: usize, amt: usize, desc: &str) -> Result<()> {
let new = slot.saturating_add(amt);
if new > max {
bail!(
"resource limit exceeded: {} count too high at {}",
desc,
new
);
}
*slot = new;
Ok(())
}
let module = module.env_module();
let memories = module.memory_plans.len() - module.num_imported_memories;
let tables = module.table_plans.len() - module.num_imported_tables;
let (max_instances, max_memories, max_tables) = self.limits();
bump(&mut self.instance_count, max_instances, 1, "instance")?;
bump(&mut self.memory_count, max_memories, memories, "memory")?;
bump(&mut self.table_count, max_tables, tables, "table")?;
Ok(())
}
fn limits(&self) -> (usize, usize, usize) {
self.limiter
.as_ref()
.map(|l| (l.instances(), l.memories(), l.tables()))
.unwrap_or((
crate::limits::DEFAULT_INSTANCE_LIMIT,
crate::limits::DEFAULT_MEMORY_LIMIT,
crate::limits::DEFAULT_TABLE_LIMIT,
))
}
pub fn lookup_trampoline(&self, anyfunc: &VMCallerCheckedAnyfunc) -> VMTrampoline {
// Look up the trampoline with the store's trampolines (from `Func`).
if let Some(trampoline) = self.host_trampolines.get(&anyfunc.type_index) {
@@ -706,6 +791,7 @@ impl<T: ?Sized> StoreInner<T> {
}
#[cfg(feature = "async")]
#[inline]
pub fn async_cx(&self) -> AsyncCx {
debug_assert!(self.async_support());
AsyncCx {
@@ -817,11 +903,13 @@ impl<T: ?Sized> StoreInner<T> {
Ok(())
}
#[inline]
pub fn signal_handler(&self) -> Option<*const SignalHandler<'static>> {
let handler = self.signal_handler.as_ref()?;
Some(&**handler as *const _)
}
#[inline]
pub fn vminterrupts(&self) -> *mut VMInterrupts {
&*self.interrupts as *const VMInterrupts as *mut VMInterrupts
}
@@ -831,6 +919,7 @@ impl<T: ?Sized> StoreInner<T> {
.insert_with_gc(r, &self.modules)
}
#[inline]
pub fn default_callee(&self) -> *mut VMContext {
self.default_callee.vmctx_ptr()
}
@@ -1121,7 +1210,7 @@ impl AsyncCx {
unsafe impl<T> wasmtime_runtime::Store for StoreInner<T> {
fn vminterrupts(&self) -> *mut VMInterrupts {
<StoreInner<T>>::vminterrupts(self)
<StoreInnermost>::vminterrupts(self)
}
fn externref_activations_table(
@@ -1130,11 +1219,12 @@ unsafe impl<T> wasmtime_runtime::Store for StoreInner<T> {
&mut VMExternRefActivationsTable,
&dyn wasmtime_runtime::ModuleInfoLookup,
) {
(&mut self.externref_activations_table, &self.modules)
let inner = &mut self.inner;
(&mut inner.externref_activations_table, &inner.modules)
}
fn limiter(&mut self) -> Option<&mut dyn wasmtime_runtime::ResourceLimiter> {
self.limiter.as_mut().map(|l| &mut **l)
<Self>::limiter(self)
}
fn out_of_gas(&mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
@@ -1196,7 +1286,7 @@ impl<T> Drop for Store<T> {
}
}
impl<T: ?Sized> Drop for StoreInner<T> {
impl Drop for StoreInnermost {
fn drop(&mut self) {
// NB it's important that this destructor does not access `self.data`.
// That is deallocated by `Drop for Store<T>` above.

View File

@@ -1,4 +1,4 @@
use crate::store::{Store, StoreInner};
use crate::store::{Store, StoreInner, StoreInnermost};
use std::ops::{Deref, DerefMut};
/// A temporary handle to a [`&Store<T>`][`Store`].
@@ -17,7 +17,7 @@ pub struct StoreContext<'a, T>(pub(super) &'a StoreInner<T>);
/// methods if desired. For more information, see [`Store`].
// NB the repr(transparent) here is for the same reason as above.
#[repr(transparent)]
pub struct StoreContextMut<'a, T>(pub(super) &'a mut StoreInner<T>);
pub struct StoreContextMut<'a, T>(pub(crate) &'a mut StoreInner<T>);
impl<'a, T> StoreContextMut<'a, T> {
/// One of the unsafe lynchpins of Wasmtime.
@@ -210,7 +210,7 @@ impl<'a, T: AsContextMut> From<&'a mut T> for StoreContextMut<'a, T::Data> {
#[doc(hidden)] // this is part of `WasmTy`, but a hidden part, so hide this
pub struct StoreOpaque<'a> {
/// The actual pointer to the `StoreInner` internals.
inner: &'a mut StoreInner<dyn Opaque + 'a>,
inner: &'a mut StoreInnermost,
/// A raw trait object that can be used to invoke functions with. Note that
/// this is a pointer which aliases with `inner` above, so extreme care
@@ -224,7 +224,7 @@ impl<T> Opaque for T {}
// Deref impls to forward all methods on `StoreOpaque` to `StoreInner`.
impl<'a> Deref for StoreOpaque<'a> {
type Target = StoreInner<dyn Opaque + 'a>;
type Target = StoreInnermost;
#[inline]
fn deref(&self) -> &Self::Target {
@@ -241,7 +241,7 @@ impl<'a> DerefMut for StoreOpaque<'a> {
pub struct StoreOpaqueSend<'a> {
/// The actual pointer to the `StoreInner` internals.
inner: &'a mut StoreInner<dyn Opaque + Send + 'a>,
inner: &'a mut StoreInnermost,
pub traitobj: *mut dyn wasmtime_runtime::Store,
}
@@ -259,7 +259,7 @@ impl StoreOpaqueSend<'_> {
}
impl<'a> Deref for StoreOpaqueSend<'a> {
type Target = StoreInner<dyn Opaque + Send + 'a>;
type Target = StoreInnermost;
#[inline]
fn deref(&self) -> &Self::Target {