use std::cell::Cell;
use crate::error::Result;
use crate::function::Function;
use crate::multi::MultiValue;
use crate::state::Lua;
use crate::sys::*;
use crate::traits::{FromLuaMulti, IntoLuaMulti};
type RawFn = Box<dyn FnOnce(*mut lua_State)>;
struct RawFnSlot(Cell<Option<RawFn>>);
unsafe extern "C" fn raw_fn_dtor(ptr: *mut c_void) {
if !ptr.is_null() {
unsafe { core::ptr::drop_in_place(ptr as *mut RawFnSlot) };
}
}
unsafe fn exec_raw_trampoline(state: *mut lua_State) -> c_int {
unsafe {
let ud = lua_touserdata(state, lua_upvalueindex(1));
if ud.is_null() {
return 0;
}
let slot = &*(ud as *const RawFnSlot);
let f = slot.0.take();
let base = lua_gettop(state);
if let Some(f) = f {
f(state);
}
let top = lua_gettop(state);
(top - base).max(0)
}
}
fn exec_raw_trampoline_ptr() -> lua_CFunction {
Some(exec_raw_trampoline)
}
impl Lua {
pub unsafe fn exec_raw<R, F>(&self, args: impl IntoLuaMulti, f: F) -> Result<R>
where
R: FromLuaMulti,
F: FnOnce(*mut lua_State),
{
let state = self.state();
let args: MultiValue = args.into_lua_multi(self)?;
let boxed: RawFn = {
let f: Box<dyn FnOnce(*mut lua_State) + '_> = Box::new(f);
unsafe { core::mem::transmute::<Box<dyn FnOnce(*mut lua_State) + '_>, RawFn>(f) }
};
unsafe {
let nargs = args.len() as c_int;
if lua_checkstack(state, nargs.saturating_add(2)) == 0 {
return Err(crate::error::Error::runtime("stack overflow in exec_raw"));
}
let storage =
lua_newuserdatadtor(state, core::mem::size_of::<RawFnSlot>(), Some(raw_fn_dtor));
if storage.is_null() {
return Err(crate::error::Error::runtime(
"exec_raw: failed to allocate closure userdata",
));
}
core::ptr::write(storage as *mut RawFnSlot, RawFnSlot(Cell::new(Some(boxed))));
lua_pushcclosurek(
state,
exec_raw_trampoline_ptr(),
c"luaur-rt-exec-raw".as_ptr(),
1,
None,
);
let base = lua_gettop(state) - 1; for v in args.iter() {
self.push_value(v)?;
}
let status = lua_pcall(state, nargs, -1, 0);
if status != 0 {
return Err(self.pop_error(status));
}
let top = lua_gettop(state);
let nresults = top - base;
let mut results = MultiValue::with_capacity(nresults.max(0) as usize);
for i in 0..nresults {
results.push_back(self.value_from_stack(base + 1 + i)?);
}
lua_settop(state, base);
R::from_lua_multi(results, self)
}
}
pub unsafe fn create_c_function(&self, func: lua_CFunction) -> Result<Function> {
let state = self.state();
unsafe {
lua_pushcclosurek(state, func, c"luaur-rt-c-function".as_ptr(), 0, None);
Ok(Function::from_ref(self.pop_ref()))
}
}
}