use crate::model::Instruction;
use crate::model::InstructionWord;
use std::fmt;
pub const BYTECODE_VERSION_MIN: u8 = 3;
pub const BYTECODE_VERSION_MAX: u8 = 13;
pub const BYTECODE_VERSION_TARGET: u8 = 9;
pub const BYTECODE_VERSION_CLASSES: u8 = 100;
pub const BYTECODE_TYPE_VERSION_MIN: u8 = 1;
pub const BYTECODE_TYPE_VERSION_MAX: u8 = 3;
pub const BYTECODE_TYPE_VERSION_TARGET: u8 = 3;
pub const PROTO_FLAG_NATIVE_MODULE: u8 = 1 << 0;
pub const PROTO_FLAG_NATIVE_COLD: u8 = 1 << 1;
pub const PROTO_FLAG_NATIVE_FUNCTION: u8 = 1 << 2;
pub const PROTO_FLAG_INLINABLE: u8 = 1 << 3;
pub const PROTO_FLAG_USES_EXPORT: u8 = 1 << 4;
pub const FEEDBACK_TYPE_CALLTARGET: u8 = 0;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Opcode {
Nop,
Break,
LoadNil,
LoadB,
LoadN,
LoadK,
Move,
GetGlobal,
SetGlobal,
GetUpval,
SetUpval,
CloseUpvals,
GetImport,
GetTable,
SetTable,
GetTableKs,
SetTableKs,
GetTableN,
SetTableN,
NewClosure,
NameCall,
Call,
Return,
Jump,
JumpBack,
JumpIf,
JumpIfNot,
JumpIfEq,
JumpIfLe,
JumpIfLt,
JumpIfNotEq,
JumpIfNotLe,
JumpIfNotLt,
Add,
Sub,
Mul,
Div,
Mod,
Pow,
AddK,
SubK,
MulK,
DivK,
ModK,
PowK,
And,
Or,
AndK,
OrK,
Concat,
Not,
Minus,
Length,
NewTable,
DupTable,
SetList,
ForNPrep,
ForNLoop,
ForGLoop,
ForGPrepInext,
FastCall3,
ForGPrepNext,
NativeCall,
GetVarargs,
DupClosure,
PrepVarargs,
LoadKx,
JumpX,
FastCall,
Coverage,
Capture,
SubRK,
DivRK,
FastCall1,
FastCall2,
FastCall2K,
ForGPrep,
JumpXEqKNil,
JumpXEqKB,
JumpXEqKN,
JumpXEqKS,
IDiv,
IDivK,
GetUDataKs,
SetUDataKs,
NameCallUData,
NewClassMember,
CallFb,
CmpProto,
NewClass,
}
impl Opcode {
pub const COUNT: usize = Self::NewClass as usize + 1;
pub(super) fn word(self) -> InstructionWord {
self as InstructionWord
}
pub fn from_byte(value: u8) -> Option<Self> {
Some(match value {
0 => Self::Nop,
1 => Self::Break,
2 => Self::LoadNil,
3 => Self::LoadB,
4 => Self::LoadN,
5 => Self::LoadK,
6 => Self::Move,
7 => Self::GetGlobal,
8 => Self::SetGlobal,
9 => Self::GetUpval,
10 => Self::SetUpval,
11 => Self::CloseUpvals,
12 => Self::GetImport,
13 => Self::GetTable,
14 => Self::SetTable,
15 => Self::GetTableKs,
16 => Self::SetTableKs,
17 => Self::GetTableN,
18 => Self::SetTableN,
19 => Self::NewClosure,
20 => Self::NameCall,
21 => Self::Call,
22 => Self::Return,
23 => Self::Jump,
24 => Self::JumpBack,
25 => Self::JumpIf,
26 => Self::JumpIfNot,
27 => Self::JumpIfEq,
28 => Self::JumpIfLe,
29 => Self::JumpIfLt,
30 => Self::JumpIfNotEq,
31 => Self::JumpIfNotLe,
32 => Self::JumpIfNotLt,
33 => Self::Add,
34 => Self::Sub,
35 => Self::Mul,
36 => Self::Div,
37 => Self::Mod,
38 => Self::Pow,
39 => Self::AddK,
40 => Self::SubK,
41 => Self::MulK,
42 => Self::DivK,
43 => Self::ModK,
44 => Self::PowK,
45 => Self::And,
46 => Self::Or,
47 => Self::AndK,
48 => Self::OrK,
49 => Self::Concat,
50 => Self::Not,
51 => Self::Minus,
52 => Self::Length,
53 => Self::NewTable,
54 => Self::DupTable,
55 => Self::SetList,
56 => Self::ForNPrep,
57 => Self::ForNLoop,
58 => Self::ForGLoop,
59 => Self::ForGPrepInext,
60 => Self::FastCall3,
61 => Self::ForGPrepNext,
62 => Self::NativeCall,
63 => Self::GetVarargs,
64 => Self::DupClosure,
65 => Self::PrepVarargs,
66 => Self::LoadKx,
67 => Self::JumpX,
68 => Self::FastCall,
69 => Self::Coverage,
70 => Self::Capture,
71 => Self::SubRK,
72 => Self::DivRK,
73 => Self::FastCall1,
74 => Self::FastCall2,
75 => Self::FastCall2K,
76 => Self::ForGPrep,
77 => Self::JumpXEqKNil,
78 => Self::JumpXEqKB,
79 => Self::JumpXEqKN,
80 => Self::JumpXEqKS,
81 => Self::IDiv,
82 => Self::IDivK,
83 => Self::GetUDataKs,
84 => Self::SetUDataKs,
85 => Self::NameCallUData,
86 => Self::NewClassMember,
87 => Self::CallFb,
88 => Self::CmpProto,
89 => Self::NewClass,
_ => return None,
})
}
pub fn jump_target(self, instruction: Instruction, pc: u32) -> Option<i32> {
if self.is_jump_d() {
Some(pc as i32 + i32::from(instruction.d()) + 1)
} else if self.is_fast_call() {
Some(pc as i32 + i32::from(instruction.c()) + 2)
} else if self.is_skip_c() && instruction.c() != 0 {
Some(pc as i32 + i32::from(instruction.c()) + 1)
} else if self == Self::JumpX {
Some(pc as i32 + instruction.e() + 1)
} else {
None
}
}
pub fn length(self) -> usize {
match self {
Self::GetGlobal
| Self::SetGlobal
| Self::GetImport
| Self::GetTableKs
| Self::SetTableKs
| Self::NameCall
| Self::JumpIfEq
| Self::JumpIfLe
| Self::JumpIfLt
| Self::JumpIfNotEq
| Self::JumpIfNotLe
| Self::JumpIfNotLt
| Self::NewTable
| Self::SetList
| Self::ForGLoop
| Self::LoadKx
| Self::FastCall2
| Self::FastCall2K
| Self::FastCall3
| Self::JumpXEqKNil
| Self::JumpXEqKB
| Self::JumpXEqKN
| Self::JumpXEqKS
| Self::GetUDataKs
| Self::SetUDataKs
| Self::NameCallUData
| Self::NewClassMember
| Self::CallFb
| Self::CmpProto
| Self::NewClass => 2,
_ => 1,
}
}
pub fn is_fast_call(self) -> bool {
matches!(
self,
Self::FastCall | Self::FastCall1 | Self::FastCall2 | Self::FastCall2K | Self::FastCall3
)
}
pub fn is_jump_d(self) -> bool {
matches!(
self,
Self::Jump
| Self::JumpIf
| Self::JumpIfNot
| Self::JumpIfEq
| Self::JumpIfLe
| Self::JumpIfLt
| Self::JumpIfNotEq
| Self::JumpIfNotLe
| Self::JumpIfNotLt
| Self::ForNPrep
| Self::ForNLoop
| Self::ForGPrep
| Self::ForGLoop
| Self::ForGPrepInext
| Self::ForGPrepNext
| Self::JumpBack
| Self::JumpXEqKNil
| Self::JumpXEqKB
| Self::JumpXEqKN
| Self::JumpXEqKS
| Self::CmpProto
)
}
pub fn is_skip_c(self) -> bool {
self == Self::LoadB
}
pub fn is_fallthrough(self) -> bool {
!matches!(
self,
Self::Return | Self::Jump | Self::JumpBack | Self::JumpX
)
}
pub fn is_loop_jump(self) -> bool {
matches!(self, Self::JumpBack | Self::ForGLoop | Self::ForNLoop)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InvalidOpcode {
opcode: u8,
}
impl InvalidOpcode {
pub const fn new(opcode: u8) -> Self {
Self { opcode }
}
pub const fn opcode(self) -> u8 {
self.opcode
}
}
impl fmt::Display for InvalidOpcode {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "invalid Luau opcode {}", self.opcode)
}
}
impl std::error::Error for InvalidOpcode {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BytecodeConstantTag {
Nil = 0,
Boolean = 1,
Number = 2,
String = 3,
Import = 4,
Table = 5,
Closure = 6,
Vector = 7,
TableWithConstants = 8,
Integer = 9,
ClassShape = 10,
VectorDouble = 11,
Count = 12,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[repr(u16)]
pub enum BytecodeTypeTag {
Nil = 0,
Boolean = 1,
Number = 2,
String = 3,
Table = 4,
Function = 5,
Thread = 6,
Userdata = 7,
Vector = 8,
Buffer = 9,
Integer = 10,
#[default]
Any = 15,
TaggedUserdataBase = 64,
TaggedUserdataEnd = 96,
OptionalBit = 128,
Invalid = 256,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BuiltinFunction {
None = 0,
Assert = 1,
MathAbs = 2,
MathAcos = 3,
MathAsin = 4,
MathAtan2 = 5,
MathAtan = 6,
MathCeil = 7,
MathCosh = 8,
MathCos = 9,
MathDeg = 10,
MathExp = 11,
MathFloor = 12,
MathFmod = 13,
MathFrexp = 14,
MathLdexp = 15,
MathLog10 = 16,
MathLog = 17,
MathMax = 18,
MathMin = 19,
MathModf = 20,
MathPow = 21,
MathRad = 22,
MathSinh = 23,
MathSin = 24,
MathSqrt = 25,
MathTanh = 26,
MathTan = 27,
Bit32Arshift = 28,
Bit32Band = 29,
Bit32Bnot = 30,
Bit32Bor = 31,
Bit32Bxor = 32,
Bit32Btest = 33,
Bit32Extract = 34,
Bit32Lrotate = 35,
Bit32Lshift = 36,
Bit32Replace = 37,
Bit32Rrotate = 38,
Bit32Rshift = 39,
Type = 40,
StringByte = 41,
StringChar = 42,
StringLen = 43,
TypeOf = 44,
StringSub = 45,
MathClamp = 46,
MathSign = 47,
MathRound = 48,
RawSet = 49,
RawGet = 50,
RawEqual = 51,
TableInsert = 52,
TableUnpack = 53,
Vector = 54,
Bit32CountLz = 55,
Bit32CountRz = 56,
SelectVararg = 57,
RawLen = 58,
Bit32ExtractK = 59,
GetMetatable = 60,
SetMetatable = 61,
ToNumber = 62,
ToString = 63,
Bit32ByteSwap = 64,
BufferReadI8 = 65,
BufferReadU8 = 66,
BufferWriteU8 = 67,
BufferReadI16 = 68,
BufferReadU16 = 69,
BufferWriteU16 = 70,
BufferReadI32 = 71,
BufferReadU32 = 72,
BufferWriteU32 = 73,
BufferReadF32 = 74,
BufferWriteF32 = 75,
BufferReadF64 = 76,
BufferWriteF64 = 77,
VectorMagnitude = 78,
VectorNormalize = 79,
VectorCross = 80,
VectorDot = 81,
VectorFloor = 82,
VectorCeil = 83,
VectorAbs = 84,
VectorSign = 85,
VectorClamp = 86,
VectorMin = 87,
VectorMax = 88,
MathLerp = 89,
VectorLerp = 90,
MathIsNaN = 91,
MathIsInf = 92,
MathIsFinite = 93,
IntegerCreate = 94,
IntegerToNumber = 95,
IntegerNeg = 96,
IntegerAdd = 97,
IntegerSub = 98,
IntegerMul = 99,
IntegerDiv = 100,
IntegerMin = 101,
IntegerMax = 102,
IntegerRem = 103,
IntegerIDiv = 104,
IntegerUDiv = 105,
IntegerURem = 106,
IntegerMod = 107,
IntegerClamp = 108,
IntegerBand = 109,
IntegerBor = 110,
IntegerBnot = 111,
IntegerBxor = 112,
IntegerLt = 113,
IntegerLe = 114,
IntegerUlt = 115,
IntegerUle = 116,
IntegerGt = 117,
IntegerGe = 118,
IntegerUgt = 119,
IntegerUge = 120,
IntegerLshift = 121,
IntegerRshift = 122,
IntegerArshift = 123,
IntegerLrotate = 124,
IntegerRrotate = 125,
IntegerExtract = 126,
IntegerBtest = 127,
IntegerCountRz = 128,
IntegerCountLz = 129,
IntegerBswap = 130,
BufferReadInteger = 131,
BufferWriteInteger = 132,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum CaptureType {
Val = 0,
Ref = 1,
Upval = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InvalidCaptureType {
value: u8,
}
impl InvalidCaptureType {
pub const fn new(value: u8) -> Self {
Self { value }
}
pub const fn value(self) -> u8 {
self.value
}
}
impl fmt::Display for InvalidCaptureType {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "invalid capture type {}", self.value)
}
}
impl std::error::Error for InvalidCaptureType {}
impl TryFrom<u8> for CaptureType {
type Error = InvalidCaptureType;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Val),
1 => Ok(Self::Ref),
2 => Ok(Self::Upval),
_ => Err(InvalidCaptureType::new(value)),
}
}
}