use crate::{Engine, FuncType, Trap, ValRaw};
use anyhow::Result;
use std::any::Any;
use std::panic::{self, AssertUnwindSafe};
use std::sync::Arc;
use wasmtime_environ::{EntityIndex, Module, ModuleType, PrimaryMap, SignatureIndex};
use wasmtime_jit::{CodeMemory, MmapVec};
use wasmtime_runtime::{
Imports, InstanceAllocationRequest, InstanceAllocator, InstanceHandle,
OnDemandInstanceAllocator, StorePtr, VMContext, VMFunctionBody, VMSharedSignatureIndex,
VMTrampoline,
};
struct TrampolineState<F> {
func: F,
#[allow(dead_code)]
code_memory: CodeMemory,
}
unsafe extern "C" fn stub_fn<F>(
vmctx: *mut VMContext,
caller_vmctx: *mut VMContext,
values_vec: *mut ValRaw,
) where
F: Fn(*mut VMContext, *mut ValRaw) -> Result<(), Trap> + 'static,
{
let result = panic::catch_unwind(AssertUnwindSafe(|| {
let state = (*vmctx).host_state();
debug_assert!(state.is::<TrampolineState<F>>());
let state = &*(state as *const _ as *const TrampolineState<F>);
(state.func)(caller_vmctx, values_vec)
}));
match result {
Ok(Ok(())) => {}
Ok(Err(trap)) => wasmtime_runtime::raise_user_trap(trap.into()),
Err(panic) => wasmtime_runtime::resume_panic(panic),
}
}
#[cfg(compiler)]
pub fn create_function<F>(
ft: &FuncType,
func: F,
engine: &Engine,
) -> Result<(InstanceHandle, VMTrampoline)>
where
F: Fn(*mut VMContext, *mut ValRaw) -> Result<(), Trap> + Send + Sync + 'static,
{
let mut obj = engine.compiler().object()?;
let (t1, t2) = engine.compiler().emit_trampoline_obj(
ft.as_wasm_func_type(),
stub_fn::<F> as usize,
&mut obj,
)?;
let obj = MmapVec::from_obj(obj)?;
let mut code_memory = CodeMemory::new(obj);
let code = code_memory.publish()?;
engine.config().profiler.trampoline_load(&code.obj);
let host_trampoline = code.text[t1.start as usize..][..t1.length as usize].as_ptr();
let wasm_trampoline = &code.text[t2.start as usize..][..t2.length as usize];
let wasm_trampoline = wasm_trampoline as *const [u8] as *mut [VMFunctionBody];
let sig = engine.signatures().register(ft.as_wasm_func_type());
unsafe {
let instance = create_raw_function(
wasm_trampoline,
sig,
Box::new(TrampolineState { func, code_memory }),
)?;
let host_trampoline = std::mem::transmute::<*const u8, VMTrampoline>(host_trampoline);
Ok((instance, host_trampoline))
}
}
pub unsafe fn create_raw_function(
func: *mut [VMFunctionBody],
sig: VMSharedSignatureIndex,
host_state: Box<dyn Any + Send + Sync>,
) -> Result<InstanceHandle> {
let mut module = Module::new();
let mut functions = PrimaryMap::new();
functions.push(Default::default());
let sig_id = SignatureIndex::from_u32(u32::max_value() - 1);
module.types.push(ModuleType::Function(sig_id));
let func_id = module.functions.push(sig_id);
module
.exports
.insert(String::new(), EntityIndex::Function(func_id));
Ok(
OnDemandInstanceAllocator::default().allocate(InstanceAllocationRequest {
module: Arc::new(module),
functions: &functions,
image_base: (*func).as_ptr() as usize,
imports: Imports::default(),
shared_signatures: sig.into(),
host_state,
store: StorePtr::empty(),
wasm_data: &[],
})?,
)
}