luaur_bytecode/functions/
from_function_bytecode.rs1extern 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}