Merge branch 'bench'

This commit is contained in:
kyren 2018-03-12 12:29:27 -04:00
commit 95633ce915
16 changed files with 926 additions and 502 deletions

View File

@ -22,7 +22,7 @@ default = ["builtin-lua"]
# * LUA_NUMBER as double # * LUA_NUMBER as double
# * LUA_EXTRASPACE is sizeof(void*) # * LUA_EXTRASPACE is sizeof(void*)
# * LUAI_MAXSTACK is 1000000 # * LUAI_MAXSTACK is 1000000
builtin-lua = ["gcc"] builtin-lua = ["cc"]
[dependencies] [dependencies]
libc = { version = "0.2" } libc = { version = "0.2" }
@ -30,7 +30,12 @@ failure = { version = "0.1.1" }
compiletest_rs = { version = "0.3", optional = true } compiletest_rs = { version = "0.3", optional = true }
[build-dependencies] [build-dependencies]
gcc = { version = "0.3.52", optional = true } cc = { version = "1.0", optional = true }
[dev-dependencies] [dev-dependencies]
rustyline = "1.0.0" rustyline = "1.0.0"
criterion = "0.2.0"
[[bench]]
name = "benchmark"
harness = false

187
benches/benchmark.rs Normal file
View File

@ -0,0 +1,187 @@
#[macro_use]
extern crate criterion;
extern crate rlua;
use criterion::Criterion;
use rlua::prelude::*;
fn create_table(c: &mut Criterion) {
c.bench_function("create table", |b| {
b.iter_with_setup(
|| Lua::new(),
|lua| -> Lua {
lua.create_table().unwrap();
lua
},
);
});
}
fn create_array(c: &mut Criterion) {
c.bench_function("create array 10", |b| {
b.iter_with_setup(
|| Lua::new(),
|lua| -> Lua {
{
let table = lua.create_table().unwrap();
for i in 1..11 {
table.set(i, i).unwrap();
}
}
lua
},
);
});
}
fn create_string_table(c: &mut Criterion) {
c.bench_function("create string table 10", |b| {
b.iter_with_setup(
|| Lua::new(),
|lua| -> Lua {
{
let table = lua.create_table().unwrap();
for &s in &["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] {
let s = lua.create_string(s).unwrap();
table.set(s.clone(), s).unwrap();
}
}
lua
},
);
});
}
fn call_add_function(c: &mut Criterion) {
c.bench_function("call add function 3 10", |b| {
b.iter_with_setup(
|| {
let lua = Lua::new();
let f = {
let f: LuaFunction = lua.eval(
r#"
function(a, b, c)
return a + b + c
end
"#,
None,
).unwrap();
lua.create_registry_value(f).unwrap()
};
(lua, f)
},
|(lua, f)| -> Lua {
{
let add_function: LuaFunction = lua.registry_value(&f).unwrap();
for i in 0..10 {
let _result: i64 = add_function.call((i, i + 1, i + 2)).unwrap();
}
}
lua
},
);
});
}
fn call_add_callback(c: &mut Criterion) {
c.bench_function("call callback add 2 10", |b| {
b.iter_with_setup(
|| {
let lua = Lua::new();
let f = {
let c: LuaFunction = lua.create_function(|_, (a, b, c): (i64, i64, i64)| {
Ok(a + b + c)
}).unwrap();
lua.globals().set("callback", c).unwrap();
let f: LuaFunction = lua.eval(
r#"
function()
for i = 1,10 do
callback(i, i, i)
end
end
"#,
None,
).unwrap();
lua.create_registry_value(f).unwrap()
};
(lua, f)
},
|(lua, f)| -> Lua {
{
let entry_function: LuaFunction = lua.registry_value(&f).unwrap();
entry_function.call::<_, ()>(()).unwrap();
}
lua
},
);
});
}
fn call_append_callback(c: &mut Criterion) {
c.bench_function("call callback append 10", |b| {
b.iter_with_setup(
|| {
let lua = Lua::new();
let f = {
let c: LuaFunction =
lua.create_function(|_, (a, b): (LuaString, LuaString)| {
Ok(format!("{}{}", a.to_str()?, b.to_str()?))
}).unwrap();
lua.globals().set("callback", c).unwrap();
let f: LuaFunction = lua.eval(
r#"
function()
for _ = 1,10 do
callback("a", "b")
end
end
"#,
None,
).unwrap();
lua.create_registry_value(f).unwrap()
};
(lua, f)
},
|(lua, f)| -> Lua {
{
let entry_function: LuaFunction = lua.registry_value(&f).unwrap();
entry_function.call::<_, ()>(()).unwrap();
}
lua
},
);
});
}
fn create_registry_values(c: &mut Criterion) {
c.bench_function("create registry 10", |b| {
b.iter_with_setup(
|| Lua::new(),
|lua| -> Lua {
for _ in 0..10 {
lua.create_registry_value(lua.pack(true).unwrap()).unwrap();
}
lua.expire_registry_values();
lua
},
);
});
}
criterion_group! {
name = benches;
config = Criterion::default()
.sample_size(200)
.noise_threshold(0.02);
targets =
create_table,
create_array,
create_string_table,
call_add_function,
call_add_callback,
call_append_callback,
create_registry_values
}
criterion_main!(benches);

