Error correctly on too many arguments / returns / binds / recursions
There are also some other drive-by changes to fix panicking in extern "C" functions and other edge case stack errors
This commit is contained in:
parent
fe6e4bdf35
commit
d331e4b97c
26
src/error.rs
26
src/error.rs
|
@ -26,17 +26,25 @@ pub enum Error {
|
|||
///
|
||||
/// The Lua VM returns this error when there is an error running a `__gc` metamethod.
|
||||
GarbageCollectorError(String),
|
||||
/// A callback has triggered Lua code that has called the same callback again.
|
||||
/// A mutable callback has triggered Lua code that has called the same mutable callback again.
|
||||
///
|
||||
/// This is an error because `rlua` callbacks are FnMut and thus can only be mutably borrowed
|
||||
/// once.
|
||||
RecursiveCallback,
|
||||
/// This is an error because a mutable callback can only be borrowed mutably once.
|
||||
RecursiveMutCallback,
|
||||
/// Either a callback or a userdata method has been called, but the callback or userdata has
|
||||
/// been destructed.
|
||||
///
|
||||
/// This can happen either due to to being destructed in a previous __gc, or due to being
|
||||
/// destructed from exiting a `Lua::scope` call.
|
||||
CallbackDestructed,
|
||||
/// Not enough stack space to place arguments to Lua functions or return values from callbacks.
|
||||
///
|
||||
/// Due to the way `rlua` works, it should not be directly possible to run out of stack space
|
||||
/// during normal use. The only way that this error can be triggered is if a `Function` is
|
||||
/// called with a huge number of arguments, or a rust callback returns a huge number of return
|
||||
/// values.
|
||||
StackError,
|
||||
/// Too many arguments to `Function::bind`
|
||||
BindError,
|
||||
/// A Rust value could not be converted to a Lua value.
|
||||
ToLuaConversionError {
|
||||
/// Name of the Rust type that could not be converted.
|
||||
|
@ -123,11 +131,19 @@ impl fmt::Display for Error {
|
|||
Error::GarbageCollectorError(ref msg) => {
|
||||
write!(fmt, "garbage collector error: {}", msg)
|
||||
}
|
||||
Error::RecursiveCallback => write!(fmt, "callback called recursively"),
|
||||
Error::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"),
|
||||
Error::CallbackDestructed => write!(
|
||||
fmt,
|
||||
"a destructed callback or destructed userdata method was called"
|
||||
),
|
||||
Error::StackError => write!(
|
||||
fmt,
|
||||
"out of Lua stack, too many arguments to a Lua function or too many return values from a callback"
|
||||
),
|
||||
Error::BindError => write!(
|
||||
fmt,
|
||||
"too many arguments to Function::bind"
|
||||
),
|
||||
Error::ToLuaConversionError {
|
||||
from,
|
||||
to,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::ptr;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use ffi;
|
||||
|
@ -65,7 +66,7 @@ impl<'lua> Function<'lua> {
|
|||
stack_err_guard(lua.state, 0, || {
|
||||
let args = args.to_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
check_stack(lua.state, nargs + 3);
|
||||
check_stack_err(lua.state, nargs + 3)?;
|
||||
|
||||
ffi::lua_pushcfunction(lua.state, error_traceback);
|
||||
let stack_start = ffi::lua_gettop(lua.state);
|
||||
|
@ -124,7 +125,7 @@ impl<'lua> Function<'lua> {
|
|||
unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(2)) as c_int;
|
||||
check_stack(state, nbinds + 2);
|
||||
ffi::luaL_checkstack(state, nbinds + 2, ptr::null());
|
||||
|
||||
ffi::lua_settop(state, nargs + nbinds + 1);
|
||||
ffi::lua_rotate(state, -(nargs + nbinds + 1), nbinds + 1);
|
||||
|
@ -144,10 +145,16 @@ impl<'lua> Function<'lua> {
|
|||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
stack_err_guard(lua.state, 0, || {
|
||||
const MAX_LUA_UPVALUES: c_int = 255;
|
||||
|
||||
let args = args.to_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
|
||||
check_stack(lua.state, nargs + 3);
|
||||
if nargs > MAX_LUA_UPVALUES {
|
||||
return Err(Error::BindError);
|
||||
}
|
||||
|
||||
check_stack_err(lua.state, nargs + 3)?;
|
||||
lua.push_ref(lua.state, &self.0);
|
||||
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
|
||||
for arg in args {
|
||||
|
|
74
src/lua.rs
74
src/lua.rs
|
@ -1,6 +1,5 @@
|
|||
use std::{mem, process, ptr, str};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::ops::DerefMut;
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::CString;
|
||||
use std::any::{Any, TypeId};
|
||||
|
@ -32,7 +31,7 @@ pub struct Lua {
|
|||
/// !Send, and callbacks that are !Send and not 'static.
|
||||
pub struct Scope<'lua, 'scope> {
|
||||
lua: &'lua Lua,
|
||||
destructors: RefCell<Vec<Box<FnMut(*mut ffi::lua_State) -> Box<Any>>>>,
|
||||
destructors: RefCell<Vec<Box<Fn(*mut ffi::lua_State) -> Box<Any>>>>,
|
||||
// 'scope lifetime must be invariant
|
||||
_scope: PhantomData<&'scope mut &'scope ()>,
|
||||
}
|
||||
|
@ -265,18 +264,31 @@ impl Lua {
|
|||
///
|
||||
/// [`ToLua`]: trait.ToLua.html
|
||||
/// [`ToLuaMulti`]: trait.ToLuaMulti.html
|
||||
pub fn create_function<'lua, 'callback, A, R, F>(
|
||||
pub fn create_function<'lua, 'callback, A, R, F>(&'lua self, func: F) -> Result<Function<'lua>>
|
||||
where
|
||||
A: FromLuaMulti<'callback>,
|
||||
R: ToLuaMulti<'callback>,
|
||||
F: 'static + Send + Fn(&'callback Lua, A) -> Result<R>,
|
||||
{
|
||||
self.create_callback_function(Box::new(move |lua, args| {
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn create_function_mut<'lua, 'callback, A, R, F>(
|
||||
&'lua self,
|
||||
mut func: F,
|
||||
func: F,
|
||||
) -> Result<Function<'lua>>
|
||||
where
|
||||
A: FromLuaMulti<'callback>,
|
||||
R: ToLuaMulti<'callback>,
|
||||
F: 'static + Send + FnMut(&'callback Lua, A) -> Result<R>,
|
||||
{
|
||||
self.create_callback_function(Box::new(move |lua, args| {
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}))
|
||||
let func = RefCell::new(func);
|
||||
self.create_function(move |lua, args| {
|
||||
(&mut *func.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?)(lua, args)
|
||||
})
|
||||
}
|
||||
|
||||
/// Wraps a Lua function into a new thread (or coroutine).
|
||||
|
@ -354,7 +366,7 @@ impl Lua {
|
|||
Value::String(s) => Ok(s),
|
||||
v => unsafe {
|
||||
stack_err_guard(self.state, 0, || {
|
||||
check_stack(self.state, 2);
|
||||
check_stack(self.state, 4);
|
||||
let ty = v.type_name();
|
||||
self.push_value(self.state, v);
|
||||
let s =
|
||||
|
@ -383,7 +395,7 @@ impl Lua {
|
|||
Value::Integer(i) => Ok(i),
|
||||
v => unsafe {
|
||||
stack_guard(self.state, 0, || {
|
||||
check_stack(self.state, 1);
|
||||
check_stack(self.state, 2);
|
||||
let ty = v.type_name();
|
||||
self.push_value(self.state, v);
|
||||
let mut isint = 0;
|
||||
|
@ -412,7 +424,7 @@ impl Lua {
|
|||
Value::Number(n) => Ok(n),
|
||||
v => unsafe {
|
||||
stack_guard(self.state, 0, || {
|
||||
check_stack(self.state, 1);
|
||||
check_stack(self.state, 2);
|
||||
let ty = v.type_name();
|
||||
self.push_value(self.state, v);
|
||||
let mut isnum = 0;
|
||||
|
@ -511,7 +523,7 @@ impl Lua {
|
|||
pub fn create_registry_value<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> Result<RegistryKey> {
|
||||
unsafe {
|
||||
stack_guard(self.state, 0, || {
|
||||
check_stack(self.state, 1);
|
||||
check_stack(self.state, 2);
|
||||
|
||||
self.push_value(self.state, t.to_lua(self)?);
|
||||
let registry_id = gc_guard(self.state, || {
|
||||
|
@ -592,7 +604,7 @@ impl Lua {
|
|||
}
|
||||
}
|
||||
|
||||
// Uses 1 stack space, does not call checkstack
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
pub(crate) unsafe fn push_value(&self, state: *mut ffi::lua_State, value: Value) {
|
||||
match value {
|
||||
Value::Nil => {
|
||||
|
@ -730,7 +742,7 @@ impl Lua {
|
|||
// Used if both an __index metamethod is set and regular methods, checks methods table
|
||||
// first, then __index metamethod.
|
||||
unsafe extern "C" fn meta_index_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
check_stack(state, 2);
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_gettable(state, ffi::lua_upvalueindex(1));
|
||||
|
@ -922,7 +934,7 @@ impl Lua {
|
|||
ffi::lua_newtable(state);
|
||||
|
||||
push_string(state, "__gc").unwrap();
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<RefCell<Callback>>);
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<Callback>);
|
||||
ffi::lua_rawset(state, -3);
|
||||
|
||||
push_string(state, "__metatable").unwrap();
|
||||
|
@ -977,10 +989,7 @@ impl Lua {
|
|||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
|
||||
let func = get_userdata::<RefCell<Callback>>(state, ffi::lua_upvalueindex(1));
|
||||
let mut func = (*func)
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveCallback)?;
|
||||
let func = get_userdata::<Callback>(state, ffi::lua_upvalueindex(1));
|
||||
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
let mut args = MultiValue::new();
|
||||
|
@ -989,10 +998,10 @@ impl Lua {
|
|||
args.push_front(lua.pop_value(state));
|
||||
}
|
||||
|
||||
let results = func.deref_mut()(&lua, args)?;
|
||||
let results = (*func)(&lua, args)?;
|
||||
let nresults = results.len() as c_int;
|
||||
|
||||
check_stack(state, nresults);
|
||||
check_stack_err(state, nresults)?;
|
||||
|
||||
for r in results {
|
||||
lua.push_value(state, r);
|
||||
|
@ -1006,7 +1015,7 @@ impl Lua {
|
|||
stack_err_guard(self.state, 0, move || {
|
||||
check_stack(self.state, 2);
|
||||
|
||||
push_userdata::<RefCell<Callback>>(self.state, RefCell::new(func))?;
|
||||
push_userdata::<Callback>(self.state, func)?;
|
||||
|
||||
ffi::lua_pushlightuserdata(
|
||||
self.state,
|
||||
|
@ -1053,15 +1062,15 @@ impl Lua {
|
|||
}
|
||||
|
||||
impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
pub fn create_function<'callback, A, R, F>(&self, mut func: F) -> Result<Function<'lua>>
|
||||
pub fn create_function<'callback, A, R, F>(&self, func: F) -> Result<Function<'lua>>
|
||||
where
|
||||
A: FromLuaMulti<'callback>,
|
||||
R: ToLuaMulti<'callback>,
|
||||
F: 'scope + FnMut(&'callback Lua, A) -> Result<R>,
|
||||
F: 'scope + Fn(&'callback Lua, A) -> Result<R>,
|
||||
{
|
||||
unsafe {
|
||||
let f: Box<
|
||||
FnMut(&'callback Lua, MultiValue<'callback>) -> Result<MultiValue<'callback>>,
|
||||
Fn(&'callback Lua, MultiValue<'callback>) -> Result<MultiValue<'callback>>,
|
||||
> = Box::new(move |lua, args| {
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
});
|
||||
|
@ -1082,7 +1091,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||
ffi::luaL_unref(state, ffi::LUA_REGISTRYINDEX, registry_id);
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let ud = take_userdata::<RefCell<Callback>>(state);
|
||||
let ud = take_userdata::<Callback>(state);
|
||||
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
|
@ -1094,6 +1103,19 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn create_function_mut<'callback, A, R, F>(&self, func: F) -> Result<Function<'lua>>
|
||||
where
|
||||
A: FromLuaMulti<'callback>,
|
||||
R: ToLuaMulti<'callback>,
|
||||
F: 'scope + FnMut(&'callback Lua, A) -> Result<R>,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
self.create_function(move |lua, args| {
|
||||
(&mut *func.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?)(lua, args)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: UserData,
|
||||
|
@ -1128,7 +1150,7 @@ impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
|
|||
let to_drop = self.destructors
|
||||
.get_mut()
|
||||
.drain(..)
|
||||
.map(|mut destructor| destructor(state))
|
||||
.map(|destructor| destructor(state))
|
||||
.collect::<Vec<_>>();
|
||||
drop(to_drop);
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ impl<'lua> Table<'lua> {
|
|||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
stack_err_guard(lua.state, 0, || {
|
||||
check_stack(lua.state, 3);
|
||||
check_stack(lua.state, 6);
|
||||
lua.push_ref(lua.state, &self.0);
|
||||
lua.push_value(lua.state, key.to_lua(lua)?);
|
||||
lua.push_value(lua.state, value.to_lua(lua)?);
|
||||
|
@ -142,7 +142,7 @@ impl<'lua> Table<'lua> {
|
|||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
stack_err_guard(lua.state, 0, || {
|
||||
check_stack(lua.state, 2);
|
||||
check_stack(lua.state, 3);
|
||||
lua.push_ref(lua.state, &self.0);
|
||||
lua.push_value(lua.state, key.to_lua(lua)?);
|
||||
ffi::lua_rawget(lua.state, -2);
|
||||
|
|
75
src/tests.rs
75
src/tests.rs
|
@ -1,5 +1,5 @@
|
|||
use std::fmt;
|
||||
use std::error;
|
||||
use std::{error, fmt};
|
||||
use std::iter::FromIterator;
|
||||
use std::rc::Rc;
|
||||
use std::cell::Cell;
|
||||
use std::sync::Arc;
|
||||
|
@ -432,11 +432,11 @@ fn test_pcall_xpcall() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_recursive_callback_error() {
|
||||
fn test_recursive_mut_callback_error() {
|
||||
let lua = Lua::new();
|
||||
|
||||
let mut v = Some(Box::new(123));
|
||||
let f = lua.create_function::<_, (), _>(move |lua, mutate: bool| {
|
||||
let f = lua.create_function_mut::<_, (), _>(move |lua, mutate: bool| {
|
||||
if mutate {
|
||||
v = None;
|
||||
} else {
|
||||
|
@ -459,7 +459,7 @@ fn test_recursive_callback_error() {
|
|||
{
|
||||
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
|
||||
Error::CallbackError { ref cause, .. } => match *cause.as_ref() {
|
||||
Error::RecursiveCallback { .. } => {}
|
||||
Error::RecursiveMutCallback { .. } => {}
|
||||
ref other => panic!("incorrect result: {:?}", other),
|
||||
},
|
||||
ref other => panic!("incorrect result: {:?}", other),
|
||||
|
@ -527,7 +527,7 @@ fn test_registry_value() {
|
|||
let lua = Lua::new();
|
||||
|
||||
let mut r = Some(lua.create_registry_value::<i32>(42).unwrap());
|
||||
let f = lua.create_function(move |lua, ()| {
|
||||
let f = lua.create_function_mut(move |lua, ()| {
|
||||
if let Some(r) = r.take() {
|
||||
assert_eq!(lua.registry_value::<i32>(&r)?, 42);
|
||||
lua.remove_registry_value(r).unwrap();
|
||||
|
@ -666,7 +666,7 @@ fn scope_capture() {
|
|||
let lua = Lua::new();
|
||||
lua.scope(|scope| {
|
||||
scope
|
||||
.create_function(|_, ()| {
|
||||
.create_function_mut(|_, ()| {
|
||||
i = 42;
|
||||
Ok(())
|
||||
})
|
||||
|
@ -677,6 +677,67 @@ fn scope_capture() {
|
|||
assert_eq!(i, 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_many_returns() {
|
||||
let lua = Lua::new();
|
||||
let f = lua.create_function(|_, ()| Ok(Variadic::from_iter(1..1000000)))
|
||||
.unwrap();
|
||||
assert!(f.call::<_, Vec<u32>>(()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_many_arguments() {
|
||||
let lua = Lua::new();
|
||||
lua.exec::<()>("function test(...) end", None).unwrap();
|
||||
let args = Variadic::from_iter(1..1000000);
|
||||
assert!(
|
||||
lua.globals()
|
||||
.get::<_, Function>("test")
|
||||
.unwrap()
|
||||
.call::<_, ()>(args)
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_many_recursions() {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua.create_function(move |lua, ()| {
|
||||
lua.globals().get::<_, Function>("f")?.call::<_, ()>(())
|
||||
}).unwrap();
|
||||
lua.globals().set("f", f).unwrap();
|
||||
|
||||
assert!(
|
||||
lua.globals()
|
||||
.get::<_, Function>("f")
|
||||
.unwrap()
|
||||
.call::<_, ()>(())
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_many_binds() {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
lua.exec::<()>(
|
||||
r#"
|
||||
function f(...)
|
||||
end
|
||||
"#,
|
||||
None,
|
||||
).unwrap();
|
||||
|
||||
let concat = globals.get::<_, Function>("f").unwrap();
|
||||
assert!(concat.bind(Variadic::from_iter(1..1000000)).is_err());
|
||||
assert!(
|
||||
concat
|
||||
.call::<_, ()>(Variadic::from_iter(1..1000000))
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Need to use compiletest-rs or similar to make sure these don't compile.
|
||||
/*
|
||||
#[test]
|
||||
|
|
|
@ -93,7 +93,7 @@ impl<'lua> Thread<'lua> {
|
|||
|
||||
let args = args.to_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
check_stack(thread_state, nargs);
|
||||
check_stack_err(thread_state, nargs + 1)?;
|
||||
|
||||
for arg in args {
|
||||
lua.push_value(thread_state, arg);
|
||||
|
|
|
@ -39,8 +39,7 @@ impl Drop for RegistryKey {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) type Callback<'lua> =
|
||||
Box<FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>;
|
||||
pub(crate) type Callback<'lua> = Box<Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>;
|
||||
|
||||
pub(crate) struct LuaRef<'lua> {
|
||||
pub lua: &'lua Lua,
|
||||
|
|
|
@ -89,7 +89,7 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
|
|||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + Send + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result<R>,
|
||||
M: 'static + Send + for<'a> Fn(&'lua Lua, &'a T, A) -> Result<R>,
|
||||
{
|
||||
self.methods
|
||||
.insert(name.to_owned(), Self::box_method(method));
|
||||
|
@ -121,7 +121,7 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
|
|||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
self.methods
|
||||
.insert(name.to_owned(), Self::box_function(function));
|
||||
|
@ -139,7 +139,7 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
|
|||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + Send + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result<R>,
|
||||
M: 'static + Send + for<'a> Fn(&'lua Lua, &'a T, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods.insert(meta, Self::box_method(method));
|
||||
}
|
||||
|
@ -170,25 +170,25 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
|
|||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods.insert(meta, Self::box_function(function));
|
||||
}
|
||||
|
||||
fn box_function<A, R, F>(mut function: F) -> Callback<'lua>
|
||||
fn box_function<A, R, F>(function: F) -> Callback<'lua>
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
Box::new(move |lua, args| function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua))
|
||||
}
|
||||
|
||||
fn box_method<A, R, M>(mut method: M) -> Callback<'lua>
|
||||
fn box_method<A, R, M>(method: M) -> Callback<'lua>
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + Send + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result<R>,
|
||||
M: 'static + Send + for<'a> Fn(&'lua Lua, &'a T, A) -> Result<R>,
|
||||
{
|
||||
Box::new(move |lua, mut args| {
|
||||
if let Some(front) = args.pop_front() {
|
||||
|
@ -205,17 +205,21 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
|
|||
})
|
||||
}
|
||||
|
||||
fn box_method_mut<A, R, M>(mut method: M) -> Callback<'lua>
|
||||
fn box_method_mut<A, R, M>(method: M) -> Callback<'lua>
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + Send + for<'a> FnMut(&'lua Lua, &'a mut T, A) -> Result<R>,
|
||||
{
|
||||
let method = RefCell::new(method);
|
||||
Box::new(move |lua, mut args| {
|
||||
if let Some(front) = args.pop_front() {
|
||||
let userdata = AnyUserData::from_lua(front, lua)?;
|
||||
let mut userdata = userdata.borrow_mut::<T>()?;
|
||||
method(lua, &mut userdata, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
let mut method = method
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
(&mut *method)(lua, &mut userdata, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: "missing argument",
|
||||
|
|
29
src/util.rs
29
src/util.rs
|
@ -8,8 +8,8 @@ use std::panic::{catch_unwind, resume_unwind, UnwindSafe};
|
|||
use ffi;
|
||||
use error::{Error, Result};
|
||||
|
||||
// Checks that Lua has enough free stack space for future stack operations.
|
||||
// On failure, this will clear the stack and panic.
|
||||
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
||||
// clear the stack and panic.
|
||||
pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) {
|
||||
lua_internal_assert!(
|
||||
state,
|
||||
|
@ -18,6 +18,16 @@ pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) {
|
|||
);
|
||||
}
|
||||
|
||||
// Similar to `check_stack`, but returns `Error::StackError` on failure. Useful for user controlled
|
||||
// sizes, which should not cause a panic.
|
||||
pub unsafe fn check_stack_err(state: *mut ffi::lua_State, amount: c_int) -> Result<()> {
|
||||
if ffi::lua_checkstack(state, amount) == 0 {
|
||||
Err(Error::StackError)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Run an operation on a lua_State and check that the stack change is what is
|
||||
// expected. If the stack change does not match, clears the stack and panics.
|
||||
pub unsafe fn stack_guard<F, R>(state: *mut ffi::lua_State, change: c_int, op: F) -> R
|
||||
|
@ -279,10 +289,12 @@ where
|
|||
match catch_unwind(f) {
|
||||
Ok(Ok(r)) => r,
|
||||
Ok(Err(err)) => {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
push_wrapped_error(state, err);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
Err(p) => {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
push_wrapped_panic(state, p);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
|
@ -293,6 +305,8 @@ where
|
|||
// Error::CallbackError with a traceback, if it is some lua type, prints the error along with a
|
||||
// traceback, and if it is a WrappedPanic, does not modify it.
|
||||
pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
|
||||
if let Some(error) = pop_wrapped_error(state) {
|
||||
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
||||
let traceback = CStr::from_ptr(ffi::lua_tostring(state, -1))
|
||||
|
@ -386,10 +400,9 @@ pub unsafe fn main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State {
|
|||
main_state
|
||||
}
|
||||
|
||||
// Pushes a WrappedError::Error to the top of the stack
|
||||
// Pushes a WrappedError::Error to the top of the stack. Uses two stack spaces and does not call
|
||||
// lua_checkstack.
|
||||
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
|
||||
gc_guard(state, || {
|
||||
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
|
||||
ptr::write(ud, WrappedError(err))
|
||||
|
@ -432,10 +445,9 @@ pub unsafe fn gc_guard<R, F: FnOnce() -> R>(state: *mut ffi::lua_State, f: F) ->
|
|||
struct WrappedError(pub Error);
|
||||
struct WrappedPanic(pub Option<Box<Any + Send>>);
|
||||
|
||||
// Pushes a WrappedError::Panic to the top of the stack
|
||||
// Pushes a WrappedError::Panic to the top of the stack. Uses two stack spaces and does not call
|
||||
// lua_checkstack.
|
||||
unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box<Any + Send>) {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
|
||||
gc_guard(state, || {
|
||||
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedPanic>()) as *mut WrappedPanic;
|
||||
ptr::write(ud, WrappedPanic(Some(panic)))
|
||||
|
@ -598,6 +610,7 @@ unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) -> c_int
|
|||
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
||||
|
||||
unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
push_wrapped_error(state, Error::CallbackDestructed);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue