1use core::fmt;
2use core::fmt::{Write, Formatter};
3use core::iter;
4use string_interner::Symbol as _;
5
6use crate::language::FloatType;
7use crate::codegen::OpCode;
8use crate::codegen::chunk::{UnloadedProgram, Chunk};
9use crate::codegen::consts::{Constant, ConstID};
10use crate::codegen::funproto::{UnloadedFunction, FunctionID};
11use crate::debug::symbol::{DebugSymbol, DebugSymbolTable, ResolvedSymbol, ResolvedSymbolTable, ChunkSymbols};
12use crate::debug::symbol::errors::SymbolResolutionError;
13
14
15const PAD_WIDTH: usize = 60;
16
17pub struct Disassembler<'c, 's> {
18 program: &'c UnloadedProgram,
19 symbols: Option<&'s ChunkSymbols>,
20 symbol_table: Option<&'s ResolvedSymbolTable<'s>>,
21}
22
23type ResolvedSymbolResult<'s> = Result<&'s ResolvedSymbol, &'s SymbolResolutionError>;
25enum Symbol<'s> {
26 Unresolved(Option<&'s DebugSymbol>),
28 Resolved(Option<ResolvedSymbolResult<'s>>),
29}
30
31impl<'c, 's> Disassembler<'c, 's> {
32 pub fn new(program: &'c UnloadedProgram) -> Self {
33 Self { program, symbols: None, symbol_table: None }
34 }
35
36 pub fn with_symbols(mut self, symbols: &'s ChunkSymbols) -> Self {
37 self.symbols.replace(symbols); self
38 }
39
40 pub fn with_symbol_table(mut self, symbol_table: &'s ResolvedSymbolTable<'s>) -> Self {
41 self.symbol_table.replace(symbol_table); self
42 }
43
44 pub fn write_disassembly(&self, fmt: &mut impl Write) -> fmt::Result {
45 writeln!(fmt, "\n\nmain:\n")?;
46 let symbols = self.symbols.and_then(|symbols| symbols.get(&Chunk::Main));
47 self.decode_chunk(fmt, self.program.main(), symbols)?;
48
49 for (chunk_id, chunk) in self.program.iter_chunks() {
50 match chunk_id {
51 Chunk::Function(fun_id) => {
52 let function = self.program.get_function(fun_id);
53 let name = function.signature.name
54 .and_then(|cid| self.try_get_string(cid));
55
56 if let Some(name) = name {
57 writeln!(fmt, "\n\nchunk {} ({}):\n", fun_id, name)?;
58 } else {
59
60 writeln!(fmt, "\n\nchunk {}:\n", fun_id)?;
61 }
62 },
63
64 _ => {
65 writeln!(fmt, "\n\nchunk:\n")?;
66 }
67 }
68
69 let symbols = self.symbols.and_then(|symbols| symbols.get(&chunk_id));
70 self.decode_chunk(fmt, chunk, symbols)?;
71 }
72
73 Ok(())
74 }
75
76 fn decode_chunk(&self, fmt: &mut impl Write, chunk: &[u8], symbols: Option<&'s DebugSymbolTable>) -> fmt::Result {
77 let mut symbols = symbols.map(|symbols| symbols.iter().peekable());
78 let mut last_symbol = None;
79
80 let mut offset = 0;
81 while offset < chunk.len() {
82 let (_, bytes) = chunk.split_at(offset);
83
84 let unresolved = symbols.as_mut().and_then(|iter| Self::seek_next_symbol(offset, iter));
86 let symbol = self.try_resolve_symbol(unresolved, last_symbol);
87 last_symbol = unresolved;
88
89 offset = self.decode_instr(fmt, &offset, bytes, symbol)?;
90 }
91 Ok(())
92 }
93
94 fn seek_next_symbol(offset: usize, symbols: &mut iter::Peekable<impl Iterator<Item=(usize, &'s DebugSymbol)>>) -> Option<&'s DebugSymbol> {
95 while matches!(symbols.peek(), Some((next_offset, _)) if *next_offset < offset) {
96 symbols.next();
97 }
98
99 match symbols.next() {
100 Some((next_offset, symbol)) if next_offset == offset => Some(symbol),
101 _ => None,
102 }
103 }
104
105 fn try_resolve_symbol<'a>(&self, unresolved: Option<&'a DebugSymbol>, last_symbol: Option<&DebugSymbol>) -> Option<Symbol<'a>> where 's: 'a {
107 let resolved = unresolved.and_then(|symbol| self.symbol_table.and_then(
108 |symbol_table| symbol_table.lookup(symbol)
109 ));
110
111 let is_repeat = last_symbol.and(unresolved).is_some() && last_symbol.unwrap() == unresolved.unwrap();
112
113 if resolved.is_some() {
114 if !is_repeat { Some(Symbol::Resolved(resolved)) }
115 else { Some(Symbol::Resolved(None)) }
116 } else if unresolved.is_some() {
117 if !is_repeat { Some(Symbol::Unresolved(unresolved)) }
118 else { Some(Symbol::Unresolved(None)) }
119 } else { None }
120 }
121
122 fn decode_instr(&self, fmt: &mut impl Write, offset: &usize, instr: &[u8], symbol: Option<Symbol>) -> Result<usize, fmt::Error> { let mut line = String::new();
123
124 write!(line, "{:04X} ", offset)?;
125
126
127 let opcode = OpCode::from_byte(instr[0]);
128 match opcode {
129 Some(opcode) => match opcode {
130
131 OpCode::Drop | OpCode::DropLocals => {
132 let len = instr[1];
133 write!(line, "{:16} {: >4}", opcode, len)?;
134 }
135
136 OpCode::LoadConst => {
137 let cid = ConstID::from(instr[1]);
138 write!(line, "{:16} {: >4} ", opcode, cid)?;
139 self.write_const(&mut line, self.program.get_const(cid))?;
140 },
141
142 OpCode::LoadConst16 => {
143 let cid = ConstID::from_le_bytes(instr[1..=2].try_into().unwrap());
144 write!(line, "{:16} {: >4} ", opcode, cid)?;
145 self.write_const(&mut line, self.program.get_const(cid))?;
146 },
147
148 OpCode::LoadFunction => {
149 let fun_id = FunctionID::from(instr[1]);
150 write!(line, "{:16} {: >4} ", opcode, fun_id)?;
151 self.write_function(&mut line, self.program.get_function(fun_id))?;
152 },
153
154 OpCode::LoadFunction16 => {
155 let fun_id = FunctionID::from_le_bytes(instr[1..=2].try_into().unwrap());
156 write!(line, "{:16} {: >4} ", opcode, fun_id)?;
157 self.write_function(&mut line, self.program.get_function(fun_id))?;
158 },
159
160 OpCode::StoreLocal | OpCode::LoadLocal => {
161 let index = instr[1];
162 write!(line, "{:16} {: >4}", opcode, index)?;
163 },
164 OpCode::StoreLocal16 | OpCode::LoadLocal16 => {
165 let index = u16::from_le_bytes(instr[1..=2].try_into().unwrap());
166 write!(line, "{:16} {: >4}", opcode, index)?;
167 },
168
169 OpCode::StoreUpvalue | OpCode::LoadUpvalue => {
170 let index = instr[1];
171 write!(line, "{:16} {: >4}", opcode, index)?;
172 }
173 OpCode::StoreUpvalue16 | OpCode::LoadUpvalue16 => {
174 let index = u16::from_le_bytes(instr[1..=2].try_into().unwrap());
175 write!(line, "{:16} {: >4}", opcode, index)?;
176 }
177
178 OpCode::CloseUpvalue => {
179 let index = instr[1];
180 write!(line, "{:16} {: >4}", opcode, index)?;
181 }
182 OpCode::CloseUpvalue16 => {
183 let index = u16::from_le_bytes(instr[1..=2].try_into().unwrap());
184 write!(line, "{:16} {: >4}", opcode, index)?;
185 }
186
187 OpCode::Tuple => {
188 let len = instr[1];
189 write!(line, "{:16} {: >4}", opcode, len)?;
190 }
191
192 OpCode::UInt8 => {
193 let value = Constant::Integer(instr[1].into());
194 write!(line, "{:16} ", opcode)?;
195 self.write_const(&mut line, &value)?;
196 }
197
198 OpCode::Int8 => {
199 let value = Constant::Integer(i8::from_le_bytes([instr[1]]).into());
200 write!(line, "{:16} ", opcode)?;
201 self.write_const(&mut line, &value)?;
202 }
203
204 OpCode::Int16 => {
205 let value = Constant::Integer(i16::from_le_bytes([instr[1], instr[2]]).into());
206 write!(line, "{:16} ", opcode)?;
207 self.write_const(&mut line, &value)?;
208 }
209
210 OpCode::Jump |
211 OpCode::JumpIfFalse |
212 OpCode::JumpIfTrue |
213 OpCode::PopJumpIfFalse |
214 OpCode::PopJumpIfTrue => {
215 let jmp = i16::from_le_bytes(instr[1..=2].try_into().unwrap());
216 let dest = i128::from(jmp) + i128::try_from(offset + opcode.instr_len()).expect("offset too large");
217 let relative = i64::from(jmp) + i64::try_from(opcode.instr_len()).unwrap();
218 write!(line, "{:16} {: >4} -> {:04X}", opcode, relative, dest)?;
219 }
220
221 OpCode::LongJump |
222 OpCode::LongJumpIfFalse |
223 OpCode::LongJumpIfTrue |
224 OpCode::PopLongJumpIfFalse |
225 OpCode::PopLongJumpIfTrue => {
226 let jmp = i32::from_le_bytes(instr[1..=4].try_into().unwrap());
227 let dest = i128::from(jmp) + i128::try_from(offset + opcode.instr_len()).expect("offset too large");
228 let relative = i64::from(jmp) + i64::try_from(opcode.instr_len()).unwrap();
229 write!(line, "{:16} {: >4} -> {:04X}", opcode, relative, dest)?;
230 }
231
232 opcode => write!(line, "{:16}", opcode)?,
233 },
234
235 None => write!(line, "Unknown! {:#x}", instr[0])?,
236 }
237
238 if let Some(symbol) = symbol {
239 if line.len() < PAD_WIDTH {
240 line.extend(iter::repeat(' ').take(PAD_WIDTH - line.len()))
241 }
242 match symbol {
243 Symbol::Unresolved(symbol) => self.write_unresolved_symbol(&mut line, symbol)?,
244 Symbol::Resolved(symbol) => self.write_debug_symbol(&mut line, symbol)?,
245 }
246 }
247
248 writeln!(fmt, "{}", line)?;
249
250 Ok(offset + opcode.map_or(1, |op| op.instr_len()))
251 }
252
253 fn write_unresolved_symbol(&self, fmt: &mut impl fmt::Write, symbol: Option<&DebugSymbol>) -> fmt::Result {
254 match symbol {
255 Some(symbol) => write!(fmt, "| ${}:{}", symbol.start(), symbol.end()),
256 None => write!(fmt, "|"), }
258
259 }
260
261 fn write_debug_symbol(&self, fmt: &mut impl fmt::Write, symbol: Option<ResolvedSymbolResult>) -> fmt::Result {
262 match symbol {
263 Some(Ok(symbol)) => {
264 write!(fmt, "{: >4}| ", symbol.lineno())?;
265
266 let line = symbol.iter_whole_lines().next().unwrap_or("").trim_end();
267 if symbol.is_multiline() {
268 let (before, sym_text) = line.split_at(symbol.start());
269 write!(fmt, "{}`{}...`", before, sym_text)
270 } else {
271 let (before, rest) = line.split_at(symbol.start());
272 let (sym_text, after) = rest.split_at(symbol.end() - symbol.start());
273 write!(fmt, "{}`{}`{}", before, sym_text, after)
274 }
275 },
276
277 Some(Err(error)) => write!(fmt, " ERROR: {}", error),
278
279 None => write!(fmt, " |"),
280 }
281 }
282
283 fn write_const(&self, fmt: &mut impl fmt::Write, value: &Constant) -> fmt::Result {
284 if let Constant::String(index) = value {
285 let string = self.program.get_string(*index);
286 if string.len() > 16 {
287 return write!(fmt, "\"{}...\"", &string[..13]);
288 }
289 return write!(fmt, "\"{}\"", string);
290 }
291
292 write!(fmt, "{}", value)
293 }
294
295 fn write_function(&self, fmt: &mut impl fmt::Write, function: &UnloadedFunction) -> fmt::Result {
296 if let Some(name) = function.signature.name.and_then(|cid| self.try_get_string(cid)) {
297 write!(fmt, "'fun {}()'", name)
298 } else {
299 write!(fmt, "'fun()'")
300 }
301 }
302
303 fn try_get_string(&self, cid: ConstID) -> Option<&str> {
304 match self.program.get_const(cid) {
305 Constant::String(index) => Some(self.program.get_string(*index)),
306 _ => None,
307 }
308 }
309}
310
311impl fmt::Display for Disassembler<'_, '_> {
312 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
313 self.write_disassembly(fmt)
314 }
315}
316
317
318impl fmt::Display for Constant {
319 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
320 match self {
321 Self::Integer(value) => write!(fmt, "'{}'", value),
322 Self::Float(bytes) => write!(fmt, "'{:.6}'", FloatType::from_le_bytes(*bytes)),
323 Self::String(symbol) => write!(fmt, "${}", symbol.to_usize() + 1),
324 Self::Error { error, .. } => write!(fmt, "{:?}", error),
325 }
326 }
327}
328
329impl fmt::Display for UnloadedFunction {
330 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
331 write!(fmt, "chunk {}", self.fun_id)
332 }
333}