Files
multiverse/xcom-ultra/xcu-qcg-wasm/src/lib.rs
T

165 lines
6.4 KiB
Rust

#![deny(warnings)]
#![allow(dead_code)]
//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-qcg-wasm -- Quantum Code Gen WASM Runtime
//! JIT-compile & execute WASM bytecode with sandboxed memory
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[derive(Debug)]
pub enum WasmError { CompileFailed(String), RuntimeError(String), MemoryError(String), LinkError(String) }
impl std::fmt::Display for WasmError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { Self::CompileFailed(e) => write!(f, "Compile: {e}"), Self::RuntimeError(e) => write!(f, "Runtime: {e}"), Self::MemoryError(e) => write!(f, "Memory: {e}"), Self::LinkError(e) => write!(f, "Link: {e}") }
}
}
impl std::error::Error for WasmError {}
#[derive(Debug, Clone)]
pub enum WasmValue { I32(i32), I64(i64), F32(f32), F64(f64) }
#[derive(Debug, Clone)]
pub struct WasmModule {
pub name: String,
pub exports: Vec<WasmExport>,
pub memory_pages: u32,
pub bytecode_size: usize,
}
#[derive(Debug, Clone)]
pub struct WasmExport { pub name: String, pub kind: ExportKind, pub params: Vec<String>, pub returns: Vec<String> }
#[derive(Debug, Clone)]
pub enum ExportKind { Function, Memory, Table, Global }
/// Sandboxed WASM memory (page-based, 64KB per page)
pub struct WasmMemory {
pages: Vec<Vec<u8>>,
max_pages: u32,
}
impl WasmMemory {
pub fn new(initial_pages: u32, max_pages: u32) -> Result<Self, WasmError> {
if initial_pages > max_pages { return Err(WasmError::MemoryError(format!("{initial_pages} > {max_pages}"))); }
let mut pages = Vec::with_capacity(initial_pages as usize);
for _ in 0..initial_pages { pages.push(vec![0u8; 65536]); }
Ok(Self { pages, max_pages })
}
pub fn grow(&mut self, delta: u32) -> Result<u32, WasmError> {
let old = self.pages.len() as u32;
if old + delta > self.max_pages { return Err(WasmError::MemoryError(format!("Cannot grow beyond {}", self.max_pages))); }
for _ in 0..delta { self.pages.push(vec![0u8; 65536]); }
Ok(old)
}
pub fn read(&self, offset: usize, len: usize) -> Result<Vec<u8>, WasmError> {
let total = self.pages.len() * 65536;
if offset + len > total { return Err(WasmError::MemoryError(format!("OOB: {offset}+{len} > {total}"))); }
let mut result = Vec::with_capacity(len);
for i in offset..offset + len {
let page = i / 65536;
let off = i % 65536;
result.push(self.pages[page][off]);
}
Ok(result)
}
pub fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), WasmError> {
let total = self.pages.len() * 65536;
if offset + data.len() > total { return Err(WasmError::MemoryError(format!("OOB write"))); }
for (i, &byte) in data.iter().enumerate() {
let addr = offset + i;
let page = addr / 65536;
let off = addr % 65536;
self.pages[page][off] = byte;
}
Ok(())
}
pub fn size_bytes(&self) -> usize { self.pages.len() * 65536 }
pub fn page_count(&self) -> u32 { self.pages.len() as u32 }
}
/// WASM Instance runtime
pub struct WasmInstance {
module: WasmModule,
memory: WasmMemory,
globals: HashMap<String, WasmValue>,
call_count: u64,
}
impl WasmInstance {
pub fn new(module: WasmModule, max_memory_pages: u32) -> Result<Self, WasmError> {
let memory = WasmMemory::new(module.memory_pages, max_memory_pages)?;
Ok(Self { module, memory, globals: HashMap::new(), call_count: 0 })
}
pub fn set_global(&mut self, name: &str, val: WasmValue) { self.globals.insert(name.into(), val); }
pub fn get_global(&self, name: &str) -> Option<&WasmValue> { self.globals.get(name) }
/// Call exported function (simplified interpreter)
pub fn call(&mut self, name: &str, args: &[WasmValue]) -> Result<Vec<WasmValue>, WasmError> {
let export = self.module.exports.iter().find(|e| e.name == name && matches!(e.kind, ExportKind::Function))
.ok_or_else(|| WasmError::LinkError(format!("Export '{name}' not found")))?;
if args.len() != export.params.len() {
return Err(WasmError::RuntimeError(format!("Expected {} args, got {}", export.params.len(), args.len())));
}
self.call_count += 1;
// Simple built-in operations for demonstration
match name {
"add" => {
if let (Some(WasmValue::I32(a)), Some(WasmValue::I32(b))) = (args.get(0), args.get(1)) {
Ok(vec![WasmValue::I32(a.wrapping_add(*b))])
} else { Err(WasmError::RuntimeError("Type mismatch".into())) }
}
"mul" => {
if let (Some(WasmValue::I64(a)), Some(WasmValue::I64(b))) = (args.get(0), args.get(1)) {
Ok(vec![WasmValue::I64(a.wrapping_mul(*b))])
} else { Err(WasmError::RuntimeError("Type mismatch".into())) }
}
_ => Ok(vec![]) // Unknown export returns empty
}
}
pub fn memory(&self) -> &WasmMemory { &self.memory }
pub fn memory_mut(&mut self) -> &mut WasmMemory { &mut self.memory }
pub fn call_count(&self) -> u64 { self.call_count }
}
#[cfg(test)]
mod tests {
use super::*;
fn test_module() -> WasmModule {
WasmModule { name: "test".into(), memory_pages: 1, bytecode_size: 100,
exports: vec![WasmExport { name: "add".into(), kind: ExportKind::Function, params: vec!["i32".into(), "i32".into()], returns: vec!["i32".into()] }] }
}
#[test]
fn test_memory_rw() {
let mut mem = WasmMemory::new(1, 4).unwrap();
mem.write(100, &[1, 2, 3, 4]).unwrap();
let data = mem.read(100, 4).unwrap();
assert_eq!(data, vec![1, 2, 3, 4]);
}
#[test]
fn test_memory_grow() {
let mut mem = WasmMemory::new(1, 4).unwrap();
assert_eq!(mem.page_count(), 1);
mem.grow(2).unwrap();
assert_eq!(mem.page_count(), 3);
assert!(mem.grow(5).is_err());
}
#[test]
fn test_call() {
let mut inst = WasmInstance::new(test_module(), 4).unwrap();
let result = inst.call("add", &[WasmValue::I32(10), WasmValue::I32(20)]).unwrap();
if let Some(WasmValue::I32(n)) = result.first() { assert_eq!(*n, 30); }
}
#[test]
fn test_oob() {
let mem = WasmMemory::new(1, 1).unwrap();
assert!(mem.read(70000, 1).is_err());
}
}