1use crate::model::Instruction;
2use crate::model::InstructionWord;
3use std::fmt;
4
5pub const BYTECODE_VERSION_MIN: u8 = 3;
6pub const BYTECODE_VERSION_MAX: u8 = 13;
7pub const BYTECODE_VERSION_TARGET: u8 = 9;
8pub const BYTECODE_VERSION_CLASSES: u8 = 100;
9
10pub const BYTECODE_TYPE_VERSION_MIN: u8 = 1;
11pub const BYTECODE_TYPE_VERSION_MAX: u8 = 3;
12pub const BYTECODE_TYPE_VERSION_TARGET: u8 = 3;
13
14pub const PROTO_FLAG_NATIVE_MODULE: u8 = 1 << 0;
15pub const PROTO_FLAG_NATIVE_COLD: u8 = 1 << 1;
16pub const PROTO_FLAG_NATIVE_FUNCTION: u8 = 1 << 2;
17pub const PROTO_FLAG_INLINABLE: u8 = 1 << 3;
18pub const PROTO_FLAG_USES_EXPORT: u8 = 1 << 4;
19
20pub const FEEDBACK_TYPE_CALLTARGET: u8 = 0;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23#[repr(u8)]
24pub enum Opcode {
25 Nop,
26 Break,
27 LoadNil,
28 LoadB,
29 LoadN,
30 LoadK,
31 Move,
32 GetGlobal,
33 SetGlobal,
34 GetUpval,
35 SetUpval,
36 CloseUpvals,
37 GetImport,
38 GetTable,
39 SetTable,
40 GetTableKs,
41 SetTableKs,
42 GetTableN,
43 SetTableN,
44 NewClosure,
45 NameCall,
46 Call,
47 Return,
48 Jump,
49 JumpBack,
50 JumpIf,
51 JumpIfNot,
52 JumpIfEq,
53 JumpIfLe,
54 JumpIfLt,
55 JumpIfNotEq,
56 JumpIfNotLe,
57 JumpIfNotLt,
58 Add,
59 Sub,
60 Mul,
61 Div,
62 Mod,
63 Pow,
64 AddK,
65 SubK,
66 MulK,
67 DivK,
68 ModK,
69 PowK,
70 And,
71 Or,
72 AndK,
73 OrK,
74 Concat,
75 Not,
76 Minus,
77 Length,
78 NewTable,
79 DupTable,
80 SetList,
81 ForNPrep,
82 ForNLoop,
83 ForGLoop,
84 ForGPrepInext,
85 FastCall3,
86 ForGPrepNext,
87 NativeCall,
88 GetVarargs,
89 DupClosure,
90 PrepVarargs,
91 LoadKx,
92 JumpX,
93 FastCall,
94 Coverage,
95 Capture,
96 SubRK,
97 DivRK,
98 FastCall1,
99 FastCall2,
100 FastCall2K,
101 ForGPrep,
102 JumpXEqKNil,
103 JumpXEqKB,
104 JumpXEqKN,
105 JumpXEqKS,
106 IDiv,
107 IDivK,
108 GetUDataKs,
109 SetUDataKs,
110 NameCallUData,
111 NewClassMember,
112 CallFb,
113 CmpProto,
114 NewClass,
115}
116
117impl Opcode {
118 pub const COUNT: usize = Self::NewClass as usize + 1;
119
120 pub(super) fn word(self) -> InstructionWord {
121 self as InstructionWord
122 }
123
124 pub fn from_byte(value: u8) -> Option<Self> {
125 Some(match value {
126 0 => Self::Nop,
127 1 => Self::Break,
128 2 => Self::LoadNil,
129 3 => Self::LoadB,
130 4 => Self::LoadN,
131 5 => Self::LoadK,
132 6 => Self::Move,
133 7 => Self::GetGlobal,
134 8 => Self::SetGlobal,
135 9 => Self::GetUpval,
136 10 => Self::SetUpval,
137 11 => Self::CloseUpvals,
138 12 => Self::GetImport,
139 13 => Self::GetTable,
140 14 => Self::SetTable,
141 15 => Self::GetTableKs,
142 16 => Self::SetTableKs,
143 17 => Self::GetTableN,
144 18 => Self::SetTableN,
145 19 => Self::NewClosure,
146 20 => Self::NameCall,
147 21 => Self::Call,
148 22 => Self::Return,
149 23 => Self::Jump,
150 24 => Self::JumpBack,
151 25 => Self::JumpIf,
152 26 => Self::JumpIfNot,
153 27 => Self::JumpIfEq,
154 28 => Self::JumpIfLe,
155 29 => Self::JumpIfLt,
156 30 => Self::JumpIfNotEq,
157 31 => Self::JumpIfNotLe,
158 32 => Self::JumpIfNotLt,
159 33 => Self::Add,
160 34 => Self::Sub,
161 35 => Self::Mul,
162 36 => Self::Div,
163 37 => Self::Mod,
164 38 => Self::Pow,
165 39 => Self::AddK,
166 40 => Self::SubK,
167 41 => Self::MulK,
168 42 => Self::DivK,
169 43 => Self::ModK,
170 44 => Self::PowK,
171 45 => Self::And,
172 46 => Self::Or,
173 47 => Self::AndK,
174 48 => Self::OrK,
175 49 => Self::Concat,
176 50 => Self::Not,
177 51 => Self::Minus,
178 52 => Self::Length,
179 53 => Self::NewTable,
180 54 => Self::DupTable,
181 55 => Self::SetList,
182 56 => Self::ForNPrep,
183 57 => Self::ForNLoop,
184 58 => Self::ForGLoop,
185 59 => Self::ForGPrepInext,
186 60 => Self::FastCall3,
187 61 => Self::ForGPrepNext,
188 62 => Self::NativeCall,
189 63 => Self::GetVarargs,
190 64 => Self::DupClosure,
191 65 => Self::PrepVarargs,
192 66 => Self::LoadKx,
193 67 => Self::JumpX,
194 68 => Self::FastCall,
195 69 => Self::Coverage,
196 70 => Self::Capture,
197 71 => Self::SubRK,
198 72 => Self::DivRK,
199 73 => Self::FastCall1,
200 74 => Self::FastCall2,
201 75 => Self::FastCall2K,
202 76 => Self::ForGPrep,
203 77 => Self::JumpXEqKNil,
204 78 => Self::JumpXEqKB,
205 79 => Self::JumpXEqKN,
206 80 => Self::JumpXEqKS,
207 81 => Self::IDiv,
208 82 => Self::IDivK,
209 83 => Self::GetUDataKs,
210 84 => Self::SetUDataKs,
211 85 => Self::NameCallUData,
212 86 => Self::NewClassMember,
213 87 => Self::CallFb,
214 88 => Self::CmpProto,
215 89 => Self::NewClass,
216 _ => return None,
217 })
218 }
219
220 pub fn jump_target(self, instruction: Instruction, pc: u32) -> Option<i32> {
225 if self.is_jump_d() {
226 Some(pc as i32 + i32::from(instruction.d()) + 1)
227 } else if self.is_fast_call() {
228 Some(pc as i32 + i32::from(instruction.c()) + 2)
229 } else if self.is_skip_c() && instruction.c() != 0 {
230 Some(pc as i32 + i32::from(instruction.c()) + 1)
231 } else if self == Self::JumpX {
232 Some(pc as i32 + instruction.e() + 1)
233 } else {
234 None
235 }
236 }
237
238 pub fn length(self) -> usize {
239 match self {
240 Self::GetGlobal
241 | Self::SetGlobal
242 | Self::GetImport
243 | Self::GetTableKs
244 | Self::SetTableKs
245 | Self::NameCall
246 | Self::JumpIfEq
247 | Self::JumpIfLe
248 | Self::JumpIfLt
249 | Self::JumpIfNotEq
250 | Self::JumpIfNotLe
251 | Self::JumpIfNotLt
252 | Self::NewTable
253 | Self::SetList
254 | Self::ForGLoop
255 | Self::LoadKx
256 | Self::FastCall2
257 | Self::FastCall2K
258 | Self::FastCall3
259 | Self::JumpXEqKNil
260 | Self::JumpXEqKB
261 | Self::JumpXEqKN
262 | Self::JumpXEqKS
263 | Self::GetUDataKs
264 | Self::SetUDataKs
265 | Self::NameCallUData
266 | Self::NewClassMember
267 | Self::CallFb
268 | Self::CmpProto
269 | Self::NewClass => 2,
270 _ => 1,
271 }
272 }
273
274 pub fn is_fast_call(self) -> bool {
275 matches!(
276 self,
277 Self::FastCall | Self::FastCall1 | Self::FastCall2 | Self::FastCall2K | Self::FastCall3
278 )
279 }
280
281 pub fn is_jump_d(self) -> bool {
282 matches!(
283 self,
284 Self::Jump
285 | Self::JumpIf
286 | Self::JumpIfNot
287 | Self::JumpIfEq
288 | Self::JumpIfLe
289 | Self::JumpIfLt
290 | Self::JumpIfNotEq
291 | Self::JumpIfNotLe
292 | Self::JumpIfNotLt
293 | Self::ForNPrep
294 | Self::ForNLoop
295 | Self::ForGPrep
296 | Self::ForGLoop
297 | Self::ForGPrepInext
298 | Self::ForGPrepNext
299 | Self::JumpBack
300 | Self::JumpXEqKNil
301 | Self::JumpXEqKB
302 | Self::JumpXEqKN
303 | Self::JumpXEqKS
304 | Self::CmpProto
305 )
306 }
307
308 pub fn is_skip_c(self) -> bool {
309 self == Self::LoadB
310 }
311
312 pub fn is_fallthrough(self) -> bool {
313 !matches!(
314 self,
315 Self::Return | Self::Jump | Self::JumpBack | Self::JumpX
316 )
317 }
318
319 pub fn is_loop_jump(self) -> bool {
320 matches!(self, Self::JumpBack | Self::ForGLoop | Self::ForNLoop)
321 }
322}
323
324#[derive(Debug, Clone, Copy, PartialEq, Eq)]
326pub struct InvalidOpcode {
327 opcode: u8,
328}
329
330impl InvalidOpcode {
331 pub const fn new(opcode: u8) -> Self {
332 Self { opcode }
333 }
334
335 pub const fn opcode(self) -> u8 {
336 self.opcode
337 }
338}
339
340impl fmt::Display for InvalidOpcode {
341 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
342 write!(formatter, "invalid Luau opcode {}", self.opcode)
343 }
344}
345
346impl std::error::Error for InvalidOpcode {}
347
348#[derive(Debug, Clone, Copy, PartialEq, Eq)]
349#[repr(u8)]
350pub enum BytecodeConstantTag {
351 Nil = 0,
352 Boolean = 1,
353 Number = 2,
354 String = 3,
355 Import = 4,
356 Table = 5,
357 Closure = 6,
358 Vector = 7,
359 TableWithConstants = 8,
360 Integer = 9,
361 ClassShape = 10,
362 VectorDouble = 11,
363 Count = 12,
364}
365
366#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
367#[repr(u16)]
368pub enum BytecodeTypeTag {
369 Nil = 0,
370 Boolean = 1,
371 Number = 2,
372 String = 3,
373 Table = 4,
374 Function = 5,
375 Thread = 6,
376 Userdata = 7,
377 Vector = 8,
378 Buffer = 9,
379 Integer = 10,
380 #[default]
381 Any = 15,
382 TaggedUserdataBase = 64,
383 TaggedUserdataEnd = 96,
384 OptionalBit = 128,
385 Invalid = 256,
386}
387
388#[derive(Debug, Clone, Copy, PartialEq, Eq)]
389#[repr(u8)]
390pub enum BuiltinFunction {
391 None = 0,
392 Assert = 1,
393 MathAbs = 2,
394 MathAcos = 3,
395 MathAsin = 4,
396 MathAtan2 = 5,
397 MathAtan = 6,
398 MathCeil = 7,
399 MathCosh = 8,
400 MathCos = 9,
401 MathDeg = 10,
402 MathExp = 11,
403 MathFloor = 12,
404 MathFmod = 13,
405 MathFrexp = 14,
406 MathLdexp = 15,
407 MathLog10 = 16,
408 MathLog = 17,
409 MathMax = 18,
410 MathMin = 19,
411 MathModf = 20,
412 MathPow = 21,
413 MathRad = 22,
414 MathSinh = 23,
415 MathSin = 24,
416 MathSqrt = 25,
417 MathTanh = 26,
418 MathTan = 27,
419 Bit32Arshift = 28,
420 Bit32Band = 29,
421 Bit32Bnot = 30,
422 Bit32Bor = 31,
423 Bit32Bxor = 32,
424 Bit32Btest = 33,
425 Bit32Extract = 34,
426 Bit32Lrotate = 35,
427 Bit32Lshift = 36,
428 Bit32Replace = 37,
429 Bit32Rrotate = 38,
430 Bit32Rshift = 39,
431 Type = 40,
432 StringByte = 41,
433 StringChar = 42,
434 StringLen = 43,
435 TypeOf = 44,
436 StringSub = 45,
437 MathClamp = 46,
438 MathSign = 47,
439 MathRound = 48,
440 RawSet = 49,
441 RawGet = 50,
442 RawEqual = 51,
443 TableInsert = 52,
444 TableUnpack = 53,
445 Vector = 54,
446 Bit32CountLz = 55,
447 Bit32CountRz = 56,
448 SelectVararg = 57,
449 RawLen = 58,
450 Bit32ExtractK = 59,
451 GetMetatable = 60,
452 SetMetatable = 61,
453 ToNumber = 62,
454 ToString = 63,
455 Bit32ByteSwap = 64,
456 BufferReadI8 = 65,
457 BufferReadU8 = 66,
458 BufferWriteU8 = 67,
459 BufferReadI16 = 68,
460 BufferReadU16 = 69,
461 BufferWriteU16 = 70,
462 BufferReadI32 = 71,
463 BufferReadU32 = 72,
464 BufferWriteU32 = 73,
465 BufferReadF32 = 74,
466 BufferWriteF32 = 75,
467 BufferReadF64 = 76,
468 BufferWriteF64 = 77,
469 VectorMagnitude = 78,
470 VectorNormalize = 79,
471 VectorCross = 80,
472 VectorDot = 81,
473 VectorFloor = 82,
474 VectorCeil = 83,
475 VectorAbs = 84,
476 VectorSign = 85,
477 VectorClamp = 86,
478 VectorMin = 87,
479 VectorMax = 88,
480 MathLerp = 89,
481 VectorLerp = 90,
482 MathIsNaN = 91,
483 MathIsInf = 92,
484 MathIsFinite = 93,
485 IntegerCreate = 94,
486 IntegerToNumber = 95,
487 IntegerNeg = 96,
488 IntegerAdd = 97,
489 IntegerSub = 98,
490 IntegerMul = 99,
491 IntegerDiv = 100,
492 IntegerMin = 101,
493 IntegerMax = 102,
494 IntegerRem = 103,
495 IntegerIDiv = 104,
496 IntegerUDiv = 105,
497 IntegerURem = 106,
498 IntegerMod = 107,
499 IntegerClamp = 108,
500 IntegerBand = 109,
501 IntegerBor = 110,
502 IntegerBnot = 111,
503 IntegerBxor = 112,
504 IntegerLt = 113,
505 IntegerLe = 114,
506 IntegerUlt = 115,
507 IntegerUle = 116,
508 IntegerGt = 117,
509 IntegerGe = 118,
510 IntegerUgt = 119,
511 IntegerUge = 120,
512 IntegerLshift = 121,
513 IntegerRshift = 122,
514 IntegerArshift = 123,
515 IntegerLrotate = 124,
516 IntegerRrotate = 125,
517 IntegerExtract = 126,
518 IntegerBtest = 127,
519 IntegerCountRz = 128,
520 IntegerCountLz = 129,
521 IntegerBswap = 130,
522 BufferReadInteger = 131,
523 BufferWriteInteger = 132,
524}
525
526#[derive(Debug, Clone, Copy, PartialEq, Eq)]
527#[repr(u8)]
528pub enum CaptureType {
529 Val = 0,
530 Ref = 1,
531 Upval = 2,
532}
533
534#[derive(Debug, Clone, Copy, PartialEq, Eq)]
535pub struct InvalidCaptureType {
536 value: u8,
537}
538
539impl InvalidCaptureType {
540 pub const fn new(value: u8) -> Self {
541 Self { value }
542 }
543
544 pub const fn value(self) -> u8 {
545 self.value
546 }
547}
548
549impl fmt::Display for InvalidCaptureType {
550 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
551 write!(formatter, "invalid capture type {}", self.value)
552 }
553}
554
555impl std::error::Error for InvalidCaptureType {}
556
557impl TryFrom<u8> for CaptureType {
558 type Error = InvalidCaptureType;
559
560 fn try_from(value: u8) -> Result<Self, Self::Error> {
561 match value {
562 0 => Ok(Self::Val),
563 1 => Ok(Self::Ref),
564 2 => Ok(Self::Upval),
565 _ => Err(InvalidCaptureType::new(value)),
566 }
567 }
568}