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.
|
/// The Lua VM returns this error when there is an error running a `__gc` metamethod.
|
||||||
GarbageCollectorError(String),
|
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
|
/// This is an error because a mutable callback can only be borrowed mutably once.
|
||||||
/// once.
|
RecursiveMutCallback,
|
||||||
RecursiveCallback,
|
|
||||||
/// Either a callback or a userdata method has been called, but the callback or userdata has
|
/// Either a callback or a userdata method has been called, but the callback or userdata has
|
||||||
/// been destructed.
|
/// been destructed.
|
||||||
///
|
///
|
||||||
/// This can happen either due to to being destructed in a previous __gc, or due to being
|
/// This can happen either due to to being destructed in a previous __gc, or due to being
|
||||||
/// destructed from exiting a `Lua::scope` call.
|
/// destructed from exiting a `Lua::scope` call.
|
||||||
CallbackDestructed,
|
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.
|
/// A Rust value could not be converted to a Lua value.
|
||||||
ToLuaConversionError {
|
ToLuaConversionError {
|
||||||
/// Name of the Rust type that could not be converted.
|
/// Name of the Rust type that could not be converted.
|
||||||
|
@ -123,11 +131,19 @@ impl fmt::Display for Error {
|
||||||
Error::GarbageCollectorError(ref msg) => {
|
Error::GarbageCollectorError(ref msg) => {
|
||||||
write!(fmt, "garbage collector error: {}", 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!(
|
Error::CallbackDestructed => write!(
|
||||||
fmt,
|
fmt,
|
||||||
"a destructed callback or destructed userdata method was called"
|
"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 {
|
Error::ToLuaConversionError {
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::ptr;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
use ffi;
|
use ffi;
|
||||||
|
@ -65,7 +66,7 @@ impl<'lua> Function<'lua> {
|
||||||
stack_err_guard(lua.state, 0, || {
|
stack_err_guard(lua.state, 0, || {
|
||||||
let args = args.to_lua_multi(lua)?;
|
let args = args.to_lua_multi(lua)?;
|
||||||
let nargs = args.len() as c_int;
|
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);
|
ffi::lua_pushcfunction(lua.state, error_traceback);
|
||||||
let stack_start = ffi::lua_gettop(lua.state);
|
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 {
|
unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int {
|
||||||
let nargs = ffi::lua_gettop(state);
|
let nargs = ffi::lua_gettop(state);
|
||||||
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(2)) as c_int;
|
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_settop(state, nargs + nbinds + 1);
|
||||||
ffi::lua_rotate(state, -(nargs + nbinds + 1), nbinds + 1);
|
ffi::lua_rotate(state, -(nargs + nbinds + 1), nbinds + 1);
|
||||||
|
@ -144,10 +145,16 @@ impl<'lua> Function<'lua> {
|
||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_err_guard(lua.state, 0, || {
|
stack_err_guard(lua.state, 0, || {
|
||||||
|
const MAX_LUA_UPVALUES: c_int = 255;
|
||||||
|
|
||||||
let args = args.to_lua_multi(lua)?;
|
let args = args.to_lua_multi(lua)?;
|
||||||
let nargs = args.len() as c_int;
|
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);
|
lua.push_ref(lua.state, &self.0);
|
||||||
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
|
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
|
||||||
for arg in args {
|
for arg in args {
|
||||||
|
|
74
src/lua.rs
74
src/lua.rs
|
@ -1,6 +1,5 @@
|
||||||
use std::{mem, process, ptr, str};
|
use std::{mem, process, ptr, str};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::ops::DerefMut;
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::any::{Any, TypeId};
|
use std::any::{Any, TypeId};
|
||||||
|
@ -32,7 +31,7 @@ pub struct Lua {
|
||||||
/// !Send, and callbacks that are !Send and not 'static.
|
/// !Send, and callbacks that are !Send and not 'static.
|
||||||
pub struct Scope<'lua, 'scope> {
|
pub struct Scope<'lua, 'scope> {
|
||||||
lua: &'lua Lua,
|
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 lifetime must be invariant
|
||||||
_scope: PhantomData<&'scope mut &'scope ()>,
|
_scope: PhantomData<&'scope mut &'scope ()>,
|
||||||
}
|
}
|
||||||
|
@ -265,18 +264,31 @@ impl Lua {
|
||||||
///
|
///
|
||||||
/// [`ToLua`]: trait.ToLua.html
|
/// [`ToLua`]: trait.ToLua.html
|
||||||
/// [`ToLuaMulti`]: trait.ToLuaMulti.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,
|
&'lua self,
|
||||||
mut func: F,
|
func: F,
|
||||||
) -> Result<Function<'lua>>
|
) -> Result<Function<'lua>>
|
||||||
where
|
where
|
||||||
A: FromLuaMulti<'callback>,
|
A: FromLuaMulti<'callback>,
|
||||||
R: ToLuaMulti<'callback>,
|
R: ToLuaMulti<'callback>,
|
||||||
F: 'static + Send + FnMut(&'callback Lua, A) -> Result<R>,
|
F: 'static + Send + FnMut(&'callback Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.create_callback_function(Box::new(move |lua, args| {
|
let func = RefCell::new(func);
|
||||||
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
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).
|
/// Wraps a Lua function into a new thread (or coroutine).
|
||||||
|
@ -354,7 +366,7 @@ impl Lua {
|
||||||
Value::String(s) => Ok(s),
|
Value::String(s) => Ok(s),
|
||||||
v => unsafe {
|
v => unsafe {
|
||||||
stack_err_guard(self.state, 0, || {
|
stack_err_guard(self.state, 0, || {
|
||||||
check_stack(self.state, 2);
|
check_stack(self.state, 4);
|
||||||
let ty = v.type_name();
|
let ty = v.type_name();
|
||||||
self.push_value(self.state, v);
|
self.push_value(self.state, v);
|
||||||
let s =
|
let s =
|
||||||
|
@ -383,7 +395,7 @@ impl Lua {
|
||||||
Value::Integer(i) => Ok(i),
|
Value::Integer(i) => Ok(i),
|
||||||
v => unsafe {
|
v => unsafe {
|
||||||
stack_guard(self.state, 0, || {
|
stack_guard(self.state, 0, || {
|
||||||
check_stack(self.state, 1);
|
check_stack(self.state, 2);
|
||||||
let ty = v.type_name();
|
let ty = v.type_name();
|
||||||
self.push_value(self.state, v);
|
self.push_value(self.state, v);
|
||||||
let mut isint = 0;
|
let mut isint = 0;
|
||||||
|
@ -412,7 +424,7 @@ impl Lua {
|
||||||
Value::Number(n) => Ok(n),
|
Value::Number(n) => Ok(n),
|
||||||
v => unsafe {
|
v => unsafe {
|
||||||
stack_guard(self.state, 0, || {
|
stack_guard(self.state, 0, || {
|
||||||
check_stack(self.state, 1);
|
check_stack(self.state, 2);
|
||||||
let ty = v.type_name();
|
let ty = v.type_name();
|
||||||
self.push_value(self.state, v);
|
self.push_value(self.state, v);
|
||||||
let mut isnum = 0;
|
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> {
|
pub fn create_registry_value<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> Result<RegistryKey> {
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_guard(self.state, 0, || {
|
stack_guard(self.state, 0, || {
|
||||||
check_stack(self.state, 1);
|
check_stack(self.state, 2);
|
||||||
|
|
||||||
self.push_value(self.state, t.to_lua(self)?);
|
self.push_value(self.state, t.to_lua(self)?);
|
||||||
let registry_id = gc_guard(self.state, || {
|
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) {
|
pub(crate) unsafe fn push_value(&self, state: *mut ffi::lua_State, value: Value) {
|
||||||
match value {
|
match value {
|
||||||
Value::Nil => {
|
Value::Nil => {
|
||||||
|
@ -730,7 +742,7 @@ impl Lua {
|
||||||
// Used if both an __index metamethod is set and regular methods, checks methods table
|
// Used if both an __index metamethod is set and regular methods, checks methods table
|
||||||
// first, then __index metamethod.
|
// first, then __index metamethod.
|
||||||
unsafe extern "C" fn meta_index_impl(state: *mut ffi::lua_State) -> c_int {
|
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_pushvalue(state, -1);
|
||||||
ffi::lua_gettable(state, ffi::lua_upvalueindex(1));
|
ffi::lua_gettable(state, ffi::lua_upvalueindex(1));
|
||||||
|
@ -922,7 +934,7 @@ impl Lua {
|
||||||
ffi::lua_newtable(state);
|
ffi::lua_newtable(state);
|
||||||
|
|
||||||
push_string(state, "__gc").unwrap();
|
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);
|
ffi::lua_rawset(state, -3);
|
||||||
|
|
||||||
push_string(state, "__metatable").unwrap();
|
push_string(state, "__metatable").unwrap();
|
||||||
|
@ -977,10 +989,7 @@ impl Lua {
|
||||||
return Err(Error::CallbackDestructed);
|
return Err(Error::CallbackDestructed);
|
||||||
}
|
}
|
||||||
|
|
||||||
let func = get_userdata::<RefCell<Callback>>(state, ffi::lua_upvalueindex(1));
|
let func = get_userdata::<Callback>(state, ffi::lua_upvalueindex(1));
|
||||||
let mut func = (*func)
|
|
||||||
.try_borrow_mut()
|
|
||||||
.map_err(|_| Error::RecursiveCallback)?;
|
|
||||||
|
|
||||||
let nargs = ffi::lua_gettop(state);
|
let nargs = ffi::lua_gettop(state);
|
||||||
let mut args = MultiValue::new();
|
let mut args = MultiValue::new();
|
||||||
|
@ -989,10 +998,10 @@ impl Lua {
|
||||||
args.push_front(lua.pop_value(state));
|
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;
|
let nresults = results.len() as c_int;
|
||||||
|
|
||||||
check_stack(state, nresults);
|
check_stack_err(state, nresults)?;
|
||||||
|
|
||||||
for r in results {
|
for r in results {
|
||||||
lua.push_value(state, r);
|
lua.push_value(state, r);
|
||||||
|
@ -1006,7 +1015,7 @@ impl Lua {
|
||||||
stack_err_guard(self.state, 0, move || {
|
stack_err_guard(self.state, 0, move || {
|
||||||
check_stack(self.state, 2);
|
check_stack(self.state, 2);
|
||||||
|
|
||||||
push_userdata::<RefCell<Callback>>(self.state, RefCell::new(func))?;
|
push_userdata::<Callback>(self.state, func)?;
|
||||||
|
|
||||||
ffi::lua_pushlightuserdata(
|
ffi::lua_pushlightuserdata(
|
||||||
self.state,
|
self.state,
|
||||||
|
@ -1053,15 +1062,15 @@ impl Lua {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua, 'scope> Scope<'lua, 'scope> {
|
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
|
where
|
||||||
A: FromLuaMulti<'callback>,
|
A: FromLuaMulti<'callback>,
|
||||||
R: ToLuaMulti<'callback>,
|
R: ToLuaMulti<'callback>,
|
||||||
F: 'scope + FnMut(&'callback Lua, A) -> Result<R>,
|
F: 'scope + Fn(&'callback Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let f: Box<
|
let f: Box<
|
||||||
FnMut(&'callback Lua, MultiValue<'callback>) -> Result<MultiValue<'callback>>,
|
Fn(&'callback Lua, MultiValue<'callback>) -> Result<MultiValue<'callback>>,
|
||||||
> = Box::new(move |lua, args| {
|
> = Box::new(move |lua, args| {
|
||||||
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
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::luaL_unref(state, ffi::LUA_REGISTRYINDEX, registry_id);
|
||||||
|
|
||||||
ffi::lua_getupvalue(state, -1, 1);
|
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_pushnil(state);
|
||||||
ffi::lua_setupvalue(state, -2, 1);
|
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>>
|
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||||
where
|
where
|
||||||
T: UserData,
|
T: UserData,
|
||||||
|
@ -1128,7 +1150,7 @@ impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
|
||||||
let to_drop = self.destructors
|
let to_drop = self.destructors
|
||||||
.get_mut()
|
.get_mut()
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|mut destructor| destructor(state))
|
.map(|destructor| destructor(state))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
drop(to_drop);
|
drop(to_drop);
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,7 +125,7 @@ impl<'lua> Table<'lua> {
|
||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_err_guard(lua.state, 0, || {
|
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_ref(lua.state, &self.0);
|
||||||
lua.push_value(lua.state, key.to_lua(lua)?);
|
lua.push_value(lua.state, key.to_lua(lua)?);
|
||||||
lua.push_value(lua.state, value.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;
|
let lua = self.0.lua;
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_err_guard(lua.state, 0, || {
|
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_ref(lua.state, &self.0);
|
||||||
lua.push_value(lua.state, key.to_lua(lua)?);
|
lua.push_value(lua.state, key.to_lua(lua)?);
|
||||||
ffi::lua_rawget(lua.state, -2);
|
ffi::lua_rawget(lua.state, -2);
|
||||||
|
|
75
src/tests.rs
75
src/tests.rs
|
@ -1,5 +1,5 @@
|
||||||
use std::fmt;
|
use std::{error, fmt};
|
||||||
use std::error;
|
use std::iter::FromIterator;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -432,11 +432,11 @@ fn test_pcall_xpcall() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_recursive_callback_error() {
|
fn test_recursive_mut_callback_error() {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
let mut v = Some(Box::new(123));
|
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 {
|
if mutate {
|
||||||
v = None;
|
v = None;
|
||||||
} else {
|
} else {
|
||||||
|
@ -459,7 +459,7 @@ fn test_recursive_callback_error() {
|
||||||
{
|
{
|
||||||
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
|
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
|
||||||
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),
|
||||||
},
|
},
|
||||||
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 lua = Lua::new();
|
||||||
|
|
||||||
let mut r = Some(lua.create_registry_value::<i32>(42).unwrap());
|
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() {
|
if let Some(r) = r.take() {
|
||||||
assert_eq!(lua.registry_value::<i32>(&r)?, 42);
|
assert_eq!(lua.registry_value::<i32>(&r)?, 42);
|
||||||
lua.remove_registry_value(r).unwrap();
|
lua.remove_registry_value(r).unwrap();
|
||||||
|
@ -666,7 +666,7 @@ fn scope_capture() {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
lua.scope(|scope| {
|
lua.scope(|scope| {
|
||||||
scope
|
scope
|
||||||
.create_function(|_, ()| {
|
.create_function_mut(|_, ()| {
|
||||||
i = 42;
|
i = 42;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
@ -677,6 +677,67 @@ fn scope_capture() {
|
||||||
assert_eq!(i, 42);
|
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.
|
// TODO: Need to use compiletest-rs or similar to make sure these don't compile.
|
||||||
/*
|
/*
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -93,7 +93,7 @@ impl<'lua> Thread<'lua> {
|
||||||
|
|
||||||
let args = args.to_lua_multi(lua)?;
|
let args = args.to_lua_multi(lua)?;
|
||||||
let nargs = args.len() as c_int;
|
let nargs = args.len() as c_int;
|
||||||
check_stack(thread_state, nargs);
|
check_stack_err(thread_state, nargs + 1)?;
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
lua.push_value(thread_state, arg);
|
lua.push_value(thread_state, arg);
|
||||||
|
|
|
@ -39,8 +39,7 @@ impl Drop for RegistryKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type Callback<'lua> =
|
pub(crate) type Callback<'lua> = Box<Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>;
|
||||||
Box<FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>;
|
|
||||||
|
|
||||||
pub(crate) struct LuaRef<'lua> {
|
pub(crate) struct LuaRef<'lua> {
|
||||||
pub lua: &'lua Lua,
|
pub lua: &'lua Lua,
|
||||||
|
|
|
@ -89,7 +89,7 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
|
||||||
where
|
where
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'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
|
self.methods
|
||||||
.insert(name.to_owned(), Self::box_method(method));
|
.insert(name.to_owned(), Self::box_method(method));
|
||||||
|
@ -121,7 +121,7 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
|
||||||
where
|
where
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.methods
|
self.methods
|
||||||
.insert(name.to_owned(), Self::box_function(function));
|
.insert(name.to_owned(), Self::box_function(function));
|
||||||
|
@ -139,7 +139,7 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
|
||||||
where
|
where
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'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));
|
self.meta_methods.insert(meta, Self::box_method(method));
|
||||||
}
|
}
|
||||||
|
@ -170,25 +170,25 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
|
||||||
where
|
where
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'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));
|
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
|
where
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'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))
|
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
|
where
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'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| {
|
Box::new(move |lua, mut args| {
|
||||||
if let Some(front) = args.pop_front() {
|
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
|
where
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + for<'a> FnMut(&'lua Lua, &'a mut T, A) -> Result<R>,
|
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| {
|
Box::new(move |lua, mut args| {
|
||||||
if let Some(front) = args.pop_front() {
|
if let Some(front) = args.pop_front() {
|
||||||
let userdata = AnyUserData::from_lua(front, lua)?;
|
let userdata = AnyUserData::from_lua(front, lua)?;
|
||||||
let mut userdata = userdata.borrow_mut::<T>()?;
|
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 {
|
} else {
|
||||||
Err(Error::FromLuaConversionError {
|
Err(Error::FromLuaConversionError {
|
||||||
from: "missing argument",
|
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 ffi;
|
||||||
use error::{Error, Result};
|
use error::{Error, Result};
|
||||||
|
|
||||||
// Checks that Lua has enough free stack space for future stack operations.
|
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
||||||
// On failure, this will clear the stack and panic.
|
// clear the stack and panic.
|
||||||
pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) {
|
pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) {
|
||||||
lua_internal_assert!(
|
lua_internal_assert!(
|
||||||
state,
|
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
|
// 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.
|
// 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
|
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) {
|
match catch_unwind(f) {
|
||||||
Ok(Ok(r)) => r,
|
Ok(Ok(r)) => r,
|
||||||
Ok(Err(err)) => {
|
Ok(Err(err)) => {
|
||||||
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||||
push_wrapped_error(state, err);
|
push_wrapped_error(state, err);
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
Err(p) => {
|
Err(p) => {
|
||||||
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||||
push_wrapped_panic(state, p);
|
push_wrapped_panic(state, p);
|
||||||
ffi::lua_error(state)
|
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
|
// 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.
|
// 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 {
|
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) {
|
if let Some(error) = pop_wrapped_error(state) {
|
||||||
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
||||||
let traceback = CStr::from_ptr(ffi::lua_tostring(state, -1))
|
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
|
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) {
|
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) {
|
||||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
|
||||||
|
|
||||||
gc_guard(state, || {
|
gc_guard(state, || {
|
||||||
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
|
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
|
||||||
ptr::write(ud, WrappedError(err))
|
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 WrappedError(pub Error);
|
||||||
struct WrappedPanic(pub Option<Box<Any + Send>>);
|
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>) {
|
unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box<Any + Send>) {
|
||||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
|
||||||
|
|
||||||
gc_guard(state, || {
|
gc_guard(state, || {
|
||||||
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedPanic>()) as *mut WrappedPanic;
|
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedPanic>()) as *mut WrappedPanic;
|
||||||
ptr::write(ud, WrappedPanic(Some(panic)))
|
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;
|
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
||||||
|
|
||||||
unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
|
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);
|
push_wrapped_error(state, Error::CallbackDestructed);
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue