use crate::lua_value::{LuaValue, lua_value_to_udvalue, udvalue_to_lua_value};
use crate::lua_vm::call_info::call_status::CIST_PENDING_FINISH;
use crate::lua_vm::execute::call::{self, call_c_function};
use crate::lua_vm::execute::execute_loop::lua_execute;
use crate::lua_vm::execute::helper::{get_binop_metamethod, get_metamethod_from_meta_ptr};
use crate::lua_vm::{LuaError, LuaResult, LuaState, get_metamethod_event};
use crate::stdlib::debug;
use crate::{CallInfo, TablePtr};
pub fn try_unary_tm(
lua_state: &mut LuaState,
operand: LuaValue,
result_pos: usize,
tm_kind: TmKind,
) -> LuaResult<()> {
if tm_kind == TmKind::Unm
&& operand.ttisfulluserdata()
&& let Some(ud) = operand.as_userdata_mut()
&& let Some(udv) = ud.get_trait().lua_unm()
{
let result = udvalue_to_lua_value(lua_state, udv)?;
let stack = lua_state.stack_mut();
stack[result_pos] = result;
return Ok(());
}
let metamethod = get_metamethod_event(lua_state, &operand, tm_kind);
if let Some(mm) = metamethod {
let result = call_tm_res(lua_state, mm, operand, operand)?;
let stack = lua_state.stack_mut();
stack[result_pos] = result;
Ok(())
} else {
if tm_kind == TmKind::Bnot && operand.is_number() {
Err(lua_state.error("number has no integer representation".to_string()))
} else {
let op_desc = match tm_kind {
TmKind::Bnot => "perform bitwise operation on",
TmKind::Unm => "perform arithmetic on",
TmKind::Len => "get length of",
_ => "perform arithmetic on",
};
Err(crate::stdlib::debug::typeerror(
lua_state, &operand, op_desc,
))
}
}
}
pub fn try_bin_tm(
lua_state: &mut LuaState,
p1: LuaValue,
p2: LuaValue,
res: u32,
p1_reg: u32,
p2_reg: u32,
tm_kind: TmKind,
) -> LuaResult<()> {
if p1.ttisfulluserdata() || p2.ttisfulluserdata() {
let trait_result = if let Some(ud) = p1.as_userdata_mut() {
let other = lua_value_to_udvalue(&p2);
match tm_kind {
TmKind::Add => ud.get_trait().lua_add(&other),
TmKind::Sub => ud.get_trait().lua_sub(&other),
TmKind::Mul => ud.get_trait().lua_mul(&other),
TmKind::Div => ud.get_trait().lua_div(&other),
TmKind::Mod => ud.get_trait().lua_mod(&other),
_ => None,
}
} else {
None
};
if let Some(udv) = trait_result {
unsafe {
*lua_state.stack_mut().get_unchecked_mut(res as usize) =
udvalue_to_lua_value(lua_state, udv)?;
}
return Ok(());
}
let trait_result2 = if let Some(ud) = p2.as_userdata_mut() {
let other = lua_value_to_udvalue(&p1);
match tm_kind {
TmKind::Add => ud.get_trait().lua_add(&other),
TmKind::Sub => ud.get_trait().lua_sub(&other),
TmKind::Mul => ud.get_trait().lua_mul(&other),
TmKind::Div => ud.get_trait().lua_div(&other),
TmKind::Mod => ud.get_trait().lua_mod(&other),
_ => None,
}
} else {
None
};
if let Some(udv) = trait_result2 {
unsafe {
*lua_state.stack_mut().get_unchecked_mut(res as usize) =
udvalue_to_lua_value(lua_state, udv)?;
}
return Ok(());
}
}
let metamethod = get_binop_metamethod(lua_state, &p1, &p2, tm_kind);
if let Some(mm) = metamethod {
let r = call_tm_res(lua_state, mm, p1, p2)?;
unsafe {
*lua_state.stack_mut().get_unchecked_mut(res as usize) = r;
}
Ok(())
} else {
let msg = match tm_kind {
TmKind::Band
| TmKind::Bor
| TmKind::Bxor
| TmKind::Shl
| TmKind::Shr
| TmKind::Bnot => {
if p1.is_number() && p2.is_number() {
let blame_reg = if !p1.is_integer() { p1_reg } else { p2_reg };
let info = debug::varinfo_for_reg(lua_state, blame_reg);
return Err(
lua_state.error(format!("number has no integer representation{}", info))
);
} else {
"perform bitwise operation on"
}
}
_ => "perform arithmetic on",
};
Err(debug::opinterror(lua_state, p1_reg, p2_reg, &p1, &p2, msg))
}
}
pub fn call_tm_res(
lua_state: &mut LuaState,
metamethod: LuaValue,
arg1: LuaValue,
arg2: LuaValue,
) -> LuaResult<LuaValue> {
let func_pos = {
let ci_top = lua_state.current_frame_top_unchecked();
let top = lua_state.get_top();
if top != ci_top {
lua_state.set_top_raw(ci_top);
}
ci_top
};
unsafe {
let sp = lua_state.stack_mut().as_mut_ptr();
*sp.add(func_pos) = metamethod;
*sp.add(func_pos + 1) = arg1;
*sp.add(func_pos + 2) = arg2;
}
lua_state.set_top_raw(func_pos + 3);
if metamethod.is_lua_function() {
let lua_func = unsafe { metamethod.as_lua_function_unchecked() };
let chunk = lua_func.chunk();
let upvalue_ptrs = lua_func.upvalues().as_ptr();
let new_base = func_pos + 1;
let caller_depth = lua_state.call_depth();
if !(chunk.param_count == 2
&& lua_state.try_push_lua_frame_exact(
new_base,
1,
chunk.max_stack_size,
chunk as *const _,
upvalue_ptrs,
)?)
{
lua_state.push_lua_frame(
new_base,
2,
1,
chunk.param_count,
chunk.max_stack_size,
chunk as *const _,
upvalue_ptrs,
)?;
}
lua_state.inc_n_ccalls()?;
let r = lua_execute(lua_state, caller_depth);
lua_state.dec_n_ccalls();
r?;
} else if metamethod.is_cfunction() {
call_c_function(lua_state, func_pos, 2, 1)?;
} else {
return Err(crate::stdlib::debug::callerror(lua_state, &metamethod));
}
let result_val = unsafe { *lua_state.stack_mut().as_ptr().add(func_pos) };
lua_state.set_top_raw(func_pos);
Ok(result_val)
}
pub fn call_tm_res1(
lua_state: &mut LuaState,
metamethod: LuaValue,
arg1: LuaValue,
) -> LuaResult<LuaValue> {
let func_pos = {
let ci_top = lua_state.current_frame_top_unchecked();
let top = lua_state.get_top();
if top != ci_top {
lua_state.set_top_raw(ci_top);
}
ci_top
};
unsafe {
let sp = lua_state.stack_mut().as_mut_ptr();
*sp.add(func_pos) = metamethod;
*sp.add(func_pos + 1) = arg1;
}
lua_state.set_top_raw(func_pos + 2);
if metamethod.is_lua_function() {
let lua_func = unsafe { metamethod.as_lua_function_unchecked() };
let chunk = lua_func.chunk();
let upvalue_ptrs = lua_func.upvalues().as_ptr();
let new_base = func_pos + 1;
let caller_depth = lua_state.call_depth();
if !(chunk.param_count == 1
&& lua_state.try_push_lua_frame_exact(
new_base,
1,
chunk.max_stack_size,
chunk as *const _,
upvalue_ptrs,
)?)
{
lua_state.push_lua_frame(
new_base,
1,
1,
chunk.param_count,
chunk.max_stack_size,
chunk as *const _,
upvalue_ptrs,
)?;
}
lua_state.inc_n_ccalls()?;
let r = lua_execute(lua_state, caller_depth);
lua_state.dec_n_ccalls();
r?;
} else if metamethod.is_cfunction() {
call_c_function(lua_state, func_pos, 1, 1)?;
} else {
return Err(crate::stdlib::debug::callerror(lua_state, &metamethod));
}
let result_val = unsafe { *lua_state.stack_mut().as_ptr().add(func_pos) };
lua_state.set_top_raw(func_pos);
Ok(result_val)
}
pub fn call_tm_res_into(
lua_state: &mut LuaState,
metamethod: LuaValue,
arg1: LuaValue,
arg2: LuaValue,
dest_reg: usize,
) -> LuaResult<()> {
let func_pos = {
let ci_top = lua_state.current_frame_top_unchecked();
let top = lua_state.get_top();
if top != ci_top {
lua_state.set_top_raw(ci_top);
}
ci_top
};
unsafe {
let sp = lua_state.stack_mut().as_mut_ptr();
*sp.add(func_pos) = metamethod;
*sp.add(func_pos + 1) = arg1;
*sp.add(func_pos + 2) = arg2;
}
lua_state.set_top_raw(func_pos + 3);
if metamethod.is_lua_function() {
let lua_func = unsafe { metamethod.as_lua_function_unchecked() };
let chunk = lua_func.chunk();
let upvalue_ptrs = lua_func.upvalues().as_ptr();
let new_base = func_pos + 1;
let caller_depth = lua_state.call_depth();
if !(chunk.param_count == 2
&& lua_state.try_push_lua_frame_exact(
new_base,
1,
chunk.max_stack_size,
chunk as *const _,
upvalue_ptrs,
)?)
{
lua_state.push_lua_frame(
new_base,
2,
1,
chunk.param_count,
chunk.max_stack_size,
chunk as *const _,
upvalue_ptrs,
)?;
}
lua_state.inc_n_ccalls()?;
let r = lua_execute(lua_state, caller_depth);
lua_state.dec_n_ccalls();
r?;
} else if metamethod.is_cfunction() {
call_c_function(lua_state, func_pos, 2, 1)?;
} else {
return Err(crate::stdlib::debug::callerror(lua_state, &metamethod));
}
unsafe {
let sp = lua_state.stack_mut().as_mut_ptr();
*sp.add(dest_reg) = *sp.add(func_pos);
}
lua_state.set_top_raw(func_pos);
Ok(())
}
pub fn call_tm(
lua_state: &mut LuaState,
metamethod: LuaValue,
arg1: LuaValue,
arg2: LuaValue,
arg3: LuaValue,
) -> LuaResult<()> {
let func_pos = {
let ci_top = lua_state.current_frame_top_unchecked();
let top = lua_state.get_top();
if top != ci_top {
lua_state.set_top_raw(ci_top);
}
ci_top
};
unsafe {
let sp = lua_state.stack_mut().as_mut_ptr();
*sp.add(func_pos) = metamethod;
*sp.add(func_pos + 1) = arg1;
*sp.add(func_pos + 2) = arg2;
*sp.add(func_pos + 3) = arg3;
}
lua_state.set_top_raw(func_pos + 4);
if metamethod.is_lua_function() {
let lua_func = unsafe { metamethod.as_lua_function_unchecked() };
let chunk = lua_func.chunk();
let upvalue_ptrs = lua_func.upvalues().as_ptr();
let new_base = func_pos + 1;
let caller_depth = lua_state.call_depth();
if !(chunk.param_count == 3
&& lua_state.try_push_lua_frame_exact(
new_base,
0,
chunk.max_stack_size,
chunk as *const _,
upvalue_ptrs,
)?)
{
lua_state.push_lua_frame(
new_base,
3,
0,
chunk.param_count,
chunk.max_stack_size,
chunk as *const _,
upvalue_ptrs,
)?;
}
lua_state.inc_n_ccalls()?;
let r = lua_execute(lua_state, caller_depth);
lua_state.dec_n_ccalls();
r?;
} else if metamethod.is_cfunction() {
call::call_c_function(lua_state, func_pos, 3, 0)?;
} else {
return Err(crate::stdlib::debug::callerror(lua_state, &metamethod));
}
Ok(())
}
pub fn try_comp_tm(
lua_state: &mut LuaState,
p1: LuaValue,
p2: LuaValue,
tm_kind: TmKind,
) -> LuaResult<Option<bool>> {
if p1.ttisfulluserdata()
&& let Some(ud1) = p1.as_userdata_mut()
&& let Some(ud2) = p2.as_userdata_mut()
{
let result = match tm_kind {
TmKind::Lt => ud1.get_trait().lua_lt(ud2.get_trait()),
TmKind::Le => ud1.get_trait().lua_le(ud2.get_trait()),
_ => None,
};
if let Some(b) = result {
return Ok(Some(b));
}
}
let metamethod = get_binop_metamethod(lua_state, &p1, &p2, tm_kind);
if let Some(mm) = metamethod {
let result = call_tm_res(lua_state, mm, p1, p2)?;
Ok(Some(!result.is_falsy()))
} else {
Ok(None)
}
}
#[inline(always)]
pub fn call_newindex_tm_fast(
lua_state: &mut LuaState,
ci: &mut CallInfo,
obj: LuaValue,
meta: TablePtr,
key: LuaValue,
value: LuaValue,
) -> LuaResult<bool> {
let Some(tm) = get_metamethod_from_meta_ptr(lua_state, meta, TmKind::NewIndex) else {
return Ok(false);
};
if !tm.is_function() {
return Ok(false);
}
match call_tm(lua_state, tm, obj, key, value) {
Ok(()) => Ok(true),
Err(LuaError::Yield) => {
ci.set_pending_finish_get(-2);
ci.call_status |= CIST_PENDING_FINISH;
Err(LuaError::Yield)
}
Err(e) => Err(e),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum TmKind {
Index = 0,
NewIndex = 1,
Gc = 2,
Mode = 3,
Len = 4,
Eq = 5,
Add = 6,
Sub = 7,
Mul = 8,
Mod = 9,
Pow = 10,
Div = 11,
IDiv = 12,
Band = 13,
Bor = 14,
Bxor = 15,
Shl = 16,
Shr = 17,
Unm = 18,
Bnot = 19,
Lt = 20,
Le = 21,
Concat = 22,
Call = 23,
Close = 24,
ToString = 25,
N = 26, }
impl TmKind {
pub fn from_u8(value: u8) -> Option<Self> {
if value <= TmKind::ToString as u8 {
Some(unsafe { Self::from_u8_unchecked(value) })
} else {
None
}
}
#[inline(always)]
pub unsafe fn from_u8_unchecked(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
pub const fn name(self) -> &'static str {
match self {
TmKind::Index => "__index",
TmKind::NewIndex => "__newindex",
TmKind::Gc => "__gc",
TmKind::Mode => "__mode",
TmKind::Len => "__len",
TmKind::Eq => "__eq",
TmKind::Add => "__add",
TmKind::Sub => "__sub",
TmKind::Mul => "__mul",
TmKind::Mod => "__mod",
TmKind::Pow => "__pow",
TmKind::Div => "__div",
TmKind::IDiv => "__idiv",
TmKind::Band => "__band",
TmKind::Bor => "__bor",
TmKind::Bxor => "__bxor",
TmKind::Shl => "__shl",
TmKind::Shr => "__shr",
TmKind::Unm => "__unm",
TmKind::Bnot => "__bnot",
TmKind::Lt => "__lt",
TmKind::Le => "__le",
TmKind::Concat => "__concat",
TmKind::Call => "__call",
TmKind::Close => "__close",
TmKind::ToString => "__tostring",
TmKind::N => "__n", }
}
}
impl From<TmKind> for u8 {
fn from(value: TmKind) -> Self {
value as u8
}
}