1use std::sync::Arc;
7
8use anyhow::Result;
9use serde::{Deserialize, Serialize};
10use tokio::sync::RwLock;
11use wasmtime::{Engine, Linker, Module, Store, StoreLimits, StoreLimitsBuilder, WasmBacktraceDetails};
12
13use crate::{
14 WASM::{
15 DEFAULT_MAX_EXECUTION_TIME_MS,
16 DEFAULT_MEMORY_LIMIT_MB,
17 MemoryManager::{MemoryLimits, MemoryManagerImpl},
18 },
19 dev_log,
20};
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct WASMConfig {
25 pub memory_limit_mb:u64,
27
28 pub max_execution_time_ms:u64,
30
31 pub enable_wasi:bool,
33
34 pub enable_debug:bool,
36
37 pub allow_threads:bool,
39
40 pub allow_host_memory:bool,
42
43 pub enable_fuel_metering:bool,
45}
46
47impl Default for WASMConfig {
48 fn default() -> Self {
49 Self {
50 memory_limit_mb:DEFAULT_MEMORY_LIMIT_MB,
51
52 max_execution_time_ms:DEFAULT_MAX_EXECUTION_TIME_MS,
53
54 enable_wasi:true,
55
56 enable_debug:cfg!(debug_assertions),
57
58 allow_threads:false,
59
60 allow_host_memory:false,
61
62 enable_fuel_metering:true,
63 }
64 }
65}
66
67impl WASMConfig {
68 pub fn new(memory_limit_mb:u64, max_execution_time_ms:u64, enable_wasi:bool) -> Self {
70 Self { memory_limit_mb, max_execution_time_ms, enable_wasi, ..Default::default() }
71 }
72
73 fn apply_to_engine_builder(&self, mut builder:wasmtime::Config) -> Result<wasmtime::Config> {
75 builder.wasm_component_model(false);
77
78 if self.enable_wasi {
83 dev_log!("wasm", "[WASMRuntime] WASI support enabled, will be configured in linker");
86 }
87
88 if self.enable_fuel_metering {
90 builder.consume_fuel(true);
91 }
92
93 builder.wasm_multi_memory(false);
95
96 builder.wasm_threads(self.allow_threads);
98
99 builder.wasm_reference_types(true);
101
102 builder.wasm_simd(true);
104
105 builder.wasm_bulk_memory(true);
107
108 if self.enable_debug {
110 builder.debug_info(true);
111
112 builder.wasm_backtrace_details(WasmBacktraceDetails::Enable);
113 }
114
115 Ok(builder)
116 }
117}
118
119#[derive(Clone)]
121pub struct WASMRuntime {
122 engine:Engine,
123
124 config:WASMConfig,
125
126 memory_manager:Arc<RwLock<MemoryManagerImpl>>,
127
128 instances:Arc<RwLock<Vec<String>>>,
129}
130
131impl WASMRuntime {
132 pub async fn new(config:WASMConfig) -> Result<Self> {
134 dev_log!("wasm", "Creating WASM runtime with config: {:?}", config);
135
136 let engine_config = wasmtime::Config::new();
138
139 let engine_config = config.apply_to_engine_builder(engine_config)?;
140
141 let engine =
142 Engine::new(&engine_config).map_err(|e| anyhow::anyhow!("Failed to create WASMtime engine: {}", e))?;
143
144 let memory_limits = MemoryLimits {
146 max_memory_mb:config.memory_limit_mb,
147
148 initial_memory_mb:(config.memory_limit_mb as f64 * 0.75) as u64,
150
151 max_table_size:1024,
152
153 max_instances:100,
155
156 max_memories:10,
157
158 max_tables:10,
159 };
160
161 let memory_manager = Arc::new(RwLock::new(MemoryManagerImpl::new(memory_limits)));
162
163 dev_log!("wasm", "WASM runtime created successfully");
164
165 Ok(Self { engine, config, memory_manager, instances:Arc::new(RwLock::new(Vec::new())) })
166 }
167
168 pub fn engine(&self) -> &Engine { &self.engine }
170
171 pub fn config(&self) -> &WASMConfig { &self.config }
173
174 pub fn memory_manager(&self) -> Arc<RwLock<MemoryManagerImpl>> { Arc::clone(&self.memory_manager) }
176
177 pub fn create_store(&self) -> Result<Store<StoreLimits>> {
179 let store_limits = StoreLimitsBuilder::new()
180 .memory_size((self.config.memory_limit_mb * 1024 * 1024) as usize) .table_elements(1024)
182 .instances(100)
183 .memories(10)
184 .tables(10)
185 .build();
186
187 let mut store = Store::new(&self.engine, store_limits);
189
190 if self.config.enable_fuel_metering {
191 let fuel = self.config.max_execution_time_ms * 1_000; store
194 .set_fuel(fuel)
195 .map_err(|e| anyhow::anyhow!("Failed to set fuel limit: {}", e))?;
196 }
197
198 Ok(store)
199 }
200
201 pub fn create_linker<T>(&self, async_support:bool) -> Result<Linker<T>>
203 where
204 T: Send, {
205 let mut linker = Linker::new(&self.engine);
206
207 if self.config.enable_wasi {
209 dev_log!("wasm", "[WASMRuntime] WASI support enabled, will be configured per-instance");
221 }
222
223 if async_support {
225 linker.allow_shadowing(true);
226 }
227
228 Ok(linker)
229 }
230
231 pub fn compile_module(&self, wasm_bytes:&[u8]) -> Result<Module> {
233 dev_log!("wasm", "Compiling WASM module ({} bytes)", wasm_bytes.len());
234
235 let module = Module::from_binary(&self.engine, wasm_bytes)
236 .map_err(|e| anyhow::anyhow!("Failed to compile WASM module: {}", e))?;
237
238 dev_log!("wasm", "WASM module compiled successfully");
239
240 Ok(module)
241 }
242
243 pub fn validate_module(&self, wasm_bytes:&[u8]) -> Result<bool> {
245 dev_log!("wasm", "Validating WASM module ({} bytes)", wasm_bytes.len());
246
247 let result = Module::validate(&self.engine, wasm_bytes);
248
249 match result {
250 Ok(()) => {
251 dev_log!("wasm", "WASM module validation passed");
252
253 Ok(true)
254 },
255
256 Err(e) => {
257 dev_log!("wasm", "WASM module validation failed: {}", e);
258
259 Ok(false)
260 },
261 }
262 }
263
264 pub async fn register_instance(&self, instance_id:String) -> Result<()> {
266 let mut instances = self.instances.write().await;
267
268 if instances.len() >= self.config.memory_limit_mb as usize * 100 {
270 return Err(anyhow::anyhow!("Maximum number of instances exceeded: {}", instances.len()));
271 }
272
273 instances.push(instance_id);
274
275 Ok(())
276 }
277
278 pub async fn unregister_instance(&self, instance_id:&str) -> Result<bool> {
280 let mut instances = self.instances.write().await;
281
282 let pos = instances.iter().position(|id| id == instance_id);
283
284 if let Some(pos) = pos {
285 instances.remove(pos);
286
287 Ok(true)
288 } else {
289 Ok(false)
290 }
291 }
292
293 pub async fn instance_count(&self) -> usize { self.instances.read().await.len() }
295
296 pub async fn shutdown(&self) -> Result<()> {
298 dev_log!("wasm", "Shutting down WASM runtime");
299
300 let instance_count = self.instance_count().await;
301
302 if instance_count > 0 {
303 dev_log!("wasm", "warn: shutting down with {} active instances", instance_count);
304 }
305
306 self.instances.write().await.clear();
308
309 dev_log!("wasm", "WASM runtime shutdown complete");
310
311 Ok(())
312 }
313}
314
315#[cfg(test)]
316mod tests {
317
318 use super::*;
319
320 #[tokio::test]
321 async fn test_wasm_runtime_creation() {
322 let runtime = WASMRuntime::new(WASMConfig::default()).await;
323
324 assert!(runtime.is_ok());
325 }
326
327 #[tokio::test]
328 async fn test_wasm_config_default() {
329 let config = WASMConfig::default();
330
331 assert!(config.enable_wasi);
332
333 assert_eq!(config.memory_limit_mb, 512);
334 }
335
336 #[tokio::test]
337 async fn test_create_store() {
338 let runtime = WASMRuntime::new(WASMConfig::default()).await.unwrap();
339
340 let store = runtime.create_store();
341
342 assert!(store.is_ok());
343 }
344
345 #[tokio::test]
346 async fn test_instance_registration() {
347 let runtime = WASMRuntime::new(WASMConfig::default()).await.unwrap();
348
349 runtime.register_instance("test-instance".to_string()).await.unwrap();
350
351 assert_eq!(runtime.instance_count().await, 1);
352
353 runtime.unregister_instance("test-instance").await.unwrap();
354
355 assert_eq!(runtime.instance_count().await, 0);
356 }
357
358 #[tokio::test]
359 async fn test_validate_module() {
360 let runtime = WASMRuntime::new(WASMConfig::default()).await.unwrap();
361
362 let empty_wasm = vec![
364 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, ];
367
368 let result = runtime.validate_module(&empty_wasm);
370
371 }
374}
375
376impl std::fmt::Debug for WASMRuntime {
377 fn fmt(&self, f:&mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "WASMRuntime") }
378}