/*! * WASM Memory Management - Box layout and heap allocation * * Phase 8.3 PoC2: Implements bump allocator and Box memory layout * Memory Layout: 0x000-0x3FF (reserved), 0x400-0x7FF (stack), 0x800+ (heap) */ use super::WasmError; use std::collections::HashMap; /// Box memory layout definition #[derive(Debug, Clone)] pub struct BoxLayout { pub type_id: u32, pub size: u32, pub field_offsets: HashMap, } impl BoxLayout { pub fn new(type_name: &str) -> Self { // Assign consistent type IDs for standard Box types let type_id = match type_name { "StringBox" => 0x1001, "IntegerBox" => 0x1002, "BoolBox" => 0x1003, "ArrayBox" => 0x1004, "DataBox" => 0x1005, // For testing _ => { // Generate ID from hash for custom types type_name.chars().map(|c| c as u32).sum::() % 65536 + 0x2000 } }; Self { type_id, size: 12, // Header: type_id + ref_count + field_count field_offsets: HashMap::new(), } } pub fn add_field(&mut self, field_name: String) { let offset = self.size; self.field_offsets.insert(field_name, offset); self.size += 4; // Each field is 4 bytes (i32) } pub fn get_field_offset(&self, field_name: &str) -> Option { self.field_offsets.get(field_name).copied() } } /// WASM memory manager pub struct MemoryManager { /// Known Box layouts box_layouts: HashMap, /// Current heap pointer (starts at 0x800) heap_start: u32, } impl MemoryManager { pub fn new() -> Self { let mut manager = Self { box_layouts: HashMap::new(), heap_start: 0x800, // 2KB reserved for stack/globals }; // Register standard Box types manager.register_standard_box_types(); manager } /// Register standard built-in Box types fn register_standard_box_types(&mut self) { // StringBox: [type_id][ref_count][field_count][ptr_to_chars][length] self.register_box_type("StringBox".to_string(), vec!["data_ptr".to_string(), "length".to_string()]); // IntegerBox: [type_id][ref_count][field_count][value] self.register_box_type("IntegerBox".to_string(), vec!["value".to_string()]); // BoolBox: [type_id][ref_count][field_count][value] self.register_box_type("BoolBox".to_string(), vec!["value".to_string()]); // DataBox: [type_id][ref_count][field_count][value] - for testing self.register_box_type("DataBox".to_string(), vec!["value".to_string()]); } /// Register a Box type layout pub fn register_box_type(&mut self, type_name: String, fields: Vec) { let mut layout = BoxLayout::new(&type_name); for field in fields { layout.add_field(field); } self.box_layouts.insert(type_name, layout); } /// Get Box layout by type name pub fn get_box_layout(&self, type_name: &str) -> Option<&BoxLayout> { self.box_layouts.get(type_name) } /// Generate WASM globals for heap management pub fn get_globals(&self) -> Vec { vec![ format!("(global $heap_ptr (mut i32) (i32.const {}))", self.heap_start), ] } /// Generate heap allocation function with 4-byte alignment pub fn get_malloc_function(&self) -> String { format!( r#"(func $malloc (param $size i32) (result i32) (local $ptr i32) (local $aligned_size i32) ;; Align size to 4-byte boundary local.get $size i32.const 3 i32.add i32.const -4 i32.and local.set $aligned_size ;; Get current heap pointer global.get $heap_ptr local.set $ptr ;; Advance heap pointer by aligned size global.get $heap_ptr local.get $aligned_size i32.add global.set $heap_ptr ;; Return allocated pointer local.get $ptr )"# ) } /// Generate Box allocation function for specific type pub fn get_box_alloc_function(&self, type_name: &str) -> Result { let layout = self.get_box_layout(type_name) .ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?; Ok(format!( r#"(func $alloc_{} (result i32) (local $ptr i32) ;; Allocate memory for box i32.const {} call $malloc local.set $ptr ;; Initialize type_id local.get $ptr i32.const {} i32.store ;; Initialize ref_count to 1 local.get $ptr i32.const 4 i32.add i32.const 1 i32.store ;; Initialize field_count local.get $ptr i32.const 8 i32.add i32.const {} i32.store ;; Return box pointer local.get $ptr )"#, type_name.to_lowercase(), layout.size, layout.type_id, layout.field_offsets.len() )) } /// Generate field getter function pub fn get_field_get_function(&self, type_name: &str, field_name: &str) -> Result { let layout = self.get_box_layout(type_name) .ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?; let offset = layout.get_field_offset(field_name) .ok_or_else(|| WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name)))?; Ok(format!( r#"(func $get_{}_{} (param $box_ptr i32) (result i32) ;; Verify type_id (optional safety check) local.get $box_ptr i32.load i32.const {} i32.ne if i32.const 0 return end ;; Load field value local.get $box_ptr i32.const {} i32.add i32.load )"#, type_name.to_lowercase(), field_name, layout.type_id, offset )) } /// Generate field setter function pub fn get_field_set_function(&self, type_name: &str, field_name: &str) -> Result { let layout = self.get_box_layout(type_name) .ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?; let offset = layout.get_field_offset(field_name) .ok_or_else(|| WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name)))?; Ok(format!( r#"(func $set_{}_{} (param $box_ptr i32) (param $value i32) ;; Verify type_id (optional safety check) local.get $box_ptr i32.load i32.const {} i32.ne if return end ;; Store field value local.get $box_ptr i32.const {} i32.add local.get $value i32.store )"#, type_name.to_lowercase(), field_name, layout.type_id, offset )) } /// Get memory layout constants for documentation pub fn get_memory_layout_info(&self) -> String { format!( r#" ;; Memory Layout: ;; 0x000-0x3FF: Reserved/globals (1KB) ;; 0x400-0x7FF: Stack space (1KB) ;; 0x800+: Heap (bump allocator) ;; ;; Box Layout: [type_id:i32][ref_count:i32][field_count:i32][field0:i32][field1:i32]... ;; ;; Standard Type IDs: ;; StringBox: 0x1001, IntegerBox: 0x1002, BoolBox: 0x1003 ;; ArrayBox: 0x1004, DataBox: 0x1005 ;; Custom: 0x2000+ ;; ;; Heap start: 0x{:x} "#, self.heap_start ) } /// Get type ID for a Box type pub fn get_type_id(&self, type_name: &str) -> Option { self.box_layouts.get(type_name).map(|layout| layout.type_id) } /// Generate generic Box creation helper pub fn get_generic_box_alloc_function(&self) -> String { format!( r#"(func $box_alloc (param $type_id i32) (param $field_count i32) (result i32) (local $ptr i32) (local $total_size i32) ;; Calculate total size: header (12) + fields (field_count * 4) local.get $field_count i32.const 4 i32.mul i32.const 12 i32.add local.set $total_size ;; Allocate memory local.get $total_size call $malloc local.set $ptr ;; Initialize type_id local.get $ptr local.get $type_id i32.store ;; Initialize ref_count to 1 local.get $ptr i32.const 4 i32.add i32.const 1 i32.store ;; Initialize field_count local.get $ptr i32.const 8 i32.add local.get $field_count i32.store ;; Return box pointer local.get $ptr )"# ) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_box_layout_creation() { let layout = BoxLayout::new("DataBox"); assert_eq!(layout.size, 12); // type_id + ref_count + field_count assert_eq!(layout.type_id, 0x1005); // DataBox has specific ID assert!(layout.field_offsets.is_empty()); } #[test] fn test_box_layout_field_addition() { let mut layout = BoxLayout::new("DataBox"); layout.add_field("field1".to_string()); layout.add_field("field2".to_string()); assert_eq!(layout.size, 20); // 12 + 4 + 4 assert_eq!(layout.get_field_offset("field1"), Some(12)); assert_eq!(layout.get_field_offset("field2"), Some(16)); } #[test] fn test_memory_manager_standard_types() { let manager = MemoryManager::new(); // Verify standard types are registered assert!(manager.get_box_layout("StringBox").is_some()); assert!(manager.get_box_layout("IntegerBox").is_some()); assert!(manager.get_box_layout("BoolBox").is_some()); assert!(manager.get_box_layout("DataBox").is_some()); // Verify type IDs assert_eq!(manager.get_type_id("StringBox"), Some(0x1001)); assert_eq!(manager.get_type_id("IntegerBox"), Some(0x1002)); assert_eq!(manager.get_type_id("DataBox"), Some(0x1005)); } #[test] fn test_memory_manager_registration() { let mut manager = MemoryManager::new(); manager.register_box_type("CustomBox".to_string(), vec!["x".to_string(), "y".to_string()]); let layout = manager.get_box_layout("CustomBox").unwrap(); assert_eq!(layout.field_offsets.len(), 2); assert!(layout.get_field_offset("x").is_some()); assert!(layout.get_field_offset("y").is_some()); assert!(layout.type_id >= 0x2000); // Custom types start at 0x2000 } #[test] fn test_malloc_function_generation() { let manager = MemoryManager::new(); let malloc_func = manager.get_malloc_function(); assert!(malloc_func.contains("$malloc")); assert!(malloc_func.contains("$heap_ptr")); assert!(malloc_func.contains("global.get")); assert!(malloc_func.contains("i32.and")); // Alignment check } #[test] fn test_box_alloc_function_generation() { let manager = MemoryManager::new(); let alloc_func = manager.get_box_alloc_function("DataBox").unwrap(); assert!(alloc_func.contains("$alloc_databox")); assert!(alloc_func.contains("call $malloc")); assert!(alloc_func.contains("4101")); // 0x1005 type ID for DataBox assert!(alloc_func.contains("i32.const 1")); // ref_count initialization } #[test] fn test_generic_box_alloc_function() { let manager = MemoryManager::new(); let generic_func = manager.get_generic_box_alloc_function(); assert!(generic_func.contains("$box_alloc")); assert!(generic_func.contains("$type_id")); assert!(generic_func.contains("$field_count")); assert!(generic_func.contains("i32.const 12")); // Header size } }