106 lines
3.6 KiB
Rust
106 lines
3.6 KiB
Rust
|
|
use crate::{obj::prelude::*, vm::{consts::ConstPool, inst::Inst}};
|
||
|
|
use std::io::{self, Write};
|
||
|
|
|
||
|
|
/// A compiled package that can be executed by a VM.
|
||
|
|
#[derive(Debug)]
|
||
|
|
pub struct Package {
|
||
|
|
names: Vec<Sym>, // local names mappings
|
||
|
|
const_pool: ConstPool,
|
||
|
|
code: Vec<Inst>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Package {
|
||
|
|
/// Creates a new VM executable package with the given local name mappings, constant pool, and
|
||
|
|
/// executable code.
|
||
|
|
pub fn new(names: Vec<Sym>, const_pool: ConstPool, code: Vec<Inst>) -> Self {
|
||
|
|
Self { names, const_pool, code, }
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Get the local name mappings for this package.
|
||
|
|
pub fn names(&self) -> &Vec<Sym> {
|
||
|
|
&self.names
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Gets the constant pool that is used by this package.
|
||
|
|
pub fn const_pool(&self) -> &ConstPool {
|
||
|
|
&self.const_pool
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Gets the executable code that was compiled for this package.
|
||
|
|
pub fn code(&self) -> &Vec<Inst> {
|
||
|
|
&self.code
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn dump(&self, writer: &mut dyn Write) -> io::Result<()> {
|
||
|
|
// column widths
|
||
|
|
let addr_w = num_digits(self.code().len(), 16).max(4);
|
||
|
|
let inst_col = 16 - addr_w;
|
||
|
|
let inst_w = 16;
|
||
|
|
|
||
|
|
for (addr, inst) in self.code().iter().enumerate() {
|
||
|
|
let (param_val, mut param_name) = match inst {
|
||
|
|
Inst::PushSym(sym) | Inst::GetAttr(sym) | Inst::SetAttr(sym) => (
|
||
|
|
sym.index().to_string(),
|
||
|
|
global_sym_lookup(*sym).unwrap().to_string(),
|
||
|
|
),
|
||
|
|
Inst::PushConst(hdl) => (
|
||
|
|
hdl.index().to_string(),
|
||
|
|
{
|
||
|
|
let obj_ref = self.const_pool().get(*hdl);
|
||
|
|
read_obj!(let obj = obj_ref);
|
||
|
|
// XXX weirdness with coercion, can't deref as a &dyn Obj because
|
||
|
|
// RwReadLockGuard is not Obj - but using Deref::deref works
|
||
|
|
let obj: &dyn Obj = std::ops::Deref::deref(obj);
|
||
|
|
format!("{:?}", obj)
|
||
|
|
},
|
||
|
|
),
|
||
|
|
Inst::LoadName(local) => (
|
||
|
|
local.index().to_string(),
|
||
|
|
global_sym_lookup(self.names()[local.index()]).unwrap().to_string(),
|
||
|
|
),
|
||
|
|
Inst::Pop(local) => {
|
||
|
|
if let Some(local) = local {
|
||
|
|
let index = local.index();
|
||
|
|
let sym = self.names()[index];
|
||
|
|
let name = global_sym_lookup(sym).unwrap().to_string();
|
||
|
|
(index.to_string(), name)
|
||
|
|
} else {
|
||
|
|
("(discarded)".to_string(), String::new())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Inst::Jump(addr) | Inst::JumpTrue(addr) => (
|
||
|
|
addr.to_string(),
|
||
|
|
String::new(),
|
||
|
|
),
|
||
|
|
Inst::Call(argc) => (
|
||
|
|
argc.to_string(),
|
||
|
|
String::new(),
|
||
|
|
),
|
||
|
|
_ => (String::new(), String::new()),
|
||
|
|
};
|
||
|
|
|
||
|
|
if !param_name.is_empty() {
|
||
|
|
param_name = format!("({})", param_name);
|
||
|
|
}
|
||
|
|
|
||
|
|
writeln!(
|
||
|
|
writer,
|
||
|
|
"{:0addr_w$x}{space: >inst_col$}{: <inst_w$}{: <4} {}",
|
||
|
|
addr,
|
||
|
|
inst.name(),
|
||
|
|
param_val,
|
||
|
|
param_name,
|
||
|
|
space = "",
|
||
|
|
addr_w = addr_w,
|
||
|
|
inst_w = inst_w,
|
||
|
|
inst_col = inst_col,
|
||
|
|
)?;
|
||
|
|
}
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn num_digits(n: usize, radix: usize) -> usize {
|
||
|
|
((n as f64) + 1.0).log(radix as f64).ceil() as usize
|
||
|
|
}
|