Skip to main content

luau_bytecode/
opcodes.rs

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    /// Computes the jump target for this opcode/instruction pair.
221    ///
222    /// `instruction` must be the instruction word whose opcode decoded to
223    /// `self`; AUX words are not valid inputs.
224    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/// Error returned when an instruction word does not contain a valid opcode.
325#[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}