2026-03-24 13:40:37 +00:00

125 lines
4.3 KiB
Rust

use crate::abi::TrapInfo;
use crate::model::{BytecodeModule, ConstantPoolEntry, DebugInfo, Export, FunctionMeta};
use crate::value::Value;
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]>,
pub constant_pool: Arc<[Value]>,
pub functions: Arc<[FunctionMeta]>,
pub debug_info: Option<DebugInfo>,
pub exports: Arc<HashMap<String, u32>>,
}
impl ProgramImage {
pub fn new(
rom: Vec<u8>,
constant_pool: Vec<Value>,
functions: Vec<FunctionMeta>,
debug_info: Option<DebugInfo>,
exports: HashMap<String, u32>,
) -> 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<usize> {
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<BytecodeModule> for ProgramImage {
fn from(module: BytecodeModule) -> Self {
let constant_pool: Vec<Value> = 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<ProgramImage> 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::HeapRef(_) => 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,
}
}
}