Skip to main content

luaur_vm/functions/
lua_d_pcall.rs

1//! Node: `cxx:Function:Luau.VM:VM/src/ldo.cpp:729:lua_d_pcall`
2//! Source: `VM/src/ldo.cpp` (ldo.cpp:729-795, hand-ported)
3
4use crate::enums::lua_status::lua_Status;
5use crate::functions::callerrfunc::callerrfunc;
6use crate::functions::lua_d_rawrunprotected_ldo_alt_b::lua_d_rawrunprotected_mut;
7use crate::functions::lua_d_seterrorobj::luaD_seterrorobj;
8use crate::functions::lua_f_close::lua_f_close as luaF_close;
9use crate::functions::restore_stack_limit::restore_stack_limit;
10use crate::macros::clvalue::clvalue;
11use crate::macros::restoreci::restoreci;
12use crate::macros::restorestack::restorestack;
13use crate::macros::saveci::saveci;
14use crate::records::call_info::CallInfo;
15use crate::type_aliases::lua_state::lua_State;
16use crate::type_aliases::pfunc::Pfunc;
17use crate::type_aliases::stk_id::StkId;
18use luaur_common::macros::luau_assert::LUAU_ASSERT;
19
20#[allow(non_snake_case)]
21pub unsafe fn luaD_pcall(
22    L: *mut lua_State,
23    func: Pfunc,
24    u: *mut core::ffi::c_void,
25    old_top: isize,
26    ef: isize,
27) -> i32 {
28    let oldnCcalls: u16 = (*L).nCcalls;
29    let oldbaseCcalls: u16 = (*L).baseCcalls;
30    let old_ci: isize = saveci!(L, (*L).ci);
31    let oldactive: bool = (*L).isactive;
32    let mut status: i32 = lua_d_rawrunprotected_mut(L, func, u);
33    if status != 0 {
34        let mut errstatus: i32 = status;
35
36        if luaur_common::FFlag::LuauClosureUsageCounter.get() {
37            let mut lastci: *mut CallInfo = (*L).ci;
38            let savedci: *mut CallInfo = restoreci!(L, old_ci);
39            while lastci != savedci {
40                let cl =
41                    clvalue!((*lastci).func) as *const _ as *mut crate::records::closure::Closure;
42                LUAU_ASSERT!((*cl).usage > 0);
43                (*cl).usage -= 1;
44                lastci = lastci.offset(-1);
45            }
46        }
47
48        // call user-defined error function (used in xpcall)
49        if ef != 0 {
50            // push error object to stack top if it's not already there
51            if status != lua_Status::LUA_ERRRUN as i32 {
52                luaD_seterrorobj(L, status, (*L).top);
53            }
54
55            // if errfunc fails, we fail with "error in error handling" or "not enough memory"
56            let err = lua_d_rawrunprotected_mut(
57                L,
58                Some(callerrfunc),
59                restorestack!(L, ef) as *mut core::ffi::c_void,
60            );
61
62            // in general we preserve the status, except for cases when the error handler fails
63            // out of memory is treated specially because it's common for it to be cascading, in which case we preserve the code
64            if err == 0 {
65                errstatus = lua_Status::LUA_ERRRUN as i32;
66            } else if status == lua_Status::LUA_ERRMEM as i32
67                && err == lua_Status::LUA_ERRMEM as i32
68            {
69                errstatus = lua_Status::LUA_ERRMEM as i32;
70            } else {
71                errstatus = lua_Status::LUA_ERRERR as i32;
72                status = lua_Status::LUA_ERRERR as i32;
73                LUAU_ASSERT!(errstatus != 0);
74            }
75        }
76
77        // since the call failed with an error, we might have to reset the 'active' thread state
78        if !oldactive {
79            (*L).isactive = false;
80        }
81
82        // Inlined logic from 'lua_isyieldable' to avoid potential for an out of line call.
83        let yieldable: bool = (*L).nCcalls <= (*L).baseCcalls;
84
85        // restore nCcalls and baseCcalls before calling the debugprotectederror callback which may rely on the proper value to have been restored.
86        (*L).nCcalls = oldnCcalls;
87        (*L).baseCcalls = oldbaseCcalls;
88
89        // an error occurred, check if we have a protected error callback
90        if yieldable {
91            if let Some(debugprotectederror) = (*(*L).global).cb.debugprotectederror {
92                debugprotectederror(L);
93
94                // debug hook is only allowed to break
95                if (*L).status as i32 == lua_Status::LUA_BREAK as i32 {
96                    return 0;
97                }
98            }
99        }
100
101        let oldtop: StkId = restorestack!(L, old_top);
102        luaF_close(L, oldtop); // close eventual pending closures
103        luaD_seterrorobj(L, errstatus, oldtop);
104        (*L).ci = restoreci!(L, old_ci);
105        (*L).base = (*(*L).ci).base;
106        restore_stack_limit(L);
107    }
108    status
109}
110
111#[allow(unused_imports)]
112pub use luaD_pcall as lua_d_pcall;