#[allow(unused_imports)] use crate::prelude::*;
use lua_types::{
CallInfoIdx, GcRef, LuaError, LuaValue, StackIdx,
};
use lua_types::tagmethod::TagMethod;
use lua_types::opcode::Instruction;
use crate::state::LuaState;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(non_camel_case_types)]
#[repr(u8)]
pub enum OpCode {
Move = 0,
LoadI = 1,
LoadF = 2,
LoadK = 3,
LoadKX = 4,
LoadFalse = 5,
LFalseSkip = 6,
LoadTrue = 7,
LoadNil = 8,
GetUpVal = 9,
SetUpVal = 10,
GetTabUp = 11,
GetTable = 12,
GetI = 13,
GetField = 14,
SetTabUp = 15,
SetTable = 16,
SetI = 17,
SetField = 18,
NewTable = 19,
Self_ = 20,
AddI = 21,
AddK = 22,
SubK = 23,
MulK = 24,
ModK = 25,
PowK = 26,
DivK = 27,
IDivK = 28,
BAndK = 29,
BOrK = 30,
BXOrK = 31,
ShrI = 32,
ShlI = 33,
Add = 34,
Sub = 35,
Mul = 36,
Mod = 37,
Pow = 38,
Div = 39,
IDiv = 40,
BAnd = 41,
BOr = 42,
BXOr = 43,
Shl = 44,
Shr = 45,
MmBin = 46,
MmBinI = 47,
MmBinK = 48,
Unm = 49,
BNot = 50,
Not = 51,
Len = 52,
Concat = 53,
Close = 54,
Tbc = 55,
Jmp = 56,
Eq = 57,
Lt = 58,
Le = 59,
EqK = 60,
EqI = 61,
LtI = 62,
LeI = 63,
GtI = 64,
GeI = 65,
Test = 66,
TestSet = 67,
Call = 68,
TailCall = 69,
Return = 70,
Return0 = 71,
Return1 = 72,
ForLoop = 73,
ForPrep = 74,
TForPrep = 75,
TForCall = 76,
TForLoop = 77,
SetList = 78,
Closure = 79,
VarArg = 80,
VarArgPrep = 81,
ExtraArg = 82,
}
#[allow(dead_code)]
const NUM_OPCODES: u8 = 83;
impl OpCode {
#[allow(non_upper_case_globals)]
pub const LoadKx: OpCode = OpCode::LoadKX;
#[allow(non_upper_case_globals)]
pub const GetUpval: OpCode = OpCode::GetUpVal;
}
pub trait InstructionExt {
fn opcode(&self) -> OpCode;
fn arg_a(&self) -> i32;
fn arg_b(&self) -> i32;
fn arg_c(&self) -> i32;
fn arg_k(&self) -> i32;
fn arg_ax(&self) -> i32;
fn arg_bx(&self) -> i32;
fn arg_s_b(&self) -> i32;
fn arg_s_c(&self) -> i32;
fn arg_s_j(&self) -> i32;
fn arg_s_bx(&self) -> i32;
fn test_k(&self) -> bool;
fn test_a_mode(&self) -> bool;
fn is_mm_mode(&self) -> bool;
fn is_vararg_prep(&self) -> bool;
fn is_in_top(&self) -> bool;
}
impl InstructionExt for Instruction {
#[inline(always)]
fn opcode(&self) -> OpCode {
match (self.raw() & 0x7F) as u8 {
0 => OpCode::Move,
1 => OpCode::LoadI,
2 => OpCode::LoadF,
3 => OpCode::LoadK,
4 => OpCode::LoadKX,
5 => OpCode::LoadFalse,
6 => OpCode::LFalseSkip,
7 => OpCode::LoadTrue,
8 => OpCode::LoadNil,
9 => OpCode::GetUpVal,
10 => OpCode::SetUpVal,
11 => OpCode::GetTabUp,
12 => OpCode::GetTable,
13 => OpCode::GetI,
14 => OpCode::GetField,
15 => OpCode::SetTabUp,
16 => OpCode::SetTable,
17 => OpCode::SetI,
18 => OpCode::SetField,
19 => OpCode::NewTable,
20 => OpCode::Self_,
21 => OpCode::AddI,
22 => OpCode::AddK,
23 => OpCode::SubK,
24 => OpCode::MulK,
25 => OpCode::ModK,
26 => OpCode::PowK,
27 => OpCode::DivK,
28 => OpCode::IDivK,
29 => OpCode::BAndK,
30 => OpCode::BOrK,
31 => OpCode::BXOrK,
32 => OpCode::ShrI,
33 => OpCode::ShlI,
34 => OpCode::Add,
35 => OpCode::Sub,
36 => OpCode::Mul,
37 => OpCode::Mod,
38 => OpCode::Pow,
39 => OpCode::Div,
40 => OpCode::IDiv,
41 => OpCode::BAnd,
42 => OpCode::BOr,
43 => OpCode::BXOr,
44 => OpCode::Shl,
45 => OpCode::Shr,
46 => OpCode::MmBin,
47 => OpCode::MmBinI,
48 => OpCode::MmBinK,
49 => OpCode::Unm,
50 => OpCode::BNot,
51 => OpCode::Not,
52 => OpCode::Len,
53 => OpCode::Concat,
54 => OpCode::Close,
55 => OpCode::Tbc,
56 => OpCode::Jmp,
57 => OpCode::Eq,
58 => OpCode::Lt,
59 => OpCode::Le,
60 => OpCode::EqK,
61 => OpCode::EqI,
62 => OpCode::LtI,
63 => OpCode::LeI,
64 => OpCode::GtI,
65 => OpCode::GeI,
66 => OpCode::Test,
67 => OpCode::TestSet,
68 => OpCode::Call,
69 => OpCode::TailCall,
70 => OpCode::Return,
71 => OpCode::Return0,
72 => OpCode::Return1,
73 => OpCode::ForLoop,
74 => OpCode::ForPrep,
75 => OpCode::TForPrep,
76 => OpCode::TForCall,
77 => OpCode::TForLoop,
78 => OpCode::SetList,
79 => OpCode::Closure,
80 => OpCode::VarArg,
81 => OpCode::VarArgPrep,
82 => OpCode::ExtraArg,
_ => OpCode::ExtraArg,
}
}
#[inline] fn arg_a(&self) -> i32 { ((self.raw() >> 7) & 0xFF) as i32 }
#[inline] fn arg_b(&self) -> i32 { ((self.raw() >> 16) & 0xFF) as i32 }
#[inline] fn arg_c(&self) -> i32 { ((self.raw() >> 24) & 0xFF) as i32 }
#[inline] fn arg_k(&self) -> i32 { ((self.raw() >> 15) & 0x1) as i32 }
#[inline] fn arg_ax(&self) -> i32 { (self.raw() >> 7) as i32 }
#[inline] fn arg_bx(&self) -> i32 { (self.raw() >> 15) as i32 }
#[inline] fn arg_s_b(&self) -> i32 { self.arg_b() - 0x7F }
#[inline] fn arg_s_c(&self) -> i32 { self.arg_c() - 0x7F }
#[inline] fn arg_s_j(&self) -> i32 { self.arg_ax() - 0xFFFFFF }
#[inline] fn arg_s_bx(&self) -> i32 { self.arg_bx() - 0xFFFF }
#[inline] fn test_k(&self) -> bool { (self.raw() & (1 << 15)) != 0 }
#[inline]
fn test_a_mode(&self) -> bool {
(op_mode_byte(self.opcode()) & (1 << 3)) != 0
}
#[inline]
fn is_mm_mode(&self) -> bool {
(op_mode_byte(self.opcode()) & (1 << 7)) != 0
}
#[inline]
fn is_vararg_prep(&self) -> bool {
matches!(self.opcode(), OpCode::VarArgPrep)
}
#[inline]
fn is_in_top(&self) -> bool {
(op_mode_byte(self.opcode()) & (1 << 5)) != 0 && self.arg_b() == 0
}
}
const OP_MODE_BYTES: [u8; NUM_OPCODES as usize] = [
0x08, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x80, 0x80, 0x80, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x04, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x18, 0x68, 0x68, 0x20, 0x00, 0x00, 0x09, 0x09, 0x01, 0x00, 0x09, 0x20, 0x09, 0x48, 0x28, 0x03, ];
#[inline(always)]
fn op_mode_byte(op: OpCode) -> u8 {
OP_MODE_BYTES[op as usize]
}
const MAX_TAG_LOOP: i32 = 2000;
const NBITS: u32 = 64;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum F2Imod {
Eq,
Floor,
Ceil,
}
#[inline]
fn intop_add(a: i64, b: i64) -> i64 {
(a as u64).wrapping_add(b as u64) as i64
}
#[inline]
fn intop_sub(a: i64, b: i64) -> i64 {
(a as u64).wrapping_sub(b as u64) as i64
}
#[inline]
fn intop_mul(a: i64, b: i64) -> i64 {
(a as u64).wrapping_mul(b as u64) as i64
}
#[inline]
fn intop_shr(x: i64, n: u32) -> i64 {
(x as u64 >> n) as i64
}
#[inline]
fn intop_shl(x: i64, n: u32) -> i64 {
(x as u64).wrapping_shl(n) as i64
}
#[inline]
fn intop_band(a: i64, b: i64) -> i64 { ((a as u64) & (b as u64)) as i64 }
#[inline]
fn intop_bor(a: i64, b: i64) -> i64 { ((a as u64) | (b as u64)) as i64 }
#[inline]
fn intop_bxor(a: i64, b: i64) -> i64 { ((a as u64) ^ (b as u64)) as i64 }
#[inline]
fn int_fits_float(i: i64) -> bool {
const MAXINTFITSF: u64 = 1u64 << f64::MANTISSA_DIGITS;
(MAXINTFITSF.wrapping_add(i as u64)) <= 2 * MAXINTFITSF
}
fn str_to_number(obj: &LuaValue) -> Option<LuaValue> {
let s = match obj {
LuaValue::Str(ts) => ts.as_bytes().to_vec(),
_ => return None,
};
let trimmed = trim_whitespace(&s);
if trimmed.is_empty() {
return None;
}
let mut result = LuaValue::Nil;
if crate::object::str2num(trimmed, &mut result) != 0 {
return Some(result);
}
None
}
fn trim_whitespace(s: &[u8]) -> &[u8] {
let start = s.iter().position(|&b| !b.is_ascii_whitespace()).unwrap_or(s.len());
let end = s.iter().rposition(|&b| !b.is_ascii_whitespace()).map(|i| i + 1).unwrap_or(0);
if start <= end { &s[start..end] } else { &s[0..0] }
}
pub(crate) fn tonumber_(obj: &LuaValue) -> Option<f64> {
if let LuaValue::Int(i) = obj {
return Some(*i as f64);
}
if let Some(v) = str_to_number(obj) {
return match v {
LuaValue::Float(f) => Some(f),
LuaValue::Int(i) => Some(i as f64),
_ => None,
};
}
None
}
fn tonumber(obj: &LuaValue) -> Option<f64> {
if let LuaValue::Float(f) = obj {
return Some(*f);
}
tonumber_(obj)
}
pub(crate) fn flt_to_integer(n: f64, mode: F2Imod) -> Option<i64> {
let f = n.floor();
if n != f {
match mode {
F2Imod::Eq => return None,
F2Imod::Ceil => {
let f = f + 1.0;
if f >= i64::MIN as f64 && f < (i64::MAX as f64 + 1.0) {
return Some(f as i64);
}
return None;
}
F2Imod::Floor => { }
}
}
if f >= i64::MIN as f64 && f < (i64::MAX as f64 + 1.0) {
Some(f as i64)
} else {
None
}
}
pub(crate) fn to_integer_ns(obj: &LuaValue, mode: F2Imod) -> Option<i64> {
if let LuaValue::Float(f) = obj {
return flt_to_integer(*f, mode);
}
if let LuaValue::Int(i) = obj {
return Some(*i);
}
None
}
pub(crate) fn to_integer(obj: &LuaValue, mode: F2Imod) -> Option<i64> {
let coerced;
let obj = if let Some(v) = str_to_number(obj) {
coerced = v;
&coerced
} else {
obj
};
to_integer_ns(obj, mode)
}
fn forlimit(
state: &mut LuaState,
init: i64,
lim: &LuaValue,
step: i64,
) -> Result<(bool, i64), LuaError> {
let round = if step < 0 { F2Imod::Ceil } else { F2Imod::Floor };
if let Some(p) = to_integer(lim, round) {
let skip = if step > 0 { init > p } else { init < p };
return Ok((skip, p));
}
let flim = match tonumber(lim) {
Some(f) => f,
None => return Err(crate::debug::for_error(state, lim, b"limit")),
};
if 0.0_f64 < flim {
if step < 0 {
return Ok((true, 0));
}
Ok((false, i64::MAX))
} else {
if step > 0 {
return Ok((true, 0));
}
Ok((false, i64::MIN))
}
}
pub(crate) fn forprep(state: &mut LuaState, ra: StackIdx) -> Result<bool, LuaError> {
let pinit = state.get_at(ra);
let plimit = state.get_at(ra + 1);
let pstep = state.get_at(ra + 2);
if let (LuaValue::Int(init), LuaValue::Int(step)) = (&pinit, &pstep) {
let init = *init;
let step = *step;
if step == 0 {
return Err(LuaError::runtime(format_args!("'for' step is zero")));
}
state.set_at(ra + 3, LuaValue::Int(init));
let (skip, limit) = forlimit(state, init, &plimit, step)?;
if skip {
return Ok(true);
}
let count: u64 = if step > 0 {
let c = (limit as u64).wrapping_sub(init as u64);
if step != 1 { c / (step as u64) } else { c }
} else {
let c = (init as u64).wrapping_sub(limit as u64);
c / (((-(step + 1)) as u64).wrapping_add(1))
};
state.set_at(ra + 1, LuaValue::Int(count as i64));
Ok(false)
} else {
let limit_f = match tonumber(&plimit) {
Some(f) => f,
None => return Err(crate::debug::for_error(state, &plimit, b"limit")),
};
let step_f = match tonumber(&pstep) {
Some(f) => f,
None => return Err(crate::debug::for_error(state, &pstep, b"step")),
};
let init_f = match tonumber(&pinit) {
Some(f) => f,
None => return Err(crate::debug::for_error(state, &pinit, b"initial value")),
};
if step_f == 0.0 {
return Err(LuaError::runtime(format_args!("'for' step is zero")));
}
let skip = if step_f > 0.0 { limit_f < init_f } else { init_f < limit_f };
if skip {
return Ok(true);
}
state.set_at(ra + 1, LuaValue::Float(limit_f));
state.set_at(ra + 2, LuaValue::Float(step_f));
state.set_at(ra, LuaValue::Float(init_f));
state.set_at(ra + 3, LuaValue::Float(init_f));
Ok(false)
}
}
fn float_for_loop(state: &mut LuaState, ra: StackIdx) -> bool {
let step = match state.get_at(ra + 2) {
LuaValue::Float(f) => f,
_ => return false,
};
let limit = match state.get_at(ra + 1) {
LuaValue::Float(f) => f,
_ => return false,
};
let idx = match state.get_at(ra) {
LuaValue::Float(f) => f,
_ => return false,
};
let idx = idx + step;
if if step > 0.0 { idx <= limit } else { limit <= idx } {
state.set_at(ra, LuaValue::Float(idx));
state.set_at(ra + 3, LuaValue::Float(idx));
true
} else {
false
}
}
pub(crate) fn finish_get(
state: &mut LuaState,
t_val: LuaValue,
key: LuaValue,
result_idx: StackIdx,
slot_empty: bool,
t_idx: Option<StackIdx>,
) -> Result<(), LuaError> {
let mut t = t_val;
let mut t_idx = t_idx;
for _loop in 0..MAX_TAG_LOOP {
let tm: LuaValue;
if slot_empty && !matches!(t, LuaValue::Table(_)) {
tm = state.get_tm_by_obj(&t, TagMethod::Index);
if matches!(tm, LuaValue::Nil) {
return Err(match t_idx {
Some(idx) => crate::debug::type_error(state, &t, idx, b"index"),
None => LuaError::type_error(&t, "index"),
});
}
} else {
let mt = state.table_metatable(&t);
tm = state.fast_tm_table(mt.as_ref(), TagMethod::Index);
if matches!(tm, LuaValue::Nil) {
state.set_at(result_idx, LuaValue::Nil);
return Ok(());
}
}
if matches!(tm, LuaValue::Function(_)) {
state.call_tm_res(tm, &t, &key, result_idx)?;
return Ok(());
}
t = tm.clone();
t_idx = None;
if let Some(v) = state.fast_get(&t, &key)? {
state.set_at(result_idx, v);
return Ok(());
}
}
Err(LuaError::runtime(format_args!("'__index' chain too long; possible loop")))
}
pub(crate) fn finish_set(
state: &mut LuaState,
t_val: LuaValue,
key: LuaValue,
val: LuaValue,
_slot_present: bool,
t_idx: Option<StackIdx>,
var_hint: Option<(&[u8], &[u8])>,
) -> Result<(), LuaError> {
let mut t = t_val;
let mut t_idx = t_idx;
for _loop in 0..MAX_TAG_LOOP {
let tm: LuaValue;
if matches!(t, LuaValue::Table(_)) {
let mt = state.table_metatable(&t);
tm = state.fast_tm_table(mt.as_ref(), TagMethod::NewIndex);
if matches!(tm, LuaValue::Nil) {
state.table_raw_set(&t, key, val.clone())?;
state.gc_barrier_back(&t, &val);
return Ok(());
}
} else {
tm = state.get_tm_by_obj(&t, TagMethod::NewIndex);
if matches!(tm, LuaValue::Nil) {
return Err(match (t_idx, var_hint) {
(Some(idx), _) => crate::debug::type_error(state, &t, idx, b"index"),
(None, Some((kind, name))) => {
crate::debug::type_error_with_hint(state, &t, b"index", kind, name)
}
(None, None) => LuaError::type_error(&t, "index"),
});
}
}
if matches!(tm, LuaValue::Function(_)) {
state.call_tm(tm, &t, &key, &val)?;
return Ok(());
}
t = tm.clone();
t_idx = None;
if state.fast_get(&t, &key)?.is_some() {
state.table_raw_set(&t, key.clone(), val.clone())?;
state.gc_barrier_back(&t, &val);
return Ok(());
}
}
Err(LuaError::runtime(format_args!("'__newindex' chain too long; possible loop")))
}
fn str_cmp(s1: &[u8], s2: &[u8]) -> std::cmp::Ordering {
let mut s1 = s1;
let mut s2 = s2;
loop {
let z1 = s1.iter().position(|&b| b == 0).unwrap_or(s1.len());
let z2 = s2.iter().position(|&b| b == 0).unwrap_or(s2.len());
let seg_cmp = s1[..z1].cmp(&s2[..z2]);
if seg_cmp != std::cmp::Ordering::Equal {
return seg_cmp;
}
if z2 == s2.len() {
if z1 == s1.len() {
return std::cmp::Ordering::Equal;
}
return std::cmp::Ordering::Greater; }
if z1 == s1.len() {
return std::cmp::Ordering::Less; }
s1 = &s1[z1 + 1..];
s2 = &s2[z2 + 1..];
}
}
#[inline]
fn lt_int_float(i: i64, f: f64) -> bool {
if int_fits_float(i) {
(i as f64) < f
} else {
match flt_to_integer(f, F2Imod::Ceil) {
Some(fi) => i < fi,
None => f > 0.0, }
}
}
#[inline]
fn le_int_float(i: i64, f: f64) -> bool {
if int_fits_float(i) {
(i as f64) <= f
} else {
match flt_to_integer(f, F2Imod::Floor) {
Some(fi) => i <= fi,
None => f > 0.0,
}
}
}
#[inline]
fn lt_float_int(f: f64, i: i64) -> bool {
if int_fits_float(i) {
f < (i as f64)
} else {
match flt_to_integer(f, F2Imod::Floor) {
Some(fi) => fi < i,
None => f < 0.0,
}
}
}
#[inline]
fn le_float_int(f: f64, i: i64) -> bool {
if int_fits_float(i) {
f <= (i as f64)
} else {
match flt_to_integer(f, F2Imod::Ceil) {
Some(fi) => fi <= i,
None => f < 0.0,
}
}
}
#[inline]
fn lt_num(l: &LuaValue, r: &LuaValue) -> bool {
debug_assert!(matches!(l, LuaValue::Int(_) | LuaValue::Float(_)));
debug_assert!(matches!(r, LuaValue::Int(_) | LuaValue::Float(_)));
match (l, r) {
(LuaValue::Int(li), LuaValue::Int(ri)) => li < ri,
(LuaValue::Int(li), LuaValue::Float(rf)) => lt_int_float(*li, *rf),
(LuaValue::Float(lf), LuaValue::Float(rf)) => lf < rf,
(LuaValue::Float(lf), LuaValue::Int(ri)) => lt_float_int(*lf, *ri),
_ => false,
}
}
#[inline]
fn le_num(l: &LuaValue, r: &LuaValue) -> bool {
debug_assert!(matches!(l, LuaValue::Int(_) | LuaValue::Float(_)));
debug_assert!(matches!(r, LuaValue::Int(_) | LuaValue::Float(_)));
match (l, r) {
(LuaValue::Int(li), LuaValue::Int(ri)) => li <= ri,
(LuaValue::Int(li), LuaValue::Float(rf)) => le_int_float(*li, *rf),
(LuaValue::Float(lf), LuaValue::Float(rf)) => lf <= rf,
(LuaValue::Float(lf), LuaValue::Int(ri)) => le_float_int(*lf, *ri),
_ => false,
}
}
fn less_than_others(state: &mut LuaState, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
debug_assert!(!(matches!(l, LuaValue::Int(_) | LuaValue::Float(_))
&& matches!(r, LuaValue::Int(_) | LuaValue::Float(_))));
match (l, r) {
(LuaValue::Str(ts1), LuaValue::Str(ts2)) => {
Ok(str_cmp(ts1.as_bytes(), ts2.as_bytes()) == std::cmp::Ordering::Less)
}
_ => state.call_order_tm(l, r, TagMethod::Lt),
}
}
pub(crate) fn less_than(state: &mut LuaState, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
if matches!(l, LuaValue::Int(_) | LuaValue::Float(_))
&& matches!(r, LuaValue::Int(_) | LuaValue::Float(_))
{
Ok(lt_num(l, r))
} else {
less_than_others(state, l, r)
}
}
fn less_equal_others(state: &mut LuaState, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
match (l, r) {
(LuaValue::Str(ts1), LuaValue::Str(ts2)) => {
Ok(str_cmp(ts1.as_bytes(), ts2.as_bytes()) != std::cmp::Ordering::Greater)
}
_ => state.call_order_tm(l, r, TagMethod::Le),
}
}
pub(crate) fn less_equal(state: &mut LuaState, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
if matches!(l, LuaValue::Int(_) | LuaValue::Float(_))
&& matches!(r, LuaValue::Int(_) | LuaValue::Float(_))
{
Ok(le_num(l, r))
} else {
less_equal_others(state, l, r)
}
}
pub(crate) fn equal_obj(
state: Option<&mut LuaState>,
t1: &LuaValue,
t2: &LuaValue,
) -> Result<bool, LuaError> {
let same_variant = std::mem::discriminant(t1) == std::mem::discriminant(t2);
if !same_variant {
let t1_is_num = matches!(t1, LuaValue::Int(_) | LuaValue::Float(_));
let t2_is_num = matches!(t2, LuaValue::Int(_) | LuaValue::Float(_));
if !(t1_is_num && t2_is_num) {
return Ok(false);
}
let i1 = to_integer_ns(t1, F2Imod::Eq);
let i2 = to_integer_ns(t2, F2Imod::Eq);
return Ok(i1.is_some() && i2.is_some() && i1 == i2);
}
match (t1, t2) {
(LuaValue::Nil, LuaValue::Nil) => Ok(true),
(LuaValue::Bool(b1), LuaValue::Bool(b2)) => Ok(b1 == b2),
(LuaValue::Int(i1), LuaValue::Int(i2)) => Ok(i1 == i2),
(LuaValue::Float(f1), LuaValue::Float(f2)) => Ok(f1 == f2),
(LuaValue::LightUserData(p1), LuaValue::LightUserData(p2)) => Ok(p1 == p2),
(LuaValue::Function(f1), LuaValue::Function(f2)) => {
use lua_types::closure::LuaClosure;
let same = match (f1, f2) {
(LuaClosure::Lua(a), LuaClosure::Lua(b)) => GcRef::ptr_eq(a, b),
(LuaClosure::C(a), LuaClosure::C(b)) => GcRef::ptr_eq(a, b),
(LuaClosure::LightC(a), LuaClosure::LightC(b)) => a == b,
_ => false,
};
Ok(same)
}
(LuaValue::Str(s1), LuaValue::Str(s2)) => {
Ok(s1 == s2)
}
(LuaValue::UserData(u1), LuaValue::UserData(u2)) => {
if std::ptr::eq(u1.as_ptr(), u2.as_ptr()) {
return Ok(true);
}
let Some(state) = state else { return Ok(false); };
let tm1 = state.fast_tm_ud(u1, TagMethod::Eq);
let tm = if matches!(tm1, LuaValue::Nil) {
state.fast_tm_ud(u2, TagMethod::Eq)
} else {
tm1
};
if matches!(tm, LuaValue::Nil) {
return Ok(false);
}
let result = state.call_tm_res_bool(tm, t1, t2)?;
Ok(result)
}
(LuaValue::Table(h1), LuaValue::Table(h2)) => {
if std::ptr::eq(h1.as_ptr(), h2.as_ptr()) {
return Ok(true);
}
let Some(state) = state else { return Ok(false); };
let mt1 = h1.metatable();
let mt2 = h2.metatable();
let tm1 = state.fast_tm_table(mt1.as_ref(), TagMethod::Eq);
let tm = if matches!(tm1, LuaValue::Nil) {
state.fast_tm_table(mt2.as_ref(), TagMethod::Eq)
} else {
tm1
};
if matches!(tm, LuaValue::Nil) {
return Ok(false);
}
let result = state.call_tm_res_bool(tm, t1, t2)?;
Ok(result)
}
(LuaValue::Thread(a), LuaValue::Thread(b)) => Ok(GcRef::ptr_eq(a, b)),
_ => Ok(std::ptr::eq(t1 as *const _, t2 as *const _)),
}
}
fn copy_to_buf(state: &LuaState, top: StackIdx, n: u32, buf: &mut Vec<u8>) {
buf.clear();
let mut remaining = n;
loop {
let idx = top - remaining as i32;
let v = state.get_at(idx);
if let LuaValue::Str(ts) = v {
buf.extend_from_slice(ts.as_bytes());
}
if remaining <= 1 {
break;
}
remaining -= 1;
}
}
pub(crate) fn concat(state: &mut LuaState, total: i32) -> Result<(), LuaError> {
if total == 1 {
return Ok(());
}
let mut total = total;
loop {
let top = state.top_idx();
let v_tm1 = state.get_at(top - 1); let v_tm2 = state.get_at(top - 2);
let top2_coercible = matches!(v_tm2, LuaValue::Str(_))
|| matches!(v_tm2, LuaValue::Int(_) | LuaValue::Float(_));
let top1_stringlike = matches!(v_tm1, LuaValue::Str(_))
|| matches!(v_tm1, LuaValue::Int(_) | LuaValue::Float(_));
if !top2_coercible || !top1_stringlike {
state.try_concat_tm(&v_tm1, &v_tm2)?;
total -= 1;
let top = state.top_idx();
state.set_top(top - 1);
if total <= 1 {
break;
}
continue;
}
let is_empty = |v: &LuaValue| -> bool {
matches!(v, LuaValue::Str(s) if s.as_bytes().is_empty())
};
let n: u32;
if is_empty(&v_tm1) {
state.coerce_to_string(top - 2)?;
n = 2;
} else if is_empty(&v_tm2) {
state.coerce_to_string(top - 1)?;
let v = state.get_at(top - 1);
state.set_at(top - 2, v);
n = 2;
} else {
state.coerce_to_string(top - 1)?;
let s1 = match state.get_at(top - 1) {
LuaValue::Str(ts) => ts.as_bytes().len(),
_ => 0,
};
let mut total_len = s1;
let mut count: u32 = 1;
let top = state.top_idx();
loop {
if count as i32 >= total {
break;
}
let idx = top - (count as i32 + 1);
let v = state.get_at(idx);
if !matches!(v, LuaValue::Str(_) | LuaValue::Int(_) | LuaValue::Float(_)) {
break;
}
state.coerce_to_string(idx)?;
let l = match state.get_at(idx) {
LuaValue::Str(ts) => ts.as_bytes().len(),
_ => 0,
};
if l >= usize::MAX - total_len {
state.set_top(top - total as i32);
return Err(LuaError::runtime(format_args!("string length overflow")));
}
total_len += l;
count += 1;
}
n = count;
let mut buf: Vec<u8> = Vec::with_capacity(total_len);
let top = state.top_idx();
copy_to_buf(state, top, n, &mut buf);
let ts = state.intern_or_create_str(&buf)?;
state.set_at(top - n as i32, LuaValue::Str(ts));
}
total -= n as i32 - 1;
let top = state.top_idx();
state.set_top(top - ((n - 1) as i32));
if total <= 1 {
break;
}
}
Ok(())
}
pub(crate) fn obj_len(state: &mut LuaState, ra: StackIdx, rb: LuaValue) -> Result<(), LuaError> {
match &rb {
LuaValue::Table(_) => {
let mt = state.table_metatable(&rb);
let tm = state.fast_tm_table(mt.as_ref(), TagMethod::Len);
if matches!(tm, LuaValue::Nil) {
let n = state.table_length(&rb)?;
state.set_at(ra, LuaValue::Int(n as i64));
return Ok(());
}
state.call_tm_res(tm, &rb, &rb, ra)?;
}
LuaValue::Str(ts) => {
let n = ts.len();
state.set_at(ra, LuaValue::Int(n as i64));
}
other => {
let tm = state.get_tm_by_obj(other, TagMethod::Len);
if matches!(tm, LuaValue::Nil) {
return Err(LuaError::type_error(other, "get length of"));
}
state.call_tm_res(tm, &rb, &rb, ra)?;
}
}
Ok(())
}
pub(crate) fn idiv(m: i64, n: i64) -> Result<i64, LuaError> {
if (n as u64).wrapping_add(1) <= 1 {
if n == 0 {
return Err(LuaError::runtime(format_args!("attempt to divide by zero")));
}
return Ok(intop_sub(0, m));
}
let q = m / n;
if (m ^ n) < 0 && m % n != 0 {
Ok(q - 1)
} else {
Ok(q)
}
}
pub(crate) fn imod(m: i64, n: i64) -> Result<i64, LuaError> {
if (n as u64).wrapping_add(1) <= 1 {
if n == 0 {
return Err(LuaError::runtime(format_args!("attempt to perform 'n%0'")));
}
return Ok(0);
}
let r = m % n;
if r != 0 && (r ^ n) < 0 {
Ok(r + n)
} else {
Ok(r)
}
}
pub(crate) fn fmodf(m: f64, n: f64) -> f64 {
let r = m % n;
let opposite_signs = if r > 0.0 { n < 0.0 } else { r < 0.0 && n > 0.0 };
if opposite_signs {
r + n
} else {
r
}
}
pub(crate) fn tagmethod_from_index(i: usize) -> TagMethod {
use TagMethod::*;
match i {
0 => Index, 1 => NewIndex, 2 => Gc, 3 => Mode, 4 => Len, 5 => Eq,
6 => Add, 7 => Sub, 8 => Mul, 9 => Mod, 10 => Pow, 11 => Div,
12 => Idiv, 13 => Band, 14 => Bor, 15 => Bxor, 16 => Shl, 17 => Shr,
18 => Unm, 19 => Bnot, 20 => Lt, 21 => Le, 22 => Concat, 23 => Call,
24 => Close,
_ => Index,
}
}
pub(crate) fn int_floor_mod(_state: &mut LuaState, a: i64, b: i64) -> Result<i64, LuaError> {
imod(a, b)
}
pub(crate) fn int_floor_div(_state: &mut LuaState, a: i64, b: i64) -> Result<i64, LuaError> {
idiv(a, b)
}
pub(crate) fn float_floor_mod(_state: &mut LuaState, a: f64, b: f64) -> Result<f64, LuaError> {
Ok(fmodf(a, b))
}
pub(crate) fn shiftl(x: i64, y: i64) -> i64 {
if y < 0 {
if y <= -(NBITS as i64) {
0
} else {
intop_shr(x, (-y) as u32)
}
} else {
if y >= NBITS as i64 {
0
} else {
intop_shl(x, y as u32)
}
}
}
fn push_closure(
state: &mut LuaState,
proto_idx: usize, ci: CallInfoIdx,
base: StackIdx,
ra: StackIdx,
) -> Result<(), LuaError> {
state.push_closure(proto_idx, ci, base, ra)
}
pub(crate) fn finish_op(state: &mut LuaState) -> Result<(), LuaError> {
let ci = state.current_ci_idx();
let base = state.ci_base(ci);
let inst = state.ci_prev_instruction(ci);
let op = inst.opcode();
match op {
OpCode::MmBin | OpCode::MmBinI | OpCode::MmBinK => {
let prev_inst = state.ci_prev2_instruction(ci);
let a = prev_inst.arg_a();
state.dec_top();
let top = state.top_idx();
let v = state.get_at(top);
state.set_at(base + a, v);
}
OpCode::Unm | OpCode::BNot | OpCode::Len
| OpCode::GetTabUp | OpCode::GetTable | OpCode::GetI
| OpCode::GetField | OpCode::Self_ => {
let a = inst.arg_a();
state.dec_top();
let top = state.top_idx();
let v = state.get_at(top);
state.set_at(base + a, v);
}
OpCode::Lt | OpCode::Le | OpCode::LtI | OpCode::LeI
| OpCode::GtI | OpCode::GeI | OpCode::Eq => {
let top_minus1 = state.top_idx() - 1;
let v = state.get_at(top_minus1);
let res = !matches!(v, LuaValue::Nil | LuaValue::Bool(false));
state.dec_top();
if (res as i32) != inst.arg_k() {
state.ci_skip_next_instruction(ci);
}
}
OpCode::Concat => {
let top = state.top_idx() - 1; let a = inst.arg_a();
let total_concat = (top - 1 - (base + a)) as i32;
let v = state.get_at(top);
state.set_at(top - 2, v);
state.set_top(top - 1);
concat(state, total_concat)?;
}
OpCode::Close => {
state.ci_step_pc_back(ci);
}
OpCode::Return => {
let a = inst.arg_a();
let ra = base + a;
let nres = state.ci_nres(ci);
state.set_top(ra + nres);
state.ci_step_pc_back(ci);
}
other => {
debug_assert!(
matches!(
other,
OpCode::TForCall | OpCode::Call | OpCode::TailCall
| OpCode::SetTabUp | OpCode::SetTable | OpCode::SetI | OpCode::SetField
),
"unexpected opcode in finish_op: {:?}",
other
);
}
}
Ok(())
}
pub(crate) fn execute(state: &mut LuaState, mut ci: CallInfoIdx) -> Result<(), LuaError> {
let mut trap: bool;
'startfunc: loop {
trap = state.hook_mask() != 0;
'returning: loop {
let cl = match state.ci_lua_closure(ci) {
Some(c) => c,
None => {
return Err(LuaError::runtime(format_args!(
"internal: execute called on non-Lua frame"
)));
}
};
let mut pc: u32 = state.ci_savedpc(ci);
if trap {
trap = state.trace_call(ci)?;
}
let mut base: StackIdx = state.ci_base(ci);
'dispatch: loop {
if trap {
trap = state.trace_exec(ci, pc)?;
base = state.ci_base(ci); }
let i: Instruction = state.proto_code(&cl, pc);
pc += 1;
let op = i.opcode();
debug_assert!(base == state.ci_base(ci));
#[cfg(debug_assertions)]
{
let op_mode = op_mode_byte(op);
if (op_mode & (1 << 5)) == 0 || i.arg_b() != 0 {
state.set_top(base);
}
}
match op {
OpCode::Move => {
let ra = base + i.arg_a();
let rb = base + i.arg_b();
let v = state.get_at(rb);
state.set_at(ra, v);
}
OpCode::LoadI => {
let ra = base + i.arg_a();
let b = i.arg_s_bx() as i64;
state.set_at(ra, LuaValue::Int(b));
}
OpCode::LoadF => {
let ra = base + i.arg_a();
let b = i.arg_s_bx() as f64;
state.set_at(ra, LuaValue::Float(b));
}
OpCode::LoadK => {
let ra = base + i.arg_a();
let k_idx = i.arg_bx() as usize;
let v = state.proto_const(&cl, k_idx).clone();
state.set_at(ra, v);
}
OpCode::LoadKX => {
let ra = base + i.arg_a();
let extra = state.proto_code(&cl, pc);
pc += 1;
let k_idx = extra.arg_ax() as usize;
let v = state.proto_const(&cl, k_idx).clone();
state.set_at(ra, v);
}
OpCode::LoadFalse => {
let ra = base + i.arg_a();
state.set_at(ra, LuaValue::Bool(false));
}
OpCode::LFalseSkip => {
let ra = base + i.arg_a();
state.set_at(ra, LuaValue::Bool(false));
pc += 1;
}
OpCode::LoadTrue => {
let ra = base + i.arg_a();
state.set_at(ra, LuaValue::Bool(true));
}
OpCode::LoadNil => {
let ra = base + i.arg_a();
let b = i.arg_b();
for k in 0..=b {
state.set_at(ra + k, LuaValue::Nil);
}
}
OpCode::GetUpVal => {
let ra = base + i.arg_a();
let b = i.arg_b() as usize;
let v = state.upvalue_get(&cl, b);
state.set_at(ra, v);
}
OpCode::SetUpVal => {
let ra = base + i.arg_a();
let b = i.arg_b() as usize;
let v = state.stack[ra.0 as usize].val;
let uv = cl.upval(b);
match uv.try_open_payload() {
Some((thread_id, idx)) if thread_id as u64 == state.cached_thread_id => {
state.stack[idx.0 as usize].val = v;
}
_ => {
state.upvalue_set(&cl, b, v)?;
}
}
}
OpCode::GetTabUp => {
let ra = base + i.arg_a();
let b = i.arg_b() as usize;
let k_idx = i.arg_c() as usize;
let upval = state.upvalue_get(&cl, b);
let key = state.proto_const(&cl, k_idx).clone();
match state.fast_get_short_str(&upval, &key)? {
Some(v) => state.set_at(ra, v),
None => {
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
finish_get(state, upval, key, ra, true, None)?;
trap = state.ci_trap(ci);
}
}
}
OpCode::GetTable => {
let ra = base + i.arg_a();
let rb_idx = base + i.arg_b();
let rb_v = state.get_at(rb_idx);
let rc_v = state.get_at(base + i.arg_c());
let fast_result = if let LuaValue::Int(n) = &rc_v {
state.fast_get_int(&rb_v, *n)?
} else {
state.fast_get(&rb_v, &rc_v)?
};
match fast_result {
Some(v) => state.set_at(ra, v),
None => {
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
finish_get(state, rb_v, rc_v, ra, true, Some(rb_idx))?;
trap = state.ci_trap(ci);
}
}
}
OpCode::GetI => {
let ra = base + i.arg_a();
let rb_idx = base + i.arg_b();
let rb_v = state.get_at(rb_idx);
let c = i.arg_c() as i64;
match state.fast_get_int(&rb_v, c)? {
Some(v) => state.set_at(ra, v),
None => {
let key = LuaValue::Int(c);
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
finish_get(state, rb_v, key, ra, true, Some(rb_idx))?;
trap = state.ci_trap(ci);
}
}
}
OpCode::GetField => {
let ra = base + i.arg_a();
let rb_idx = base + i.arg_b();
let rb_v = state.get_at(rb_idx);
let k_idx = i.arg_c() as usize;
let key = state.proto_const(&cl, k_idx).clone();
match state.fast_get_short_str(&rb_v, &key)? {
Some(v) => state.set_at(ra, v),
None => {
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
finish_get(state, rb_v, key, ra, true, Some(rb_idx))?;
trap = state.ci_trap(ci);
}
}
}
OpCode::SetTabUp => {
let a = i.arg_a() as usize;
let b_idx = i.arg_b() as usize; let rc_v = if i.test_k() {
state.proto_const(&cl, i.arg_c() as usize).clone()
} else {
state.get_at(base + i.arg_c())
};
let upval = state.upvalue_get(&cl, a);
let key = state.proto_const(&cl, b_idx).clone();
match state.fast_get_short_str(&upval, &key)? {
Some(_slot) => {
state.table_raw_set(&upval, key, rc_v.clone())?;
state.gc_barrier_back(&upval, &rc_v);
}
None => {
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
let upval_name: Vec<u8> = cl
.proto
.upvalues
.get(a)
.and_then(|uv| uv.name.as_ref())
.map(|s| s.as_bytes().to_vec())
.unwrap_or_else(|| b"?".to_vec());
let hint: Option<(&[u8], &[u8])> =
Some((b"upvalue", &upval_name));
finish_set(state, upval, key, rc_v, false, None, hint)?;
trap = state.ci_trap(ci);
}
}
}
OpCode::SetTable => {
let ra_idx = base + i.arg_a();
let ra_v = state.get_at(ra_idx);
let rb_v = state.get_at(base + i.arg_b());
let rc_v = if i.test_k() {
state.proto_const(&cl, i.arg_c() as usize).clone()
} else {
state.get_at(base + i.arg_c())
};
let fast = if let LuaValue::Int(n) = &rb_v {
state.fast_get_int(&ra_v, *n)?
} else {
state.fast_get(&ra_v, &rb_v)?
};
if fast.is_some() {
state.table_raw_set(&ra_v, rb_v, rc_v.clone())?;
state.gc_barrier_back(&ra_v, &rc_v);
} else {
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
finish_set(state, ra_v, rb_v, rc_v, false, Some(ra_idx), None)?;
trap = state.ci_trap(ci);
}
}
OpCode::SetI => {
let ra_idx = base + i.arg_a();
let ra_v = state.get_at(ra_idx);
let c = i.arg_b() as i64;
let rc_v = if i.test_k() {
state.proto_const(&cl, i.arg_c() as usize).clone()
} else {
state.get_at(base + i.arg_c())
};
let fast = state.fast_get_int(&ra_v, c)?;
if fast.is_some() {
state.table_raw_set(&ra_v, LuaValue::Int(c), rc_v.clone())?;
state.gc_barrier_back(&ra_v, &rc_v);
} else {
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
finish_set(state, ra_v, LuaValue::Int(c), rc_v, false, Some(ra_idx), None)?;
trap = state.ci_trap(ci);
}
}
OpCode::SetField => {
let ra_idx = base + i.arg_a();
let ra_v = state.get_at(ra_idx);
let b_idx = i.arg_b() as usize;
let key = state.proto_const(&cl, b_idx).clone();
let rc_v = if i.test_k() {
state.proto_const(&cl, i.arg_c() as usize).clone()
} else {
state.get_at(base + i.arg_c())
};
match state.fast_get_short_str(&ra_v, &key)? {
Some(_) => {
state.table_raw_set(&ra_v, key, rc_v.clone())?;
state.gc_barrier_back(&ra_v, &rc_v);
}
None => {
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
finish_set(state, ra_v, key, rc_v, false, Some(ra_idx), None)?;
trap = state.ci_trap(ci);
}
}
}
OpCode::NewTable => {
let ra = base + i.arg_a();
let mut b = i.arg_b();
let mut c = i.arg_c();
if b > 0 {
b = 1 << (b - 1);
}
if i.test_k() {
let extra = state.proto_code(&cl, pc);
pc += 1;
const MAXARG_C: i32 = (1 << 8) - 1;
c += extra.arg_ax() * (MAXARG_C + 1);
} else {
pc += 1; }
state.set_top(ra + 1);
let t = state.new_table();
state.set_at(ra, LuaValue::Table(t.clone()));
if b != 0 || c != 0 {
state.table_resize(&t, c as usize, b as usize)?;
}
state.set_ci_savedpc(ci, pc);
state.set_top(ra + 1);
state.gc_cond_step();
trap = state.ci_trap(ci);
}
OpCode::Self_ => {
let ra = base + i.arg_a();
let rb_idx = base + i.arg_b();
let rb_v = state.get_at(rb_idx);
let k_idx = i.arg_c() as usize; let key = if i.test_k() {
state.proto_const(&cl, k_idx).clone()
} else {
state.get_at(base + i.arg_c())
};
state.set_at(ra + 1, rb_v.clone());
match state.fast_get_short_str(&rb_v, &key)? {
Some(v) => state.set_at(ra, v),
None => {
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
finish_get(state, rb_v, key, ra, true, Some(rb_idx))?;
trap = state.ci_trap(ci);
}
}
}
OpCode::AddI => {
let ra = base + i.arg_a();
let rb = base + i.arg_b();
let imm = i.arg_s_c() as i64;
if let Some(iv1) = state.get_int_at(rb) {
pc += 1;
state.set_at(ra, LuaValue::Int(intop_add(iv1, imm)));
} else if let Some(nb) = state.get_float_at(rb) {
pc += 1;
state.set_at(ra, LuaValue::Float(nb + imm as f64));
}
}
OpCode::AddK => {
let ra = base + i.arg_a();
let rb = base + i.arg_b();
let kidx = i.arg_c() as usize;
if let (Some(i1), Some(i2)) = (state.get_int_at(rb), state.proto_const_int(&cl, kidx)) {
pc += 1;
state.set_at(ra, LuaValue::Int(intop_add(i1, i2)));
} else if let (Some(n1), Some(n2)) = (state.get_num_at(rb), state.proto_const_num(&cl, kidx)) {
pc += 1;
state.set_at(ra, LuaValue::Float(n1 + n2));
}
}
OpCode::SubK => {
let ra = base + i.arg_a();
let rb = base + i.arg_b();
let kidx = i.arg_c() as usize;
if let (Some(i1), Some(i2)) = (state.get_int_at(rb), state.proto_const_int(&cl, kidx)) {
pc += 1;
state.set_at(ra, LuaValue::Int(intop_sub(i1, i2)));
} else if let (Some(n1), Some(n2)) = (state.get_num_at(rb), state.proto_const_num(&cl, kidx)) {
pc += 1;
state.set_at(ra, LuaValue::Float(n1 - n2));
}
}
OpCode::MulK => {
let ra = base + i.arg_a();
let rb = base + i.arg_b();
let kidx = i.arg_c() as usize;
if let (Some(i1), Some(i2)) = (state.get_int_at(rb), state.proto_const_int(&cl, kidx)) {
pc += 1;
state.set_at(ra, LuaValue::Int(intop_mul(i1, i2)));
} else if let (Some(n1), Some(n2)) = (state.get_num_at(rb), state.proto_const_num(&cl, kidx)) {
pc += 1;
state.set_at(ra, LuaValue::Float(n1 * n2));
}
}
OpCode::ModK => {
let ra = base + i.arg_a();
let v1 = state.get_at(base + i.arg_b());
let v2 = state.proto_const(&cl, i.arg_c() as usize).clone();
state.set_ci_savedpc(ci, pc); state.set_top(state.ci_top(ci));
arith_op_checked(state, ra, &v1, &v2, &mut pc,
|a, b| imod(a, b), fmodf)?;
}
OpCode::PowK => {
let ra = base + i.arg_a();
let rb = base + i.arg_b();
let kidx = i.arg_c() as usize;
if let (Some(n1), Some(n2)) = (state.get_num_at(rb), state.proto_const_num(&cl, kidx)) {
pc += 1;
let r = if n2 == 2.0 { n1 * n1 } else { n1.powf(n2) };
state.set_at(ra, LuaValue::Float(r));
}
}
OpCode::DivK => {
let ra = base + i.arg_a();
let rb = base + i.arg_b();
let kidx = i.arg_c() as usize;
if let (Some(n1), Some(n2)) = (state.get_num_at(rb), state.proto_const_num(&cl, kidx)) {
pc += 1;
state.set_at(ra, LuaValue::Float(n1 / n2));
}
}
OpCode::IDivK => {
let ra = base + i.arg_a();
let v1 = state.get_at(base + i.arg_b());
let v2 = state.proto_const(&cl, i.arg_c() as usize).clone();
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
arith_op_checked(state, ra, &v1, &v2, &mut pc,
|a, b| idiv(a, b), |a, b| (a / b).floor())?;
}
OpCode::BAndK => {
let ra = base + i.arg_a();
let v1 = state.get_at(base + i.arg_b());
let v2 = state.proto_const(&cl, i.arg_c() as usize).clone();
bitwise_op_k(state, ra, &v1, &v2, &mut pc, intop_band);
}
OpCode::BOrK => {
let ra = base + i.arg_a();
let v1 = state.get_at(base + i.arg_b());
let v2 = state.proto_const(&cl, i.arg_c() as usize).clone();
bitwise_op_k(state, ra, &v1, &v2, &mut pc, intop_bor);
}
OpCode::BXOrK => {
let ra = base + i.arg_a();
let v1 = state.get_at(base + i.arg_b());
let v2 = state.proto_const(&cl, i.arg_c() as usize).clone();
bitwise_op_k(state, ra, &v1, &v2, &mut pc, intop_bxor);
}
OpCode::ShrI => {
let ra = base + i.arg_a();
let v = state.get_at(base + i.arg_b());
let ic = i.arg_s_c() as i64;
if let Some(ib) = to_integer_ns(&v, F2Imod::Eq) {
pc += 1;
state.set_at(ra, LuaValue::Int(shiftl(ib, -ic)));
}
}
OpCode::ShlI => {
let ra = base + i.arg_a();
let v = state.get_at(base + i.arg_b());
let ic = i.arg_s_c() as i64;
if let Some(ib) = to_integer_ns(&v, F2Imod::Eq) {
pc += 1;
state.set_at(ra, LuaValue::Int(shiftl(ic, ib)));
}
}
OpCode::Add => {
let ra = base + i.arg_a();
let rb = base + i.arg_b();
let rc = base + i.arg_c();
let ra_u = ra.0 as usize;
let rb_v = state.stack[rb.0 as usize].val;
let rc_v = state.stack[rc.0 as usize].val;
if let (LuaValue::Int(i1), LuaValue::Int(i2)) = (rb_v, rc_v) {
pc += 1;
state.stack[ra_u].val = LuaValue::Int(intop_add(i1, i2));
} else if let (Some(n1), Some(n2)) = (number_value(rb_v), number_value(rc_v)) {
pc += 1;
state.stack[ra_u].val = LuaValue::Float(n1 + n2);
}
}
OpCode::Sub => {
let ra = base + i.arg_a();
let rb = base + i.arg_b();
let rc = base + i.arg_c();
let ra_u = ra.0 as usize;
let rb_v = state.stack[rb.0 as usize].val;
let rc_v = state.stack[rc.0 as usize].val;
if let (LuaValue::Int(i1), LuaValue::Int(i2)) = (rb_v, rc_v) {
pc += 1;
state.stack[ra_u].val = LuaValue::Int(intop_sub(i1, i2));
} else if let (Some(n1), Some(n2)) = (number_value(rb_v), number_value(rc_v)) {
pc += 1;
state.stack[ra_u].val = LuaValue::Float(n1 - n2);
}
}
OpCode::Mul => {
let ra = base + i.arg_a();
let rb = base + i.arg_b();
let rc = base + i.arg_c();
if let Some((i1, i2)) = state.get_int_pair_at(rb, rc) {
pc += 1;
state.set_at(ra, LuaValue::Int(intop_mul(i1, i2)));
} else if let Some((n1, n2)) = state.get_num_pair_at(rb, rc) {
pc += 1;
state.set_at(ra, LuaValue::Float(n1 * n2));
}
}
OpCode::Mod => {
let ra = base + i.arg_a();
let v1 = state.get_at(base + i.arg_b());
let v2 = state.get_at(base + i.arg_c());
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
arith_op_checked(state, ra, &v1, &v2, &mut pc,
|a, b| imod(a, b), fmodf)?;
}
OpCode::Pow => {
let ra = base + i.arg_a();
let rb = base + i.arg_b();
let rc = base + i.arg_c();
if let Some((n1, n2)) = state.get_num_pair_at(rb, rc) {
pc += 1;
let r = if n2 == 2.0 { n1 * n1 } else { n1.powf(n2) };
state.set_at(ra, LuaValue::Float(r));
}
}
OpCode::Div => {
let ra = base + i.arg_a();
let rb = base + i.arg_b();
let rc = base + i.arg_c();
if let Some((n1, n2)) = state.get_num_pair_at(rb, rc) {
pc += 1;
state.set_at(ra, LuaValue::Float(n1 / n2));
}
}
OpCode::IDiv => {
let ra = base + i.arg_a();
let v1 = state.get_at(base + i.arg_b());
let v2 = state.get_at(base + i.arg_c());
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
arith_op_checked(state, ra, &v1, &v2, &mut pc,
|a, b| idiv(a, b), |a, b| (a / b).floor())?;
}
OpCode::BAnd => {
let ra = base + i.arg_a();
let v1 = state.get_at(base + i.arg_b());
let v2 = state.get_at(base + i.arg_c());
bitwise_op_rr(state, ra, &v1, &v2, &mut pc, intop_band);
}
OpCode::BOr => {
let ra = base + i.arg_a();
let v1 = state.get_at(base + i.arg_b());
let v2 = state.get_at(base + i.arg_c());
bitwise_op_rr(state, ra, &v1, &v2, &mut pc, intop_bor);
}
OpCode::BXOr => {
let ra = base + i.arg_a();
let v1 = state.get_at(base + i.arg_b());
let v2 = state.get_at(base + i.arg_c());
bitwise_op_rr(state, ra, &v1, &v2, &mut pc, intop_bxor);
}
OpCode::Shr => {
let ra = base + i.arg_a();
let v1 = state.get_at(base + i.arg_b());
let v2 = state.get_at(base + i.arg_c());
bitwise_shift_rr(state, ra, &v1, &v2, &mut pc, true);
}
OpCode::Shl => {
let ra = base + i.arg_a();
let v1 = state.get_at(base + i.arg_b());
let v2 = state.get_at(base + i.arg_c());
bitwise_shift_rr(state, ra, &v1, &v2, &mut pc, false);
}
OpCode::MmBin => {
let ra_idx = base + i.arg_a();
let rb_idx = base + i.arg_b();
let ra_v = state.get_at(ra_idx);
let rb_v = state.get_at(rb_idx);
let tm = tagmethod_from_index(i.arg_c() as usize);
let prev_inst = state.proto_code(&cl, pc - 2);
let result_idx = base + prev_inst.arg_a();
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
state.try_bin_tm(&ra_v, Some(ra_idx), &rb_v, Some(rb_idx), result_idx, tm)?;
trap = state.ci_trap(ci);
}
OpCode::MmBinI => {
let ra_idx = base + i.arg_a();
let ra_v = state.get_at(ra_idx);
let imm = i.arg_s_b() as i64;
let tm = tagmethod_from_index(i.arg_c() as usize);
let flip = i.arg_k() != 0;
let prev_inst = state.proto_code(&cl, pc - 2);
let result_idx = base + prev_inst.arg_a();
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
state.try_bin_i_tm(&ra_v, Some(ra_idx), imm, flip, result_idx, tm)?;
trap = state.ci_trap(ci);
}
OpCode::MmBinK => {
let ra_idx = base + i.arg_a();
let ra_v = state.get_at(ra_idx);
let imm = state.proto_const(&cl, i.arg_b() as usize).clone();
let tm = tagmethod_from_index(i.arg_c() as usize);
let flip = i.arg_k() != 0;
let prev_inst = state.proto_code(&cl, pc - 2);
let result_idx = base + prev_inst.arg_a();
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
state.try_bin_assoc_tm(&ra_v, Some(ra_idx), &imm, None, flip, result_idx, tm)?;
trap = state.ci_trap(ci);
}
OpCode::Unm => {
let ra = base + i.arg_a();
let rb_idx = base + i.arg_b();
let rb_v = state.get_at(rb_idx);
match &rb_v {
LuaValue::Int(ib) => {
state.set_at(ra, LuaValue::Int(intop_sub(0, *ib)));
}
LuaValue::Float(nb) => {
state.set_at(ra, LuaValue::Float(-nb));
}
_ => {
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
state.try_bin_tm(&rb_v, Some(rb_idx), &rb_v, Some(rb_idx), ra, TagMethod::Unm)?;
trap = state.ci_trap(ci);
}
}
}
OpCode::BNot => {
let ra = base + i.arg_a();
let rb_idx = base + i.arg_b();
let rb_v = state.get_at(rb_idx);
if let Some(ib) = to_integer_ns(&rb_v, F2Imod::Eq) {
state.set_at(ra, LuaValue::Int(!ib));
} else {
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
state.try_bin_tm(&rb_v, Some(rb_idx), &rb_v, Some(rb_idx), ra, TagMethod::Bnot)?;
trap = state.ci_trap(ci);
}
}
OpCode::Not => {
let ra = base + i.arg_a();
let rb_v = state.get_at(base + i.arg_b());
let falsy = matches!(rb_v, LuaValue::Nil | LuaValue::Bool(false));
state.set_at(ra, LuaValue::Bool(falsy));
}
OpCode::Len => {
let ra = base + i.arg_a();
let rb_v = state.get_at(base + i.arg_b());
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
obj_len(state, ra, rb_v)?;
trap = state.ci_trap(ci);
}
OpCode::Concat => {
let ra = base + i.arg_a();
let n = i.arg_b() as i32;
state.set_top(ra + n as i32);
state.set_ci_savedpc(ci, pc); concat(state, n)?;
trap = state.ci_trap(ci);
let top = state.top_idx();
state.set_ci_savedpc(ci, pc);
state.set_top(top);
state.gc_cond_step();
trap = state.ci_trap(ci);
}
OpCode::Close => {
let ra = base + i.arg_a();
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
crate::func::close(state, ra, lua_types::status::LuaStatus::Ok as i32, true)?;
trap = state.ci_trap(ci);
}
OpCode::Tbc => {
let ra = base + i.arg_a();
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
state.new_tbc_upval(ra)?;
}
OpCode::Jmp => {
pc = (pc as i64 + i.arg_s_j() as i64) as u32;
trap = state.ci_trap(ci);
}
OpCode::Eq => {
let ra_v = state.get_at(base + i.arg_a());
let rb_v = state.get_at(base + i.arg_b());
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
let cond = equal_obj(Some(state), &ra_v, &rb_v)? as u32;
trap = state.ci_trap(ci);
if (cond as i32) != i.arg_k() {
pc += 1;
} else {
let next = state.proto_code(&cl, pc);
pc = (pc as i64 + next.arg_s_j() as i64 + 1) as u32;
trap = state.ci_trap(ci);
}
}
OpCode::Lt => {
let ra_v = state.get_at(base + i.arg_a());
let rb_v = state.get_at(base + i.arg_b());
let cond = if let (LuaValue::Int(ia), LuaValue::Int(ib)) = (&ra_v, &rb_v) {
*ia < *ib
} else if matches!((&ra_v, &rb_v),
(LuaValue::Int(_) | LuaValue::Float(_),
LuaValue::Int(_) | LuaValue::Float(_))) {
lt_num(&ra_v, &rb_v)
} else {
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
let r = less_than_others(state, &ra_v, &rb_v)?;
trap = state.ci_trap(ci);
r
};
if (cond as i32) != i.arg_k() {
pc += 1;
} else {
let next = state.proto_code(&cl, pc);
pc = (pc as i64 + next.arg_s_j() as i64 + 1) as u32;
trap = state.ci_trap(ci);
}
}
OpCode::Le => {
let ra_v = state.get_at(base + i.arg_a());
let rb_v = state.get_at(base + i.arg_b());
let cond = if let (LuaValue::Int(ia), LuaValue::Int(ib)) = (&ra_v, &rb_v) {
*ia <= *ib
} else if matches!((&ra_v, &rb_v),
(LuaValue::Int(_) | LuaValue::Float(_),
LuaValue::Int(_) | LuaValue::Float(_))) {
le_num(&ra_v, &rb_v)
} else {
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
let r = less_equal_others(state, &ra_v, &rb_v)?;
trap = state.ci_trap(ci);
r
};
if (cond as i32) != i.arg_k() {
pc += 1;
} else {
let next = state.proto_code(&cl, pc);
pc = (pc as i64 + next.arg_s_j() as i64 + 1) as u32;
trap = state.ci_trap(ci);
}
}
OpCode::EqK => {
let ra_v = state.get_at(base + i.arg_a());
let rb_v = state.proto_const(&cl, i.arg_b() as usize).clone();
let cond = equal_obj(None, &ra_v, &rb_v)? as u32;
if (cond as i32) != i.arg_k() {
pc += 1;
} else {
let next = state.proto_code(&cl, pc);
pc = (pc as i64 + next.arg_s_j() as i64 + 1) as u32;
trap = state.ci_trap(ci);
}
}
OpCode::EqI => {
let ra_v = state.get_at(base + i.arg_a());
let im = i.arg_s_b() as i64;
let cond: bool = match &ra_v {
LuaValue::Int(iv) => *iv == im,
LuaValue::Float(fv) => *fv == im as f64,
_ => false,
};
if (cond as i32) != i.arg_k() {
pc += 1;
} else {
let next = state.proto_code(&cl, pc);
pc = (pc as i64 + next.arg_s_j() as i64 + 1) as u32;
trap = state.ci_trap(ci);
}
}
OpCode::LtI => {
let ra = base + i.arg_a();
let im = i.arg_s_b() as i64;
let fast_cond = match &state.stack[ra.0 as usize].val {
LuaValue::Int(ia) => Some(*ia < im),
LuaValue::Float(fa) => Some(*fa < im as f64),
_ => None,
};
let cond = match fast_cond {
Some(cond) => cond,
None => order_imm_slow(state, ra, pc, &mut trap, ci, i, im, false, TagMethod::Lt)?,
};
finish_order_imm_jump(state, &cl, &mut pc, &mut trap, ci, i, cond);
}
OpCode::LeI => {
let ra = base + i.arg_a();
let im = i.arg_s_b() as i64;
let fast_cond = match &state.stack[ra.0 as usize].val {
LuaValue::Int(ia) => Some(*ia <= im),
LuaValue::Float(fa) => Some(*fa <= im as f64),
_ => None,
};
let cond = match fast_cond {
Some(cond) => cond,
None => order_imm_slow(state, ra, pc, &mut trap, ci, i, im, false, TagMethod::Le)?,
};
finish_order_imm_jump(state, &cl, &mut pc, &mut trap, ci, i, cond);
}
OpCode::GtI => {
let ra = base + i.arg_a();
let im = i.arg_s_b() as i64;
let fast_cond = match &state.stack[ra.0 as usize].val {
LuaValue::Int(ia) => Some(*ia > im),
LuaValue::Float(fa) => Some(*fa > im as f64),
_ => None,
};
let cond = match fast_cond {
Some(cond) => cond,
None => order_imm_slow(state, ra, pc, &mut trap, ci, i, im, true, TagMethod::Lt)?,
};
finish_order_imm_jump(state, &cl, &mut pc, &mut trap, ci, i, cond);
}
OpCode::GeI => {
let ra = base + i.arg_a();
let im = i.arg_s_b() as i64;
let fast_cond = match &state.stack[ra.0 as usize].val {
LuaValue::Int(ia) => Some(*ia >= im),
LuaValue::Float(fa) => Some(*fa >= im as f64),
_ => None,
};
let cond = match fast_cond {
Some(cond) => cond,
None => order_imm_slow(state, ra, pc, &mut trap, ci, i, im, true, TagMethod::Le)?,
};
finish_order_imm_jump(state, &cl, &mut pc, &mut trap, ci, i, cond);
}
OpCode::Test => {
let ra_v = state.get_at(base + i.arg_a());
let cond = !matches!(ra_v, LuaValue::Nil | LuaValue::Bool(false));
if (cond as i32) != i.arg_k() {
pc += 1;
} else {
let next = state.proto_code(&cl, pc);
pc = (pc as i64 + next.arg_s_j() as i64 + 1) as u32;
trap = state.ci_trap(ci);
}
}
OpCode::TestSet => {
let ra = base + i.arg_a();
let rb_v = state.get_at(base + i.arg_b());
let falsy = matches!(rb_v, LuaValue::Nil | LuaValue::Bool(false));
if (falsy as i32) == i.arg_k() {
pc += 1;
} else {
state.set_at(ra, rb_v);
let next = state.proto_code(&cl, pc);
pc = (pc as i64 + next.arg_s_j() as i64 + 1) as u32;
trap = state.ci_trap(ci);
}
}
OpCode::Call => {
let ra = base + i.arg_a();
let b = i.arg_b();
let nresults = i.arg_c() as i32 - 1;
if b != 0 {
state.set_top(ra + b);
}
state.set_ci_savedpc(ci, pc); match state.precall(ra, nresults)? {
None => {
trap = state.ci_trap(ci); }
Some(new_ci) => {
ci = new_ci;
continue 'startfunc;
}
}
}
OpCode::TailCall => {
let ra = base + i.arg_a();
let b = i.arg_b();
let nparams1 = i.arg_c();
let delta = if nparams1 != 0 {
state.ci_nextraargs(ci) + nparams1 as i32
} else {
0
};
let top_b: i32 = if b != 0 {
state.set_top(ra + b);
b
} else {
state.top_idx() - ra
};
state.set_ci_savedpc(ci, pc);
if i.test_k() {
state.close_upvals_from_base(ci)?;
}
let n = state.pretailcall(ci, ra, top_b, delta)?;
if n < 0 {
continue 'startfunc;
} else {
state.ci_adjust_func(ci, delta);
state.poscall(ci, n as u32)?;
trap = state.ci_trap(ci);
break 'dispatch; }
}
OpCode::Return => {
let ra = base + i.arg_a();
let n_raw = i.arg_b() as i32 - 1;
let nparams1 = i.arg_c();
let n: u32 = if n_raw < 0 {
(state.top_idx() - ra) as u32
} else {
n_raw as u32
};
state.set_ci_savedpc(ci, pc);
if i.test_k() {
state.ci_nres_set(ci, n as i32);
let ci_top = state.ci_top(ci);
if state.top_idx().0 < ci_top.0 {
state.set_top(ci_top);
}
crate::func::close(state, base, crate::func::CLOSE_K_TOP, true)?;
trap = state.ci_trap(ci);
base = state.ci_base(ci); }
if nparams1 != 0 {
let nextraargs = state.ci_nextraargs(ci) as u32;
state.ci_adjust_func(ci, (nextraargs as i32 + nparams1 as i32));
}
state.set_top(ra + n as i32);
state.poscall(ci, n)?;
trap = state.ci_trap(ci);
break 'dispatch; }
OpCode::Return0 => {
if state.hookmask == 0 {
let ci_slot = ci.as_usize();
let nres = state.call_info[ci_slot].nresults as i32;
state.ci = state.call_info[ci_slot]
.previous
.expect("RETURN0: returning frame has no previous CallInfo");
state.top = base - 1;
for _ in 0..nres.max(0) {
state.push(LuaValue::Nil);
}
} else {
return0_hook(state, ci, base, i, pc, &mut trap)?;
}
break 'dispatch; }
OpCode::Return1 => {
if state.hookmask == 0 {
let ci_slot = ci.as_usize();
let nres = state.call_info[ci_slot].nresults as i32;
state.ci = state.call_info[ci_slot]
.previous
.expect("RETURN1: returning frame has no previous CallInfo");
if nres == 0 {
state.top = base - 1;
} else {
let ra = base + i.arg_a();
state.stack[(base - 1).0 as usize].val =
state.stack[ra.0 as usize].val; state.top = base;
for _ in 1..nres.max(0) {
state.push(LuaValue::Nil);
}
}
} else {
return1_hook(state, ci, base, i, pc, &mut trap)?;
}
break 'dispatch; }
OpCode::ForLoop => {
let ra = base + i.arg_a();
let ra_u = ra.0 as usize;
if let LuaValue::Int(step) = state.stack[ra_u + 2].val {
let count = match state.stack[ra_u + 1].val {
LuaValue::Int(c) => c as u64,
_ => 0,
};
if count > 0 {
let idx = match state.stack[ra_u].val {
LuaValue::Int(x) => x,
_ => 0,
};
state.stack[ra_u + 1].val = LuaValue::Int((count - 1) as i64);
let new_idx = intop_add(idx, step);
state.stack[ra_u].val = LuaValue::Int(new_idx);
state.stack[ra_u + 3].val = LuaValue::Int(new_idx);
pc = (pc as i64 - i.arg_bx() as i64) as u32;
}
} else if float_for_loop(state, ra) {
pc = (pc as i64 - i.arg_bx() as i64) as u32;
}
trap = state.ci_trap(ci);
}
OpCode::ForPrep => {
let ra = base + i.arg_a();
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
if forprep(state, ra)? {
pc = (pc as i64 + i.arg_bx() as i64 + 1) as u32;
}
}
OpCode::TForPrep => {
let ra = base + i.arg_a();
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
state.new_tbc_upval(ra + 3)?;
pc = (pc as i64 + i.arg_bx() as i64) as u32;
let tfc_i = state.proto_code(&cl, pc);
pc += 1;
debug_assert!(tfc_i.opcode() == OpCode::TForCall);
let tfc_ra = base + tfc_i.arg_a();
for k in 0..3u32 {
let v = state.get_at(tfc_ra + k as i32);
state.set_at(tfc_ra + 4 + k as i32, v);
}
state.set_top(tfc_ra + 4 + 3);
state.set_ci_savedpc(ci, pc);
state.call_at(tfc_ra + 4, tfc_i.arg_c() as i32)?;
trap = state.ci_trap(ci);
base = state.ci_base(ci); let tfl_i = state.proto_code(&cl, pc);
pc += 1;
debug_assert!(tfl_i.opcode() == OpCode::TForLoop);
let tfl_ra = base + tfl_i.arg_a();
if !matches!(state.get_at(tfl_ra + 4), LuaValue::Nil) {
let v = state.get_at(tfl_ra + 4);
state.set_at(tfl_ra + 2, v);
pc = (pc as i64 - tfl_i.arg_bx() as i64) as u32;
}
}
OpCode::TForCall => {
let ra = base + i.arg_a();
for k in 0..3u32 {
let v = state.get_at(ra + k as i32);
state.set_at(ra + 4 + k as i32, v);
}
state.set_top(ra + 4 + 3);
state.set_ci_savedpc(ci, pc);
state.call_at(ra + 4, i.arg_c() as i32)?;
trap = state.ci_trap(ci);
base = state.ci_base(ci); let tfl_i = state.proto_code(&cl, pc);
pc += 1;
debug_assert!(tfl_i.opcode() == OpCode::TForLoop);
let tfl_ra = base + tfl_i.arg_a();
if !matches!(state.get_at(tfl_ra + 4), LuaValue::Nil) {
let v = state.get_at(tfl_ra + 4);
state.set_at(tfl_ra + 2, v);
pc = (pc as i64 - tfl_i.arg_bx() as i64) as u32;
}
}
OpCode::TForLoop => {
let ra = base + i.arg_a();
if !matches!(state.get_at(ra + 4), LuaValue::Nil) {
let v = state.get_at(ra + 4);
state.set_at(ra + 2, v);
pc = (pc as i64 - i.arg_bx() as i64) as u32;
}
}
OpCode::SetList => {
let ra = base + i.arg_a();
let n_raw = i.arg_b();
let mut last = i.arg_c();
let t_val = state.get_at(ra);
let n: i32 = if n_raw == 0 {
state.top_idx() - ra - 1
} else {
state.set_top(state.ci_top(ci));
n_raw
};
last += n;
if i.test_k() {
let extra = state.proto_code(&cl, pc);
pc += 1;
const MAXARG_C: i32 = (1 << 8) - 1;
last += extra.arg_ax() * (MAXARG_C + 1);
}
state.table_ensure_array(&t_val, last as usize)?;
for k in (1..=n).rev() {
let val = state.get_at(ra + k as i32);
state.table_array_set(&t_val, (last - 1) as usize, val.clone())?;
last -= 1;
state.gc_barrier_back(&t_val, &val);
}
}
OpCode::Closure => {
let ra = base + i.arg_a();
let proto_idx = i.arg_bx() as usize;
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
push_closure(state, proto_idx, ci, base, ra)?;
state.set_ci_savedpc(ci, pc);
state.set_top(ra + 1);
state.gc_cond_step();
trap = state.ci_trap(ci);
}
OpCode::VarArg => {
let ra = base + i.arg_a();
let n = i.arg_c() as i32 - 1;
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
state.get_varargs(ci, ra, n)?;
trap = state.ci_trap(ci);
}
OpCode::VarArgPrep => {
let nparams = i.arg_a();
state.set_ci_savedpc(ci, pc);
state.adjust_varargs(ci, nparams, &cl)?;
trap = state.ci_trap(ci);
if trap {
state.hook_call(ci)?;
state.set_oldpc(1);
}
base = state.ci_base(ci);
}
OpCode::ExtraArg => {
debug_assert!(false, "OP_EXTRAARG executed directly");
}
#[allow(unreachable_patterns)]
_ => {
todo!("unrecognised opcode");
}
} }
if state.ci_is_fresh(ci) {
return Ok(());
} else {
ci = state.ci_previous(ci).expect("ci_previous: not fresh frame must have previous");
continue 'returning;
}
} } }
#[inline(always)]
fn number_value(v: LuaValue) -> Option<f64> {
match v {
LuaValue::Float(f) => Some(f),
LuaValue::Int(i) => Some(i as f64),
_ => None,
}
}
#[allow(dead_code)]
#[inline]
fn arith_op_aux_rr(
state: &mut LuaState,
ra: StackIdx,
v1: &LuaValue,
v2: &LuaValue,
pc: &mut u32,
iop: fn(i64, i64) -> i64,
fop: fn(f64, f64) -> f64,
) {
if let (LuaValue::Int(i1), LuaValue::Int(i2)) = (v1, v2) {
*pc += 1;
state.set_at(ra, LuaValue::Int(iop(*i1, *i2)));
} else {
arith_float_aux(state, ra, v1, v2, pc, fop);
}
}
#[allow(dead_code)]
#[inline]
fn arith_float_aux(
state: &mut LuaState,
ra: StackIdx,
v1: &LuaValue,
v2: &LuaValue,
pc: &mut u32,
fop: fn(f64, f64) -> f64,
) {
let n1 = match v1 {
LuaValue::Float(f) => Some(*f),
LuaValue::Int(i) => Some(*i as f64),
_ => None,
};
let n2 = match v2 {
LuaValue::Float(f) => Some(*f),
LuaValue::Int(i) => Some(*i as f64),
_ => None,
};
if let (Some(n1), Some(n2)) = (n1, n2) {
*pc += 1;
state.set_at(ra, LuaValue::Float(fop(n1, n2)));
}
}
#[allow(dead_code)]
#[inline]
fn arith_op_checked(
state: &mut LuaState,
ra: StackIdx,
v1: &LuaValue,
v2: &LuaValue,
pc: &mut u32,
iop: fn(i64, i64) -> Result<i64, LuaError>,
fop: fn(f64, f64) -> f64,
) -> Result<(), LuaError> {
if let (LuaValue::Int(i1), LuaValue::Int(i2)) = (v1, v2) {
*pc += 1;
let result = iop(*i1, *i2).map_err(|e| match e {
LuaError::Runtime(LuaValue::Str(s)) => {
crate::debug::prefixed_runtime_pub(state, s.as_bytes().to_vec())
}
other => other,
})?;
state.set_at(ra, LuaValue::Int(result));
} else {
arith_float_aux(state, ra, v1, v2, pc, fop);
}
Ok(())
}
#[allow(dead_code)]
#[inline]
fn bitwise_op_k(
state: &mut LuaState,
ra: StackIdx,
v1: &LuaValue,
v2: &LuaValue, pc: &mut u32,
op: fn(i64, i64) -> i64,
) {
let i2 = match v2 {
LuaValue::Int(i) => *i,
_ => return,
};
if let Some(i1) = to_integer_ns(v1, F2Imod::Eq) {
*pc += 1;
state.set_at(ra, LuaValue::Int(op(i1, i2)));
}
}
#[allow(dead_code)]
#[inline]
fn bitwise_op_rr(
state: &mut LuaState,
ra: StackIdx,
v1: &LuaValue,
v2: &LuaValue,
pc: &mut u32,
op: fn(i64, i64) -> i64,
) {
if let (Some(i1), Some(i2)) = (
to_integer_ns(v1, F2Imod::Eq),
to_integer_ns(v2, F2Imod::Eq),
) {
*pc += 1;
state.set_at(ra, LuaValue::Int(op(i1, i2)));
}
}
#[allow(dead_code)]
#[inline]
fn bitwise_shift_rr(
state: &mut LuaState,
ra: StackIdx,
v1: &LuaValue,
v2: &LuaValue,
pc: &mut u32,
right: bool,
) {
if let (Some(i1), Some(i2)) = (
to_integer_ns(v1, F2Imod::Eq),
to_integer_ns(v2, F2Imod::Eq),
) {
let y = if right { intop_sub(0, i2) } else { i2 };
*pc += 1;
state.set_at(ra, LuaValue::Int(shiftl(i1, y)));
}
}
#[cold]
#[inline(never)]
#[allow(clippy::too_many_arguments)]
fn order_imm_slow(
state: &mut LuaState,
ra: StackIdx,
pc: u32,
trap: &mut bool,
ci: CallInfoIdx,
i: Instruction,
im: i64,
inv: bool,
tm: TagMethod,
) -> Result<bool, LuaError> {
let ra_v = state.get_at(ra);
let isf = i.arg_c() != 0;
state.set_ci_savedpc(ci, pc);
state.set_top(state.ci_top(ci));
let r = state.call_order_i_tm(&ra_v, im, inv, isf, tm)?;
*trap = state.ci_trap(ci);
Ok(r)
}
#[inline(always)]
fn finish_order_imm_jump(
state: &mut LuaState,
cl: &lua_types::GcRef<lua_types::LuaLClosure>,
pc: &mut u32,
trap: &mut bool,
ci: CallInfoIdx,
i: Instruction,
cond: bool,
) {
if (cond as i32) != i.arg_k() {
*pc += 1;
} else {
let next = state.proto_code(&cl, *pc);
*pc = (*pc as i64 + next.arg_s_j() as i64 + 1) as u32;
*trap = state.ci_trap(ci);
}
}
#[cold]
#[inline(never)]
fn return0_hook(
state: &mut LuaState,
ci: CallInfoIdx,
base: StackIdx,
i: Instruction,
pc: u32,
trap: &mut bool,
) -> Result<(), LuaError> {
let ra = base + i.arg_a();
state.set_top(ra);
state.set_ci_savedpc(ci, pc);
state.poscall(ci, 0)?;
*trap = true;
Ok(())
}
#[cold]
#[inline(never)]
fn return1_hook(
state: &mut LuaState,
ci: CallInfoIdx,
base: StackIdx,
i: Instruction,
pc: u32,
trap: &mut bool,
) -> Result<(), LuaError> {
let ra = base + i.arg_a();
state.set_top(ra + 1);
state.set_ci_savedpc(ci, pc);
state.poscall(ci, 1)?;
*trap = true;
Ok(())
}