use crate::{
CallInfo, Chunk, LuaResult, LuaValue, TablePtr,
lua_value::{LUA_VNUMFLT, LUA_VNUMINT, udvalue_to_lua_value},
lua_vm::{
LuaError, LuaState, TmKind,
call_info::call_status::{
CIST_C, CIST_PENDING_FINISH, CIST_RECST, CIST_XPCALL, CIST_YCALL, CIST_YPCALL,
},
execute::{call::poscall, call_tm_res, concat::concat, metamethod::call_tm_res_into},
lua_limits::{EXTRA_STACK, MAXTAGLOOP},
},
};
pub fn buildhiddenargs(
lua_state: &mut LuaState,
ci: &mut CallInfo,
chunk: &Chunk,
totalargs: usize,
nfixparams: usize,
_nextra: usize,
) -> LuaResult<usize> {
let old_base = ci.base;
let func_pos = if old_base > 0 { old_base - 1 } else { 0 };
let new_func_pos = func_pos + totalargs + 1;
let new_base = new_func_pos + 1;
let new_needed_size = new_base + chunk.max_stack_size + EXTRA_STACK;
if new_needed_size > lua_state.stack_len() {
lua_state.grow_stack(new_needed_size)?;
}
let stack = lua_state.stack_mut();
unsafe { *stack.get_unchecked_mut(new_func_pos) = *stack.get_unchecked(func_pos) };
for i in 0..nfixparams {
unsafe { *stack.get_unchecked_mut(new_base + i) = *stack.get_unchecked(func_pos + 1 + i) };
unsafe {
psetnilvalue(&mut stack[func_pos + 1 + i] as *mut LuaValue);
}
}
ci.base = new_base;
ci.top = (new_base + chunk.max_stack_size) as u32;
ci.func_offset = (new_base - func_pos) as u32;
let new_call_info_top = new_base + chunk.max_stack_size;
lua_state.set_top(new_call_info_top)?;
Ok(new_base)
}
#[inline(always)]
pub unsafe fn pttisinteger(v: *const LuaValue) -> bool {
unsafe { (*v).ttisinteger() }
}
#[inline(always)]
pub fn ttisinteger(v: &LuaValue) -> bool {
v.ttisinteger()
}
#[inline(always)]
pub unsafe fn pttisfloat(v: *const LuaValue) -> bool {
unsafe { (*v).ttisfloat() }
}
#[inline(always)]
pub fn ttisfloat(v: &LuaValue) -> bool {
v.ttisfloat()
}
#[inline(always)]
pub fn ttisstring(v: &LuaValue) -> bool {
v.is_string()
}
#[inline(always)]
pub unsafe fn pivalue(v: *const LuaValue) -> i64 {
unsafe { (*v).ivalue() }
}
#[inline(always)]
pub fn ivalue(v: &LuaValue) -> i64 {
v.ivalue()
}
#[inline(always)]
pub fn chgivalue(v: &mut LuaValue, i: i64) {
v.value.i = i;
}
#[inline(always)]
pub unsafe fn pfltvalue(v: *const LuaValue) -> f64 {
unsafe { (*v).fltvalue() }
}
#[inline(always)]
pub fn fltvalue(v: &LuaValue) -> f64 {
v.fltvalue()
}
#[inline(always)]
pub fn chgfltvalue(v: &mut LuaValue, n: f64) {
v.value.n = n;
}
#[inline(always)]
pub unsafe fn psetivalue(v: *mut LuaValue, i: i64) {
unsafe {
*v = LuaValue::integer(i);
}
}
#[inline(always)]
pub fn setivalue(v: &mut LuaValue, i: i64) {
*v = LuaValue::integer(i);
}
#[inline(always)]
pub fn luai_numpow(a: f64, b: f64) -> f64 {
if b == 2.0 { a * a } else { a.powf(b) }
}
#[inline(always)]
pub unsafe fn psetfltvalue(v: *mut LuaValue, n: f64) {
unsafe {
*v = LuaValue::float(n);
}
}
#[inline(always)]
pub fn setfltvalue(v: &mut LuaValue, n: f64) {
*v = LuaValue::float(n);
}
#[inline(always)]
pub fn setbfvalue(v: &mut LuaValue) {
*v = LuaValue::boolean(false);
}
#[inline(always)]
pub fn setbtvalue(v: &mut LuaValue) {
*v = LuaValue::boolean(true);
}
#[inline(always)]
pub unsafe fn psetnilvalue(v: *mut LuaValue) {
unsafe {
*v = LuaValue::nil();
}
}
#[inline(always)]
pub fn setnilvalue(v: &mut LuaValue) {
*v = LuaValue::nil();
}
#[inline(always)]
pub fn setobjs2s(l: &mut LuaState, a: usize, b: usize) {
let stack = l.stack_mut();
unsafe {
*stack.get_unchecked_mut(a) = *stack.get_unchecked(b);
}
}
#[inline(always)]
pub fn setobj2s(l: &mut LuaState, a: usize, b: &LuaValue) {
let stack = l.stack_mut();
unsafe {
*stack.get_unchecked_mut(a) = *b;
}
}
#[inline(always)]
pub fn lua_shiftl(x: i64, y: i64) -> i64 {
if y < 0 {
if y <= -64 {
0
} else {
((x as u64) >> ((-y) as u32)) as i64
}
} else {
if y >= 64 {
0
} else {
((x as u64) << (y as u32)) as i64
}
}
}
#[inline(always)]
pub fn lua_shiftr(x: i64, y: i64) -> i64 {
lua_shiftl(x, y.wrapping_neg())
}
#[inline(always)]
pub fn lua_idiv(a: i64, b: i64) -> i64 {
if b == -1 {
return a.wrapping_neg();
}
let q = a / b;
if (a ^ b) < 0 && a % b != 0 {
q.wrapping_sub(1)
} else {
q
}
}
#[inline(always)]
pub fn lua_imod(a: i64, b: i64) -> i64 {
if b == -1 {
return 0;
}
let m = a % b;
if m != 0 && (m ^ b) < 0 {
m.wrapping_add(b)
} else {
m
}
}
#[inline(always)]
pub fn lua_fmod(a: f64, b: f64) -> f64 {
let mut m = a % b; if m != 0.0 && ((m > 0.0) != (b > 0.0)) {
m += b;
}
m
}
#[inline(always)]
pub unsafe fn ptointegerns(v: *const LuaValue, out: *mut i64) -> bool {
unsafe {
if pttisinteger(v) {
*out = pivalue(v);
true
} else if pttisfloat(v) {
let f = pfltvalue(v);
if f >= (i64::MIN as f64) && f < -(i64::MIN as f64) && f == (f as i64 as f64) {
*out = f as i64;
true
} else {
false
}
} else {
false
}
}
}
#[inline(always)]
pub fn tointegerns(v: &LuaValue, out: &mut i64) -> bool {
unsafe { ptointegerns(v as *const LuaValue, out as *mut i64) }
}
pub fn tonumber(v: &LuaValue, out: &mut f64) -> bool {
if tonumberns(v, out) {
return true;
}
if v.is_string() {
let result = crate::stdlib::basic::parse_number::parse_lua_number(v.as_str().unwrap_or(""));
if result.is_float() {
*out = unsafe { result.value.n };
return true;
} else if result.is_integer() {
*out = unsafe { result.value.i } as f64;
return true;
}
}
false
}
#[inline(always)]
pub unsafe fn ptonumberns(v: *const LuaValue, out: *mut f64) -> bool {
unsafe {
if pttisfloat(v) {
*out = pfltvalue(v);
true
} else if pttisinteger(v) {
*out = pivalue(v) as f64;
true
} else {
false
}
}
}
#[inline(always)]
pub fn tonumberns(v: &LuaValue, out: &mut f64) -> bool {
unsafe { ptonumberns(v as *const LuaValue, out as *mut f64) }
}
#[inline(always)]
pub fn tointeger(v: &LuaValue, out: &mut i64) -> bool {
if v.tt() == LUA_VNUMINT {
unsafe {
*out = v.value.i;
}
true
} else if v.tt() == LUA_VNUMFLT {
let f = unsafe { v.value.n };
if f >= (i64::MIN as f64) && f < -(i64::MIN as f64) && f == (f as i64 as f64) {
*out = f as i64;
true
} else {
false
}
} else {
false
}
}
fn tointeger_mode(v: &LuaValue, mode: i32) -> Option<i64> {
if v.tt() == LUA_VNUMINT {
return Some(unsafe { v.value.i });
}
let f = if v.tt() == LUA_VNUMFLT {
unsafe { v.value.n }
} else if v.is_string() {
let result = crate::stdlib::basic::parse_number::parse_lua_number(v.as_str().unwrap_or(""));
if result.is_float() {
unsafe { result.value.n }
} else if result.is_integer() {
return Some(unsafe { result.value.i });
} else {
return None;
}
} else {
return None;
};
let rounded = match mode {
1 => f.floor(),
2 => f.ceil(),
_ => f, };
if rounded.is_nan() {
return None;
}
if mode == 0 && rounded != f {
return None;
}
if rounded >= (i64::MIN as f64) && rounded < -(i64::MIN as f64) {
Some(rounded as i64)
} else {
None
}
}
fn finishget_inner(
lua_state: &mut LuaState,
obj: &LuaValue,
key: &LuaValue,
skip_first_raw_lookup: bool,
) -> LuaResult<Option<LuaValue>> {
let mut t = *obj;
let mut skip_raw_lookup = skip_first_raw_lookup;
for _ in 0..MAXTAGLOOP {
let tm = if let Some(table) = t.as_table_mut() {
if !skip_raw_lookup {
if let Some(val) = table.raw_get(key) {
return Ok(Some(val));
}
} else {
skip_raw_lookup = false;
}
match get_metamethod_from_meta_ptr(lua_state, table.meta_ptr(), TmKind::Index) {
Some(v) => v,
None => return Ok(None),
}
} else {
if t.ttisfulluserdata()
&& let Some(ud) = t.as_userdata_mut()
{
if let Some(key_str) = key.as_str()
&& let Some(udv) = ud.get_trait().get_field(key_str)
{
let result = crate::lua_value::udvalue_to_lua_value(lua_state, udv)?;
return Ok(Some(result));
}
}
match get_metamethod_event(lua_state, &t, TmKind::Index) {
Some(tm) => tm,
None => {
return Err(crate::stdlib::debug::typeerror(lua_state, &t, "index"));
}
}
};
if tm.is_function() {
let result = call_tm_res(lua_state, tm, t, *key)?;
return Ok(Some(result));
}
t = tm;
if let Some(table) = t.as_table() {
let value = if key.ttisinteger() {
table.impl_table.fast_geti(key.ivalue())
} else if key.is_short_string() {
table.impl_table.get_shortstr_fast(key)
} else {
table.raw_get(key)
};
if let Some(value) = value {
return Ok(Some(value));
}
skip_raw_lookup = true;
}
}
Err(lua_state.error("'__index' chain too long; possible loop".to_string()))
}
pub fn finishget(
lua_state: &mut LuaState,
obj: &LuaValue,
key: &LuaValue,
) -> LuaResult<Option<LuaValue>> {
finishget_inner(lua_state, obj, key, false)
}
#[inline]
fn get_metamethod_from_metatable(
lua_state: &mut LuaState,
metatable: LuaValue,
tm_kind: TmKind,
) -> Option<LuaValue> {
metatable
.as_table_ptr()
.and_then(|meta_ptr| get_metamethod_from_meta_ptr(lua_state, meta_ptr, tm_kind))
}
#[inline]
pub(crate) fn get_metamethod_from_meta_ptr(
lua_state: &mut LuaState,
meta_ptr: TablePtr,
tm_kind: TmKind,
) -> Option<LuaValue> {
if meta_ptr.is_null() {
return None;
}
let mt = unsafe { &mut (*meta_ptr.as_mut_ptr()).data };
let tm_idx = tm_kind as u8;
if mt.no_tm(tm_idx) {
return None;
}
let vm = lua_state.vm_mut();
let event_key = vm.const_strings.get_tm_value(tm_kind);
let result = mt.impl_table.get_shortstr_fast(&event_key);
if result.is_none() {
mt.set_tm_absent(tm_idx);
}
result
}
fn finishset_inner(
lua_state: &mut LuaState,
obj: &LuaValue,
key: &LuaValue,
value: LuaValue,
skip_existing_check: bool,
) -> LuaResult<bool> {
if key.is_nil() {
return Err(lua_state.error("table index is nil".to_string()));
}
if key.ttisfloat()
&& let Some(f) = key.as_float()
&& f.is_nan()
{
return Err(lua_state.error("table index is NaN".to_string()));
}
let mut t = *obj;
let mut skip_existing = skip_existing_check;
for _ in 0..MAXTAGLOOP {
if let Some(table) = t.as_table_mut() {
let tm_val =
get_metamethod_from_meta_ptr(lua_state, table.meta_ptr(), TmKind::NewIndex);
if tm_val.is_none() {
lua_state.raw_set(&t, *key, value);
return Ok(true);
}
if !skip_existing {
if let Some(existing) = table.raw_get(key)
&& !existing.is_nil()
{
lua_state.raw_set(&t, *key, value);
return Ok(true);
}
} else {
skip_existing = false;
}
if let Some(tm) = tm_val {
if tm.is_function() {
use crate::lua_vm::execute;
execute::metamethod::call_tm(lua_state, tm, t, *key, value)?;
return Ok(true);
}
t = tm;
continue;
}
} else {
if t.ttisfulluserdata()
&& let Some(ud) = t.as_userdata_mut()
&& let Some(key_str) = key.as_str()
{
let udv = crate::lua_value::lua_value_to_udvalue(&value);
match ud.get_trait_mut().set_field(key_str, udv) {
Some(Ok(())) => return Ok(true),
Some(Err(msg)) => {
return Err(lua_state.error(msg));
}
None => {} }
}
if let Some(tm) = get_metamethod_event(lua_state, &t, TmKind::NewIndex) {
if tm.is_function() {
use crate::lua_vm::execute;
execute::metamethod::call_tm(lua_state, tm, t, *key, value)?;
return Ok(true);
}
t = tm;
continue;
}
return Err(crate::stdlib::debug::typeerror(lua_state, &t, "index"));
}
}
Err(lua_state.error("'__newindex' chain too long; possible loop".to_string()))
}
pub fn finishset(
lua_state: &mut LuaState,
obj: &LuaValue,
key: &LuaValue,
value: LuaValue,
) -> LuaResult<bool> {
finishset_inner(lua_state, obj, key, value, false)
}
pub fn finishset_known_miss(
lua_state: &mut LuaState,
obj: &LuaValue,
key: &LuaValue,
value: LuaValue,
) -> LuaResult<bool> {
finishset_inner(lua_state, obj, key, value, true)
}
pub fn get_metamethod_event(
lua_state: &mut LuaState,
value: &LuaValue,
tm_kind: TmKind,
) -> Option<LuaValue> {
let mt = get_metatable(lua_state, value)?;
get_metamethod_from_metatable(lua_state, mt, tm_kind)
}
pub fn get_binop_metamethod(
lua_state: &mut LuaState,
v1: &LuaValue,
v2: &LuaValue,
tm_kind: TmKind,
) -> Option<LuaValue> {
if let Some(mt) = get_metatable(lua_state, v1)
&& let Some(mm) = get_metamethod_from_metatable(lua_state, mt, tm_kind)
{
return Some(mm);
}
if let Some(mt) = get_metatable(lua_state, v2)
&& let Some(mm) = get_metamethod_from_metatable(lua_state, mt, tm_kind)
{
return Some(mm);
}
None
}
pub fn get_metatable(lua_state: &mut LuaState, value: &LuaValue) -> Option<LuaValue> {
if let Some(table) = value.as_table_mut() {
return table.get_metatable();
} else if let Some(ud) = value.as_userdata_mut() {
return ud.get_metatable();
}
lua_state.vm_mut().get_basic_metatable(value.kind())
}
#[cold]
#[inline(never)]
fn finish_c_frame(lua_state: &mut LuaState, ci: &mut CallInfo) -> LuaResult<()> {
let pcall_func_pos = ci.base - ci.func_offset as usize;
let nresults = ci.nresults();
let has_recst = ci.call_status & CIST_RECST != 0;
let is_xpcall = ci.call_status & CIST_XPCALL != 0;
if ci.call_status & CIST_YPCALL != 0 {
if has_recst {
let handler = if is_xpcall {
lua_state.stack_get(pcall_func_pos).unwrap_or_default()
} else {
LuaValue::nil()
};
let error_val = std::mem::take(&mut lua_state.error_object);
lua_state.clear_error();
let close_level = pcall_func_pos + 1;
let close_result = lua_state.close_tbc_with_error(close_level, error_val);
match close_result {
Ok(()) => {
let final_err = std::mem::take(&mut lua_state.error_object);
let result_err = if !final_err.is_nil() {
final_err
} else {
error_val
};
lua_state.clear_error();
let result_err = if is_xpcall {
lua_state.nny += 1;
let handler_result = lua_state.pcall(handler, vec![result_err]);
lua_state.nny -= 1;
match handler_result {
Ok((true, results)) => {
results.into_iter().next().unwrap_or(LuaValue::nil())
}
_ => lua_state.create_string("error in error handling")?,
}
} else {
result_err
};
lua_state.stack_set(pcall_func_pos, LuaValue::boolean(false))?;
lua_state.stack_set(pcall_func_pos + 1, result_err)?;
let n = 2;
lua_state.pop_frame();
let final_n = if nresults == -1 { n } else { nresults as usize };
let new_top = pcall_func_pos + final_n;
if nresults >= 0 {
let wanted = nresults as usize;
for i in n..wanted {
lua_state.stack_set(pcall_func_pos + i, LuaValue::nil())?;
}
}
lua_state.set_top_raw(new_top);
if lua_state.call_depth() > 0 {
let ci_idx = lua_state.call_depth() - 1;
if nresults == -1 {
let ci_top = lua_state.get_call_info(ci_idx).top as usize;
if ci_top < new_top {
lua_state.get_call_info_mut(ci_idx).top = new_top as u32;
}
} else {
let frame_top = lua_state.get_call_info(ci_idx).top as usize;
lua_state.set_top_raw(frame_top);
}
}
Ok(())
}
Err(LuaError::Yield) => {
let cascaded = std::mem::take(&mut lua_state.error_object);
lua_state.error_object = if !cascaded.is_nil() {
cascaded
} else {
error_val
};
Err(LuaError::Yield)
}
Err(e) => {
Err(e)
}
}
} else {
let stack_top = lua_state.get_top();
let body_results_start = pcall_func_pos + 1;
let body_nres = stack_top.saturating_sub(body_results_start);
lua_state.stack_set(pcall_func_pos, LuaValue::boolean(true))?;
let n = 1 + body_nres;
lua_state.pop_frame();
let final_n = if nresults == -1 { n } else { nresults as usize };
let new_top = pcall_func_pos + final_n;
if nresults >= 0 {
let wanted = nresults as usize;
for i in n..wanted {
lua_state.stack_set(pcall_func_pos + i, LuaValue::nil())?;
}
}
lua_state.set_top_raw(new_top);
if lua_state.call_depth() > 0 {
let ci_idx = lua_state.call_depth() - 1;
if nresults == -1 {
let ci_top = lua_state.get_call_info(ci_idx).top as usize;
if ci_top < new_top {
lua_state.get_call_info_mut(ci_idx).top = new_top as u32;
}
} else {
let frame_top = lua_state.get_call_info(ci_idx).top as usize;
lua_state.set_top_raw(frame_top);
}
}
Ok(())
}
} else if ci.call_status & CIST_YCALL != 0 {
let stack_top = lua_state.get_top();
let body_results_start = pcall_func_pos + 1;
let body_nres = stack_top.saturating_sub(body_results_start);
for i in 0..body_nres {
let val = lua_state
.stack_get(body_results_start + i)
.unwrap_or_default();
lua_state.stack_set(pcall_func_pos + i, val)?;
}
let n = body_nres;
lua_state.pop_frame();
let final_n = if nresults == -1 { n } else { nresults as usize };
let new_top = pcall_func_pos + final_n;
if nresults >= 0 {
let wanted = nresults as usize;
for i in n..wanted {
lua_state.stack_set(pcall_func_pos + i, LuaValue::nil())?;
}
}
lua_state.set_top_raw(new_top);
if lua_state.call_depth() > 0 {
let ci_idx = lua_state.call_depth() - 1;
if nresults == -1 {
let ci_top = lua_state.get_call_info(ci_idx).top as usize;
if ci_top < new_top {
lua_state.get_call_info_mut(ci_idx).top = new_top as u32;
}
} else {
let frame_top = lua_state.get_call_info(ci_idx).top as usize;
lua_state.set_top_raw(frame_top);
}
}
Ok(())
} else {
lua_state.pop_frame();
Ok(())
}
}
#[cold]
#[inline(never)]
pub fn handle_pending_ops(lua_state: &mut LuaState, ci: &mut CallInfo) -> LuaResult<bool> {
if ci.call_status & CIST_C != 0 {
finish_c_frame(lua_state, ci)?;
return Ok(true); }
let saved_pc = ci.pc as usize;
let base_tmp = ci.base;
let _nresults = ci.nresults();
if !ci.chunk_ptr.is_null() {
let chunk = unsafe { &*ci.chunk_ptr };
let code = &chunk.code;
if saved_pc > 0 && saved_pc <= code.len() {
let interrupted_instr = code[saved_pc - 1];
let op = interrupted_instr.get_opcode();
use crate::lua_vm::OpCode;
match op {
OpCode::MmBin | OpCode::MmBinI | OpCode::MmBinK => {
let top = lua_state.get_top();
if top > 0 && saved_pc >= 2 {
let arith_instr = code[saved_pc - 2];
let dest = base_tmp + arith_instr.get_a() as usize;
let result = lua_state.stack_mut()[top - 1];
lua_state.stack_mut()[dest] = result;
lua_state.set_top_raw(top - 1);
}
}
OpCode::Unm
| OpCode::BNot
| OpCode::Len
| OpCode::GetTabUp
| OpCode::GetTable
| OpCode::GetI
| OpCode::GetField
| OpCode::Self_ => {
let top = lua_state.get_top();
if top > 0 {
let dest = base_tmp + interrupted_instr.get_a() as usize;
let result = lua_state.stack_mut()[top - 1];
lua_state.stack_mut()[dest] = result;
lua_state.set_top_raw(top - 1);
}
}
OpCode::Lt
| OpCode::Le
| OpCode::LtI
| OpCode::LeI
| OpCode::GtI
| OpCode::GeI
| OpCode::Eq => {
let top = lua_state.get_top();
if top > 0 {
let res_val = lua_state.stack_mut()[top - 1];
let res = !res_val.is_nil() && !(res_val == LuaValue::boolean(false));
lua_state.set_top_raw(top - 1);
let k = interrupted_instr.get_k();
if res != k {
ci.pc += 1;
}
}
}
OpCode::Concat => {
let top = lua_state.get_top();
if top > 0 {
let a = interrupted_instr.get_a() as usize;
let n = interrupted_instr.get_b() as usize;
let concat_top = base_tmp + a + n;
let result = lua_state.stack_mut()[top - 1];
lua_state.stack_mut()[concat_top - 2] = result;
let total = concat_top - 1 - (base_tmp + a);
if total > 1 {
lua_state.set_top_raw(concat_top - 1);
concat(lua_state, total)?;
}
}
}
_ => {
}
}
}
}
let ci_top = ci.top as usize;
let current_top = lua_state.get_top();
if current_top < ci_top {
lua_state.set_top_raw(ci_top);
}
ci.set_pending_finish_get(-1);
ci.call_status &= !CIST_PENDING_FINISH;
Ok(false) }
pub fn objlen(l: &mut LuaState, result_reg: usize, value: LuaValue) -> LuaResult<()> {
if let Some(bytes) = value.as_bytes() {
let len = bytes.len();
setivalue(
unsafe { l.stack_mut().get_unchecked_mut(result_reg) },
len as i64,
);
return Ok(());
} else if value.ttistable() {
if let Some(tm) = get_metamethod_event(l, &value, TmKind::Len) {
return call_tm_res_into(l, tm, value, value, result_reg);
}
let len = value.as_table().unwrap().len();
setivalue(
unsafe { l.stack_mut().get_unchecked_mut(result_reg) },
len as i64,
);
return Ok(());
} else {
if value.ttisfulluserdata()
&& let Some(ud) = value.as_userdata_mut()
&& let Some(udv) = ud.get_trait().lua_len()
{
let result = udvalue_to_lua_value(l, udv)?;
setivalue(
unsafe { l.stack_mut().get_unchecked_mut(result_reg) },
result.as_integer().unwrap_or(0),
);
return Ok(());
}
}
let tm = get_metamethod_event(l, &value, TmKind::Len);
if let Some(tm) = tm {
call_tm_res_into(l, tm, value, value, result_reg)?;
} else {
return Err(crate::stdlib::debug::typeerror(l, &value, "get length of"));
}
Ok(())
}
pub fn equalobj(lua_state: &mut LuaState, t1: LuaValue, t2: LuaValue) -> LuaResult<bool> {
if t1 == t2 {
return Ok(true);
}
if t1.tt() != t2.tt() {
return Ok(false);
}
if t1.ttisfulluserdata() {
if let (Some(u_ptr1), Some(u_ptr2)) = (t1.as_userdata_ptr(), t2.as_userdata_ptr())
&& u_ptr1 == u_ptr2
{
return Ok(true);
}
if let Some(ud1) = t1.as_userdata_mut()
&& let Some(ud2) = t2.as_userdata_mut()
&& let Some(result) = ud1.get_trait().lua_eq(ud2.get_trait())
{
return Ok(result);
}
let tm = get_binop_metamethod(lua_state, &t1, &t2, TmKind::Eq);
if let Some(metamethod) = tm {
let result = call_tm_res(lua_state, metamethod, t1, t2)?;
return Ok(!result.is_falsy());
} else {
return Ok(false);
}
}
if t1.ttistable() {
if let (Some(t_ptr1), Some(t_ptr2)) = (t1.as_table_ptr(), t2.as_table_ptr())
&& t_ptr1 == t_ptr2
{
return Ok(true);
}
let tm = get_binop_metamethod(lua_state, &t1, &t2, TmKind::Eq);
if let Some(metamethod) = tm {
let result = call_tm_res(lua_state, metamethod, t1, t2)?;
return Ok(!result.is_falsy());
} else {
return Ok(false);
}
}
if t1.ttiscfunction() {
return Ok(unsafe { t1.value.f == t2.value.f });
}
if let (Some(f_ptr1), Some(f_ptr2)) = (t1.as_function_ptr(), t2.as_function_ptr()) {
return Ok(f_ptr1 == f_ptr2);
}
Ok(false)
}
pub fn forprep(lua_state: &mut LuaState, ra_pos: usize) -> LuaResult<bool> {
let stack = lua_state.stack();
let init_pos = ra_pos;
let limit_pos = ra_pos + 1;
let step_pos = ra_pos + 2;
if ttisinteger(unsafe { stack.get_unchecked(init_pos) })
&& ttisinteger(unsafe { stack.get_unchecked(step_pos) })
{
let init = ivalue(unsafe { stack.get_unchecked(init_pos) });
let step = ivalue(unsafe { stack.get_unchecked(step_pos) });
if step == 0 {
return Err(lua_state.error("'for' step is zero".to_string()));
}
let limit_val = *unsafe { stack.get_unchecked(limit_pos) };
let (limit, should_skip) = for_limit(lua_state, limit_val, init, step)?;
if should_skip {
return Ok(true);
}
if step > 0 {
if init > limit {
return Ok(true); }
} else if limit > init {
return Ok(true); }
{
let count = if step > 0 {
((limit as u64).wrapping_sub(init as u64)) / (step as u64)
} else {
let step_abs = if step == i64::MIN {
i64::MAX as u64 + 1
} else {
(-step) as u64
};
((init as u64).wrapping_sub(limit as u64)) / step_abs
};
let stack = lua_state.stack_mut();
chgivalue(unsafe { stack.get_unchecked_mut(ra_pos) }, count as i64);
setivalue(unsafe { stack.get_unchecked_mut(ra_pos + 1) }, step);
chgivalue(unsafe { stack.get_unchecked_mut(ra_pos + 2) }, init);
}
} else {
let mut init = 0.0;
let mut limit = 0.0;
let mut step = 0.0;
let init_val = stack[init_pos];
let limit_val = stack[limit_pos];
let step_val = stack[step_pos];
if !tonumber(&limit_val, &mut limit) {
let t = crate::stdlib::debug::objtypename(lua_state, &limit_val);
return Err(lua_state.error(format!("bad 'for' limit (number expected, got {})", t)));
}
if !tonumber(&step_val, &mut step) {
let t = crate::stdlib::debug::objtypename(lua_state, &step_val);
return Err(lua_state.error(format!("bad 'for' step (number expected, got {})", t)));
}
if !tonumber(&init_val, &mut init) {
let t = crate::stdlib::debug::objtypename(lua_state, &init_val);
return Err(lua_state.error(format!(
"bad 'for' initial value (number expected, got {})",
t
)));
}
if step == 0.0 {
return Err(lua_state.error("'for' step is zero".to_string()));
}
let should_skip = if step > 0.0 {
limit < init
} else {
init < limit
};
let stack = lua_state.stack_mut();
if should_skip {
return Ok(true);
} else {
setfltvalue(&mut stack[ra_pos], limit);
setfltvalue(&mut stack[ra_pos + 1], step);
setfltvalue(&mut stack[ra_pos + 2], init);
}
}
Ok(false)
}
fn for_limit(
lua_state: &mut LuaState,
limit_val: LuaValue,
init: i64,
step: i64,
) -> LuaResult<(i64, bool)> {
let mode = if step < 0 { 2 } else { 1 }; if let Some(lim) = tointeger_mode(&limit_val, mode) {
let skip = if step > 0 { init > lim } else { init < lim };
Ok((lim, skip))
} else {
let mut flimit = 0.0;
if !tonumber(&limit_val, &mut flimit) {
return Err(error_for_bad_limit(lua_state, &limit_val));
}
if 0.0 < flimit {
if step < 0 {
return Ok((i64::MAX, true)); }
Ok((i64::MAX, false)) } else {
if step > 0 {
return Ok((i64::MIN, true)); }
Ok((i64::MIN, false)) }
}
}
#[cold]
#[inline(never)]
pub fn float_for_loop(lua_state: &mut LuaState, ra_pos: usize) -> bool {
let stack = lua_state.stack_mut();
let step = fltvalue(unsafe { stack.get_unchecked(ra_pos + 1) });
let limit = fltvalue(unsafe { stack.get_unchecked(ra_pos) });
let mut idx = fltvalue(unsafe { stack.get_unchecked(ra_pos + 2) });
idx += step;
if (step > 0.0 && idx <= limit) || (step <= 0.0 && idx >= limit) {
chgfltvalue(unsafe { stack.get_unchecked_mut(ra_pos + 2) }, idx);
return true;
}
false
}
#[cold]
#[inline(never)]
fn error_for_bad_limit(lua_state: &mut LuaState, limit_val: &LuaValue) -> LuaError {
let t = crate::stdlib::debug::objtypename(lua_state, limit_val);
lua_state.error(format!("bad 'for' limit (number expected, got {})", t))
}
#[cold]
#[inline(never)]
pub fn error_div_by_zero(lua_state: &mut LuaState) -> LuaError {
lua_state.error("attempt to divide by zero".to_string())
}
#[cold]
#[inline(never)]
pub fn error_mod_by_zero(lua_state: &mut LuaState) -> LuaError {
lua_state.error("attempt to perform 'n%0'".to_string())
}
#[cold]
#[inline(never)]
pub fn error_global(lua_state: &mut LuaState, global_name: &str) -> LuaError {
lua_state.error(format!("global '{}' already defined", global_name))
}
#[cold]
#[inline(never)]
pub fn order_tm_fallback(
lua_state: &mut LuaState,
ci: &mut CallInfo,
va: LuaValue,
vb: LuaValue,
tm: TmKind,
) -> LuaResult<bool> {
use crate::lua_vm::execute::metamethod::try_comp_tm;
match try_comp_tm(lua_state, va, vb, tm) {
Ok(Some(result)) => Ok(result),
Ok(None) => Err(crate::stdlib::debug::ordererror(lua_state, &va, &vb)),
Err(LuaError::Yield) => {
ci.call_status |= CIST_PENDING_FINISH;
Err(LuaError::Yield)
}
Err(e) => Err(e),
}
}
#[cold]
#[inline(never)]
pub fn bin_tm_fallback(
lua_state: &mut LuaState,
ci: &mut CallInfo,
ra: LuaValue,
rb: LuaValue,
result_reg: u32,
a_reg: u32,
b_reg: u32,
tm: TmKind,
) -> LuaResult<()> {
use crate::lua_vm::execute::metamethod::try_bin_tm;
match try_bin_tm(lua_state, ra, rb, result_reg, a_reg, b_reg, tm) {
Ok(_) => Ok(()),
Err(LuaError::Yield) => {
ci.set_pending_finish_get(result_reg as i32);
ci.call_status |= CIST_PENDING_FINISH;
Err(LuaError::Yield)
}
Err(e) => Err(e),
}
}
#[cold]
#[inline(never)]
pub fn unary_tm_fallback(
lua_state: &mut LuaState,
ci: &mut CallInfo,
rb: LuaValue,
result_reg: usize,
tm: TmKind,
) -> LuaResult<()> {
use crate::lua_vm::execute::metamethod::try_unary_tm;
match try_unary_tm(lua_state, rb, result_reg, tm) {
Ok(_) => Ok(()),
Err(LuaError::Yield) => {
ci.set_pending_finish_get(result_reg as i32);
ci.call_status |= CIST_PENDING_FINISH;
Err(LuaError::Yield)
}
Err(e) => Err(e),
}
}
#[inline(never)]
pub fn finishget_fallback(
lua_state: &mut LuaState,
ci: &mut CallInfo,
obj: &LuaValue,
key: &LuaValue,
dest_reg: usize,
) -> LuaResult<()> {
match finishget_to_reg_known_miss(lua_state, obj, key, dest_reg) {
Ok(()) => Ok(()),
Err(LuaError::Yield) => {
ci.set_pending_finish_get(dest_reg as i32);
ci.call_status |= CIST_PENDING_FINISH;
Err(LuaError::Yield)
}
Err(e) => Err(e),
}
}
#[inline(never)]
pub fn self_shortstr_index_chain_fast(
lua_state: &mut LuaState,
obj: &LuaValue,
key: &LuaValue,
dest_reg: usize,
) -> bool {
const TM_INDEX_BIT: u8 = TmKind::Index as u8;
debug_assert!(key.is_short_string());
let event_key = lua_state.vm_mut().const_strings.get_tm_value(TmKind::Index);
let mut current = *obj;
for _ in 0..MAXTAGLOOP {
let Some(table) = current.as_table_mut() else {
return false;
};
if let Some(value) = table.impl_table.get_shortstr_fast(key) {
setobj2s(lua_state, dest_reg, &value);
return true;
}
let meta = table.meta_ptr();
if meta.is_null() {
return false;
}
let mt = unsafe { &mut (*meta.as_mut_ptr()).data };
if mt.no_tm(TM_INDEX_BIT) {
return false;
}
let Some(tm) = mt.impl_table.get_shortstr_fast(&event_key) else {
mt.set_tm_absent(TM_INDEX_BIT);
return false;
};
if tm.is_function() || !tm.is_table() {
return false;
}
current = tm;
}
false
}
fn finishget_to_reg_inner(
lua_state: &mut LuaState,
obj: &LuaValue,
key: &LuaValue,
dest_reg: usize,
skip_first_raw_lookup: bool,
) -> LuaResult<()> {
const TM_INDEX_BIT: u8 = TmKind::Index as u8;
let mut t = *obj;
let mut skip_raw_lookup = skip_first_raw_lookup;
for _ in 0..MAXTAGLOOP {
let tm = if let Some(table) = t.as_table_mut() {
if !skip_raw_lookup {
if let Some(val) = table.raw_get(key) {
setobj2s(lua_state, dest_reg, &val);
return Ok(());
}
} else {
skip_raw_lookup = false;
}
let meta = table.meta_ptr();
if meta.is_null() {
setobj2s(lua_state, dest_reg, &LuaValue::nil());
return Ok(());
}
let mt = unsafe { &mut (*meta.as_mut_ptr()).data };
if mt.no_tm(TM_INDEX_BIT) {
setobj2s(lua_state, dest_reg, &LuaValue::nil());
return Ok(());
}
let vm = lua_state.vm_mut();
let event_key = vm.const_strings.get_tm_value(TmKind::Index);
match mt.impl_table.get_shortstr_fast(&event_key) {
Some(v) => v,
None => {
mt.set_tm_absent(TM_INDEX_BIT);
setobj2s(lua_state, dest_reg, &LuaValue::nil());
return Ok(());
}
}
} else {
if t.ttisfulluserdata()
&& let Some(ud) = t.as_userdata_mut()
&& let Some(key_str) = key.as_str()
&& let Some(udv) = ud.get_trait().get_field(key_str)
{
let result = crate::lua_value::udvalue_to_lua_value(lua_state, udv)?;
setobj2s(lua_state, dest_reg, &result);
return Ok(());
}
match get_metamethod_event(lua_state, &t, TmKind::Index) {
Some(tm) => tm,
None => {
return Err(crate::stdlib::debug::typeerror(lua_state, &t, "index"));
}
}
};
if tm.is_function() {
return call_tm_res_into(lua_state, tm, t, *key, dest_reg);
}
t = tm;
if let Some(table) = t.as_table() {
let value = if key.ttisinteger() {
table.impl_table.fast_geti(key.ivalue())
} else if key.is_short_string() {
table.impl_table.get_shortstr_fast(key)
} else {
table.raw_get(key)
};
if let Some(value) = value {
setobj2s(lua_state, dest_reg, &value);
return Ok(());
}
skip_raw_lookup = true;
}
}
Err(lua_state.error("'__index' chain too long; possible loop".to_string()))
}
fn finishget_to_reg_known_miss(
lua_state: &mut LuaState,
obj: &LuaValue,
key: &LuaValue,
dest_reg: usize,
) -> LuaResult<()> {
finishget_to_reg_inner(lua_state, obj, key, dest_reg, true)
}
#[inline(never)]
pub fn finishset_fallback(
lua_state: &mut LuaState,
ci: &mut CallInfo,
obj: &LuaValue,
key: &LuaValue,
value: LuaValue,
) -> LuaResult<()> {
match finishset(lua_state, obj, key, value) {
Ok(_) => Ok(()),
Err(LuaError::Yield) => {
ci.set_pending_finish_get(-2);
ci.call_status |= CIST_PENDING_FINISH;
Err(LuaError::Yield)
}
Err(e) => Err(e),
}
}
#[inline(never)]
pub fn finishset_fallback_known_miss(
lua_state: &mut LuaState,
ci: &mut CallInfo,
obj: &LuaValue,
key: &LuaValue,
value: LuaValue,
) -> LuaResult<()> {
match finishset_known_miss(lua_state, obj, key, value) {
Ok(_) => Ok(()),
Err(LuaError::Yield) => {
ci.set_pending_finish_get(-2);
ci.call_status |= CIST_PENDING_FINISH;
Err(LuaError::Yield)
}
Err(e) => Err(e),
}
}
#[inline(never)]
pub fn eq_fallback(
lua_state: &mut LuaState,
ci: &mut CallInfo,
ra: LuaValue,
rb: LuaValue,
) -> LuaResult<bool> {
match equalobj(lua_state, ra, rb) {
Ok(eq) => Ok(eq),
Err(LuaError::Yield) => {
ci.call_status |= CIST_PENDING_FINISH;
Err(LuaError::Yield)
}
Err(e) => Err(e),
}
}
#[inline(never)]
pub fn return0_with_hook(
lua_state: &mut LuaState,
ci: &mut CallInfo,
a_pos: usize,
pc: usize,
) -> LuaResult<()> {
lua_state.set_top_raw(a_pos);
ci.save_pc(pc);
poscall(lua_state, ci, 0, pc)
}
#[inline(never)]
pub fn return1_with_hook(
lua_state: &mut LuaState,
ci: &mut CallInfo,
a_pos: usize,
pc: usize,
) -> LuaResult<()> {
lua_state.set_top_raw(a_pos + 1);
ci.save_pc(pc);
poscall(lua_state, ci, 1, pc)
}