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(Copy, Clone, Debug, PartialEq, Eq)]
29pub enum LuaOpMode {
30    IA,
31    IAB,
32    IAC,
33    IABC,
34
35    IAx,
36    IABx,
37    IAsBx,
38}
39
40#[cfg(feature = "lua51")]
41#[repr(u8)]
42#[derive(Copy, Clone, Debug, PartialEq, Eq)]
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#[cfg(feature = "luau")]
99#[repr(u8)]
100#[derive(Copy, Clone, Debug, PartialEq, Eq)]
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(Copy, Clone, Debug, PartialEq, Eq)]
274pub enum Opcode {
275    #[cfg(feature = "lua51")]
276    LuaOpcode(LuaOpcode),
277    #[cfg(feature = "luau")]
278    LuauOpcode(LuauOpcode),
279}
280
281#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
282pub struct Instruction(pub u32);
283
284#[cfg(feature = "lua51")]
285pub trait LuaInstruction {
286    fn opcode(&self) -> Opcode;
287    fn from_abc(op: Opcode, a: u32, b: u32, c: u32) -> Self;
288    fn from_abx(opcode: Opcode, a: u32, bx: u32) -> Self;
289
290    fn get(&self, size: u32, position: u32) -> u32;
291    fn a(&self) -> u32;
292    fn b(&self) -> u32;
293    fn c(&self) -> u32;
294    fn bx(&self) -> u32;
295    fn sbx(&self) -> i32;
296
297    fn set(&mut self, value: u32, position: u32, size: u32) -> &mut Self;
298    fn set_a(&mut self, a: u32) -> &mut Self;
299    fn set_b(&mut self, b: u32) -> &mut Self;
300    fn set_c(&mut self, c: u32) -> &mut Self;
301    fn set_bx(&mut self, bx: u32) -> &mut Self;
302    fn set_sbx(&mut self, sbx: i32) -> &mut Self;
303
304    /* creates a mask with `n' 0 bits at position `p' */
305    fn mask_0(&self, n: u32, p: u32) -> u32;
306    /* creates a mask with `n' 1 bits at position `p' */
307    fn mask_1(&self, n: u32, p: u32) -> u32;
308}
309
310#[cfg(feature = "luau")]
311pub trait LuauInstruction {
312    fn opcode(&self) -> Opcode;
313
314    fn from_abc(op: Opcode, a: u32, b: u32, c: u32) -> Self;
315    fn from_ad(opcode: Opcode, a: u32, d: u32) -> Self;
316    fn from_e(opcode: Opcode, e: u32) -> Self;
317
318    fn a(&self) -> u32;
319    fn b(&self) -> u32;
320    fn c(&self) -> u32;
321    fn d(&self) -> u32;
322    fn e(&self) -> u32;
323}
324
325#[cfg(feature = "lua51")]
326impl LuaInstruction for Instruction {
327    fn opcode(&self) -> Opcode {
328        let op = ((self.0 >> LUA_OP_POSITION) as u8) & self.mask_1(LUA_OP_SIZE, 0) as u8;
329        Opcode::LuaOpcode(LuaOpcode::index(op))
330    }
331
332    fn from_abc(opcode: Opcode, a: u32, b: u32, c: u32) -> Self {
333        let mut instruction = match opcode {
334            Opcode::LuaOpcode(op) => Instruction(op as u32),
335            _ => unreachable!(),
336        };
337
338        Instruction(instruction.set_a(a).set_b(b).set_c(c).0)
339    }
340
341    fn from_abx(opcode: Opcode, a: u32, bx: u32) -> Self {
342        let mut instruction = match opcode {
343            Opcode::LuaOpcode(op) => Instruction(op as u32),
344            _ => unreachable!(),
345        };
346
347        Instruction(instruction.set_a(a).set_bx(bx).0)
348    }
349
350    fn a(&self) -> u32 {
351        self.get(LUA_A_SIZE, LUA_A_POSITION)
352    }
353
354    fn b(&self) -> u32 {
355        self.get(LUA_B_SIZE, LUA_B_POSITION)
356    }
357
358    fn c(&self) -> u32 {
359        self.get(LUA_C_SIZE, LUA_C_POSITION)
360    }
361
362    fn bx(&self) -> u32 {
363        self.get(LUA_BX_SIZE, LUA_BX_POSITION)
364    }
365
366    fn sbx(&self) -> i32 {
367        (self.bx() as i32) - MAX_ARG_SBX
368    }
369
370    fn set_a(&mut self, a: u32) -> &mut Self {
371        self.set(a, LUA_A_SIZE, LUA_A_POSITION)
372    }
373
374    fn set_b(&mut self, b: u32) -> &mut Self {
375        self.set(b, LUA_B_SIZE, LUA_B_POSITION)
376    }
377
378    fn set_c(&mut self, c: u32) -> &mut Self {
379        self.set(c, LUA_C_SIZE, LUA_C_POSITION)
380    }
381
382    fn set_bx(&mut self, bx: u32) -> &mut Self {
383        self.set(bx, LUA_BX_SIZE, LUA_BX_POSITION)
384    }
385
386    fn set_sbx(&mut self, bx: i32) -> &mut Self {
387        self.set_bx((bx + MAX_ARG_SBX) as u32)
388    }
389
390    fn get(&self, size: u32, position: u32) -> u32 {
391        (self.0 >> position) & self.mask_1(size, 0)
392    }
393
394    fn set(&mut self, value: u32, size: u32, position: u32) -> &mut Self {
395        self.0 = (self.mask_1(size, position) & (value << position))
396            | (self.0 & self.mask_0(size, position));
397
398        self
399    }
400
401    fn mask_1(&self, n: u32, p: u32) -> u32 {
402        ((1u32 << n) - 1) << p
403    }
404
405    fn mask_0(&self, n: u32, p: u32) -> u32 {
406        !self.mask_1(n, p)
407    }
408}
409
410#[cfg(feature = "luau")]
411impl LuauInstruction for Instruction {
412    fn opcode(&self) -> Opcode {
413        let op = (self.0 & 0xff) as u8;
414        Opcode::LuauOpcode(LuauOpcode::index(op))
415    }
416
417    fn from_abc(opcode: Opcode, a: u32, b: u32, c: u32) -> Self {
418        let op = match opcode {
419            Opcode::LuauOpcode(op) => op as u32,
420            _ => unreachable!(),
421        };
422
423        Instruction((op) | (a << 8) | (b << 16) | (c << 24))
424    }
425
426    fn from_ad(opcode: Opcode, a: u32, d: u32) -> Self {
427        let op = match opcode {
428            Opcode::LuauOpcode(op) => op as u32,
429            _ => unreachable!(),
430        };
431
432        Instruction((op) | (a << 8) | (d << 16))
433    }
434
435    fn from_e(opcode: Opcode, e: u32) -> Self {
436        let op = match opcode {
437            Opcode::LuauOpcode(op) => op as u32,
438            _ => unreachable!(),
439        };
440
441        Instruction(op | e << 8)
442    }
443
444    fn a(&self) -> u32 {
445        (self.0 >> 8) & 0xff
446    }
447
448    fn b(&self) -> u32 {
449        (self.0 >> 16) & 0xff
450    }
451
452    fn c(&self) -> u32 {
453        (self.0 >> 24) & 0xff
454    }
455
456    fn d(&self) -> u32 {
457        LuauInstruction::b(self) | LuauInstruction::c(self)
458    }
459
460    fn e(&self) -> u32 {
461        LuauInstruction::a(self) | LuauInstruction::d(self)
462    }
463}
464
465impl Instruction {
466    pub fn from_bytes(bytes: &[u8]) -> Self {
467        Instruction(u32::from_le_bytes(bytes.try_into().unwrap()))
468    }
469}