Files
hakorune/src/backend/wasm/mod.rs

183 lines
6.4 KiB
Rust
Raw Normal View History

/*!
* WASM Backend - Phase 8 Implementation
*
* Converts MIR instructions to WebAssembly for sandboxed execution
* Targets browser execution and wasmtime runtime
*/
mod codegen;
mod memory;
mod runtime;
// mod executor; // TODO: Fix WASM executor build errors
pub use codegen::{WasmCodegen, WasmModule};
pub use memory::{MemoryManager, BoxLayout};
pub use runtime::RuntimeImports;
// pub use executor::WasmExecutor; // TODO: Fix WASM executor build errors
use crate::mir::MirModule;
/// WASM compilation error
#[derive(Debug)]
pub enum WasmError {
CodegenError(String),
MemoryError(String),
UnsupportedInstruction(String),
WasmValidationError(String),
IOError(String),
}
impl std::fmt::Display for WasmError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
WasmError::CodegenError(msg) => write!(f, "Codegen error: {}", msg),
WasmError::MemoryError(msg) => write!(f, "Memory error: {}", msg),
WasmError::UnsupportedInstruction(msg) => write!(f, "Unsupported instruction: {}", msg),
WasmError::WasmValidationError(msg) => write!(f, "WASM validation error: {}", msg),
WasmError::IOError(msg) => write!(f, "IO error: {}", msg),
}
}
}
impl std::error::Error for WasmError {}
/// Main WASM backend compiler
pub struct WasmBackend {
codegen: WasmCodegen,
memory_manager: MemoryManager,
runtime: RuntimeImports,
}
impl WasmBackend {
/// Create a new WASM backend
pub fn new() -> Self {
Self {
codegen: WasmCodegen::new(),
memory_manager: MemoryManager::new(),
runtime: RuntimeImports::new(),
}
}
/// Compile MIR module to WASM bytes
pub fn compile_module(&mut self, mir_module: MirModule) -> Result<Vec<u8>, WasmError> {
// Generate WAT (WebAssembly Text) first for debugging
let wat_text = self.compile_to_wat(mir_module)?;
// Phase 9.77 Task 1.3: Fix UTF-8 encoding error in WAT→WASM conversion
self.wat_to_wasm(&wat_text)
}
/// Convert WAT text to WASM binary with proper UTF-8 handling
fn wat_to_wasm(&self, wat_source: &str) -> Result<Vec<u8>, WasmError> {
// Debug: Print WAT source for analysis
eprintln!("🔍 WAT Source Debug (length: {}):", wat_source.len());
eprintln!("WAT Content:\n{}", wat_source);
// UTF-8 validation to prevent encoding errors
if !wat_source.is_ascii() {
eprintln!("❌ WAT source contains non-ASCII characters");
return Err(WasmError::WasmValidationError(
"WAT source contains non-ASCII characters".to_string()
));
}
eprintln!("✅ WAT source is ASCII-compatible");
// Convert to bytes as required by wabt::wat2wasm
eprintln!("🔄 Converting WAT to WASM bytes...");
let wasm_bytes = wabt::wat2wasm(wat_source.as_bytes())
.map_err(|e| {
eprintln!("❌ wabt::wat2wasm failed: {}", e);
WasmError::WasmValidationError(format!("WAT to WASM conversion failed: {}", e))
})?;
eprintln!("✅ WASM conversion successful, {} bytes generated", wasm_bytes.len());
Ok(wasm_bytes)
}
/// Compile MIR module to WAT text format (for debugging)
pub fn compile_to_wat(&mut self, mir_module: MirModule) -> Result<String, WasmError> {
let wasm_module = self.codegen.generate_module(mir_module, &self.memory_manager, &self.runtime)?;
Ok(wasm_module.to_wat())
}
/// Execute WASM bytes using wasmtime (for testing)
pub fn execute_wasm(&self, wasm_bytes: &[u8]) -> Result<i32, WasmError> {
let engine = wasmtime::Engine::default();
let module = wasmtime::Module::new(&engine, wasm_bytes)
.map_err(|e| WasmError::WasmValidationError(format!("Module creation failed: {}", e)))?;
let mut store = wasmtime::Store::new(&engine, ());
// Create print function import
let print_func = wasmtime::Func::wrap(&mut store, |value: i32| {
println!("{}", value);
});
// Create print_str function import for string debugging
let print_str_func = wasmtime::Func::wrap(&mut store, |mut caller: wasmtime::Caller<'_, ()>, ptr: i32, len: i32| -> Result<(), wasmtime::Error> {
let memory = caller.get_export("memory")
.and_then(|export| export.into_memory())
.ok_or_else(|| wasmtime::Error::msg("Memory export not found"))?;
let data = memory.data(&caller);
let start = ptr as usize;
let end = start + len as usize;
if end <= data.len() {
let bytes = &data[start..end];
if let Ok(s) = std::str::from_utf8(bytes) {
println!("String: {}", s);
} else {
println!("Invalid UTF-8 bytes: {:?}", bytes);
}
} else {
println!("String out of bounds: ptr={}, len={}, memory_size={}", ptr, len, data.len());
}
Ok(())
});
let imports = [print_func.into(), print_str_func.into()];
let instance = wasmtime::Instance::new(&mut store, &module, &imports)
.map_err(|e| WasmError::WasmValidationError(format!("Instance creation failed: {}", e)))?;
// Call main function
let main_func = instance.get_typed_func::<(), i32>(&mut store, "main")
.map_err(|e| WasmError::WasmValidationError(format!("Main function not found: {}", e)))?;
let result = main_func.call(&mut store, ())
.map_err(|e| WasmError::WasmValidationError(format!("Execution failed: {}", e)))?;
Ok(result)
}
}
impl Default for WasmBackend {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mir::MirModule;
#[test]
fn test_backend_creation() {
let _backend = WasmBackend::new();
// Should not panic
assert!(true);
}
#[test]
fn test_empty_module_compilation() {
let mut backend = WasmBackend::new();
let module = MirModule::new("test".to_string());
// Should handle empty module gracefully
let result = backend.compile_to_wat(module);
assert!(result.is_ok());
}
}