luaur_bytecode/methods/
bytecode_builder_dump_current_function.rs1use crate::enums::dump_flags::DumpFlags;
2use crate::functions::get_base_type_string::get_base_type_string;
3use crate::records::bytecode_builder::BytecodeBuilder;
4use alloc::string::String;
5use alloc::vec::Vec;
6use core::ffi::CStr;
7use luaur_common::enums::luau_bytecode_type::{LuauBytecodeType, LBC_TYPE_OPTIONAL_BIT};
8use luaur_common::enums::luau_opcode::LuauOpcode;
9use luaur_common::functions::format_append::formatAppend;
10use luaur_common::functions::get_jump_target::get_jump_target;
11use luaur_common::functions::get_op_length::get_op_length;
12use luaur_common::macros::luau_assert::LUAU_ASSERT;
13use luaur_common::macros::luau_insn_op::LUAU_INSN_OP;
14
15impl BytecodeBuilder {
16 pub fn dump_current_function(&self, dumpinstoffs: &mut Vec<i32>) -> String {
17 if (self.dump_flags & (DumpFlags::Dump_Code as u32 | DumpFlags::Dump_Constants as u32)) == 0
18 {
19 return String::new();
20 }
21
22 let mut last_line = -1;
23 let mut next_remark = 0;
24 let mut result = String::new();
25
26 if self.dump_flags & DumpFlags::Dump_Locals as u32 != 0 {
27 for i in 0..self.debug_locals.len() {
28 let l = &self.debug_locals[i as usize];
29
30 if l.startpc == l.endpc {
31 LUAU_ASSERT!(l.startpc < self.lines.len() as u32);
32
33 formatAppend(
34 &mut result,
35 format_args!(
36 "local {}: reg {}, start pc {} line {}, no live range\n",
37 i, l.reg, l.startpc, self.lines[l.startpc as usize]
38 ),
39 );
40 } else {
41 LUAU_ASSERT!(l.startpc < l.endpc);
42 LUAU_ASSERT!(l.startpc < self.lines.len() as u32);
43 LUAU_ASSERT!(l.endpc <= self.lines.len() as u32);
44
45 formatAppend(
46 &mut result,
47 format_args!(
48 "local {}: reg {}, start pc {} line {}, end pc {} line {}\n",
49 i,
50 l.reg,
51 l.startpc,
52 self.lines[l.startpc as usize],
53 l.endpc - 1,
54 self.lines[(l.endpc - 1) as usize]
55 ),
56 );
57 }
58 }
59 }
60
61 if self.dump_flags & DumpFlags::Dump_Types as u32 != 0 {
62 let typeinfo = &self.functions.last().unwrap().typeinfo;
63 let typeinfo_bytes = typeinfo.as_bytes();
64
65 for i in 2..typeinfo_bytes.len() {
66 let et = typeinfo_bytes[i];
67
68 let name = match self.try_get_userdata_type_name(LuauBytecodeType(et as u16)) {
70 Some(s) => alloc::borrow::Cow::Borrowed(s),
71 None => unsafe { CStr::from_ptr(get_base_type_string(et)).to_string_lossy() },
72 };
73 let optional = if (et as u16 & LBC_TYPE_OPTIONAL_BIT.0) != 0 {
74 "?"
75 } else {
76 ""
77 };
78
79 formatAppend(
80 &mut result,
81 format_args!("R{}: {}{} [argument]\n", i - 2, name, optional),
82 );
83 }
84
85 for i in 0..self.typed_upvals.len() {
86 let l = &self.typed_upvals[i];
87
88 let name = match self.try_get_userdata_type_name(l.r#type) {
89 Some(s) => alloc::borrow::Cow::Borrowed(s),
90 None => unsafe {
91 CStr::from_ptr(get_base_type_string(l.r#type.0 as u8)).to_string_lossy()
92 },
93 };
94 let optional = if (l.r#type.0 & LBC_TYPE_OPTIONAL_BIT.0) != 0 {
95 "?"
96 } else {
97 ""
98 };
99
100 formatAppend(&mut result, format_args!("U{}: {}{}\n", i, name, optional));
101 }
102
103 for i in 0..self.typed_locals.len() {
104 let l = &self.typed_locals[i];
105
106 let name = match self.try_get_userdata_type_name(l.r#type) {
107 Some(s) => alloc::borrow::Cow::Borrowed(s),
108 None => unsafe {
109 CStr::from_ptr(get_base_type_string(l.r#type.0 as u8)).to_string_lossy()
110 },
111 };
112 let optional = if (l.r#type.0 & LBC_TYPE_OPTIONAL_BIT.0) != 0 {
113 "?"
114 } else {
115 ""
116 };
117
118 formatAppend(
119 &mut result,
120 format_args!(
121 "R{}: {}{} from {} to {}\n",
122 l.reg, name, optional, l.startpc, l.endpc
123 ),
124 );
125 }
126 }
127
128 if self.dump_flags & DumpFlags::Dump_Constants as u32 != 0 {
129 for i in 0..self.constants.len() {
130 formatAppend(&mut result, format_args!("K{}: ", i));
131 self.dump_constant(&mut result, i as i32, true);
132 formatAppend(&mut result, format_args!("\n"));
133 }
134 }
135
136 if self.dump_flags & DumpFlags::Dump_Code as u32 != 0 {
137 let mut labels = vec![-1; self.insns.len()];
138
139 let mut i = 0;
140 while i < self.insns.len() {
141 let target = get_jump_target(self.insns[i], i as u32);
142
143 if target >= 0 {
144 LUAU_ASSERT!((target as usize) < self.insns.len());
145 labels[target as usize] = 0;
146 }
147
148 let op: LuauOpcode =
149 unsafe { core::mem::transmute(LUAU_INSN_OP(self.insns[i]) as u8) };
150 let op_len = get_op_length(op) as usize;
151 i += op_len;
152 LUAU_ASSERT!(i <= self.insns.len());
153 }
154
155 let mut next_label = 0;
156
157 for i in 0..labels.len() {
158 if labels[i] == 0 {
159 labels[i] = next_label;
160 next_label += 1;
161 }
162 }
163
164 dumpinstoffs.resize(self.insns.len() + 1, -1);
165
166 let mut i = 0;
167 while i < self.insns.len() {
168 let code = &self.insns[i];
169 let op = LUAU_INSN_OP(*code) as u8;
170
171 dumpinstoffs[i] = result.len() as i32;
172
173 if op == LuauOpcode::LOP_PREPVARARGS as u8 {
179 i += 1;
180 continue;
181 }
182
183 if self.dump_flags & DumpFlags::Dump_Remarks as u32 != 0 {
184 while next_remark < self.debug_remarks.len()
185 && self.debug_remarks[next_remark].0 == i as u32
186 {
187 let remark_start = self.debug_remarks[next_remark].1 as usize;
188 let remark_end = if next_remark + 1 < self.debug_remarks.len() {
189 self.debug_remarks[next_remark + 1].1 as usize
190 } else {
191 self.debug_remark_buffer.len()
192 };
193 let remark_str = self.debug_remark_buffer[remark_start..remark_end]
197 .split('\0')
198 .next()
199 .unwrap_or("");
200 formatAppend(&mut result, format_args!("REMARK {}\n", remark_str));
201 next_remark += 1;
202 }
203 }
204
205 if self.dump_flags & DumpFlags::Dump_Source as u32 != 0 {
206 let line = self.lines[i];
207
208 if line > 0 && line != last_line {
209 LUAU_ASSERT!(((line - 1) as usize) < self.dump_source.len());
210 formatAppend(
211 &mut result,
212 format_args!("{:5}: {}\n", line, self.dump_source[(line - 1) as usize]),
213 );
214 last_line = line;
215 }
216 }
217
218 if self.dump_flags & DumpFlags::Dump_Lines as u32 != 0 {
219 formatAppend(&mut result, format_args!("{}: ", self.lines[i]));
220 }
221
222 if labels[i] != -1 {
223 formatAppend(&mut result, format_args!("L{}: ", labels[i]));
224 }
225
226 let target = get_jump_target(*code, i as u32);
227 let target_label = if target >= 0 {
228 labels[target as usize]
229 } else {
230 -1
231 };
232
233 self.dump_instruction(&self.insns[i..], &mut result, target_label);
238
239 let op: LuauOpcode = unsafe { core::mem::transmute(op) };
240 let op_len = get_op_length(op) as usize;
241 i += op_len;
242 LUAU_ASSERT!(i <= self.insns.len());
243 }
244
245 dumpinstoffs[self.insns.len()] = result.len() as i32;
246 }
247
248 result
249 }
250}