1use std::sync::Arc;
7
8use anyhow::Result;
9use serde::{Deserialize, Serialize};
10use tokio::sync::RwLock;
11use crate::dev_log;
12use wasmtime::{Engine, Linker, Module, Store, StoreLimits, StoreLimitsBuilder, WasmBacktraceDetails};
13
14use crate::WASM::{
15 DEFAULT_MAX_EXECUTION_TIME_MS,
16 DEFAULT_MEMORY_LIMIT_MB,
17 MemoryManager::{MemoryLimits, MemoryManagerImpl},
18};
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct WASMConfig {
23 pub memory_limit_mb:u64,
25 pub max_execution_time_ms:u64,
27 pub enable_wasi:bool,
29 pub enable_debug:bool,
31 pub allow_threads:bool,
33 pub allow_host_memory:bool,
35 pub enable_fuel_metering:bool,
37}
38
39impl Default for WASMConfig {
40 fn default() -> Self {
41 Self {
42 memory_limit_mb:DEFAULT_MEMORY_LIMIT_MB,
43 max_execution_time_ms:DEFAULT_MAX_EXECUTION_TIME_MS,
44 enable_wasi:true,
45 enable_debug:cfg!(debug_assertions),
46 allow_threads:false,
47 allow_host_memory:false,
48 enable_fuel_metering:true,
49 }
50 }
51}
52
53impl WASMConfig {
54 pub fn new(memory_limit_mb:u64, max_execution_time_ms:u64, enable_wasi:bool) -> Self {
56 Self { memory_limit_mb, max_execution_time_ms, enable_wasi, ..Default::default() }
57 }
58
59 fn apply_to_engine_builder(&self, mut builder:wasmtime::Config) -> Result<wasmtime::Config> {
61 builder.wasm_component_model(false);
63
64 if self.enable_wasi {
69 dev_log!("wasm", "[WASMRuntime] WASI support enabled, will be configured in linker");
72 }
73
74 if self.enable_fuel_metering {
76 builder.consume_fuel(true);
77 }
78
79 builder.wasm_multi_memory(false);
81
82 builder.wasm_threads(self.allow_threads);
84
85 builder.wasm_reference_types(true);
87
88 builder.wasm_simd(true);
90
91 builder.wasm_bulk_memory(true);
93
94 if self.enable_debug {
96 builder.debug_info(true);
97 builder.wasm_backtrace_details(WasmBacktraceDetails::Enable);
98 }
99
100 Ok(builder)
101 }
102}
103
104#[derive(Clone)]
106pub struct WASMRuntime {
107 engine:Engine,
108 config:WASMConfig,
109 memory_manager:Arc<RwLock<MemoryManagerImpl>>,
110 instances:Arc<RwLock<Vec<String>>>,
111}
112
113impl WASMRuntime {
114 pub async fn new(config:WASMConfig) -> Result<Self> {
116 dev_log!("wasm", "Creating WASM runtime with config: {:?}", config);
117
118 let engine_config = wasmtime::Config::new();
120 let engine_config = config.apply_to_engine_builder(engine_config)?;
121 let engine =
122 Engine::new(&engine_config).map_err(|e| anyhow::anyhow!("Failed to create WASMtime engine: {}", e))?;
123
124 let memory_limits = MemoryLimits {
126 max_memory_mb:config.memory_limit_mb,
127 initial_memory_mb:(config.memory_limit_mb as f64 * 0.75) as u64,
129 max_table_size:1024,
130 max_instances:100,
132 max_memories:10,
133 max_tables:10,
134 };
135 let memory_manager = Arc::new(RwLock::new(MemoryManagerImpl::new(memory_limits)));
136
137 dev_log!("wasm", "WASM runtime created successfully");
138
139 Ok(Self { engine, config, memory_manager, instances:Arc::new(RwLock::new(Vec::new())) })
140 }
141
142 pub fn engine(&self) -> &Engine { &self.engine }
144
145 pub fn config(&self) -> &WASMConfig { &self.config }
147
148 pub fn memory_manager(&self) -> Arc<RwLock<MemoryManagerImpl>> { Arc::clone(&self.memory_manager) }
150
151 pub fn create_store(&self) -> Result<Store<StoreLimits>> {
153 let store_limits = StoreLimitsBuilder::new()
154 .memory_size((self.config.memory_limit_mb * 1024 * 1024) as usize) .table_elements(1024)
156 .instances(100)
157 .memories(10)
158 .tables(10)
159 .build();
160
161 let mut store = Store::new(&self.engine, store_limits);
163
164 if self.config.enable_fuel_metering {
165 let fuel = self.config.max_execution_time_ms * 1_000; store
168 .set_fuel(fuel)
169 .map_err(|e| anyhow::anyhow!("Failed to set fuel limit: {}", e))?;
170 }
171
172 Ok(store)
173 }
174
175 pub fn create_linker<T>(&self, async_support:bool) -> Result<Linker<T>>
177 where
178 T: Send, {
179 let mut linker = Linker::new(&self.engine);
180
181 if self.config.enable_wasi {
183 dev_log!("wasm", "[WASMRuntime] WASI support enabled, will be configured per-instance");
195 }
196
197 if async_support {
199 linker.allow_shadowing(true);
200 }
201
202 Ok(linker)
203 }
204
205 pub fn compile_module(&self, wasm_bytes:&[u8]) -> Result<Module> {
207 dev_log!("wasm", "Compiling WASM module ({} bytes)", wasm_bytes.len());
208
209 let module = Module::from_binary(&self.engine, wasm_bytes)
210 .map_err(|e| anyhow::anyhow!("Failed to compile WASM module: {}", e))?;
211
212 dev_log!("wasm", "WASM module compiled successfully");
213
214 Ok(module)
215 }
216
217 pub fn validate_module(&self, wasm_bytes:&[u8]) -> Result<bool> {
219 dev_log!("wasm", "Validating WASM module ({} bytes)", wasm_bytes.len());
220
221 let result = Module::validate(&self.engine, wasm_bytes);
222
223 match result {
224 Ok(()) => {
225 dev_log!("wasm", "WASM module validation passed");
226 Ok(true)
227 },
228 Err(e) => {
229 dev_log!("wasm", "WASM module validation failed: {}", e);
230 Ok(false)
231 },
232 }
233 }
234
235 pub async fn register_instance(&self, instance_id:String) -> Result<()> {
237 let mut instances = self.instances.write().await;
238
239 if instances.len() >= self.config.memory_limit_mb as usize * 100 {
241 return Err(anyhow::anyhow!("Maximum number of instances exceeded: {}", instances.len()));
242 }
243
244 instances.push(instance_id);
245 Ok(())
246 }
247
248 pub async fn unregister_instance(&self, instance_id:&str) -> Result<bool> {
250 let mut instances = self.instances.write().await;
251 let pos = instances.iter().position(|id| id == instance_id);
252
253 if let Some(pos) = pos {
254 instances.remove(pos);
255 Ok(true)
256 } else {
257 Ok(false)
258 }
259 }
260
261 pub async fn instance_count(&self) -> usize { self.instances.read().await.len() }
263
264 pub async fn shutdown(&self) -> Result<()> {
266 dev_log!("wasm", "Shutting down WASM runtime");
267
268 let instance_count = self.instance_count().await;
269 if instance_count > 0 {
270 dev_log!("wasm", "warn: shutting down with {} active instances", instance_count);
271 }
272
273 self.instances.write().await.clear();
275
276 dev_log!("wasm", "WASM runtime shutdown complete");
277
278 Ok(())
279 }
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 #[tokio::test]
287 async fn test_wasm_runtime_creation() {
288 let runtime = WASMRuntime::new(WASMConfig::default()).await;
289 assert!(runtime.is_ok());
290 }
291
292 #[tokio::test]
293 async fn test_wasm_config_default() {
294 let config = WASMConfig::default();
295 assert!(config.enable_wasi);
296 assert_eq!(config.memory_limit_mb, 512);
297 }
298
299 #[tokio::test]
300 async fn test_create_store() {
301 let runtime = WASMRuntime::new(WASMConfig::default()).await.unwrap();
302 let store = runtime.create_store();
303 assert!(store.is_ok());
304 }
305
306 #[tokio::test]
307 async fn test_instance_registration() {
308 let runtime = WASMRuntime::new(WASMConfig::default()).await.unwrap();
309
310 runtime.register_instance("test-instance".to_string()).await.unwrap();
311 assert_eq!(runtime.instance_count().await, 1);
312
313 runtime.unregister_instance("test-instance").await.unwrap();
314 assert_eq!(runtime.instance_count().await, 0);
315 }
316
317 #[tokio::test]
318 async fn test_validate_module() {
319 let runtime = WASMRuntime::new(WASMConfig::default()).await.unwrap();
320
321 let empty_wasm = vec![
323 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, ];
326
327 let result = runtime.validate_module(&empty_wasm);
329 }
332}
333
334impl std::fmt::Debug for WASMRuntime {
335 fn fmt(&self, f:&mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "WASMRuntime") }
336}