use crate::abi::TrapInfo; use std::collections::HashMap; use std::sync::Arc; use crate::model::{BytecodeModule, ConstantPoolEntry, DebugInfo, Export, FunctionMeta}; use crate::value::Value; /// 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]>, pub constant_pool: Arc<[Value]>, pub functions: Arc<[FunctionMeta]>, pub debug_info: Option, pub exports: Arc>, } impl ProgramImage { 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), functions: Arc::from(functions), debug_info, exports: Arc::new(exports), } } pub fn create_trap(&self, code: u32, opcode: u16, mut message: String, pc: u32) -> TrapInfo { let span = self.debug_info.as_ref().and_then(|di| { di.pc_to_span.iter().find(|(p, _)| *p == pc).map(|(_, s)| s.clone()) }); if let Some(func_idx) = self.find_function_index(pc) { if let Some(func_name) = self.get_function_name(func_idx) { message = format!("{} (in function {})", message, func_name); } } TrapInfo { code, opcode, message, pc, span, } } pub fn find_function_index(&self, pc: u32) -> Option { self.functions.iter().position(|f| { pc >= f.code_offset && pc < (f.code_offset + f.code_len) }) } pub fn get_function_name(&self, func_idx: usize) -> Option<&str> { self.debug_info.as_ref() .and_then(|di| di.function_names.iter().find(|(idx, _)| *idx as usize == func_idx)) .map(|(_, name)| name.as_str()) } } impl From for ProgramImage { fn from(module: BytecodeModule) -> Self { let constant_pool: Vec = module.const_pool.iter().map(|entry| { match entry { ConstantPoolEntry::Null => Value::Null, ConstantPoolEntry::Int64(v) => Value::Int64(*v), ConstantPoolEntry::Float64(v) => Value::Float(*v), ConstantPoolEntry::Boolean(v) => Value::Boolean(*v), ConstantPoolEntry::String(v) => Value::String(v.clone()), ConstantPoolEntry::Int32(v) => Value::Int32(*v), } }).collect(); let mut exports = HashMap::new(); for export in module.exports { exports.insert(export.symbol, export.func_idx); } ProgramImage::new( module.code, constant_pool, module.functions, module.debug_info, exports, ) } } impl From for BytecodeModule { fn from(program: ProgramImage) -> Self { let const_pool = program.constant_pool.iter().map(|v| match v { Value::Null => ConstantPoolEntry::Null, Value::Int64(v) => ConstantPoolEntry::Int64(*v), Value::Float(v) => ConstantPoolEntry::Float64(*v), Value::Boolean(v) => ConstantPoolEntry::Boolean(*v), Value::String(v) => ConstantPoolEntry::String(v.clone()), Value::Int32(v) => ConstantPoolEntry::Int32(*v), Value::Bounded(v) => ConstantPoolEntry::Int32(*v as i32), Value::Gate(_) => ConstantPoolEntry::Null, }).collect(); let exports = program.exports.iter().map(|(symbol, &func_idx)| Export { symbol: symbol.clone(), func_idx, }).collect(); BytecodeModule { version: 0, const_pool, functions: program.functions.as_ref().to_vec(), code: program.rom.as_ref().to_vec(), debug_info: program.debug_info.clone(), exports, } } }