View File

@ -1,5 +1,5 @@
#[cfg(feature = "builtin-lua")] #[cfg(feature = "builtin-lua")]
extern crate gcc; extern crate cc;
fn main() { fn main() {
#[cfg(feature = "builtin-lua")] #[cfg(feature = "builtin-lua")]
@ -9,7 +9,7 @@ fn main() {
let target_os = env::var("CARGO_CFG_TARGET_OS"); let target_os = env::var("CARGO_CFG_TARGET_OS");
let target_family = env::var("CARGO_CFG_TARGET_FAMILY"); let target_family = env::var("CARGO_CFG_TARGET_FAMILY");
let mut config = gcc::Build::new(); let mut config = cc::Build::new();
if target_os == Ok("linux".to_string()) { if target_os == Ok("linux".to_string()) {
config.define("LUA_USE_LINUX", None); config.define("LUA_USE_LINUX", None);

View File

@ -2,7 +2,7 @@ use std::collections::{BTreeMap, HashMap};
use std::hash::{BuildHasher, Hash}; use std::hash::{BuildHasher, Hash};
use std::string::String as StdString; use std::string::String as StdString;
use error::*; use error::{Error, Result};
use types::{Integer, LightUserData, Number}; use types::{Integer, LightUserData, Number};
use string::String; use string::String;
use table::Table; use table::Table;

View File

@ -123,6 +123,7 @@ extern "C" {
pub fn lua_rotate(state: *mut lua_State, index: c_int, n: c_int); 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_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_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_isinteger(state: *mut lua_State, index: c_int) -> c_int;
pub fn lua_isnumber(state: *mut lua_State, index: c_int) -> c_int; pub fn lua_isnumber(state: *mut lua_State, index: c_int) -> c_int;

View File

@ -2,8 +2,9 @@ use std::ptr;
use std::os::raw::c_int; use std::os::raw::c_int;
use ffi; use ffi;
use error::*; use error::{Error, Result};
use util::*; use util::{check_stack, check_stack_err, error_traceback, pop_error, protect_lua_closure,
stack_guard};
use types::LuaRef; use types::LuaRef;
use value::{FromLuaMulti, MultiValue, ToLuaMulti}; use value::{FromLuaMulti, MultiValue, ToLuaMulti};
@ -63,16 +64,16 @@ impl<'lua> Function<'lua> {
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> { pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
let args = args.to_lua_multi(lua)?; let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int; let nargs = args.len() as c_int;
check_stack_err(lua.state, nargs + 3)?; check_stack_err(lua.state, nargs + 3)?;
ffi::lua_pushcfunction(lua.state, error_traceback); ffi::lua_pushcfunction(lua.state, error_traceback);
let stack_start = ffi::lua_gettop(lua.state); let stack_start = ffi::lua_gettop(lua.state);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
for arg in args { 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); let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start);
if ret != ffi::LUA_OK { if ret != ffi::LUA_OK {
@ -82,7 +83,7 @@ impl<'lua> Function<'lua> {
let mut results = MultiValue::new(); let mut results = MultiValue::new();
check_stack(lua.state, 2); check_stack(lua.state, 2);
for _ in 0..nresults { for _ in 0..nresults {
results.push_front(lua.pop_value(lua.state)); results.push_front(lua.pop_value());
} }
ffi::lua_pop(lua.state, 1); ffi::lua_pop(lua.state, 1);
R::from_lua_multi(results, lua) R::from_lua_multi(results, lua)
@ -144,7 +145,7 @@ impl<'lua> Function<'lua> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
let args = args.to_lua_multi(lua)?; let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int; let nargs = args.len() as c_int;
@ -152,18 +153,18 @@ impl<'lua> Function<'lua> {
return Err(Error::BindError); return Err(Error::BindError);
} }
check_stack_err(lua.state, nargs + 3)?; check_stack_err(lua.state, nargs + 5)?;
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer); ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
for arg in args { for arg in args {
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); ffi::lua_pushcclosure(state, bind_call_impl, nargs + 2);
})?; })?;
Ok(Function(lua.pop_ref(lua.state))) Ok(Function(lua.pop_ref()))
}) })
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -2,9 +2,9 @@ use std::ops::{Deref, DerefMut};
use std::iter::FromIterator; use std::iter::FromIterator;
use std::result::Result as StdResult; use std::result::Result as StdResult;
use error::*; use error::Result;
use value::*; use value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
use lua::*; use lua::Lua;
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result /// 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. /// 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<T, E>
let mut result = MultiValue::new(); let mut result = MultiValue::new();
match self { match self {
Ok(v) => result.push_back(v.to_lua(lua)?), Ok(v) => result.push_front(v.to_lua(lua)?),
Err(e) => { Err(e) => {
result.push_back(Nil); result.push_front(e.to_lua(lua)?);
result.push_back(e.to_lua(lua)?); result.push_front(Nil);
} }
} }
@ -27,7 +27,7 @@ impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E>
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T { impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> { fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut v = MultiValue::new(); let mut v = MultiValue::new();
v.push_back(self.to_lua(lua)?); v.push_front(self.to_lua(lua)?);
Ok(v) Ok(v)
} }
} }

View File

@ -71,7 +71,7 @@ impl<'lua> String<'lua> {
unsafe { unsafe {
stack_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 1); check_stack(lua.state, 1);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
rlua_assert!( rlua_assert!(
ffi::lua_type(lua.state, -1) == ffi::LUA_TSTRING, ffi::lua_type(lua.state, -1) == ffi::LUA_TSTRING,
"string ref is not string type" "string ref is not string type"
@ -82,7 +82,6 @@ impl<'lua> String<'lua> {
// string type // string type
let data = ffi::lua_tolstring(lua.state, -1, &mut size); 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) slice::from_raw_parts(data as *const u8, size + 1)
}) })
} }

View File

@ -1,9 +1,10 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::os::raw::c_int;
use ffi; use ffi;
use error::Result; use error::Result;
use util::*; use util::{check_stack, protect_lua, protect_lua_closure, stack_guard};
use types::{Integer, LuaRef}; use types::{Integer, LuaRef, RefType};
use value::{FromLua, ToLua}; use value::{FromLua, ToLua};
/// Handle to an internal Lua table. /// Handle to an internal Lua table.
@ -51,14 +52,17 @@ impl<'lua> Table<'lua> {
pub fn set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> { pub fn set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 6); check_stack(lua.state, 6);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
lua.push_value(lua.state, key.to_lua(lua)?); lua.push_value(key.to_lua(lua)?);
lua.push_value(lua.state, value.to_lua(lua)?); lua.push_value(value.to_lua(lua)?);
protect_lua_call(lua.state, 3, 0, |state| {
unsafe extern "C" fn set_table(state: *mut ffi::lua_State) -> c_int {
ffi::lua_settable(state, -3); ffi::lua_settable(state, -3);
}) 1
}
protect_lua(lua.state, 3, set_table)
}) })
} }
} }
@ -94,12 +98,18 @@ impl<'lua> Table<'lua> {
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> { pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 5); check_stack(lua.state, 5);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
lua.push_value(lua.state, key.to_lua(lua)?); lua.push_value(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) 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<K: ToLua<'lua>>(&self, key: K) -> Result<bool> { pub fn contains_key<K: ToLua<'lua>>(&self, key: K) -> Result<bool> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 5); check_stack(lua.state, 5);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
lua.push_value(lua.state, key.to_lua(lua)?); lua.push_value(key.to_lua(lua)?);
protect_lua_call(lua.state, 2, 1, |state| ffi::lua_gettable(state, -2))?;
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; let has = ffi::lua_isnil(lua.state, -1) == 0;
ffi::lua_pop(lua.state, 1);
Ok(has) Ok(has)
}) })
} }
@ -124,14 +139,18 @@ impl<'lua> Table<'lua> {
pub fn raw_set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> { pub fn raw_set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 6); check_stack(lua.state, 6);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
lua.push_value(lua.state, key.to_lua(lua)?); lua.push_value(key.to_lua(lua)?);
lua.push_value(lua.state, value.to_lua(lua)?); lua.push_value(value.to_lua(lua)?);
protect_lua_call(lua.state, 3, 0, |state| {
unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int {
ffi::lua_rawset(state, -3); ffi::lua_rawset(state, -3);
})?; 0
}
protect_lua(lua.state, 3, raw_set)?;
Ok(()) Ok(())
}) })
} }
@ -141,13 +160,12 @@ impl<'lua> Table<'lua> {
pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> { pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 3); check_stack(lua.state, 3);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
lua.push_value(lua.state, key.to_lua(lua)?); lua.push_value(key.to_lua(lua)?);
ffi::lua_rawget(lua.state, -2); ffi::lua_rawget(lua.state, -2);
let res = V::from_lua(lua.pop_value(lua.state), lua)?; let res = V::from_lua(lua.pop_value(), lua)?;
ffi::lua_pop(lua.state, 1);
Ok(res) Ok(res)
}) })
} }
@ -161,10 +179,10 @@ impl<'lua> Table<'lua> {
pub fn len(&self) -> Result<Integer> { pub fn len(&self) -> Result<Integer> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 4); check_stack(lua.state, 4);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
protect_lua_call(lua.state, 1, 0, |state| ffi::luaL_len(state, -1)) protect_lua_closure(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
}) })
} }
} }
@ -175,9 +193,8 @@ impl<'lua> Table<'lua> {
unsafe { unsafe {
stack_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 1); 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); let len = ffi::lua_rawlen(lua.state, -1);
ffi::lua_pop(lua.state, 1);
len as Integer len as Integer
}) })
} }
@ -191,13 +208,11 @@ impl<'lua> Table<'lua> {
unsafe { unsafe {
stack_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 1); 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 { if ffi::lua_getmetatable(lua.state, -1) == 0 {
ffi::lua_pop(lua.state, 1);
None None
} else { } else {
let table = Table(lua.pop_ref(lua.state)); let table = Table(lua.pop_ref());
ffi::lua_pop(lua.state, 1);
Some(table) Some(table)
} }
}) })
@ -213,14 +228,13 @@ impl<'lua> Table<'lua> {
unsafe { unsafe {
stack_guard(lua.state, move || { stack_guard(lua.state, move || {
check_stack(lua.state, 1); check_stack(lua.state, 1);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
if let Some(metatable) = metatable { if let Some(metatable) = metatable {
lua.push_ref(lua.state, &metatable.0); lua.push_ref(&metatable.0);
} else { } else {
ffi::lua_pushnil(lua.state); ffi::lua_pushnil(lua.state);
} }
ffi::lua_setmetatable(lua.state, -2); ffi::lua_setmetatable(lua.state, -2);
ffi::lua_pop(lua.state, 1);
}) })
} }
} }
@ -265,8 +279,7 @@ impl<'lua> Table<'lua> {
pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> { pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> {
let next_key = Some(LuaRef { let next_key = Some(LuaRef {
lua: self.0.lua, lua: self.0.lua,
registry_id: ffi::LUA_REFNIL, ref_type: RefType::Nil,
drop_unref: true,
}); });
TablePairs { TablePairs {
@ -349,26 +362,18 @@ where
stack_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 6); check_stack(lua.state, 6);
lua.push_ref(lua.state, &self.table); lua.push_ref(&self.table);
lua.push_ref(lua.state, &next_key); lua.push_ref(&next_key);
match protect_lua_call(lua.state, 2, ffi::LUA_MULTRET, |state| { match protect_lua_closure(lua.state, 2, ffi::LUA_MULTRET, |state| {
if ffi::lua_next(state, -2) == 0 { ffi::lua_next(state, -2) != 0
0
} else {
1
}
}) { }) {
Ok(0) => { Ok(false) => None,
ffi::lua_pop(lua.state, 1); Ok(true) => {
None
}
Ok(_) => {
ffi::lua_pushvalue(lua.state, -2); ffi::lua_pushvalue(lua.state, -2);
let key = lua.pop_value(lua.state); let key = lua.pop_value();
let value = lua.pop_value(lua.state); let value = lua.pop_value();
self.next_key = Some(lua.pop_ref(lua.state)); self.next_key = Some(lua.pop_ref());
ffi::lua_pop(lua.state, 1);
Some((|| { Some((|| {
let key = K::from_lua(key, lua)?; let key = K::from_lua(key, lua)?;
@ -411,15 +416,13 @@ where
stack_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 5); check_stack(lua.state, 5);
lua.push_ref(lua.state, &self.table); lua.push_ref(&self.table);
match protect_lua_call(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index)) match protect_lua_closure(lua.state, 1, 1, |state| {
{ ffi::lua_geti(state, -1, index)
Ok(ffi::LUA_TNIL) => { }) {
ffi::lua_pop(lua.state, 1); Ok(ffi::LUA_TNIL) => None,
None
}
Ok(_) => { Ok(_) => {
let value = lua.pop_value(lua.state); let value = lua.pop_value();
self.index = Some(index + 1); self.index = Some(index + 1);
Some(V::from_lua(value, lua)) Some(V::from_lua(value, lua))
} }

View File

@ -744,3 +744,38 @@ fn too_many_binds() {
.is_err() .is_err()
); );
} }
#[test]
fn large_args() {
let lua = Lua::new();
let globals = lua.globals();
globals
.set(
"c",
lua.create_function(|_, args: Variadic<usize>| {
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::<Variadic<usize>>())
.unwrap(),
4950
);
}

View File

@ -1,8 +1,8 @@
use std::os::raw::c_int; use std::os::raw::c_int;
use ffi; use ffi;
use error::*; use error::{Error, Result};
use util::*; use util::{check_stack, check_stack_err, error_traceback, pop_error, stack_guard};
use types::LuaRef; use types::LuaRef;
use value::{FromLuaMulti, MultiValue, ToLuaMulti}; use value::{FromLuaMulti, MultiValue, ToLuaMulti};
@ -78,10 +78,10 @@ impl<'lua> Thread<'lua> {
{ {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 1); 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 thread_state = ffi::lua_tothread(lua.state, -1);
let status = ffi::lua_status(thread_state); let status = ffi::lua_status(thread_state);
@ -93,11 +93,13 @@ impl<'lua> Thread<'lua> {
let args = args.to_lua_multi(lua)?; let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int; let nargs = args.len() as c_int;
check_stack_err(lua.state, nargs)?;
check_stack_err(thread_state, nargs + 1)?; check_stack_err(thread_state, nargs + 1)?;
for arg in args { 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); let ret = ffi::lua_resume(thread_state, lua.state, nargs);
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD { 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 nresults = ffi::lua_gettop(thread_state);
let mut results = MultiValue::new(); 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 { for _ in 0..nresults {
results.push_front(lua.pop_value(thread_state)); results.push_front(lua.pop_value());
} }
R::from_lua_multi(results, lua) R::from_lua_multi(results, lua)
}) })
@ -123,7 +127,7 @@ impl<'lua> Thread<'lua> {
stack_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 1); 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 thread_state = ffi::lua_tothread(lua.state, -1);
ffi::lua_pop(lua.state, 1); ffi::lua_pop(lua.state, 1);

View File

@ -1,4 +1,4 @@
use std::fmt; use std::{fmt, mem, ptr};
use std::os::raw::{c_int, c_void}; use std::os::raw::{c_int, c_void};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -16,6 +16,9 @@ pub type Number = ffi::lua_Number;
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct LightUserData(pub *mut c_void); pub struct LightUserData(pub *mut c_void);
pub(crate) type Callback<'lua, 'a> =
Box<Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>;
/// An auto generated key into the Lua registry. /// 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 /// 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 struct RegistryKey {
pub(crate) registry_id: c_int, pub(crate) registry_id: c_int,
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>, pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
pub(crate) drop_unref: bool,
} }
impl Drop for RegistryKey { impl Drop for RegistryKey {
fn drop(&mut self) { fn drop(&mut self) {
if self.drop_unref { if let Some(list) = self.unref_list.lock().unwrap().as_mut() {
if let Some(list) = self.unref_list.lock().unwrap().as_mut() { list.push(self.registry_id);
list.push(self.registry_id);
}
} }
} }
} }
pub(crate) type Callback<'lua, 'a> = impl RegistryKey {
Box<Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>; // 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(crate) struct LuaRef<'lua> {
pub lua: &'lua Lua, pub(crate) lua: &'lua Lua,
pub registry_id: c_int, pub(crate) ref_type: RefType,
pub drop_unref: bool,
} }
impl<'lua> fmt::Debug for LuaRef<'lua> { impl<'lua> fmt::Debug for LuaRef<'lua> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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> { impl<'lua> Clone for LuaRef<'lua> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
if self.drop_unref { self.lua.clone_ref(self)
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,
}
}
} }
} }
impl<'lua> Drop for LuaRef<'lua> { impl<'lua> Drop for LuaRef<'lua> {
fn drop(&mut self) { fn drop(&mut self) {
if self.drop_unref { self.lua.drop_ref(self)
unsafe {
ffi::luaL_unref(self.lua.state, ffi::LUA_REGISTRYINDEX, self.registry_id);
}
}
} }
} }

View File

@ -4,8 +4,8 @@ use std::collections::HashMap;
use std::string::String as StdString; use std::string::String as StdString;
use ffi; use ffi;
use error::*; use error::{Error, Result};
use util::*; use util::{check_stack, get_userdata, stack_guard};
use types::{Callback, LuaRef}; use types::{Callback, LuaRef};
use value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti}; use value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
use lua::Lua; use lua::Lua;
@ -415,10 +415,10 @@ impl<'lua> AnyUserData<'lua> {
{ {
unsafe { unsafe {
let lua = self.0.lua; let lua = self.0.lua;
stack_err_guard(lua.state, move || { stack_guard(lua.state, move || {
check_stack(lua.state, 3); check_stack(lua.state, 3);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
rlua_assert!( rlua_assert!(
ffi::lua_getmetatable(lua.state, -1) != 0, ffi::lua_getmetatable(lua.state, -1) != 0,
@ -432,11 +432,9 @@ impl<'lua> AnyUserData<'lua> {
); );
if ffi::lua_rawequal(lua.state, -1, -2) == 0 { if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
ffi::lua_pop(lua.state, 3);
Err(Error::UserDataTypeMismatch) Err(Error::UserDataTypeMismatch)
} else { } else {
let res = func(&*get_userdata::<RefCell<T>>(lua.state, -3)); let res = func(&*get_userdata::<RefCell<T>>(lua.state, -3));
ffi::lua_pop(lua.state, 3);
res res
} }
}) })
@ -451,12 +449,11 @@ impl<'lua> AnyUserData<'lua> {
pub fn set_user_value<V: ToLua<'lua>>(&self, v: V) -> Result<()> { pub fn set_user_value<V: ToLua<'lua>>(&self, v: V) -> Result<()> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 2); check_stack(lua.state, 2);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
lua.push_value(lua.state, v.to_lua(lua)?); lua.push_value(v.to_lua(lua)?);
ffi::lua_setuservalue(lua.state, -2); ffi::lua_setuservalue(lua.state, -2);
ffi::lua_pop(lua.state, 1);
Ok(()) Ok(())
}) })
} }
@ -468,12 +465,11 @@ impl<'lua> AnyUserData<'lua> {
pub fn get_user_value<V: FromLua<'lua>>(&self) -> Result<V> { pub fn get_user_value<V: FromLua<'lua>>(&self) -> Result<V> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 3); check_stack(lua.state, 3);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
ffi::lua_getuservalue(lua.state, -1); ffi::lua_getuservalue(lua.state, -1);
let res = V::from_lua(lua.pop_value(lua.state), lua)?; let res = V::from_lua(lua.pop_value(), lua)?;
ffi::lua_pop(lua.state, 1);
Ok(res) Ok(res)
}) })
} }

View File

@ -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 pub struct StackGuard {
// restored on panic. 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<F, R>(state: *mut ffi::lua_State, op: F) -> R pub unsafe fn stack_guard<F, R>(state: *mut ffi::lua_State, op: F) -> R
where where
F: FnOnce() -> R, F: FnOnce() -> R,
{ {
let begin = ffi::lua_gettop(state); let _stack_guard = StackGuard::new(state);
op()
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
} }
// Run an operation on a lua_State and automatically clean up the stack on error. Takes the // Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
// lua_State and an operation to run. If the operation results in success, then the stack is // Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
// inspected to make sure there is not a stack leak, and otherwise this is a logic error and will // limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is
// panic. If the operation results in an error, or if the operation panics, the stack is shrunk to // always LUA_MULTRET. Internally uses 2 extra stack spaces, and does not call checkstack.
// the value before the call. // Provided function must *never* panic.
pub unsafe fn stack_err_guard<F, R>(state: *mut ffi::lua_State, op: F) -> Result<R> pub unsafe fn protect_lua(
where state: *mut ffi::lua_State,
F: FnOnce() -> Result<R>, nargs: c_int,
{ f: unsafe extern "C" fn(*mut ffi::lua_State) -> c_int,
let begin = ffi::lua_gettop(state); ) -> Result<()> {
let stack_start = ffi::lua_gettop(state) - nargs;
let res = match catch_unwind(AssertUnwindSafe(op)) { ffi::lua_pushcfunction(state, error_traceback);
Ok(r) => r, ffi::lua_pushcfunction(state, f);
Err(p) => { if nargs > 0 {
let top = ffi::lua_gettop(state); ffi::lua_rotate(state, stack_start + 1, 2);
if top > begin { }
ffi::lua_settop(state, begin);
} let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start + 1);
resume_unwind(p); ffi::lua_remove(state, stack_start + 1);
}
}; if ret == ffi::LUA_OK {
Ok(())
let top = ffi::lua_gettop(state); } else {
if res.is_ok() { Err(pop_error(state, ret))
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);
}
} }
res
} }
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way. // 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 // 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 // not call checkstack. Provided function must *not* panic, and since it will generally be
// lonjmping, should not contain any values that implement Drop. // lonjmping, should not contain any values that implement Drop.
pub unsafe fn protect_lua_call<F, R>( pub unsafe fn protect_lua_closure<F, R>(
state: *mut ffi::lua_State, state: *mut ffi::lua_State,
nargs: c_int, nargs: c_int,
nresults: c_int, nresults: c_int,
@ -115,7 +112,7 @@ where
R: Copy, R: Copy,
{ {
struct Params<F, R> { struct Params<F, R> {
function: *const F, function: F,
result: R, result: R,
nresults: c_int, nresults: c_int,
} }
@ -127,7 +124,7 @@ where
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>; let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
(*params).result = (*(*params).function)(state); (*params).result = ((*params).function)(state);
if (*params).nresults == ffi::LUA_MULTRET { if (*params).nresults == ffi::LUA_MULTRET {
ffi::lua_gettop(state) ffi::lua_gettop(state)
@ -140,10 +137,12 @@ where
ffi::lua_pushcfunction(state, error_traceback); ffi::lua_pushcfunction(state, error_traceback);
ffi::lua_pushcfunction(state, do_call::<F, R>); ffi::lua_pushcfunction(state, do_call::<F, R>);
ffi::lua_rotate(state, stack_start + 1, 2); if nargs > 0 {
ffi::lua_rotate(state, stack_start + 1, 2);
}
let mut params = Params { let mut params = Params {
function: &f, function: f,
result: mem::uninitialized(), result: mem::uninitialized(),
nresults, 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" "pop_error called with non-error return code"
); );
if let Some(err) = pop_wrapped_error(state) { if let Some(err) = get_wrapped_error(state, -1).as_ref() {
err ffi::lua_pop(state, 1);
err.clone()
} else if is_wrapped_panic(state, -1) { } else if is_wrapped_panic(state, -1) {
let panic = get_userdata::<WrappedPanic>(state, -1); let panic = get_userdata::<WrappedPanic>(state, -1);
if let Some(p) = (*panic).0.take() { 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 => { ffi::LUA_ERRMEM => {
// This should be impossible, as we set the lua allocator to one that aborts // This should be impossible, as we set the lua allocator to one that aborts
// instead of failing. // instead of failing.
rlua_abort!("impossible Lua allocation error, aborting!") rlua_abort!("impossible Lua allocation error")
} }
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string), ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
_ => rlua_panic!("unrecognized lua error code"), _ => 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 // Internally uses 4 stack spaces, does not call checkstack
pub unsafe fn push_string(state: *mut ffi::lua_State, s: &str) -> Result<()> { 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()); ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
}) })
} }
// Internally uses 4 stack spaces, does not call checkstack // Internally uses 4 stack spaces, does not call checkstack
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> { pub unsafe fn push_userdata<T>(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::<T>()) as *mut T ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
})?; })?;
ptr::write(ud, 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 ffi::lua_checkstack(state, 2) == 0 {
// If we don't have enough stack space to even check the error type, do nothing // 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 { let traceback = if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 {
gc_guard(state, || { gc_guard(state, || {
ffi::luaL_traceback(state, state, ptr::null(), 0); 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() "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( push_wrapped_error(
state, state,
Error::CallbackError { 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 // Pushes a WrappedError::Error to the top of the stack. Uses two stack spaces and does not call
// lua_checkstack. // lua_checkstack.
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) { pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) {
@ -424,18 +418,26 @@ pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) {
ffi::lua_setmetatable(state, -2); 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 // Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it,
// WrappedError, returns None and does not pop anything. Uses 2 stack spaces and does not call // otherwise returns null. Uses 2 stack spaces and does not call lua_checkstack.
// lua_checkstack. pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *const Error {
pub unsafe fn pop_wrapped_error(state: *mut ffi::lua_State) -> Option<Error> { let userdata = ffi::lua_touserdata(state, index);
if !is_wrapped_error(state, -1) { if userdata.is_null() {
None 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::<WrappedError>(state, -1)).0
} else { } else {
let err = &*get_userdata::<WrappedError>(state, -1); ptr::null()
// We are assuming here that Error::clone() cannot panic.
let err = err.0.clone();
ffi::lua_pop(state, 1);
Some(err)
} }
} }
@ -466,9 +468,8 @@ pub unsafe fn init_error_metatables(state: *mut ffi::lua_State) {
ffi::luaL_checkstack(state, 2, ptr::null()); ffi::luaL_checkstack(state, 2, ptr::null());
callback_error(state, || { callback_error(state, || {
if is_wrapped_error(state, -1) { if let Some(error) = get_wrapped_error(state, -1).as_ref() {
let error = get_userdata::<WrappedError>(state, -1); let error_str = error.to_string();
let error_str = (*error).0.to_string();
gc_guard(state, || { gc_guard(state, || {
ffi::lua_pushlstring( ffi::lua_pushlstring(
state, state,
@ -587,24 +588,6 @@ unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box<Any + Send>)
ffi::lua_setmetatable(state, -2); 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 // Checks if the value at the given index is a WrappedPanic. Uses 2 stack spaces and does not call
// lua_checkstack. // lua_checkstack.
unsafe fn is_wrapped_panic(state: *mut ffi::lua_State, index: c_int) -> bool { unsafe fn is_wrapped_panic(state: *mut ffi::lua_State, index: c_int) -> bool {

View File

@ -1,9 +1,7 @@
use std::str; use std::{slice, str, vec};
use std::ops::{Deref, DerefMut}; use std::iter::{self, FromIterator};
use std::iter::FromIterator;
use std::collections::VecDeque;
use error::*; use error::{Error, Result};
use types::{Integer, LightUserData, Number}; use types::{Integer, LightUserData, Number};
use string::String; use string::String;
use table::Table; 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. /// Multiple Lua values used for both argument passing and also for multiple return values.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MultiValue<'lua>(VecDeque<Value<'lua>>); pub struct MultiValue<'lua>(Vec<Value<'lua>>);
impl<'lua> MultiValue<'lua> { impl<'lua> MultiValue<'lua> {
/// Creates an empty `MultiValue` containing no values. /// Creates an empty `MultiValue` containing no values.
pub fn new() -> MultiValue<'lua> { pub fn new() -> MultiValue<'lua> {
MultiValue(VecDeque::new()) MultiValue(Vec::new())
} }
} }
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> { impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self { fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
MultiValue(VecDeque::from_iter(iter)) MultiValue::from_vec(Vec::from_iter(iter))
} }
} }
impl<'lua> IntoIterator for MultiValue<'lua> { impl<'lua> IntoIterator for MultiValue<'lua> {
type Item = Value<'lua>; type Item = Value<'lua>;
type IntoIter = <VecDeque<Value<'lua>> as IntoIterator>::IntoIter; type IntoIter = iter::Rev<vec::IntoIter<Value<'lua>>>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
self.0.into_iter() self.0.into_iter().rev()
} }
} }
impl<'lua> Deref for MultiValue<'lua> { impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
type Target = VecDeque<Value<'lua>>; type Item = &'a Value<'lua>;
type IntoIter = iter::Rev<slice::Iter<'a, Value<'lua>>>;
fn deref(&self) -> &Self::Target { fn into_iter(self) -> Self::IntoIter {
&self.0 (&self.0).into_iter().rev()
} }
} }
impl<'lua> DerefMut for MultiValue<'lua> { impl<'lua> MultiValue<'lua> {
fn deref_mut(&mut self) -> &mut Self::Target { pub fn from_vec(mut v: Vec<Value<'lua>>) -> MultiValue<'lua> {
&mut self.0 v.reverse();
MultiValue(v)
}
pub fn into_vec(self) -> Vec<Value<'lua>> {
let mut v = self.0;
v.reverse();
v
}
pub fn from_vec_rev(v: Vec<Value<'lua>>) -> MultiValue<'lua> {
MultiValue(v)
}
pub fn into_vec_rev(self) -> Vec<Value<'lua>> {
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<Value<'lua>> {
self.0.pop()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
self.0.iter().rev()
} }
} }