Skip to content

Commit 5162a0f

Browse files
committed
Add Lua::with_raw_state to provide easy low-level access to the Lua state.
1 parent 640d276 commit 5162a0f

File tree

6 files changed

+74
-11
lines changed

6 files changed

+74
-11
lines changed

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Resul
109109
pub use crate::function::{Function, FunctionInfo};
110110
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
111111
pub use crate::multi::Variadic;
112+
pub use crate::scope::Scope;
112113
pub use crate::state::{GCMode, Lua, LuaOptions};
113-
// pub use crate::scope::Scope;
114114
pub use crate::stdlib::StdLib;
115115
pub use crate::string::{BorrowedBytes, BorrowedStr, String};
116116
pub use crate::table::{Table, TablePairs, TableSequence};

src/state.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ use crate::types::{
2222
ReentrantMutex, ReentrantMutexGuard, RegistryKey, XRc, XWeak,
2323
};
2424
use crate::userdata::{AnyUserData, UserData, UserDataProxy, UserDataRegistry, UserDataStorage};
25-
use crate::util::{assert_stack, check_stack, push_string, push_table, rawset_field, StackGuard};
25+
use crate::util::{
26+
assert_stack, check_stack, protect_lua_closure, push_string, push_table, rawset_field, StackGuard,
27+
};
2628
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
2729

2830
#[cfg(not(feature = "luau"))]
@@ -276,6 +278,28 @@ impl Lua {
276278
}
277279
}
278280

281+
/// Calls provided function passing a raw lua state.
282+
///
283+
/// The arguments will be pushed onto the stack before calling the function.
284+
///
285+
/// This method ensures that the Lua instance is locked while the function is called
286+
/// and restores Lua stack after the function returns.
287+
pub unsafe fn with_raw_state<R: FromLuaMulti>(
288+
&self,
289+
args: impl IntoLuaMulti,
290+
f: impl FnOnce(*mut ffi::lua_State),
291+
) -> Result<R> {
292+
let lua = self.lock();
293+
let state = lua.state();
294+
let _sg = StackGuard::new(state);
295+
let stack_start = ffi::lua_gettop(state);
296+
let nargs = args.push_into_stack_multi(&lua)?;
297+
check_stack(state, 3)?;
298+
protect_lua_closure::<_, ()>(state, nargs, ffi::LUA_MULTRET, f)?;
299+
let nresults = ffi::lua_gettop(state) - stack_start;
300+
R::from_stack_multi(nresults, &lua)
301+
}
302+
279303
/// FIXME: Deprecated load_from_std_lib
280304
281305
/// Loads the specified subset of the standard libraries into an existing Lua state.

src/state/raw.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use {
4343
};
4444

4545
/// An inner Lua struct which holds a raw Lua state.
46+
#[doc(hidden)]
4647
pub struct RawLua {
4748
// The state is dynamic and depends on context
4849
pub(super) state: Cell<*mut ffi::lua_State>,
@@ -83,8 +84,11 @@ impl RawLua {
8384
unsafe { (*self.extra.get()).weak() }
8485
}
8586

87+
/// Returns a pointer to the current Lua state.
88+
///
89+
/// The pointer refers to the active Lua coroutine and depends on the context.
8690
#[inline(always)]
87-
pub(crate) fn state(&self) -> *mut ffi::lua_State {
91+
pub fn state(&self) -> *mut ffi::lua_State {
8892
self.state.get()
8993
}
9094

@@ -500,10 +504,9 @@ impl RawLua {
500504

501505
/// Pushes a value that implements `IntoLua` onto the Lua stack.
502506
///
503-
/// Uses 2 stack spaces, does not call checkstack.
504-
#[doc(hidden)]
507+
/// Uses up to 2 stack spaces to push a single value, does not call `checkstack`.
505508
#[inline(always)]
506-
pub unsafe fn push(&self, value: impl IntoLua) -> Result<()> {
509+
pub(crate) unsafe fn push(&self, value: impl IntoLua) -> Result<()> {
507510
value.push_into_stack(self)
508511
}
509512

src/util/error.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -204,24 +204,25 @@ pub(crate) unsafe fn protect_lua_closure<F, R>(
204204
f: F,
205205
) -> Result<R>
206206
where
207-
F: Fn(*mut ffi::lua_State) -> R,
207+
F: FnOnce(*mut ffi::lua_State) -> R,
208208
R: Copy,
209209
{
210210
struct Params<F, R: Copy> {
211-
function: F,
211+
function: Option<F>,
212212
result: MaybeUninit<R>,
213213
nresults: c_int,
214214
}
215215

216216
unsafe extern "C-unwind" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
217217
where
218-
F: Fn(*mut ffi::lua_State) -> R,
218+
F: FnOnce(*mut ffi::lua_State) -> R,
219219
R: Copy,
220220
{
221221
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
222222
ffi::lua_pop(state, 1);
223223

224-
(*params).result.write(((*params).function)(state));
224+
let f = (*params).function.take().unwrap();
225+
(*params).result.write(f(state));
225226

226227
if (*params).nresults == ffi::LUA_MULTRET {
227228
ffi::lua_gettop(state)
@@ -241,7 +242,7 @@ where
241242
}
242243

243244
let mut params = Params {
244-
function: f,
245+
function: Some(f),
245246
result: MaybeUninit::uninit(),
246247
nresults,
247248
};

src/util/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ impl StackGuard {
6969
}
7070

7171
impl Drop for StackGuard {
72+
#[track_caller]
7273
fn drop(&mut self) {
7374
unsafe {
7475
let top = ffi::lua_gettop(self.state);

tests/tests.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,3 +1289,37 @@ fn test_multi_thread() -> Result<()> {
12891289

12901290
Ok(())
12911291
}
1292+
1293+
#[test]
1294+
fn test_with_raw_state() -> Result<()> {
1295+
let lua = Lua::new();
1296+
1297+
let sum = lua.create_function(|_, args: Variadic<i32>| {
1298+
let mut sum = 0;
1299+
for i in args {
1300+
sum += i;
1301+
}
1302+
Ok(sum)
1303+
})?;
1304+
lua.globals().set("sum", sum)?;
1305+
1306+
let n: i32 = unsafe {
1307+
lua.with_raw_state((), |state| {
1308+
ffi::lua_getglobal(state, b"sum\0".as_ptr() as _);
1309+
ffi::lua_pushinteger(state, 1);
1310+
ffi::lua_pushinteger(state, 7);
1311+
ffi::lua_call(state, 2, 1);
1312+
})
1313+
}?;
1314+
assert_eq!(n, 8);
1315+
1316+
// Test error handling
1317+
let res: Result<()> = unsafe {
1318+
lua.with_raw_state("test error", |state| {
1319+
ffi::lua_error(state);
1320+
})
1321+
};
1322+
assert!(matches!(res, Err(Error::RuntimeError(err)) if err.contains("test error")));
1323+
1324+
Ok(())
1325+
}

0 commit comments

Comments
 (0)