Skip to main content

luaur_bytecode/methods/
bytecode_builder_validate_variadic.rs

1use crate::records::bytecode_builder::BytecodeBuilder;
2use alloc::vec::Vec;
3use luaur_common::enums::luau_opcode::LuauOpcode;
4use luaur_common::functions::get_jump_target::getJumpTarget;
5use luaur_common::functions::get_op_length::getOpLength;
6use luaur_common::functions::is_fast_call::isFastCall;
7use luaur_common::macros::luau_assert::LUAU_ASSERT;
8use luaur_common::macros::luau_insn_b::LUAU_INSN_B;
9use luaur_common::macros::luau_insn_c::LUAU_INSN_C;
10use luaur_common::macros::luau_insn_op::LUAU_INSN_OP;
11
12impl BytecodeBuilder {
13    pub fn validate_variadic(&self) {
14        let mut variadic_seq = false;
15        let mut insn_targets = Vec::with_capacity(self.insns.len());
16        insn_targets.resize(self.insns.len(), false);
17
18        let mut i = 0;
19        while i < self.insns.len() {
20            let insn = self.insns[i];
21            let op = LuauOpcode::from(LUAU_INSN_OP(insn) as u8);
22
23            let target = getJumpTarget(insn, i as u32);
24
25            if target >= 0 && !isFastCall(op) {
26                LUAU_ASSERT!((target as usize) < self.insns.len());
27                insn_targets[target as usize] = true;
28            }
29
30            i += getOpLength(op) as usize;
31            LUAU_ASSERT!(i <= self.insns.len());
32        }
33
34        let mut i = 0;
35        while i < self.insns.len() {
36            let insn = self.insns[i];
37            let op = LuauOpcode::from(LUAU_INSN_OP(insn) as u8);
38
39            if variadic_seq {
40                LUAU_ASSERT!(!insn_targets[i]);
41            }
42
43            if op == LuauOpcode::LOP_CALL || op == LuauOpcode::LOP_CALLFB {
44                if LUAU_INSN_B(insn) == 0 {
45                    LUAU_ASSERT!(variadic_seq);
46                    variadic_seq = false;
47                } else {
48                    LUAU_ASSERT!(!variadic_seq);
49                }
50
51                if LUAU_INSN_C(insn) == 0 {
52                    LUAU_ASSERT!(!variadic_seq);
53                    variadic_seq = true;
54                }
55            } else if op == LuauOpcode::LOP_GETVARARGS && LUAU_INSN_B(insn) == 0 {
56                LUAU_ASSERT!(!variadic_seq);
57                variadic_seq = true;
58            } else if (op == LuauOpcode::LOP_RETURN && LUAU_INSN_B(insn) == 0)
59                || (op == LuauOpcode::LOP_SETLIST && LUAU_INSN_C(insn) == 0)
60            {
61                LUAU_ASSERT!(variadic_seq);
62                variadic_seq = false;
63            } else if op == LuauOpcode::LOP_FASTCALL {
64                let call_target = (i as i32 + LUAU_INSN_C(insn) as i32 + 1) as usize;
65                LUAU_ASSERT!(
66                    call_target < self.insns.len()
67                        && LuauOpcode::from(LUAU_INSN_OP(self.insns[call_target]) as u8)
68                            == LuauOpcode::LOP_CALL
69                );
70
71                if LUAU_INSN_B(self.insns[call_target]) == 0 {
72                    LUAU_ASSERT!(variadic_seq);
73                } else {
74                    LUAU_ASSERT!(!variadic_seq);
75                }
76            } else if op == LuauOpcode::LOP_CLOSEUPVALS
77                || op == LuauOpcode::LOP_NAMECALL
78                || op == LuauOpcode::LOP_GETIMPORT
79                || op == LuauOpcode::LOP_MOVE
80                || op == LuauOpcode::LOP_GETUPVAL
81                || op == LuauOpcode::LOP_GETGLOBAL
82                || op == LuauOpcode::LOP_GETTABLEKS
83                || op == LuauOpcode::LOP_COVERAGE
84            {
85            } else {
86                LUAU_ASSERT!(!variadic_seq);
87            }
88
89            i += getOpLength(op) as usize;
90            LUAU_ASSERT!(i <= self.insns.len());
91        }
92
93        LUAU_ASSERT!(!variadic_seq);
94    }
95}