luac_parser/
luau.rs

1use nom::{multi::count, number::complete::le_u8};
2
3use super::*;
4
5pub fn varint(mut input: &[u8]) -> IResult<&[u8], usize> {
6    let mut x = 0usize;
7    let mut shift = 0usize;
8    loop {
9        let (rest, b) = le_u8(input)?;
10        input = rest;
11
12        x |= ((b & 0x7f) as usize) << shift;
13        shift += 7;
14
15        if b & 0x80 == 0 {
16            break;
17        }
18    }
19
20    Ok((input, x))
21}
22
23pub fn string<'a>(input: &'a [u8], stable: &[Rc<ByteBuf>]) -> IResult<&'a [u8], Rc<ByteBuf>> {
24    map(varint, |i| {
25        if i > 0 {
26            stable[i - 1].clone()
27        } else {
28            Rc::new(ByteBuf::new())
29        }
30    })(input)
31}
32
33pub const LBC_CONSTANT_NIL: u8 = 0;
34pub const LBC_CONSTANT_BOOLEAN: u8 = 1;
35pub const LBC_CONSTANT_NUMBER: u8 = 2;
36pub const LBC_CONSTANT_STRING: u8 = 3;
37pub const LBC_CONSTANT_IMPORT: u8 = 4;
38pub const LBC_CONSTANT_TABLE: u8 = 5;
39pub const LBC_CONSTANT_CLOSURE: u8 = 6;
40
41pub fn table<'a>(mut input: &'a [u8], k: &[LuaConstant]) -> IResult<&'a [u8], ConstTable> {
42    let numk;
43    (input, numk) = varint(input)?;
44    let mut result = ConstTable {
45        hash: Vec::with_capacity(numk),
46        ..Default::default()
47    };
48    for _ in 0..numk {
49        let ik;
50        (input, ik) = varint(input)?;
51        result
52            .hash
53            .push((k[ik].clone(), LuaConstant::Number(LuaNumber::Integer(0))));
54    }
55    Ok((input, result))
56}
57
58pub fn constants<'a>(
59    mut input: &'a [u8],
60    stable: &[Rc<ByteBuf>],
61) -> IResult<&'a [u8], Vec<LuaConstant>> {
62    let num;
63    (input, num) = varint(input)?;
64    let mut result = Vec::with_capacity(num);
65    for _ in 0..num {
66        let ty;
67        let k;
68        (input, ty) = le_u8(input)?;
69        (input, k) = match ty {
70            LBC_CONSTANT_NIL => Ok((input, LuaConstant::Null)),
71            LBC_CONSTANT_BOOLEAN => map(le_u8, |b| LuaConstant::Bool(b != 0))(input),
72            LBC_CONSTANT_NUMBER => map(complete::f64(Endianness::Little), |n| {
73                LuaConstant::Number(LuaNumber::Float(n))
74            })(input),
75            LBC_CONSTANT_STRING => map(|i| string(i, stable), LuaConstant::String)(input),
76            // LBC_CONSTANT_IMPORT => map(complete::be_u32, |i| LuaConstant::Imp(i as _))(input),
77            LBC_CONSTANT_IMPORT => map(complete::be_u32, |_| LuaConstant::Null)(input),
78            LBC_CONSTANT_TABLE => {
79                let (input, t) = table(input, &result)?;
80                Ok((input, LuaConstant::Table(t.into())))
81            }
82            LBC_CONSTANT_CLOSURE => map(varint, LuaConstant::Proto)(input),
83            // _ => context("string", fail::<&u8, LuaConstant, _>).parse(input),
84            _ => unreachable!("const type: {ty}"),
85        }?;
86        result.push(k);
87    }
88    Ok((input, result))
89}
90
91pub fn bytecode(input: &[u8]) -> IResult<&[u8], LuaChunk> {
92    let (mut input, _version) = le_u8(input)?;
93    let mut types_version = 0;
94
95    if _version >= 4 {
96        let (input2, _types_version) = le_u8(input)?;
97        types_version = _types_version;
98        input = input2
99    }
100
101    // string table
102    let (input, stable) = length_count(
103        varint,
104        map(
105            |input| {
106                let (input, n) = varint(input)?;
107                context("string", take(n))(input)
108            },
109            |s| Rc::new(ByteBuf::from(s.to_vec())),
110        ),
111    )(input)?;
112
113    // proto table
114    let (mut input, num) = varint(input)?;
115    let mut protos = Vec::with_capacity(num);
116
117    let string = |i| string(i, &stable);
118
119    for _ in 0..num {
120        let (mut input1, (max_stack, num_params, num_upvalues, is_vararg)) =
121            tuple((be_u8, be_u8, be_u8, be_u8))(input)?;
122
123        if _version >= 4 {
124            let (input2, _flags) = be_u8(input1)?;
125            let (mut input2, types_size) = varint(input2)?;
126
127            if types_size > 0 && types_version == 1 {
128                for _ in 0..types_size {
129                    let (input3, _byte) = le_u8(input2)?;
130                    input2 = input3
131                }
132            }
133
134            input1 = input2;
135        }
136
137        let (mut input1, (instructions, constants, prototypes, line_defined, name, has_lineinfo)) =
138            tuple((
139                length_count(varint, complete::u32(Endianness::Little)),
140                |i| constants(i, stable.as_slice()),
141                length_count(varint, map(varint, |i| core::mem::take(&mut protos[i]))),
142                map(varint, |n| n as u64),
143                string,
144                le_u8,
145            ))(input1)?;
146
147        if has_lineinfo > 0 {
148            let (input2, linegaplog2) = be_u8(input1)?;
149            let intervals = ((instructions.len() - 1) >> (linegaplog2 as usize)) + 1;
150            let (input2, _lineinfo) = count(be_u8, instructions.len())(input2)?;
151            let (input2, _abslineinfo) = count(complete::be_i32, intervals)(input2)?;
152            input1 = input2;
153        }
154
155        let (mut input1, has_debuginfo) = le_u8(input1)?;
156        let mut locals = vec![];
157        let mut upvalue_names = vec![];
158        if has_debuginfo > 0 {
159            (input1, (locals, upvalue_names)) = tuple((
160                length_count(
161                    varint,
162                    map(
163                        tuple((string, varint, varint, le_u8)),
164                        |(name, start, end, reg)| LuaLocal {
165                            name: String::from_utf8_lossy(name.as_slice()).into(),
166                            start_pc: start as _,
167                            end_pc: end as _,
168                            reg,
169                        },
170                    ),
171                ),
172                length_count(varint, map(string, |s| s.as_ref().clone().into_vec())),
173            ))(input1)?;
174        }
175
176        input = input1;
177        let proto = LuaChunk {
178            name: name.to_vec(),
179            line_defined,
180            last_line_defined: 0,
181            num_upvalues,
182            num_params,
183            max_stack,
184            prototypes,
185            is_vararg: if is_vararg > 0 {
186                Some(LuaVarArgInfo {
187                    has_arg: true,
188                    needs_arg: true,
189                })
190            } else {
191                None
192            },
193            instructions,
194            constants,
195            locals,
196            upvalue_names,
197            ..Default::default()
198        };
199        protos.push(proto);
200    }
201
202    let (input, mainid) = varint(input)?;
203    let main = core::mem::take(&mut protos[mainid]);
204    assert!(!main.is_empty());
205
206    Ok((input, main))
207}