#![allow(dead_code)]
use std::convert::Infallible;
#[allow(unused_imports)] use crate::prelude::*;
use crate::state::{LuaState, LuaCFunction, GlobalState, CallInfo, CallInfoIdx, StackIdx,
LuaValueExt, LuaTypeExt, StackIdxExt,
LuaTableRefExt, LuaUserDataRefExt, LuaStringRefExt,
LuaLClosureRefExt, LuaClosureExt, LuaProtoExt};
use lua_types::{
LuaValue, LuaType, LuaError, LuaString, LuaUserData, LuaClosure, UpVal,
GcRef, LuaStatus,
};
use lua_types::value::LuaTable;
pub const LUA_IDENT: &[u8] =
b"$LuaVersion: Lua 5.4.7 Copyright (C) 1994-2024 Lua.org, PUC-Rio $\
$LuaAuthors: R. Ierusalimschy, L. H. Figueiredo, W. Celes $";
const LUA_REGISTRYINDEX: i32 = -(1_000_000) - 1000;
const LUA_MULTRET: i32 = -1;
const LUA_RIDX_GLOBALS: i64 = 2;
const MAX_UPVAL: u8 = 255;
#[inline]
fn is_pseudo(idx: i32) -> bool {
idx <= LUA_REGISTRYINDEX
}
#[inline]
fn is_upvalue(idx: i32) -> bool {
idx < LUA_REGISTRYINDEX
}
#[inline]
fn is_valid_index(state: &LuaState, idx: i32) -> bool {
if idx == 0 {
return false;
}
let ci = state.current_call_info();
if idx > 0 {
let slot = ci.func + idx;
slot.0 < state.top_idx().0
} else if !is_pseudo(idx) {
(-idx) as u32 <= state.top_idx().0.saturating_sub(ci.func.0 + 1)
} else if idx == LUA_REGISTRYINDEX {
true
} else {
let upval_n = (LUA_REGISTRYINDEX - idx) as usize;
let func_val = state.get_at(ci.func);
if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
upval_n >= 1 && upval_n <= ccl.upvalues.len()
} else {
false
}
}
}
fn index_to_value(state: &LuaState, idx: i32) -> LuaValue {
let ci = state.current_call_info();
if idx > 0 {
let func_idx = ci.func;
let slot = func_idx + idx;
debug_assert!(
idx as u32 <= ci.top.saturating_sub(func_idx + 1),
"unacceptable index"
);
if slot.0 >= state.top_idx().0 {
LuaValue::Nil
} else {
state.get_at(slot)
}
} else if !is_pseudo(idx) {
debug_assert!(
idx != 0,
"invalid index"
);
let top = state.top_idx();
let slot = (top.0 as i32 + idx) as u32;
state.get_at(slot)
} else if idx == LUA_REGISTRYINDEX {
state.registry_value()
} else {
let upval_n = (LUA_REGISTRYINDEX - idx) as usize;
debug_assert!(upval_n <= MAX_UPVAL as usize + 1, "upvalue index too large");
let func_val = state.get_at(ci.func);
if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
if upval_n >= 1 && upval_n <= ccl.upvalues.len() {
ccl.upvalues[upval_n - 1].clone()
} else {
LuaValue::Nil
}
} else {
LuaValue::Nil
}
}
}
#[inline]
fn index_to_stack_idx(state: &LuaState, idx: i32) -> StackIdx {
let ci = state.current_call_info();
if idx > 0 {
let slot = ci.func + idx;
debug_assert!(slot.0 < state.top_idx().0, "invalid index");
slot
} else {
debug_assert!(idx != 0 && !is_pseudo(idx), "invalid index");
StackIdx((state.top_idx().0 as i32 + idx) as u32)
}
}
pub fn check_stack(state: &mut LuaState, n: i32) -> bool {
debug_assert!(n >= 0, "negative 'n'");
let available = state.stack_available();
let res = if available > n as usize {
true
} else {
crate::do_::grow_stack(state, n, false).unwrap_or(false)
};
if res {
let needed_top = state.top_idx() + n as i32;
let ci_idx = state.current_ci_idx();
if state.get_ci(ci_idx).top.0 < needed_top.0 {
let live_top = state.top_idx();
state.get_ci_mut(ci_idx).top = needed_top;
state.clear_stack_range(live_top, needed_top);
}
}
res
}
pub fn xmove(from: &mut LuaState, to: &mut LuaState, n: i32) {
if n <= 0 {
return;
}
if std::ptr::eq(from as *const LuaState, to as *const LuaState) {
return;
}
let abs_top = from.top_idx().0 as i32;
debug_assert!(abs_top >= n, "lua_xmove: from stack underflow");
let first_abs = abs_top - n;
let mut buf: Vec<lua_types::LuaValue> = Vec::with_capacity(n as usize);
for i in 0..n {
let idx = StackIdx((first_abs + i) as u32);
buf.push(from.get_at(idx));
}
from.set_top(StackIdx(first_abs as u32));
for v in buf {
to.push(v);
}
}
pub fn at_panic(
state: &mut LuaState,
panicf: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
let old = state.global_mut().panic;
state.global_mut().panic = panicf;
old
}
pub fn version(_state: &LuaState) -> f64 {
504.0
}
pub fn abs_index(state: &LuaState, idx: i32) -> i32 {
if idx > 0 || is_pseudo(idx) {
idx
} else {
let ci = state.current_call_info();
(state.top_idx().0 as i32 - ci.func.0 as i32) + idx
}
}
pub fn get_top(state: &LuaState) -> i32 {
let ci = state.current_call_info();
(state.top_idx().0 as i32) - (ci.func.0 as i32 + 1)
}
pub fn set_top(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
let func = state.current_call_info().func;
let ci_top = state.current_call_info().top;
if idx >= 0 {
debug_assert!(
idx as u32 <= ci_top.saturating_sub(func + 1),
"new top too large"
);
let new_top = func + 1 + idx as i32;
let old_top = state.top_idx();
if new_top.0 > old_top.0 {
for i in old_top.0..new_top.0 {
state.set_at(i, LuaValue::Nil);
}
}
state.set_top_idx(new_top);
} else {
debug_assert!(
-(idx + 1) <= (state.top_idx().0 as i32 - (func.0 as i32 + 1)),
"invalid new top"
);
let new_top = (state.top_idx().0 as i32 + idx + 1) as u32;
state.set_top_idx(new_top);
}
Ok(())
}
pub fn close_slot(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
let level = index_to_stack_idx(state, idx);
state.set_at(level, LuaValue::Nil);
Ok(())
}
#[inline]
fn reverse_segment(state: &mut LuaState, from: StackIdx, to: StackIdx) {
let mut lo = from.0;
let mut hi = to.0;
while lo < hi {
let temp = state.get_at(StackIdx(lo));
let hi_val = state.get_at(StackIdx(hi));
state.set_at(StackIdx(lo), hi_val);
state.set_at(StackIdx(hi), temp);
lo += 1;
hi -= 1;
}
}
pub fn rotate(state: &mut LuaState, idx: i32, n: i32) {
let t = state.top_idx() - 1;
let p = index_to_stack_idx(state, idx);
debug_assert!((n.unsigned_abs() as i32) <= ((t.0 as i32) - (p.0 as i32) + 1), "invalid 'n'");
let m = if n >= 0 {
t - n
} else {
StackIdx((p.0 as i32 - n - 1) as u32)
};
reverse_segment(state, p, m);
reverse_segment(state, m + 1, t);
reverse_segment(state, p, t);
}
pub fn copy(state: &mut LuaState, fromidx: i32, toidx: i32) {
let fr = index_to_value(state, fromidx);
if is_upvalue(toidx) {
let upval_n = (LUA_REGISTRYINDEX - toidx) as usize;
let func_val = state.get_at(state.current_call_info().func);
if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
let _ = (upval_n, ccl);
}
} else if toidx == LUA_REGISTRYINDEX {
} else {
let to_slot = index_to_stack_idx(state, toidx);
state.set_at(to_slot, fr);
}
}
pub fn push_value(state: &mut LuaState, idx: i32) {
let v = index_to_value(state, idx);
state.push(v);
}
impl LuaState {
pub fn push_copy(&mut self, idx: i32) -> Result<(), LuaError> {
push_value(self, idx);
Ok(())
}
pub fn push_value_at(&mut self, idx: i32) -> Result<(), LuaError> {
push_value(self, idx);
Ok(())
}
pub fn insert(&mut self, idx: i32) -> Result<(), LuaError> {
rotate(self, idx, 1);
Ok(())
}
pub fn length_at(&mut self, idx: i32) -> Result<i64, LuaError> {
len(self, idx)?;
let l = match to_integer_x(self, -1) {
Some(n) => n,
None => {
return Err(LuaError::runtime(format_args!(
"object length is not an integer"
)));
}
};
self.pop_n(1);
Ok(l)
}
pub fn write_output(&mut self, msg: &[u8]) -> Result<(), LuaError> {
use std::io::Write;
let stdout = std::io::stdout();
let mut handle = stdout.lock();
handle
.write_all(msg)
.map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
handle
.flush()
.map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
Ok(())
}
pub fn to_display_string(&mut self, idx: i32) -> Result<Vec<u8>, LuaError> {
let abs = abs_index(self, idx);
let v = index_to_value(self, abs);
let mt: Option<GcRef<LuaTable>> = match &v {
LuaValue::Table(t) => t.metatable(),
LuaValue::UserData(u) => u.metatable(),
_ => self.global().mt[v.base_type() as usize].clone(),
};
if let Some(mt_ref) = mt {
let key = self.intern_str(b"__tostring")?;
let f = mt_ref.get_short_str(&key);
if !matches!(f, LuaValue::Nil) {
let func_idx = self.top_idx();
self.push(f);
self.push(v.clone());
if self.current_ci().is_lua_code() {
self.do_call(func_idx, 1)?;
} else {
self.do_call_no_yield(func_idx, 1)?;
}
let top = self.top_idx();
let result = self.get_at(StackIdx(top.0 - 1));
if let LuaValue::Str(s) = result {
return Ok(s.as_bytes().to_vec());
}
return Err(LuaError::runtime(format_args!(
"'__tostring' must return a string"
)));
}
}
let bytes: Vec<u8> = match &v {
LuaValue::Str(s) => {
let out = s.as_bytes().to_vec();
self.push(LuaValue::Str(s.clone()));
out
}
LuaValue::Int(_) | LuaValue::Float(_) => {
let s = crate::object::num_to_string(self, &v)?;
let out = s.as_bytes().to_vec();
self.push(LuaValue::Str(s));
out
}
LuaValue::Bool(b) => {
let lit: &[u8] = if *b { b"true" } else { b"false" };
let s = self.intern_str(lit)?;
self.push(LuaValue::Str(s));
lit.to_vec()
}
LuaValue::Nil => {
let s = self.intern_str(b"nil")?;
self.push(LuaValue::Str(s));
b"nil".to_vec()
}
_ => {
let kind = crate::tagmethods::obj_type_name(self, &v)?;
let ptr = to_pointer(self, abs).unwrap_or(0);
let mut buf = kind;
buf.extend_from_slice(b": 0x");
buf.extend_from_slice(format!("{:x}", ptr).as_bytes());
let s = self.intern_str(&buf)?;
self.push(LuaValue::Str(s));
buf
}
};
Ok(bytes)
}
pub fn top(&mut self) -> i32 {
get_top(self)
}
pub fn get_top(&mut self) -> i32 {
get_top(self)
}
pub fn type_at(&mut self, idx: i32) -> LuaType {
lua_type_at(self, idx)
}
pub fn check_arg_any(&mut self, arg: i32) -> Result<(), LuaError> {
if lua_type_at(self, arg) == LuaType::None {
return Err(LuaError::arg_error(arg, "value expected"));
}
Ok(())
}
pub fn check_arg_string(&mut self, arg: i32) -> Result<Vec<u8>, LuaError> {
match to_lua_string(self, arg)? {
Some(s) => Ok(s.as_bytes().to_vec()),
None => {
let got = index_to_value(self, arg);
let got_name = crate::tagmethods::obj_type_name(self, &got)?;
let extramsg = format!(
"string expected, got {}",
String::from_utf8_lossy(&got_name)
);
Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
}
}
}
pub fn check_arg_integer(&mut self, arg: i32) -> Result<i64, LuaError> {
match to_integer_x(self, arg) {
Some(d) => Ok(d),
None => {
if is_number(self, arg) {
Err(LuaError::arg_error(
arg,
"number has no integer representation",
))
} else {
let got = index_to_value(self, arg);
let got_name = crate::tagmethods::obj_type_name(self, &got)?;
let extramsg = format!(
"number expected, got {}",
String::from_utf8_lossy(&got_name)
);
Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
}
}
}
}
pub fn check_number(&mut self, arg: i32) -> Result<f64, LuaError> {
match to_number_x(self, arg) {
Some(d) => Ok(d),
None => {
let got = index_to_value(self, arg);
let got_name = crate::tagmethods::obj_type_name(self, &got)?;
let extramsg = format!(
"number expected, got {}",
String::from_utf8_lossy(&got_name)
);
Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
}
}
}
pub fn opt_arg_integer(&mut self, arg: i32, def: i64) -> Result<i64, LuaError> {
match lua_type_at(self, arg) {
LuaType::None | LuaType::Nil => Ok(def),
_ => match to_integer_x(self, arg) {
Some(d) => Ok(d),
None => {
if is_number(self, arg) {
Err(LuaError::arg_error(
arg,
"number has no integer representation",
))
} else {
let got = index_to_value(self, arg);
Err(LuaError::type_arg_error(arg, "number", &got))
}
}
},
}
}
pub fn protected_call(&mut self, nargs: i32, nresults: i32, msgh: i32) -> Result<(), LuaError> {
pcall_k(self, nargs, nresults, msgh, 0, None).map(|_| ())
}
pub fn protected_call_k(
&mut self,
nargs: i32,
nresults: i32,
msgh: i32,
ctx: isize,
k: Option<crate::state::LuaKFunction>,
) -> Result<(), LuaError> {
pcall_k(self, nargs, nresults, msgh, ctx, k).map(|_| ())
}
pub fn push_string(&mut self, s: &[u8]) -> Result<(), LuaError> {
push_lstring(self, s)?;
Ok(())
}
pub fn push_c_closure(
&mut self,
f: fn(&mut LuaState) -> Result<usize, LuaError>,
n: i32,
) -> Result<(), LuaError> {
push_cclosure(self, f, n)
}
pub fn raw_seti(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
raw_set_i(self, idx, n)
}
pub fn table_set_i(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
set_i(self, idx, n)
}
pub fn table_get_i_value(&mut self, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
get_i_value(self, t, n)
}
pub fn table_set_i_value(&mut self, t: &LuaValue, n: i64) -> Result<(), LuaError> {
set_i_value(self, t, n)
}
pub fn create_table(&mut self, narr: i32, nrec: i32) -> Result<(), LuaError> {
create_table(self, narr, nrec)
}
pub fn registry_set(&mut self, key: &[u8]) -> Result<(), LuaError> {
set_field(self, LUA_REGISTRYINDEX, key)
}
pub fn new_metatable(&mut self, tname: &[u8]) -> Result<bool, LuaError> {
if get_field(self, LUA_REGISTRYINDEX, tname)? != LuaType::Nil {
return Ok(false);
}
self.pop_n(1);
create_table(self, 0, 2)?;
push_lstring(self, tname)?;
set_field(self, -2, b"__name")?;
push_value(self, -1);
set_field(self, LUA_REGISTRYINDEX, tname)?;
Ok(true)
}
pub fn new_lib(
&mut self,
funcs: &[(&[u8], LuaCFunction)],
) -> Result<(), LuaError> {
create_table(self, 0, funcs.len() as i32)?;
for (name, f) in funcs {
push_cclosure(self, *f, 0)?;
set_field(self, -2, name)?;
}
Ok(())
}
pub fn register_lib(
&mut self,
_name: &[u8],
funcs: &[(&[u8], LuaCFunction)],
) -> Result<(), LuaError> {
self.new_lib(funcs)
}
pub fn new_lib_table(
&mut self,
funcs: &[(&[u8], LuaCFunction)],
) -> Result<(), LuaError> {
create_table(self, 0, funcs.len() as i32)
}
pub fn set_funcs_with_upvalues(
&mut self,
funcs: &[(&[u8], LuaCFunction)],
nup: i32,
) -> Result<(), LuaError> {
check_stack(self, nup);
for (name, f) in funcs {
for _ in 0..nup {
push_value(self, -nup);
}
push_cclosure(self, *f, nup)?;
set_field(self, -(nup + 2), name)?;
}
self.pop_n(nup as usize);
Ok(())
}
pub fn set_metatable(&mut self, objindex: i32) -> Result<(), LuaError> {
set_metatable(self, objindex)?;
Ok(())
}
pub fn set_metatable_by_name(&mut self, name: &[u8]) -> Result<(), LuaError> {
get_field(self, LUA_REGISTRYINDEX, name)?;
set_metatable(self, -2)?;
Ok(())
}
pub fn get_subtable_registry(&mut self, name: &[u8]) -> Result<bool, LuaError> {
if get_field(self, LUA_REGISTRYINDEX, name)? == LuaType::Table {
return Ok(true);
}
self.pop_n(1);
let idx = abs_index(self, LUA_REGISTRYINDEX);
let new_tbl = self.new_table();
self.push(LuaValue::Table(new_tbl));
push_value(self, -1);
set_field(self, idx, name)?;
Ok(false)
}
pub fn new_userdata_typed(
&mut self,
_name: &[u8],
size: usize,
nuvalue: i32,
) -> Result<GcRef<LuaUserData>, LuaError> {
debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
let u = GcRef::new(LuaUserData {
data: vec![0u8; size].into_boxed_slice(),
uv: vec![LuaValue::Nil; nuvalue as usize],
metatable: std::cell::RefCell::new(None),
});
self.push(LuaValue::UserData(u.clone()));
self.gc().check_step();
Ok(u)
}
}
pub fn lua_type_at(state: &LuaState, idx: i32) -> LuaType {
if !is_valid_index(state, idx) {
return LuaType::None;
}
index_to_value(state, idx).base_type()
}
pub fn type_name(_state: &LuaState, t: LuaType) -> &'static [u8] {
t.type_name()
}
pub fn is_cfunction(state: &LuaState, idx: i32) -> bool {
let o = index_to_value(state, idx);
matches!(o, LuaValue::Function(LuaClosure::LightC(_)) | LuaValue::Function(LuaClosure::C(_)))
}
pub fn is_integer(state: &LuaState, idx: i32) -> bool {
let o = index_to_value(state, idx);
matches!(o, LuaValue::Int(_))
}
pub fn is_number(state: &LuaState, idx: i32) -> bool {
let o = index_to_value(state, idx);
o.to_number_with_strconv().is_some()
}
pub fn is_string(state: &LuaState, idx: i32) -> bool {
let o = index_to_value(state, idx);
matches!(o, LuaValue::Str(_) | LuaValue::Int(_) | LuaValue::Float(_))
}
pub fn is_userdata(state: &LuaState, idx: i32) -> bool {
let o = index_to_value(state, idx);
matches!(o, LuaValue::UserData(_) | LuaValue::LightUserData(_))
}
pub fn raw_equal(state: &LuaState, index1: i32, index2: i32) -> bool {
if !is_valid_index(state, index1) || !is_valid_index(state, index2) {
return false;
}
let o1 = index_to_value(state, index1);
let o2 = index_to_value(state, index2);
state.equal_obj(None, &o1, &o2)
}
pub fn arith(state: &mut LuaState, op: i32) -> Result<(), LuaError> {
const LUA_OPUNM: i32 = 12;
const LUA_OPBNOT: i32 = 14;
if op == LUA_OPUNM || op == LUA_OPBNOT {
let top_val = state.get_at(state.top_idx() - 1);
state.push(top_val);
}
let top = state.top_idx();
let a = state.get_at(top - 2);
let b = state.get_at(top - 1);
let result = state.arith_op(op, &a, &b)?;
state.set_at(top - 2, result);
state.pop();
Ok(())
}
pub fn compare(state: &mut LuaState, index1: i32, index2: i32, op: i32) -> Result<bool, LuaError> {
let valid = is_valid_index(state, index1) && is_valid_index(state, index2);
let o1 = index_to_value(state, index1);
let o2 = index_to_value(state, index2);
if valid {
match op {
0 => Ok(state.equal_obj_with_tm(&o1, &o2)?),
1 => state.less_than(&o1, &o2),
2 => state.less_equal(&o1, &o2),
_ => {
debug_assert!(false, "invalid option");
Ok(false)
}
}
} else {
Ok(false)
}
}
pub fn string_to_number(state: &mut LuaState, s: &[u8]) -> usize {
match state.str_to_num(s) {
Some((val, consumed)) => {
state.push(val);
consumed
}
None => 0,
}
}
pub fn to_number_x(state: &LuaState, idx: i32) -> Option<f64> {
let o = index_to_value(state, idx);
o.to_number_with_strconv()
}
pub fn to_integer_x(state: &LuaState, idx: i32) -> Option<i64> {
let o = index_to_value(state, idx);
o.to_integer_with_strconv()
}
pub fn to_boolean(state: &LuaState, idx: i32) -> bool {
let o = index_to_value(state, idx);
!matches!(o, LuaValue::Nil | LuaValue::Bool(false))
}
pub fn to_lua_string(
state: &mut LuaState,
idx: i32,
) -> Result<Option<GcRef<LuaString>>, LuaError> {
let o = index_to_value(state, idx);
if let LuaValue::Str(s) = &o {
return Ok(Some(s.clone()));
}
if !matches!(o, LuaValue::Int(_) | LuaValue::Float(_)) {
return Ok(None);
}
state.obj_to_string(idx)?;
state.gc().check_step();
let updated = index_to_value(state, idx);
if let LuaValue::Str(s) = updated {
Ok(Some(s))
} else {
Ok(None)
}
}
pub fn raw_len(state: &LuaState, idx: i32) -> u64 {
let o = index_to_value(state, idx);
match &o {
LuaValue::Str(s) => s.len() as u64,
LuaValue::UserData(u) => u.len() as u64,
LuaValue::Table(t) => state.table_getn(t) as u64,
_ => 0,
}
}
pub fn to_cfunction(
state: &LuaState,
idx: i32,
) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
let o = index_to_value(state, idx);
match o {
LuaValue::Function(LuaClosure::LightC(_f)) => None,
LuaValue::Function(LuaClosure::C(_ccl)) => None,
_ => None,
}
}
#[inline]
fn to_userdata_ptr(o: &LuaValue) -> Option<*mut core::ffi::c_void> {
match o {
LuaValue::UserData(u) => {
let _ = u;
None
}
LuaValue::LightUserData(p) => Some(*p),
_ => None,
}
}
pub fn to_userdata(state: &LuaState, idx: i32) -> Option<*mut core::ffi::c_void> {
let o = index_to_value(state, idx);
to_userdata_ptr(&o)
}
pub fn to_thread(state: &LuaState, idx: i32) -> Option<GcRef<lua_types::value::LuaThread>> {
let o = index_to_value(state, idx);
if let LuaValue::Thread(t) = o {
Some(t)
} else {
None
}
}
pub fn to_pointer(state: &LuaState, idx: i32) -> Option<usize> {
let o = index_to_value(state, idx);
match &o {
LuaValue::Function(LuaClosure::LightC(f)) => Some(*f as usize),
LuaValue::LightUserData(p) => Some(*p as usize),
LuaValue::Str(s) => Some(GcRef::identity(s)),
LuaValue::Table(t) => Some(GcRef::identity(t)),
LuaValue::Function(LuaClosure::Lua(f)) => Some(GcRef::identity(f)),
LuaValue::Function(LuaClosure::C(f)) => Some(GcRef::identity(f)),
LuaValue::UserData(u) => Some(GcRef::identity(u)),
LuaValue::Thread(t) => Some(GcRef::identity(t)),
_ => None,
}
}
pub fn push_nil(state: &mut LuaState) {
state.push(LuaValue::Nil);
}
pub fn push_number(state: &mut LuaState, n: f64) {
state.push(LuaValue::Float(n));
}
pub fn push_integer(state: &mut LuaState, n: i64) {
state.push(LuaValue::Int(n));
}
pub fn push_lstring(state: &mut LuaState, s: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
let ts = state.intern_str(s)?;
state.push(LuaValue::Str(ts.clone()));
state.gc().check_step();
Ok(ts)
}
pub fn push_string(state: &mut LuaState, s: Option<&[u8]>) -> Result<Option<GcRef<LuaString>>, LuaError> {
match s {
None => {
state.push(LuaValue::Nil);
state.gc().check_step();
Ok(None)
}
Some(bytes) => {
let ts = state.intern_str(bytes)?;
state.push(LuaValue::Str(ts.clone()));
state.gc().check_step();
Ok(Some(ts))
}
}
}
pub fn push_vfstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
let ts = state.intern_str(formatted)?;
state.push(LuaValue::Str(ts.clone()));
state.gc().check_step();
Ok(ts)
}
pub fn push_fstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
push_vfstring(state, formatted)
}
pub fn push_cclosure(
state: &mut LuaState,
f: fn(&mut LuaState) -> Result<usize, LuaError>,
n: i32,
) -> Result<(), LuaError> {
let idx: lua_types::closure::LuaCFnPtr = {
let mut g = state.global_mut();
if n == 0 {
match g.c_functions.iter().position(|&existing| existing == f) {
Some(i) => i,
None => {
let i = g.c_functions.len();
g.c_functions.push(f);
i
}
}
} else {
let i = g.c_functions.len();
g.c_functions.push(f);
i
}
};
if n == 0 {
state.push(LuaValue::Function(LuaClosure::LightC(idx)));
} else {
debug_assert!(n > 0 && (n as u32) <= MAX_UPVAL as u32, "upvalue index too large");
let n_usize = n as usize;
let top = state.top_idx();
debug_assert!((top.0 as usize) >= n_usize, "not enough elements on stack");
let base = top.0 as usize - n_usize;
let mut upvalues: Vec<LuaValue> = Vec::with_capacity(n_usize);
for i in 0..n_usize {
upvalues.push(state.get_at(crate::state::StackIdx((base + i) as u32)));
}
state.pop_n(n_usize);
let cl = LuaClosure::C(GcRef::new(lua_types::closure::LuaCClosure {
func: idx,
upvalues,
}));
state.push(LuaValue::Function(cl));
state.gc().check_step();
}
Ok(())
}
pub fn push_boolean(state: &mut LuaState, b: bool) {
state.push(LuaValue::Bool(b));
}
pub fn push_light_userdata(state: &mut LuaState, p: *mut core::ffi::c_void) {
state.push(LuaValue::LightUserData(p));
}
pub fn push_thread(state: &mut LuaState) -> bool {
let (value, is_main) = {
let g = state.global();
let id = g.current_thread_id;
let v = g
.thread_value_for(id)
.expect("current_thread_id must always resolve to a registered thread");
(v, id == g.main_thread_id)
};
state.push(LuaValue::Thread(value));
is_main
}
fn aux_get_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<LuaType, LuaError> {
let str_val = {
let ts = state.intern_str(k)?;
LuaValue::Str(ts)
};
let result = state.table_get_with_tm(&t, &str_val)?;
state.push(result);
let top = state.top_idx();
Ok(state.get_at(top - 1).base_type())
}
fn get_global_table(state: &LuaState) -> LuaValue {
state.global().globals.clone()
}
pub fn get_global(state: &mut LuaState, name: &[u8]) -> Result<LuaType, LuaError> {
let g = get_global_table(state);
aux_get_str(state, g, name)
}
pub fn get_table(state: &mut LuaState, idx: i32) -> Result<LuaType, LuaError> {
let t = index_to_value(state, idx);
let top = state.top_idx();
let key = state.get_at(top - 1);
let result = state.table_get_with_tm(&t, &key)?;
state.set_at(top - 1, result);
let val = state.get_at(top - 1);
Ok(val.base_type())
}
pub fn get_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<LuaType, LuaError> {
let t = index_to_value(state, idx);
aux_get_str(state, t, k)
}
pub fn get_i(state: &mut LuaState, idx: i32, n: i64) -> Result<LuaType, LuaError> {
let t = index_to_value(state, idx);
let key = LuaValue::Int(n);
let result = state.table_get_with_tm(&t, &key)?;
state.push(result);
let top = state.top_idx();
Ok(state.get_at(top - 1).base_type())
}
pub fn get_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
let key = LuaValue::Int(n);
let result = state.table_get_with_tm(t, &key)?;
state.push(result);
let top = state.top_idx();
Ok(state.get_at(top - 1).base_type())
}
fn finish_raw_get(state: &mut LuaState, val: Option<LuaValue>) -> LuaType {
let v = val.unwrap_or(LuaValue::Nil);
state.push(v);
let top = state.top_idx();
state.get_at(top - 1).base_type()
}
fn get_table_value(state: &LuaState, idx: i32) -> Option<GcRef<LuaTable>> {
let t = index_to_value(state, idx);
debug_assert!(matches!(t, LuaValue::Table(_)), "table expected");
if let LuaValue::Table(tbl) = t {
Some(tbl)
} else {
None
}
}
pub fn raw_get(state: &mut LuaState, idx: i32) -> LuaType {
let t = get_table_value(state, idx);
let top = state.top_idx();
let key = state.get_at(top - 1);
let val = t.as_ref().map(|tbl| tbl.get(&key));
state.set_top_idx(top - 1);
finish_raw_get(state, val)
}
pub fn raw_get_i(state: &mut LuaState, idx: i32, n: i64) -> LuaType {
let t = get_table_value(state, idx);
let val = t.as_ref().map(|tbl| tbl.get_int(n));
finish_raw_get(state, val)
}
pub fn raw_get_p(state: &mut LuaState, idx: i32, p: *const core::ffi::c_void) -> LuaType {
let t = get_table_value(state, idx);
let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
let val = t.as_ref().map(|tbl| tbl.get(&key));
finish_raw_get(state, val)
}
pub fn create_table(state: &mut LuaState, narray: i32, nrec: i32) -> Result<(), LuaError> {
let t = state.new_table();
if narray > 0 || nrec > 0 {
t.resize(state, narray as usize, nrec as usize)?;
}
state.push(LuaValue::Table(t));
state.gc().check_step();
Ok(())
}
pub fn get_metatable(state: &mut LuaState, objindex: i32) -> bool {
let obj = index_to_value(state, objindex);
let mt: Option<GcRef<LuaTable>> = match &obj {
LuaValue::Table(t) => t.metatable(),
LuaValue::UserData(u) => u.metatable(),
other => {
let idx = other.base_type() as usize;
state.global().mt[idx].clone()
}
};
if let Some(mt_table) = mt {
state.push(LuaValue::Table(mt_table));
true
} else {
false
}
}
pub fn get_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> LuaType {
let o = index_to_value(state, idx);
debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
if let LuaValue::UserData(ref u) = o {
let uv_count = u.uv.len() as i32;
if n <= 0 || n > uv_count {
state.push(LuaValue::Nil);
LuaType::None
} else {
let val = u.uv[(n - 1) as usize].clone();
let t = val.base_type();
state.push(val);
t
}
} else {
state.push(LuaValue::Nil);
LuaType::None
}
}
fn aux_set_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<(), LuaError> {
let str_val = {
let ts = state.intern_str(k)?;
LuaValue::Str(ts)
};
let top = state.top_idx();
let val = state.get_at(top - 1);
state.table_set_with_tm(&t, str_val, val)?;
state.pop();
Ok(())
}
pub fn set_global(state: &mut LuaState, name: &[u8]) -> Result<(), LuaError> {
let g = get_global_table(state);
aux_set_str(state, g, name)
}
pub fn set_table(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
let t = index_to_value(state, idx);
let top = state.top_idx();
let key = state.get_at(top - 2);
let val = state.get_at(top - 1);
state.table_set_with_tm(&t, key, val)?;
state.set_top_idx(top - 2);
Ok(())
}
pub fn set_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<(), LuaError> {
let t = index_to_value(state, idx);
aux_set_str(state, t, k)
}
pub fn set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
let t = index_to_value(state, idx);
let top = state.top_idx();
let val = state.get_at(top - 1);
let key = LuaValue::Int(n);
state.table_set_with_tm(&t, key, val)?;
state.pop();
Ok(())
}
pub fn set_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<(), LuaError> {
let top = state.top_idx();
let val = state.get_at(top - 1);
let key = LuaValue::Int(n);
state.table_set_with_tm(t, key, val)?;
state.pop();
Ok(())
}
fn aux_raw_set(state: &mut LuaState, idx: i32, key: LuaValue, n: u32) -> Result<(), LuaError> {
let t = get_table_value(state, idx)
.ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
let top = state.top_idx();
let val = state.get_at(top - 1);
t.raw_set(state, key, val)?;
t.invalidate_tm_cache();
let top_val = state.get_at(top - 1);
state.gc().barrier_back(&t, &top_val);
state.set_top_idx(top - n as i32);
Ok(())
}
pub fn raw_set(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
let top = state.top_idx();
let key = state.get_at(top - 2);
aux_raw_set(state, idx, key, 2)
}
pub fn raw_set_p(state: &mut LuaState, idx: i32, p: *const core::ffi::c_void) -> Result<(), LuaError> {
let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
aux_raw_set(state, idx, key, 1)
}
pub fn raw_set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
let t = get_table_value(state, idx)
.ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
let top = state.top_idx();
let val = state.get_at(top - 1);
t.raw_set_int(state, n, val)?;
let top_val = state.get_at(top - 1);
state.gc().barrier_back(&t, &top_val);
state.pop();
Ok(())
}
fn metatable_has_gc(state: &LuaState, mt: &GcRef<LuaTable>) -> bool {
let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
!matches!(mt.get_short_str(&name), LuaValue::Nil)
}
fn register_finalizable_table(state: &mut LuaState, tbl: &GcRef<LuaTable>) {
let already = state
.global()
.pending_finalizers
.iter()
.any(|t| GcRef::ptr_eq(t, tbl));
if !already {
state.global_mut().pending_finalizers.push(tbl.clone());
}
}
pub fn run_pending_finalizers(state: &mut LuaState) {
let mut did_run = false;
loop {
let target_idx = {
let to_fin = &state.global().to_be_finalized;
if to_fin.is_empty() { None } else { Some(to_fin.len() - 1) }
};
let Some(i) = target_idx else { break; };
let tbl = state.global_mut().to_be_finalized.swap_remove(i);
let mt = tbl.metatable();
let gc_fn = match mt {
Some(ref m) => {
let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
m.get_short_str(&name)
}
None => LuaValue::Nil,
};
if !matches!(gc_fn, LuaValue::Function(_)) {
continue;
}
did_run = true;
let saved_top = state.top_idx();
let ci_top = state.current_call_info().top;
if saved_top.0 < ci_top.0 {
state.clear_stack_range(saved_top, ci_top);
state.set_top(ci_top);
}
state.push(gc_fn);
state.push(LuaValue::Table(tbl));
let func_idx = state.top_idx() - 2;
let _heap_guard = {
let g = state.global.borrow();
lua_gc::HeapGuard::push(&g.heap)
};
let old_allowhook = state.allowhook;
let old_gcstp = state.global_mut().stop_gc_internal();
state.allowhook = false;
let caller_ci = state.ci;
let caller_status = state.get_ci(caller_ci).callstatus;
state.get_ci_mut(caller_ci).callstatus = caller_status | crate::state::CIST_FIN;
let _ = crate::do_::pcall(
state,
|s| s.call_no_yield(func_idx, 0),
func_idx,
0,
);
state.get_ci_mut(caller_ci).callstatus = caller_status;
state.allowhook = old_allowhook;
state.global_mut().set_gc_stop_flags(old_gcstp);
state.set_top(saved_top);
}
let _ = did_run;
}
fn collect_live_weak_tables(state: &mut LuaState) -> Vec<GcRef<lua_types::value::LuaTable>> {
let mut g = state.global_mut();
g.weak_tables_registry.retain(|w| w.strong_count() > 0);
let mut seen = std::collections::HashSet::<usize>::new();
g.weak_tables_registry
.iter()
.filter_map(|w| w.upgrade())
.filter_map(|rc| {
let id = rc.identity();
if seen.insert(id) {
Some(rc)
} else {
None
}
})
.collect()
}
pub fn set_metatable(state: &mut LuaState, objindex: i32) -> Result<bool, LuaError> {
let top = state.top_idx();
let mt_val = state.get_at(top - 1);
let mt: Option<GcRef<LuaTable>> = if matches!(mt_val, LuaValue::Nil) {
None
} else {
debug_assert!(matches!(mt_val, LuaValue::Table(_)), "table expected");
if let LuaValue::Table(t) = mt_val {
Some(t)
} else {
None
}
};
let obj = index_to_value(state, objindex);
match obj {
LuaValue::Table(ref tbl) => {
if mt.is_some() {
state.gc().obj_barrier(tbl, mt.as_ref().unwrap());
}
tbl.set_metatable(mt.clone());
if tbl.weak_mode() != 0 {
state
.global_mut()
.weak_tables_registry
.push(tbl.downgrade());
}
if let Some(ref mt_table) = mt {
if metatable_has_gc(state, mt_table) {
register_finalizable_table(state, tbl);
}
}
}
LuaValue::UserData(ref ud) => {
if let Some(ref mt_table) = mt {
state.gc().obj_barrier(ud, mt_table);
}
ud.set_metatable(mt);
}
ref other => {
let idx = other.base_type() as usize;
state.global_mut().mt[idx] = mt;
}
}
state.pop();
Ok(true)
}
pub fn set_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> Result<bool, LuaError> {
let o = index_to_value(state, idx);
debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
let top = state.top_idx();
let val = state.get_at(top - 1);
let res = if let LuaValue::UserData(ref ud) = o {
let nuvalue = ud.uv.len() as i32;
if n < 1 || n > nuvalue {
false
} else {
state.gc().barrier_back(ud, &val);
let _ = (n, ud);
true
}
} else {
false
};
state.pop();
Ok(res)
}
pub fn call_k(
state: &mut LuaState,
nargs: i32,
nresults: i32,
ctx: isize,
k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
) -> Result<(), LuaError> {
let top = state.top_idx();
let func_idx = top - (nargs + 1);
if k.is_some() && state.is_yieldable() {
let ci_idx = state.ci;
{
let ci = state.get_ci_mut(ci_idx);
ci.set_u_c_k(k);
ci.set_u_c_ctx(ctx);
}
state.call_at(func_idx, nresults)?;
} else {
state.call_no_yield(func_idx, nresults)?;
}
state.adjust_results(nresults);
Ok(())
}
pub fn pcall_k(
state: &mut LuaState,
nargs: i32,
nresults: i32,
errfunc: i32,
ctx: isize,
k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
) -> Result<LuaStatus, LuaError> {
let _heap_guard = {
let g = state.global.borrow();
lua_gc::HeapGuard::push(&g.heap)
};
let err_handler_idx: isize = if errfunc == 0 {
0
} else {
let o = index_to_stack_idx(state, errfunc);
debug_assert!(
matches!(state.get_at(o), LuaValue::Function(_)),
"error handler must be a function"
);
o.0 as isize
};
let top = state.top_idx();
let func_idx = top - (nargs + 1);
if k.is_none() || !state.is_yieldable() {
state.protected_call_raw(func_idx, nresults, StackIdx(err_handler_idx as u32))?;
state.adjust_results(nresults);
return Ok(LuaStatus::Ok);
}
let ci_idx = state.ci;
let allow = state.allowhook;
let saved_errfunc = state.errfunc;
{
let ci = state.get_ci_mut(ci_idx);
ci.set_u_c_k(k);
ci.set_u_c_ctx(ctx);
ci.set_u2_funcidx(func_idx.0 as i32);
ci.set_u_c_old_errfunc(saved_errfunc);
ci.set_oah(allow);
ci.callstatus |= crate::state::CIST_YPCALL;
}
state.errfunc = err_handler_idx;
let call_result = crate::do_::call(state, func_idx, nresults);
match call_result {
Ok(()) => {
state.get_ci_mut(ci_idx).callstatus &= !crate::state::CIST_YPCALL;
state.errfunc = saved_errfunc;
state.adjust_results(nresults);
Ok(LuaStatus::Ok)
}
Err(crate::state::LuaError::Yield) => {
Err(crate::state::LuaError::Yield)
}
Err(e) => {
Err(e)
}
}
}
pub fn load(
state: &mut LuaState,
reader: Box<dyn FnMut() -> Option<Vec<u8>>>,
chunkname: Option<&[u8]>,
mode: Option<&[u8]>,
) -> Result<LuaStatus, LuaError> {
let name = chunkname.unwrap_or(b"?");
let z = crate::zio::ZIO::new(reader);
let status = state.protected_parser(z, name, mode);
if status == LuaStatus::Ok {
let top = state.top_idx();
let func_val = state.get_at(top - 1);
if let LuaValue::Function(LuaClosure::Lua(lcl)) = func_val {
if !lcl.upvals.is_empty() {
let gt = get_global_table(state);
let uv = state.new_upval_closed(gt);
lcl.set_upval(0, uv);
}
}
}
Ok(status)
}
pub fn dump(
state: &LuaState,
writer: &mut dyn FnMut(&[u8]) -> Result<(), LuaError>,
strip: bool,
) -> Result<bool, LuaError> {
let top = state.top_idx();
let o = state.get_at(top - 1);
if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = o {
crate::dump::dump(state, &lcl.proto, writer, strip)?;
Ok(true)
} else {
Ok(false)
}
}
pub fn status(state: &LuaState) -> LuaStatus {
LuaStatus::from_raw(state.status as i32)
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GcWhat {
Stop = 0,
Restart = 1,
Collect = 2,
Count = 3,
CountB = 4,
Step = 5,
SetPause = 6,
SetStepMul = 7,
IsRunning = 9,
Gen = 10,
Inc = 11,
}
pub enum GcArgs {
Stop,
Restart,
Collect,
Count,
CountB,
Step { data: i32 },
SetPause { value: i32 },
SetStepMul { value: i32 },
IsRunning,
Gen { minormul: i32, majormul: i32 },
Inc { pause: i32, stepmul: i32, stepsize: i32 },
}
pub fn gc(state: &mut LuaState, args: GcArgs) -> i32 {
if state.global().is_gc_stopped_internally() {
return -1;
}
match args {
GcArgs::Stop => {
state.global_mut().set_gc_stop_user();
}
GcArgs::Restart => {
{
let mut g = state.global_mut();
crate::state::set_debt(&mut *g, 0);
}
state.global_mut().clear_gc_stop();
}
GcArgs::Collect => {
if !state.allowhook {
return 0;
}
state.gc().full_collect();
run_pending_finalizers(state);
{
let mut g = state.global_mut();
crate::state::reclaim_dead_long_strings(&mut *g);
}
{
let mut g = state.global_mut();
let target_tb = 32_768_isize;
let cur_tb = g.totalbytes + g.gc_debt;
if cur_tb < target_tb {
g.totalbytes += target_tb - cur_tb;
}
}
}
GcArgs::Count => {
{
let mut g = state.global_mut();
crate::state::reclaim_dead_long_strings(&mut *g);
}
let g = state.global();
let long_string_bytes: usize = g.gc_tracked_long_strings.iter().map(|(_, sz)| sz).sum();
let total = g.heap.bytes_used() + long_string_bytes;
return (total >> 10) as i32;
}
GcArgs::CountB => {
{
let mut g = state.global_mut();
crate::state::reclaim_dead_long_strings(&mut *g);
}
let g = state.global();
let long_string_bytes: usize = g.gc_tracked_long_strings.iter().map(|(_, sz)| sz).sum();
let total = g.heap.bytes_used() + long_string_bytes;
return (total & 0x3ff) as i32;
}
GcArgs::Step { data } => {
let old_stp = {
let mut g = state.global_mut();
let old = g.gc_stop_flags();
g.clear_gc_stop();
old
};
let stepmul = (state.global().gc_stepmul_param() as isize | 1).max(1);
let work_units = if data == 0 {
stepmul
} else {
let raw = (data as isize).saturating_mul(stepmul);
raw.max(1)
};
if data == 0 {
let mut g = state.global_mut();
crate::state::set_debt(&mut *g, 0);
} else {
let debt = data as isize * 1024 + state.global().gc_debt();
let mut g = state.global_mut();
crate::state::set_debt(&mut *g, debt);
}
let cycle_complete = state.gc().incremental_step(work_units);
if state.global().is_gen_mode() {
state.gc().prune_weak_tables_mark_only();
}
state.global_mut().set_gc_stop_flags(old_stp);
if cycle_complete {
let mut g = state.global_mut();
let floor: isize = 1024;
let cur_tb = g.totalbytes + g.gc_debt;
let new_tb = (cur_tb / 2).max(floor);
if new_tb < cur_tb {
g.totalbytes -= cur_tb - new_tb;
}
}
{
let heap_state = state.global().heap.gc_state();
let mut g = state.global_mut();
g.gcstate = if heap_state.is_pause() { 0 } else { 1 };
}
return if cycle_complete { 1 } else { 0 };
}
GcArgs::SetPause { value } => {
let old = state.global().gc_pause_param() as i32;
state.global_mut().set_gc_pause_param(value as u8);
return old;
}
GcArgs::SetStepMul { value } => {
let old = state.global().gc_stepmul_param() as i32;
state.global_mut().set_gc_stepmul_param(value as u8);
return old;
}
GcArgs::IsRunning => {
return state.global().gc_running() as i32;
}
GcArgs::Gen { minormul, majormul } => {
let old_mode = if state.global().is_gen_mode() { 10i32 } else { 11i32 };
if minormul != 0 {
state.global_mut().genminormul = minormul as u8;
}
if majormul != 0 {
state.global_mut().set_gc_genmajormul(majormul as u8);
}
state.gc().change_mode(crate::state::GcKind::Generational);
return old_mode;
}
GcArgs::Inc { pause, stepmul, stepsize } => {
let old_mode = if state.global().is_gen_mode() { 10i32 } else { 11i32 };
if pause != 0 {
state.global_mut().set_gc_pause_param(pause as u8);
}
if stepmul != 0 {
state.global_mut().set_gc_stepmul_param(stepmul as u8);
}
if stepsize != 0 {
state.global_mut().gcstepsize = stepsize as u8;
}
state.gc().change_mode(crate::state::GcKind::Incremental);
return old_mode;
}
}
0
}
pub fn lua_error(state: &mut LuaState) -> Result<Infallible, LuaError> {
let top = state.top_idx();
let errobj = state.get_at(top - 1);
let is_mem_err = if let LuaValue::Str(ref s) = errobj {
let memerr = state.global().memerrmsg.clone();
GcRef::ptr_eq(s, &memerr)
} else {
false
};
if is_mem_err {
Err(LuaError::Memory)
} else {
Err(LuaError::from_value(errobj))
}
}
pub fn next(state: &mut LuaState, idx: i32) -> Result<bool, LuaError> {
let t = get_table_value(state, idx)
.ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
let top = state.top_idx();
let key = state.get_at(top - 1);
match t.next(key)? {
Some((next_key, next_val)) => {
state.set_at(top - 1, next_key);
state.push(next_val);
Ok(true)
}
None => {
state.set_top_idx(top - 1);
Ok(false)
}
}
}
pub fn to_close(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
let _level = index_to_stack_idx(state, idx);
Ok(())
}
pub fn concat(state: &mut LuaState, n: i32) -> Result<(), LuaError> {
if n > 0 {
state.concat(n)?;
} else {
let empty = state.intern_str(b"")?;
state.push(LuaValue::Str(empty));
}
state.gc().check_step();
Ok(())
}
pub fn len(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
let t = index_to_value(state, idx);
let result = state.obj_len(&t)?;
state.push(result);
Ok(())
}
pub fn set_warn_f(
state: &mut LuaState,
f: Option<Box<dyn FnMut(&[u8], bool)>>,
) {
state.global_mut().warnf = f;
}
pub fn warning(state: &mut LuaState, msg: &[u8], tocont: bool) {
state.emit_warning(msg, tocont);
}
pub fn new_userdata_uv(
state: &mut LuaState,
size: usize,
nuvalue: i32,
) -> Result<GcRef<LuaUserData>, LuaError> {
debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
let u = state.new_userdata(size, nuvalue as usize)?;
state.push(LuaValue::UserData(u.clone()));
state.gc().check_step();
Ok(u)
}
fn aux_upvalue(
state: &LuaState,
fi: &LuaValue,
n: i32,
) -> Option<(Vec<u8>, LuaValue)> {
match fi {
LuaValue::Function(LuaClosure::C(ccl)) => {
let nupvalues = ccl.upvalues.len() as i32;
if n < 1 || n > nupvalues {
return None;
}
Some((Vec::new(), ccl.upvalues[(n - 1) as usize].clone()))
}
LuaValue::Function(LuaClosure::Lua(lcl)) => {
let nupvalues = lcl.upvals.len() as i32;
if n < 1 || n > nupvalues {
return None;
}
let val = state.upvalue_get(lcl, (n - 1) as usize);
let name: Vec<u8> = lcl
.proto
.upvalues
.get((n - 1) as usize)
.and_then(|ud| ud.name.as_ref())
.map(|s| s.as_bytes().to_vec())
.unwrap_or_else(|| b"(no name)".to_vec());
Some((name, val))
}
_ => None,
}
}
pub fn get_upvalue(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
let fi = index_to_value(state, funcindex);
if let Some((name, val)) = aux_upvalue(state, &fi, n) {
state.push(val);
Some(name)
} else {
None
}
}
pub fn setup_value(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
let fi = index_to_value(state, funcindex);
let (name, _) = aux_upvalue(state, &fi, n)?;
let new_val = state.pop();
match &fi {
LuaValue::Function(LuaClosure::Lua(lcl)) => {
state.upvalue_set(lcl, (n - 1) as usize, new_val).ok()?;
}
LuaValue::Function(LuaClosure::C(_ccl)) => {
let _ = new_val;
}
_ => return None,
}
Some(name)
}
fn get_upval_ref_idx(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
let fi = index_to_value(state, fidx);
debug_assert!(matches!(fi, LuaValue::Function(LuaClosure::Lua(_))), "Lua function expected");
if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = fi {
let sizeupvalues = lcl.upvals.len() as i32;
if n >= 1 && n <= sizeupvalues {
Some((n - 1) as usize)
} else {
None
}
} else {
None
}
}
pub fn upvalue_id(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
let fi = index_to_value(state, fidx);
match &fi {
LuaValue::Function(LuaClosure::Lua(lcl)) => {
let idx = get_upval_ref_idx(state, fidx, n)?;
Some(GcRef::identity(&lcl.upval(idx)))
}
LuaValue::Function(LuaClosure::C(ccl)) => {
if n >= 1 && n <= ccl.upvalues.len() as i32 {
Some(GcRef::identity(ccl) ^ (n as usize))
} else {
None
}
}
LuaValue::Function(LuaClosure::LightC(_)) => None,
_ => {
debug_assert!(false, "function expected");
None
}
}
}
pub fn upvalue_join(state: &mut LuaState, fidx1: i32, n1: i32, fidx2: i32, n2: i32) {
let idx1 = match get_upval_ref_idx(state, fidx1, n1) {
Some(i) => i,
None => return,
};
let idx2 = match get_upval_ref_idx(state, fidx2, n2) {
Some(i) => i,
None => return,
};
let f1 = index_to_value(state, fidx1);
let f2 = index_to_value(state, fidx2);
if let (
LuaValue::Function(LuaClosure::Lua(lcl1)),
LuaValue::Function(LuaClosure::Lua(lcl2)),
) = (&f1, &f2)
{
let shared = lcl2.upval(idx2);
lcl1.set_upval(idx1, shared);
}
}