1use std::fmt;
31use std::sync::Arc;
32
33use anyhow::Result;
34use async_trait::async_trait;
35use surrealism_types::args::Args;
36use surrealism_types::err::PrefixError;
37use surrealism_types::transfer::AsyncTransfer;
38use wasmtime::*;
39use wasmtime_wasi::p1::{self, WasiP1Ctx};
40
41use crate::config::SurrealismConfig;
42use crate::host::{InvocationContext, implement_host_functions};
43use crate::package::SurrealismPackage;
44
45pub struct StoreData {
47 pub wasi: WasiP1Ctx,
48 pub config: Arc<SurrealismConfig>,
49 pub(crate) context: Box<dyn InvocationContext>,
50}
51
52impl fmt::Debug for StoreData {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 write!(f, "StoreData {{ wasi: ?, context: ?, config: {:?} }}", self.config)?;
55 Ok(())
56 }
57}
58
59#[derive(Debug)]
63pub struct Runtime {
64 engine: Engine,
65 module: Module,
66 linker: Linker<StoreData>,
67 config: Arc<SurrealismConfig>,
68}
69
70impl Runtime {
71 pub fn new(
75 SurrealismPackage {
76 wasm,
77 config,
78 }: SurrealismPackage,
79 ) -> Result<Self> {
80 let mut engine_config = Config::new();
82 engine_config.async_support(true);
84 #[cfg(debug_assertions)]
85 {
86 engine_config.strategy(Strategy::Winch);
89 }
90 #[cfg(not(debug_assertions))]
91 {
92 engine_config.cranelift_opt_level(OptLevel::Speed);
94 }
95 let engine = Engine::new(&engine_config)?;
96 let module =
97 Module::new(&engine, wasm).prefix_err(|| "Failed to construct module from bytes")?;
98
99 let mut linker: Linker<StoreData> = Linker::new(&engine);
100 p1::add_to_linker_async(&mut linker, |data| &mut data.wasi)
101 .prefix_err(|| "failed to add WASI to linker")?;
102 implement_host_functions(&mut linker)
103 .prefix_err(|| "failed to implement host functions")?;
104
105 Ok(Self {
106 engine,
107 module,
108 linker,
109 config: Arc::new(config),
110 })
111 }
112
113 pub async fn new_controller(&self, context: Box<dyn InvocationContext>) -> Result<Controller> {
118 let wasi_ctx = super::wasi_context::build()?;
119
120 let store_data = StoreData {
121 wasi: wasi_ctx,
122 config: self.config.clone(),
123 context,
124 };
125 let mut store = Store::new(&self.engine, store_data);
126 let instance = self
127 .linker
128 .instantiate_async(&mut store, &self.module)
129 .await
130 .prefix_err(|| "failed to instantiate WASM module")?;
131 let memory = instance
132 .get_memory(&mut store, "memory")
133 .prefix_err(|| "WASM module must export 'memory'")?;
134
135 Ok(Controller {
136 store,
137 instance,
138 memory,
139 })
140 }
141}
142
143#[derive(Debug)]
146pub struct Controller {
147 pub(super) store: Store<StoreData>,
148 pub(super) instance: Instance,
149 pub(super) memory: Memory,
150}
151
152impl Controller {
153 pub async fn alloc(&mut self, len: u32) -> Result<u32> {
154 let alloc = self.instance.get_typed_func::<(u32,), i32>(&mut self.store, "__sr_alloc")?;
155 let result = alloc.call_async(&mut self.store, (len,)).await?;
156 if result == -1 {
157 anyhow::bail!("Memory allocation failed");
158 }
159 Ok(result as u32)
160 }
161
162 pub async fn free(&mut self, ptr: u32, len: u32) -> Result<()> {
163 let free = self.instance.get_typed_func::<(u32, u32), i32>(&mut self.store, "__sr_free")?;
164 let result = free.call_async(&mut self.store, (ptr, len)).await?;
165 if result == -1 {
166 anyhow::bail!("Memory deallocation failed");
167 }
168 Ok(())
169 }
170
171 pub async fn init(&mut self) -> Result<()> {
172 let init: Option<Extern> = self.instance.get_export(&mut self.store, "__sr_init");
173 if init.is_none() {
174 return Ok(());
175 }
176
177 let init = self.instance.get_typed_func::<(), ()>(&mut self.store, "__sr_init")?;
178 init.call_async(&mut self.store, ()).await
179 }
180
181 pub async fn invoke<A: Args>(
182 &mut self,
183 name: Option<String>,
184 args: A,
185 ) -> Result<surrealdb_types::Value> {
186 let name = format!("__sr_fnc__{}", name.unwrap_or_default());
187 let args = AsyncTransfer::transfer(args.to_values(), self).await?;
188 let invoke = self.instance.get_typed_func::<(u32,), (i32,)>(&mut self.store, &name)?;
189 let (ptr,) = invoke.call_async(&mut self.store, (*args,)).await?;
190 if ptr == -1 {
191 anyhow::bail!("WASM function returned error (-1)");
192 }
193 let ptr_u32: u32 = ptr.try_into()?;
194 let result: Result<surrealdb_types::Value, String> =
195 AsyncTransfer::receive(ptr_u32.into(), self).await?;
196 result.map_err(|e| anyhow::anyhow!("WASM function returned error: {}", e))
197 }
198
199 pub async fn args(&mut self, name: Option<String>) -> Result<Vec<surrealdb_types::Kind>> {
200 let name = format!("__sr_args__{}", name.unwrap_or_default());
201 let args = self.instance.get_typed_func::<(), (i32,)>(&mut self.store, &name)?;
202 let (ptr,) = args.call_async(&mut self.store, ()).await?;
203 AsyncTransfer::receive(ptr.try_into()?, self).await
204 }
205
206 pub async fn returns(&mut self, name: Option<String>) -> Result<surrealdb_types::Kind> {
207 let name = format!("__sr_returns__{}", name.unwrap_or_default());
208 let returns = self.instance.get_typed_func::<(), (i32,)>(&mut self.store, &name)?;
209 let (ptr,) = returns.call_async(&mut self.store, ()).await?;
210 if ptr == -1 {
211 anyhow::bail!("WASM function returned error (-1)");
212 }
213 AsyncTransfer::receive(ptr.try_into()?, self).await
214 }
215
216 pub fn list(&mut self) -> Result<Vec<String>> {
217 let mut functions = Vec::new();
219
220 let function_names: Vec<String> = {
222 let exports = self.instance.exports(&mut self.store);
223 exports
224 .filter_map(|export| {
225 let name = export.name();
226 if name.starts_with("__sr_fnc__") {
227 Some(name.to_string())
228 } else {
229 None
230 }
231 })
232 .collect()
233 };
234
235 for name in function_names {
237 if let Some(export) = self.instance.get_export(&mut self.store, &name)
238 && let ExternType::Func(_) = export.ty(&self.store)
239 {
240 let function_name = name.strip_prefix("__sr_fnc__").unwrap_or(&name).to_string();
242 functions.push(function_name);
243 }
244 }
245
246 Ok(functions)
247 }
248}
249
250#[async_trait]
251impl surrealism_types::controller::AsyncMemoryController for Controller {
252 async fn alloc(&mut self, len: u32) -> Result<u32> {
253 Controller::alloc(self, len).await
254 }
255
256 async fn free(&mut self, ptr: u32, len: u32) -> Result<()> {
257 Controller::free(self, ptr, len).await
258 }
259
260 fn mut_mem(&mut self, ptr: u32, len: u32) -> Result<&mut [u8]> {
261 let mem = self.memory.data_mut(&mut self.store);
262 let start = ptr as usize;
263 let end = start
264 .checked_add(len as usize)
265 .ok_or_else(|| anyhow::anyhow!("Memory access overflow: ptr={ptr}, len={len}"))?;
266
267 if end > mem.len() {
268 anyhow::bail!(
269 "Memory access out of bounds: attempting to access [{start}..{end}), but memory size is {}",
270 mem.len()
271 );
272 }
273
274 Ok(&mut mem[start..end])
275 }
276}