luaur-bytecode 0.1.1

Luau bytecode format and builder (Rust).
Documentation
use crate::enums::bc_vm_const_kind::BcVmConstKind;
use crate::records::bytecode_builder::BytecodeBuilder;
use crate::records::comp_time_bytecode_graph_serializer::CompTimeBytecodeGraphSerializer;
use crate::records::string_ref::StringRef;
use crate::type_aliases::comp_time_bc_function::CompTimeBcFunction;
use alloc::string::String;
use alloc::vec::Vec;
use luaur_common::macros::luau_assert::LUAU_ASSERT;

fn string_ref(value: &str) -> StringRef {
    StringRef {
        data: value.as_ptr() as *const i8,
        length: value.len(),
    }
}

pub fn to_function_bytecode_bytecode_builder_comp_time_bc_function(
    bcb: &mut BytecodeBuilder,
    fn_: &mut CompTimeBcFunction,
) -> String {
    let function_id = bcb.begin_function(fn_.numparams, fn_.is_vararg);

    if !fn_.debugname.is_empty() {
        bcb.set_debug_function_name(string_ref(&fn_.debugname));
    }

    bcb.set_debug_function_line_defined(fn_.linedefined as i32);
    bcb.set_function_type_info(fn_.type_info.clone());

    for t in &fn_.upvalue_types {
        bcb.push_upval_type_info(*t);
    }

    for upval in &fn_.upvalue_names {
        bcb.push_debug_upval(string_ref(upval));
    }

    let mut consts: Vec<u16> = Vec::with_capacity(fn_.constants.len());

    for c in &fn_.constants {
        match c.kind {
            BcVmConstKind::Nil => consts.push(bcb.add_constant_nil() as u16),
            BcVmConstKind::Boolean => {
                consts.push(bcb.add_constant_boolean(unsafe { c.value.valueBoolean }) as u16)
            }
            BcVmConstKind::Number => {
                consts.push(bcb.add_constant_number(unsafe { c.value.valueNumber }) as u16)
            }
            BcVmConstKind::Vector => {
                let value = unsafe { c.value.valueVector };
                consts.push(bcb.add_constant_vector(value[0], value[1], value[2], value[3]) as u16);
            }
            BcVmConstKind::String => consts
                .push(bcb.add_constant_string(string_ref(unsafe { c.value.valueString })) as u16),
            BcVmConstKind::Import => {
                consts.push(bcb.add_import(unsafe { c.value.valueImport }) as u16)
            }
            BcVmConstKind::Table => {
                let value_table = unsafe { c.value.valueTable };
                LUAU_ASSERT!(value_table < fn_.table_shapes.len() as u32);
                consts.push(bcb.add_constant_table(&fn_.table_shapes[value_table as usize]) as u16);
            }
            BcVmConstKind::Closure => {
                consts.push(bcb.add_constant_closure(unsafe { c.value.valueClosure }) as u16)
            }
            BcVmConstKind::Integer => {
                consts.push(bcb.add_constant_integer(unsafe { c.value.valueInteger }) as u16)
            }
        }
    }

    for fid in &fn_.protos {
        bcb.add_child_function(*fid);
    }

    let mut serializer = CompTimeBytecodeGraphSerializer::comp_time_bytecode_graph_serializer_comp_time_bytecode_graph_serializer(bcb, fn_, &mut consts);
    let insns_pc = serializer.emit_bytecode();

    for local in &fn_.local_types {
        let startpc = if local.startpc < insns_pc.len() as u32 {
            insns_pc[local.startpc as usize]
        } else {
            bcb.get_debug_pc()
        };
        let endpc = if local.endpc < insns_pc.len() as u32 {
            insns_pc[local.endpc as usize]
        } else {
            bcb.get_debug_pc()
        };
        bcb.push_local_type_info(local.r#type, local.reg, startpc, endpc);
    }

    for local in &fn_.locals {
        let startpc = if local.startpc < insns_pc.len() as u32 {
            insns_pc[local.startpc as usize]
        } else {
            bcb.get_debug_pc()
        };
        let endpc = if local.endpc < insns_pc.len() as u32 {
            insns_pc[local.endpc as usize]
        } else {
            bcb.get_debug_pc()
        };
        bcb.push_debug_local(string_ref(local.varname), local.reg, startpc, endpc);
    }

    bcb.fold_jumps();
    bcb.expand_jumps();
    bcb.end_function(fn_.maxstacksize, fn_.nups, fn_.flags);

    if serializer.error() {
        return String::new();
    }

    bcb.get_function_data(function_id)
}