luaur-compiler 0.1.3

Luau source-to-bytecode compiler (Rust).
Documentation
use crate::enums::type_constant_folding::Type;
use crate::functions::escape_and_append::escapeAndAppend;
use crate::functions::sref_compiler::sref_ast_name;
use crate::functions::sref_compiler_alt_c::sref_ast_array_c_char;
use crate::records::compile_error::CompileError;
use crate::records::compiler::Compiler;
use crate::records::constant::Constant;
use crate::records::reg_scope::RegScope;
use alloc::vec::Vec;
use core::ffi::c_char;
use luaur_ast::records::ast_array::AstArray;
use luaur_ast::records::ast_expr_interp_string::AstExprInterpString;
use luaur_ast::records::ast_name::AstName;
use luaur_bytecode::methods::bytecode_builder_get_string_hash::bytecode_builder_get_string_hash;
use luaur_common::enums::luau_opcode::LuauOpcode;

impl Compiler {
    pub fn compile_expr_interp_string(
        &mut self,
        expr: *mut AstExprInterpString,
        target: u8,
        target_temp: bool,
    ) {
        unsafe {
            let expr_ref = &*expr;
            let mut format_capacity = 0;
            for string in expr_ref.strings.iter() {
                format_capacity +=
                    (*string).size + (*string).iter().filter(|&&c| c == b'%' as i8).count();
            }

            let mut skipped_sub_expr = 0;
            for i in 0..expr_ref.expressions.size {
                let sub_expr = *expr_ref.expressions.data.add(i);
                if let Some(c) = self.constants.find(&sub_expr) {
                    if c.r#type == Type::Type_String {
                        format_capacity += c.string_length as usize
                            + c.get_string().iter().filter(|&&c| c == b'%' as i8).count();
                        skipped_sub_expr += 1;
                    } else {
                        format_capacity += 2;
                    }
                } else {
                    format_capacity += 2;
                }
            }

            let mut format_string = Vec::with_capacity(format_capacity);
            for i in 0..expr_ref.strings.size {
                let string = *expr_ref.strings.data.add(i);
                escapeAndAppend(&mut format_string, string.data, string.size);
                if i < expr_ref.expressions.size {
                    let sub_expr = *expr_ref.expressions.data.add(i);
                    if let Some(c) = self.constants.find(&sub_expr) {
                        if c.r#type == Type::Type_String {
                            escapeAndAppend(
                                &mut format_string,
                                c.get_string().data,
                                c.string_length as usize,
                            );
                        } else {
                            format_string.extend_from_slice(b"%*");
                        }
                    } else {
                        format_string.extend_from_slice(b"%*");
                    }
                }
            }

            let format_string_index = if format_string.is_empty() {
                let interned = (*self.names).get_or_add(c"".as_ptr(), 0);
                (*self.bytecode).add_constant_string(sref_ast_name(interned))
            } else {
                let interned = (*self.names)
                    .get_or_add(format_string.as_ptr() as *const c_char, format_string.len());
                let format_string_array = AstArray {
                    data: interned.value as *mut c_char,
                    size: format_string.len(),
                };
                (*self.bytecode).add_constant_string(sref_ast_array_c_char(format_string_array))
            };

            if format_string_index < 0 {
                CompileError::raise(
                    &expr_ref.base.base.location,
                    format_args!("Exceeded constant limit; simplify the code to compile"),
                );
            }

            let mut rs = self.reg_scope_compiler();
            let reg_count = 2 + expr_ref.expressions.size - skipped_sub_expr;
            let target_top = luaur_common::FFlag::LuauCompileStringInterpTargetTop.get()
                && target_temp
                && target as u32 == self.reg_top - 1;
            let base_reg = if target_top {
                self.alloc_reg(expr as *mut _, (reg_count - 1) as u32) - 1
            } else {
                self.alloc_reg(expr as *mut _, reg_count as u32)
            };

            self.emit_load_k(base_reg, format_string_index);

            let mut skipped = 0;
            for i in 0..expr_ref.expressions.size {
                let sub_expr = *expr_ref.expressions.data.add(i);
                if self
                    .constants
                    .find(&sub_expr)
                    .map_or(true, |c| c.r#type != Type::Type_String)
                {
                    self.compile_expr_temp_top(sub_expr, base_reg + 2 + i as u8 - skipped as u8);
                } else {
                    skipped += 1;
                }
            }

            let format_method = sref_ast_name(AstName::ast_name_c_char(c"format".as_ptr()));
            let format_method_index = (*self.bytecode).add_constant_string(format_method);
            if format_method_index < 0 {
                CompileError::raise(
                    &expr_ref.base.base.location,
                    format_args!("Exceeded constant limit; simplify the code to compile"),
                );
            }

            (*self.bytecode).emit_abc(
                LuauOpcode::LOP_NAMECALL,
                base_reg,
                base_reg,
                bytecode_builder_get_string_hash(format_method) as u8,
            );
            (*self.bytecode).emit_aux(format_method_index as u32);
            (*self.bytecode).emit_abc(
                LuauOpcode::LOP_CALL,
                base_reg,
                (expr_ref.expressions.size + 2 - skipped_sub_expr) as u8,
                2,
            );
            if target != base_reg {
                (*self.bytecode).emit_abc(LuauOpcode::LOP_MOVE, target, base_reg, 0);
            }
        }
    }
}