Skip to main content

luaur_bytecode/methods/
bytecode_builder_dump_constant.rs

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}