124 lines
4.3 KiB
Rust
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,
|
|
}
|
|
}
|
|
}
|