use std::os::raw::c_void;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use wasm3x_sys as ffi;
use crate::engine::Engine;
use crate::error::Error;
use crate::trampoline::HostFuncEntry;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct StoreId(u64);
impl StoreId {
pub(crate) const PLACEHOLDER: StoreId = StoreId(0);
}
fn next_store_id() -> StoreId {
static NEXT: AtomicU64 = AtomicU64::new(1);
StoreId(NEXT.fetch_add(1, Ordering::Relaxed))
}
pub(crate) struct CallState {
pub(crate) last_host_error: Option<Error>,
pub(crate) store_id: StoreId,
pub(crate) data_ptr: *mut c_void,
}
pub struct Store<T> {
runtime: ffi::IM3Runtime,
engine: Engine,
id: StoreId,
call_state: Box<CallState>,
module_bytes: Vec<Arc<[u8]>>,
host_entries: Vec<Arc<HostFuncEntry>>,
data: T,
}
impl<T> Store<T> {
pub fn new(engine: &Engine, data: T) -> Self {
let id = next_store_id();
let mut call_state = Box::new(CallState {
last_host_error: None,
store_id: id,
data_ptr: core::ptr::null_mut(),
});
let user_data = (&mut *call_state) as *mut CallState as *mut c_void;
let runtime = unsafe {
ffi::m3_NewRuntime(engine.raw(), engine.config().get_stack_size(), user_data)
};
assert!(!runtime.is_null(), "wasm3: failed to allocate runtime");
Self {
runtime,
engine: engine.clone(),
id,
call_state,
module_bytes: Vec::new(),
host_entries: Vec::new(),
data,
}
}
pub fn data(&self) -> &T {
&self.data
}
pub fn data_mut(&mut self) -> &mut T {
&mut self.data
}
pub fn engine(&self) -> &Engine {
&self.engine
}
pub(crate) fn raw(&self) -> ffi::IM3Runtime {
self.runtime
}
pub(crate) fn id(&self) -> StoreId {
self.id
}
pub(crate) fn register_bytes(&mut self, bytes: Arc<[u8]>) {
self.module_bytes.push(bytes);
}
pub(crate) fn register_host_entry(&mut self, entry: Arc<HostFuncEntry>) {
self.host_entries.push(entry);
}
#[cfg(test)]
pub(crate) fn host_entry_count(&self) -> usize {
self.host_entries.len()
}
pub(crate) fn clear_host_error(&mut self) {
self.call_state.last_host_error = None;
}
pub(crate) fn set_call_data(&mut self) {
let ptr = (&mut self.data) as *mut T as *mut c_void;
self.call_state.data_ptr = ptr;
}
pub(crate) fn clear_call_data(&mut self) {
self.call_state.data_ptr = core::ptr::null_mut();
}
pub(crate) fn take_host_error(&mut self) -> Option<Error> {
self.call_state.last_host_error.take()
}
pub(crate) fn ensure_owns(&self, id: StoreId) {
assert!(
self.id == id,
"wasm3: attempted to use an item with a different `Store` than the one that owns it"
);
}
}
impl<T: Default> Default for Store<T> {
fn default() -> Self {
Self::new(&Engine::default(), T::default())
}
}
pub(crate) fn assert_same_store(ctx: StoreId, handle: StoreId) {
assert!(
ctx == handle,
"wasm3: attempted to use an item with a different `Store` than the one that owns it"
);
}
pub struct StoreContext<'a, T> {
runtime: ffi::IM3Runtime,
store_id: StoreId,
data: &'a T,
}
pub struct StoreContextMut<'a, T> {
runtime: ffi::IM3Runtime,
store_id: StoreId,
data: &'a mut T,
}
impl<'a, T> StoreContext<'a, T> {
pub(crate) fn new(runtime: ffi::IM3Runtime, store_id: StoreId, data: &'a T) -> Self {
Self {
runtime,
store_id,
data,
}
}
pub fn data(&self) -> &T {
self.data
}
pub(crate) fn runtime(&self) -> ffi::IM3Runtime {
self.runtime
}
pub(crate) fn store_id(&self) -> StoreId {
self.store_id
}
}
impl<'a, T> StoreContextMut<'a, T> {
pub(crate) fn new(runtime: ffi::IM3Runtime, store_id: StoreId, data: &'a mut T) -> Self {
Self {
runtime,
store_id,
data,
}
}
pub fn data(&self) -> &T {
self.data
}
pub fn data_mut(&mut self) -> &mut T {
self.data
}
pub(crate) fn runtime(&self) -> ffi::IM3Runtime {
self.runtime
}
pub(crate) fn store_id(&self) -> StoreId {
self.store_id
}
}
pub trait AsContext {
type Data;
fn as_context(&self) -> StoreContext<'_, Self::Data>;
}
pub trait AsContextMut: AsContext {
fn as_context_mut(&mut self) -> StoreContextMut<'_, Self::Data>;
}
impl<T> AsContext for Store<T> {
type Data = T;
fn as_context(&self) -> StoreContext<'_, T> {
StoreContext {
runtime: self.runtime,
store_id: self.id,
data: &self.data,
}
}
}
impl<T> AsContextMut for Store<T> {
fn as_context_mut(&mut self) -> StoreContextMut<'_, T> {
StoreContextMut {
runtime: self.runtime,
store_id: self.id,
data: &mut self.data,
}
}
}
impl<C: AsContext + ?Sized> AsContext for &C {
type Data = C::Data;
fn as_context(&self) -> StoreContext<'_, C::Data> {
C::as_context(self)
}
}
impl<C: AsContext + ?Sized> AsContext for &mut C {
type Data = C::Data;
fn as_context(&self) -> StoreContext<'_, C::Data> {
C::as_context(self)
}
}
impl<C: AsContextMut + ?Sized> AsContextMut for &mut C {
fn as_context_mut(&mut self) -> StoreContextMut<'_, C::Data> {
C::as_context_mut(self)
}
}
impl<'a, T> AsContext for StoreContext<'a, T> {
type Data = T;
fn as_context(&self) -> StoreContext<'_, T> {
StoreContext {
runtime: self.runtime,
store_id: self.store_id,
data: self.data,
}
}
}
impl<'a, T> AsContext for StoreContextMut<'a, T> {
type Data = T;
fn as_context(&self) -> StoreContext<'_, T> {
StoreContext {
runtime: self.runtime,
store_id: self.store_id,
data: &*self.data,
}
}
}
impl<'a, T> AsContextMut for StoreContextMut<'a, T> {
fn as_context_mut(&mut self) -> StoreContextMut<'_, T> {
StoreContextMut {
runtime: self.runtime,
store_id: self.store_id,
data: &mut *self.data,
}
}
}
impl<T> Drop for Store<T> {
fn drop(&mut self) {
unsafe { ffi::m3_FreeRuntime(self.runtime) }
}
}