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

124 lines
4.3 KiB
Rust

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<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::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,
}
}
}