Skip to main content

luaur_bytecode/methods/
bytecode_builder_finalize.rs

1//! Node: `cxx:Method:Luau.Bytecode:Bytecode/src/BytecodeBuilder.cpp:676:finalize`
2//!
3//! Faithful port of `BytecodeBuilder::finalize`: assemble the final bytecode
4//! blob — version byte, type-encoding version, string table, userdata type-name
5//! mapping, then every function's pre-serialized `data` blob, then the main
6//! function index. `bytecode` is taken out of `self` while writing so the
7//! `&self` helpers (`write_string_table`) and `&mut self` field reads don't
8//! alias the buffer being filled.
9
10use crate::functions::write_byte::write_byte;
11use crate::functions::write_var_int::write_var_int;
12use crate::records::bytecode_builder::BytecodeBuilder;
13use crate::records::string_ref::StringRef;
14use luaur_common::enums::luau_bytecode_tag::{
15    LBC_TYPE_VERSION_MAX, LBC_TYPE_VERSION_MIN, LBC_VERSION_MAX, LBC_VERSION_MIN,
16};
17use luaur_common::macros::luau_assert::LUAU_ASSERT;
18
19impl BytecodeBuilder {
20    pub fn finalize(&mut self) {
21        LUAU_ASSERT!(self.bytecode.is_empty());
22
23        for i in 0..self.userdata_types.len() {
24            if self.userdata_types[i].used {
25                let sref = {
26                    let name = &self.userdata_types[i].name;
27                    StringRef {
28                        data: name.as_ptr() as *const i8,
29                        length: name.len(),
30                    }
31                };
32                self.userdata_types[i].nameRef = self.add_string_table_entry(sref);
33            }
34        }
35
36        // preallocate space for bytecode blob
37        let mut capacity: usize = 16;
38
39        for (string_ref, _index) in self.string_table.iter() {
40            capacity += string_ref.length + 2;
41        }
42
43        for func in &self.functions {
44            capacity += func.data.len();
45        }
46
47        // assemble final bytecode blob — taken out of `self` so the buffer is
48        // not aliased by the `&self`/field reads below.
49        let mut bytecode = core::mem::take(&mut self.bytecode);
50        bytecode.reserve(capacity);
51
52        let version = self.get_version();
53        LUAU_ASSERT!(version >= LBC_VERSION_MIN.0 as u8 && version <= LBC_VERSION_MAX.0 as u8);
54
55        unsafe {
56            bytecode.as_mut_vec().push(version);
57        }
58
59        let typesversion = self.get_type_encoding_version();
60        LUAU_ASSERT!(
61            typesversion >= LBC_TYPE_VERSION_MIN.0 as u8
62                && typesversion <= LBC_TYPE_VERSION_MAX.0 as u8
63        );
64        write_byte(&mut bytecode, typesversion);
65
66        self.write_string_table(&mut bytecode);
67
68        // Write the mapping between used type name indices and their name
69        for i in 0..self.userdata_types.len() {
70            if self.userdata_types[i].used {
71                write_byte(&mut bytecode, (i + 1) as u8);
72                write_var_int(&mut bytecode, self.userdata_types[i].nameRef as u64);
73            }
74        }
75
76        // 0 marks the end of the mapping
77        write_byte(&mut bytecode, 0);
78
79        write_var_int(&mut bytecode, self.functions.len() as u64);
80
81        for func in &self.functions {
82            unsafe {
83                bytecode
84                    .as_mut_vec()
85                    .extend_from_slice(func.data.as_bytes());
86            }
87        }
88
89        LUAU_ASSERT!((self.main_function as usize) < self.functions.len());
90        write_var_int(&mut bytecode, self.main_function as u64);
91
92        self.bytecode = bytecode;
93    }
94}