lua_bytecode/
opcode.rs

1#[cfg(feature = "lua51")]
2const LUA_OP_SIZE: u32 = 6;
3#[cfg(feature = "lua51")]
4const LUA_A_SIZE: u32 = 8;
5#[cfg(feature = "lua51")]
6const LUA_B_SIZE: u32 = 9;
7#[cfg(feature = "lua51")]
8const LUA_C_SIZE: u32 = 9;
9#[cfg(feature = "lua51")]
10const LUA_BX_SIZE: u32 = LUA_B_SIZE + LUA_C_SIZE;
11
12#[cfg(feature = "lua51")]
13const LUA_OP_POSITION: u32 = 0;
14#[cfg(feature = "lua51")]
15const LUA_A_POSITION: u32 = LUA_OP_SIZE;
16#[cfg(feature = "lua51")]
17const LUA_C_POSITION: u32 = LUA_A_POSITION + LUA_A_SIZE;
18#[cfg(feature = "lua51")]
19const LUA_B_POSITION: u32 = LUA_C_POSITION + LUA_C_SIZE;
20#[cfg(feature = "lua51")]
21const LUA_BX_POSITION: u32 = LUA_C_POSITION;
22
23#[cfg(feature = "lua51")]
24pub const MAX_ARG_BX: u32 = (1 << LUA_BX_SIZE) - 1;
25#[cfg(feature = "lua51")]
26pub const MAX_ARG_SBX: i32 = (MAX_ARG_BX as i32) >> 1;
27
28#[derive(Debug, PartialEq, Eq)]
29pub enum LuaOpMode {
30    IA,
31    IAB,
32    IAC,
33    IABC,
34
35    IAx,
36    IABx,
37    IAsBx,
38}
39
40#[repr(u8)]
41#[derive(Copy, Clone, Debug, PartialEq, Eq)]
42#[cfg(feature = "lua51")]
43pub enum LuaOpcode {
44    Move,
45    LoadK,
46    LoadBool,
47    LoadNil,
48    GetUpval,
49
50    GetGlobal,
51    GetTable,
52
53    SetGlobal,
54    SetUpval,
55    SetTable,
56
57    NewTable,
58
59    Self_,
60
61    Add,
62    Sub,
63    Mul,
64    Div,
65    Mod,
66    Pow,
67    Unm,
68    Not,
69    Len,
70
71    Concat,
72
73    Jmp,
74
75    Eq,
76    Lt,
77    Le,
78
79    Test,
80    TestSet,
81
82    Call,
83    TailCall,
84    Return,
85
86    ForLoop,
87    ForPrep,
88
89    TForLoop,
90    SetList,
91
92    Close,
93    Closure,
94
95    Vararg,
96}
97
98#[repr(u8)]
99#[derive(Copy, Clone, Debug, PartialEq, Eq)]
100#[cfg(feature = "luau")]
101pub enum LuauOpcode {
102    Nop,
103    Break,
104    LoadNil,
105    LoadB,
106    LoadN,
107    LoadK,
108    Move,
109    GetGlobal,
110    SetGlobal,
111    GetUpval,
112    SetUpval,
113    CloseUpvals,
114    GetImport,
115    GetTable,
116    SetTable,
117    GetTableKs,
118    SetTableKs,
119    GetTableN,
120    SetTableN,
121    NewClosure,
122    NameCall,
123    Call,
124    Return,
125    Jump,
126    JumpBack,
127    JumpIf,
128    JumpIfNot,
129    JumpIfEq,
130    JumpIfLe,
131    JumpIfLt,
132    JumpIfNotEq,
133    JumpIfNotLe,
134    JumpIfNotLt,
135    Add,
136    Sub,
137    Mul,
138    Div,
139    Mod,
140    Pow,
141    AddK,
142    SubK,
143    MulK,
144    DivK,
145    ModK,
146    PowK,
147    And,
148    Or,
149    AndK,
150    OrK,
151    Concat,
152    Not,
153    Minus,
154    Length,
155    NewTable,
156    DupTable,
157    SetList,
158    ForNPrep,
159    ForNLoop,
160    ForGLoop,
161    ForGPrepInext,
162    FastCall3,
163    ForGPrepNext,
164    NativeCall,
165    GetVarargs,
166    DupClosure,
167    PrepVarargs,
168    LoadKx,
169    JumpX,
170    FastCall,
171    Coverage,
172    Capture,
173    SubRk,
174    DivRk,
175    FastCall1,
176    FastCall2,
177    FastCall2K,
178    ForGPrep,
179    JumpXeqkNil,
180    JumpXeqkB,
181    JumpXeqkN,
182    JumpXeqkS,
183    IDiv,
184    IDivK,
185}
186
187#[cfg(feature = "lua51")]
188impl LuaOpcode {
189    pub fn index(op: u8) -> LuaOpcode {
190        unsafe { std::mem::transmute(op) }
191    }
192
193    pub fn mode(&self) -> LuaOpMode {
194        match self {
195            LuaOpcode::Move => LuaOpMode::IABC,
196            LuaOpcode::LoadK => LuaOpMode::IABx,
197            LuaOpcode::LoadBool => LuaOpMode::IABC,
198            LuaOpcode::LoadNil => LuaOpMode::IAB,
199
200            LuaOpcode::GetUpval | LuaOpcode::SetUpval => LuaOpMode::IAB,
201            LuaOpcode::GetGlobal | LuaOpcode::SetGlobal => LuaOpMode::IABx,
202            LuaOpcode::GetTable | LuaOpcode::SetTable | LuaOpcode::NewTable => LuaOpMode::IABC,
203            LuaOpcode::Self_ => LuaOpMode::IABC,
204
205            LuaOpcode::Add
206            | LuaOpcode::Sub
207            | LuaOpcode::Mul
208            | LuaOpcode::Div
209            | LuaOpcode::Mod
210            | LuaOpcode::Pow => LuaOpMode::IABC,
211
212            LuaOpcode::Unm | LuaOpcode::Not | LuaOpcode::Len => LuaOpMode::IAB,
213
214            LuaOpcode::Concat => LuaOpMode::IABC,
215            LuaOpcode::Jmp => LuaOpMode::IAsBx, // sBx?
216
217            LuaOpcode::Eq | LuaOpcode::Lt | LuaOpcode::Le => LuaOpMode::IABC,
218
219            LuaOpcode::Test => LuaOpMode::IAC,
220            LuaOpcode::TestSet => LuaOpMode::IABC,
221
222            LuaOpcode::Call | LuaOpcode::TailCall => LuaOpMode::IABC,
223            LuaOpcode::Return => LuaOpMode::IAB,
224
225            LuaOpcode::ForLoop => LuaOpMode::IAsBx,
226            LuaOpcode::ForPrep => LuaOpMode::IAsBx,
227            LuaOpcode::TForLoop => LuaOpMode::IAC,
228            LuaOpcode::SetList => LuaOpMode::IABC,
229
230            LuaOpcode::Close => LuaOpMode::IA,
231            LuaOpcode::Closure => LuaOpMode::IABx,
232
233            LuaOpcode::Vararg => LuaOpMode::IAB,
234        }
235    }
236}
237
238#[cfg(feature = "luau")]
239impl LuauOpcode {
240    pub fn index(op: u8) -> LuauOpcode {
241        unsafe { std::mem::transmute(op) }
242    }
243
244    pub fn length(&self) -> u8 {
245        match self {
246            LuauOpcode::GetGlobal
247            | LuauOpcode::SetGlobal
248            | LuauOpcode::GetImport
249            | LuauOpcode::GetTableKs
250            | LuauOpcode::SetTableKs
251            | LuauOpcode::NameCall
252            | LuauOpcode::JumpIfEq
253            | LuauOpcode::JumpIfLt
254            | LuauOpcode::JumpIfLe
255            | LuauOpcode::JumpIfNotEq
256            | LuauOpcode::JumpIfNotLt
257            | LuauOpcode::JumpIfNotLe
258            | LuauOpcode::NewTable
259            | LuauOpcode::SetList
260            | LuauOpcode::ForGLoop
261            | LuauOpcode::FastCall3
262            | LuauOpcode::LoadKx
263            | LuauOpcode::FastCall2
264            | LuauOpcode::FastCall2K
265            | LuauOpcode::JumpXeqkB
266            | LuauOpcode::JumpXeqkN
267            | LuauOpcode::JumpXeqkS => 2,
268            _ => 1,
269        }
270    }
271}
272
273#[derive(Clone, Copy, Debug, PartialEq, Eq)]
274pub enum Opcode {
275    #[cfg(feature = "lua51")]
276    LuaOpcode(LuaOpcode),
277    #[cfg(feature = "luau")]
278    LuauOpcode(LuauOpcode),
279}
280
281pub struct Instruction(pub u32);
282
283#[cfg(feature = "lua51")]
284pub trait LuaInstruction {
285    fn opcode(&self) -> Opcode;
286    fn from_abc(op: Opcode, a: u32, b: u32, c: u32) -> Self;
287    fn from_abx(opcode: Opcode, a: u32, bx: u32) -> Self;
288
289    fn get(&self, size: u32, position: u32) -> u32;
290    fn a(&self) -> u32;
291    fn b(&self) -> u32;
292    fn c(&self) -> u32;
293    fn bx(&self) -> u32;
294    fn sbx(&self) -> i32;
295
296    fn set(&mut self, value: u32, position: u32, size: u32) -> &mut Self;
297    fn set_a(&mut self, a: u32) -> &mut Self;
298    fn set_b(&mut self, b: u32) -> &mut Self;
299    fn set_c(&mut self, c: u32) -> &mut Self;
300    fn set_bx(&mut self, bx: u32) -> &mut Self;
301    fn set_sbx(&mut self, sbx: i32) -> &mut Self;
302
303    /* creates a mask with `n' 0 bits at position `p' */
304    fn mask_0(&self, n: u32, p: u32) -> u32;
305    /* creates a mask with `n' 1 bits at position `p' */
306    fn mask_1(&self, n: u32, p: u32) -> u32;
307}
308
309#[cfg(feature = "luau")]
310pub trait LuauInstruction {
311    fn opcode(&self) -> Opcode;
312
313    fn from_abc(op: Opcode, a: u32, b: u32, c: u32) -> Self;
314    fn from_ad(opcode: Opcode, a: u32, d: u32) -> Self;
315    fn from_e(opcode: Opcode, e: u32) -> Self;
316
317    fn a(&self) -> u32;
318    fn b(&self) -> u32;
319    fn c(&self) -> u32;
320    fn d(&self) -> u32;
321    fn e(&self) -> u32;
322}
323
324#[cfg(feature = "lua51")]
325impl LuaInstruction for Instruction {
326    fn opcode(&self) -> Opcode {
327        let op = ((self.0 >> LUA_OP_POSITION) as u8) & self.mask_1(LUA_OP_SIZE, 0) as u8;
328        Opcode::LuaOpcode(LuaOpcode::index(op))
329    }
330
331    fn from_abc(opcode: Opcode, a: u32, b: u32, c: u32) -> Self {
332        let mut instruction = match opcode {
333            Opcode::LuaOpcode(op) => Instruction(op as u32),
334            _ => unreachable!(),
335        };
336
337        Instruction(instruction.set_a(a).set_b(b).set_c(c).0)
338    }
339
340    fn from_abx(opcode: Opcode, a: u32, bx: u32) -> Self {
341        let mut instruction = match opcode {
342            Opcode::LuaOpcode(op) => Instruction(op as u32),
343            _ => unreachable!(),
344        };
345
346        Instruction(instruction.set_a(a).set_bx(bx).0)
347    }
348
349    fn a(&self) -> u32 {
350        self.get(LUA_A_SIZE, LUA_A_POSITION)
351    }
352
353    fn b(&self) -> u32 {
354        self.get(LUA_B_SIZE, LUA_B_POSITION)
355    }
356
357    fn c(&self) -> u32 {
358        self.get(LUA_C_SIZE, LUA_C_POSITION)
359    }
360
361    fn bx(&self) -> u32 {
362        self.get(LUA_BX_SIZE, LUA_BX_POSITION)
363    }
364
365    fn sbx(&self) -> i32 {
366        (self.bx() as i32) - MAX_ARG_SBX
367    }
368
369    fn set_a(&mut self, a: u32) -> &mut Self {
370        self.set(a, LUA_A_SIZE, LUA_A_POSITION)
371    }
372
373    fn set_b(&mut self, b: u32) -> &mut Self {
374        self.set(b, LUA_B_SIZE, LUA_B_POSITION)
375    }
376
377    fn set_c(&mut self, c: u32) -> &mut Self {
378        self.set(c, LUA_C_SIZE, LUA_C_POSITION)
379    }
380
381    fn set_bx(&mut self, bx: u32) -> &mut Self {
382        self.set(bx, LUA_BX_SIZE, LUA_BX_POSITION)
383    }
384
385    fn set_sbx(&mut self, bx: i32) -> &mut Self {
386        self.set_bx((bx + MAX_ARG_SBX) as u32)
387    }
388
389    fn get(&self, size: u32, position: u32) -> u32 {
390        (self.0 >> position) & self.mask_1(size, 0)
391    }
392
393    fn set(&mut self, value: u32, size: u32, position: u32) -> &mut Self {
394        self.0 = (self.mask_1(size, position) & (value << position))
395            | (self.0 & self.mask_0(size, position));
396
397        self
398    }
399
400    fn mask_1(&self, n: u32, p: u32) -> u32 {
401        ((1u32 << n) - 1) << p
402    }
403
404    fn mask_0(&self, n: u32, p: u32) -> u32 {
405        !self.mask_1(n, p)
406    }
407}
408
409#[cfg(feature = "luau")]
410impl LuauInstruction for Instruction {
411    fn opcode(&self) -> Opcode {
412        let op = (self.0 & 0xff) as u8;
413        Opcode::LuauOpcode(LuauOpcode::index(op))
414    }
415
416    fn from_abc(opcode: Opcode, a: u32, b: u32, c: u32) -> Self {
417        let op = match opcode {
418            Opcode::LuauOpcode(op) => op as u32,
419            _ => unreachable!(),
420        };
421
422        Instruction((op) | (a << 8) | (b << 16) | (c << 24))
423    }
424
425    fn from_ad(opcode: Opcode, a: u32, d: u32) -> Self {
426        let op = match opcode {
427            Opcode::LuauOpcode(op) => op as u32,
428            _ => unreachable!(),
429        };
430
431        Instruction((op) | (a << 8) | (d << 16))
432    }
433
434    fn from_e(opcode: Opcode, e: u32) -> Self {
435        let op = match opcode {
436            Opcode::LuauOpcode(op) => op as u32,
437            _ => unreachable!(),
438        };
439
440        Instruction(op | e << 8)
441    }
442
443    fn a(&self) -> u32 {
444        (self.0 >> 8) & 0xff
445    }
446
447    fn b(&self) -> u32 {
448        (self.0 >> 16) & 0xff
449    }
450
451    fn c(&self) -> u32 {
452        (self.0 >> 24) & 0xff
453    }
454
455    fn d(&self) -> u32 {
456        LuauInstruction::b(self) | LuauInstruction::c(self)
457    }
458
459    fn e(&self) -> u32 {
460        LuauInstruction::a(self) | LuauInstruction::d(self)
461    }
462}
463
464impl Instruction {
465    pub fn from_bytes(bytes: &[u8]) -> Self {
466        Instruction(u32::from_le_bytes(bytes.try_into().unwrap()))
467    }
468}