#[allow(unused_imports)] use crate::prelude::*;
use crate::{
func,
state::{CallInfoIdx, LuaState},
vm,
};
use lua_types::{
error::LuaError,
status::LuaStatus,
value::LuaValue,
};
use lua_types::StackIdx;
use lua_types::closure::LuaClosure;
use lua_types::tagmethod::TagMethod;
use crate::zio::{ZIO, LexBuffer};
struct DynDataStub;
impl DynDataStub {
fn new() -> Self { DynDataStub }
}
fn parse_stub(
state: &mut LuaState,
z: &mut ZIO,
_buff: &mut LexBuffer,
_dyd: &mut DynDataStub,
name: &[u8],
c: i32,
) -> Result<lua_types::GcRef<lua_types::closure::LuaLClosure>, LuaError> {
let hook = state.global().parser_hook;
if let Some(parse) = hook {
let mut source: Vec<u8> = Vec::new();
if c >= 0 {
source.push(c as u8);
}
loop {
let b = z.getc();
if b < 0 {
break;
}
source.push(b as u8);
}
return parse(state, &source, name, c);
}
Err(LuaError::syntax(format_args!(
"{}: Lua text parser not yet wired (phase-b: lua-parse::parse)",
core::str::from_utf8(name).unwrap_or("?"),
)))
}
const LUAI_MAXSTACK: usize = 1_000_000;
const ERRORSTACKSIZE: usize = LUAI_MAXSTACK + 200;
const EXTRA_STACK: i32 = 5;
const LUA_MINSTACK: i32 = 20;
const LUA_MULTRET: i32 = -1;
const NYCI: u32 = 0x10001;
const LUAI_MAXCCALLS: u32 = 200;
const CIST_C: u16 = 1 << 1;
const CIST_FRESH: u16 = 1 << 2;
const CIST_HOOKED: u16 = 1 << 3;
const CIST_YPCALL: u16 = 1 << 4;
const CIST_TAIL: u16 = 1 << 5;
const CIST_HOOKYIELD: u16 = 1 << 6;
const CIST_TRAN: u16 = 1 << 8;
const CIST_CLSRET: u16 = 1 << 9;
const CIST_FIN: u16 = 1 << 7;
const LUA_MASKCALL: u8 = 1 << 0;
const LUA_MASKRET: u8 = 1 << 1;
const LUA_HOOKCALL: i32 = 0;
const LUA_HOOKRET: i32 = 1;
const LUA_HOOKTAILCALL: i32 = 4;
const CLOSE_K_TOP: i32 = -1;
#[inline]
fn error_status(s: LuaStatus) -> bool {
(s as i32) > (LuaStatus::Yield as i32)
}
pub(crate) fn set_error_obj(state: &mut LuaState, errcode: LuaStatus, old_top: StackIdx) {
match errcode {
LuaStatus::ErrMem => {
let memerrmsg = state.global().memerrmsg.clone();
state.set_at(old_top, LuaValue::Str(memerrmsg));
}
LuaStatus::ErrErr => {
if let Ok(s) = state.intern_str(b"error in error handling") {
state.set_at(old_top, LuaValue::Str(s));
}
}
LuaStatus::Ok => {
state.set_at(old_top, LuaValue::Nil);
}
_ => {
debug_assert!(error_status(errcode));
let top = state.top_idx();
let err_val = state.get_at(top - 1).clone();
state.set_at(old_top, err_val);
}
}
state.set_top(old_top + 1);
}
pub(crate) fn throw(state: &mut LuaState, errcode: LuaStatus) -> ! {
panic!("luaD_throw: unhandled Lua error (status = {:?}), no error handler", errcode)
}
pub(crate) fn raw_run_protected<F>(state: &mut LuaState, f: F) -> Result<(), LuaError>
where
F: FnOnce(&mut LuaState) -> Result<(), LuaError>,
{
let old_n_ccalls = state.nCcalls;
let result = f(state);
state.nCcalls = old_n_ccalls;
result
}
pub(crate) fn realloc_stack(
state: &mut LuaState,
new_size: usize,
raise_error: bool,
) -> Result<bool, LuaError> {
let old_size = state.stack_size() as usize;
debug_assert!(new_size <= LUAI_MAXSTACK || new_size == ERRORSTACKSIZE);
let old_gcstop = state.global().gcstopem;
state.global_mut().gcstopem = true;
let new_extent = new_size as usize + EXTRA_STACK as usize;
let alloc_result = state.stack_resize(new_extent);
state.global_mut().gcstopem = old_gcstop;
if alloc_result.is_err() {
if raise_error {
return Err(LuaError::Memory);
} else {
return Ok(false);
}
}
state.stack_last = StackIdx(new_size as u32);
let old_extent = old_size + EXTRA_STACK as usize;
for i in old_extent..new_extent {
state.stack_set_nil(i);
}
Ok(true)
}
pub(crate) fn grow_stack(
state: &mut LuaState,
n: i32,
raise_error: bool,
) -> Result<bool, LuaError> {
let size = state.stack_size();
if size > LUAI_MAXSTACK {
debug_assert!(state.stack_size() == ERRORSTACKSIZE);
if raise_error {
return Err(LuaError::with_status(LuaStatus::ErrErr));
}
return Ok(false);
} else if (n as usize) < LUAI_MAXSTACK {
let mut new_size = 2 * size;
let needed = (state.top_idx().0 as i32 + n) as usize;
if new_size > LUAI_MAXSTACK {
new_size = LUAI_MAXSTACK;
}
if new_size < needed {
new_size = needed;
}
if new_size <= LUAI_MAXSTACK {
return realloc_stack(state, new_size, raise_error);
}
}
realloc_stack(state, ERRORSTACKSIZE, raise_error)?;
if raise_error {
return Err(LuaError::runtime(format_args!("stack overflow")));
}
Ok(false)
}
fn stack_in_use(state: &LuaState) -> usize {
let mut lim = state.top_idx();
let mut ci_idx_opt = Some(state.ci);
while let Some(ci_idx) = ci_idx_opt {
let ci = state.get_ci(ci_idx);
if lim.0 < ci.top.0 {
lim = ci.top;
}
ci_idx_opt = ci.previous;
}
debug_assert!(true );
let res = lim.0 as usize + 1;
if res < LUA_MINSTACK as usize {
LUA_MINSTACK as usize
} else {
res
}
}
pub(crate) fn shrink_stack(state: &mut LuaState) {
let inuse = stack_in_use(state);
let max = if inuse > LUAI_MAXSTACK / 3 {
LUAI_MAXSTACK
} else {
inuse * 3
};
if inuse <= LUAI_MAXSTACK && state.stack_size() > max {
let nsize = if inuse > LUAI_MAXSTACK / 2 {
LUAI_MAXSTACK
} else {
inuse * 2
};
let _ = realloc_stack(state, nsize, false);
}
state.shrink_ci();
}
pub(crate) fn inc_top(state: &mut LuaState) -> Result<(), LuaError> {
state.check_stack(1)?;
let t = state.top_idx();
state.set_top(t + 1);
Ok(())
}
pub(crate) fn hook(
state: &mut LuaState,
event: i32,
line: i32,
ftransfer: i32,
ntransfer: i32,
) -> Result<(), LuaError> {
if !state.has_hook() || !state.allowhook {
return Ok(());
}
let ci_idx = state.ci;
let saved_top = state.top_idx();
let saved_ci_top = state.get_ci(ci_idx).top;
let mut mask = CIST_HOOKED;
if ntransfer != 0 {
mask |= CIST_TRAN;
state.set_ci_transfer_info(ci_idx, ftransfer as u16, ntransfer as u16);
}
{
let ci = state.get_ci(ci_idx);
if ci.is_lua() {
let ci_top = ci.top;
if state.top_idx().0 < ci_top.0 {
state.set_top(ci_top);
}
}
}
state.check_stack(LUA_MINSTACK as i32)?;
{
let top = state.top_idx();
let ci = state.get_ci_mut(ci_idx);
if ci.top.0 < (top + LUA_MINSTACK).0 {
let new_top = top + LUA_MINSTACK;
ci.top = new_top;
state.clear_stack_range(top, new_top);
}
}
state.allowhook = false;
state.get_ci_mut(ci_idx).callstatus |= mask;
let mut ar = crate::debug::LuaDebug::default();
ar.event = event;
ar.currentline = line;
ar.ftransfer = ftransfer as u16;
ar.ntransfer = ntransfer as u16;
ar.i_ci = Some(ci_idx);
let hook_opt = state.hook.take();
if let Some(mut h) = hook_opt {
h(state, &ar);
if state.hook.is_none() {
state.hook = Some(h);
}
}
debug_assert!(!state.allowhook);
state.allowhook = true;
state.get_ci_mut(ci_idx).top = saved_ci_top;
state.set_top(saved_top);
state.get_ci_mut(ci_idx).callstatus &= !mask;
Ok(())
}
pub(crate) fn hookcall(state: &mut LuaState, ci_idx: CallInfoIdx) -> Result<(), LuaError> {
state.oldpc = 0;
if state.hookmask & LUA_MASKCALL != 0 {
let event = if state.get_ci(ci_idx).callstatus & CIST_TAIL != 0 {
LUA_HOOKTAILCALL
} else {
LUA_HOOKCALL
};
let numparams = {
state.get_ci_lua_proto_numparams(ci_idx)
};
let pc = state.ci_savedpc(ci_idx);
state.set_ci_savedpc(ci_idx, pc + 1);
hook(state, event, -1, 1, numparams as i32)?;
state.set_ci_savedpc(ci_idx, pc);
}
Ok(())
}
fn rethook(state: &mut LuaState, ci_idx: CallInfoIdx, nres: i32) -> Result<(), LuaError> {
if state.hookmask & LUA_MASKRET != 0 {
let first_res = state.top_idx().0 as i32 - nres;
let mut delta: i32 = 0;
if state.get_ci(ci_idx).is_lua() {
let (is_vararg, nextraargs, numparams) =
state.get_ci_vararg_info(ci_idx);
if is_vararg {
delta = nextraargs + numparams as i32 + 1;
}
}
let original_func = state.get_ci(ci_idx).func;
state.get_ci_mut(ci_idx).func = StackIdx((original_func.0 as i32 + delta) as u32);
let ci_func = state.get_ci(ci_idx).func;
let ftransfer = (first_res - ci_func.0 as i32) as u16;
hook(state, LUA_HOOKRET, -1, ftransfer as i32, nres)?;
state.get_ci_mut(ci_idx).func = original_func;
}
let previous = state.get_ci(ci_idx).previous;
if let Some(prev_idx) = previous {
if state.get_ci(prev_idx).is_lua() {
state.oldpc = state.get_ci_pcrel(prev_idx);
}
}
Ok(())
}
fn try_func_tm(state: &mut LuaState, func_idx: StackIdx) -> Result<StackIdx, LuaError> {
state.check_stack(1)?;
state.gc_check_step();
let func_val = state.get_at(func_idx).clone();
let tm = state.get_tm_by_obj(&func_val, TagMethod::Call);
if matches!(tm, LuaValue::Nil) {
let offender = state.get_at(func_idx).clone();
return Err(crate::debug::call_error(state, &offender, func_idx));
}
let top = state.top_idx();
let mut p = top;
while p.0 > func_idx.0 {
let val = state.get_at(p - 1).clone();
state.set_at(p, val);
p = p - 1;
}
state.set_top(top + 1);
state.set_at(func_idx, tm);
Ok(func_idx)
}
#[inline(always)]
fn move_results(
state: &mut LuaState,
res_idx: StackIdx,
nres: i32,
wanted: i32,
) -> Result<(), LuaError> {
match wanted {
0 => {
state.set_top(res_idx);
return Ok(());
}
1 => {
if nres == 0 {
state.set_at(res_idx, LuaValue::Nil);
} else {
let top = state.top_idx();
let src = state.get_at(top - nres as i32).clone();
state.set_at(res_idx, src);
}
state.set_top(res_idx + 1);
return Ok(());
}
LUA_MULTRET => {
}
_ => {
if wanted < LUA_MULTRET {
let ci_idx = state.ci;
state.get_ci_mut(ci_idx).callstatus |= CIST_CLSRET;
state.set_ci_u2_nres(ci_idx, nres);
let res_idx = func::close(state, res_idx, CLOSE_K_TOP, true)?;
let ci_idx = state.ci;
state.get_ci_mut(ci_idx).callstatus &= !CIST_CLSRET;
if state.hookmask != 0 {
let saved_res = res_idx;
rethook(state, ci_idx, nres)?;
let _ = saved_res; }
let decoded_wanted = -(wanted) - 3;
let wanted = if decoded_wanted == LUA_MULTRET {
nres
} else {
decoded_wanted
};
let first_result = state.top_idx().0 as i32 - nres;
let actual_nres = nres.min(wanted);
for i in 0..actual_nres {
let src = state.get_at((first_result + i) as u32).clone();
state.set_at(res_idx + i as i32, src);
}
for i in actual_nres..wanted {
state.set_at(res_idx + i as i32, LuaValue::Nil);
}
state.set_top(res_idx + wanted as i32);
return Ok(());
}
}
}
let effective_wanted = if wanted == LUA_MULTRET { nres } else { wanted };
let first_result = state.top_idx().0 as i32 - nres;
let actual_nres = nres.min(effective_wanted);
for i in 0..actual_nres {
let src = state.get_at((first_result + i) as u32).clone();
state.set_at(res_idx + i as i32, src);
}
for i in actual_nres..effective_wanted {
state.set_at(res_idx + i as i32, LuaValue::Nil);
}
state.set_top(res_idx + effective_wanted as i32);
Ok(())
}
#[inline(always)]
pub(crate) fn poscall(
state: &mut LuaState,
ci_idx: CallInfoIdx,
nres: i32,
) -> Result<(), LuaError> {
let wanted = state.get_ci(ci_idx).nresults as i32;
if state.hookmask != 0 && !(wanted < LUA_MULTRET) {
rethook(state, ci_idx, nres)?;
}
let func_idx = state.get_ci(ci_idx).func;
move_results(state, func_idx, nres, wanted)?;
debug_assert!(
state.get_ci(ci_idx).callstatus
& (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET)
== 0
);
let previous = state
.get_ci(ci_idx)
.previous
.expect("poscall: no previous call frame");
state.ci = previous;
Ok(())
}
#[inline(always)]
fn prep_call_info(
state: &mut LuaState,
func_idx: StackIdx,
nret: i32,
mask: u16,
top_idx: StackIdx,
) -> Result<CallInfoIdx, LuaError> {
let ci_idx = state.next_ci()?;
state.ci = ci_idx;
{
let ci = state.get_ci_mut(ci_idx);
ci.func = func_idx;
ci.nresults = nret as i16;
ci.callstatus = mask;
ci.top = top_idx;
ci.u = if (mask & crate::state::CIST_C) != 0 {
crate::state::CallInfoFrame::c_default()
} else {
crate::state::CallInfoFrame::lua_default()
};
}
Ok(ci_idx)
}
#[inline(always)]
fn precall_c(
state: &mut LuaState,
func_idx: StackIdx,
nresults: i32,
f: crate::state::LuaCFunction,
) -> Result<i32, LuaError> {
state.check_stack(LUA_MINSTACK as i32)?;
state.gc_check_step();
let top_idx = state.top_idx();
let ci_idx = prep_call_info(state, func_idx, nresults, CIST_C, top_idx + LUA_MINSTACK)?;
debug_assert!(true );
if state.hookmask & LUA_MASKCALL != 0 {
let narg = (state.top_idx().0 as i32 - func_idx.0 as i32) - 1;
hook(state, LUA_HOOKCALL, -1, 1, narg)?;
}
let n = f(state)? as i32;
debug_assert!(
n <= state.top_idx().0 as i32,
"C function returned more values than available"
);
poscall(state, ci_idx, n)?;
Ok(n)
}
pub(crate) fn pretailcall(
state: &mut LuaState,
ci_idx: CallInfoIdx,
mut func_idx: StackIdx,
mut narg1: i32,
delta: i32,
) -> Result<i32, LuaError> {
loop {
let func_val = state.get_at(func_idx).clone();
match func_val {
LuaValue::Function(LuaClosure::C(ref cl)) => {
let cfunc = state.global().c_functions[cl.func];
return precall_c(state, func_idx, LUA_MULTRET, cfunc);
}
LuaValue::Function(LuaClosure::LightC(f)) => {
let cfunc = state.global().c_functions[f];
return precall_c(state, func_idx, LUA_MULTRET, cfunc);
}
LuaValue::Function(LuaClosure::Lua(ref cl)) => {
let proto = cl.proto.clone();
let fsize = proto.maxstacksize as i32;
let nfixparams = proto.numparams as i32;
state.check_stack(fsize - delta)?;
state.gc_check_step();
{
let ci = state.get_ci_mut(ci_idx);
ci.func = StackIdx((ci.func.0 as i32 - delta) as u32);
}
let ci_func = state.get_ci(ci_idx).func;
for i in 0..narg1 {
let src = state.get_at(func_idx + i as i32).clone();
state.set_at(ci_func + i as i32, src);
}
func_idx = ci_func;
while narg1 <= nfixparams {
state.set_at(func_idx + narg1 as i32, LuaValue::Nil);
narg1 += 1;
}
{
let new_ci_top = func_idx + 1 + fsize as i32;
let stack_last = state.stack_last;
let live_top = state.top_idx();
let ci = state.get_ci_mut(ci_idx);
ci.top = new_ci_top;
debug_assert!(ci.top.0 <= stack_last.0);
ci.set_saved_pc(0);
ci.callstatus |= CIST_TAIL;
state.clear_stack_range(live_top, new_ci_top);
}
state.set_top(func_idx + narg1 as i32);
return Ok(-1); }
_ => {
func_idx = try_func_tm(state, func_idx)?;
narg1 += 1;
}
}
}
}
#[inline(always)]
pub(crate) fn precall(
state: &mut LuaState,
func_idx: StackIdx,
nresults: i32,
) -> Result<Option<CallInfoIdx>, LuaError> {
if let LuaValue::Function(LuaClosure::Lua(cl)) =
&state.stack[func_idx.0 as usize].val
{
let nfixparams = cl.proto.numparams as i32;
let fsize = cl.proto.maxstacksize as i32;
let narg = (state.top_idx().0 as i32 - func_idx.0 as i32) - 1;
state.check_stack(fsize)?;
state.gc_check_step();
let ci_idx =
prep_call_info(state, func_idx, nresults, 0, func_idx + 1 + fsize as i32)?;
state.set_ci_savedpc(ci_idx, 0);
if narg < nfixparams {
fill_missing_params(state, narg, nfixparams);
}
return Ok(Some(ci_idx));
}
precall_slow(state, func_idx, nresults)
}
#[cold]
#[inline(never)]
fn fill_missing_params(state: &mut LuaState, mut narg: i32, nfixparams: i32) {
while narg < nfixparams {
let top = state.top_idx();
state.set_at(top, LuaValue::Nil);
state.set_top(top + 1);
narg += 1;
}
}
#[cold]
#[inline(never)]
fn precall_slow(
state: &mut LuaState,
mut func_idx: StackIdx,
nresults: i32,
) -> Result<Option<CallInfoIdx>, LuaError> {
loop {
let func_val = state.get_at(func_idx).clone();
match func_val {
LuaValue::Function(LuaClosure::C(ref cl)) => {
let cfunc = state.global().c_functions[cl.func];
precall_c(state, func_idx, nresults, cfunc)?;
return Ok(None);
}
LuaValue::Function(LuaClosure::LightC(f)) => {
state.check_stack(LUA_MINSTACK as i32)?;
state.gc_check_step();
let top_idx = state.top_idx();
let ci_idx =
prep_call_info(state, func_idx, nresults, CIST_C, top_idx + LUA_MINSTACK)?;
if state.hookmask & LUA_MASKCALL != 0 {
let narg = (state.top_idx().0 as i32 - func_idx.0 as i32) - 1;
hook(state, LUA_HOOKCALL, -1, 1, narg)?;
}
let cfunc = state.global().c_functions[f];
let n = cfunc(state)? as i32;
debug_assert!(
n <= state.top_idx().0 as i32,
"C function returned more values than available"
);
poscall(state, ci_idx, n)?;
return Ok(None);
}
LuaValue::Function(LuaClosure::Lua(ref cl)) => {
let narg = (state.top_idx().0 as i32 - func_idx.0 as i32) - 1;
let nfixparams = cl.proto.numparams as i32;
let fsize = cl.proto.maxstacksize as i32;
state.check_stack(fsize)?;
state.gc_check_step();
let ci_idx = prep_call_info(
state,
func_idx,
nresults,
0,
func_idx + 1 + fsize as i32,
)?;
state.set_ci_savedpc(ci_idx, 0);
if narg < nfixparams {
fill_missing_params(state, narg, nfixparams);
}
return Ok(Some(ci_idx));
}
_ => {
func_idx = try_func_tm(state, func_idx)?;
}
}
}
}
#[inline]
fn ccall_inner(
state: &mut LuaState,
func_idx: StackIdx,
n_results: i32,
inc: u32,
) -> Result<(), LuaError> {
ccall_inner_with_status(state, func_idx, n_results, inc, 0)
}
#[inline]
fn ccall_inner_with_status(
state: &mut LuaState,
func_idx: StackIdx,
n_results: i32,
inc: u32,
extra_callstatus: u16,
) -> Result<(), LuaError> {
state.nCcalls += inc;
if state.c_calls() >= LUAI_MAXCCALLS {
state.check_stack(0)?;
state.check_c_stack()?;
}
if let Some(ci_idx) = precall(state, func_idx, n_results)? {
state.get_ci_mut(ci_idx).callstatus = CIST_FRESH | extra_callstatus;
vm::execute(state, ci_idx)?;
}
state.nCcalls -= inc;
Ok(())
}
pub(crate) fn call(
state: &mut LuaState,
func_idx: StackIdx,
n_results: i32,
) -> Result<(), LuaError> {
ccall_inner(state, func_idx, n_results, 1)
}
pub(crate) fn callnoyield(
state: &mut LuaState,
func_idx: StackIdx,
n_results: i32,
) -> Result<(), LuaError> {
ccall_inner(state, func_idx, n_results, NYCI)
}
fn finish_pcallk(state: &mut LuaState, ci_idx: CallInfoIdx) -> Result<LuaStatus, LuaError> {
let mut status = LuaStatus::from_raw(state.get_ci(ci_idx).recover_status());
if status == LuaStatus::Ok {
status = LuaStatus::Yield;
} else {
let func_idx = StackIdx(state.get_ci_u2_funcidx(ci_idx) as u32);
state.allowhook = state.get_ci(ci_idx).get_oah();
let _func_idx = func::close(state, func_idx, status as i32, true)?;
set_error_obj(state, status, func_idx);
if state.errfunc != 0 && error_status(status) && status != LuaStatus::ErrErr && status != LuaStatus::ErrSyntax {
let errfunc_stk = StackIdx(state.errfunc as u32);
let err_val = state.get_at(func_idx);
state.push(err_val);
let handler = state.get_at(errfunc_stk);
state.set_at(state.top_idx() - 2, handler);
if let Err(_) = state.call_no_yield(state.top_idx() - 2, 1) {
status = LuaStatus::ErrErr;
if let Ok(s) = state.intern_str(b"error in error handling") {
state.set_at(func_idx, lua_types::value::LuaValue::Str(s));
}
state.set_top(func_idx + 1);
}
}
shrink_stack(state);
state.get_ci_mut(ci_idx).set_recover_status(LuaStatus::Ok as i32);
}
state.get_ci_mut(ci_idx).callstatus &= !CIST_YPCALL;
let old_errfunc = state.get_ci(ci_idx).u_c_old_errfunc();
state.errfunc = old_errfunc;
Ok(status)
}
fn finish_ccall(state: &mut LuaState, ci_idx: CallInfoIdx) -> Result<(), LuaError> {
let n;
if state.get_ci(ci_idx).callstatus & CIST_CLSRET != 0 {
debug_assert!((state.get_ci(ci_idx).nresults as i32) < LUA_MULTRET);
n = state.get_ci_u2_nres(ci_idx);
} else {
debug_assert!(
state.get_ci(ci_idx).u_c_k().is_some() && state.is_yieldable(),
"finishCcall: no continuation or non-yieldable"
);
let mut status = LuaStatus::Yield;
if state.get_ci(ci_idx).callstatus & CIST_YPCALL != 0 {
status = finish_pcallk(state, ci_idx)?;
}
state.adjust_results(LUA_MULTRET);
let k = state.get_ci(ci_idx).u_c_k();
let ctx = state.get_ci(ci_idx).u_c_ctx();
if let Some(k_fn) = k {
n = k_fn(state, status as i32, ctx)? as i32;
} else {
return Err(LuaError::runtime(format_args!("finishCcall: missing continuation")));
}
debug_assert!(
n <= state.top_idx().0 as i32,
"continuation returned more values than available"
);
}
poscall(state, ci_idx, n)?;
Ok(())
}
fn unroll(state: &mut LuaState) -> Result<(), LuaError> {
loop {
let ci_idx = state.ci;
if state.is_base_ci(ci_idx) {
break;
}
if !state.get_ci(ci_idx).is_lua() {
finish_ccall(state, ci_idx)?;
} else {
vm::finish_op(state)?;
vm::execute(state, ci_idx)?;
}
}
Ok(())
}
fn find_pcall(state: &LuaState) -> Option<CallInfoIdx> {
let mut ci_idx_opt = Some(state.ci);
while let Some(ci_idx) = ci_idx_opt {
let ci = state.get_ci(ci_idx);
if ci.callstatus & CIST_YPCALL != 0 {
return Some(ci_idx);
}
ci_idx_opt = ci.previous;
}
None
}
fn resume_error(state: &mut LuaState, msg: &[u8], narg: i32) -> LuaStatus {
let top = state.top_idx();
state.set_top(top - narg as i32);
let s = state.intern_str(msg).ok();
let new_top = state.top_idx();
if let Some(s) = s { state.set_at(new_top, LuaValue::Str(s)); }
state.set_top(new_top + 1);
LuaStatus::ErrRun
}
fn resume_coroutine(state: &mut LuaState, nargs: i32) -> Result<(), LuaError> {
let top = state.top_idx();
let first_arg = top - nargs as i32;
let ci_idx = state.ci;
if state.status == LuaStatus::Ok as u8 {
ccall_inner(state, first_arg - 1, LUA_MULTRET, 0)?;
} else {
debug_assert!(state.status == LuaStatus::Yield as u8);
state.status = LuaStatus::Ok as u8;
if state.get_ci(ci_idx).is_lua() {
debug_assert!(state.get_ci(ci_idx).callstatus & CIST_HOOKYIELD != 0);
let pc = state.ci_savedpc(ci_idx);
state.set_ci_savedpc(ci_idx, pc.saturating_sub(1));
state.set_top(first_arg);
vm::execute(state, ci_idx)?;
} else {
if let Some(k_fn) = state.get_ci(ci_idx).u_c_k() {
let ctx = state.get_ci(ci_idx).u_c_ctx();
let n = k_fn(state, LuaStatus::Yield as i32, ctx)? as i32;
debug_assert!(n <= state.top_idx().0 as i32);
poscall(state, ci_idx, n)?;
} else {
let n = (state.top_idx().0 as i32 - first_arg.0 as i32).max(0);
poscall(state, ci_idx, n)?;
}
}
unroll(state)?;
}
Ok(())
}
fn precover(state: &mut LuaState, mut status: LuaStatus) -> LuaStatus {
while error_status(status) {
if let Some(ci_idx) = find_pcall(state) {
state.ci = ci_idx;
state.get_ci_mut(ci_idx).set_recover_status(status as i32);
status = match raw_run_protected(state, |s| unroll(s)) {
Ok(()) => LuaStatus::Ok,
Err(e) => {
let s = e.to_status();
if error_status(s) {
state.push(e.into_value());
}
s
}
};
} else {
break;
}
}
status
}
pub fn lua_resume(
state: &mut LuaState,
from: Option<&mut LuaState>,
nargs: i32,
nresults: &mut i32,
) -> LuaStatus {
if state.status == LuaStatus::Ok as u8 {
if !state.is_base_ci(state.ci) {
return resume_error(state, b"cannot resume non-suspended coroutine", nargs);
}
let ci_func = state.get_ci(state.ci).func;
if state.top_idx().0 as i32 - (ci_func.0 as i32 + 1) == nargs {
return resume_error(state, b"cannot resume dead coroutine", nargs);
}
} else if state.status != LuaStatus::Yield as u8 {
return resume_error(state, b"cannot resume dead coroutine", nargs);
}
state.nCcalls = from
.as_ref()
.map(|f| f.c_calls() as u32)
.unwrap_or(0);
if state.c_calls() >= LUAI_MAXCCALLS {
return resume_error(state, b"C stack overflow", nargs);
}
state.nCcalls += 1;
debug_assert!(
if state.status == LuaStatus::Ok as u8 {
nargs + 1 <= state.top_idx().0 as i32
} else {
nargs <= state.top_idx().0 as i32
},
"lua_resume: not enough stack elements"
);
let (mut status, err_value) = match raw_run_protected(state, |s| resume_coroutine(s, nargs)) {
Ok(()) => (LuaStatus::Ok, None),
Err(e) => {
let s = e.to_status();
let v = if error_status(s) { Some(e.into_value()) } else { None };
(s, v)
}
};
if let Some(v) = err_value {
state.push(v);
}
status = precover(state, status);
if !error_status(status) {
debug_assert!(status as u8 == state.status, "lua_resume: status mismatch");
} else {
state.status = status as u8;
let top = state.top_idx();
set_error_obj(state, status, top);
let new_top = state.top_idx();
let ci_idx = state.ci;
state.get_ci_mut(ci_idx).top = new_top;
}
let ci_idx = state.ci;
*nresults = if status == LuaStatus::Yield {
state.get_ci_u2_nyield(ci_idx)
} else {
let ci_func = state.get_ci(ci_idx).func;
state.top_idx().0 as i32 - (ci_func.0 as i32 + 1)
};
status
}
pub fn lua_isyieldable(state: &LuaState) -> bool {
state.is_yieldable()
}
pub fn lua_yieldk(
state: &mut LuaState,
nresults: i32,
ctx: isize,
k: Option<crate::state::LuaKFunction>,
) -> Result<i32, LuaError> {
let ci_idx = state.ci;
debug_assert!(
nresults <= state.top_idx().0 as i32,
"lua_yieldk: not enough elements on stack"
);
if !state.is_yieldable() {
if !state.is_main_thread() {
return Err(LuaError::runtime(format_args!(
"attempt to yield across a C-call boundary"
)));
} else {
return Err(LuaError::runtime(format_args!(
"attempt to yield from outside a coroutine"
)));
}
}
state.status = LuaStatus::Yield as u8;
state.set_ci_u2_nyield(ci_idx, nresults);
if state.get_ci(ci_idx).is_lua() {
debug_assert!(!state.get_ci(ci_idx).is_lua_code());
debug_assert!(nresults == 0, "hooks cannot yield values");
debug_assert!(k.is_none(), "hooks cannot continue after yielding");
} else {
if let crate::state::CallInfoFrame::C { k: ref mut frame_k, ctx: ref mut frame_ctx, .. } =
state.get_ci_mut(ci_idx).u {
*frame_k = k;
if k.is_some() {
*frame_ctx = ctx;
}
}
return Err(LuaError::Yield);
}
debug_assert!(
state.get_ci(ci_idx).callstatus & CIST_HOOKED != 0,
"lua_yieldk called outside a hook"
);
Ok(0) }
struct CloseP {
level: StackIdx,
status: LuaStatus,
}
fn close_aux(state: &mut LuaState, pcl: &mut CloseP) -> Result<(), LuaError> {
func::close(state, pcl.level, pcl.status as i32, false)?;
Ok(())
}
pub(crate) fn close_protected(
state: &mut LuaState,
level: StackIdx,
status: LuaStatus,
) -> LuaStatus {
let old_ci = state.ci;
let old_allowhook = state.allowhook;
let mut status = status;
loop {
let mut pcl = CloseP { level, status };
let (run_status, err_value) = match raw_run_protected(state, |s| close_aux(s, &mut pcl)) {
Ok(()) => (LuaStatus::Ok, None),
Err(e) => (e.to_status(), Some(e.into_value())),
};
if run_status == LuaStatus::Ok {
return pcl.status;
}
state.ci = old_ci;
state.allowhook = old_allowhook;
if let Some(v) = err_value {
state.push(v);
}
status = run_status;
}
}
pub(crate) fn pcall<F>(
state: &mut LuaState,
func: F,
old_top: StackIdx,
ef: isize,
) -> LuaStatus
where
F: FnOnce(&mut LuaState) -> Result<(), LuaError>,
{
let old_ci = state.ci;
let old_allowhook = state.allowhook;
let old_errfunc = state.errfunc;
state.errfunc = ef;
let mut status = match raw_run_protected(state, func) {
Ok(()) => LuaStatus::Ok,
Err(e) => {
let s = e.to_status();
state.push(e.into_value());
if ef != 0 && error_status(s) && s != LuaStatus::ErrErr && s != LuaStatus::ErrSyntax {
let errfunc_idx = StackIdx(ef as u32);
let arg = state.get_at(state.top_idx() - 1).clone();
state.push(arg);
let handler = state.get_at(errfunc_idx).clone();
state.set_at(state.top_idx() - 2, handler);
match state.call_no_yield(state.top_idx() - 2, 1) {
Ok(()) => s,
Err(_) => LuaStatus::ErrErr,
}
} else {
s
}
}
};
if status != LuaStatus::Ok {
state.ci = old_ci;
state.allowhook = old_allowhook;
status = close_protected(state, old_top, status);
set_error_obj(state, status, old_top);
shrink_stack(state);
}
state.errfunc = old_errfunc;
status
}
struct SParser {
z: ZIO,
buff: LexBuffer,
dyd: DynDataStub,
mode: Option<Vec<u8>>,
name: Vec<u8>,
}
fn check_mode(
state: &mut LuaState,
mode: Option<&[u8]>,
kind: &[u8],
) -> Result<(), LuaError> {
if let Some(mode_bytes) = mode {
let kind_char = kind[0];
if !mode_bytes.contains(&kind_char) {
return Err(LuaError::syntax(format_args!(
"attempt to load a {} chunk (mode is '{}')",
core::str::from_utf8(kind).unwrap_or("?"),
core::str::from_utf8(mode_bytes).unwrap_or("?"),
)));
}
}
Ok(())
}
fn f_parser(state: &mut LuaState, p: &mut SParser) -> Result<(), LuaError> {
let c = p.z.getc();
let cl = if c == b'\x1b' as i32 {
check_mode(state, p.mode.as_deref(), b"binary")?;
crate::undump::undump(state, &mut p.z, &p.name)?
} else {
check_mode(state, p.mode.as_deref(), b"text")?;
parse_stub(state, &mut p.z, &mut p.buff, &mut p.dyd, &p.name, c)?
};
debug_assert!(cl.upvals.len() == cl.proto.upvalues.len());
func::init_upvals(state, &cl)?;
state.check_stack(1)?;
state.push(LuaValue::Function(LuaClosure::Lua(cl)));
Ok(())
}
pub(crate) fn protected_parser(
state: &mut LuaState,
z: ZIO,
name: &[u8],
mode: Option<&[u8]>,
) -> LuaStatus {
state.inc_nny();
let mut p = SParser {
z,
buff: LexBuffer::new(),
dyd: DynDataStub::new(),
mode: mode.map(|m| m.to_vec()),
name: name.to_vec(),
};
let top_idx = state.top_idx();
let errfunc = state.errfunc;
let status = pcall(state, |s| f_parser(s, &mut p), top_idx, errfunc);
state.dec_nny();
status
}