diff options
author | kyren <kerriganw@gmail.com> | 2018-03-12 16:00:11 -0400 |
---|---|---|
committer | kyren <kerriganw@gmail.com> | 2018-03-12 16:00:11 -0400 |
commit | f79d771f1a9e49cc5e7e7cf2553abb0d37339e36 (patch) | |
tree | a51441efe97a46bd99ba426d4621be1462aec5be | |
parent | ee23f199f0670c2c159bd653baaddd4015c602f0 (diff) | |
download | mlua-f79d771f1a9e49cc5e7e7cf2553abb0d37339e36.zip |
Documentation improvements, split scope into its own module, improved tests
Also makes `Lua` and associated types !UnwindSafe and !RefUnwindSafe, which they
should be because they are intensely internally mutable. Lua IS still panic
safe, but that doesn't mean it should be marked as UnwindSafe (as I understand
it).
-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() } |