use std::fmt;
use wasmer_types::OnCalledAction;
pub type OnCalledHandler = Box<
dyn FnOnce(StoreMut<'_>) -> Result<OnCalledAction, Box<dyn std::error::Error + Send + Sync>>,
>;
pub(crate) struct StoreInner {
pub(crate) objects: StoreObjects,
pub(crate) on_called: Option<OnCalledHandler>,
}
pub struct Store {
pub(crate) inner: Box<StoreInner>,
}
impl Store {
pub fn new() -> Self {
Self {
inner: Box::new(StoreInner {
objects: Default::default(),
on_called: None,
}),
}
}
pub fn same(_a: &Self, _b: &Self) -> bool {
true
}
pub fn id(&self) -> StoreId {
self.inner.objects.id()
}
}
impl PartialEq for Store {
fn eq(&self, other: &Self) -> bool {
Self::same(self, other)
}
}
unsafe impl Send for Store {}
unsafe impl Sync for Store {}
impl Default for Store {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for Store {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Store").finish()
}
}
pub trait StoreObject {
fn comes_from_same_store(&self, _store: &Store) -> bool {
true
}
}
impl AsStoreRef for Store {
fn as_store_ref(&self) -> StoreRef<'_> {
StoreRef { inner: &self.inner }
}
}
impl AsStoreMut for Store {
fn as_store_mut(&mut self) -> StoreMut<'_> {
StoreMut {
inner: &mut self.inner,
}
}
fn objects_mut(&mut self) -> &mut StoreObjects {
&mut self.inner.objects
}
}
pub struct StoreRef<'a> {
pub(crate) inner: &'a StoreInner,
}
impl<'a> StoreRef<'a> {
pub(crate) fn objects(&self) -> &'a StoreObjects {
&self.inner.objects
}
pub fn same(a: &Self, b: &Self) -> bool {
a.inner.objects.id() == b.inner.objects.id()
}
}
pub struct StoreMut<'a> {
pub(crate) inner: &'a mut StoreInner,
}
impl<'a> StoreMut<'a> {
pub fn same(a: &Self, b: &Self) -> bool {
a.inner.objects.id() == b.inner.objects.id()
}
pub(crate) fn as_raw(&self) -> *mut StoreInner {
self.inner as *const StoreInner as *mut StoreInner
}
pub(crate) unsafe fn from_raw(raw: *mut StoreInner) -> Self {
Self { inner: &mut *raw }
}
pub fn on_called<F>(&mut self, callback: F)
where
F: FnOnce(StoreMut<'_>) -> Result<OnCalledAction, Box<dyn std::error::Error + Send + Sync>>
+ Send
+ Sync
+ 'static,
{
self.inner.on_called.replace(Box::new(callback));
}
}
pub trait AsStoreRef {
fn as_store_ref(&self) -> StoreRef<'_>;
}
pub trait AsStoreMut: AsStoreRef {
fn as_store_mut(&mut self) -> StoreMut<'_>;
fn objects_mut(&mut self) -> &mut StoreObjects;
}
impl AsStoreRef for StoreRef<'_> {
fn as_store_ref(&self) -> StoreRef<'_> {
StoreRef { inner: self.inner }
}
}
impl AsStoreRef for StoreMut<'_> {
fn as_store_ref(&self) -> StoreRef<'_> {
StoreRef { inner: self.inner }
}
}
impl AsStoreMut for StoreMut<'_> {
fn as_store_mut(&mut self) -> StoreMut<'_> {
StoreMut { inner: self.inner }
}
fn objects_mut(&mut self) -> &mut StoreObjects {
&mut self.inner.objects
}
}
impl<T: AsStoreRef> AsStoreRef for &'_ T {
fn as_store_ref(&self) -> StoreRef<'_> {
T::as_store_ref(*self)
}
}
impl<T: AsStoreRef> AsStoreRef for &'_ mut T {
fn as_store_ref(&self) -> StoreRef<'_> {
T::as_store_ref(*self)
}
}
impl<T: AsStoreMut> AsStoreMut for &'_ mut T {
fn as_store_mut(&mut self) -> StoreMut<'_> {
T::as_store_mut(*self)
}
fn objects_mut(&mut self) -> &mut StoreObjects {
T::objects_mut(*self)
}
}
pub use objects::*;
mod objects {
use wasm_bindgen::JsValue;
use crate::js::{
export::{VMFunction, VMGlobal, VMMemory, VMTable},
function_env::VMFunctionEnvironment,
};
use std::{
cell::UnsafeCell,
fmt,
marker::PhantomData,
num::{NonZeroU64, NonZeroUsize},
ptr::NonNull,
sync::atomic::{AtomicU64, Ordering},
};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct StoreId(NonZeroU64);
impl Default for StoreId {
fn default() -> Self {
static NEXT_ID: AtomicU64 = AtomicU64::new(1);
Self(NonZeroU64::new(NEXT_ID.fetch_add(1, Ordering::Relaxed)).unwrap())
}
}
pub trait StoreObject: Sized {
fn list(store: &StoreObjects) -> &Vec<Self>;
fn list_mut(store: &mut StoreObjects) -> &mut Vec<Self>;
}
macro_rules! impl_store_object {
($($field:ident => $ty:ty,)*) => {
$(
impl StoreObject for $ty {
fn list(store: &StoreObjects) -> &Vec<Self> {
&store.$field
}
fn list_mut(store: &mut StoreObjects) -> &mut Vec<Self> {
&mut store.$field
}
}
)*
};
}
impl_store_object! {
functions => VMFunction,
tables => VMTable,
globals => VMGlobal,
memories => VMMemory,
instances => js_sys::WebAssembly::Instance,
function_environments => VMFunctionEnvironment,
}
#[derive(Default)]
pub struct StoreObjects {
id: StoreId,
memories: Vec<VMMemory>,
tables: Vec<VMTable>,
globals: Vec<VMGlobal>,
functions: Vec<VMFunction>,
instances: Vec<js_sys::WebAssembly::Instance>,
function_environments: Vec<VMFunctionEnvironment>,
}
impl StoreObjects {
pub fn id(&self) -> StoreId {
self.id
}
pub fn set_id(&mut self, id: StoreId) {
self.id = id;
}
pub fn get_2_mut<T: StoreObject>(
&mut self,
a: InternalStoreHandle<T>,
b: InternalStoreHandle<T>,
) -> (&mut T, &mut T) {
assert_ne!(a.index(), b.index());
let list = T::list_mut(self);
if a.index() < b.index() {
let (low, high) = list.split_at_mut(b.index());
(&mut low[a.index()], &mut high[0])
} else {
let (low, high) = list.split_at_mut(a.index());
(&mut high[0], &mut low[a.index()])
}
}
pub fn iter_globals(&self) -> core::slice::Iter<VMGlobal> {
self.globals.iter()
}
pub fn set_global_unchecked(&self, idx: usize, val: u128) {
assert!(idx < self.globals.len());
let value = JsValue::from(val);
self.globals[idx].global.set_value(&value);
}
}
pub struct StoreHandle<T> {
id: StoreId,
internal: InternalStoreHandle<T>,
}
impl<T> core::cmp::PartialEq for StoreHandle<T> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl<T> std::hash::Hash for StoreHandle<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
self.internal.idx.hash(state);
}
}
impl<T> Clone for StoreHandle<T> {
fn clone(&self) -> Self {
Self {
id: self.id,
internal: self.internal,
}
}
}
impl<T: StoreObject> fmt::Debug for StoreHandle<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("StoreHandle")
.field("id", &self.id)
.field("internal", &self.internal.index())
.finish()
}
}
impl<T: StoreObject> StoreHandle<T> {
pub fn new(store: &mut StoreObjects, val: T) -> Self {
Self {
id: store.id,
internal: InternalStoreHandle::new(store, val),
}
}
pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T {
assert_eq!(self.id, store.id, "object used with the wrong context");
self.internal.get(store)
}
pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T {
assert_eq!(self.id, store.id, "object used with the wrong context");
self.internal.get_mut(store)
}
pub fn internal_handle(&self) -> InternalStoreHandle<T> {
self.internal
}
pub fn store_id(&self) -> StoreId {
self.id
}
pub fn set_store_id(&mut self, id: StoreId) {
self.id = id;
}
pub unsafe fn from_internal(id: StoreId, internal: InternalStoreHandle<T>) -> Self {
Self { id, internal }
}
}
#[repr(transparent)]
pub struct InternalStoreHandle<T> {
idx: NonZeroUsize,
marker: PhantomData<fn() -> T>,
}
impl<T> Clone for InternalStoreHandle<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for InternalStoreHandle<T> {}
impl<T> fmt::Debug for InternalStoreHandle<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("InternalStoreHandle")
.field("idx", &self.idx)
.finish()
}
}
impl<T> PartialEq for InternalStoreHandle<T> {
fn eq(&self, other: &Self) -> bool {
self.idx == other.idx
}
}
impl<T> Eq for InternalStoreHandle<T> {}
impl<T: StoreObject> InternalStoreHandle<T> {
pub fn new(store: &mut StoreObjects, val: T) -> Self {
let list = T::list_mut(store);
let idx = NonZeroUsize::new(list.len() + 1).unwrap();
list.push(val);
Self {
idx,
marker: PhantomData,
}
}
pub fn get<'a>(&self, store: &'a StoreObjects) -> &'a T {
&T::list(store)[self.idx.get() - 1]
}
pub fn get_mut<'a>(&self, store: &'a mut StoreObjects) -> &'a mut T {
&mut T::list_mut(store)[self.idx.get() - 1]
}
pub(crate) fn index(&self) -> usize {
self.idx.get()
}
pub(crate) fn from_index(idx: usize) -> Option<Self> {
NonZeroUsize::new(idx).map(|idx| Self {
idx,
marker: PhantomData,
})
}
}
#[allow(dead_code)]
pub enum MaybeInstanceOwned<T> {
Host(Box<UnsafeCell<T>>),
Instance(NonNull<T>),
}
#[allow(dead_code)]
impl<T> MaybeInstanceOwned<T> {
#[allow(dead_code)]
pub fn as_ptr(&self) -> NonNull<T> {
match self {
MaybeInstanceOwned::Host(p) => unsafe { NonNull::new_unchecked(p.get()) },
MaybeInstanceOwned::Instance(p) => *p,
}
}
}
}