From 3af2aa13282b33c555f80aaa3b87ff0817141d51 Mon Sep 17 00:00:00 2001 From: Nilton Constantino Date: Mon, 2 Feb 2026 21:08:46 +0000 Subject: [PATCH] pr 63 --- crates/prometeu-bytecode/src/lib.rs | 2 - crates/prometeu-bytecode/src/model.rs | 73 +---- crates/prometeu-bytecode/src/module_linker.rs | 296 ------------------ .../src/backend/emit_bytecode.rs | 3 +- .../src/prometeu_os/prometeu_os.rs | 3 - .../src/virtual_machine/program.rs | 18 +- .../src/virtual_machine/virtual_machine.rs | 170 +++++----- crates/prometeu-core/tests/heartbeat.rs | 15 +- 8 files changed, 109 insertions(+), 471 deletions(-) delete mode 100644 crates/prometeu-bytecode/src/module_linker.rs diff --git a/crates/prometeu-bytecode/src/lib.rs b/crates/prometeu-bytecode/src/lib.rs index 23f4cc01..0ec9b972 100644 --- a/crates/prometeu-bytecode/src/lib.rs +++ b/crates/prometeu-bytecode/src/lib.rs @@ -20,7 +20,5 @@ pub mod asm; pub mod disasm; mod model; -mod module_linker; pub use model::*; -pub use module_linker::*; diff --git a/crates/prometeu-bytecode/src/model.rs b/crates/prometeu-bytecode/src/model.rs index 7ab8c6d9..44565d52 100644 --- a/crates/prometeu-bytecode/src/model.rs +++ b/crates/prometeu-bytecode/src/model.rs @@ -61,12 +61,12 @@ pub struct Export { pub func_idx: u32, } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Import { - pub symbol: String, - pub relocation_pcs: Vec, -} - +/// Represents the final serialized format of a PBS v0 module. +/// +/// This structure is a pure data container for the PBS format. It does NOT +/// contain any linker-like logic (symbol resolution, patching, etc.). +/// All multi-module programs must be flattened and linked by the compiler +/// before being serialized into this format. #[derive(Debug, Clone, PartialEq)] pub struct BytecodeModule { pub version: u16, @@ -75,7 +75,6 @@ pub struct BytecodeModule { pub code: Vec, pub debug_info: Option, pub exports: Vec, - pub imports: Vec, } impl BytecodeModule { @@ -85,7 +84,6 @@ impl BytecodeModule { let code_data = self.code.clone(); let debug_data = self.debug_info.as_ref().map(|di| self.serialize_debug(di)).unwrap_or_default(); let export_data = self.serialize_exports(); - let import_data = self.serialize_imports(); let mut final_sections = Vec::new(); if !cp_data.is_empty() { final_sections.push((0, cp_data)); } @@ -93,7 +91,6 @@ impl BytecodeModule { if !code_data.is_empty() { final_sections.push((2, code_data)); } if !debug_data.is_empty() { final_sections.push((3, debug_data)); } if !export_data.is_empty() { final_sections.push((4, export_data)); } - if !import_data.is_empty() { final_sections.push((5, import_data)); } let mut out = Vec::new(); // Magic "PBS\0" @@ -207,21 +204,6 @@ impl BytecodeModule { data } - fn serialize_imports(&self) -> Vec { - if self.imports.is_empty() { return Vec::new(); } - let mut data = Vec::new(); - data.extend_from_slice(&(self.imports.len() as u32).to_le_bytes()); - for imp in &self.imports { - let s_bytes = imp.symbol.as_bytes(); - data.extend_from_slice(&(s_bytes.len() as u32).to_le_bytes()); - data.extend_from_slice(s_bytes); - data.extend_from_slice(&(imp.relocation_pcs.len() as u32).to_le_bytes()); - for pc in &imp.relocation_pcs { - data.extend_from_slice(&pc.to_le_bytes()); - } - } - data - } } pub struct BytecodeLoader; @@ -287,7 +269,6 @@ impl BytecodeLoader { code: Vec::new(), debug_info: None, exports: Vec::new(), - imports: Vec::new(), }; for (kind, offset, length) in sections { @@ -308,9 +289,6 @@ impl BytecodeLoader { 4 => { // Exports module.exports = parse_exports(section_data)?; } - 5 => { // Imports - module.imports = parse_imports(section_data)?; - } _ => {} // Skip unknown or optional sections } } @@ -493,45 +471,6 @@ fn parse_exports(data: &[u8]) -> Result, LoadError> { Ok(exports) } -fn parse_imports(data: &[u8]) -> Result, LoadError> { - if data.is_empty() { - return Ok(Vec::new()); - } - if data.len() < 4 { - return Err(LoadError::MalformedSection); - } - let count = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize; - let mut imports = Vec::with_capacity(count); - let mut pos = 4; - - for _ in 0..count { - if pos + 8 > data.len() { - return Err(LoadError::UnexpectedEof); - } - let relocation_count = u32::from_le_bytes(data[pos..pos+4].try_into().unwrap()) as usize; - let name_len = u32::from_le_bytes(data[pos+4..pos+8].try_into().unwrap()) as usize; - pos += 8; - - if pos + name_len > data.len() { - return Err(LoadError::UnexpectedEof); - } - let symbol = String::from_utf8_lossy(&data[pos..pos+name_len]).into_owned(); - pos += name_len; - - if pos + relocation_count * 4 > data.len() { - return Err(LoadError::UnexpectedEof); - } - let mut relocation_pcs = Vec::with_capacity(relocation_count); - for _ in 0..relocation_count { - let pc = u32::from_le_bytes(data[pos..pos+4].try_into().unwrap()); - relocation_pcs.push(pc); - pos += 4; - } - - imports.push(Import { symbol, relocation_pcs }); - } - Ok(imports) -} fn validate_module(module: &BytecodeModule) -> Result<(), LoadError> { for func in &module.functions { diff --git a/crates/prometeu-bytecode/src/module_linker.rs b/crates/prometeu-bytecode/src/module_linker.rs deleted file mode 100644 index 53ef7160..00000000 --- a/crates/prometeu-bytecode/src/module_linker.rs +++ /dev/null @@ -1,296 +0,0 @@ -use crate::opcode::OpCode; -use crate::{BytecodeModule, ConstantPoolEntry, DebugInfo, FunctionMeta}; -use std::collections::HashMap; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum LinkError { - UnresolvedSymbol(String), - DuplicateExport(String), -} - -pub struct ModuleLinker; - -/// Internal representation for linking process -#[derive(Debug)] -pub struct LinkedProgram { - pub rom: Vec, - pub constant_pool: Vec, - pub functions: Vec, - pub debug_info: Option, - pub exports: HashMap, -} - -impl ModuleLinker { - pub fn link(modules: &[BytecodeModule]) -> Result { - let mut combined_code = Vec::new(); - let mut combined_functions = Vec::new(); - let mut combined_constants = Vec::new(); - let mut combined_debug_pc_to_span = Vec::new(); - let mut combined_debug_function_names = Vec::new(); - - let mut exports = HashMap::new(); - - // Offset mapping for each module - let mut module_code_offsets = Vec::with_capacity(modules.len()); - let mut module_function_offsets = Vec::with_capacity(modules.len()); - - // First pass: collect exports and calculate offsets - for module in modules { - let code_offset = combined_code.len() as u32; - let function_offset = combined_functions.len() as u32; - - module_code_offsets.push(code_offset); - module_function_offsets.push(function_offset); - - for export in &module.exports { - if exports.contains_key(&export.symbol) { - return Err(LinkError::DuplicateExport(export.symbol.clone())); - } - exports.insert(export.symbol.clone(), (function_offset + export.func_idx) as u32); - } - - combined_code.extend_from_slice(&module.code); - - for func in &module.functions { - let mut linked_func = func.clone(); - linked_func.code_offset += code_offset; - combined_functions.push(linked_func); - } - } - - // Second pass: resolve imports and relocate constants/code - for (i, module) in modules.iter().enumerate() { - let code_offset = module_code_offsets[i] as usize; - let const_base = combined_constants.len() as u32; - - // Relocate constant pool entries for this module - for entry in &module.const_pool { - combined_constants.push(entry.clone()); - } - - // Patch relocations for imports - for import in &module.imports { - let target_func_idx = exports.get(&import.symbol) - .ok_or_else(|| LinkError::UnresolvedSymbol(import.symbol.clone()))?; - - for &reloc_pc in &import.relocation_pcs { - let absolute_pc = code_offset + reloc_pc as usize; - // CALL opcode is 2 bytes, immediate is next 4 bytes - let imm_offset = absolute_pc + 2; - if imm_offset + 4 <= combined_code.len() { - let bytes = target_func_idx.to_le_bytes(); - combined_code[imm_offset..imm_offset+4].copy_from_slice(&bytes); - } - } - } - - // Relocate PUSH_CONST instructions - if const_base > 0 { - let mut pos = code_offset; - let end = code_offset + module.code.len(); - while pos < end { - if pos + 2 > end { break; } - let op_val = u16::from_le_bytes([combined_code[pos], combined_code[pos+1]]); - let opcode = match OpCode::try_from(op_val) { - Ok(op) => op, - Err(_) => { - pos += 2; - continue; - } - }; - pos += 2; - - match opcode { - OpCode::PushConst => { - if pos + 4 <= end { - let old_idx = u32::from_le_bytes(combined_code[pos..pos+4].try_into().unwrap()); - let new_idx = old_idx + const_base; - combined_code[pos..pos+4].copy_from_slice(&new_idx.to_le_bytes()); - pos += 4; - } - } - OpCode::PushI32 | OpCode::PushBounded | OpCode::Jmp | OpCode::JmpIfFalse | OpCode::JmpIfTrue - | OpCode::GetGlobal | OpCode::SetGlobal | OpCode::GetLocal | OpCode::SetLocal - | OpCode::PopN | OpCode::Syscall | OpCode::GateLoad | OpCode::GateStore | OpCode::Call => { - pos += 4; - } - OpCode::PushI64 | OpCode::PushF64 | OpCode::Alloc => { - pos += 8; - } - OpCode::PushBool => { - pos += 1; - } - _ => {} - } - } - } - - // Handle debug info - if let Some(debug_info) = &module.debug_info { - for (pc, span) in &debug_info.pc_to_span { - combined_debug_pc_to_span.push((pc + module_code_offsets[i], span.clone())); - } - for (func_idx, name) in &debug_info.function_names { - combined_debug_function_names.push((func_idx + module_function_offsets[i], name.clone())); - } - } - } - - let debug_info = if !combined_debug_pc_to_span.is_empty() { - Some(DebugInfo { - pc_to_span: combined_debug_pc_to_span, - function_names: combined_debug_function_names, - }) - } else { - None - }; - - Ok(LinkedProgram { - rom: combined_code, - constant_pool: combined_constants, - functions: combined_functions, - debug_info, - exports, - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::opcode::OpCode; - use crate::{BytecodeModule, Export, FunctionMeta, Import}; - - #[test] - fn test_linker_basic() { - // Module 1: defines 'foo', calls 'bar' - let mut code1 = Vec::new(); - // Function 'foo' at offset 0 - code1.extend_from_slice(&(OpCode::Call as u16).to_le_bytes()); - code1.extend_from_slice(&0u32.to_le_bytes()); // placeholder for 'bar' - code1.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes()); - - let m1 = BytecodeModule { - version: 0, - const_pool: vec![], - functions: vec![FunctionMeta { - code_offset: 0, - code_len: code1.len() as u32, - ..Default::default() - }], - code: code1, - debug_info: None, - exports: vec![Export { symbol: "foo".to_string(), func_idx: 0 }], - imports: vec![Import { symbol: "bar".to_string(), relocation_pcs: vec![0] }], - }; - - // Module 2: defines 'bar' - let mut code2 = Vec::new(); - code2.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes()); - - let m2 = BytecodeModule { - version: 0, - const_pool: vec![], - functions: vec![FunctionMeta { - code_offset: 0, - code_len: code2.len() as u32, - ..Default::default() - }], - code: code2, - debug_info: None, - exports: vec![Export { symbol: "bar".to_string(), func_idx: 0 }], - imports: vec![], - }; - - let result = ModuleLinker::link(&[m1, m2]).unwrap(); - - assert_eq!(result.functions.len(), 2); - // 'foo' is func 0, 'bar' is func 1 - assert_eq!(result.functions[0].code_offset, 0); - assert_eq!(result.functions[1].code_offset, 8); - - // Let's check patched code - let patched_func_id = u32::from_le_bytes(result.rom[2..6].try_into().unwrap()); - assert_eq!(patched_func_id, 1); // Points to 'bar' - } - - #[test] - fn test_linker_unresolved() { - let m1 = BytecodeModule { - version: 0, - const_pool: vec![], - functions: vec![], - code: vec![], - debug_info: None, - exports: vec![], - imports: vec![Import { symbol: "missing".to_string(), relocation_pcs: vec![] }], - }; - let result = ModuleLinker::link(&[m1]); - assert_eq!(result.unwrap_err(), LinkError::UnresolvedSymbol("missing".to_string())); - } - - #[test] - fn test_linker_duplicate_export() { - let m1 = BytecodeModule { - version: 0, - const_pool: vec![], - functions: vec![], - code: vec![], - debug_info: None, - exports: vec![Export { symbol: "dup".to_string(), func_idx: 0 }], - imports: vec![], - }; - let m2 = m1.clone(); - let result = ModuleLinker::link(&[m1, m2]); - assert_eq!(result.unwrap_err(), LinkError::DuplicateExport("dup".to_string())); - } - - #[test] - fn test_linker_const_relocation() { - // Module 1: uses constants - let mut code1 = Vec::new(); - code1.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - code1.extend_from_slice(&0u32.to_le_bytes()); // Index 0 - code1.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes()); - - let m1 = BytecodeModule { - version: 0, - const_pool: vec![ConstantPoolEntry::Int32(42)], - functions: vec![FunctionMeta { code_offset: 0, code_len: code1.len() as u32, ..Default::default() }], - code: code1, - debug_info: None, - exports: vec![], - imports: vec![], - }; - - // Module 2: also uses constants - let mut code2 = Vec::new(); - code2.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - code2.extend_from_slice(&0u32.to_le_bytes()); // Index 0 (local to module 2) - code2.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes()); - - let m2 = BytecodeModule { - version: 0, - const_pool: vec![ConstantPoolEntry::Int32(99)], - functions: vec![FunctionMeta { code_offset: 0, code_len: code2.len() as u32, ..Default::default() }], - code: code2, - debug_info: None, - exports: vec![], - imports: vec![], - }; - - let result = ModuleLinker::link(&[m1, m2]).unwrap(); - - assert_eq!(result.constant_pool.len(), 2); - assert_eq!(result.constant_pool[0], ConstantPoolEntry::Int32(42)); - assert_eq!(result.constant_pool[1], ConstantPoolEntry::Int32(99)); - - // Code for module 1 (starts at 0) - let idx1 = u32::from_le_bytes(result.rom[2..6].try_into().unwrap()); - assert_eq!(idx1, 0); - - // Code for module 2 (starts at 8) - let idx2 = u32::from_le_bytes(result.rom[10..14].try_into().unwrap()); - assert_eq!(idx2, 1); - } -} diff --git a/crates/prometeu-compiler/src/backend/emit_bytecode.rs b/crates/prometeu-compiler/src/backend/emit_bytecode.rs index 5df96f74..cc278258 100644 --- a/crates/prometeu-compiler/src/backend/emit_bytecode.rs +++ b/crates/prometeu-compiler/src/backend/emit_bytecode.rs @@ -50,8 +50,7 @@ pub fn emit_module(module: &ir_vm::Module) -> Result { functions: fragments.functions, code: fragments.code, debug_info: fragments.debug_info, - exports, - imports: vec![], + exports, }; Ok(EmitResult { diff --git a/crates/prometeu-core/src/prometeu_os/prometeu_os.rs b/crates/prometeu-core/src/prometeu_os/prometeu_os.rs index 5b95597f..e2e1f068 100644 --- a/crates/prometeu-core/src/prometeu_os/prometeu_os.rs +++ b/crates/prometeu-core/src/prometeu_os/prometeu_os.rs @@ -448,7 +448,6 @@ mod tests { code: vec![0x02, 0x00, 0x00, 0x00, 0x00, 0x00], debug_info: None, exports: vec![prometeu_bytecode::Export { symbol: "main".into(), func_idx: 0 }], - imports: vec![], }.serialize(); let cartridge = Cartridge { app_id: 1234, @@ -505,7 +504,6 @@ mod tests { ], debug_info: None, exports: vec![prometeu_bytecode::Export { symbol: "main".into(), func_idx: 0 }], - imports: vec![], }.serialize(); let cartridge = Cartridge { app_id: 1234, @@ -721,7 +719,6 @@ mod tests { ], debug_info: None, exports: vec![prometeu_bytecode::Export { symbol: "main".into(), func_idx: 0 }], - imports: vec![], }.serialize(); let cartridge = Cartridge { app_id: 1234, diff --git a/crates/prometeu-core/src/virtual_machine/program.rs b/crates/prometeu-core/src/virtual_machine/program.rs index 88e819e8..4653c62d 100644 --- a/crates/prometeu-core/src/virtual_machine/program.rs +++ b/crates/prometeu-core/src/virtual_machine/program.rs @@ -4,6 +4,14 @@ use prometeu_bytecode::{BytecodeModule, ConstantPoolEntry, DebugInfo, Export, Fu use std::collections::HashMap; use std::sync::Arc; +/// Represents a fully linked, executable PBS program image. +/// +/// Under the Prometeu architecture, the ProgramImage is a "closed-world" artifact +/// produced by the compiler. All linking, relocation, and symbol resolution +/// MUST be performed by the compiler before this image is created. +/// +/// The runtime (VM) assumes this image is authoritative and performs no +/// additional linking or fixups. #[derive(Debug, Clone, Default)] pub struct ProgramImage { pub rom: Arc<[u8]>, @@ -14,14 +22,7 @@ pub struct ProgramImage { } impl ProgramImage { - pub fn new(rom: Vec, constant_pool: Vec, mut functions: Vec, debug_info: Option, exports: HashMap) -> Self { - if functions.is_empty() && !rom.is_empty() { - functions.push(FunctionMeta { - code_offset: 0, - code_len: rom.len() as u32, - ..Default::default() - }); - } + pub fn new(rom: Vec, constant_pool: Vec, functions: Vec, debug_info: Option, exports: HashMap) -> Self { Self { rom: Arc::from(rom), constant_pool: Arc::from(constant_pool), @@ -117,7 +118,6 @@ impl From for BytecodeModule { code: program.rom.as_ref().to_vec(), debug_info: program.debug_info.clone(), exports, - imports: vec![], } } } diff --git a/crates/prometeu-core/src/virtual_machine/virtual_machine.rs b/crates/prometeu-core/src/virtual_machine/virtual_machine.rs index e3b1e525..5cad31ac 100644 --- a/crates/prometeu-core/src/virtual_machine/virtual_machine.rs +++ b/crates/prometeu-core/src/virtual_machine/virtual_machine.rs @@ -148,14 +148,13 @@ impl VirtualMachine { return Err(VmInitError::InvalidFormat); }; - // Resolve the entrypoint: empty (defaults to 0), numeric PC, or symbol name. + // Resolve the entrypoint: empty (defaults to func 0), numeric func_idx, or symbol name. let pc = if entrypoint.is_empty() { - 0 - } else if let Ok(addr) = entrypoint.parse::() { - if addr >= program.rom.len() && (addr > 0 || !program.rom.is_empty()) { - return Err(VmInitError::EntrypointNotFound); - } - addr + program.functions.get(0).map(|f| f.code_offset as usize).unwrap_or(0) + } else if let Ok(func_idx) = entrypoint.parse::() { + program.functions.get(func_idx) + .map(|f| f.code_offset as usize) + .ok_or(VmInitError::EntrypointNotFound)? } else { // Try to resolve as a symbol name from the exports map if let Some(&func_idx) = program.exports.get(entrypoint) { @@ -163,18 +162,7 @@ impl VirtualMachine { .map(|f| f.code_offset as usize) .ok_or(VmInitError::EntrypointNotFound)? } else { - // Suffix match fallback (e.g. "frame" matches "src/main/modules/main:frame") - let suffix = format!(":{}", entrypoint); - let found = program.exports.iter() - .find(|(name, _)| name == &entrypoint || name.ends_with(&suffix)); - - if let Some((_, &func_idx)) = found { - program.functions.get(func_idx as usize) - .map(|f| f.code_offset as usize) - .ok_or(VmInitError::EntrypointNotFound)? - } else { - return Err(VmInitError::EntrypointNotFound); - } + return Err(VmInitError::EntrypointNotFound); } }; @@ -189,33 +177,18 @@ impl VirtualMachine { /// Prepares the VM to execute a specific entrypoint by setting the PC and /// pushing an initial call frame. pub fn prepare_call(&mut self, entrypoint: &str) { - let (addr, func_idx) = if let Ok(addr) = entrypoint.parse::() { - let idx = self.program.functions.iter().position(|f| { - addr >= f.code_offset as usize && addr < (f.code_offset + f.code_len) as usize - }).unwrap_or(0); - (addr, idx) + let func_idx = if let Ok(idx) = entrypoint.parse::() { + idx } else { // Try to resolve as a symbol name - let found = self.program.exports.get(entrypoint) - .map(|&idx| (idx, entrypoint.to_string())) - .or_else(|| { - let suffix = format!(":{}", entrypoint); - self.program.exports.iter() - .find(|(name, _)| name == &entrypoint || name.ends_with(&suffix)) - .map(|(name, &idx)| (idx, name.clone())) - }); - - if let Some((func_idx, _)) = found { - let addr = self.program.functions.get(func_idx as usize) - .map(|f| f.code_offset as usize) - .unwrap_or(0); - (addr, func_idx as usize) - } else { - // Default to 0 for non-numeric entrypoints that aren't found. - (0, 0) - } + self.program.exports.get(entrypoint) + .map(|&idx| idx as usize) + .ok_or(()).unwrap_or(0) // Default to 0 if not found }; + let callee = self.program.functions.get(func_idx).cloned().unwrap_or_default(); + let addr = callee.code_offset as usize; + self.pc = addr; self.halted = false; @@ -943,6 +916,17 @@ impl VirtualMachine { #[cfg(test)] mod tests { use super::*; + + fn new_test_vm(rom: Vec, constant_pool: Vec) -> VirtualMachine { + let rom_len = rom.len() as u32; + let mut vm = VirtualMachine::new(rom, constant_pool); + vm.program.functions = std::sync::Arc::from(vec![prometeu_bytecode::FunctionMeta { + code_offset: 0, + code_len: rom_len, + ..Default::default() + }]); + vm + } use crate::hardware::HardwareBridge; use crate::virtual_machine::{expect_int, HostReturn, Value, VmFault}; use prometeu_bytecode::abi::SourceSpan; @@ -993,7 +977,7 @@ mod tests { rom.extend_from_slice(&(OpCode::Mod as u16).to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.run_budget(100, &mut native, &mut hw).unwrap(); assert_eq!(vm.pop().unwrap(), Value::Int32(0)); @@ -1012,7 +996,7 @@ mod tests { rom.extend_from_slice(&(OpCode::Div as u16).to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let report = vm.run_budget(100, &mut native, &mut hw).unwrap(); match report.reason { @@ -1035,7 +1019,7 @@ mod tests { rom.extend_from_slice(&(OpCode::IntToBoundChecked as u16).to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let report = vm.run_budget(100, &mut native, &mut hw).unwrap(); match report.reason { @@ -1060,7 +1044,7 @@ mod tests { rom.extend_from_slice(&(OpCode::Add as u16).to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let report = vm.run_budget(100, &mut native, &mut hw).unwrap(); match report.reason { @@ -1086,7 +1070,7 @@ mod tests { rom.extend_from_slice(&(OpCode::Lt as u16).to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.run_budget(100, &mut native, &mut hw).unwrap(); assert_eq!(vm.pop().unwrap(), Value::Boolean(true)); } @@ -1098,7 +1082,7 @@ mod tests { rom.extend_from_slice(&42i64.to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let mut native = MockNative; let mut hw = MockHardware; @@ -1113,7 +1097,7 @@ mod tests { rom.extend_from_slice(&3.14f64.to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let mut native = MockNative; let mut hw = MockHardware; @@ -1132,7 +1116,7 @@ mod tests { rom.push(0); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let mut native = MockNative; let mut hw = MockHardware; @@ -1328,7 +1312,7 @@ mod tests { rom.extend_from_slice(&(OpCode::PopScope as u16).to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let mut native = MockNative; let mut hw = MockHardware; @@ -1427,7 +1411,7 @@ mod tests { rom.extend_from_slice(&42i32.to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let mut native = MockNative; let mut hw = MockHardware; @@ -1449,7 +1433,7 @@ mod tests { rom.extend_from_slice(&(OpCode::BitAnd as u16).to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.step(&mut native, &mut hw).unwrap(); vm.step(&mut native, &mut hw).unwrap(); vm.step(&mut native, &mut hw).unwrap(); @@ -1464,7 +1448,7 @@ mod tests { rom.extend_from_slice(&(OpCode::BitOr as u16).to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.step(&mut native, &mut hw).unwrap(); vm.step(&mut native, &mut hw).unwrap(); vm.step(&mut native, &mut hw).unwrap(); @@ -1485,7 +1469,7 @@ mod tests { rom.extend_from_slice(&(OpCode::Lte as u16).to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.step(&mut native, &mut hw).unwrap(); vm.step(&mut native, &mut hw).unwrap(); vm.step(&mut native, &mut hw).unwrap(); @@ -1500,7 +1484,7 @@ mod tests { rom.extend_from_slice(&(OpCode::Gte as u16).to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.step(&mut native, &mut hw).unwrap(); vm.step(&mut native, &mut hw).unwrap(); vm.step(&mut native, &mut hw).unwrap(); @@ -1518,7 +1502,7 @@ mod tests { rom.extend_from_slice(&(OpCode::Neg as u16).to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.step(&mut native, &mut hw).unwrap(); vm.step(&mut native, &mut hw).unwrap(); assert_eq!(vm.pop().unwrap(), Value::Int32(-42)); @@ -1549,7 +1533,7 @@ mod tests { rom.extend_from_slice(&100i32.to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.step(&mut native, &mut hw).unwrap(); // PushBool vm.step(&mut native, &mut hw).unwrap(); // JmpIfTrue assert_eq!(vm.pc, 11); @@ -1568,7 +1552,7 @@ mod tests { rom.extend_from_slice(&(OpCode::Trap as u16).to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let report = vm.run_budget(100, &mut native, &mut hw).unwrap(); assert_eq!(report.reason, LogicalFrameEndingReason::Breakpoint); @@ -1592,7 +1576,7 @@ mod tests { rom.extend_from_slice(&2u32.to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.run_budget(100, &mut native, &mut hw).unwrap(); assert_eq!(vm.pop().unwrap(), Value::Int32(1)); @@ -1614,7 +1598,7 @@ mod tests { rom.extend_from_slice(&1u32.to_le_bytes()); // offset 1 rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let report = vm.run_budget(100, &mut native, &mut hw).unwrap(); match report.reason { @@ -1641,7 +1625,7 @@ mod tests { rom.extend_from_slice(&0u32.to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let report = vm.run_budget(100, &mut native, &mut hw).unwrap(); match report.reason { @@ -1662,7 +1646,12 @@ mod tests { 0x11, 0x00, // Pop 0x51, 0x00 // Ret ]; - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = VirtualMachine::new(rom.clone(), vec![]); + vm.program.functions = std::sync::Arc::from(vec![prometeu_bytecode::FunctionMeta { + code_offset: 0, + code_len: rom.len() as u32, + ..Default::default() + }]); let mut hw = crate::Hardware::new(); struct TestNative; impl NativeInterface for TestNative { @@ -1692,7 +1681,12 @@ mod tests { } } - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = VirtualMachine::new(rom.clone(), vec![]); + vm.program.functions = std::sync::Arc::from(vec![prometeu_bytecode::FunctionMeta { + code_offset: 0, + code_len: rom.len() as u32, + ..Default::default() + }]); let mut native = MultiReturnNative; let mut hw = crate::Hardware::new(); @@ -1718,7 +1712,12 @@ mod tests { } } - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = VirtualMachine::new(rom.clone(), vec![]); + vm.program.functions = std::sync::Arc::from(vec![prometeu_bytecode::FunctionMeta { + code_offset: 0, + code_len: rom.len() as u32, + ..Default::default() + }]); let mut native = VoidReturnNative; let mut hw = crate::Hardware::new(); @@ -1748,7 +1747,12 @@ mod tests { } } - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = VirtualMachine::new(rom.clone(), vec![]); + vm.program.functions = std::sync::Arc::from(vec![prometeu_bytecode::FunctionMeta { + code_offset: 0, + code_len: rom.len() as u32, + ..Default::default() + }]); let mut native = ArgCheckNative; let mut hw = crate::Hardware::new(); @@ -1770,7 +1774,7 @@ mod tests { 0x70, 0x00, // Syscall + Reserved 0xEF, 0xBE, 0xAD, 0xDE, // 0xDEADBEEF ]; - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let mut native = MockNative; let mut hw = MockHardware; @@ -1795,7 +1799,7 @@ mod tests { 0x70, 0x00, // Syscall + Reserved 0x01, 0x10, 0x00, 0x00, // Syscall ID 0x1001 ]; - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let mut native = MockNative; let mut hw = MockHardware; @@ -1832,7 +1836,7 @@ mod tests { } } - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let mut native = BadNative; let mut hw = MockHardware; @@ -1962,7 +1966,7 @@ mod tests { rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes()); let f1_len = rom.len() as u32 - f1_start; - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.program.functions = std::sync::Arc::from(vec![ FunctionMeta { code_offset: f0_start as u32, @@ -2014,7 +2018,7 @@ mod tests { rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes()); let f1_len = rom.len() as u32 - f1_start; - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.program.functions = std::sync::Arc::from(vec![ FunctionMeta { code_offset: f0_start as u32, @@ -2065,7 +2069,7 @@ mod tests { rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes()); let f1_len = rom.len() as u32 - f1_start; - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.program.functions = std::sync::Arc::from(vec![ FunctionMeta { code_offset: f0_start as u32, @@ -2097,7 +2101,7 @@ mod tests { rom.extend_from_slice(&(OpCode::Call as u16).to_le_bytes()); rom.extend_from_slice(&99u32.to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); let report = vm.run_budget(100, &mut native, &mut hw).unwrap(); match report.reason { @@ -2130,7 +2134,7 @@ mod tests { rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes()); let f1_len = rom.len() as u32 - f1_start; - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.program.functions = std::sync::Arc::from(vec![ FunctionMeta { code_offset: f0_start as u32, @@ -2180,7 +2184,7 @@ mod tests { rom.extend_from_slice(&0u32.to_le_bytes()); rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.program.functions = std::sync::Arc::from(vec![FunctionMeta { code_offset: 0, code_len: 20, @@ -2234,7 +2238,7 @@ mod tests { rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes()); let f1_len = rom.len() as u32 - f1_start; - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.program.functions = std::sync::Arc::from(vec![ FunctionMeta { code_offset: f0_start as u32, @@ -2271,7 +2275,7 @@ mod tests { rom.extend_from_slice(&1u32.to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.program.functions = std::sync::Arc::from(vec![FunctionMeta { code_offset: 0, code_len: 8, @@ -2346,7 +2350,7 @@ mod tests { // 48: HALT rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); // We need to set up the function meta for absolute jumps to work correctly vm.program.functions = std::sync::Arc::from(vec![FunctionMeta { code_offset: 0, @@ -2385,7 +2389,7 @@ mod tests { // 15-16: HALT rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.program.functions = std::sync::Arc::from(vec![FunctionMeta { code_offset: 0, code_len: 17, @@ -2413,7 +2417,7 @@ mod tests { rom.extend_from_slice(&9u32.to_le_bytes()); rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); - let mut vm = VirtualMachine::new(rom, vec![]); + let mut vm = new_test_vm(rom.clone(), vec![]); vm.program.functions = std::sync::Arc::from(vec![FunctionMeta { code_offset: 0, code_len: 14, @@ -2454,7 +2458,11 @@ mod tests { function_names: vec![(0, "main".to_string())], }; - let program = ProgramImage::new(rom, vec![], vec![], Some(debug_info), std::collections::HashMap::new()); + let program = ProgramImage::new(rom.clone(), vec![], vec![FunctionMeta { + code_offset: 0, + code_len: rom.len() as u32, + ..Default::default() + }], Some(debug_info), std::collections::HashMap::new()); let mut vm = VirtualMachine { program, ..Default::default() diff --git a/crates/prometeu-core/tests/heartbeat.rs b/crates/prometeu-core/tests/heartbeat.rs index 349b6db3..6b854714 100644 --- a/crates/prometeu-core/tests/heartbeat.rs +++ b/crates/prometeu-core/tests/heartbeat.rs @@ -39,20 +39,13 @@ fn test_canonical_cartridge_heartbeat() { let pbc_bytes = fs::read(pbc_path).expect("Failed to read canonical PBC. Did you run the generation test?"); - // Determine numeric entrypoint PC from the compiled module exports + // Determine entrypoint from the compiled module exports let module = BytecodeLoader::load(&pbc_bytes).expect("Failed to parse PBC"); - let func_idx = module - .exports - .iter() - .find(|e| e.symbol == "src/main/modules:frame") - .map(|e| e.func_idx as usize) - .expect("Entrypoint symbol not found in exports"); - let entry_pc = module.functions[func_idx].code_offset as usize; - let entry_pc_str = entry_pc.to_string(); + let entry_symbol = "src/main/modules:frame"; let mut vm = VirtualMachine::new(vec![], vec![]); - vm.initialize(pbc_bytes, &entry_pc_str).expect("Failed to initialize VM with canonical cartridge"); - vm.prepare_call(&entry_pc_str); + vm.initialize(pbc_bytes, entry_symbol).expect("Failed to initialize VM with canonical cartridge"); + vm.prepare_call(entry_symbol); let mut native = MockNative; let mut hw = Hardware::new();