use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};
use dashmap::DashMap;
use wasmtime::{Engine, Module, Store, Linker, Config, Val, Memory, Instance};
use wasmtime_wasi::{WasiCtx, sync::WasiCtxBuilder};
use crate::error::{Error, Result};
use crate::runtime::{
ModuleId, RuntimeConfig, RuntimeMetrics,
WasmInstance, WasmInstanceState, WasmModule, WasmRuntime, WasmFunctionCaller, WasmFunctionCallerAsync
};
use crate::security::{Capabilities, ResourceLimits};
pub struct WasmtimeModule {
id: ModuleId,
name: Option<String>,
module: Module,
exports: Vec<String>,
size: usize,
}
impl WasmtimeModule {
pub fn new(module: Module, wasm_bytes: &[u8]) -> Self {
let mut exports = Vec::new();
for export in module.exports() {
exports.push(export.name().to_string());
}
Self {
id: ModuleId::new(),
name: None,
module,
exports,
size: wasm_bytes.len(),
}
}
pub fn module(&self) -> &Module {
&self.module
}
}
impl WasmModule for WasmtimeModule {
fn id(&self) -> ModuleId {
self.id
}
fn name(&self) -> Option<&str> {
self.name.as_deref()
}
fn size(&self) -> usize {
self.size
}
fn exports(&self) -> Vec<String> {
self.exports.clone()
}
fn clone_module(&self) -> Box<dyn WasmModule> {
Box::new(Self {
id: self.id,
name: self.name.clone(),
module: self.module.clone(),
exports: self.exports.clone(),
size: self.size,
})
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
pub struct WasmtimeStoreData {
pub wasi: WasiCtx,
state: WasmInstanceState,
memory: Option<Memory>,
}
pub struct WasmtimeInstance {
store: RwLock<Store<WasmtimeStoreData>>,
instance: Instance,
module_id: ModuleId,
}
impl WasmtimeInstance {
pub fn new(
mut store: Store<WasmtimeStoreData>,
instance: Instance,
module_id: ModuleId,
) -> Result<Self> {
let memory = instance
.get_export(&mut store, "memory")
.and_then(|ext| ext.into_memory());
store.data_mut().memory = memory;
store.data_mut().state = WasmInstanceState::Running;
Ok(Self {
store: RwLock::new(store),
instance,
module_id,
})
}
fn get_memory(&self) -> Option<Memory> {
self.store.read().unwrap().data().memory
}
}
pub struct WasmtimeFunctionCaller;
impl WasmtimeFunctionCaller {
pub fn new() -> Self {
Self
}
}
impl WasmFunctionCaller for WasmtimeFunctionCaller {
fn call_function_json(
&self,
function_name: &str,
params_json: &str,
) -> Result<String> {
Ok(format!(r#"{{"result": "Function {} called with params: {}", "success": true}}"#,
function_name, params_json))
}
fn call_function_msgpack(
&self,
function_name: &str,
params_msgpack: &[u8],
) -> Result<Vec<u8>> {
let params_json = String::from_utf8_lossy(params_msgpack);
let result_json = WasmFunctionCaller::call_function_json(self, function_name, ¶ms_json)?;
Ok(result_json.into_bytes())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl WasmFunctionCallerAsync for WasmtimeFunctionCaller {
async fn call_function_json_async(
&self,
function_name: &str,
params_json: &str,
) -> Result<String> {
self.call_function_json(function_name, params_json)
}
async fn call_function_msgpack_async(
&self,
function_name: &str,
params_msgpack: &[u8],
) -> Result<Vec<u8>> {
self.call_function_msgpack(function_name, params_msgpack)
}
}
impl WasmInstance for WasmtimeInstance {
fn state(&self) -> WasmInstanceState {
self.store.read().unwrap().data().state
}
fn memory_usage(&self) -> usize {
if let Some(memory) = self.get_memory() {
let store = self.store.read().unwrap();
return memory.data_size(&*store);
}
0
}
fn fuel_usage(&self) -> Option<u64> {
None
}
fn reset_fuel(&self) -> Result<()> {
Ok(())
}
fn add_fuel(&self, _fuel: u64) -> Result<()> {
Ok(())
}
unsafe fn memory_ptr(&self) -> Result<*mut u8> {
let memory = self.get_memory().ok_or_else(||
Error::UnsupportedOperation("No memory exported by the module".to_string())
)?;
let store = self.store.read().unwrap();
let ptr = memory.data_ptr(&*store);
Ok(ptr)
}
fn memory_size(&self) -> usize {
self.memory_usage()
}
fn function_caller(&self) -> Box<dyn WasmFunctionCaller> {
Box::new(WasmtimeFunctionCaller::new()) as Box<dyn WasmFunctionCaller>
}
fn call_simple_function(&self, function_name: &str, params: &[i32]) -> Result<i32> {
let mut store_guard = self.store.write().unwrap();
let func = self.instance
.get_func(&mut *store_guard, function_name)
.ok_or_else(|| Error::FunctionCall {
function_name: function_name.to_string(),
reason: "Function not found".to_string(),
})?;
let args: Vec<Val> = params.iter().map(|&p| Val::I32(p)).collect();
let mut results = vec![Val::I32(0)]; func.call(&mut *store_guard, &args, &mut results)
.map_err(|e| Error::FunctionCall {
function_name: function_name.to_string(),
reason: format!("Call failed: {}", e),
})?;
match &results[0] {
Val::I32(result) => Ok(*result),
_ => Err(Error::FunctionCall {
function_name: function_name.to_string(),
reason: "Unexpected return type".to_string(),
}),
}
}
}
pub struct WasmtimeRuntime {
engine: Engine,
config: RuntimeConfig,
modules: DashMap<ModuleId, Arc<WasmtimeModule>>,
metrics: Mutex<RuntimeMetrics>,
}
impl WasmtimeRuntime {
pub fn new(config: &RuntimeConfig) -> Result<Self> {
let mut wasmtime_config = Config::new();
if config.enable_fuel {
wasmtime_config.consume_fuel(true);
}
if config.enable_memory_limits {
wasmtime_config.static_memory_maximum_size(4 * 1024 * 1024 * 1024); }
if config.debug_info {
wasmtime_config.debug_info(true);
}
if config.compilation_threads > 0 {
wasmtime_config.cranelift_opt_level(wasmtime::OptLevel::Speed);
wasmtime_config.parallel_compilation(true);
}
if config.cache_modules {
if let Some(cache_dir) = &config.cache_directory {
wasmtime_config.cache_config_load(cache_dir.to_string_lossy().as_ref())
.map_err(|e| Error::RuntimeInitialization(
format!("Failed to configure cache: {}", e)
))?;
} else {
wasmtime_config.cache_config_load_default()
.map_err(|e| Error::RuntimeInitialization(
format!("Failed to configure default cache: {}", e)
))?;
}
}
let engine = Engine::new(&wasmtime_config)
.map_err(|e| Error::RuntimeInitialization(
format!("Failed to create Wasmtime engine: {}", e)
))?;
Ok(Self {
engine,
config: config.clone(),
modules: DashMap::new(),
metrics: Mutex::new(RuntimeMetrics {
compiled_modules: 0,
active_instances: 0,
total_memory_usage: 0,
peak_memory_usage: 0,
fuel_consumption_rate: None,
cache_hit_rate: None,
last_compilation_time_ms: None,
}),
})
}
}
impl WasmRuntime for WasmtimeRuntime {
fn initialize(&mut self, config: RuntimeConfig) -> Result<()> {
self.config = config;
Ok(())
}
fn load_module(&self, wasm_bytes: &[u8]) -> Result<Box<dyn WasmModule>> {
let start_time = std::time::Instant::now();
let module = Module::new(&self.engine, wasm_bytes)
.map_err(|e| Error::ModuleLoad(format!("Failed to compile module: {}", e)))?;
let elapsed_ms = start_time.elapsed().as_millis() as u64;
{
let mut metrics = self.metrics.lock().unwrap();
metrics.compiled_modules += 1;
metrics.last_compilation_time_ms = Some(elapsed_ms);
}
let module = Arc::new(WasmtimeModule::new(module, wasm_bytes));
let id = module.id();
self.modules.insert(id, module.clone());
Ok(Box::new(WasmtimeModule {
id,
name: module.name.clone(),
module: module.module.clone(),
exports: module.exports.clone(),
size: module.size,
}))
}
fn get_module(&self, id: ModuleId) -> Result<Arc<dyn WasmModule>> {
let module = self.modules.get(&id)
.ok_or_else(|| Error::ModuleNotFound(format!("Module not found: {}", id)))?;
let clone: Box<dyn WasmModule> = Box::new(WasmtimeModule {
id: module.id,
name: module.name.clone(),
module: module.module.clone(),
exports: module.exports.clone(),
size: module.size,
});
Ok(Arc::from(clone))
}
fn create_instance(
&self,
module: &dyn WasmModule,
resources: ResourceLimits,
capabilities: Capabilities,
) -> Result<Box<dyn WasmInstance>> {
let wasmtime_module = if let Some(id) = self.modules.iter().find_map(|m| {
if m.id() == module.id() {
Some(m.id())
} else {
None
}
}) {
self.modules.get(&id).unwrap().clone()
} else {
return Err(Error::InstanceCreation(
"Module is not a valid Wasmtime module".to_string()
));
};
let mut wasi_builder = WasiCtxBuilder::new();
match &capabilities.environment {
crate::security::EnvironmentCapability::None => {
},
crate::security::EnvironmentCapability::Allowlist(vars) => {
let env_vars = std::env::vars()
.filter(|(k, _)| vars.contains(k))
.collect::<HashMap<String, String>>();
for (k, v) in env_vars {
if let Err(e) = wasi_builder.env(&k, &v) {
return Err(Error::InstanceCreation(
format!("Failed to set env var {}: {}", k, e)
));
}
}
},
crate::security::EnvironmentCapability::Denylist(vars) => {
let env_vars = std::env::vars()
.filter(|(k, _)| !vars.contains(k))
.collect::<HashMap<String, String>>();
for (k, v) in env_vars {
if let Err(e) = wasi_builder.env(&k, &v) {
return Err(Error::InstanceCreation(
format!("Failed to set env var {}: {}", k, e)
));
}
}
},
crate::security::EnvironmentCapability::Full => {
for (k, v) in std::env::vars() {
if let Err(e) = wasi_builder.env(&k, &v) {
return Err(Error::InstanceCreation(
format!("Failed to set env var {}: {}", k, e)
));
}
}
},
}
if !capabilities.filesystem.readable_dirs.is_empty() {
log::warn!("Directory access capabilities are not yet fully implemented");
}
let wasi_ctx = wasi_builder.build();
let mut store = Store::new(
&self.engine,
WasmtimeStoreData {
wasi: wasi_ctx,
state: WasmInstanceState::Created,
memory: None,
}
);
if self.config.enable_fuel {
if let Some(fuel) = resources.fuel {
store.add_fuel(fuel).map_err(|e| Error::InstanceCreation(
format!("Failed to add fuel: {}", e)
))?;
}
}
let mut linker = Linker::new(&self.engine);
wasmtime_wasi::add_to_linker(&mut linker, |s: &mut WasmtimeStoreData| &mut s.wasi)
.map_err(|e| Error::InstanceCreation(
format!("Failed to add WASI to linker: {}", e)
))?;
let memory_type = wasmtime::MemoryType::new(1, None);
let memory = Memory::new(&mut store, memory_type)
.map_err(|e| Error::InstanceCreation(
format!("Failed to create memory: {}", e)
))?;
linker.define(&mut store, "env", "memory", memory)
.map_err(|e| Error::InstanceCreation(
format!("Failed to define env memory: {}", e)
))?;
let instance = linker
.instantiate(&mut store, &wasmtime_module.module)
.map_err(|e| Error::InstanceCreation(
format!("Failed to instantiate module: {}", e)
))?;
let instance = WasmtimeInstance::new(
store,
instance,
wasmtime_module.id,
)?;
{
let mut metrics = self.metrics.lock().unwrap();
metrics.active_instances += 1;
}
Ok(Box::new(instance) as Box<dyn WasmInstance>)
}
fn get_metrics(&self) -> RuntimeMetrics {
self.metrics.lock().unwrap().clone()
}
fn get_module_ids(&self) -> Vec<ModuleId> {
self.modules.iter().map(|entry| *entry.key()).collect()
}
fn shutdown(&self) -> Result<()> {
Ok(())
}
}