luac_parser/
luajit.rs

1#![allow(dead_code)]
2
3use std::cell::RefCell;
4
5use nom::{multi::count, number::complete::le_u8};
6use nom_leb128::{leb128_u32, leb128_u64, leb128_usize};
7
8use super::*;
9
10/* Header flags of bytecode */
11pub const FLAG_IS_BIG_ENDIAN: u8 = 0b00000001;
12pub const FLAG_IS_STRIPPED: u8 = 0b00000010;
13pub const FLAG_HAS_FFI: u8 = 0b00000100;
14// for luajit2.x
15pub const FLAG_F_FR2: u8 = 0x08;
16
17/* Flags for prototype. */
18pub const PROTO_CHILD: u8 = 0x01; /* Has child prototypes. */
19pub const PROTO_VARARG: u8 = 0x02; /* Vararg function. */
20pub const PROTO_FFI: u8 = 0x04; /* Uses BC_KCDATA for FFI datatypes. */
21pub const PROTO_NOJIT: u8 = 0x08; /* JIT disabled for this function. */
22pub const PROTO_ILOOP: u8 = 0x10; /* Patched bytecode with ILOOP etc. */
23/* Only used during parsing. */
24pub const PROTO_HAS_RETURN: u8 = 0x20; /* Already emitted a return. */
25pub const PROTO_FIXUP_RETURN: u8 = 0x40; /* Need to fixup emitted returns. */
26
27pub const BCDUMP_KGC_CHILD: u64 = 0;
28pub const BCDUMP_KGC_TAB: u64 = 1;
29pub const BCDUMP_KGC_I64: u64 = 2;
30pub const BCDUMP_KGC_U64: u64 = 3;
31pub const BCDUMP_KGC_COMPLEX: u64 = 4;
32pub const BCDUMP_KGC_STR: u64 = 5;
33
34pub const BCDUMP_KTAB_NIL: usize = 0;
35pub const BCDUMP_KTAB_FALSE: usize = 1;
36pub const BCDUMP_KTAB_TRUE: usize = 2;
37pub const BCDUMP_KTAB_INT: usize = 3;
38pub const BCDUMP_KTAB_NUM: usize = 4;
39pub const BCDUMP_KTAB_STR: usize = 5;
40
41pub fn uleb128_33(mut input: &[u8]) -> IResult<&[u8], u32, ErrorTree<&[u8]>> {
42    let v;
43    (input, v) = le_u8(input)?;
44    let mut v = v as u32 >> 1; // uint32_t v = (*p++ >> 1);
45    if v >= 0x40 {
46        let mut sh = -1i32;
47        v &= 0x3f;
48        loop {
49            sh += 7;
50            let mut p = le_u8(input)?.1;
51            v |= (p as u32 & 0x7f) << (sh as u32);
52            (input, p) = le_u8(input)?;
53            if p < 0x80 {
54                break;
55            } // while (*p++ >= 0x80);
56        }
57    }
58    Ok((input, v))
59}
60
61pub fn lj_header(input: &[u8]) -> IResult<&[u8], LuaHeader, ErrorTree<&[u8]>> {
62    let (rest, (_, result)) = tuple((
63        tag(b"\x1bLJ"),
64        alt((
65            map(tuple((tag(b"\x01"), be_u8)), |(_, lj_flags)| LuaHeader {
66                lua_version: LUAJ1.0,
67                format_version: 0,
68                big_endian: lj_flags & FLAG_IS_BIG_ENDIAN != 0,
69                int_size: 4,
70                size_t_size: 4,
71                instruction_size: 4,
72                number_size: 4,
73                number_integral: false,
74                lj_flags,
75            })
76            .context("luajit1"),
77            map(tuple((tag(b"\x02"), be_u8)), |(_, lj_flags)| LuaHeader {
78                lua_version: LUAJ2.0,
79                format_version: 0,
80                big_endian: lj_flags & FLAG_IS_BIG_ENDIAN != 0,
81                int_size: 4,
82                size_t_size: 4,
83                instruction_size: 4,
84                number_size: 4,
85                number_integral: false,
86                lj_flags,
87            })
88            .context("luajit2"),
89        )),
90    ))(input)?;
91    Ok((rest, result))
92}
93
94pub fn lj_complex_constant<'a, 'h>(
95    stack: &'h RefCell<Vec<LuaChunk>>,
96    protos: &'h RefCell<Vec<LuaChunk>>,
97    endian: Endianness,
98) -> impl Parser<&'a [u8], LuaConstant, ErrorTree<&'a [u8]>> + 'h {
99    move |input| {
100        let (input, ty) = leb128_u64(input)?;
101        Ok(match ty {
102            BCDUMP_KGC_I64 => map(
103                tuple((nom_leb128::leb128_u32, nom_leb128::leb128_u32)),
104                |(lo, hi)| LuaConstant::Number(LuaNumber::Integer(lo as i64 | ((hi as i64) << 32))),
105            )(input)?,
106            BCDUMP_KGC_U64 => map(
107                tuple((nom_leb128::leb128_u32, nom_leb128::leb128_u32)),
108                |(lo, hi)| {
109                    LuaConstant::Number(LuaNumber::Integer(
110                        (lo as u64 | ((hi as u64) << 32)) as i64,
111                    ))
112                },
113            )(input)?,
114            BCDUMP_KGC_TAB => lj_tab(endian).context("read table").parse(input)?,
115            BCDUMP_KGC_CHILD => match stack.borrow_mut().pop() {
116                Some(proto) => {
117                    let result = LuaConstant::Proto(protos.borrow().len());
118                    protos.borrow_mut().push(proto);
119                    (input, result)
120                }
121                None => context("pop proto", fail).parse(input)?,
122            },
123            _ if ty >= BCDUMP_KGC_STR => {
124                let len = ty - BCDUMP_KGC_STR;
125                let (input, s) = take(len as usize)(input)?;
126                (input, LuaConstant::from(s.to_vec()))
127            }
128            _ => unreachable!("BCDUMP_KGC: {ty}"),
129        })
130    }
131}
132
133pub fn lj_tab<'a>(endian: Endianness) -> impl Parser<&'a [u8], LuaConstant, ErrorTree<&'a [u8]>> {
134    move |input: &'a [u8]| {
135        let (input, (narray, nhash)) = tuple((leb128_u32, leb128_u32))(input)?;
136        let (input, (array, hash)) = tuple((
137            count(lj_tabk(endian).context("count table array"), narray as _),
138            count(
139                tuple((lj_tabk(endian), lj_tabk(endian))).context("count table hash"),
140                nhash as _,
141            ),
142        ))(input)?;
143        Ok((input, LuaConstant::Table(ConstTable { array, hash }.into())))
144    }
145}
146
147fn combine_number(lo: u32, hi: u32, endian: Endianness) -> f64 {
148    f64::from_bits(if endian == Endianness::Big {
149        ((lo as u64) << 32) | hi as u64
150    } else {
151        ((hi as u64) << 32) | lo as u64
152    })
153}
154
155pub fn lj_tabk<'a>(endian: Endianness) -> impl Parser<&'a [u8], LuaConstant, ErrorTree<&'a [u8]>> {
156    move |input: &'a [u8]| {
157        let (input, ty) = leb128_usize(input)?;
158        // println!("tabk: {ty}");
159        Ok(match ty {
160            BCDUMP_KTAB_NIL => (input, LuaConstant::Null),
161            BCDUMP_KTAB_FALSE => (input, LuaConstant::Bool(false)),
162            BCDUMP_KTAB_TRUE => (input, LuaConstant::Bool(true)),
163            BCDUMP_KTAB_INT => map(leb128_u32, |n| {
164                LuaConstant::Number(LuaNumber::Integer(n as _))
165            })(input)?,
166            BCDUMP_KTAB_NUM => map(tuple((leb128_u32, leb128_u32)), |(lo, hi)| {
167                LuaConstant::Number(LuaNumber::Float(combine_number(lo, hi, endian)))
168            })(input)?,
169            _ if ty >= BCDUMP_KTAB_STR as usize => {
170                let len = ty - BCDUMP_KTAB_STR as usize;
171                let (input, s) = take(len)(input)?;
172                (input, LuaConstant::from(s.to_vec()))
173            }
174            _ => unreachable!("BCDUMP_KTAB: {ty}"),
175        })
176    }
177}
178
179fn lj_num_constant<'a>(
180    endian: Endianness,
181) -> impl Parser<&'a [u8], LuaNumber, ErrorTree<&'a [u8]>> {
182    move |input: &'a [u8]| {
183        let isnum = be_u8(input)?.1 & 1 != 0;
184        let (input, lo) = uleb128_33(input)?;
185        if isnum {
186            map(leb128_u32, |hi| {
187                LuaNumber::Float(combine_number(lo, hi, endian))
188            })(input)
189        } else {
190            Ok((input, LuaNumber::Integer(lo as i32 as _)))
191        }
192    }
193}
194
195fn lj_proto<'a, 'h>(
196    header: &'h LuaHeader,
197    stack: &'h RefCell<Vec<LuaChunk>>,
198) -> impl Parser<&'a [u8], Option<LuaChunk>, ErrorTree<&'a [u8]>> + 'h {
199    move |input| {
200        // proto header
201        let (input, size) = leb128_u32.parse(input)?;
202        if size == 0 {
203            return Ok((input, None));
204        }
205        let (
206            mut input,
207            (
208                flags,
209                num_params,
210                framesize,
211                num_upvalues,
212                complex_constants_count,
213                numeric_constants_count,
214                instructions_count,
215            ),
216        ) = tuple((
217            be_u8, be_u8, be_u8, be_u8, leb128_u32, leb128_u32, leb128_u32,
218        ))(input)?;
219
220        let mut line_defined = 0;
221        let mut numline = 0;
222        let mut debuginfo_size = 0;
223        if !header.test_luajit_flag(FLAG_IS_STRIPPED) {
224            (input, (debuginfo_size, line_defined, numline)) =
225                tuple((leb128_u64, leb128_u64, leb128_u64))(input)?;
226        }
227        let last_line_defined = line_defined + numline;
228
229        let instructions;
230        let upvalue_infos;
231        let mut constants;
232        let num_constants;
233        let protos = RefCell::new(vec![]);
234        (
235            input,
236            (instructions, upvalue_infos, constants, num_constants),
237        ) = tuple((
238            count(complete::u32(header.endian()), instructions_count as usize)
239                .context("count instruction"),
240            count(
241                map(complete::u16(header.endian()), |v| UpVal {
242                    on_stack: v & 0x8000 != 0,
243                    id: (v & 0x7FFF) as _,
244                    kind: 0,
245                }),
246                num_upvalues as usize,
247            )
248            .context("count upvals"),
249            count(
250                lj_complex_constant(stack, &protos, header.endian()),
251                complex_constants_count as usize,
252            )
253            .context("count complex_constant"),
254            count(
255                lj_num_constant(header.endian()),
256                numeric_constants_count as usize,
257            )
258            .context("count numeric_constants"),
259        ))(input)?;
260        constants.reverse();
261
262        if debuginfo_size > 0 {
263            (input, _) = take(debuginfo_size as usize)(input)?;
264        }
265
266        Ok((
267            input,
268            Some(LuaChunk {
269                name: vec![],
270                num_upvalues,
271                num_params,
272                line_defined,
273                last_line_defined,
274                flags,
275                instructions,
276                upvalue_infos,
277                constants,
278                num_constants,
279                max_stack: framesize,
280                is_vararg: if flags & PROTO_VARARG != 0 {
281                    Some(LuaVarArgInfo::new())
282                } else {
283                    None
284                },
285                prototypes: protos.into_inner(),
286                ..Default::default()
287            }),
288        ))
289    }
290}
291
292pub fn lj_chunk<'h, 'a: 'h>(
293    header: &'h LuaHeader,
294) -> impl Parser<&'a [u8], LuaChunk, ErrorTree<&'a [u8]>> + 'h {
295    move |mut input| {
296        let mut name = &b""[..];
297        if !header.test_luajit_flag(FLAG_IS_STRIPPED) {
298            let namelen;
299            (input, namelen) = leb128_u32.parse(input)?;
300            (input, name) = take(namelen as usize)(input)?;
301        }
302        let protos = RefCell::new(vec![]);
303        while let (i, Some(proto)) = lj_proto(&header, &protos).parse(input)? {
304            protos.borrow_mut().push(proto);
305            input = i;
306        }
307        let mut protos = protos.into_inner();
308        Ok((
309            input,
310            if let Some(mut chunk) = protos.pop().filter(|_| protos.is_empty()) {
311                chunk.name = name.to_vec();
312                chunk
313            } else {
314                context("stack unbalanced", fail).parse(input)?.1
315            },
316        ))
317    }
318}
319
320bitflags::bitflags! {
321    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
322    pub struct ProtoFlags: u8 {
323        const HAS_CHILD = 0b00000001;
324        const IS_VARIADIC = 0b00000010;
325        const HAS_FFI = 0b00000100;
326        const JIT_DISABLED = 0b00001000;
327        const HAS_ILOOP = 0b0001000;
328    }
329}