Skip to main content

luaur_bytecode/methods/
bytecode_builder_write_function.rs

1use crate::enums::r#type::Type;
2use crate::functions::write_byte::write_byte;
3use crate::functions::write_double::write_double;
4use crate::functions::write_float::write_float;
5use crate::functions::write_int::write_int;
6use crate::functions::write_var_int::write_var_int;
7use crate::records::bytecode_builder::BytecodeBuilder;
8use crate::records::class_shape::ClassShape;
9use crate::records::constant::Constant;
10use crate::records::debug_local_bytecode_builder::DebugLocal;
11use crate::records::debug_upval::DebugUpval;
12use crate::records::function::Function;
13use crate::records::table_shape::TableShape;
14use crate::records::typed_local_bytecode_builder::TypedLocal;
15use crate::records::typed_upval::TypedUpval;
16use alloc::string::String;
17use alloc::vec::Vec;
18use core::cmp;
19use luaur_common::enums::luau_bytecode_tag::LuauBytecodeTag;
20use luaur_common::enums::luau_feedback_type::LuauFeedbackType;
21use luaur_common::macros::luau_assert::LUAU_ASSERT;
22use luaur_common::FFlag;
23
24impl BytecodeBuilder {
25    pub fn write_function(&mut self, ss: &mut String, id: u32, flags: u8) {
26        LUAU_ASSERT!(id < self.functions.len() as u32);
27        let func = &self.functions[id as usize];
28
29        // header
30        write_byte(ss, func.maxstacksize);
31        write_byte(ss, func.numparams);
32        write_byte(ss, func.numupvalues);
33        write_byte(ss, if func.isvararg { 1 } else { 0 });
34
35        write_byte(ss, flags);
36
37        if !func.typeinfo.is_empty()
38            || !self.typed_upvals.is_empty()
39            || !self.typed_locals.is_empty()
40        {
41            // collect type info into a temporary string to know the overall size of type data
42            self.temp_type_info.clear();
43            write_var_int(&mut self.temp_type_info, func.typeinfo.len() as u64);
44            write_var_int(&mut self.temp_type_info, self.typed_upvals.len() as u64);
45            write_var_int(&mut self.temp_type_info, self.typed_locals.len() as u64);
46
47            self.temp_type_info.push_str(&func.typeinfo);
48
49            for l in &self.typed_upvals {
50                write_byte(&mut self.temp_type_info, l.r#type.0 as u8);
51            }
52
53            for l in &self.typed_locals {
54                write_byte(&mut self.temp_type_info, l.r#type.0 as u8);
55                write_byte(&mut self.temp_type_info, l.reg);
56                write_var_int(&mut self.temp_type_info, l.startpc as u64);
57                LUAU_ASSERT!(l.endpc >= l.startpc);
58                write_var_int(&mut self.temp_type_info, (l.endpc - l.startpc) as u64);
59            }
60
61            write_var_int(ss, self.temp_type_info.len() as u64);
62            ss.push_str(&self.temp_type_info);
63        } else {
64            write_var_int(ss, 0);
65        }
66
67        // instructions
68        write_var_int(ss, self.insns.len() as u64);
69
70        for &insn in &self.insns {
71            write_int(ss, insn as i32);
72        }
73
74        // constants
75        write_var_int(ss, self.constants.len() as u64);
76
77        for c in &self.constants {
78            match c.r#type {
79                Type::Type_Nil => {
80                    write_byte(ss, LuauBytecodeTag::LBC_CONSTANT_NIL.0 as u8);
81                }
82                Type::Type_Boolean => {
83                    write_byte(ss, LuauBytecodeTag::LBC_CONSTANT_BOOLEAN.0 as u8);
84                    write_byte(ss, unsafe { c.value.valueBoolean } as u8);
85                }
86                Type::Type_Number => {
87                    write_byte(ss, LuauBytecodeTag::LBC_CONSTANT_NUMBER.0 as u8);
88                    write_double(ss, unsafe { c.value.valueNumber });
89                }
90                Type::Type_Integer => {
91                    write_byte(ss, LuauBytecodeTag::LBC_CONSTANT_INTEGER.0 as u8);
92                    let value = unsafe { c.value.valueInteger64 };
93                    if value < 0 {
94                        write_byte(ss, 1);
95                        write_var_int(ss, (!(value as u64)).wrapping_add(1));
96                    } else {
97                        write_byte(ss, 0);
98                        write_var_int(ss, value as u64);
99                    }
100                }
101                Type::Type_Vector => {
102                    write_byte(ss, LuauBytecodeTag::LBC_CONSTANT_VECTOR.0 as u8);
103                    let vec = unsafe { c.value.valueVector };
104                    write_float(ss, vec[0]);
105                    write_float(ss, vec[1]);
106                    write_float(ss, vec[2]);
107                    write_float(ss, vec[3]);
108                }
109                Type::Type_String => {
110                    write_byte(ss, LuauBytecodeTag::LBC_CONSTANT_STRING.0 as u8);
111                    write_var_int(ss, unsafe { c.value.valueString } as u64);
112                }
113                Type::Type_Import => {
114                    write_byte(ss, LuauBytecodeTag::LBC_CONSTANT_IMPORT.0 as u8);
115                    write_int(ss, unsafe { c.value.valueImport } as i32);
116                }
117                Type::Type_Table => {
118                    let shape = &self.table_shapes[unsafe { c.value.valueTable } as usize];
119                    if FFlag::LuauCompileDuptableConstantPack2.get() && shape.hasConstants {
120                        write_byte(
121                            ss,
122                            LuauBytecodeTag::LBC_CONSTANT_TABLE_WITH_CONSTANTS.0 as u8,
123                        );
124                        write_var_int(ss, shape.length as u64);
125                        for i in 0..shape.length as usize {
126                            write_var_int(ss, shape.keys[i] as u64);
127                            write_int(ss, shape.constants[i]);
128                        }
129                    } else {
130                        write_byte(ss, LuauBytecodeTag::LBC_CONSTANT_TABLE.0 as u8);
131                        write_var_int(ss, shape.length as u64);
132                        for i in 0..shape.length as usize {
133                            write_var_int(ss, shape.keys[i] as u64);
134                        }
135                    }
136                }
137                Type::Type_Closure => {
138                    write_byte(ss, LuauBytecodeTag::LBC_CONSTANT_CLOSURE.0 as u8);
139                    write_var_int(ss, unsafe { c.value.valueClosure } as u64);
140                }
141                Type::Type_ClassShape => {
142                    write_byte(ss, LuauBytecodeTag::LBC_CONSTANT_CLASS_SHAPE.0 as u8);
143                    let cs = &self.class_shapes[unsafe { c.value.valueClassShape } as usize];
144                    self.write_class_shape(ss, cs);
145                }
146            }
147        }
148
149        // child protos
150        write_var_int(ss, self.protos.len() as u64);
151
152        for &child in &self.protos {
153            write_var_int(ss, child as u64);
154        }
155
156        // debug info
157        write_var_int(ss, func.debuglinedefined as u64);
158        write_var_int(ss, func.debugname as u64);
159
160        let mut has_lines = true;
161
162        for &line in &self.lines {
163            if line == 0 {
164                has_lines = false;
165                break;
166            }
167        }
168
169        if has_lines {
170            write_byte(ss, 1);
171
172            self.write_line_info(ss);
173        } else {
174            write_byte(ss, 0);
175        }
176
177        let has_debug = !self.debug_locals.is_empty() || !self.debug_upvals.is_empty();
178
179        if has_debug {
180            write_byte(ss, 1);
181
182            write_var_int(ss, self.debug_locals.len() as u64);
183
184            for l in &self.debug_locals {
185                write_var_int(ss, l.name as u64);
186                write_var_int(ss, l.startpc as u64);
187                write_var_int(ss, l.endpc as u64);
188                write_byte(ss, l.reg);
189            }
190
191            write_var_int(ss, self.debug_upvals.len() as u64);
192
193            for l in &self.debug_upvals {
194                write_var_int(ss, l.name as u64);
195            }
196        } else {
197            write_byte(ss, 0);
198        }
199
200        if FFlag::LuauEmitCallFeedback.get() {
201            // Feedback Slots
202            write_var_int(ss, self.fb_slots.len() as u64);
203            for &pc in &self.fb_slots {
204                write_byte(ss, LuauFeedbackType::LFT_CALLTARGET as u8);
205                write_var_int(ss, pc as u64);
206            }
207        }
208    }
209}