use std::marker::PhantomData;
use std::pin::Pin;
use std::sync::Arc;
use anyhow::Result;
pub(crate) const CALLBACK_SYNC_BEFORE: u32 = 1 << 0;
pub(crate) const CALLBACK_SYNC_AFTER: u32 = 1 << 1;
pub(crate) fn should_sync_before(sync_flags: u32) -> bool {
sync_flags & CALLBACK_SYNC_BEFORE != 0
}
pub(crate) fn should_sync_after(sync_flags: u32) -> bool {
sync_flags & CALLBACK_SYNC_AFTER != 0
}
pub(crate) fn callback_storage_len(
params: &[ValType],
results: &[ValType],
) -> usize {
params.len().max(results.len())
}
pub(crate) type StoreContext<'a, T, B> = &'a Store<T, B>;
pub(crate) type StoreContextMut<'a, T, B> = &'a mut Store<T, B>;
pub(crate) trait RuntimeBackend:
Clone + Default + Sized + 'static
{
type RuntimeState: Default;
type ModuleInner;
type InstanceInner;
type TypedFuncHandle;
fn set_epoch_deadline(runtime: &mut Self::RuntimeState, deadline: u64);
fn prepare_for_instantiation(runtime: &mut Self::RuntimeState);
fn reset_for_store_reuse(runtime: &mut Self::RuntimeState);
fn create_global(
runtime: &mut Self::RuntimeState,
ty: GlobalType,
value: Val,
) -> Result<usize>;
fn get_global(runtime: &mut Self::RuntimeState, id: usize) -> Val;
fn set_global(
runtime: &mut Self::RuntimeState,
id: usize,
value: Val,
) -> Result<()>;
fn create_memory(
runtime: &mut Self::RuntimeState,
ty: MemoryType,
) -> Result<usize>;
fn memory_data<'a>(runtime: &'a Self::RuntimeState, id: usize)
-> &'a [u8];
fn memory_data_mut<'a>(
runtime: &'a mut Self::RuntimeState,
id: usize,
) -> &'a mut [u8];
fn memory_data_ptr(runtime: &mut Self::RuntimeState, id: usize)
-> *mut u8;
fn module_from_binary(
engine: &Engine,
bytes: &[u8],
) -> Result<Self::ModuleInner>;
fn instantiate<T: 'static>(
store: &mut Store<T, Self>,
linker: &Linker<T, Self>,
module: &Module<Self>,
) -> Result<Self::InstanceInner>;
fn get_typed_func_handle<P, R>(
instance: &Self::InstanceInner,
name: &str,
) -> Result<Self::TypedFuncHandle>;
fn typed_func_call_i32<T>(
store: &mut Store<T, Self>,
func: &Self::TypedFuncHandle,
) -> Result<i32>;
}
pub(crate) trait AsContext {
type Data;
type Backend: RuntimeBackend;
fn as_context(&self) -> StoreContext<'_, Self::Data, Self::Backend>;
}
pub(crate) trait AsContextMut: AsContext {
fn as_context_mut(
&mut self,
) -> StoreContextMut<'_, Self::Data, Self::Backend>;
}
pub(crate) struct Store<T, B: RuntimeBackend> {
data: T,
pub(crate) runtime: B::RuntimeState,
_engine: Engine,
_backend: PhantomData<B>,
}
impl<T, B: RuntimeBackend> Store<T, B> {
pub fn new(engine: &Engine, data: T) -> Self {
Self {
data,
runtime: B::RuntimeState::default(),
_engine: engine.clone(),
_backend: PhantomData,
}
}
pub fn data(&self) -> &T {
&self.data
}
pub fn data_mut(&mut self) -> &mut T {
&mut self.data
}
pub fn set_epoch_deadline(&mut self, deadline: u64) {
B::set_epoch_deadline(&mut self.runtime, deadline);
}
pub fn epoch_deadline_callback<F>(&mut self, _callback: F)
where
F: FnMut(StoreContextMut<'_, T, B>) -> Result<()> + 'static,
{
}
}
impl<T, B: RuntimeBackend> Drop for Store<T, B> {
fn drop(&mut self) {
B::reset_for_store_reuse(&mut self.runtime);
}
}
impl<T, B: RuntimeBackend> AsContext for Store<T, B> {
type Data = T;
type Backend = B;
fn as_context(&self) -> StoreContext<'_, T, B> {
self
}
}
impl<T, B: RuntimeBackend> AsContextMut for Store<T, B> {
fn as_context_mut(&mut self) -> StoreContextMut<'_, T, B> {
self
}
}
impl<T, B: RuntimeBackend> AsContext for &Store<T, B> {
type Data = T;
type Backend = B;
fn as_context(&self) -> StoreContext<'_, T, B> {
self
}
}
impl<T, B: RuntimeBackend> AsContext for &mut Store<T, B> {
type Data = T;
type Backend = B;
fn as_context(&self) -> StoreContext<'_, T, B> {
self
}
}
impl<T, B: RuntimeBackend> AsContextMut for &mut Store<T, B> {
fn as_context_mut(&mut self) -> StoreContextMut<'_, T, B> {
self
}
}
impl<T, B: RuntimeBackend> AsContext for Pin<Box<Store<T, B>>> {
type Data = T;
type Backend = B;
fn as_context(&self) -> StoreContext<'_, T, B> {
self.as_ref().get_ref()
}
}
impl<T, B: RuntimeBackend> AsContextMut for Pin<Box<Store<T, B>>> {
fn as_context_mut(&mut self) -> StoreContextMut<'_, T, B> {
unsafe { self.as_mut().get_unchecked_mut() }
}
}
pub(crate) struct Caller<'a, T, B: RuntimeBackend> {
pub(crate) store: &'a mut Store<T, B>,
}
impl<'a, T, B: RuntimeBackend> Caller<'a, T, B> {
pub(crate) fn new(store: &'a mut Store<T, B>) -> Self {
Self { store }
}
pub fn data(&self) -> &T {
self.store.data()
}
pub fn data_mut(&mut self) -> &mut T {
self.store.data_mut()
}
}
impl<T, B: RuntimeBackend> AsContext for Caller<'_, T, B> {
type Data = T;
type Backend = B;
fn as_context(&self) -> StoreContext<'_, T, B> {
self.store
}
}
impl<T, B: RuntimeBackend> AsContextMut for Caller<'_, T, B> {
fn as_context_mut(&mut self) -> StoreContextMut<'_, T, B> {
self.store
}
}
#[derive(Clone, Default)]
pub(crate) struct Config;
impl Config {
#[cfg(target_env = "musl")]
pub fn native_unwind_info(&mut self, _enabled: bool) -> &mut Self {
self
}
pub fn cranelift_opt_level(&mut self, _level: OptLevel) -> &mut Self {
self
}
pub fn epoch_interruption(&mut self, _enabled: bool) -> &mut Self {
self
}
pub fn memory_reservation(&mut self, _bytes: u64) -> &mut Self {
self
}
pub fn memory_reservation_for_growth(&mut self, _bytes: u64) -> &mut Self {
self
}
pub fn memory_may_move(&mut self, _enabled: bool) -> &mut Self {
self
}
}
#[derive(Clone, Copy)]
pub(crate) enum OptLevel {
SpeedAndSize,
}
#[derive(Clone, Default)]
pub(crate) struct Engine;
impl Engine {
pub fn new(_config: &Config) -> Result<Self> {
Ok(Self)
}
pub fn increment_epoch(&self) {}
#[cfg(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "riscv64",
target_arch = "s390x",
))]
pub fn unload_process_handlers(self) {}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum ValType {
I64,
I32,
F64,
F32,
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct ValRaw(u64);
impl ValRaw {
#[inline]
pub fn i64(value: i64) -> Self {
Self(value as u64)
}
#[inline]
pub fn i32(value: i32) -> Self {
Self((value as u32) as u64)
}
#[inline]
pub fn f64(value: u64) -> Self {
Self(value)
}
#[inline]
pub fn f32(value: u32) -> Self {
Self(value as u64)
}
#[inline]
pub fn get_i64(self) -> i64 {
self.0 as i64
}
#[inline]
pub fn get_i32(self) -> i32 {
self.0 as u32 as i32
}
#[inline]
pub fn get_f64(self) -> u64 {
self.0
}
#[inline]
pub fn get_f32(self) -> u32 {
self.0 as u32
}
}
#[derive(Clone)]
pub(crate) struct FuncType {
pub(crate) params: Vec<ValType>,
pub(crate) results: Vec<ValType>,
}
impl FuncType {
pub fn new(
_engine: &Engine,
params: Vec<ValType>,
results: impl IntoIterator<Item = ValType>,
) -> Self {
Self { params, results: results.into_iter().collect() }
}
}
pub(crate) type HostFunc<T, B> =
Arc<dyn Fn(Caller<'_, T, B>, &mut [ValRaw]) -> Result<()> + Send + Sync>;
pub(crate) struct RegisteredFunc<T, B: RuntimeBackend> {
pub(crate) module: String,
pub(crate) name: String,
pub(crate) ty: FuncType,
pub(crate) sync_flags: u32,
pub(crate) trampoline: HostFunc<T, B>,
}
#[derive(Clone, Copy)]
pub(crate) struct Global {
pub(crate) id: usize,
}
#[derive(Clone, Copy)]
pub(crate) struct Memory {
pub(crate) id: usize,
}
pub(crate) enum Extern {
Global(Global),
Memory(Memory),
}
impl From<Global> for Extern {
fn from(value: Global) -> Self {
Self::Global(value)
}
}
impl From<Memory> for Extern {
fn from(value: Memory) -> Self {
Self::Memory(value)
}
}
pub(crate) struct DefinedExtern {
pub(crate) module: String,
pub(crate) name: String,
pub(crate) value: Extern,
}
pub(crate) struct Linker<T, B: RuntimeBackend> {
pub(crate) functions: Vec<RegisteredFunc<T, B>>,
pub(crate) externs: Vec<DefinedExtern>,
_phantom: PhantomData<T>,
}
impl<T: 'static, B: RuntimeBackend> Linker<T, B> {
pub fn new(_engine: &Engine) -> Self {
Self {
functions: Vec::new(),
externs: Vec::new(),
_phantom: PhantomData,
}
}
pub unsafe fn func_new_unchecked(
&mut self,
module: &str,
name: &str,
ty: FuncType,
sync_flags: u32,
trampoline: Box<
dyn Fn(Caller<'_, T, B>, &mut [ValRaw]) -> Result<()>
+ Send
+ Sync
+ 'static,
>,
) -> Result<()> {
self.functions.push(RegisteredFunc {
module: module.to_owned(),
name: name.to_owned(),
ty,
sync_flags,
trampoline: Arc::from(trampoline),
});
Ok(())
}
pub fn define(
&mut self,
_store: StoreContext<'_, T, B>,
module: &str,
name: &str,
value: impl Into<Extern>,
) -> Result<&mut Self> {
self.externs.push(DefinedExtern {
module: module.to_owned(),
name: name.to_owned(),
value: value.into(),
});
Ok(self)
}
pub fn instantiate(
&self,
store: StoreContextMut<'_, T, B>,
module: &Module<B>,
) -> Result<Instance<B>> {
B::prepare_for_instantiation(&mut store.runtime);
Ok(Instance {
inner: B::instantiate(store, self, module)?,
_backend: PhantomData,
})
}
}
#[derive(Clone, Copy)]
pub(crate) enum Mutability {
Const,
Var,
}
pub(crate) struct GlobalType {
pub(crate) val_type: ValType,
pub(crate) mutability: Mutability,
}
impl GlobalType {
pub fn new(val_type: ValType, mutability: Mutability) -> Self {
Self { val_type, mutability }
}
}
#[derive(Clone, Copy)]
pub(crate) enum Val {
I32(i32),
I64(i64),
F32(u32),
F64(u64),
}
impl Val {
pub fn i64(self) -> Option<i64> {
match self {
Self::I64(v) => Some(v),
_ => None,
}
}
}
impl Global {
pub fn new<T, B: RuntimeBackend>(
store: StoreContextMut<'_, T, B>,
ty: GlobalType,
value: Val,
) -> Result<Self> {
Ok(Self { id: B::create_global(&mut store.runtime, ty, value)? })
}
pub fn get<T, B: RuntimeBackend>(
&self,
store: StoreContextMut<'_, T, B>,
) -> Val {
B::get_global(&mut store.runtime, self.id)
}
pub fn set<T, B: RuntimeBackend>(
&self,
store: StoreContextMut<'_, T, B>,
value: Val,
) -> Result<()> {
B::set_global(&mut store.runtime, self.id, value)
}
}
pub(crate) struct MemoryType {
pub(crate) initial: u32,
pub(crate) maximum: Option<u32>,
}
impl MemoryType {
pub fn new(initial: u32, maximum: Option<u32>) -> Self {
Self { initial, maximum }
}
}
impl Memory {
pub fn new<T, B: RuntimeBackend>(
store: StoreContextMut<'_, T, B>,
ty: MemoryType,
) -> Result<Self> {
Ok(Self { id: B::create_memory(&mut store.runtime, ty)? })
}
pub fn data<'a, T, B: RuntimeBackend>(
&self,
store: StoreContext<'a, T, B>,
) -> &'a [u8] {
B::memory_data(&store.runtime, self.id)
}
pub fn data_mut<'a, T, B: RuntimeBackend>(
&self,
store: StoreContextMut<'a, T, B>,
) -> &'a mut [u8] {
B::memory_data_mut(&mut store.runtime, self.id)
}
pub fn data_ptr<T, B: RuntimeBackend>(
&self,
store: StoreContextMut<'_, T, B>,
) -> *mut u8 {
B::memory_data_ptr(&mut store.runtime, self.id)
}
}
pub(crate) struct Module<B: RuntimeBackend> {
pub(crate) inner: B::ModuleInner,
_backend: PhantomData<B>,
}
impl<B: RuntimeBackend> Module<B> {
pub fn from_binary(engine: &Engine, bytes: &[u8]) -> Result<Self> {
Ok(Self {
inner: B::module_from_binary(engine, bytes)?,
_backend: PhantomData,
})
}
pub unsafe fn deserialize(engine: &Engine, bytes: &[u8]) -> Result<Self> {
Self::from_binary(engine, bytes)
}
}
pub(crate) struct Instance<B: RuntimeBackend> {
pub(crate) inner: B::InstanceInner,
_backend: PhantomData<B>,
}
impl<B: RuntimeBackend> Instance<B> {
pub fn get_typed_func<P, R>(
&self,
_store: impl AsContextMut<Backend = B>,
name: &str,
) -> Result<TypedFunc<P, R, B>> {
Ok(TypedFunc {
inner: B::get_typed_func_handle::<P, R>(&self.inner, name)?,
_params: PhantomData,
_results: PhantomData,
_backend: PhantomData,
})
}
}
pub(crate) struct TypedFunc<P, R, B: RuntimeBackend> {
inner: B::TypedFuncHandle,
_params: PhantomData<P>,
_results: PhantomData<R>,
_backend: PhantomData<B>,
}
impl<B: RuntimeBackend> TypedFunc<(), i32, B> {
pub fn call<T>(
&self,
store: StoreContextMut<'_, T, B>,
_params: (),
) -> Result<i32> {
B::typed_func_call_i32(store, &self.inner)
}
}
pub(crate) fn default_val(ty: ValType) -> Val {
match ty {
ValType::I32 => Val::I32(0),
ValType::I64 => Val::I64(0),
ValType::F32 => Val::F32(0),
ValType::F64 => Val::F64(0),
}
}