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, 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 fn mask_0(&self, n: u32, p: u32) -> u32;
305 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}