diff --git a/src/conversion.rs b/src/conversion.rs index a6749f5..d7db574 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -2,7 +2,7 @@ use std::collections::{BTreeMap, HashMap}; use std::hash::{BuildHasher, Hash}; use std::string::String as StdString; -use error::*; +use error::{Error, Result}; use types::{Integer, LightUserData, Number}; use string::String; use table::Table; diff --git a/src/ffi.rs b/src/ffi.rs index eaa40ae..d3dc2bb 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -123,6 +123,7 @@ extern "C" { pub fn lua_rotate(state: *mut lua_State, index: c_int, n: c_int); pub fn lua_copy(state: *mut lua_State, from: c_int, to: c_int); pub fn lua_absindex(state: *mut lua_State, index: c_int) -> c_int; + pub fn lua_xmove(from: *mut lua_State, to: *mut lua_State, n: c_int); pub fn lua_isinteger(state: *mut lua_State, index: c_int) -> c_int; pub fn lua_isnumber(state: *mut lua_State, index: c_int) -> c_int; diff --git a/src/function.rs b/src/function.rs index 246e852..0f97dac 100644 --- a/src/function.rs +++ b/src/function.rs @@ -2,8 +2,9 @@ use std::ptr; use std::os::raw::c_int; use ffi; -use error::*; -use util::*; +use error::{Error, Result}; +use util::{check_stack, check_stack_err, error_traceback, pop_error, protect_lua_closure, + stack_guard}; use types::LuaRef; use value::{FromLuaMulti, MultiValue, ToLuaMulti}; @@ -63,16 +64,16 @@ impl<'lua> Function<'lua> { pub fn call, R: FromLuaMulti<'lua>>(&self, args: A) -> Result { let lua = self.0.lua; unsafe { - stack_err_guard(lua.state, || { + stack_guard(lua.state, || { let args = args.to_lua_multi(lua)?; let nargs = args.len() as c_int; check_stack_err(lua.state, nargs + 3)?; ffi::lua_pushcfunction(lua.state, error_traceback); let stack_start = ffi::lua_gettop(lua.state); - lua.push_ref(lua.state, &self.0); + lua.push_ref(&self.0); for arg in args { - lua.push_value(lua.state, arg); + lua.push_value(arg); } let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start); if ret != ffi::LUA_OK { @@ -82,7 +83,7 @@ impl<'lua> Function<'lua> { let mut results = MultiValue::new(); check_stack(lua.state, 2); for _ in 0..nresults { - results.push_front(lua.pop_value(lua.state)); + results.push_front(lua.pop_value()); } ffi::lua_pop(lua.state, 1); R::from_lua_multi(results, lua) @@ -144,7 +145,7 @@ impl<'lua> Function<'lua> { let lua = self.0.lua; unsafe { - stack_err_guard(lua.state, || { + stack_guard(lua.state, || { let args = args.to_lua_multi(lua)?; let nargs = args.len() as c_int; @@ -152,18 +153,18 @@ impl<'lua> Function<'lua> { return Err(Error::BindError); } - check_stack_err(lua.state, nargs + 3)?; - lua.push_ref(lua.state, &self.0); + check_stack_err(lua.state, nargs + 5)?; + lua.push_ref(&self.0); ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer); for arg in args { - lua.push_value(lua.state, arg); + lua.push_value(arg); } - protect_lua_call(lua.state, nargs + 2, 1, |state| { + protect_lua_closure(lua.state, nargs + 2, 1, |state| { ffi::lua_pushcclosure(state, bind_call_impl, nargs + 2); })?; - Ok(Function(lua.pop_ref(lua.state))) + Ok(Function(lua.pop_ref())) }) } } diff --git a/src/lua.rs b/src/lua.rs index df34f9f..767f507 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -1,19 +1,23 @@ -use std::{mem, ptr, str}; +use std::{cmp, mem, ptr, str}; use std::sync::{Arc, Mutex}; -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::ffi::CString; use std::any::{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; use ffi; -use error::*; -use util::*; +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, stack_guard, + take_userdata, userdata_destructor}; use value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value}; -use types::{Callback, Integer, LightUserData, LuaRef, Number, RegistryKey}; +use types::{Callback, Integer, LightUserData, LuaRef, Number, RefType, RegistryKey}; use string::String; use table::Table; use function::Function; @@ -23,8 +27,8 @@ use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods}; /// Top level Lua struct which holds the Lua state itself. pub struct Lua { pub(crate) state: *mut ffi::lua_State, - main_state: *mut ffi::lua_State, ephemeral: bool, + ref_stack_slots: [Cell; REF_STACK_SIZE as usize], } /// Constructed by the [`Lua::scope`] method, allows temporarily passing to Lua userdata that is @@ -35,7 +39,7 @@ pub struct Lua { /// [`Lua::scope`]: struct.Lua.html#method.scope pub struct Scope<'scope> { lua: &'scope Lua, - destructors: RefCell Box>>>, + destructors: RefCell Box + 'scope>>>, // 'scope lifetime must be invariant _scope: PhantomData<&'scope mut &'scope ()>, } @@ -46,14 +50,25 @@ struct ExtraData { registry_unref_list: Arc>>>, } +const REF_STACK_SIZE: c_int = 16; + +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 { if !self.ephemeral { let top = ffi::lua_gettop(self.state); - rlua_assert!(top == 0, "stack leak detected, stack top is {}", top); + rlua_assert!( + top == REF_STACK_SIZE, + "stack problem detected, stack top is {}", + top - REF_STACK_SIZE + ); let extra_data = *(ffi::lua_getextraspace(self.state) as *mut *mut ExtraData); *(*extra_data).registry_unref_list.lock().unwrap() = None; @@ -87,7 +102,7 @@ impl Lua { /// Equivalent to Lua's `load` function. pub fn load(&self, source: &str, name: Option<&str>) -> Result { unsafe { - stack_err_guard(self.state, || { + stack_guard(self.state, || { check_stack(self.state, 1); match if let Some(name) = name { @@ -111,7 +126,7 @@ impl Lua { ptr::null(), ) } { - ffi::LUA_OK => Ok(Function(self.pop_ref(self.state))), + ffi::LUA_OK => Ok(Function(self.pop_ref())), err => Err(pop_error(self.state, err)), } }) @@ -152,10 +167,10 @@ impl Lua { /// Pass a `&str` slice to Lua, creating and returning an interned Lua string. pub fn create_string(&self, s: &str) -> Result { unsafe { - stack_err_guard(self.state, || { + stack_guard(self.state, || { check_stack(self.state, 4); push_string(self.state, s)?; - Ok(String(self.pop_ref(self.state))) + Ok(String(self.pop_ref())) }) } } @@ -163,12 +178,14 @@ impl Lua { /// Creates and returns a new table. pub fn create_table(&self) -> Result { unsafe { - stack_err_guard(self.state, || { - check_stack(self.state, 4); - protect_lua_call(self.state, 0, 1, |state| { + stack_guard(self.state, || { + check_stack(self.state, 3); + unsafe extern "C" fn new_table(state: *mut ffi::lua_State) -> c_int { ffi::lua_newtable(state); - })?; - Ok(Table(self.pop_ref(self.state))) + 1 + } + protect_lua(self.state, 0, new_table)?; + Ok(Table(self.pop_ref())) }) } } @@ -181,20 +198,24 @@ impl Lua { I: IntoIterator, { unsafe { - stack_err_guard(self.state, || { - check_stack(self.state, 6); - protect_lua_call(self.state, 0, 1, |state| { + stack_guard(self.state, || { + check_stack(self.state, 5); + unsafe extern "C" fn new_table(state: *mut ffi::lua_State) -> c_int { ffi::lua_newtable(state); - })?; + 1 + } + protect_lua(self.state, 0, new_table)?; for (k, v) in cont { - self.push_value(self.state, k.to_lua(self)?); - self.push_value(self.state, v.to_lua(self)?); - protect_lua_call(self.state, 3, 1, |state| { + self.push_value(k.to_lua(self)?); + self.push_value(v.to_lua(self)?); + unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int { ffi::lua_rawset(state, -3); - })?; + 1 + } + protect_lua(self.state, 3, raw_set)?; } - Ok(Table(self.pop_ref(self.state))) + Ok(Table(self.pop_ref())) }) } } @@ -301,14 +322,15 @@ impl Lua { /// Equivalent to `coroutine.create`. pub fn create_thread<'lua>(&'lua self, func: Function<'lua>) -> Result> { unsafe { - stack_err_guard(self.state, move || { + stack_guard(self.state, move || { check_stack(self.state, 2); let thread_state = - protect_lua_call(self.state, 0, 1, |state| ffi::lua_newthread(state))?; - self.push_ref(thread_state, &func.0); + protect_lua_closure(self.state, 0, 1, |state| ffi::lua_newthread(state))?; + self.push_ref(&func.0); + ffi::lua_xmove(self.state, thread_state, 1); - Ok(Thread(self.pop_ref(self.state))) + Ok(Thread(self.pop_ref())) }) } } @@ -327,7 +349,7 @@ impl Lua { stack_guard(self.state, move || { check_stack(self.state, 2); ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); - Table(self.pop_ref(self.state)) + Table(self.pop_ref()) }) } } @@ -370,21 +392,21 @@ impl Lua { match v { Value::String(s) => Ok(s), v => unsafe { - stack_err_guard(self.state, || { + stack_guard(self.state, || { check_stack(self.state, 4); let ty = v.type_name(); - self.push_value(self.state, v); - let s = - protect_lua_call(self.state, 1, 1, |state| ffi::lua_tostring(state, -1))?; + self.push_value(v); + let s = protect_lua_closure(self.state, 1, 1, |state| { + ffi::lua_tostring(state, -1) + })?; if s.is_null() { - ffi::lua_pop(self.state, 1); Err(Error::FromLuaConversionError { from: ty, to: "String", message: Some("expected string or number".to_string()), }) } else { - Ok(String(self.pop_ref(self.state))) + Ok(String(self.pop_ref())) } }) }, @@ -402,10 +424,9 @@ impl Lua { stack_guard(self.state, || { check_stack(self.state, 2); let ty = v.type_name(); - self.push_value(self.state, v); + self.push_value(v); let mut isint = 0; let i = ffi::lua_tointegerx(self.state, -1, &mut isint); - ffi::lua_pop(self.state, 1); if isint == 0 { Err(Error::FromLuaConversionError { from: ty, @@ -431,10 +452,9 @@ impl Lua { stack_guard(self.state, || { check_stack(self.state, 2); let ty = v.type_name(); - self.push_value(self.state, v); + self.push_value(v); let mut isnum = 0; let n = ffi::lua_tonumberx(self.state, -1, &mut isnum); - ffi::lua_pop(self.state, 1); if isnum == 0 { Err(Error::FromLuaConversionError { from: ty, @@ -482,15 +502,17 @@ impl Lua { t: T, ) -> Result<()> { unsafe { - stack_err_guard(self.state, || { + stack_guard(self.state, || { check_stack(self.state, 5); push_string(self.state, name)?; - self.push_value(self.state, t.to_lua(self)?); + self.push_value(t.to_lua(self)?); - protect_lua_call(self.state, 2, 0, |state| { + unsafe extern "C" fn set_registry(state: *mut ffi::lua_State) -> c_int { ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); - }) + 0 + } + protect_lua(self.state, 2, set_registry) }) } } @@ -503,15 +525,17 @@ impl Lua { /// [`set_named_registry_value`]: #method.set_named_registry_value pub fn named_registry_value<'lua, T: FromLua<'lua>>(&'lua self, name: &str) -> Result { unsafe { - stack_err_guard(self.state, || { + stack_guard(self.state, || { check_stack(self.state, 4); push_string(self.state, name)?; - protect_lua_call(self.state, 1, 1, |state| { - ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX) - })?; + unsafe extern "C" fn get_registry(state: *mut ffi::lua_State) -> c_int { + ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX); + 1 + } + protect_lua(self.state, 1, get_registry)?; - T::from_lua(self.pop_value(self.state), self) + T::from_lua(self.pop_value(), self) }) } } @@ -534,7 +558,7 @@ impl Lua { stack_guard(self.state, || { check_stack(self.state, 2); - self.push_value(self.state, t.to_lua(self)?); + self.push_value(t.to_lua(self)?); let registry_id = gc_guard(self.state, || { ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX) }); @@ -542,7 +566,6 @@ impl Lua { Ok(RegistryKey { registry_id, unref_list: (*self.extra()).registry_unref_list.clone(), - drop_unref: true, }) }) } @@ -560,14 +583,14 @@ impl Lua { return Err(Error::MismatchedRegistryKey); } - stack_err_guard(self.state, || { + stack_guard(self.state, || { check_stack(self.state, 2); ffi::lua_rawgeti( self.state, ffi::LUA_REGISTRYINDEX, key.registry_id as ffi::lua_Integer, ); - T::from_lua(self.pop_value(self.state), self) + T::from_lua(self.pop_value(), self) }) } } @@ -581,14 +604,13 @@ impl Lua { /// /// [`create_registry_value`]: #method.create_registry_value /// [`expire_registry_values`]: #method.expire_registry_values - pub fn remove_registry_value(&self, mut key: RegistryKey) -> Result<()> { + pub fn remove_registry_value(&self, key: RegistryKey) -> Result<()> { unsafe { if !Arc::ptr_eq(&key.unref_list, &(*self.extra()).registry_unref_list) { return Err(Error::MismatchedRegistryKey); } - ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, key.registry_id); - key.drop_unref = false; + ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, key.take()); Ok(()) } } @@ -620,119 +642,129 @@ impl Lua { } // 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, value: Value) { match value { Value::Nil => { - ffi::lua_pushnil(state); + ffi::lua_pushnil(self.state); } Value::Boolean(b) => { - ffi::lua_pushboolean(state, if b { 1 } else { 0 }); + ffi::lua_pushboolean(self.state, if b { 1 } else { 0 }); } Value::LightUserData(ud) => { - ffi::lua_pushlightuserdata(state, ud.0); + ffi::lua_pushlightuserdata(self.state, ud.0); } Value::Integer(i) => { - ffi::lua_pushinteger(state, i); + ffi::lua_pushinteger(self.state, i); } Value::Number(n) => { - ffi::lua_pushnumber(state, n); + ffi::lua_pushnumber(self.state, n); } Value::String(s) => { - self.push_ref(state, &s.0); + self.push_ref(&s.0); } Value::Table(t) => { - self.push_ref(state, &t.0); + self.push_ref(&t.0); } Value::Function(f) => { - self.push_ref(state, &f.0); + self.push_ref(&f.0); } Value::Thread(t) => { - self.push_ref(state, &t.0); + self.push_ref(&t.0); } Value::UserData(ud) => { - self.push_ref(state, &ud.0); + self.push_ref(&ud.0); } Value::Error(e) => { - push_wrapped_error(state, e); + push_wrapped_error(self.state, e); } } } // Uses 2 stack spaces, does not call checkstack - pub(crate) unsafe fn pop_value(&self, state: *mut ffi::lua_State) -> Value { - match ffi::lua_type(state, -1) { + pub(crate) unsafe fn pop_value(&self) -> Value { + match ffi::lua_type(self.state, -1) { ffi::LUA_TNIL => { - ffi::lua_pop(state, 1); + ffi::lua_pop(self.state, 1); Nil } ffi::LUA_TBOOLEAN => { - let b = Value::Boolean(ffi::lua_toboolean(state, -1) != 0); - ffi::lua_pop(state, 1); + let b = Value::Boolean(ffi::lua_toboolean(self.state, -1) != 0); + ffi::lua_pop(self.state, 1); b } ffi::LUA_TLIGHTUSERDATA => { - let ud = Value::LightUserData(LightUserData(ffi::lua_touserdata(state, -1))); - ffi::lua_pop(state, 1); + let ud = Value::LightUserData(LightUserData(ffi::lua_touserdata(self.state, -1))); + ffi::lua_pop(self.state, 1); ud } - ffi::LUA_TNUMBER => if ffi::lua_isinteger(state, -1) != 0 { - let i = Value::Integer(ffi::lua_tointeger(state, -1)); - ffi::lua_pop(state, 1); + ffi::LUA_TNUMBER => if ffi::lua_isinteger(self.state, -1) != 0 { + let i = Value::Integer(ffi::lua_tointeger(self.state, -1)); + ffi::lua_pop(self.state, 1); i } else { - let n = Value::Number(ffi::lua_tonumber(state, -1)); - ffi::lua_pop(state, 1); + let n = Value::Number(ffi::lua_tonumber(self.state, -1)); + ffi::lua_pop(self.state, 1); n }, - ffi::LUA_TSTRING => Value::String(String(self.pop_ref(state))), + ffi::LUA_TSTRING => Value::String(String(self.pop_ref())), - ffi::LUA_TTABLE => Value::Table(Table(self.pop_ref(state))), + ffi::LUA_TTABLE => Value::Table(Table(self.pop_ref())), - ffi::LUA_TFUNCTION => Value::Function(Function(self.pop_ref(state))), + ffi::LUA_TFUNCTION => Value::Function(Function(self.pop_ref())), ffi::LUA_TUSERDATA => { // It should not be possible to interact with userdata types other than custom // UserData types OR a WrappedError. WrappedPanic should never be able to be caught // in lua, so it should never be here. - if let Some(err) = pop_wrapped_error(state) { + if let Some(err) = get_wrapped_error(self.state, -1).as_ref() { + let err = err.clone(); + ffi::lua_pop(self.state, 1); Value::Error(err) } else { - Value::UserData(AnyUserData(self.pop_ref(state))) + Value::UserData(AnyUserData(self.pop_ref())) } } - ffi::LUA_TTHREAD => Value::Thread(Thread(self.pop_ref(state))), + ffi::LUA_TTHREAD => Value::Thread(Thread(self.pop_ref())), _ => unreachable!("internal error: LUA_TNONE in pop_value"), } } // Used 1 stack space, does not call checkstack - pub(crate) unsafe fn push_ref(&self, state: *mut ffi::lua_State, lref: &LuaRef) { + pub(crate) unsafe fn push_ref(&self, lref: &LuaRef) { assert!( - lref.lua.main_state == self.main_state, + lref.lua as *const Lua == self as *const Lua, "Lua instance passed Value created from a different Lua" ); - ffi::lua_rawgeti( - state, - ffi::LUA_REGISTRYINDEX, - lref.registry_id as ffi::lua_Integer, - ); + match lref.ref_type { + RefType::Nil => ffi::lua_pushnil(self.state), + RefType::Stack { stack_slot } => { + ffi::lua_pushvalue(self.state, stack_slot); + } + RefType::Registry { registry_id } => { + ffi::lua_rawgeti( + self.state, + ffi::LUA_REGISTRYINDEX, + registry_id as ffi::lua_Integer, + ); + } + } } // Pops the topmost element of the stack and stores a reference to it in the @@ -742,12 +774,91 @@ impl Lua { // `LuaRef` is dropped. // // pop_ref uses 1 extra stack space and does not call checkstack - pub(crate) unsafe fn pop_ref(&self, state: *mut ffi::lua_State) -> LuaRef { - let registry_id = gc_guard(state, || ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)); - LuaRef { - lua: self, - registry_id: registry_id, - drop_unref: true, + pub(crate) unsafe fn pop_ref(&self) -> LuaRef { + for i in 0..REF_STACK_SIZE { + let ref_slot = &self.ref_stack_slots[i as usize]; + if ref_slot.get() == 0 { + ref_slot.set(1); + ffi::lua_replace(self.state, i + 1); + return LuaRef { + lua: self, + ref_type: RefType::Stack { stack_slot: i + 1 }, + }; + } + } + + let registry_id = gc_guard(self.state, || { + ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX) + }); + if registry_id == ffi::LUA_REFNIL { + LuaRef { + lua: self, + ref_type: RefType::Nil, + } + } else { + LuaRef { + lua: self, + ref_type: RefType::Registry { + registry_id: registry_id, + }, + } + } + } + + pub(crate) fn clone_ref(&self, lref: &LuaRef) -> LuaRef { + unsafe { + match lref.ref_type { + RefType::Nil => LuaRef { + lua: self, + ref_type: RefType::Nil, + }, + RefType::Stack { stack_slot } => { + let ref_slot = &self.ref_stack_slots[(stack_slot - 1) as usize]; + ref_slot.set(ref_slot.get() + 1); + LuaRef { + lua: self, + ref_type: RefType::Stack { stack_slot }, + } + } + RefType::Registry { registry_id } => { + check_stack(self.state, 2); + ffi::lua_rawgeti( + self.state, + ffi::LUA_REGISTRYINDEX, + registry_id as ffi::lua_Integer, + ); + let registry_id = gc_guard(self.state, || { + ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX) + }); + LuaRef { + lua: self, + ref_type: RefType::Registry { + registry_id: registry_id, + }, + } + } + } + } + } + + pub(crate) fn drop_ref(&self, lref: &mut LuaRef) { + unsafe { + match lref.ref_type { + RefType::Nil => {} + RefType::Stack { stack_slot } => { + let ref_slot = &self.ref_stack_slots[(stack_slot - 1) as usize]; + let ref_count = ref_slot.get(); + rlua_assert!(ref_count > 0, "ref slot use count has gone below zero"); + ref_slot.set(ref_count - 1); + if ref_count == 1 { + ffi::lua_pushnil(self.state); + ffi::lua_replace(self.state, stack_slot); + } + } + RefType::Registry { registry_id } => { + ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, registry_id); + } + } } } @@ -772,13 +883,13 @@ impl Lua { } } - stack_err_guard(self.state, move || { - check_stack(self.state, 5); - + stack_guard(self.state, move || { if let Some(table_id) = (*self.extra()).registered_userdata.get(&TypeId::of::()) { return Ok(*table_id); } + check_stack(self.state, 6); + let mut methods = UserDataMethods { methods: HashMap::new(), meta_methods: HashMap::new(), @@ -786,7 +897,7 @@ impl Lua { }; T::add_methods(&mut methods); - protect_lua_call(self.state, 0, 1, |state| { + protect_lua_closure(self.state, 0, 1, |state| { ffi::lua_newtable(state); })?; @@ -794,22 +905,19 @@ impl Lua { if has_methods { push_string(self.state, "__index")?; - protect_lua_call(self.state, 0, 1, |state| { + protect_lua_closure(self.state, 0, 1, |state| { ffi::lua_newtable(state); })?; for (k, m) in methods.methods { push_string(self.state, &k)?; - self.push_value( - self.state, - Value::Function(self.create_callback_function(m)?), - ); - protect_lua_call(self.state, 3, 1, |state| { + self.push_value(Value::Function(self.create_callback_function(m)?)); + protect_lua_closure(self.state, 3, 1, |state| { ffi::lua_rawset(state, -3); })?; } - protect_lua_call(self.state, 3, 1, |state| { + protect_lua_closure(self.state, 3, 1, |state| { ffi::lua_rawset(state, -3); })?; } @@ -819,15 +927,12 @@ impl Lua { push_string(self.state, "__index")?; ffi::lua_pushvalue(self.state, -1); ffi::lua_gettable(self.state, -3); - self.push_value( - self.state, - Value::Function(self.create_callback_function(m)?), - ); - protect_lua_call(self.state, 2, 1, |state| { + self.push_value(Value::Function(self.create_callback_function(m)?)); + protect_lua_closure(self.state, 2, 1, |state| { ffi::lua_pushcclosure(state, meta_index_impl, 2); })?; - protect_lua_call(self.state, 3, 1, |state| { + protect_lua_closure(self.state, 3, 1, |state| { ffi::lua_rawset(state, -3); })?; } else { @@ -857,11 +962,8 @@ impl Lua { MetaMethod::ToString => "__tostring", }; push_string(self.state, name)?; - self.push_value( - self.state, - Value::Function(self.create_callback_function(m)?), - ); - protect_lua_call(self.state, 3, 1, |state| { + self.push_value(Value::Function(self.create_callback_function(m)?)); + protect_lua_closure(self.state, 3, 1, |state| { ffi::lua_rawset(state, -3); })?; } @@ -869,13 +971,13 @@ impl Lua { push_string(self.state, "__gc")?; ffi::lua_pushcfunction(self.state, userdata_destructor::>); - protect_lua_call(self.state, 3, 1, |state| { + protect_lua_closure(self.state, 3, 1, |state| { ffi::lua_rawset(state, -3); })?; push_string(self.state, "__metatable")?; ffi::lua_pushboolean(self.state, 0); - protect_lua_call(self.state, 3, 1, |state| { + protect_lua_closure(self.state, 3, 1, |state| { ffi::lua_rawset(state, -3); })?; @@ -918,72 +1020,75 @@ impl Lua { // Ignores or `unwrap()`s 'm' errors, because this is assuming that nothing in the lua // standard library will have a `__gc` metamethod error. - stack_guard(state, || { - // Do not open the debug library, it can be used to cause unsafety. - ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1); - ffi::luaL_requiref(state, cstr!("coroutine"), ffi::luaopen_coroutine, 1); - ffi::luaL_requiref(state, cstr!("table"), ffi::luaopen_table, 1); - ffi::luaL_requiref(state, cstr!("io"), ffi::luaopen_io, 1); - ffi::luaL_requiref(state, cstr!("os"), ffi::luaopen_os, 1); - ffi::luaL_requiref(state, cstr!("string"), ffi::luaopen_string, 1); - ffi::luaL_requiref(state, cstr!("utf8"), ffi::luaopen_utf8, 1); - ffi::luaL_requiref(state, cstr!("math"), ffi::luaopen_math, 1); - ffi::luaL_requiref(state, cstr!("package"), ffi::luaopen_package, 1); - ffi::lua_pop(state, 9); - init_error_metatables(state); + // Do not open the debug library, it can be used to cause unsafety. + ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1); + ffi::luaL_requiref(state, cstr!("coroutine"), ffi::luaopen_coroutine, 1); + ffi::luaL_requiref(state, cstr!("table"), ffi::luaopen_table, 1); + ffi::luaL_requiref(state, cstr!("io"), ffi::luaopen_io, 1); + ffi::luaL_requiref(state, cstr!("os"), ffi::luaopen_os, 1); + ffi::luaL_requiref(state, cstr!("string"), ffi::luaopen_string, 1); + ffi::luaL_requiref(state, cstr!("utf8"), ffi::luaopen_utf8, 1); + ffi::luaL_requiref(state, cstr!("math"), ffi::luaopen_math, 1); + ffi::luaL_requiref(state, cstr!("package"), ffi::luaopen_package, 1); + ffi::lua_pop(state, 9); - if load_debug { - ffi::luaL_requiref(state, cstr!("debug"), ffi::luaopen_debug, 1); - ffi::lua_pop(state, 1); - } - - // Create the function metatable - - ffi::lua_pushlightuserdata( - state, - &FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, - ); - - ffi::lua_newtable(state); - - push_string(state, "__gc").unwrap(); - ffi::lua_pushcfunction(state, userdata_destructor::); - ffi::lua_rawset(state, -3); - - push_string(state, "__metatable").unwrap(); - ffi::lua_pushboolean(state, 0); - ffi::lua_rawset(state, -3); - - ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); - - // Override pcall and xpcall with versions that cannot be used to catch rust panics. - - ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); - - push_string(state, "pcall").unwrap(); - ffi::lua_pushcfunction(state, safe_pcall); - ffi::lua_rawset(state, -3); - - push_string(state, "xpcall").unwrap(); - ffi::lua_pushcfunction(state, safe_xpcall); - ffi::lua_rawset(state, -3); + init_error_metatables(state); + if load_debug { + ffi::luaL_requiref(state, cstr!("debug"), ffi::luaopen_debug, 1); ffi::lua_pop(state, 1); + } - // Create ExtraData, and place it in the lua_State "extra space" + // Create the function metatable - let extra_data = Box::into_raw(Box::new(ExtraData { - registered_userdata: HashMap::new(), - registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))), - })); - *(ffi::lua_getextraspace(state) as *mut *mut ExtraData) = extra_data; - }); + ffi::lua_pushlightuserdata( + state, + &FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, + ); + + ffi::lua_newtable(state); + + push_string(state, "__gc").unwrap(); + ffi::lua_pushcfunction(state, userdata_destructor::); + ffi::lua_rawset(state, -3); + + push_string(state, "__metatable").unwrap(); + ffi::lua_pushboolean(state, 0); + ffi::lua_rawset(state, -3); + + ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); + + // Override pcall and xpcall with versions that cannot be used to catch rust panics. + + ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); + + push_string(state, "pcall").unwrap(); + ffi::lua_pushcfunction(state, safe_pcall); + ffi::lua_rawset(state, -3); + + push_string(state, "xpcall").unwrap(); + ffi::lua_pushcfunction(state, safe_xpcall); + ffi::lua_rawset(state, -3); + + ffi::lua_pop(state, 1); + + // Create ExtraData, and place it in the lua_State "extra space" + + let extra_data = Box::into_raw(Box::new(ExtraData { + registered_userdata: HashMap::new(), + registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))), + })); + *(ffi::lua_getextraspace(state) as *mut *mut ExtraData) = extra_data; + + rlua_assert!(ffi::lua_gettop(state) == 0, "stack leak during creation"); + check_stack(state, REF_STACK_SIZE); + ffi::lua_settop(state, REF_STACK_SIZE); Lua { state, - main_state: state, ephemeral: false, + ref_stack_slots: Default::default(), } } @@ -993,24 +1098,18 @@ impl Lua { ) -> Result> { unsafe extern "C" fn callback_call_impl(state: *mut ffi::lua_State) -> c_int { callback_error(state, || { - let lua = Lua { - state: state, - main_state: main_state(state), - ephemeral: true, - }; - if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL { return Err(Error::CallbackDestructed); } - let func = get_userdata::(state, ffi::lua_upvalueindex(1)); + let lua = Lua { + state: state, + ephemeral: true, + ref_stack_slots: Default::default(), + }; + let args = lua.setup_callback_stack_slots(); - let nargs = ffi::lua_gettop(state); - let mut args = MultiValue::new(); - check_stack(state, 2); - for _ in 0..nargs { - args.push_front(lua.pop_value(state)); - } + let func = get_userdata::(state, ffi::lua_upvalueindex(1)); let results = (*func)(&lua, args)?; let nresults = results.len() as c_int; @@ -1018,7 +1117,7 @@ impl Lua { check_stack_err(state, nresults)?; for r in results { - lua.push_value(state, r); + lua.push_value(r); } Ok(nresults) @@ -1026,8 +1125,8 @@ impl Lua { } unsafe { - stack_err_guard(self.state, move || { - check_stack(self.state, 2); + stack_guard(self.state, move || { + check_stack(self.state, 4); push_userdata::(self.state, func)?; @@ -1038,11 +1137,11 @@ impl Lua { ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX); ffi::lua_setmetatable(self.state, -2); - protect_lua_call(self.state, 1, 1, |state| { + protect_lua_closure(self.state, 1, 1, |state| { ffi::lua_pushcclosure(state, callback_call_impl, 1); })?; - Ok(Function(self.pop_ref(self.state))) + Ok(Function(self.pop_ref())) }) } } @@ -1052,8 +1151,8 @@ impl Lua { T: UserData, { unsafe { - stack_err_guard(self.state, move || { - check_stack(self.state, 3); + stack_guard(self.state, move || { + check_stack(self.state, 4); push_userdata::>(self.state, RefCell::new(data))?; @@ -1065,13 +1164,104 @@ impl Lua { ffi::lua_setmetatable(self.state, -2); - Ok(AnyUserData(self.pop_ref(self.state))) + 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> { + unsafe { + check_stack(self.state, 2); + + let nargs = ffi::lua_gettop(self.state); + let stack_nargs = cmp::min(REF_STACK_SIZE, nargs); + + let mut args = MultiValue::new(); + args.reserve(stack_nargs as usize); + + for i in 0..stack_nargs { + let n = stack_nargs - i; + + let make_ref = || { + self.ref_stack_slots[(n - 1) as usize].set(1); + LuaRef { + lua: self, + ref_type: RefType::Stack { stack_slot: n }, + } + }; + + match ffi::lua_type(self.state, n) { + ffi::LUA_TNIL => { + args.push_front(Value::Nil); + } + + ffi::LUA_TBOOLEAN => { + args.push_front(Value::Boolean(ffi::lua_toboolean(self.state, n) != 0)); + } + + ffi::LUA_TLIGHTUSERDATA => { + args.push_front(Value::LightUserData(LightUserData( + ffi::lua_touserdata(self.state, n), + ))); + } + + ffi::LUA_TNUMBER => if ffi::lua_isinteger(self.state, n) != 0 { + args.push_front(Value::Integer(ffi::lua_tointeger(self.state, n))); + } else { + args.push_front(Value::Number(ffi::lua_tonumber(self.state, n))); + }, + + ffi::LUA_TSTRING => { + args.push_front(Value::String(String(make_ref()))); + } + + ffi::LUA_TTABLE => { + args.push_front(Value::Table(Table(make_ref()))); + } + + ffi::LUA_TFUNCTION => { + args.push_front(Value::Function(Function(make_ref()))); + } + + ffi::LUA_TUSERDATA => { + if let Some(err) = get_wrapped_error(self.state, n).as_ref() { + args.push_front(Value::Error(err.clone())); + } else { + args.push_front(Value::UserData(AnyUserData(make_ref()))); + } + } + + ffi::LUA_TTHREAD => { + args.push_front(Value::Thread(Thread(make_ref()))); + } + + _ => unreachable!("internal error: LUA_TNONE in pop_value"), + } + } + + if nargs < REF_STACK_SIZE { + check_stack(self.state, REF_STACK_SIZE - nargs); + ffi::lua_settop(self.state, REF_STACK_SIZE); + args + } else if nargs > REF_STACK_SIZE { + let mut extra_args = Vec::new(); + extra_args.reserve((nargs - REF_STACK_SIZE) as usize); + for _ in REF_STACK_SIZE..nargs { + extra_args.push(self.pop_value()); + } + extra_args.extend(args.into_vec_rev()); + + MultiValue::from_vec_rev(extra_args) + } else { + args + } + } + } + unsafe fn extra(&self) -> *mut ExtraData { - *(ffi::lua_getextraspace(self.main_state) as *mut *mut ExtraData) + *(ffi::lua_getextraspace(self.state) as *mut *mut ExtraData) } } @@ -1094,21 +1284,15 @@ impl<'scope> Scope<'scope> { func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) }); let f = mem::transmute::, Callback<'callback, 'static>>(f); - let mut f = self.lua.create_callback_function(f)?; + let f = self.lua.create_callback_function(f)?; - f.0.drop_unref = false; let mut destructors = self.destructors.borrow_mut(); - let registry_id = f.0.registry_id; - destructors.push(Box::new(move |state| { + let f_destruct = f.0.clone(); + destructors.push(Box::new(move || { + let state = f_destruct.lua.state; stack_guard(state, || { check_stack(state, 2); - - ffi::lua_rawgeti( - state, - ffi::LUA_REGISTRYINDEX, - registry_id as ffi::lua_Integer, - ); - ffi::luaL_unref(state, ffi::LUA_REGISTRYINDEX, registry_id); + f_destruct.lua.push_ref(&f_destruct); ffi::lua_getupvalue(state, -1, 1); let ud = take_userdata::(state); @@ -1159,19 +1343,14 @@ impl<'scope> Scope<'scope> { T: UserData, { unsafe { - let mut u = self.lua.do_create_userdata(data)?; - u.0.drop_unref = false; + let u = self.lua.do_create_userdata(data)?; let mut destructors = self.destructors.borrow_mut(); - let registry_id = u.0.registry_id; - destructors.push(Box::new(move |state| { + let u_destruct = u.0.clone(); + destructors.push(Box::new(move || { + let state = u_destruct.lua.state; stack_guard(state, || { check_stack(state, 1); - ffi::lua_rawgeti( - state, - ffi::LUA_REGISTRYINDEX, - registry_id as ffi::lua_Integer, - ); - ffi::luaL_unref(state, ffi::LUA_REGISTRYINDEX, registry_id); + u_destruct.lua.push_ref(&u_destruct); Box::new(take_userdata::>(state)) }) })); @@ -1186,14 +1365,11 @@ impl<'scope> Drop for Scope<'scope> { // 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 state = self.lua.state; let to_drop = self.destructors .get_mut() .drain(..) - .map(|destructor| destructor(state)) + .map(|destructor| destructor()) .collect::>(); drop(to_drop); } } - -static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0; diff --git a/src/multi.rs b/src/multi.rs index e0c10e0..3b22c5a 100644 --- a/src/multi.rs +++ b/src/multi.rs @@ -2,9 +2,9 @@ use std::ops::{Deref, DerefMut}; use std::iter::FromIterator; use std::result::Result as StdResult; -use error::*; -use value::*; -use lua::*; +use error::Result; +use value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti}; +use lua::Lua; /// Result is convertible to `MultiValue` following the common Lua idiom of returning the result /// on success, or in the case of an error, returning `nil` and an error message. @@ -13,10 +13,10 @@ impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult let mut result = MultiValue::new(); match self { - Ok(v) => result.push_back(v.to_lua(lua)?), + Ok(v) => result.push_front(v.to_lua(lua)?), Err(e) => { - result.push_back(Nil); - result.push_back(e.to_lua(lua)?); + result.push_front(e.to_lua(lua)?); + result.push_front(Nil); } } @@ -27,7 +27,7 @@ impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T { fn to_lua_multi(self, lua: &'lua Lua) -> Result> { let mut v = MultiValue::new(); - v.push_back(self.to_lua(lua)?); + v.push_front(self.to_lua(lua)?); Ok(v) } } diff --git a/src/string.rs b/src/string.rs index ba127ef..d854b16 100644 --- a/src/string.rs +++ b/src/string.rs @@ -71,7 +71,7 @@ impl<'lua> String<'lua> { unsafe { stack_guard(lua.state, || { check_stack(lua.state, 1); - lua.push_ref(lua.state, &self.0); + lua.push_ref(&self.0); rlua_assert!( ffi::lua_type(lua.state, -1) == ffi::LUA_TSTRING, "string ref is not string type" @@ -82,7 +82,6 @@ impl<'lua> String<'lua> { // string type let data = ffi::lua_tolstring(lua.state, -1, &mut size); - ffi::lua_pop(lua.state, 1); slice::from_raw_parts(data as *const u8, size + 1) }) } diff --git a/src/table.rs b/src/table.rs index 0b7875c..693e017 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1,9 +1,10 @@ use std::marker::PhantomData; +use std::os::raw::c_int; use ffi; use error::Result; -use util::*; -use types::{Integer, LuaRef}; +use util::{check_stack, protect_lua, protect_lua_closure, stack_guard}; +use types::{Integer, LuaRef, RefType}; use value::{FromLua, ToLua}; /// Handle to an internal Lua table. @@ -51,14 +52,17 @@ impl<'lua> Table<'lua> { pub fn set, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> { let lua = self.0.lua; unsafe { - stack_err_guard(lua.state, || { + stack_guard(lua.state, || { 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)?); - protect_lua_call(lua.state, 3, 0, |state| { + lua.push_ref(&self.0); + lua.push_value(key.to_lua(lua)?); + lua.push_value(value.to_lua(lua)?); + + unsafe extern "C" fn set_table(state: *mut ffi::lua_State) -> c_int { ffi::lua_settable(state, -3); - }) + 1 + } + protect_lua(lua.state, 3, set_table) }) } } @@ -94,12 +98,18 @@ impl<'lua> Table<'lua> { pub fn get, V: FromLua<'lua>>(&self, key: K) -> Result { let lua = self.0.lua; unsafe { - stack_err_guard(lua.state, || { + stack_guard(lua.state, || { check_stack(lua.state, 5); - lua.push_ref(lua.state, &self.0); - lua.push_value(lua.state, key.to_lua(lua)?); - protect_lua_call(lua.state, 2, 1, |state| ffi::lua_gettable(state, -2))?; - V::from_lua(lua.pop_value(lua.state), lua) + lua.push_ref(&self.0); + lua.push_value(key.to_lua(lua)?); + + unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int { + ffi::lua_gettable(state, -2); + 1 + } + protect_lua(lua.state, 2, get_table)?; + + V::from_lua(lua.pop_value(), lua) }) } } @@ -108,13 +118,18 @@ impl<'lua> Table<'lua> { pub fn contains_key>(&self, key: K) -> Result { let lua = self.0.lua; unsafe { - stack_err_guard(lua.state, || { + stack_guard(lua.state, || { check_stack(lua.state, 5); - lua.push_ref(lua.state, &self.0); - lua.push_value(lua.state, key.to_lua(lua)?); - protect_lua_call(lua.state, 2, 1, |state| ffi::lua_gettable(state, -2))?; + lua.push_ref(&self.0); + lua.push_value(key.to_lua(lua)?); + + unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int { + ffi::lua_gettable(state, -2); + 1 + } + protect_lua(lua.state, 2, get_table)?; + let has = ffi::lua_isnil(lua.state, -1) == 0; - ffi::lua_pop(lua.state, 1); Ok(has) }) } @@ -124,14 +139,18 @@ impl<'lua> Table<'lua> { pub fn raw_set, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> { let lua = self.0.lua; unsafe { - stack_err_guard(lua.state, || { + stack_guard(lua.state, || { 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)?); - protect_lua_call(lua.state, 3, 0, |state| { + lua.push_ref(&self.0); + lua.push_value(key.to_lua(lua)?); + lua.push_value(value.to_lua(lua)?); + + unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int { ffi::lua_rawset(state, -3); - })?; + 0 + } + protect_lua(lua.state, 3, raw_set)?; + Ok(()) }) } @@ -141,13 +160,12 @@ impl<'lua> Table<'lua> { pub fn raw_get, V: FromLua<'lua>>(&self, key: K) -> Result { let lua = self.0.lua; unsafe { - stack_err_guard(lua.state, || { + stack_guard(lua.state, || { check_stack(lua.state, 3); - lua.push_ref(lua.state, &self.0); - lua.push_value(lua.state, key.to_lua(lua)?); + lua.push_ref(&self.0); + lua.push_value(key.to_lua(lua)?); ffi::lua_rawget(lua.state, -2); - let res = V::from_lua(lua.pop_value(lua.state), lua)?; - ffi::lua_pop(lua.state, 1); + let res = V::from_lua(lua.pop_value(), lua)?; Ok(res) }) } @@ -161,10 +179,10 @@ impl<'lua> Table<'lua> { pub fn len(&self) -> Result { let lua = self.0.lua; unsafe { - stack_err_guard(lua.state, || { + stack_guard(lua.state, || { check_stack(lua.state, 4); - lua.push_ref(lua.state, &self.0); - protect_lua_call(lua.state, 1, 0, |state| ffi::luaL_len(state, -1)) + lua.push_ref(&self.0); + protect_lua_closure(lua.state, 1, 0, |state| ffi::luaL_len(state, -1)) }) } } @@ -175,9 +193,8 @@ impl<'lua> Table<'lua> { unsafe { stack_guard(lua.state, || { check_stack(lua.state, 1); - lua.push_ref(lua.state, &self.0); + lua.push_ref(&self.0); let len = ffi::lua_rawlen(lua.state, -1); - ffi::lua_pop(lua.state, 1); len as Integer }) } @@ -191,13 +208,11 @@ impl<'lua> Table<'lua> { unsafe { stack_guard(lua.state, || { check_stack(lua.state, 1); - lua.push_ref(lua.state, &self.0); + lua.push_ref(&self.0); if ffi::lua_getmetatable(lua.state, -1) == 0 { - ffi::lua_pop(lua.state, 1); None } else { - let table = Table(lua.pop_ref(lua.state)); - ffi::lua_pop(lua.state, 1); + let table = Table(lua.pop_ref()); Some(table) } }) @@ -213,14 +228,13 @@ impl<'lua> Table<'lua> { unsafe { stack_guard(lua.state, move || { check_stack(lua.state, 1); - lua.push_ref(lua.state, &self.0); + lua.push_ref(&self.0); if let Some(metatable) = metatable { - lua.push_ref(lua.state, &metatable.0); + lua.push_ref(&metatable.0); } else { ffi::lua_pushnil(lua.state); } ffi::lua_setmetatable(lua.state, -2); - ffi::lua_pop(lua.state, 1); }) } } @@ -265,8 +279,7 @@ impl<'lua> Table<'lua> { pub fn pairs, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> { let next_key = Some(LuaRef { lua: self.0.lua, - registry_id: ffi::LUA_REFNIL, - drop_unref: true, + ref_type: RefType::Nil, }); TablePairs { @@ -349,26 +362,18 @@ where stack_guard(lua.state, || { check_stack(lua.state, 6); - lua.push_ref(lua.state, &self.table); - lua.push_ref(lua.state, &next_key); + lua.push_ref(&self.table); + lua.push_ref(&next_key); - match protect_lua_call(lua.state, 2, ffi::LUA_MULTRET, |state| { - if ffi::lua_next(state, -2) == 0 { - 0 - } else { - 1 - } + match protect_lua_closure(lua.state, 2, ffi::LUA_MULTRET, |state| { + ffi::lua_next(state, -2) != 0 }) { - Ok(0) => { - ffi::lua_pop(lua.state, 1); - None - } - Ok(_) => { + Ok(false) => None, + Ok(true) => { ffi::lua_pushvalue(lua.state, -2); - let key = lua.pop_value(lua.state); - let value = lua.pop_value(lua.state); - self.next_key = Some(lua.pop_ref(lua.state)); - ffi::lua_pop(lua.state, 1); + let key = lua.pop_value(); + let value = lua.pop_value(); + self.next_key = Some(lua.pop_ref()); Some((|| { let key = K::from_lua(key, lua)?; @@ -411,15 +416,13 @@ where stack_guard(lua.state, || { check_stack(lua.state, 5); - lua.push_ref(lua.state, &self.table); - match protect_lua_call(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index)) - { - Ok(ffi::LUA_TNIL) => { - ffi::lua_pop(lua.state, 1); - None - } + lua.push_ref(&self.table); + match protect_lua_closure(lua.state, 1, 1, |state| { + ffi::lua_geti(state, -1, index) + }) { + Ok(ffi::LUA_TNIL) => None, Ok(_) => { - let value = lua.pop_value(lua.state); + let value = lua.pop_value(); self.index = Some(index + 1); Some(V::from_lua(value, lua)) } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 0490c7d..7491e8d 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -744,3 +744,38 @@ fn too_many_binds() { .is_err() ); } + +#[test] +fn large_args() { + let lua = Lua::new(); + let globals = lua.globals(); + + globals + .set( + "c", + lua.create_function(|_, args: Variadic| { + let mut s = 0; + for i in 0..args.len() { + s += i; + assert_eq!(i, args[i]); + } + Ok(s) + }).unwrap(), + ) + .unwrap(); + + let f: Function = lua.eval( + r#" + return function(...) + return c(...) + end + "#, + None, + ).unwrap(); + + assert_eq!( + f.call::<_, usize>((0..100).collect::>()) + .unwrap(), + 4950 + ); +} diff --git a/src/thread.rs b/src/thread.rs index 1c8d753..7174240 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -1,8 +1,8 @@ use std::os::raw::c_int; use ffi; -use error::*; -use util::*; +use error::{Error, Result}; +use util::{check_stack, check_stack_err, error_traceback, pop_error, stack_guard}; use types::LuaRef; use value::{FromLuaMulti, MultiValue, ToLuaMulti}; @@ -78,10 +78,10 @@ impl<'lua> Thread<'lua> { { let lua = self.0.lua; unsafe { - stack_err_guard(lua.state, || { + stack_guard(lua.state, || { check_stack(lua.state, 1); - lua.push_ref(lua.state, &self.0); + lua.push_ref(&self.0); let thread_state = ffi::lua_tothread(lua.state, -1); let status = ffi::lua_status(thread_state); @@ -93,11 +93,13 @@ impl<'lua> Thread<'lua> { let args = args.to_lua_multi(lua)?; let nargs = args.len() as c_int; + check_stack_err(lua.state, nargs)?; check_stack_err(thread_state, nargs + 1)?; for arg in args { - lua.push_value(thread_state, arg); + lua.push_value(arg); } + ffi::lua_xmove(lua.state, thread_state, nargs); let ret = ffi::lua_resume(thread_state, lua.state, nargs); if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD { @@ -107,9 +109,11 @@ impl<'lua> Thread<'lua> { let nresults = ffi::lua_gettop(thread_state); let mut results = MultiValue::new(); - check_stack(thread_state, 2); + ffi::lua_xmove(thread_state, lua.state, nresults); + + check_stack(lua.state, 2); for _ in 0..nresults { - results.push_front(lua.pop_value(thread_state)); + results.push_front(lua.pop_value()); } R::from_lua_multi(results, lua) }) @@ -123,7 +127,7 @@ impl<'lua> Thread<'lua> { stack_guard(lua.state, || { check_stack(lua.state, 1); - lua.push_ref(lua.state, &self.0); + lua.push_ref(&self.0); let thread_state = ffi::lua_tothread(lua.state, -1); ffi::lua_pop(lua.state, 1); diff --git a/src/types.rs b/src/types.rs index 4b09f24..48fe6c0 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{fmt, mem, ptr}; use std::os::raw::{c_int, c_void}; use std::sync::{Arc, Mutex}; @@ -16,6 +16,9 @@ pub type Number = ffi::lua_Number; #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct LightUserData(pub *mut c_void); +pub(crate) type Callback<'lua, 'a> = + Box) -> Result> + 'a>; + /// An auto generated key into the Lua registry. /// /// This is a handle into a value stored inside the Lua registry, similar to the normal handle types @@ -32,57 +35,54 @@ pub struct LightUserData(pub *mut c_void); pub struct RegistryKey { pub(crate) registry_id: c_int, pub(crate) unref_list: Arc>>>, - pub(crate) drop_unref: bool, } impl Drop for RegistryKey { fn drop(&mut self) { - if self.drop_unref { - if let Some(list) = self.unref_list.lock().unwrap().as_mut() { - list.push(self.registry_id); - } + if let Some(list) = self.unref_list.lock().unwrap().as_mut() { + list.push(self.registry_id); } } } -pub(crate) type Callback<'lua, 'a> = - Box) -> Result> + 'a>; +impl RegistryKey { + // Destroys the RegistryKey without adding to the drop list + pub(crate) fn take(self) -> c_int { + let registry_id = self.registry_id; + unsafe { + ptr::read(&self.unref_list); + mem::forget(self); + } + registry_id + } +} + +#[derive(Debug)] +pub(crate) enum RefType { + Nil, + Stack { stack_slot: c_int }, + Registry { registry_id: c_int }, +} pub(crate) struct LuaRef<'lua> { - pub lua: &'lua Lua, - pub registry_id: c_int, - pub drop_unref: bool, + pub(crate) lua: &'lua Lua, + pub(crate) ref_type: RefType, } impl<'lua> fmt::Debug for LuaRef<'lua> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "LuaRef({})", self.registry_id) + write!(f, "{:?}", self.ref_type) } } impl<'lua> Clone for LuaRef<'lua> { fn clone(&self) -> Self { - if self.drop_unref { - unsafe { - self.lua.push_ref(self.lua.state, self); - self.lua.pop_ref(self.lua.state) - } - } else { - LuaRef { - lua: self.lua, - registry_id: self.registry_id, - drop_unref: self.drop_unref, - } - } + self.lua.clone_ref(self) } } impl<'lua> Drop for LuaRef<'lua> { fn drop(&mut self) { - if self.drop_unref { - unsafe { - ffi::luaL_unref(self.lua.state, ffi::LUA_REGISTRYINDEX, self.registry_id); - } - } + self.lua.drop_ref(self) } } diff --git a/src/userdata.rs b/src/userdata.rs index d40eb1f..6ff6c07 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -4,8 +4,8 @@ use std::collections::HashMap; use std::string::String as StdString; use ffi; -use error::*; -use util::*; +use error::{Error, Result}; +use util::{check_stack, get_userdata, stack_guard}; use types::{Callback, LuaRef}; use value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti}; use lua::Lua; @@ -415,10 +415,10 @@ impl<'lua> AnyUserData<'lua> { { unsafe { let lua = self.0.lua; - stack_err_guard(lua.state, move || { + stack_guard(lua.state, move || { check_stack(lua.state, 3); - lua.push_ref(lua.state, &self.0); + lua.push_ref(&self.0); rlua_assert!( ffi::lua_getmetatable(lua.state, -1) != 0, @@ -432,11 +432,9 @@ impl<'lua> AnyUserData<'lua> { ); if ffi::lua_rawequal(lua.state, -1, -2) == 0 { - ffi::lua_pop(lua.state, 3); Err(Error::UserDataTypeMismatch) } else { let res = func(&*get_userdata::>(lua.state, -3)); - ffi::lua_pop(lua.state, 3); res } }) @@ -451,12 +449,11 @@ impl<'lua> AnyUserData<'lua> { pub fn set_user_value>(&self, v: V) -> Result<()> { let lua = self.0.lua; unsafe { - stack_err_guard(lua.state, || { + stack_guard(lua.state, || { check_stack(lua.state, 2); - lua.push_ref(lua.state, &self.0); - lua.push_value(lua.state, v.to_lua(lua)?); + lua.push_ref(&self.0); + lua.push_value(v.to_lua(lua)?); ffi::lua_setuservalue(lua.state, -2); - ffi::lua_pop(lua.state, 1); Ok(()) }) } @@ -468,12 +465,11 @@ impl<'lua> AnyUserData<'lua> { pub fn get_user_value>(&self) -> Result { let lua = self.0.lua; unsafe { - stack_err_guard(lua.state, || { + stack_guard(lua.state, || { check_stack(lua.state, 3); - lua.push_ref(lua.state, &self.0); + lua.push_ref(&self.0); ffi::lua_getuservalue(lua.state, -1); - let res = V::from_lua(lua.pop_value(lua.state), lua)?; - ffi::lua_pop(lua.state, 1); + let res = V::from_lua(lua.pop_value(), lua)?; Ok(res) }) } diff --git a/src/util.rs b/src/util.rs index ba4dda8..724fde8 100644 --- a/src/util.rs +++ b/src/util.rs @@ -27,74 +27,71 @@ pub unsafe fn check_stack_err(state: *mut ffi::lua_State, amount: c_int) -> Resu } } -// Run an operation on a lua_State and ensure that there are no stack leaks and the stack is -// restored on panic. +pub struct StackGuard { + state: *mut ffi::lua_State, + top: c_int, +} + +impl StackGuard { + // Creates a StackGuard instance with wa record of the stack size, and on Drop will check the + // stack size and drop any extra elements. If the stack size at the end is *smaller* than at + // the beginning, this is considered a fatal logic error and will result in an abort. + pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard { + StackGuard { + state, + top: ffi::lua_gettop(state), + } + } +} + +impl Drop for StackGuard { + fn drop(&mut self) { + unsafe { + let top = ffi::lua_gettop(self.state); + if top > self.top { + ffi::lua_settop(self.state, self.top); + } else if top < self.top { + rlua_panic!("{} too many stack values popped", self.top - top); + } + } + } +} + +// Run an operation on a lua_State and restores the stack state at the end, using `StackGuard`. pub unsafe fn stack_guard(state: *mut ffi::lua_State, op: F) -> R where F: FnOnce() -> R, { - let begin = ffi::lua_gettop(state); - - let res = match catch_unwind(AssertUnwindSafe(op)) { - Ok(r) => r, - Err(p) => { - let top = ffi::lua_gettop(state); - if top > begin { - ffi::lua_settop(state, begin); - } - resume_unwind(p); - } - }; - - let top = ffi::lua_gettop(state); - if top > begin { - ffi::lua_settop(state, begin); - rlua_panic!("expected stack to be {}, got {}", begin, top); - } else if top < begin { - rlua_abort!("{} too many stack values popped", begin - top); - } - - res + let _stack_guard = StackGuard::new(state); + op() } -// Run an operation on a lua_State and automatically clean up the stack on error. Takes the -// lua_State and an operation to run. If the operation results in success, then the stack is -// inspected to make sure there is not a stack leak, and otherwise this is a logic error and will -// panic. If the operation results in an error, or if the operation panics, the stack is shrunk to -// the value before the call. -pub unsafe fn stack_err_guard(state: *mut ffi::lua_State, op: F) -> Result -where - F: FnOnce() -> Result, -{ - let begin = ffi::lua_gettop(state); +// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way. +// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a +// limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is +// always LUA_MULTRET. Internally uses 2 extra stack spaces, and does not call checkstack. +// Provided function must *never* panic. +pub unsafe fn protect_lua( + state: *mut ffi::lua_State, + nargs: c_int, + f: unsafe extern "C" fn(*mut ffi::lua_State) -> c_int, +) -> Result<()> { + let stack_start = ffi::lua_gettop(state) - nargs; - let res = match catch_unwind(AssertUnwindSafe(op)) { - Ok(r) => r, - Err(p) => { - let top = ffi::lua_gettop(state); - if top > begin { - ffi::lua_settop(state, begin); - } - resume_unwind(p); - } - }; - - let top = ffi::lua_gettop(state); - if res.is_ok() { - if top > begin { - ffi::lua_settop(state, begin); - rlua_panic!("expected stack to be {}, got {}", begin, top); - } else if top < begin { - rlua_abort!("{} too many stack values popped", begin - top); - } - } else { - if top > begin { - ffi::lua_settop(state, begin); - } else if top < begin { - rlua_abort!("{} too many stack values popped", begin - top); - } + ffi::lua_pushcfunction(state, error_traceback); + ffi::lua_pushcfunction(state, f); + if nargs > 0 { + ffi::lua_rotate(state, stack_start + 1, 2); + } + + let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start + 1); + ffi::lua_remove(state, stack_start + 1); + + if ret == ffi::LUA_OK { + Ok(()) + } else { + Err(pop_error(state, ret)) } - res } // Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way. @@ -104,7 +101,7 @@ where // values are assumed to match the `nresults` param. Internally uses 3 extra stack spaces, and does // not call checkstack. Provided function must *not* panic, and since it will generally be // lonjmping, should not contain any values that implement Drop. -pub unsafe fn protect_lua_call( +pub unsafe fn protect_lua_closure( state: *mut ffi::lua_State, nargs: c_int, nresults: c_int, @@ -115,7 +112,7 @@ where R: Copy, { struct Params { - function: *const F, + function: F, result: R, nresults: c_int, } @@ -127,7 +124,7 @@ where let params = ffi::lua_touserdata(state, -1) as *mut Params; ffi::lua_pop(state, 1); - (*params).result = (*(*params).function)(state); + (*params).result = ((*params).function)(state); if (*params).nresults == ffi::LUA_MULTRET { ffi::lua_gettop(state) @@ -140,10 +137,12 @@ where ffi::lua_pushcfunction(state, error_traceback); ffi::lua_pushcfunction(state, do_call::); - ffi::lua_rotate(state, stack_start + 1, 2); + if nargs > 0 { + ffi::lua_rotate(state, stack_start + 1, 2); + } let mut params = Params { - function: &f, + function: f, result: mem::uninitialized(), nresults, }; @@ -173,8 +172,9 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error { "pop_error called with non-error return code" ); - if let Some(err) = pop_wrapped_error(state) { - err + if let Some(err) = get_wrapped_error(state, -1).as_ref() { + ffi::lua_pop(state, 1); + err.clone() } else if is_wrapped_panic(state, -1) { let panic = get_userdata::(state, -1); if let Some(p) = (*panic).0.take() { @@ -212,7 +212,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error { ffi::LUA_ERRMEM => { // This should be impossible, as we set the lua allocator to one that aborts // instead of failing. - rlua_abort!("impossible Lua allocation error, aborting!") + rlua_abort!("impossible Lua allocation error") } ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string), _ => rlua_panic!("unrecognized lua error code"), @@ -222,14 +222,14 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error { // Internally uses 4 stack spaces, does not call checkstack pub unsafe fn push_string(state: *mut ffi::lua_State, s: &str) -> Result<()> { - protect_lua_call(state, 0, 1, |state| { + protect_lua_closure(state, 0, 1, |state| { ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len()); }) } // Internally uses 4 stack spaces, does not call checkstack pub unsafe fn push_userdata(state: *mut ffi::lua_State, t: T) -> Result<()> { - let ud = protect_lua_call(state, 0, 1, move |state| { + let ud = protect_lua_closure(state, 0, 1, move |state| { ffi::lua_newuserdata(state, mem::size_of::()) as *mut T })?; ptr::write(ud, t); @@ -300,7 +300,7 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int { if ffi::lua_checkstack(state, 2) == 0 { // If we don't have enough stack space to even check the error type, do nothing - } else if is_wrapped_error(state, 1) { + } else if let Some(error) = get_wrapped_error(state, 1).as_ref() { let traceback = if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 { gc_guard(state, || { ffi::luaL_traceback(state, state, ptr::null(), 0); @@ -314,7 +314,9 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int { "not enough stack space for traceback".to_owned() }; - let error = pop_wrapped_error(state).unwrap(); + let error = error.clone(); + ffi::lua_pop(state, 1); + push_wrapped_error( state, Error::CallbackError { @@ -404,14 +406,6 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int { } } -// Does not call lua_checkstack, uses 1 stack space. -pub unsafe fn main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State { - ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD); - let main_state = ffi::lua_tothread(state, -1); - ffi::lua_pop(state, 1); - main_state -} - // 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) { @@ -424,18 +418,26 @@ pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) { ffi::lua_setmetatable(state, -2); } -// Pops a WrappedError off of the top of the stack, if it is a WrappedError. If it is not a -// WrappedError, returns None and does not pop anything. Uses 2 stack spaces and does not call -// lua_checkstack. -pub unsafe fn pop_wrapped_error(state: *mut ffi::lua_State) -> Option { - if !is_wrapped_error(state, -1) { - None +// Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it, +// otherwise returns null. Uses 2 stack spaces and does not call lua_checkstack. +pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *const Error { + let userdata = ffi::lua_touserdata(state, index); + if userdata.is_null() { + return ptr::null(); + } + + if ffi::lua_getmetatable(state, index) == 0 { + return ptr::null(); + } + + get_error_metatable(state); + let res = ffi::lua_rawequal(state, -1, -2) != 0; + ffi::lua_pop(state, 2); + + if res { + &(*get_userdata::(state, -1)).0 } else { - let err = &*get_userdata::(state, -1); - // We are assuming here that Error::clone() cannot panic. - let err = err.0.clone(); - ffi::lua_pop(state, 1); - Some(err) + ptr::null() } } @@ -466,9 +468,8 @@ pub unsafe fn init_error_metatables(state: *mut ffi::lua_State) { ffi::luaL_checkstack(state, 2, ptr::null()); callback_error(state, || { - if is_wrapped_error(state, -1) { - let error = get_userdata::(state, -1); - let error_str = (*error).0.to_string(); + if let Some(error) = get_wrapped_error(state, -1).as_ref() { + let error_str = error.to_string(); gc_guard(state, || { ffi::lua_pushlstring( state, @@ -587,24 +588,6 @@ unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box) ffi::lua_setmetatable(state, -2); } -// Checks if the value at the given index is a WrappedError, uses 2 stack spaces and does not call -// lua_checkstack. -unsafe fn is_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> bool { - let userdata = ffi::lua_touserdata(state, index); - if userdata.is_null() { - return false; - } - - if ffi::lua_getmetatable(state, index) == 0 { - return false; - } - - get_error_metatable(state); - let res = ffi::lua_rawequal(state, -1, -2) != 0; - ffi::lua_pop(state, 2); - res -} - // Checks if the value at the given index is a WrappedPanic. Uses 2 stack spaces and does not call // lua_checkstack. unsafe fn is_wrapped_panic(state: *mut ffi::lua_State, index: c_int) -> bool { diff --git a/src/value.rs b/src/value.rs index b3b4f2f..18c8810 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,9 +1,7 @@ -use std::str; -use std::ops::{Deref, DerefMut}; -use std::iter::FromIterator; -use std::collections::VecDeque; +use std::{slice, str, vec}; +use std::iter::{self, FromIterator}; -use error::*; +use error::{Error, Result}; use types::{Integer, LightUserData, Number}; use string::String; use table::Table; @@ -76,41 +74,77 @@ pub trait FromLua<'lua>: Sized { /// Multiple Lua values used for both argument passing and also for multiple return values. #[derive(Debug, Clone)] -pub struct MultiValue<'lua>(VecDeque>); +pub struct MultiValue<'lua>(Vec>); impl<'lua> MultiValue<'lua> { /// Creates an empty `MultiValue` containing no values. pub fn new() -> MultiValue<'lua> { - MultiValue(VecDeque::new()) + MultiValue(Vec::new()) } } impl<'lua> FromIterator> for MultiValue<'lua> { fn from_iter>>(iter: I) -> Self { - MultiValue(VecDeque::from_iter(iter)) + MultiValue::from_vec(Vec::from_iter(iter)) } } impl<'lua> IntoIterator for MultiValue<'lua> { type Item = Value<'lua>; - type IntoIter = > as IntoIterator>::IntoIter; + type IntoIter = iter::Rev>>; fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() + self.0.into_iter().rev() } } -impl<'lua> Deref for MultiValue<'lua> { - type Target = VecDeque>; +impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> { + type Item = &'a Value<'lua>; + type IntoIter = iter::Rev>>; - fn deref(&self) -> &Self::Target { - &self.0 + fn into_iter(self) -> Self::IntoIter { + (&self.0).into_iter().rev() } } -impl<'lua> DerefMut for MultiValue<'lua> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 +impl<'lua> MultiValue<'lua> { + pub fn from_vec(mut v: Vec>) -> MultiValue<'lua> { + v.reverse(); + MultiValue(v) + } + + pub fn into_vec(self) -> Vec> { + let mut v = self.0; + v.reverse(); + v + } + + pub fn from_vec_rev(v: Vec>) -> MultiValue<'lua> { + MultiValue(v) + } + + pub fn into_vec_rev(self) -> Vec> { + self.0 + } + + pub fn reserve(&mut self, size: usize) { + self.0.reserve(size); + } + + pub fn push_front(&mut self, value: Value<'lua>) { + self.0.push(value); + } + + pub fn pop_front(&mut self) -> Option> { + self.0.pop() + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn iter(&self) -> iter::Rev>> { + self.0.iter().rev() } }