use crate::register::{parameter_slots, RegOp, StackToRegister};
use crate::{Bytecode, Chunk, Instruction, Opcode, Value};
use alloc::format;
use alloc::string::{String, ToString};
use core::fmt::Write;
pub fn disassemble_stack(bytecode: &Bytecode, only: Option<usize>) -> String {
let mut out = String::new();
for (i, ch) in bytecode.chunks.iter().enumerate() {
if only.is_some_and(|o| o != i) {
continue;
}
let _ = writeln!(
out,
"== chunk {} ({}) == {} instructions, {} constants, params {}",
i,
ch.name,
ch.instructions.len(),
ch.constants.len(),
parameter_slots(bytecode, i),
);
for (ip, ins) in ch.instructions.iter().enumerate() {
let _ = writeln!(
out,
"{:5} {:<14} {:<10} {}",
ip,
opcode_name(ins.opcode),
operand_text(ins.operand),
stack_note(bytecode, ch, ins),
);
}
out.push('\n');
}
out
}
pub fn disassemble_register(bytecode: &Bytecode, only: Option<usize>) -> String {
let mut out = String::new();
for (i, ch) in bytecode.chunks.iter().enumerate() {
if only.is_some_and(|o| o != i) {
continue;
}
let slots = parameter_slots(bytecode, i);
let reg = StackToRegister::new(&ch.name, &ch.instructions, &ch.constants)
.with_param_slots(slots)
.translate();
let _ = writeln!(
out,
"== chunk {} ({}) == {} regops, {} regs, {} param slots (registers 0..{})",
i,
reg.name,
reg.code.len(),
reg.n_regs,
reg.param_slots,
reg.param_slots,
);
for (rip, op) in reg.code.iter().enumerate() {
let _ = writeln!(
out,
"{:5} {:<46} {}",
rip,
format!("{:?}", op),
reg_note(®.constants, op),
);
}
out.push('\n');
}
out
}
fn opcode_name(op: Opcode) -> String {
format!("{:?}", op)
}
fn operand_text(operand: Option<u32>) -> String {
match operand {
Some(o) => format!("#{}", o),
None => String::new(),
}
}
fn stack_note(bytecode: &Bytecode, ch: &Chunk, ins: &Instruction) -> String {
let k = ins.operand.unwrap_or(0);
match ins.opcode {
Opcode::Push => match ch.constants.get(k as usize) {
Some(v) => format!("; {}", value_text(v)),
None => String::new(),
},
Opcode::GetProperty | Opcode::SetProperty => match ch.constants.get(k as usize) {
Some(Value::Str(name)) => format!("; \"{}\"", name),
Some(v) => format!("; {}", value_text(v)),
None => String::new(),
},
Opcode::LoadGlobal | Opcode::StoreGlobal => match bytecode.global_names.get(k as usize) {
Some(name) => format!("; global {}", name),
None => String::new(),
},
Opcode::Jump
| Opcode::JumpIfFalse
| Opcode::JumpIfTrue
| Opcode::TryBegin
| Opcode::TryEnd => format!("; -> {}", k),
Opcode::LoadLocal | Opcode::StoreLocal => format!("; local r{}", k),
Opcode::Slide => format!("; keep top, drop {} below", k),
Opcode::LoadUpvalue | Opcode::StoreUpvalue => format!("; upvalue {}", k),
Opcode::Call | Opcode::TailCall | Opcode::BuildArray | Opcode::BuildMap => {
format!("; {} operands", k)
}
Opcode::CallDirect => {
let argc = k >> 16;
let callee = k & 0xFFFF;
match ch.constants.get(callee as usize) {
Some(Value::Str(name)) => format!("; {}({} args, direct)", name, argc),
_ => format!("; {} args, direct", argc),
}
}
_ => String::new(),
}
}
fn reg_note(consts: &[Value], op: &RegOp) -> String {
match op {
RegOp::LoadConst { k, .. } => match consts.get(*k as usize) {
Some(v) => format!("; {}", value_text(v)),
None => String::new(),
},
RegOp::LoadGlobal { slot, .. } | RegOp::StoreGlobal { slot, .. } => {
format!("; global #{}", slot)
}
RegOp::GetProp { name, .. } | RegOp::SetProp { name, .. } | RegOp::Invoke { name, .. } => {
match consts.get(*name as usize) {
Some(Value::Str(s)) => format!("; \"{}\"", s),
_ => String::new(),
}
}
RegOp::Jump { target } => format!("; -> {}", target),
RegOp::JumpIfFalse { target, .. } | RegOp::JumpIfTrue { target, .. } => {
format!("; -> {}", target)
}
RegOp::CreateClass { k, .. } => match consts.get(*k as usize) {
Some(v) => format!("; {}", value_text(v)),
None => String::new(),
},
_ => String::new(),
}
}
fn value_text(v: &Value) -> String {
match v {
Value::Null => "null".to_string(),
Value::Bool(b) => b.to_string(),
Value::Int(n) => n.to_string(),
Value::Float(f) => f.to_string(),
Value::Str(s) => format!("\"{}\"", s),
Value::Char(c) => format!("'{}'", c),
Value::Array(a) => format!("<array len={}>", a.borrow().len()),
Value::Tuple(t) => format!("<tuple len={}>", t.borrow().len()),
Value::Map(m) => format!("<map len={}>", m.borrow().len()),
Value::Function(f) => format!("<fn {}>", f.name),
Value::Closure(_) => "<closure>".to_string(),
Value::Class(c) => format!("<class {}>", c.name),
Value::Instance { class_index, .. } => format!("<instance of class {}>", class_index),
Value::Ok(_) => "Ok(..)".to_string(),
Value::Err(_) => "Err(..)".to_string(),
Value::Some(_) => "Some(..)".to_string(),
Value::Builtin { name, .. } => format!("<builtin {}>", name),
}
}