use std::rc::Rc;
#[allow(unused_imports)] use crate::prelude::*;
use crate::{
state::{
GcRef, LuaClosureC, LuaClosureLua, LuaState, LuaValue, UpVal,
},
tagmethods::TagMethod,
};
use lua_types::error::LuaError;
pub use lua_types::{CallInfoIdx, StackIdx};
pub(crate) const CLOSE_K_TOP: i32 = -1;
pub(crate) const MAX_UPVAL: u8 = 255;
pub(crate) const MAX_MISS: u32 = 10;
pub(crate) fn new_c_closure(
state: &mut LuaState,
nupvals: u8,
) -> GcRef<crate::state::LuaClosure> {
let closure = crate::state::LuaClosure::C(GcRef::new(LuaClosureC {
func: DUMMY_C_FUNCTION_IDX,
upvalues: vec![LuaValue::Nil; nupvals as usize],
}));
GcRef::new(closure)
}
pub(crate) fn new_lua_closure(
state: &mut LuaState,
nupvals: u8,
) -> GcRef<crate::state::LuaClosure> {
let _ = state; let _ = nupvals;
let lcl = GcRef::new(LuaClosureLua::placeholder());
let closure = crate::state::LuaClosure::Lua(lcl);
GcRef::new(closure)
}
pub(crate) fn init_upvals(state: &mut LuaState, cl: &GcRef<lua_types::LuaLClosure>) -> Result<(), LuaError> {
let n = cl.upvals.len();
for i in 0..n {
let uv: GcRef<UpVal> = state.new_upval_closed(LuaValue::Nil);
let _ = (i, uv);
}
Ok(())
}
fn new_open_upval(
state: &mut LuaState,
level: StackIdx,
insert_pos: usize,
) -> GcRef<UpVal> {
let owner_tid = state.global().current_thread_id as usize;
let uv: GcRef<UpVal> = state.new_upval_open(owner_tid, level);
state.openupval.insert(insert_pos, uv.clone());
if !state_in_twups(state) {
}
uv
}
pub(crate) fn find_upval(state: &mut LuaState, level: StackIdx) -> GcRef<UpVal> {
debug_assert!(
state_in_twups(state) || state.openupval.is_empty(),
"thread must be in twups if it has open upvalues"
);
let mut insert_pos = state.openupval.len(); for (i, uv_ref) in state.openupval.iter().enumerate() {
let uv_idx = match &*uv_ref.slot() {
lua_types::UpValState::Open { thread_id: _, idx: thread_stack_idx } => *thread_stack_idx,
lua_types::UpValState::Closed(_) => {
debug_assert!(false, "closed upvalue found in openupval list");
continue;
}
};
if uv_idx.0 >= level.0 {
if uv_idx == level {
return uv_ref.clone();
}
} else {
insert_pos = i;
break;
}
}
new_open_upval(state, level, insert_pos)
}
fn call_close_method(
state: &mut LuaState,
obj: LuaValue,
err: LuaValue,
yy: bool,
) -> Result<(), LuaError> {
let tm = state.get_tm_by_obj(&obj, lua_types::tagmethod::TagMethod::Close);
let top = state.top;
state.push(tm);
state.push(obj);
state.push(err);
if yy {
state.lua_call(top, 0)?;
} else {
state.lua_callnoyield(top, 0)?;
}
Ok(())
}
fn check_close_mth(state: &mut LuaState, level: StackIdx) -> Result<(), LuaError> {
let val = state.get_stack_value(level).clone();
let tm = state.get_tm_by_obj(&val, lua_types::tagmethod::TagMethod::Close);
if matches!(tm, LuaValue::Nil) {
let func_idx = state.current_ci().func;
let idx = (level.0 as i32) - (func_idx.0 as i32);
let vname_owned: Vec<u8> = state.debug_find_local(state.ci, idx).unwrap_or_else(|| b"?".to_vec());
return Err(LuaError::runtime(format_args!(
"variable '{}' got a non-closable value",
vname_owned.escape_ascii()
)));
}
Ok(())
}
fn prep_call_close_mth(
state: &mut LuaState,
level: StackIdx,
status: i32,
yy: bool,
) -> Result<(), LuaError> {
let uv = state.get_stack_value(level).clone();
let err = if status == CLOSE_K_TOP {
LuaValue::Nil
} else {
state.set_error_obj(status, StackIdx(level.0 + 1))?;
state.get_stack_value(StackIdx(level.0 + 1)).clone()
};
call_close_method(state, uv, err, yy)
}
pub(crate) fn new_tbc_upval(state: &mut LuaState, level: StackIdx) -> Result<(), LuaError> {
debug_assert!(
state.tbclist.last().map_or(true, |&top| level.0 > top.0),
"new tbc entry must be above current tbclist head"
);
let val = state.get_stack_value(level).clone();
if matches!(val, LuaValue::Nil | LuaValue::Bool(false)) {
return Ok(());
}
check_close_mth(state, level)?;
state.tbclist.push(level);
Ok(())
}
pub(crate) fn unlink_upval(state: &mut LuaState, uv: &GcRef<UpVal>) {
debug_assert!(
uv.is_open(),
"unlink_upval called on a closed upvalue"
);
state.openupval.retain(|candidate| !GcRef::ptr_eq(candidate, uv));
}
pub(crate) fn close_upval(state: &mut LuaState, level: StackIdx) {
loop {
let uv = match state.openupval.first() {
Some(uv) => uv.clone(),
None => break,
};
let uv_idx = match &*uv.slot() {
lua_types::UpValState::Open { thread_id: _, idx: thread_stack_idx } => *thread_stack_idx,
lua_types::UpValState::Closed(_) => {
state.openupval.remove(0);
continue;
}
};
if uv_idx.0 < level.0 {
break;
}
state.openupval.remove(0);
let stack_val = state.get_stack_value(uv_idx).clone();
uv.close_with(stack_val);
}
}
fn pop_tbc_list(state: &mut LuaState) {
state.tbclist.pop();
}
pub(crate) fn close(
state: &mut LuaState,
level: StackIdx,
status: i32,
yy: bool,
) -> Result<StackIdx, LuaError> {
close_upval(state, level);
while state.tbclist.last().copied().map_or(false, |tbc| tbc.0 >= level.0) {
let tbc = state
.tbclist
.last()
.copied()
.expect("tbclist non-empty (just checked)");
pop_tbc_list(state);
prep_call_close_mth(state, tbc, status, yy)?;
}
Ok(level)
}
pub(crate) fn new_proto(state: &mut LuaState) -> GcRef<crate::state::LuaProto> {
state.new_proto()
}
pub(crate) fn free_proto(_state: &mut LuaState, _f: GcRef<crate::state::LuaProto>) {
}
pub(crate) fn get_local_name(
f: &crate::state::LuaProto,
local_number: i32,
pc: i32,
) -> Option<&[u8]> {
let mut remaining = local_number;
for lv in f.locvars.iter() {
if lv.startpc > pc {
break;
}
if pc < lv.endpc {
remaining -= 1;
if remaining == 0 {
return Some(lv.varname.as_bytes());
}
}
}
None
}
const DUMMY_C_FUNCTION_IDX: crate::state::LuaCFnPtr = usize::MAX;
fn state_in_twups(state: &LuaState) -> bool {
let _ = state;
true
}
impl LuaState {
pub(crate) fn get_stack_value(&self, idx: StackIdx) -> &LuaValue {
&self.stack[idx.0 as usize].val
}
pub(crate) fn current_ci(&self) -> &crate::state::CallInfo {
&self.call_info[self.ci.0 as usize]
}
pub(crate) fn get_tm_by_obj(
&mut self,
val: &LuaValue,
tm: lua_types::tagmethod::TagMethod,
) -> LuaValue {
let mt: Option<GcRef<lua_types::value::LuaTable>> = match val {
LuaValue::Table(t) => t.metatable(),
LuaValue::UserData(u) => u.metatable(),
other => {
let type_idx = other.base_type() as usize;
self.global().mt[type_idx].clone()
}
};
match mt {
Some(mt_ref) => {
let ename = self.global().tmname[tm as usize].clone();
mt_ref.get_short_str(&ename)
}
None => LuaValue::Nil,
}
}
pub(crate) fn lua_call(&mut self, top: StackIdx, nresults: i32) -> Result<(), LuaError> {
crate::do_::call(self, top, nresults)
}
pub(crate) fn lua_callnoyield(
&mut self,
top: StackIdx,
nresults: i32,
) -> Result<(), LuaError> {
crate::do_::callnoyield(self, top, nresults)
}
pub(crate) fn set_error_obj(
&mut self,
status: i32,
idx: StackIdx,
) -> Result<(), LuaError> {
let s = lua_types::status::LuaStatus::from_raw(status);
crate::do_::set_error_obj(self, s, idx);
Ok(())
}
pub(crate) fn debug_find_local(
&self,
ci: CallInfoIdx,
n: i32,
) -> Option<Vec<u8>> {
crate::debug::find_local(self, ci, n, None)
}
}