use std::sync::Arc;
use crate::{
runtime::{InstanceCounter, ReleaseInstanceHandle, Store},
store_data::StoreData,
};
use sc_executor_common::error::{Backtrace, Error, MessageWithBacktrace, Result, WasmError};
use sp_wasm_interface::{Pointer, Value, WordSize};
use wasmtime::{
AsContext, AsContextMut, Engine, Extern, Instance, InstancePre, Memory, Table, Val,
};
pub struct EntryPoint(wasmtime::TypedFunc<(u32, u32), u64>);
impl EntryPoint {
pub(crate) fn call(
&self,
store: &mut Store,
data_ptr: Pointer<u8>,
data_len: WordSize,
) -> Result<u64> {
let data_ptr = u32::from(data_ptr);
self.0
.call(&mut *store, (data_ptr, data_len))
.map_err(|trap| {
let host_state = store
.data_mut()
.host_state
.as_mut()
.expect("host state cannot be empty while a function is being called; qed");
let backtrace = trap
.downcast_ref::<wasmtime::WasmBacktrace>()
.map(|backtrace| {
Backtrace {
backtrace_string: backtrace.to_string(),
}
});
if let Some(message) = host_state.take_panic_message() {
Error::AbortedDueToPanic(MessageWithBacktrace { message, backtrace })
} else {
let message = trap.root_cause().to_string();
Error::AbortedDueToTrap(MessageWithBacktrace { message, backtrace })
}
})
}
pub fn direct(
func: wasmtime::Func,
ctx: impl AsContext,
) -> std::result::Result<Self, &'static str> {
let entrypoint = func
.typed::<(u32, u32), u64>(ctx)
.map_err(|_| "Invalid signature for direct entry point")?;
Ok(Self(entrypoint))
}
}
pub struct InstanceWrapper {
instance: Instance,
store: Store,
_release_instance_handle: ReleaseInstanceHandle,
}
impl InstanceWrapper {
pub(crate) fn new(
engine: &Engine,
instance_pre: &InstancePre<StoreData>,
instance_counter: Arc<InstanceCounter>,
) -> Result<Self> {
let _release_instance_handle = instance_counter.acquire_instance();
let mut store = Store::new(engine, Default::default());
let instance = instance_pre.instantiate(&mut store).map_err(|error| {
WasmError::Other(format!(
"failed to instantiate a new WASM module instance: {:#}",
error,
))
})?;
let memory = get_linear_memory(&instance, &mut store)?;
let table = get_table(&instance, &mut store);
store.data_mut().memory = Some(memory);
store.data_mut().table = table;
Ok(InstanceWrapper {
instance,
store,
_release_instance_handle,
})
}
pub fn resolve_entrypoint(&mut self, method: &str) -> Result<EntryPoint> {
let export = self
.instance
.get_export(&mut self.store, method)
.ok_or_else(|| Error::from(format!("Exported method {} is not found", method)))?;
let func = export
.into_func()
.ok_or_else(|| Error::from(format!("Export {} is not a function", method)))?;
EntryPoint::direct(func, &self.store).map_err(|_| {
Error::from(format!(
"Exported function '{}' has invalid signature.",
method
))
})
}
pub fn extract_heap_base(&mut self) -> Result<u32> {
let heap_base_export = self
.instance
.get_export(&mut self.store, "__heap_base")
.ok_or_else(|| Error::from("__heap_base is not found"))?;
let heap_base_global = heap_base_export
.into_global()
.ok_or_else(|| Error::from("__heap_base is not a global"))?;
let heap_base = heap_base_global
.get(&mut self.store)
.i32()
.ok_or_else(|| Error::from("__heap_base is not a i32"))?;
Ok(heap_base as u32)
}
pub fn get_global_val(&mut self, name: &str) -> Result<Option<Value>> {
let global = match self.instance.get_export(&mut self.store, name) {
Some(global) => global,
None => return Ok(None),
};
let global = global
.into_global()
.ok_or_else(|| format!("`{}` is not a global", name))?;
match global.get(&mut self.store) {
Val::I32(val) => Ok(Some(Value::I32(val))),
Val::I64(val) => Ok(Some(Value::I64(val))),
Val::F32(val) => Ok(Some(Value::F32(val))),
Val::F64(val) => Ok(Some(Value::F64(val))),
_ => Err("Unknown value type".into()),
}
}
}
fn get_linear_memory(instance: &Instance, ctx: impl AsContextMut) -> Result<Memory> {
let memory_export = instance
.get_export(ctx, "memory")
.ok_or_else(|| Error::from("memory is not exported under `memory` name"))?;
let memory = memory_export
.into_memory()
.ok_or_else(|| Error::from("the `memory` export should have memory type"))?;
Ok(memory)
}
fn get_table(instance: &Instance, ctx: &mut Store) -> Option<Table> {
instance
.get_export(ctx, "__indirect_function_table")
.as_ref()
.cloned()
.and_then(Extern::into_table)
}
impl InstanceWrapper {
pub(crate) fn store(&self) -> &Store {
&self.store
}
pub(crate) fn store_mut(&mut self) -> &mut Store {
&mut self.store
}
}