Crates continued to grow in number, but most are still quite small and feature specific. This refactor consolidates them.
109 lines
3.8 KiB
Rust
109 lines
3.8 KiB
Rust
use swactor::actor::ActorAddress;
|
|
use wasmtime::{Linker, Module, Store, TypedFunc};
|
|
|
|
use crate::actor::{HostState, WasmActor};
|
|
use crate::engine::SharedEngine;
|
|
use crate::error::WasmActorError;
|
|
|
|
/// Compiles a Wasm module and produces a ready-to-use [`WasmActor`].
|
|
pub struct WasmActorBuilder {
|
|
engine: SharedEngine,
|
|
wasm_bytes: Vec<u8>,
|
|
}
|
|
|
|
impl WasmActorBuilder {
|
|
pub fn new(engine: SharedEngine, wasm_bytes: impl Into<Vec<u8>>) -> Self {
|
|
Self {
|
|
engine,
|
|
wasm_bytes: wasm_bytes.into(),
|
|
}
|
|
}
|
|
|
|
/// Compile the module, link host functions, and instantiate.
|
|
pub fn build(self) -> Result<WasmActor, WasmActorError> {
|
|
let engine = self.engine.inner();
|
|
let module = Module::new(engine, &self.wasm_bytes)?;
|
|
|
|
let mut linker: Linker<HostState> = Linker::new(engine);
|
|
Self::link_send(&mut linker)?;
|
|
|
|
let mut store = Store::new(engine, HostState::default());
|
|
let instance = linker.instantiate(&mut store, &module)?;
|
|
|
|
// Extract required exports
|
|
let memory = instance
|
|
.get_memory(&mut store, "memory")
|
|
.ok_or(WasmActorError::MissingExport("memory"))?;
|
|
|
|
let alloc: TypedFunc<i32, i32> = instance
|
|
.get_typed_func(&mut store, "alloc")
|
|
.map_err(|_| WasmActorError::MissingExport("alloc"))?;
|
|
|
|
let handle: TypedFunc<(i32, i32), ()> = instance
|
|
.get_typed_func(&mut store, "handle")
|
|
.map_err(|_| WasmActorError::MissingExport("handle"))?;
|
|
|
|
Ok(WasmActor {
|
|
store,
|
|
memory,
|
|
alloc,
|
|
handle,
|
|
})
|
|
}
|
|
|
|
/// Link the `swactor.send` host import.
|
|
fn link_send(linker: &mut Linker<HostState>) -> Result<(), WasmActorError> {
|
|
linker.func_wrap(
|
|
"swactor",
|
|
"send",
|
|
|mut caller: wasmtime::Caller<'_, HostState>,
|
|
dest_ptr: i32,
|
|
payload_ptr: i32,
|
|
payload_len: i32|
|
|
-> Result<(), wasmtime::Error> {
|
|
let mem = caller
|
|
.get_export("memory")
|
|
.and_then(|e| e.into_memory())
|
|
.ok_or_else(|| wasmtime::Error::msg("guest must export memory"))?;
|
|
let data = mem.data(&caller);
|
|
let mem_len = data.len();
|
|
|
|
// Validate non-negative arguments
|
|
if dest_ptr < 0 || payload_ptr < 0 || payload_len < 0 {
|
|
return Err(wasmtime::Error::msg(
|
|
"negative argument in swactor.send",
|
|
));
|
|
}
|
|
|
|
let dest_ptr = dest_ptr as usize;
|
|
let payload_ptr = payload_ptr as usize;
|
|
let payload_len = payload_len as usize;
|
|
|
|
// Bounds-check with overflow protection
|
|
let dest_end = dest_ptr
|
|
.checked_add(32)
|
|
.ok_or_else(|| wasmtime::Error::msg("dest_ptr overflow"))?;
|
|
let payload_end = payload_ptr
|
|
.checked_add(payload_len)
|
|
.ok_or_else(|| wasmtime::Error::msg("payload range overflow"))?;
|
|
if dest_end > mem_len || payload_end > mem_len {
|
|
return Err(wasmtime::Error::msg(
|
|
"out-of-bounds memory access in swactor.send",
|
|
));
|
|
}
|
|
|
|
// Read 32-byte destination address
|
|
let mut addr_bytes = [0u8; 32];
|
|
addr_bytes.copy_from_slice(&data[dest_ptr..dest_end]);
|
|
let dest = ActorAddress(addr_bytes);
|
|
|
|
// Read payload
|
|
let payload = data[payload_ptr..payload_end].to_vec();
|
|
|
|
caller.data_mut().outbox.push((dest, payload));
|
|
Ok(())
|
|
},
|
|
)?;
|
|
Ok(())
|
|
}
|
|
}
|