luaur-bytecode 0.1.1

Luau bytecode format and builder (Rust).
Documentation
use crate::enums::r#type::Type;
use crate::functions::ceillog_2::ceillog_2;
use crate::functions::printable_string_constant::printableStringConstant;
use crate::methods::bytecode_builder_get_string_hash::bytecode_builder_get_string_hash;
use crate::records::bytecode_builder::BytecodeBuilder;
use alloc::string::String;
use core::cmp;
use luaur_common::functions::format_append::formatAppend;
use luaur_common::functions::format_g::format_g;
use luaur_common::macros::luau_assert::LUAU_ASSERT;

impl BytecodeBuilder {
    pub fn dump_constant(&self, result: &mut String, k: i32, detailed: bool) {
        LUAU_ASSERT!((k as u32) < self.constants.len() as u32);
        let data = &self.constants[k as usize];

        match data.r#type {
            Type::Type_Nil => formatAppend(result, format_args!("nil")),
            Type::Type_Boolean => formatAppend(
                result,
                format_args!(
                    "{}",
                    if unsafe { data.value.valueBoolean } {
                        "true"
                    } else {
                        "false"
                    }
                ),
            ),
            Type::Type_Number => formatAppend(
                result,
                format_args!("{}", format_g(unsafe { data.value.valueNumber }, 17)),
            ),
            Type::Type_Integer => formatAppend(
                result,
                format_args!("{}", unsafe { data.value.valueInteger64 } as i64),
            ),
            Type::Type_Vector => {
                let v = unsafe { data.value.valueVector };
                if v[3] == 0.0 {
                    formatAppend(
                        result,
                        format_args!(
                            "{}, {}, {}",
                            format_g(v[0] as f64, 9),
                            format_g(v[1] as f64, 9),
                            format_g(v[2] as f64, 9)
                        ),
                    );
                } else {
                    formatAppend(
                        result,
                        format_args!(
                            "{}, {}, {}, {}",
                            format_g(v[0] as f64, 9),
                            format_g(v[1] as f64, 9),
                            format_g(v[2] as f64, 9),
                            format_g(v[3] as f64, 9)
                        ),
                    );
                }
            }
            Type::Type_String => {
                let str_idx = unsafe { data.value.valueString };
                let str = &self.debug_strings[str_idx as usize - 1];
                let bytes =
                    unsafe { core::slice::from_raw_parts(str.data as *const u8, str.length) };
                if printableStringConstant(bytes) {
                    if str.length < 32 {
                        formatAppend(
                            result,
                            format_args!("'{:.*}'", str.length, unsafe {
                                core::str::from_utf8_unchecked(bytes)
                            }),
                        );
                    } else {
                        formatAppend(
                            result,
                            format_args!("'{:.*}'...", 32, unsafe {
                                core::str::from_utf8_unchecked(bytes)
                            }),
                        );
                    }
                } else {
                    formatAppend(result, format_args!("'"));
                    for i in 0..cmp::min(str.length, 32) {
                        let b = bytes[i];
                        if b < b' ' {
                            formatAppend(result, format_args!("\\x{:02X}", b));
                        } else {
                            formatAppend(result, format_args!("{}", b as char));
                        }
                    }
                    if str.length >= 32 {
                        formatAppend(result, format_args!("'..."));
                    } else {
                        formatAppend(result, format_args!("'"));
                    }
                }
            }
            Type::Type_Import => {
                let mut id0: i32 = -1;
                let mut id1: i32 = -1;
                let mut id2: i32 = -1;
                let count = BytecodeBuilder::decompose_import_id(
                    unsafe { data.value.valueImport },
                    &mut id0,
                    &mut id1,
                    &mut id2,
                );
                if count > 0 {
                    let id = self.constants[id0 as usize];
                    LUAU_ASSERT!(
                        id.r#type == Type::Type_String
                            && unsafe { id.value.valueString } as usize <= self.debug_strings.len()
                    );
                    let str = &self.debug_strings[unsafe { id.value.valueString } as usize - 1];
                    formatAppend(
                        result,
                        format_args!("{}", unsafe {
                            core::str::from_utf8_unchecked(core::slice::from_raw_parts(
                                str.data as *const u8,
                                str.length,
                            ))
                        }),
                    );

                    if count > 1 {
                        let id = self.constants[id1 as usize];
                        LUAU_ASSERT!(
                            id.r#type == Type::Type_String
                                && unsafe { id.value.valueString } as usize
                                    <= self.debug_strings.len()
                        );
                        let str = &self.debug_strings[unsafe { id.value.valueString } as usize - 1];
                        formatAppend(
                            result,
                            format_args!(".{}", unsafe {
                                core::str::from_utf8_unchecked(core::slice::from_raw_parts(
                                    str.data as *const u8,
                                    str.length,
                                ))
                            }),
                        );
                    }

                    if count > 2 {
                        let id = self.constants[id2 as usize];
                        LUAU_ASSERT!(
                            id.r#type == Type::Type_String
                                && unsafe { id.value.valueString } as usize
                                    <= self.debug_strings.len()
                        );
                        let str = &self.debug_strings[unsafe { id.value.valueString } as usize - 1];
                        formatAppend(
                            result,
                            format_args!(".{}", unsafe {
                                core::str::from_utf8_unchecked(core::slice::from_raw_parts(
                                    str.data as *const u8,
                                    str.length,
                                ))
                            }),
                        );
                    }
                }
            }
            Type::Type_Table => {
                if detailed {
                    let shape = &self.table_shapes[unsafe { data.value.valueTable } as usize];
                    let sizenode = if shape.length > 0 {
                        1u32 << (ceillog_2(shape.length as i32) as u32)
                    } else {
                        0u32
                    };
                    let mask = if sizenode > 0 { sizenode - 1 } else { 0u32 };

                    let mut slots = vec![0u32; shape.length as usize];
                    let mut slot_owner = vec![!0u32; sizenode as usize];

                    for i in 0..shape.length as usize {
                        let key_const = &self.constants[shape.keys[i] as usize];
                        LUAU_ASSERT!(
                            key_const.r#type == Type::Type_String
                                && unsafe { key_const.value.valueString } != 0
                        );
                        let str = &self.debug_strings
                            [unsafe { key_const.value.valueString } as usize - 1];
                        let hash = bytecode_builder_get_string_hash(*str);
                        slots[i] = hash & mask;

                        if slot_owner[slots[i] as usize] == !0u32 {
                            slot_owner[slots[i] as usize] = i as u32;
                        }
                    }

                    formatAppend(result, format_args!("{{"));

                    for i in 0..shape.length as usize {
                        if i > 0 {
                            formatAppend(result, format_args!(", "));
                        }

                        formatAppend(result, format_args!("["));
                        self.dump_constant(result, shape.keys[i], false);
                        formatAppend(result, format_args!("]"));

                        if shape.hasConstants && shape.constants[i] != -1 {
                            formatAppend(result, format_args!(" = "));
                            self.dump_constant(result, shape.constants[i], false);
                        }

                        formatAppend(result, format_args!(" #{}", slots[i]));

                        if slot_owner[slots[i] as usize] != i as u32 {
                            formatAppend(result, format_args!(" (conflict)"));
                        }
                    }

                    formatAppend(result, format_args!("}} sizenode={}", sizenode));
                } else {
                    formatAppend(result, format_args!("{{...}}"));
                }
            }
            Type::Type_Closure => {
                let func = &self.functions[unsafe { data.value.valueClosure } as usize];
                if !func.dumpname.is_empty() {
                    formatAppend(result, format_args!("'{}'", func.dumpname));
                }
            }
            Type::Type_ClassShape => {
                let cs = &self.class_shapes[unsafe { data.value.valueClassShape } as usize];
                let class_name_const = &self.constants[cs.className as usize];
                LUAU_ASSERT!(
                    class_name_const.r#type == Type::Type_String
                        && unsafe { class_name_const.value.valueString } as usize
                            <= self.debug_strings.len()
                );
                let str =
                    &self.debug_strings[unsafe { class_name_const.value.valueString } as usize - 1];
                LUAU_ASSERT!(printableStringConstant(unsafe {
                    core::slice::from_raw_parts(str.data as *const u8, str.length)
                }));
                formatAppend(
                    result,
                    format_args!(
                        "class {} (props: {}, methods: {})",
                        unsafe {
                            core::str::from_utf8_unchecked(core::slice::from_raw_parts(
                                str.data as *const u8,
                                str.length,
                            ))
                        },
                        cs.propertyNames.len(),
                        cs.methodNames.len()
                    ),
                );
            }
        }
    }
}