1use crate::enums::r#type::Type;
2use crate::functions::ceillog_2::ceillog_2;
3use crate::functions::printable_string_constant::printableStringConstant;
4use crate::methods::bytecode_builder_get_string_hash::bytecode_builder_get_string_hash;
5use crate::records::bytecode_builder::BytecodeBuilder;
6use alloc::string::String;
7use core::cmp;
8use luaur_common::functions::format_append::formatAppend;
9use luaur_common::functions::format_g::format_g;
10use luaur_common::macros::luau_assert::LUAU_ASSERT;
11
12impl BytecodeBuilder {
13 pub fn dump_constant(&self, result: &mut String, k: i32, detailed: bool) {
14 LUAU_ASSERT!((k as u32) < self.constants.len() as u32);
15 let data = &self.constants[k as usize];
16
17 match data.r#type {
18 Type::Type_Nil => formatAppend(result, format_args!("nil")),
19 Type::Type_Boolean => formatAppend(
20 result,
21 format_args!(
22 "{}",
23 if unsafe { data.value.valueBoolean } {
24 "true"
25 } else {
26 "false"
27 }
28 ),
29 ),
30 Type::Type_Number => formatAppend(
31 result,
32 format_args!("{}", format_g(unsafe { data.value.valueNumber }, 17)),
33 ),
34 Type::Type_Integer => formatAppend(
35 result,
36 format_args!("{}", unsafe { data.value.valueInteger64 } as i64),
37 ),
38 Type::Type_Vector => {
39 let v = unsafe { data.value.valueVector };
40 if v[3] == 0.0 {
41 formatAppend(
42 result,
43 format_args!(
44 "{}, {}, {}",
45 format_g(v[0] as f64, 9),
46 format_g(v[1] as f64, 9),
47 format_g(v[2] as f64, 9)
48 ),
49 );
50 } else {
51 formatAppend(
52 result,
53 format_args!(
54 "{}, {}, {}, {}",
55 format_g(v[0] as f64, 9),
56 format_g(v[1] as f64, 9),
57 format_g(v[2] as f64, 9),
58 format_g(v[3] as f64, 9)
59 ),
60 );
61 }
62 }
63 Type::Type_String => {
64 let str_idx = unsafe { data.value.valueString };
65 let str = &self.debug_strings[str_idx as usize - 1];
66 let bytes =
67 unsafe { core::slice::from_raw_parts(str.data as *const u8, str.length) };
68 if printableStringConstant(bytes) {
69 if str.length < 32 {
70 formatAppend(
71 result,
72 format_args!("'{:.*}'", str.length, unsafe {
73 core::str::from_utf8_unchecked(bytes)
74 }),
75 );
76 } else {
77 formatAppend(
78 result,
79 format_args!("'{:.*}'...", 32, unsafe {
80 core::str::from_utf8_unchecked(bytes)
81 }),
82 );
83 }
84 } else {
85 formatAppend(result, format_args!("'"));
86 for i in 0..cmp::min(str.length, 32) {
87 let b = bytes[i];
88 if b < b' ' {
89 formatAppend(result, format_args!("\\x{:02X}", b));
90 } else {
91 formatAppend(result, format_args!("{}", b as char));
92 }
93 }
94 if str.length >= 32 {
95 formatAppend(result, format_args!("'..."));
96 } else {
97 formatAppend(result, format_args!("'"));
98 }
99 }
100 }
101 Type::Type_Import => {
102 let mut id0: i32 = -1;
103 let mut id1: i32 = -1;
104 let mut id2: i32 = -1;
105 let count = BytecodeBuilder::decompose_import_id(
106 unsafe { data.value.valueImport },
107 &mut id0,
108 &mut id1,
109 &mut id2,
110 );
111 if count > 0 {
112 let id = self.constants[id0 as usize];
113 LUAU_ASSERT!(
114 id.r#type == Type::Type_String
115 && unsafe { id.value.valueString } as usize <= self.debug_strings.len()
116 );
117 let str = &self.debug_strings[unsafe { id.value.valueString } as usize - 1];
118 formatAppend(
119 result,
120 format_args!("{}", unsafe {
121 core::str::from_utf8_unchecked(core::slice::from_raw_parts(
122 str.data as *const u8,
123 str.length,
124 ))
125 }),
126 );
127
128 if count > 1 {
129 let id = self.constants[id1 as usize];
130 LUAU_ASSERT!(
131 id.r#type == Type::Type_String
132 && unsafe { id.value.valueString } as usize
133 <= self.debug_strings.len()
134 );
135 let str = &self.debug_strings[unsafe { id.value.valueString } as usize - 1];
136 formatAppend(
137 result,
138 format_args!(".{}", unsafe {
139 core::str::from_utf8_unchecked(core::slice::from_raw_parts(
140 str.data as *const u8,
141 str.length,
142 ))
143 }),
144 );
145 }
146
147 if count > 2 {
148 let id = self.constants[id2 as usize];
149 LUAU_ASSERT!(
150 id.r#type == Type::Type_String
151 && unsafe { id.value.valueString } as usize
152 <= self.debug_strings.len()
153 );
154 let str = &self.debug_strings[unsafe { id.value.valueString } as usize - 1];
155 formatAppend(
156 result,
157 format_args!(".{}", unsafe {
158 core::str::from_utf8_unchecked(core::slice::from_raw_parts(
159 str.data as *const u8,
160 str.length,
161 ))
162 }),
163 );
164 }
165 }
166 }
167 Type::Type_Table => {
168 if detailed {
169 let shape = &self.table_shapes[unsafe { data.value.valueTable } as usize];
170 let sizenode = if shape.length > 0 {
171 1u32 << (ceillog_2(shape.length as i32) as u32)
172 } else {
173 0u32
174 };
175 let mask = if sizenode > 0 { sizenode - 1 } else { 0u32 };
176
177 let mut slots = vec![0u32; shape.length as usize];
178 let mut slot_owner = vec![!0u32; sizenode as usize];
179
180 for i in 0..shape.length as usize {
181 let key_const = &self.constants[shape.keys[i] as usize];
182 LUAU_ASSERT!(
183 key_const.r#type == Type::Type_String
184 && unsafe { key_const.value.valueString } != 0
185 );
186 let str = &self.debug_strings
187 [unsafe { key_const.value.valueString } as usize - 1];
188 let hash = bytecode_builder_get_string_hash(*str);
189 slots[i] = hash & mask;
190
191 if slot_owner[slots[i] as usize] == !0u32 {
192 slot_owner[slots[i] as usize] = i as u32;
193 }
194 }
195
196 formatAppend(result, format_args!("{{"));
197
198 for i in 0..shape.length as usize {
199 if i > 0 {
200 formatAppend(result, format_args!(", "));
201 }
202
203 formatAppend(result, format_args!("["));
204 self.dump_constant(result, shape.keys[i], false);
205 formatAppend(result, format_args!("]"));
206
207 if shape.hasConstants && shape.constants[i] != -1 {
208 formatAppend(result, format_args!(" = "));
209 self.dump_constant(result, shape.constants[i], false);
210 }
211
212 formatAppend(result, format_args!(" #{}", slots[i]));
213
214 if slot_owner[slots[i] as usize] != i as u32 {
215 formatAppend(result, format_args!(" (conflict)"));
216 }
217 }
218
219 formatAppend(result, format_args!("}} sizenode={}", sizenode));
220 } else {
221 formatAppend(result, format_args!("{{...}}"));
222 }
223 }
224 Type::Type_Closure => {
225 let func = &self.functions[unsafe { data.value.valueClosure } as usize];
226 if !func.dumpname.is_empty() {
227 formatAppend(result, format_args!("'{}'", func.dumpname));
228 }
229 }
230 Type::Type_ClassShape => {
231 let cs = &self.class_shapes[unsafe { data.value.valueClassShape } as usize];
232 let class_name_const = &self.constants[cs.className as usize];
233 LUAU_ASSERT!(
234 class_name_const.r#type == Type::Type_String
235 && unsafe { class_name_const.value.valueString } as usize
236 <= self.debug_strings.len()
237 );
238 let str =
239 &self.debug_strings[unsafe { class_name_const.value.valueString } as usize - 1];
240 LUAU_ASSERT!(printableStringConstant(unsafe {
241 core::slice::from_raw_parts(str.data as *const u8, str.length)
242 }));
243 formatAppend(
244 result,
245 format_args!(
246 "class {} (props: {}, methods: {})",
247 unsafe {
248 core::str::from_utf8_unchecked(core::slice::from_raw_parts(
249 str.data as *const u8,
250 str.length,
251 ))
252 },
253 cs.propertyNames.len(),
254 cs.methodNames.len()
255 ),
256 );
257 }
258 }
259 }
260}