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 pub max_execution_time_ms:u64,
29 pub enable_wasi:bool,
31 pub enable_debug:bool,
33 pub allow_threads:bool,
35 pub allow_host_memory:bool,
37 pub enable_fuel_metering:bool,
39}
40
41impl Default for WASMConfig {
42 fn default() -> Self {
43 Self {
44 memory_limit_mb:DEFAULT_MEMORY_LIMIT_MB,
45 max_execution_time_ms:DEFAULT_MAX_EXECUTION_TIME_MS,
46 enable_wasi:true,
47 enable_debug:cfg!(debug_assertions),
48 allow_threads:false,
49 allow_host_memory:false,
50 enable_fuel_metering:true,
51 }
52 }
53}
54
55impl WASMConfig {
56 pub fn new(memory_limit_mb:u64, max_execution_time_ms:u64, enable_wasi:bool) -> Self {
58 Self { memory_limit_mb, max_execution_time_ms, enable_wasi, ..Default::default() }
59 }
60
61 fn apply_to_engine_builder(&self, mut builder:wasmtime::Config) -> Result<wasmtime::Config> {
63 builder.wasm_component_model(false);
65
66 if self.enable_wasi {
71 dev_log!("wasm", "[WASMRuntime] WASI support enabled, will be configured in linker");
74 }
75
76 if self.enable_fuel_metering {
78 builder.consume_fuel(true);
79 }
80
81 builder.wasm_multi_memory(false);
83
84 builder.wasm_threads(self.allow_threads);
86
87 builder.wasm_reference_types(true);
89
90 builder.wasm_simd(true);
92
93 builder.wasm_bulk_memory(true);
95
96 if self.enable_debug {
98 builder.debug_info(true);
99 builder.wasm_backtrace_details(WasmBacktraceDetails::Enable);
100 }
101
102 Ok(builder)
103 }
104}
105
106#[derive(Clone)]
108pub struct WASMRuntime {
109 engine:Engine,
110 config:WASMConfig,
111 memory_manager:Arc<RwLock<MemoryManagerImpl>>,
112 instances:Arc<RwLock<Vec<String>>>,
113}
114
115impl WASMRuntime {
116 pub async fn new(config:WASMConfig) -> Result<Self> {
118 dev_log!("wasm", "Creating WASM runtime with config: {:?}", config);
119
120 let engine_config = wasmtime::Config::new();
122 let engine_config = config.apply_to_engine_builder(engine_config)?;
123 let engine =
124 Engine::new(&engine_config).map_err(|e| anyhow::anyhow!("Failed to create WASMtime engine: {}", e))?;
125
126 let memory_limits = MemoryLimits {
128 max_memory_mb:config.memory_limit_mb,
129 initial_memory_mb:(config.memory_limit_mb as f64 * 0.75) as u64,
131 max_table_size:1024,
132 max_instances:100,
134 max_memories:10,
135 max_tables:10,
136 };
137 let memory_manager = Arc::new(RwLock::new(MemoryManagerImpl::new(memory_limits)));
138
139 dev_log!("wasm", "WASM runtime created successfully");
140
141 Ok(Self { engine, config, memory_manager, instances:Arc::new(RwLock::new(Vec::new())) })
142 }
143
144 pub fn engine(&self) -> &Engine { &self.engine }
146
147 pub fn config(&self) -> &WASMConfig { &self.config }
149
150 pub fn memory_manager(&self) -> Arc<RwLock<MemoryManagerImpl>> { Arc::clone(&self.memory_manager) }
152
153 pub fn create_store(&self) -> Result<Store<StoreLimits>> {
155 let store_limits = StoreLimitsBuilder::new()
156 .memory_size((self.config.memory_limit_mb * 1024 * 1024) as usize) .table_elements(1024)
158 .instances(100)
159 .memories(10)
160 .tables(10)
161 .build();
162
163 let mut store = Store::new(&self.engine, store_limits);
165
166 if self.config.enable_fuel_metering {
167 let fuel = self.config.max_execution_time_ms * 1_000; store
170 .set_fuel(fuel)
171 .map_err(|e| anyhow::anyhow!("Failed to set fuel limit: {}", e))?;
172 }
173
174 Ok(store)
175 }
176
177 pub fn create_linker<T>(&self, async_support:bool) -> Result<Linker<T>>
179 where
180 T: Send, {
181 let mut linker = Linker::new(&self.engine);
182
183 if self.config.enable_wasi {
185 dev_log!("wasm", "[WASMRuntime] WASI support enabled, will be configured per-instance");
197 }
198
199 if async_support {
201 linker.allow_shadowing(true);
202 }
203
204 Ok(linker)
205 }
206
207 pub fn compile_module(&self, wasm_bytes:&[u8]) -> Result<Module> {
209 dev_log!("wasm", "Compiling WASM module ({} bytes)", wasm_bytes.len());
210
211 let module = Module::from_binary(&self.engine, wasm_bytes)
212 .map_err(|e| anyhow::anyhow!("Failed to compile WASM module: {}", e))?;
213
214 dev_log!("wasm", "WASM module compiled successfully");
215
216 Ok(module)
217 }
218
219 pub fn validate_module(&self, wasm_bytes:&[u8]) -> Result<bool> {
221 dev_log!("wasm", "Validating WASM module ({} bytes)", wasm_bytes.len());
222
223 let result = Module::validate(&self.engine, wasm_bytes);
224
225 match result {
226 Ok(()) => {
227 dev_log!("wasm", "WASM module validation passed");
228 Ok(true)
229 },
230 Err(e) => {
231 dev_log!("wasm", "WASM module validation failed: {}", e);
232 Ok(false)
233 },
234 }
235 }
236
237 pub async fn register_instance(&self, instance_id:String) -> Result<()> {
239 let mut instances = self.instances.write().await;
240
241 if instances.len() >= self.config.memory_limit_mb as usize * 100 {
243 return Err(anyhow::anyhow!("Maximum number of instances exceeded: {}", instances.len()));
244 }
245
246 instances.push(instance_id);
247 Ok(())
248 }
249
250 pub async fn unregister_instance(&self, instance_id:&str) -> Result<bool> {
252 let mut instances = self.instances.write().await;
253 let pos = instances.iter().position(|id| id == instance_id);
254
255 if let Some(pos) = pos {
256 instances.remove(pos);
257 Ok(true)
258 } else {
259 Ok(false)
260 }
261 }
262
263 pub async fn instance_count(&self) -> usize { self.instances.read().await.len() }
265
266 pub async fn shutdown(&self) -> Result<()> {
268 dev_log!("wasm", "Shutting down WASM runtime");
269
270 let instance_count = self.instance_count().await;
271 if instance_count > 0 {
272 dev_log!("wasm", "warn: shutting down with {} active instances", instance_count);
273 }
274
275 self.instances.write().await.clear();
277
278 dev_log!("wasm", "WASM runtime shutdown complete");
279
280 Ok(())
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 #[tokio::test]
289 async fn test_wasm_runtime_creation() {
290 let runtime = WASMRuntime::new(WASMConfig::default()).await;
291 assert!(runtime.is_ok());
292 }
293
294 #[tokio::test]
295 async fn test_wasm_config_default() {
296 let config = WASMConfig::default();
297 assert!(config.enable_wasi);
298 assert_eq!(config.memory_limit_mb, 512);
299 }
300
301 #[tokio::test]
302 async fn test_create_store() {
303 let runtime = WASMRuntime::new(WASMConfig::default()).await.unwrap();
304 let store = runtime.create_store();
305 assert!(store.is_ok());
306 }
307
308 #[tokio::test]
309 async fn test_instance_registration() {
310 let runtime = WASMRuntime::new(WASMConfig::default()).await.unwrap();
311
312 runtime.register_instance("test-instance".to_string()).await.unwrap();
313 assert_eq!(runtime.instance_count().await, 1);
314
315 runtime.unregister_instance("test-instance").await.unwrap();
316 assert_eq!(runtime.instance_count().await, 0);
317 }
318
319 #[tokio::test]
320 async fn test_validate_module() {
321 let runtime = WASMRuntime::new(WASMConfig::default()).await.unwrap();
322
323 let empty_wasm = vec![
325 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, ];
328
329 let result = runtime.validate_module(&empty_wasm);
331 }
334}
335
336impl std::fmt::Debug for WASMRuntime {
337 fn fmt(&self, f:&mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "WASMRuntime") }
338}