diff options
-rw-r--r-- | src/lib.rs | 4 | ||||
-rw-r--r-- | src/lua.rs | 298 | ||||
-rw-r--r-- | src/scope.rs | 141 | ||||
-rw-r--r-- | src/tests/mod.rs | 50 | ||||
-rw-r--r-- | src/tests/thread.rs | 25 | ||||
-rw-r--r-- | src/util.rs | 3 | ||||
-rw-r--r-- | src/value.rs | 10 |
7 files changed, 283 insertions, 248 deletions
@@ -59,6 +59,7 @@ mod table; mod function; mod thread; mod userdata; +mod scope; #[cfg(test)] mod tests; @@ -72,6 +73,7 @@ pub use function::Function; pub use thread::{Thread, ThreadStatus}; pub use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods}; pub use value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value}; -pub use lua::{Lua, Scope}; +pub use lua::Lua; +pub use scope::Scope; pub mod prelude; @@ -2,11 +2,10 @@ use std::{cmp, mem, ptr, str}; use std::sync::{Arc, Mutex}; use std::cell::{Cell, RefCell}; use std::ffi::CString; -use std::any::{Any, TypeId}; +use std::any::TypeId; use std::marker::PhantomData; use std::collections::HashMap; use std::os::raw::{c_char, c_int, c_void}; -use std::panic::{RefUnwindSafe, UnwindSafe}; use libc; @@ -14,7 +13,7 @@ use ffi; use error::{Error, Result}; use util::{callback_error, check_stack, check_stack_err, gc_guard, get_userdata, get_wrapped_error, init_error_metatables, pop_error, protect_lua, protect_lua_closure, - push_string, push_userdata, push_wrapped_error, safe_pcall, safe_xpcall, take_userdata, + push_string, push_userdata, push_wrapped_error, safe_pcall, safe_xpcall, userdata_destructor, StackGuard}; use value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value}; use types::{Callback, Integer, LightUserData, LuaRef, Number, RefType, RegistryKey}; @@ -23,6 +22,7 @@ use table::Table; use function::Function; use thread::Thread; use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods}; +use scope::Scope; /// Top level Lua struct which holds the Lua state itself. pub struct Lua { @@ -31,19 +31,6 @@ pub struct Lua { ref_stack_slots: [Cell<usize>; REF_STACK_SIZE as usize], } -/// Constructed by the [`Lua::scope`] method, allows temporarily passing to Lua userdata that is -/// !Send, and callbacks that are !Send and not 'static. -/// -/// See [`Lua::scope`] for more details. -/// -/// [`Lua::scope`]: struct.Lua.html#method.scope -pub struct Scope<'scope> { - lua: &'scope Lua, - destructors: RefCell<Vec<Box<Fn() -> Box<Any> + 'scope>>>, - // 'scope lifetime must be invariant - _scope: PhantomData<&'scope mut &'scope ()>, -} - // Data associated with the main lua_State via lua_getextraspace. struct ExtraData { registered_userdata: HashMap<TypeId, c_int>, @@ -56,9 +43,6 @@ static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0; unsafe impl Send for Lua {} -impl UnwindSafe for Lua {} -impl RefUnwindSafe for Lua {} - impl Drop for Lua { fn drop(&mut self) { unsafe { @@ -287,7 +271,7 @@ impl Lua { R: ToLuaMulti<'callback>, F: 'static + Send + Fn(&'callback Lua, A) -> Result<R>, { - self.create_callback_function(Box::new(move |lua, args| { + self.create_callback(Box::new(move |lua, args| { func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) })) } @@ -336,7 +320,7 @@ impl Lua { where T: Send + UserData, { - self.do_create_userdata(data) + unsafe { self.make_userdata(data) } } /// Returns a handle to the global environment. @@ -370,11 +354,7 @@ impl Lua { where F: FnOnce(&Scope<'scope>) -> R, { - let scope = Scope { - lua: self, - destructors: RefCell::new(Vec::new()), - _scope: PhantomData, - }; + let scope = Scope::new(self); let r = f(&scope); drop(scope); r @@ -902,7 +882,7 @@ impl Lua { for (k, m) in methods.methods { push_string(self.state, &k)?; - self.push_value(Value::Function(self.create_callback_function(m)?)); + self.push_value(Value::Function(self.create_callback(m)?)); protect_lua_closure(self.state, 3, 1, |state| { ffi::lua_rawset(state, -3); })?; @@ -918,7 +898,7 @@ impl Lua { push_string(self.state, "__index")?; ffi::lua_pushvalue(self.state, -1); ffi::lua_gettable(self.state, -3); - self.push_value(Value::Function(self.create_callback_function(m)?)); + self.push_value(Value::Function(self.create_callback(m)?)); protect_lua_closure(self.state, 2, 1, |state| { ffi::lua_pushcclosure(state, meta_index_impl, 2); })?; @@ -953,7 +933,7 @@ impl Lua { MetaMethod::ToString => "__tostring", }; push_string(self.state, name)?; - self.push_value(Value::Function(self.create_callback_function(m)?)); + self.push_value(Value::Function(self.create_callback(m)?)); protect_lua_closure(self.state, 3, 1, |state| { ffi::lua_rawset(state, -3); })?; @@ -981,6 +961,80 @@ impl Lua { Ok(id) } + pub(crate) fn create_callback<'lua, 'callback>( + &'lua self, + func: Callback<'callback, 'static>, + ) -> Result<Function<'lua>> { + unsafe extern "C" fn callback_call_impl(state: *mut ffi::lua_State) -> c_int { + callback_error(state, || { + if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL { + return Err(Error::CallbackDestructed); + } + + let lua = Lua { + state: state, + ephemeral: true, + ref_stack_slots: Default::default(), + }; + let args = lua.setup_callback_stack_slots(); + + let func = get_userdata::<Callback>(state, ffi::lua_upvalueindex(1)); + + let results = (*func)(&lua, args)?; + let nresults = results.len() as c_int; + + check_stack_err(state, nresults)?; + + for r in results { + lua.push_value(r); + } + + Ok(nresults) + }) + } + + unsafe { + let _sg = StackGuard::new(self.state); + check_stack(self.state, 4); + + push_userdata::<Callback>(self.state, func)?; + + ffi::lua_pushlightuserdata( + self.state, + &FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, + ); + ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX); + ffi::lua_setmetatable(self.state, -2); + + protect_lua_closure(self.state, 1, 1, |state| { + ffi::lua_pushcclosure(state, callback_call_impl, 1); + })?; + + Ok(Function(self.pop_ref())) + } + } + + // Does not require Send bounds, which can lead to unsafety. + pub(crate) unsafe fn make_userdata<T>(&self, data: T) -> Result<AnyUserData> + where + T: UserData, + { + let _sg = StackGuard::new(self.state); + check_stack(self.state, 4); + + push_userdata::<RefCell<T>>(self.state, RefCell::new(data))?; + + ffi::lua_rawgeti( + self.state, + ffi::LUA_REGISTRYINDEX, + self.userdata_metatable::<T>()? as ffi::lua_Integer, + ); + + ffi::lua_setmetatable(self.state, -2); + + Ok(AnyUserData(self.pop_ref())) + } + unsafe fn create_lua(load_debug: bool) -> Lua { unsafe extern "C" fn allocator( _: *mut c_void, @@ -1082,81 +1136,6 @@ impl Lua { } } - fn create_callback_function<'lua, 'callback>( - &'lua self, - func: Callback<'callback, 'static>, - ) -> Result<Function<'lua>> { - unsafe extern "C" fn callback_call_impl(state: *mut ffi::lua_State) -> c_int { - callback_error(state, || { - if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL { - return Err(Error::CallbackDestructed); - } - - let lua = Lua { - state: state, - ephemeral: true, - ref_stack_slots: Default::default(), - }; - let args = lua.setup_callback_stack_slots(); - - let func = get_userdata::<Callback>(state, ffi::lua_upvalueindex(1)); - - let results = (*func)(&lua, args)?; - let nresults = results.len() as c_int; - - check_stack_err(state, nresults)?; - - for r in results { - lua.push_value(r); - } - - Ok(nresults) - }) - } - - unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 4); - - push_userdata::<Callback>(self.state, func)?; - - ffi::lua_pushlightuserdata( - self.state, - &FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, - ); - ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX); - ffi::lua_setmetatable(self.state, -2); - - protect_lua_closure(self.state, 1, 1, |state| { - ffi::lua_pushcclosure(state, callback_call_impl, 1); - })?; - - Ok(Function(self.pop_ref())) - } - } - - fn do_create_userdata<T>(&self, data: T) -> Result<AnyUserData> - where - T: UserData, - { - unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 4); - - push_userdata::<RefCell<T>>(self.state, RefCell::new(data))?; - - ffi::lua_rawgeti( - self.state, - ffi::LUA_REGISTRYINDEX, - self.userdata_metatable::<T>()? as ffi::lua_Integer, - ); - - ffi::lua_setmetatable(self.state, -2); - - Ok(AnyUserData(self.pop_ref())) - } - } - // Set up the stack slot area in a callback, returning all arguments on the stack as a // MultiValue fn setup_callback_stack_slots<'lua>(&'lua self) -> MultiValue<'lua> { @@ -1169,6 +1148,8 @@ impl Lua { let mut args = MultiValue::new(); args.reserve(stack_nargs as usize); + // First, convert all of the reference types in the ref stack area into LuaRef types + // in-place. for i in 0..stack_nargs { let n = stack_nargs - i; @@ -1234,6 +1215,8 @@ impl Lua { ffi::lua_settop(self.state, REF_STACK_SIZE); args } else if nargs > REF_STACK_SIZE { + // If the total number of arguments exceeds the ref stack area, pop off the rest of + // the arguments as normal. let mut extra_args = Vec::new(); extra_args.reserve((nargs - REF_STACK_SIZE) as usize); for _ in REF_STACK_SIZE..nargs { @@ -1252,110 +1235,3 @@ impl Lua { *(ffi::lua_getextraspace(self.state) as *mut *mut ExtraData) } } - -impl<'scope> Scope<'scope> { - /// Wraps a Rust function or closure, creating a callable Lua function handle to it. - /// - /// This is a version of [`Lua::create_function`] that creates a callback which expires on scope - /// drop. See [`Lua::scope`] for more details. - /// - /// [`Lua::create_function`]: struct.Lua.html#method.create_function - /// [`Lua::scope`]: struct.Lua.html#method.scope - pub fn create_function<'callback, 'lua, A, R, F>(&'lua self, func: F) -> Result<Function<'lua>> - where - A: FromLuaMulti<'callback>, - R: ToLuaMulti<'callback>, - F: 'scope + Fn(&'callback Lua, A) -> Result<R>, - { - unsafe { - let f = Box::new(move |lua, args| { - func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - }); - let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'callback, 'static>>(f); - let f = self.lua.create_callback_function(f)?; - - let mut destructors = self.destructors.borrow_mut(); - let f_destruct = f.0.clone(); - destructors.push(Box::new(move || { - let state = f_destruct.lua.state; - let _sg = StackGuard::new(state); - check_stack(state, 2); - f_destruct.lua.push_ref(&f_destruct); - - ffi::lua_getupvalue(state, -1, 1); - let ud = take_userdata::<Callback>(state); - - ffi::lua_pushnil(state); - ffi::lua_setupvalue(state, -2, 1); - - ffi::lua_pop(state, 1); - Box::new(ud) - })); - Ok(f) - } - } - - /// Wraps a Rust mutable closure, creating a callable Lua function handle to it. - /// - /// This is a version of [`Lua::create_function_mut`] that creates a callback which expires on - /// scope drop. See [`Lua::scope`] for more details. - /// - /// [`Lua::create_function_mut`]: struct.Lua.html#method.create_function_mut - /// [`Lua::scope`]: struct.Lua.html#method.scope - pub fn create_function_mut<'callback, 'lua, A, R, F>( - &'lua 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) - }) - } - - /// Create a Lua userdata object from a custom userdata type. - /// - /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on scope - /// drop. See [`Lua::scope`] for more details. - /// - /// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata - /// [`Lua::scope`]: struct.Lua.html#method.scope - pub fn create_userdata<'lua, T>(&'lua self, data: T) -> Result<AnyUserData<'lua>> - where - T: UserData, - { - unsafe { - let u = self.lua.do_create_userdata(data)?; - let mut destructors = self.destructors.borrow_mut(); - let u_destruct = u.0.clone(); - destructors.push(Box::new(move || { - let state = u_destruct.lua.state; - let _sg = StackGuard::new(state); - check_stack(state, 1); - u_destruct.lua.push_ref(&u_destruct); - Box::new(take_userdata::<RefCell<T>>(state)) - })); - Ok(u) - } - } -} - -impl<'scope> Drop for Scope<'scope> { - fn drop(&mut self) { - // We separate the action of invalidating the userdata in Lua and actually dropping the - // userdata type into two phases. This is so that, in the event a userdata drop panics, we - // can be sure that all of the userdata in Lua is actually invalidated. - - let to_drop = self.destructors - .get_mut() - .drain(..) - .map(|destructor| destructor()) - .collect::<Vec<_>>(); - drop(to_drop); - } -} diff --git a/src/scope.rs b/src/scope.rs new file mode 100644 index 0000000..bff7556 --- /dev/null +++ b/src/scope.rs @@ -0,0 +1,141 @@ +use std::mem; +use std::cell::RefCell; +use std::any::Any; +use std::marker::PhantomData; + +use ffi; +use error::{Error, Result}; +use util::{check_stack, take_userdata, StackGuard}; +use value::{FromLuaMulti, ToLuaMulti}; +use types::Callback; +use lua::Lua; +use function::Function; +use userdata::{AnyUserData, UserData}; + +/// Constructed by the [`Lua::scope`] method, allows temporarily passing to Lua userdata that is +/// !Send, and callbacks that are !Send and not 'static. +/// +/// See [`Lua::scope`] for more details. +/// +/// [`Lua::scope`]: struct.Lua.html#method.scope +pub struct Scope<'scope> { + lua: &'scope Lua, + destructors: RefCell<Vec<Box<Fn() -> Box<Any> + 'scope>>>, + // 'scope lifetime must be invariant + _scope: PhantomData<&'scope mut &'scope ()>, +} + +impl<'scope> Scope<'scope> { + pub(crate) fn new(lua: &'scope Lua) -> Scope { + Scope { + lua, + destructors: RefCell::new(Vec::new()), + _scope: PhantomData, + } + } + + /// Wraps a Rust function or closure, creating a callable Lua function handle to it. + /// + /// This is a version of [`Lua::create_function`] that creates a callback which expires on scope + /// drop. See [`Lua::scope`] for more details. + /// + /// [`Lua::create_function`]: struct.Lua.html#method.create_function + /// [`Lua::scope`]: struct.Lua.html#method.scope + pub fn create_function<'callback, 'lua, A, R, F>(&'lua self, func: F) -> Result<Function<'lua>> + where + A: FromLuaMulti<'callback>, + R: ToLuaMulti<'callback>, + F: 'scope + Fn(&'callback Lua, A) -> Result<R>, + { + unsafe { + let f = Box::new(move |lua, args| { + func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + }); + let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'callback, 'static>>(f); + let f = self.lua.create_callback(f)?; + + let mut destructors = self.destructors.borrow_mut(); + let f_destruct = f.0.clone(); + destructors.push(Box::new(move || { + let state = f_destruct.lua.state; + let _sg = StackGuard::new(state); + check_stack(state, 2); + f_destruct.lua.push_ref(&f_destruct); + + ffi::lua_getupvalue(state, -1, 1); + let ud = take_userdata::<Callback>(state); + + ffi::lua_pushnil(state); + ffi::lua_setupvalue(state, -2, 1); + + ffi::lua_pop(state, 1); + Box::new(ud) + })); + Ok(f) + } + } + + /// Wraps a Rust mutable closure, creating a callable Lua function handle to it. + /// + /// This is a version of [`Lua::create_function_mut`] that creates a callback which expires on + /// scope drop. See [`Lua::scope`] for more details. + /// + /// [`Lua::create_function_mut`]: struct.Lua.html#method.create_function_mut + /// [`Lua::scope`]: struct.Lua.html#method.scope + pub fn create_function_mut<'callback, 'lua, A, R, F>( + &'lua 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) + }) + } + + /// Create a Lua userdata object from a custom userdata type. + /// + /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on scope + /// drop. See [`Lua::scope`] for more details. + /// + /// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata + /// [`Lua::scope`]: struct.Lua.html#method.scope + pub fn create_userdata<'lua, T>(&'lua self, data: T) -> Result<AnyUserData<'lua>> + where + T: UserData, + { + unsafe { + let u = self.lua.make_userdata(data)?; + let mut destructors = self.destructors.borrow_mut(); + let u_destruct = u.0.clone(); + destructors.push(Box::new(move || { + let state = u_destruct.lua.state; + let _sg = StackGuard::new(state); + check_stack(state, 1); + u_destruct.lua.push_ref(&u_destruct); + Box::new(take_userdata::<RefCell<T>>(state)) + })); + Ok(u) + } + } +} + +impl<'scope> Drop for Scope<'scope> { + fn drop(&mut self) { + // We separate the action of invalidating the userdata in Lua and actually dropping the + // userdata type into two phases. This is so that, in the event a userdata drop panics, we + // can be sure that all of the userdata in Lua is actually invalidated. + + let to_drop = self.destructors + .get_mut() + .drain(..) + .map(|destructor| destructor()) + .collect::<Vec<_>>(); + drop(to_drop); + } +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 7491e8d..f24506f 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -12,8 +12,8 @@ use std::cell::Cell; use std::sync::Arc; use std::panic::catch_unwind; -use {Error, ExternalError, Function, Lua, Nil, Result, Table, UserData, UserDataMethods, Value, - Variadic}; +use {Error, ExternalError, Function, Lua, Nil, Result, String, Table, UserData, UserDataMethods, + Value, Variadic}; #[test] fn test_load() { @@ -34,7 +34,7 @@ fn test_debug() { } let traceback_output = lua.eval::<String>("debug.traceback()", None).unwrap(); assert_eq!( - traceback_output.split("\n").next(), + traceback_output.to_str().unwrap().split("\n").next(), "stack traceback:".into() ); } @@ -282,7 +282,7 @@ fn test_error() { None, )?; let rust_panic_function = lua.create_function(|_, ()| -> Result<()> { - panic!("expected panic, this panic should be caught in rust") + panic!("test_panic") }).unwrap(); globals.set("rust_panic_function", rust_panic_function)?; @@ -292,7 +292,7 @@ fn test_error() { }) { Ok(Ok(_)) => panic!("no panic was detected, pcall caught it!"), Ok(Err(e)) => panic!("error during panic test {:?}", e), - Err(_) => {} + Err(p) => assert!(*p.downcast::<&str>().unwrap() == "test_panic"), }; match catch_unwind(|| -> Result<()> { @@ -308,7 +308,7 @@ fn test_error() { None, )?; let rust_panic_function = lua.create_function(|_, ()| -> Result<()> { - panic!("expected panic, this panic should be caught in rust") + panic!("test_panic") }).unwrap(); globals.set("rust_panic_function", rust_panic_function)?; @@ -318,7 +318,7 @@ fn test_error() { }) { Ok(Ok(_)) => panic!("no panic was detected, xpcall caught it!"), Ok(Err(e)) => panic!("error during panic test {:?}", e), - Err(_) => {} + Err(p) => assert!(*p.downcast::<&str>().unwrap() == "test_panic"), }; } @@ -382,21 +382,22 @@ fn test_pcall_xpcall() { let globals = lua.globals(); // make sure that we handle not enough arguments + assert!(lua.exec::<()>("pcall()", None).is_err()); assert!(lua.exec::<()>("xpcall()", None).is_err()); assert!(lua.exec::<()>("xpcall(function() end)", None).is_err()); // Make sure that the return values from are correct on success - assert_eq!( - lua.eval::<(bool, String)>("pcall(function(p) return p end, 'foo')", None) - .unwrap(), - (true, "foo".to_owned()) - ); - assert_eq!( - lua.eval::<(bool, String)>("xpcall(function(p) return p end, print, 'foo')", None) - .unwrap(), - (true, "foo".to_owned()) - ); + + let (r, e) = lua.eval::<(bool, String)>("pcall(function(p) return p end, 'foo')", None) + .unwrap(); + assert!(r); + assert_eq!(e, "foo"); + + let (r, e) = lua.eval::<(bool, String)>("xpcall(function(p) return p end, print, 'foo')", None) + .unwrap(); + assert!(r); + assert_eq!(e, "foo"); // Make sure that the return values are correct on errors, and that error handling works @@ -779,3 +780,18 @@ fn large_args() { 4950 ); } + +fn large_args_ref() { + let lua = Lua::new(); + let globals = lua.globals(); + + let f = lua.create_function(|_, args: Variadic<String>| { + for i in 0..args.len() { + assert_eq!(args[i], i.to_string()); + } + Ok(()) + }).unwrap(); + + f.call::<_, ()>((0..100).map(|i| i.to_string()).collect::<Variadic<_>>()) + .unwrap(); +} diff --git a/src/tests/thread.rs b/src/tests/thread.rs index ccfb397..56b671d 100644 --- a/src/tests/thread.rs +++ b/src/tests/thread.rs @@ -1,6 +1,6 @@ use std::panic::catch_unwind; -use {Error, Function, Lua, Thread, ThreadStatus}; +use {Error, Function, Lua, Result, Thread, ThreadStatus}; #[test] fn test_thread() { @@ -97,18 +97,17 @@ fn coroutine_from_closure() { #[test] fn coroutine_panic() { - // check that coroutines propagate panics correctly - let lua = Lua::new(); - let thrd_main = lua.create_function(|lua, ()| { - // whoops, 'main' has a wrong type - let _coro: u32 = lua.globals().get("main").unwrap(); - Ok(()) - }).unwrap(); - lua.globals().set("main", thrd_main.clone()).unwrap(); - let thrd: Thread = lua.create_thread(thrd_main).unwrap(); - - match catch_unwind(|| thrd.resume::<_, ()>(())) { + match catch_unwind(|| -> Result<()> { + // check that coroutines propagate panics correctly + let lua = Lua::new(); + let thrd_main = lua.create_function(|lua, ()| -> Result<()> { + panic!("test_panic"); + })?; + lua.globals().set("main", thrd_main.clone())?; + let thrd: Thread = lua.create_thread(thrd_main)?; + thrd.resume(()) + }) { Ok(r) => panic!("coroutine panic not propagated, instead returned {:?}", r), - Err(_) => {} + Err(p) => assert!(*p.downcast::<&str>().unwrap() == "test_panic"), } } diff --git a/src/util.rs b/src/util.rs index f7dc17f..b5ba46a 100644 --- a/src/util.rs +++ b/src/util.rs @@ -284,7 +284,8 @@ where // Takes an error at the top of the stack, and if it is a WrappedError, converts it to an // 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. This function should never panic or +// trigger a error (longjmp). pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int { // I believe luaL_traceback requires this much free stack to not error. const LUA_TRACEBACK_STACK: c_int = 11; diff --git a/src/value.rs b/src/value.rs index 18c8810..d8eb033 100644 --- a/src/value.rs +++ b/src/value.rs @@ -119,23 +119,23 @@ impl<'lua> MultiValue<'lua> { v } - pub fn from_vec_rev(v: Vec<Value<'lua>>) -> MultiValue<'lua> { + pub(crate) fn from_vec_rev(v: Vec<Value<'lua>>) -> MultiValue<'lua> { MultiValue(v) } - pub fn into_vec_rev(self) -> Vec<Value<'lua>> { + pub(crate) fn into_vec_rev(self) -> Vec<Value<'lua>> { self.0 } - pub fn reserve(&mut self, size: usize) { + pub(crate) fn reserve(&mut self, size: usize) { self.0.reserve(size); } - pub fn push_front(&mut self, value: Value<'lua>) { + pub(crate) fn push_front(&mut self, value: Value<'lua>) { self.0.push(value); } - pub fn pop_front(&mut self) -> Option<Value<'lua>> { + pub(crate) fn pop_front(&mut self) -> Option<Value<'lua>> { self.0.pop() } |