Merge branch 'bench'
This commit is contained in:
commit
95633ce915
|
@ -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
|
|
@ -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);
|
4
build.rs
4
build.rs
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
512
src/lua.rs
512
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::sync::{Arc, Mutex};
|
||||||
use std::cell::RefCell;
|
use std::cell::{Cell, RefCell};
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::any::{Any, TypeId};
|
use std::any::{Any, TypeId};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
|
use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||||
|
|
||||||
use libc;
|
use libc;
|
||||||
|
|
||||||
use ffi;
|
use ffi;
|
||||||
use error::*;
|
use error::{Error, Result};
|
||||||
use util::*;
|
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 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 string::String;
|
||||||
use table::Table;
|
use table::Table;
|
||||||
use function::Function;
|
use function::Function;
|
||||||
|
@ -23,8 +27,8 @@ use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
||||||
/// Top level Lua struct which holds the Lua state itself.
|
/// Top level Lua struct which holds the Lua state itself.
|
||||||
pub struct Lua {
|
pub struct Lua {
|
||||||
pub(crate) state: *mut ffi::lua_State,
|
pub(crate) state: *mut ffi::lua_State,
|
||||||
main_state: *mut ffi::lua_State,
|
|
||||||
ephemeral: bool,
|
ephemeral: bool,
|
||||||
|
ref_stack_slots: [Cell<usize>; REF_STACK_SIZE as usize],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructed by the [`Lua::scope`] method, allows temporarily passing to Lua userdata that is
|
/// 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
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
pub struct Scope<'scope> {
|
pub struct Scope<'scope> {
|
||||||
lua: &'scope Lua,
|
lua: &'scope Lua,
|
||||||
destructors: RefCell<Vec<Box<Fn(*mut ffi::lua_State) -> Box<Any>>>>,
|
destructors: RefCell<Vec<Box<Fn() -> Box<Any> + 'scope>>>,
|
||||||
// 'scope lifetime must be invariant
|
// 'scope lifetime must be invariant
|
||||||
_scope: PhantomData<&'scope mut &'scope ()>,
|
_scope: PhantomData<&'scope mut &'scope ()>,
|
||||||
}
|
}
|
||||||
|
@ -46,14 +50,25 @@ struct ExtraData {
|
||||||
registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const REF_STACK_SIZE: c_int = 16;
|
||||||
|
|
||||||
|
static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||||
|
|
||||||
unsafe impl Send for Lua {}
|
unsafe impl Send for Lua {}
|
||||||
|
|
||||||
|
impl UnwindSafe for Lua {}
|
||||||
|
impl RefUnwindSafe for Lua {}
|
||||||
|
|
||||||
impl Drop for Lua {
|
impl Drop for Lua {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
if !self.ephemeral {
|
if !self.ephemeral {
|
||||||
let top = ffi::lua_gettop(self.state);
|
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);
|
let extra_data = *(ffi::lua_getextraspace(self.state) as *mut *mut ExtraData);
|
||||||
*(*extra_data).registry_unref_list.lock().unwrap() = None;
|
*(*extra_data).registry_unref_list.lock().unwrap() = None;
|
||||||
|
@ -87,7 +102,7 @@ impl Lua {
|
||||||
/// Equivalent to Lua's `load` function.
|
/// Equivalent to Lua's `load` function.
|
||||||
pub fn load(&self, source: &str, name: Option<&str>) -> Result<Function> {
|
pub fn load(&self, source: &str, name: Option<&str>) -> Result<Function> {
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_err_guard(self.state, || {
|
stack_guard(self.state, || {
|
||||||
check_stack(self.state, 1);
|
check_stack(self.state, 1);
|
||||||
|
|
||||||
match if let Some(name) = name {
|
match if let Some(name) = name {
|
||||||
|
@ -111,7 +126,7 @@ impl Lua {
|
||||||
ptr::null(),
|
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)),
|
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.
|
/// Pass a `&str` slice to Lua, creating and returning an interned Lua string.
|
||||||
pub fn create_string(&self, s: &str) -> Result<String> {
|
pub fn create_string(&self, s: &str) -> Result<String> {
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_err_guard(self.state, || {
|
stack_guard(self.state, || {
|
||||||
check_stack(self.state, 4);
|
check_stack(self.state, 4);
|
||||||
push_string(self.state, s)?;
|
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.
|
/// Creates and returns a new table.
|
||||||
pub fn create_table(&self) -> Result<Table> {
|
pub fn create_table(&self) -> Result<Table> {
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_err_guard(self.state, || {
|
stack_guard(self.state, || {
|
||||||
check_stack(self.state, 4);
|
check_stack(self.state, 3);
|
||||||
protect_lua_call(self.state, 0, 1, |state| {
|
unsafe extern "C" fn new_table(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_newtable(state);
|
ffi::lua_newtable(state);
|
||||||
})?;
|
1
|
||||||
Ok(Table(self.pop_ref(self.state)))
|
}
|
||||||
|
protect_lua(self.state, 0, new_table)?;
|
||||||
|
Ok(Table(self.pop_ref()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,20 +198,24 @@ impl Lua {
|
||||||
I: IntoIterator<Item = (K, V)>,
|
I: IntoIterator<Item = (K, V)>,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_err_guard(self.state, || {
|
stack_guard(self.state, || {
|
||||||
check_stack(self.state, 6);
|
check_stack(self.state, 5);
|
||||||
protect_lua_call(self.state, 0, 1, |state| {
|
unsafe extern "C" fn new_table(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_newtable(state);
|
ffi::lua_newtable(state);
|
||||||
})?;
|
1
|
||||||
|
}
|
||||||
|
protect_lua(self.state, 0, new_table)?;
|
||||||
|
|
||||||
for (k, v) in cont {
|
for (k, v) in cont {
|
||||||
self.push_value(self.state, k.to_lua(self)?);
|
self.push_value(k.to_lua(self)?);
|
||||||
self.push_value(self.state, v.to_lua(self)?);
|
self.push_value(v.to_lua(self)?);
|
||||||
protect_lua_call(self.state, 3, 1, |state| {
|
unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_rawset(state, -3);
|
ffi::lua_rawset(state, -3);
|
||||||
})?;
|
1
|
||||||
}
|
}
|
||||||
Ok(Table(self.pop_ref(self.state)))
|
protect_lua(self.state, 3, raw_set)?;
|
||||||
|
}
|
||||||
|
Ok(Table(self.pop_ref()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,14 +322,15 @@ impl Lua {
|
||||||
/// Equivalent to `coroutine.create`.
|
/// Equivalent to `coroutine.create`.
|
||||||
pub fn create_thread<'lua>(&'lua self, func: Function<'lua>) -> Result<Thread<'lua>> {
|
pub fn create_thread<'lua>(&'lua self, func: Function<'lua>) -> Result<Thread<'lua>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_err_guard(self.state, move || {
|
stack_guard(self.state, move || {
|
||||||
check_stack(self.state, 2);
|
check_stack(self.state, 2);
|
||||||
|
|
||||||
let thread_state =
|
let thread_state =
|
||||||
protect_lua_call(self.state, 0, 1, |state| ffi::lua_newthread(state))?;
|
protect_lua_closure(self.state, 0, 1, |state| ffi::lua_newthread(state))?;
|
||||||
self.push_ref(thread_state, &func.0);
|
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 || {
|
stack_guard(self.state, move || {
|
||||||
check_stack(self.state, 2);
|
check_stack(self.state, 2);
|
||||||
ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
|
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 {
|
match v {
|
||||||
Value::String(s) => Ok(s),
|
Value::String(s) => Ok(s),
|
||||||
v => unsafe {
|
v => unsafe {
|
||||||
stack_err_guard(self.state, || {
|
stack_guard(self.state, || {
|
||||||
check_stack(self.state, 4);
|
check_stack(self.state, 4);
|
||||||
let ty = v.type_name();
|
let ty = v.type_name();
|
||||||
self.push_value(self.state, v);
|
self.push_value(v);
|
||||||
let s =
|
let s = protect_lua_closure(self.state, 1, 1, |state| {
|
||||||
protect_lua_call(self.state, 1, 1, |state| ffi::lua_tostring(state, -1))?;
|
ffi::lua_tostring(state, -1)
|
||||||
|
})?;
|
||||||
if s.is_null() {
|
if s.is_null() {
|
||||||
ffi::lua_pop(self.state, 1);
|
|
||||||
Err(Error::FromLuaConversionError {
|
Err(Error::FromLuaConversionError {
|
||||||
from: ty,
|
from: ty,
|
||||||
to: "String",
|
to: "String",
|
||||||
message: Some("expected string or number".to_string()),
|
message: Some("expected string or number".to_string()),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Ok(String(self.pop_ref(self.state)))
|
Ok(String(self.pop_ref()))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -402,10 +424,9 @@ impl Lua {
|
||||||
stack_guard(self.state, || {
|
stack_guard(self.state, || {
|
||||||
check_stack(self.state, 2);
|
check_stack(self.state, 2);
|
||||||
let ty = v.type_name();
|
let ty = v.type_name();
|
||||||
self.push_value(self.state, v);
|
self.push_value(v);
|
||||||
let mut isint = 0;
|
let mut isint = 0;
|
||||||
let i = ffi::lua_tointegerx(self.state, -1, &mut isint);
|
let i = ffi::lua_tointegerx(self.state, -1, &mut isint);
|
||||||
ffi::lua_pop(self.state, 1);
|
|
||||||
if isint == 0 {
|
if isint == 0 {
|
||||||
Err(Error::FromLuaConversionError {
|
Err(Error::FromLuaConversionError {
|
||||||
from: ty,
|
from: ty,
|
||||||
|
@ -431,10 +452,9 @@ impl Lua {
|
||||||
stack_guard(self.state, || {
|
stack_guard(self.state, || {
|
||||||
check_stack(self.state, 2);
|
check_stack(self.state, 2);
|
||||||
let ty = v.type_name();
|
let ty = v.type_name();
|
||||||
self.push_value(self.state, v);
|
self.push_value(v);
|
||||||
let mut isnum = 0;
|
let mut isnum = 0;
|
||||||
let n = ffi::lua_tonumberx(self.state, -1, &mut isnum);
|
let n = ffi::lua_tonumberx(self.state, -1, &mut isnum);
|
||||||
ffi::lua_pop(self.state, 1);
|
|
||||||
if isnum == 0 {
|
if isnum == 0 {
|
||||||
Err(Error::FromLuaConversionError {
|
Err(Error::FromLuaConversionError {
|
||||||
from: ty,
|
from: ty,
|
||||||
|
@ -482,15 +502,17 @@ impl Lua {
|
||||||
t: T,
|
t: T,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_err_guard(self.state, || {
|
stack_guard(self.state, || {
|
||||||
check_stack(self.state, 5);
|
check_stack(self.state, 5);
|
||||||
|
|
||||||
push_string(self.state, name)?;
|
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);
|
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
|
/// [`set_named_registry_value`]: #method.set_named_registry_value
|
||||||
pub fn named_registry_value<'lua, T: FromLua<'lua>>(&'lua self, name: &str) -> Result<T> {
|
pub fn named_registry_value<'lua, T: FromLua<'lua>>(&'lua self, name: &str) -> Result<T> {
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_err_guard(self.state, || {
|
stack_guard(self.state, || {
|
||||||
check_stack(self.state, 4);
|
check_stack(self.state, 4);
|
||||||
|
|
||||||
push_string(self.state, name)?;
|
push_string(self.state, name)?;
|
||||||
protect_lua_call(self.state, 1, 1, |state| {
|
unsafe extern "C" fn get_registry(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX)
|
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, || {
|
stack_guard(self.state, || {
|
||||||
check_stack(self.state, 2);
|
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, || {
|
let registry_id = gc_guard(self.state, || {
|
||||||
ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)
|
ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)
|
||||||
});
|
});
|
||||||
|
@ -542,7 +566,6 @@ impl Lua {
|
||||||
Ok(RegistryKey {
|
Ok(RegistryKey {
|
||||||
registry_id,
|
registry_id,
|
||||||
unref_list: (*self.extra()).registry_unref_list.clone(),
|
unref_list: (*self.extra()).registry_unref_list.clone(),
|
||||||
drop_unref: true,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -560,14 +583,14 @@ impl Lua {
|
||||||
return Err(Error::MismatchedRegistryKey);
|
return Err(Error::MismatchedRegistryKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
stack_err_guard(self.state, || {
|
stack_guard(self.state, || {
|
||||||
check_stack(self.state, 2);
|
check_stack(self.state, 2);
|
||||||
ffi::lua_rawgeti(
|
ffi::lua_rawgeti(
|
||||||
self.state,
|
self.state,
|
||||||
ffi::LUA_REGISTRYINDEX,
|
ffi::LUA_REGISTRYINDEX,
|
||||||
key.registry_id as ffi::lua_Integer,
|
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
|
/// [`create_registry_value`]: #method.create_registry_value
|
||||||
/// [`expire_registry_values`]: #method.expire_registry_values
|
/// [`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 {
|
unsafe {
|
||||||
if !Arc::ptr_eq(&key.unref_list, &(*self.extra()).registry_unref_list) {
|
if !Arc::ptr_eq(&key.unref_list, &(*self.extra()).registry_unref_list) {
|
||||||
return Err(Error::MismatchedRegistryKey);
|
return Err(Error::MismatchedRegistryKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, key.registry_id);
|
ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, key.take());
|
||||||
key.drop_unref = false;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -620,120 +642,130 @@ impl Lua {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses 2 stack spaces, does not call checkstack
|
// Uses 2 stack spaces, does not call checkstack
|
||||||
pub(crate) unsafe fn push_value(&self, state: *mut ffi::lua_State, value: Value) {
|
pub(crate) unsafe fn push_value(&self, value: Value) {
|
||||||
match value {
|
match value {
|
||||||
Value::Nil => {
|
Value::Nil => {
|
||||||
ffi::lua_pushnil(state);
|
ffi::lua_pushnil(self.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Boolean(b) => {
|
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) => {
|
Value::LightUserData(ud) => {
|
||||||
ffi::lua_pushlightuserdata(state, ud.0);
|
ffi::lua_pushlightuserdata(self.state, ud.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Integer(i) => {
|
Value::Integer(i) => {
|
||||||
ffi::lua_pushinteger(state, i);
|
ffi::lua_pushinteger(self.state, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Number(n) => {
|
Value::Number(n) => {
|
||||||
ffi::lua_pushnumber(state, n);
|
ffi::lua_pushnumber(self.state, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::String(s) => {
|
Value::String(s) => {
|
||||||
self.push_ref(state, &s.0);
|
self.push_ref(&s.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Table(t) => {
|
Value::Table(t) => {
|
||||||
self.push_ref(state, &t.0);
|
self.push_ref(&t.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Function(f) => {
|
Value::Function(f) => {
|
||||||
self.push_ref(state, &f.0);
|
self.push_ref(&f.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Thread(t) => {
|
Value::Thread(t) => {
|
||||||
self.push_ref(state, &t.0);
|
self.push_ref(&t.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::UserData(ud) => {
|
Value::UserData(ud) => {
|
||||||
self.push_ref(state, &ud.0);
|
self.push_ref(&ud.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Error(e) => {
|
Value::Error(e) => {
|
||||||
push_wrapped_error(state, e);
|
push_wrapped_error(self.state, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses 2 stack spaces, does not call checkstack
|
// Uses 2 stack spaces, does not call checkstack
|
||||||
pub(crate) unsafe fn pop_value(&self, state: *mut ffi::lua_State) -> Value {
|
pub(crate) unsafe fn pop_value(&self) -> Value {
|
||||||
match ffi::lua_type(state, -1) {
|
match ffi::lua_type(self.state, -1) {
|
||||||
ffi::LUA_TNIL => {
|
ffi::LUA_TNIL => {
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(self.state, 1);
|
||||||
Nil
|
Nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::LUA_TBOOLEAN => {
|
ffi::LUA_TBOOLEAN => {
|
||||||
let b = Value::Boolean(ffi::lua_toboolean(state, -1) != 0);
|
let b = Value::Boolean(ffi::lua_toboolean(self.state, -1) != 0);
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(self.state, 1);
|
||||||
b
|
b
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::LUA_TLIGHTUSERDATA => {
|
ffi::LUA_TLIGHTUSERDATA => {
|
||||||
let ud = Value::LightUserData(LightUserData(ffi::lua_touserdata(state, -1)));
|
let ud = Value::LightUserData(LightUserData(ffi::lua_touserdata(self.state, -1)));
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(self.state, 1);
|
||||||
ud
|
ud
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::LUA_TNUMBER => if ffi::lua_isinteger(state, -1) != 0 {
|
ffi::LUA_TNUMBER => if ffi::lua_isinteger(self.state, -1) != 0 {
|
||||||
let i = Value::Integer(ffi::lua_tointeger(state, -1));
|
let i = Value::Integer(ffi::lua_tointeger(self.state, -1));
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(self.state, 1);
|
||||||
i
|
i
|
||||||
} else {
|
} else {
|
||||||
let n = Value::Number(ffi::lua_tonumber(state, -1));
|
let n = Value::Number(ffi::lua_tonumber(self.state, -1));
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(self.state, 1);
|
||||||
n
|
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 => {
|
ffi::LUA_TUSERDATA => {
|
||||||
// It should not be possible to interact with userdata types other than custom
|
// 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
|
// UserData types OR a WrappedError. WrappedPanic should never be able to be caught
|
||||||
// in lua, so it should never be here.
|
// 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)
|
Value::Error(err)
|
||||||
} else {
|
} 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"),
|
_ => unreachable!("internal error: LUA_TNONE in pop_value"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used 1 stack space, does not call checkstack
|
// 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!(
|
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"
|
"Lua instance passed Value created from a different Lua"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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(
|
ffi::lua_rawgeti(
|
||||||
state,
|
self.state,
|
||||||
ffi::LUA_REGISTRYINDEX,
|
ffi::LUA_REGISTRYINDEX,
|
||||||
lref.registry_id as ffi::lua_Integer,
|
registry_id as ffi::lua_Integer,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Pops the topmost element of the stack and stores a reference to it in the
|
// Pops the topmost element of the stack and stores a reference to it in the
|
||||||
// registry.
|
// registry.
|
||||||
|
@ -742,12 +774,91 @@ impl Lua {
|
||||||
// `LuaRef` is dropped.
|
// `LuaRef` is dropped.
|
||||||
//
|
//
|
||||||
// pop_ref uses 1 extra stack space and does not call checkstack
|
// 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 {
|
pub(crate) unsafe fn pop_ref(&self) -> LuaRef {
|
||||||
let registry_id = gc_guard(state, || ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX));
|
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 {
|
LuaRef {
|
||||||
lua: self,
|
lua: self,
|
||||||
|
ref_type: RefType::Nil,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LuaRef {
|
||||||
|
lua: self,
|
||||||
|
ref_type: RefType::Registry {
|
||||||
registry_id: registry_id,
|
registry_id: registry_id,
|
||||||
drop_unref: true,
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 || {
|
stack_guard(self.state, move || {
|
||||||
check_stack(self.state, 5);
|
|
||||||
|
|
||||||
if let Some(table_id) = (*self.extra()).registered_userdata.get(&TypeId::of::<T>()) {
|
if let Some(table_id) = (*self.extra()).registered_userdata.get(&TypeId::of::<T>()) {
|
||||||
return Ok(*table_id);
|
return Ok(*table_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_stack(self.state, 6);
|
||||||
|
|
||||||
let mut methods = UserDataMethods {
|
let mut methods = UserDataMethods {
|
||||||
methods: HashMap::new(),
|
methods: HashMap::new(),
|
||||||
meta_methods: HashMap::new(),
|
meta_methods: HashMap::new(),
|
||||||
|
@ -786,7 +897,7 @@ impl Lua {
|
||||||
};
|
};
|
||||||
T::add_methods(&mut methods);
|
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);
|
ffi::lua_newtable(state);
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -794,22 +905,19 @@ impl Lua {
|
||||||
|
|
||||||
if has_methods {
|
if has_methods {
|
||||||
push_string(self.state, "__index")?;
|
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);
|
ffi::lua_newtable(state);
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
for (k, m) in methods.methods {
|
for (k, m) in methods.methods {
|
||||||
push_string(self.state, &k)?;
|
push_string(self.state, &k)?;
|
||||||
self.push_value(
|
self.push_value(Value::Function(self.create_callback_function(m)?));
|
||||||
self.state,
|
protect_lua_closure(self.state, 3, 1, |state| {
|
||||||
Value::Function(self.create_callback_function(m)?),
|
|
||||||
);
|
|
||||||
protect_lua_call(self.state, 3, 1, |state| {
|
|
||||||
ffi::lua_rawset(state, -3);
|
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);
|
ffi::lua_rawset(state, -3);
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
@ -819,15 +927,12 @@ impl Lua {
|
||||||
push_string(self.state, "__index")?;
|
push_string(self.state, "__index")?;
|
||||||
ffi::lua_pushvalue(self.state, -1);
|
ffi::lua_pushvalue(self.state, -1);
|
||||||
ffi::lua_gettable(self.state, -3);
|
ffi::lua_gettable(self.state, -3);
|
||||||
self.push_value(
|
self.push_value(Value::Function(self.create_callback_function(m)?));
|
||||||
self.state,
|
protect_lua_closure(self.state, 2, 1, |state| {
|
||||||
Value::Function(self.create_callback_function(m)?),
|
|
||||||
);
|
|
||||||
protect_lua_call(self.state, 2, 1, |state| {
|
|
||||||
ffi::lua_pushcclosure(state, meta_index_impl, 2);
|
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);
|
ffi::lua_rawset(state, -3);
|
||||||
})?;
|
})?;
|
||||||
} else {
|
} else {
|
||||||
|
@ -857,11 +962,8 @@ impl Lua {
|
||||||
MetaMethod::ToString => "__tostring",
|
MetaMethod::ToString => "__tostring",
|
||||||
};
|
};
|
||||||
push_string(self.state, name)?;
|
push_string(self.state, name)?;
|
||||||
self.push_value(
|
self.push_value(Value::Function(self.create_callback_function(m)?));
|
||||||
self.state,
|
protect_lua_closure(self.state, 3, 1, |state| {
|
||||||
Value::Function(self.create_callback_function(m)?),
|
|
||||||
);
|
|
||||||
protect_lua_call(self.state, 3, 1, |state| {
|
|
||||||
ffi::lua_rawset(state, -3);
|
ffi::lua_rawset(state, -3);
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
@ -869,13 +971,13 @@ impl Lua {
|
||||||
|
|
||||||
push_string(self.state, "__gc")?;
|
push_string(self.state, "__gc")?;
|
||||||
ffi::lua_pushcfunction(self.state, userdata_destructor::<RefCell<T>>);
|
ffi::lua_pushcfunction(self.state, userdata_destructor::<RefCell<T>>);
|
||||||
protect_lua_call(self.state, 3, 1, |state| {
|
protect_lua_closure(self.state, 3, 1, |state| {
|
||||||
ffi::lua_rawset(state, -3);
|
ffi::lua_rawset(state, -3);
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
push_string(self.state, "__metatable")?;
|
push_string(self.state, "__metatable")?;
|
||||||
ffi::lua_pushboolean(self.state, 0);
|
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);
|
ffi::lua_rawset(state, -3);
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -918,7 +1020,7 @@ impl Lua {
|
||||||
|
|
||||||
// Ignores or `unwrap()`s 'm' errors, because this is assuming that nothing in the lua
|
// Ignores or `unwrap()`s 'm' errors, because this is assuming that nothing in the lua
|
||||||
// standard library will have a `__gc` metamethod error.
|
// standard library will have a `__gc` metamethod error.
|
||||||
stack_guard(state, || {
|
|
||||||
// Do not open the debug library, it can be used to cause unsafety.
|
// 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!("_G"), ffi::luaopen_base, 1);
|
||||||
ffi::luaL_requiref(state, cstr!("coroutine"), ffi::luaopen_coroutine, 1);
|
ffi::luaL_requiref(state, cstr!("coroutine"), ffi::luaopen_coroutine, 1);
|
||||||
|
@ -978,12 +1080,15 @@ impl Lua {
|
||||||
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
||||||
}));
|
}));
|
||||||
*(ffi::lua_getextraspace(state) as *mut *mut ExtraData) = extra_data;
|
*(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 {
|
Lua {
|
||||||
state,
|
state,
|
||||||
main_state: state,
|
|
||||||
ephemeral: false,
|
ephemeral: false,
|
||||||
|
ref_stack_slots: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -993,24 +1098,18 @@ impl Lua {
|
||||||
) -> Result<Function<'lua>> {
|
) -> Result<Function<'lua>> {
|
||||||
unsafe extern "C" fn callback_call_impl(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn callback_call_impl(state: *mut ffi::lua_State) -> c_int {
|
||||||
callback_error(state, || {
|
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 {
|
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL {
|
||||||
return Err(Error::CallbackDestructed);
|
return Err(Error::CallbackDestructed);
|
||||||
}
|
}
|
||||||
|
|
||||||
let func = get_userdata::<Callback>(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 func = get_userdata::<Callback>(state, ffi::lua_upvalueindex(1));
|
||||||
let mut args = MultiValue::new();
|
|
||||||
check_stack(state, 2);
|
|
||||||
for _ in 0..nargs {
|
|
||||||
args.push_front(lua.pop_value(state));
|
|
||||||
}
|
|
||||||
|
|
||||||
let results = (*func)(&lua, args)?;
|
let results = (*func)(&lua, args)?;
|
||||||
let nresults = results.len() as c_int;
|
let nresults = results.len() as c_int;
|
||||||
|
@ -1018,7 +1117,7 @@ impl Lua {
|
||||||
check_stack_err(state, nresults)?;
|
check_stack_err(state, nresults)?;
|
||||||
|
|
||||||
for r in results {
|
for r in results {
|
||||||
lua.push_value(state, r);
|
lua.push_value(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(nresults)
|
Ok(nresults)
|
||||||
|
@ -1026,8 +1125,8 @@ impl Lua {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_err_guard(self.state, move || {
|
stack_guard(self.state, move || {
|
||||||
check_stack(self.state, 2);
|
check_stack(self.state, 4);
|
||||||
|
|
||||||
push_userdata::<Callback>(self.state, func)?;
|
push_userdata::<Callback>(self.state, func)?;
|
||||||
|
|
||||||
|
@ -1038,11 +1137,11 @@ impl Lua {
|
||||||
ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX);
|
ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX);
|
||||||
ffi::lua_setmetatable(self.state, -2);
|
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);
|
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,
|
T: UserData,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_err_guard(self.state, move || {
|
stack_guard(self.state, move || {
|
||||||
check_stack(self.state, 3);
|
check_stack(self.state, 4);
|
||||||
|
|
||||||
push_userdata::<RefCell<T>>(self.state, RefCell::new(data))?;
|
push_userdata::<RefCell<T>>(self.state, RefCell::new(data))?;
|
||||||
|
|
||||||
|
@ -1065,13 +1164,104 @@ impl Lua {
|
||||||
|
|
||||||
ffi::lua_setmetatable(self.state, -2);
|
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 {
|
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)
|
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
});
|
});
|
||||||
let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'callback, 'static>>(f);
|
let f = mem::transmute::<Callback<'callback, 'scope>, 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 mut destructors = self.destructors.borrow_mut();
|
||||||
let registry_id = f.0.registry_id;
|
let f_destruct = f.0.clone();
|
||||||
destructors.push(Box::new(move |state| {
|
destructors.push(Box::new(move || {
|
||||||
|
let state = f_destruct.lua.state;
|
||||||
stack_guard(state, || {
|
stack_guard(state, || {
|
||||||
check_stack(state, 2);
|
check_stack(state, 2);
|
||||||
|
f_destruct.lua.push_ref(&f_destruct);
|
||||||
ffi::lua_rawgeti(
|
|
||||||
state,
|
|
||||||
ffi::LUA_REGISTRYINDEX,
|
|
||||||
registry_id as ffi::lua_Integer,
|
|
||||||
);
|
|
||||||
ffi::luaL_unref(state, ffi::LUA_REGISTRYINDEX, registry_id);
|
|
||||||
|
|
||||||
ffi::lua_getupvalue(state, -1, 1);
|
ffi::lua_getupvalue(state, -1, 1);
|
||||||
let ud = take_userdata::<Callback>(state);
|
let ud = take_userdata::<Callback>(state);
|
||||||
|
@ -1159,19 +1343,14 @@ impl<'scope> Scope<'scope> {
|
||||||
T: UserData,
|
T: UserData,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut u = self.lua.do_create_userdata(data)?;
|
let u = self.lua.do_create_userdata(data)?;
|
||||||
u.0.drop_unref = false;
|
|
||||||
let mut destructors = self.destructors.borrow_mut();
|
let mut destructors = self.destructors.borrow_mut();
|
||||||
let registry_id = u.0.registry_id;
|
let u_destruct = u.0.clone();
|
||||||
destructors.push(Box::new(move |state| {
|
destructors.push(Box::new(move || {
|
||||||
|
let state = u_destruct.lua.state;
|
||||||
stack_guard(state, || {
|
stack_guard(state, || {
|
||||||
check_stack(state, 1);
|
check_stack(state, 1);
|
||||||
ffi::lua_rawgeti(
|
u_destruct.lua.push_ref(&u_destruct);
|
||||||
state,
|
|
||||||
ffi::LUA_REGISTRYINDEX,
|
|
||||||
registry_id as ffi::lua_Integer,
|
|
||||||
);
|
|
||||||
ffi::luaL_unref(state, ffi::LUA_REGISTRYINDEX, registry_id);
|
|
||||||
Box::new(take_userdata::<RefCell<T>>(state))
|
Box::new(take_userdata::<RefCell<T>>(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
|
// 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.
|
// can be sure that all of the userdata in Lua is actually invalidated.
|
||||||
|
|
||||||
let state = self.lua.state;
|
|
||||||
let to_drop = self.destructors
|
let to_drop = self.destructors
|
||||||
.get_mut()
|
.get_mut()
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|destructor| destructor(state))
|
.map(|destructor| destructor())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
drop(to_drop);
|
drop(to_drop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0;
|
|
||||||
|
|
14
src/multi.rs
14
src/multi.rs
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
139
src/table.rs
139
src/table.rs
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
52
src/types.rs
52
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::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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type Callback<'lua, 'a> =
|
#[derive(Debug)]
|
||||||
Box<Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
199
src/util.rs
199
src/util.rs
|
@ -27,75 +27,72 @@ 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
|
// 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;
|
||||||
|
|
||||||
|
ffi::lua_pushcfunction(state, error_traceback);
|
||||||
|
ffi::lua_pushcfunction(state, f);
|
||||||
|
if nargs > 0 {
|
||||||
|
ffi::lua_rotate(state, stack_start + 1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run an operation on a lua_State and automatically clean up the stack on error. Takes the
|
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start + 1);
|
||||||
// lua_State and an operation to run. If the operation results in success, then the stack is
|
ffi::lua_remove(state, stack_start + 1);
|
||||||
// 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<F, R>(state: *mut ffi::lua_State, op: F) -> Result<R>
|
|
||||||
where
|
|
||||||
F: FnOnce() -> Result<R>,
|
|
||||||
{
|
|
||||||
let begin = ffi::lua_gettop(state);
|
|
||||||
|
|
||||||
let res = match catch_unwind(AssertUnwindSafe(op)) {
|
if ret == ffi::LUA_OK {
|
||||||
Ok(r) => r,
|
Ok(())
|
||||||
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 {
|
} else {
|
||||||
if top > begin {
|
Err(pop_error(state, ret))
|
||||||
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.
|
||||||
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
|
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
|
||||||
|
@ -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>);
|
||||||
|
if nargs > 0 {
|
||||||
ffi::lua_rotate(state, stack_start + 1, 2);
|
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 {
|
||||||
|
|
68
src/value.rs
68
src/value.rs
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue