1use crate::register::{parameter_slots, RegOp, StackToRegister};
15use crate::{Bytecode, Chunk, Instruction, Opcode, Value};
16use alloc::format;
17use alloc::string::{String, ToString};
18use core::fmt::Write;
19
20pub fn disassemble_stack(bytecode: &Bytecode, only: Option<usize>) -> String {
23 let mut out = String::new();
24 for (i, ch) in bytecode.chunks.iter().enumerate() {
25 if only.is_some_and(|o| o != i) {
26 continue;
27 }
28 let _ = writeln!(
29 out,
30 "== chunk {} ({}) == {} instructions, {} constants, params {}",
31 i,
32 ch.name,
33 ch.instructions.len(),
34 ch.constants.len(),
35 parameter_slots(bytecode, i),
36 );
37 for (ip, ins) in ch.instructions.iter().enumerate() {
38 let _ = writeln!(
39 out,
40 "{:5} {:<14} {:<10} {}",
41 ip,
42 opcode_name(ins.opcode),
43 operand_text(ins.operand),
44 stack_note(bytecode, ch, ins),
45 );
46 }
47 out.push('\n');
48 }
49 out
50}
51
52pub fn disassemble_register(bytecode: &Bytecode, only: Option<usize>) -> String {
55 let mut out = String::new();
56 for (i, ch) in bytecode.chunks.iter().enumerate() {
57 if only.is_some_and(|o| o != i) {
58 continue;
59 }
60 let slots = parameter_slots(bytecode, i);
61 let reg = StackToRegister::new(&ch.name, &ch.instructions, &ch.constants)
62 .with_param_slots(slots)
63 .translate();
64 let _ = writeln!(
65 out,
66 "== chunk {} ({}) == {} regops, {} regs, {} param slots (registers 0..{})",
67 i,
68 reg.name,
69 reg.code.len(),
70 reg.n_regs,
71 reg.param_slots,
72 reg.param_slots,
73 );
74 for (rip, op) in reg.code.iter().enumerate() {
75 let _ = writeln!(
76 out,
77 "{:5} {:<46} {}",
78 rip,
79 format!("{:?}", op),
80 reg_note(®.constants, op),
81 );
82 }
83 out.push('\n');
84 }
85 out
86}
87
88fn opcode_name(op: Opcode) -> String {
89 format!("{:?}", op)
90}
91
92fn operand_text(operand: Option<u32>) -> String {
93 match operand {
94 Some(o) => format!("#{}", o),
95 None => String::new(),
96 }
97}
98
99fn stack_note(bytecode: &Bytecode, ch: &Chunk, ins: &Instruction) -> String {
101 let k = ins.operand.unwrap_or(0);
102 match ins.opcode {
103 Opcode::Push => match ch.constants.get(k as usize) {
104 Some(v) => format!("; {}", value_text(v)),
105 None => String::new(),
106 },
107 Opcode::GetProperty | Opcode::SetProperty => match ch.constants.get(k as usize) {
108 Some(Value::Str(name)) => format!("; \"{}\"", name),
109 Some(v) => format!("; {}", value_text(v)),
110 None => String::new(),
111 },
112 Opcode::LoadGlobal | Opcode::StoreGlobal => match bytecode.global_names.get(k as usize) {
113 Some(name) => format!("; global {}", name),
114 None => String::new(),
115 },
116 Opcode::Jump
117 | Opcode::JumpIfFalse
118 | Opcode::JumpIfTrue
119 | Opcode::TryBegin
120 | Opcode::TryEnd => format!("; -> {}", k),
121 Opcode::LoadLocal | Opcode::StoreLocal => format!("; local r{}", k),
122 Opcode::Slide => format!("; keep top, drop {} below", k),
123 Opcode::LoadUpvalue | Opcode::StoreUpvalue => format!("; upvalue {}", k),
124 Opcode::Call | Opcode::TailCall | Opcode::BuildArray | Opcode::BuildMap => {
125 format!("; {} operands", k)
126 }
127 Opcode::CallDirect => {
128 let argc = k >> 16;
130 let callee = k & 0xFFFF;
131 match ch.constants.get(callee as usize) {
132 Some(Value::Str(name)) => format!("; {}({} args, direct)", name, argc),
133 _ => format!("; {} args, direct", argc),
134 }
135 }
136 _ => String::new(),
137 }
138}
139
140fn reg_note(consts: &[Value], op: &RegOp) -> String {
142 match op {
143 RegOp::LoadConst { k, .. } => match consts.get(*k as usize) {
144 Some(v) => format!("; {}", value_text(v)),
145 None => String::new(),
146 },
147 RegOp::LoadGlobal { slot, .. } | RegOp::StoreGlobal { slot, .. } => {
148 format!("; global #{}", slot)
149 }
150 RegOp::GetProp { name, .. } | RegOp::SetProp { name, .. } | RegOp::Invoke { name, .. } => {
151 match consts.get(*name as usize) {
152 Some(Value::Str(s)) => format!("; \"{}\"", s),
153 _ => String::new(),
154 }
155 }
156 RegOp::Jump { target } => format!("; -> {}", target),
157 RegOp::JumpIfFalse { target, .. } | RegOp::JumpIfTrue { target, .. } => {
158 format!("; -> {}", target)
159 }
160 RegOp::CreateClass { k, .. } => match consts.get(*k as usize) {
161 Some(v) => format!("; {}", value_text(v)),
162 None => String::new(),
163 },
164 _ => String::new(),
165 }
166}
167
168fn value_text(v: &Value) -> String {
171 match v {
172 Value::Null => "null".to_string(),
173 Value::Bool(b) => b.to_string(),
174 Value::Int(n) => n.to_string(),
175 Value::Float(f) => f.to_string(),
176 Value::Str(s) => format!("\"{}\"", s),
177 Value::Char(c) => format!("'{}'", c),
178 Value::Array(a) => format!("<array len={}>", a.borrow().len()),
179 Value::Tuple(t) => format!("<tuple len={}>", t.borrow().len()),
180 Value::Map(m) => format!("<map len={}>", m.borrow().len()),
181 Value::Function(f) => format!("<fn {}>", f.name),
182 Value::Closure(_) => "<closure>".to_string(),
183 Value::Class(c) => format!("<class {}>", c.name),
184 Value::Instance { class_index, .. } => format!("<instance of class {}>", class_index),
185 Value::Ok(_) => "Ok(..)".to_string(),
186 Value::Err(_) => "Err(..)".to_string(),
187 Value::Some(_) => "Some(..)".to_string(),
188 Value::Builtin { name, .. } => format!("<builtin {}>", name),
189 }
190}