Skip to main content

luaur_bytecode/functions/
from_function_bytecode.rs

1extern crate alloc;
2use crate::enums::bc_vm_const_kind::BcVmConstKind;
3use crate::functions::read_string::read_string;
4use crate::records::bc_function::BcFunction;
5use crate::records::bc_vm_const::BcVmConst;
6use crate::records::bytecode_graph_parser::BytecodeGraphParser;
7use crate::records::debug_local_bytecode_graph::DebugLocal;
8use crate::records::table_shape::TableShape;
9use crate::records::typed_local_bytecode_graph::TypedLocal;
10use crate::type_aliases::comp_time_bc_function::CompTimeBcFunction;
11use crate::type_aliases::instruction::Instruction;
12use alloc::string::String;
13use alloc::vec::Vec;
14use core::option::Option;
15use luaur_common::enums::luau_bytecode_tag::LuauBytecodeTag;
16use luaur_common::enums::luau_bytecode_type::{LuauBytecodeType, LBC_TYPE_NIL};
17use luaur_common::functions::read::read;
18use luaur_common::functions::read_var_int::read_var_int;
19use luaur_common::functions::read_var_int_64::read_var_int_64;
20use luaur_common::macros::luau_assert::LUAU_ASSERT;
21
22#[allow(non_upper_case_globals)]
23const LBC_CONSTANT_NIL: u8 = LuauBytecodeTag::LBC_CONSTANT_NIL.0 as u8;
24#[allow(non_upper_case_globals)]
25const LBC_CONSTANT_BOOLEAN: u8 = LuauBytecodeTag::LBC_CONSTANT_BOOLEAN.0 as u8;
26#[allow(non_upper_case_globals)]
27const LBC_CONSTANT_NUMBER: u8 = LuauBytecodeTag::LBC_CONSTANT_NUMBER.0 as u8;
28#[allow(non_upper_case_globals)]
29const LBC_CONSTANT_STRING: u8 = LuauBytecodeTag::LBC_CONSTANT_STRING.0 as u8;
30#[allow(non_upper_case_globals)]
31const LBC_CONSTANT_IMPORT: u8 = LuauBytecodeTag::LBC_CONSTANT_IMPORT.0 as u8;
32#[allow(non_upper_case_globals)]
33const LBC_CONSTANT_TABLE: u8 = LuauBytecodeTag::LBC_CONSTANT_TABLE.0 as u8;
34#[allow(non_upper_case_globals)]
35const LBC_CONSTANT_CLOSURE: u8 = LuauBytecodeTag::LBC_CONSTANT_CLOSURE.0 as u8;
36#[allow(non_upper_case_globals)]
37const LBC_CONSTANT_VECTOR: u8 = LuauBytecodeTag::LBC_CONSTANT_VECTOR.0 as u8;
38#[allow(non_upper_case_globals)]
39const LBC_CONSTANT_TABLE_WITH_CONSTANTS: u8 =
40    LuauBytecodeTag::LBC_CONSTANT_TABLE_WITH_CONSTANTS.0 as u8;
41#[allow(non_upper_case_globals)]
42const LBC_CONSTANT_INTEGER: u8 = LuauBytecodeTag::LBC_CONSTANT_INTEGER.0 as u8;
43
44pub fn from_function_bytecode(
45    bytecode: String,
46    strings: &mut Vec<&[u8]>,
47) -> Option<CompTimeBcFunction> {
48    let mut data = bytecode.as_bytes();
49    let mut offset = 0usize;
50
51    let mut fn_ = BcFunction::default();
52
53    fn_.maxstacksize = read::<u8>(&data, &mut offset);
54    fn_.numparams = read::<u8>(&data, &mut offset);
55    fn_.nups = read::<u8>(&data, &mut offset);
56    fn_.is_vararg = read::<u8>(&data, &mut offset) != 0;
57    fn_.flags = read::<u8>(&data, &mut offset);
58
59    let types_size = read_var_int(&data, &mut offset);
60    if types_size > 0 {
61        let type_info_size = read_var_int(&data, &mut offset);
62        let typed_upval_size = read_var_int(&data, &mut offset);
63        let typed_local_size = read_var_int(&data, &mut offset);
64
65        fn_.type_info = bytecode[offset..offset + type_info_size as usize].to_string();
66        offset += type_info_size as usize;
67
68        fn_.upvalue_types
69            .resize(typed_upval_size as usize, LBC_TYPE_NIL);
70        for i in 0..typed_upval_size as usize {
71            let ty = read::<u8>(&data, &mut offset);
72            fn_.upvalue_types[i] = LuauBytecodeType(ty as u16);
73        }
74
75        fn_.local_types
76            .resize(typed_local_size as usize, TypedLocal::default());
77        for i in 0..typed_local_size as usize {
78            let ty = read::<u8>(&data, &mut offset);
79            let reg = read::<u8>(&data, &mut offset);
80            let startpc = read_var_int(&data, &mut offset);
81            let endpc = startpc + read_var_int(&data, &mut offset);
82            fn_.local_types[i] = TypedLocal {
83                r#type: LuauBytecodeType(ty as u16),
84                reg,
85                startpc,
86                endpc,
87            };
88        }
89    }
90
91    let codesize = read_var_int(&data, &mut offset) as usize;
92    let mut code: Vec<Instruction> = Vec::with_capacity(codesize);
93    for _ in 0..codesize {
94        code.push(read::<Instruction>(&data, &mut offset));
95    }
96
97    let sizek = read_var_int(&data, &mut offset) as usize;
98    fn_.constants.resize(sizek, BcVmConst::new());
99    for i in 0..sizek {
100        let const_type = read::<u8>(&data, &mut offset);
101        match const_type {
102            LBC_CONSTANT_NIL => {
103                fn_.constants[i].kind = BcVmConstKind::Nil;
104            }
105            LBC_CONSTANT_BOOLEAN => {
106                fn_.constants[i].kind = BcVmConstKind::Boolean;
107                unsafe {
108                    fn_.constants[i].value.valueBoolean = read::<u8>(&data, &mut offset) != 0;
109                }
110            }
111            LBC_CONSTANT_NUMBER => {
112                fn_.constants[i].kind = BcVmConstKind::Number;
113                unsafe {
114                    fn_.constants[i].value.valueNumber = read::<f64>(&data, &mut offset);
115                }
116            }
117            LBC_CONSTANT_VECTOR => {
118                fn_.constants[i].kind = BcVmConstKind::Vector;
119                unsafe {
120                    fn_.constants[i].value.valueVector = [
121                        read::<f32>(&data, &mut offset),
122                        read::<f32>(&data, &mut offset),
123                        read::<f32>(&data, &mut offset),
124                        read::<f32>(&data, &mut offset),
125                    ];
126                }
127            }
128            LBC_CONSTANT_STRING => {
129                fn_.constants[i].kind = BcVmConstKind::String;
130                let s = read_string(strings, &data, &mut offset);
131                unsafe {
132                    let value = core::str::from_utf8_unchecked(s);
133                    fn_.constants[i].value.valueString =
134                        core::mem::transmute::<&str, &'static str>(value);
135                }
136            }
137            LBC_CONSTANT_IMPORT => {
138                fn_.constants[i].kind = BcVmConstKind::Import;
139                unsafe {
140                    fn_.constants[i].value.valueImport = read::<u32>(&data, &mut offset);
141                }
142            }
143            LBC_CONSTANT_TABLE | LBC_CONSTANT_TABLE_WITH_CONSTANTS => {
144                fn_.constants[i].kind = BcVmConstKind::Table;
145                fn_.constants[i].value.valueTable = fn_.table_shapes.len() as u32;
146
147                let mut shape = TableShape::default();
148                shape.length = read_var_int(&data, &mut offset);
149                shape.hasConstants = const_type == LBC_CONSTANT_TABLE_WITH_CONSTANTS;
150
151                for j in 0..shape.length as usize {
152                    let key = read_var_int(&data, &mut offset) as i32;
153                    shape.keys[j] = key;
154                    if shape.hasConstants {
155                        let value = read::<i32>(&data, &mut offset);
156                        shape.constants[j] = value;
157                    }
158                }
159                fn_.table_shapes.push(shape);
160            }
161            LBC_CONSTANT_CLOSURE => {
162                fn_.constants[i].kind = BcVmConstKind::Closure;
163                fn_.constants[i].value.valueClosure = read_var_int(&data, &mut offset);
164            }
165            LBC_CONSTANT_INTEGER => {
166                fn_.constants[i].kind = BcVmConstKind::Integer;
167                let is_negative = read::<u8>(&data, &mut offset) != 0;
168                let magnitude = read_var_int_64(&data, &mut offset);
169                fn_.constants[i].value.valueInteger = if is_negative {
170                    !(magnitude - 1) as i64
171                } else {
172                    magnitude as i64
173                };
174            }
175            _ => {
176                LUAU_ASSERT!(false, "Unknown constant type!");
177                return None;
178            }
179        }
180    }
181
182    let psize = read_var_int(&data, &mut offset) as usize;
183    fn_.protos.resize(psize, 0);
184    for i in 0..psize {
185        fn_.protos[i] = read_var_int(&data, &mut offset);
186    }
187
188    fn_.linedefined = read_var_int(&data, &mut offset);
189    fn_.debugname =
190        unsafe { core::str::from_utf8_unchecked(read_string(strings, &data, &mut offset)) }
191            .to_string();
192
193    let lineinfo = read::<u8>(&data, &mut offset);
194    let mut lines: Vec<u32> = Vec::new();
195
196    if lineinfo != 0 {
197        let linegaplog2 = read::<u8>(&data, &mut offset) as usize;
198
199        let intervals = ((codesize - 1) >> linegaplog2) + 1;
200        let absoffset = (codesize + 3) & !3;
201
202        let mut lineinfo_bytes = vec![0u8; absoffset];
203        let mut abslineinfo = vec![0i32; intervals];
204
205        let mut lastoffset = 0u8;
206        for i in 0..codesize {
207            lastoffset = lastoffset.wrapping_add(read::<u8>(&data, &mut offset));
208            lineinfo_bytes[i] = lastoffset;
209        }
210
211        let mut lastline = 0i32;
212        for i in 0..intervals {
213            lastline += read::<i32>(&data, &mut offset);
214            abslineinfo[i] = lastline;
215        }
216
217        lines.resize(codesize, 0);
218        for i in 0..codesize {
219            let idx = i >> linegaplog2;
220            let abs = abslineinfo[idx];
221            let off = lineinfo_bytes[i] as i32;
222            lines[i] = (abs + off) as u32;
223        }
224    }
225
226    let debuginfo = read::<u8>(&data, &mut offset);
227
228    if debuginfo != 0 {
229        let sizelocvars = read_var_int(&data, &mut offset) as usize;
230        fn_.locals.resize(sizelocvars, DebugLocal::default());
231
232        for i in 0..sizelocvars {
233            let varname = read_string(strings, &data, &mut offset);
234            let startpc = read_var_int(&data, &mut offset);
235            let endpc = read_var_int(&data, &mut offset);
236            let reg = read::<u8>(&data, &mut offset);
237            fn_.locals[i] = DebugLocal {
238                varname: unsafe {
239                    core::str::from_utf8_unchecked(core::slice::from_raw_parts(
240                        varname.as_ptr() as *const u8,
241                        varname.len(),
242                    ))
243                },
244                reg,
245                startpc,
246                endpc,
247            };
248        }
249
250        let sizeupvalues = read_var_int(&data, &mut offset) as usize;
251        fn_.upvalue_names.resize(sizeupvalues, String::new());
252
253        for i in 0..sizeupvalues {
254            let name = read_string(strings, &data, &mut offset);
255            fn_.upvalue_names[i] = unsafe { core::str::from_utf8_unchecked(name) }.to_string();
256        }
257    }
258
259    let mut insns_pc: Vec<u32> = Vec::new();
260    let mut graph_parser = BytecodeGraphParser::new(&mut fn_);
261    if !graph_parser.rebuild_graph(code.as_ptr(), codesize as u32, &mut lines, &mut insns_pc) {
262        return None;
263    }
264
265    for l in &mut fn_.local_types {
266        l.startpc = if l.startpc < insns_pc.len() as u32 {
267            insns_pc[l.startpc as usize]
268        } else {
269            codesize as u32
270        };
271        l.endpc = if l.endpc < insns_pc.len() as u32 {
272            insns_pc[l.endpc as usize]
273        } else {
274            codesize as u32
275        };
276    }
277
278    for l in &mut fn_.locals {
279        l.startpc = if l.startpc < insns_pc.len() as u32 {
280            insns_pc[l.startpc as usize]
281        } else {
282            codesize as u32
283        };
284        l.endpc = if l.endpc < insns_pc.len() as u32 {
285            insns_pc[l.endpc as usize]
286        } else {
287            codesize as u32
288        };
289    }
290
291    Some(fn_)
292}