use std::fmt;
use std::ops::{Deref, DerefMut};
use std::iter::FromIterator;
use std::cell::{RefCell, Ref, RefMut};
use std::ptr;
use std::mem;
use std::ffi::{CStr, CString};
use std::any::TypeId;
use std::marker::PhantomData;
use std::collections::{HashMap, VecDeque};
use std::collections::hash_map::Entry as HashMapEntry;
use std::os::raw::{c_char, c_int, c_void};
use ffi;
use error::*;
use util::*;
#[derive(Debug, Clone)]
pub enum LuaValue<'lua> {
Nil,
Boolean(bool),
LightUserData(LightUserData),
Integer(LuaInteger),
Number(LuaNumber),
String(LuaString<'lua>),
Table(LuaTable<'lua>),
Function(LuaFunction<'lua>),
Thread(LuaThread<'lua>),
UserData(LuaUserData<'lua>),
Error(LuaError),
}
pub use self::LuaValue::Nil as LuaNil;
pub trait ToLua<'a> {
fn to_lua(self, lua: &'a Lua) -> LuaResult<LuaValue<'a>>;
}
pub trait FromLua<'a>: Sized {
fn from_lua(lua_value: LuaValue<'a>, lua: &'a Lua) -> LuaResult<Self>;
}
#[derive(Debug, Clone)]
pub struct LuaMultiValue<'lua>(VecDeque<LuaValue<'lua>>);
impl<'lua> LuaMultiValue<'lua> {
pub fn new() -> LuaMultiValue<'lua> {
LuaMultiValue(VecDeque::new())
}
}
impl<'lua> FromIterator<LuaValue<'lua>> for LuaMultiValue<'lua> {
fn from_iter<I: IntoIterator<Item = LuaValue<'lua>>>(iter: I) -> Self {
LuaMultiValue(VecDeque::from_iter(iter))
}
}
impl<'lua> IntoIterator for LuaMultiValue<'lua> {
type Item = LuaValue<'lua>;
type IntoIter = <VecDeque<LuaValue<'lua>> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'lua> Deref for LuaMultiValue<'lua> {
type Target = VecDeque<LuaValue<'lua>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'lua> DerefMut for LuaMultiValue<'lua> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub trait ToLuaMulti<'a> {
fn to_lua_multi(self, lua: &'a Lua) -> LuaResult<LuaMultiValue<'a>>;
}
pub trait FromLuaMulti<'a>: Sized {
fn from_lua_multi(values: LuaMultiValue<'a>, lua: &'a Lua) -> LuaResult<Self>;
}
type LuaCallback = Box<
for<'lua> FnMut(&'lua Lua, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>,
>;
struct LuaRef<'lua> {
lua: &'lua Lua,
registry_id: c_int,
}
impl<'lua> fmt::Debug for LuaRef<'lua> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LuaRef({})", self.registry_id)
}
}
impl<'lua> Clone for LuaRef<'lua> {
fn clone(&self) -> Self {
unsafe {
self.lua.push_ref(self.lua.state, self);
self.lua.pop_ref(self.lua.state)
}
}
}
impl<'lua> Drop for LuaRef<'lua> {
fn drop(&mut self) {
unsafe {
ffi::luaL_unref(self.lua.state, ffi::LUA_REGISTRYINDEX, self.registry_id);
}
}
}
pub type LuaInteger = ffi::lua_Integer;
pub type LuaNumber = ffi::lua_Number;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct LightUserData(pub *mut c_void);
#[derive(Clone, Debug)]
pub struct LuaString<'lua>(LuaRef<'lua>);
impl<'lua> LuaString<'lua> {
pub fn to_str(&self) -> LuaResult<&str> {
let lua = self.0.lua;
unsafe {
stack_err_guard(lua.state, 0, || {
check_stack(lua.state, 1);
lua.push_ref(lua.state, &self.0);
assert_eq!(ffi::lua_type(lua.state, -1), ffi::LUA_TSTRING);
let s = CStr::from_ptr(ffi::lua_tostring(lua.state, -1))
.to_str()
.map_err(|e| LuaError::FromLuaConversionError(e.to_string()))?;
ffi::lua_pop(lua.state, 1);
Ok(s)
})
}
}
}
#[derive(Clone, Debug)]
pub struct LuaTable<'lua>(LuaRef<'lua>);
impl<'lua> LuaTable<'lua> {
pub fn set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> LuaResult<()> {
let lua = self.0.lua;
let key = key.to_lua(lua)?;
let value = value.to_lua(lua)?;
unsafe {
check_stack(lua.state, 3);
lua.push_ref(lua.state, &self.0);
lua.push_value(lua.state, key);
lua.push_value(lua.state, value);
error_guard(lua.state, 3, 0, |state| {
ffi::lua_settable(state, -3);
Ok(())
})
}
}
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> LuaResult<V> {
let lua = self.0.lua;
let key = key.to_lua(lua)?;
unsafe {
check_stack(lua.state, 2);
lua.push_ref(lua.state, &self.0);
lua.push_value(lua.state, key.to_lua(lua)?);
let res = error_guard(lua.state, 2, 0, |state| {
ffi::lua_gettable(state, -2);
let res = lua.pop_value(state);
ffi::lua_pop(state, 1);
Ok(res)
})?;
V::from_lua(res, lua)
}
}
pub fn contains_key<K: ToLua<'lua>>(&self, key: K) -> LuaResult<bool> {
let lua = self.0.lua;
let key = key.to_lua(lua)?;
unsafe {
check_stack(lua.state, 2);
lua.push_ref(lua.state, &self.0);
lua.push_value(lua.state, key);
error_guard(lua.state, 2, 0, |state| {
ffi::lua_gettable(state, -2);
let has = ffi::lua_isnil(state, -1) == 0;
ffi::lua_pop(state, 2);
Ok(has)
})
}
}
pub fn raw_set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> LuaResult<()> {
let lua = self.0.lua;
unsafe {
stack_err_guard(lua.state, 0, || {
check_stack(lua.state, 3);
lua.push_ref(lua.state, &self.0);
lua.push_value(lua.state, key.to_lua(lua)?);
lua.push_value(lua.state, value.to_lua(lua)?);
ffi::lua_rawset(lua.state, -3);
ffi::lua_pop(lua.state, 1);
Ok(())
})
}
}
pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> LuaResult<V> {
let lua = self.0.lua;
unsafe {
stack_err_guard(lua.state, 0, || {
check_stack(lua.state, 2);
lua.push_ref(lua.state, &self.0);
lua.push_value(lua.state, key.to_lua(lua)?);
ffi::lua_gettable(lua.state, -2);
let res = V::from_lua(lua.pop_value(lua.state), lua)?;
ffi::lua_pop(lua.state, 1);
Ok(res)
})
}
}
pub fn len(&self) -> LuaResult<LuaInteger> {
let lua = self.0.lua;
unsafe {
error_guard(lua.state, 0, 0, |state| {
check_stack(state, 1);
lua.push_ref(state, &self.0);
Ok(ffi::luaL_len(state, -1))
})
}
}
pub fn raw_len(&self) -> LuaInteger {
let lua = self.0.lua;
unsafe {
stack_guard(lua.state, 0, || {
check_stack(lua.state, 1);
lua.push_ref(lua.state, &self.0);
let len = ffi::lua_rawlen(lua.state, -1);
ffi::lua_pop(lua.state, 1);
len as LuaInteger
})
}
}
pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> LuaTablePairs<'lua, K, V> {
let next_key = Some(LuaRef {
lua: self.0.lua,
registry_id: ffi::LUA_REFNIL,
});
LuaTablePairs {
table: self.0,
next_key,
_phantom: PhantomData,
}
}
pub fn sequence_values<V: FromLua<'lua>>(self) -> LuaTableSequence<'lua, V> {
LuaTableSequence {
table: self.0,
index: Some(1),
_phantom: PhantomData,
}
}
}
pub struct LuaTablePairs<'lua, K, V> {
table: LuaRef<'lua>,
next_key: Option<LuaRef<'lua>>,
_phantom: PhantomData<(K, V)>,
}
impl<'lua, K, V> Iterator for LuaTablePairs<'lua, K, V>
where
K: FromLua<'lua>,
V: FromLua<'lua>,
{
type Item = LuaResult<(K, V)>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(next_key) = self.next_key.take() {
let lua = self.table.lua;
unsafe {
check_stack(lua.state, 4);
lua.push_ref(lua.state, &self.table);
lua.push_ref(lua.state, &next_key);
match error_guard(lua.state, 2, 0, |state| if ffi::lua_next(state, -2) != 0 {
ffi::lua_pushvalue(state, -2);
let key = lua.pop_value(state);
let value = lua.pop_value(state);
let next_key = lua.pop_ref(lua.state);
ffi::lua_pop(lua.state, 1);
Ok(Some((key, value, next_key)))
} else {
ffi::lua_pop(lua.state, 1);
Ok(None)
}) {
Ok(Some((key, value, next_key))) => {
self.next_key = Some(next_key);
Some((|| {
let key = K::from_lua(key, lua)?;
let value = V::from_lua(value, lua)?;
Ok((key, value))
})())
}
Ok(None) => None,
Err(e) => Some(Err(e)),
}
}
} else {
None
}
}
}
pub struct LuaTableSequence<'lua, V> {
table: LuaRef<'lua>,
index: Option<LuaInteger>,
_phantom: PhantomData<V>,
}
impl<'lua, V> Iterator for LuaTableSequence<'lua, V>
where
V: FromLua<'lua>,
{
type Item = LuaResult<V>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(index) = self.index.take() {
let lua = self.table.lua;
unsafe {
check_stack(lua.state, 2);
lua.push_ref(lua.state, &self.table);
match error_guard(
lua.state,
1,
0,
|state| if ffi::lua_geti(state, -1, index) != ffi::LUA_TNIL {
let value = lua.pop_value(state);
ffi::lua_pop(state, 1);
Ok(Some(value))
} else {
ffi::lua_pop(state, 2);
Ok(None)
},
) {
Ok(Some(r)) => {
self.index = Some(index + 1);
Some(V::from_lua(r, lua))
}
Ok(None) => None,
Err(e) => Some(Err(e)),
}
}
} else {
None
}
}
}
#[derive(Clone, Debug)]
pub struct LuaFunction<'lua>(LuaRef<'lua>);
impl<'lua> LuaFunction<'lua> {
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> LuaResult<R> {
let lua = self.0.lua;
unsafe {
stack_err_guard(lua.state, 0, || {
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
check_stack(lua.state, nargs + 3);
let stack_start = ffi::lua_gettop(lua.state);
lua.push_ref(lua.state, &self.0);
for arg in args {
lua.push_value(lua.state, arg);
}
handle_error(
lua.state,
pcall_with_traceback(lua.state, nargs, ffi::LUA_MULTRET),
)?;
let nresults = ffi::lua_gettop(lua.state) - stack_start;
let mut results = LuaMultiValue::new();
for _ in 0..nresults {
results.push_front(lua.pop_value(lua.state));
}
R::from_lua_multi(results, lua)
})
}
}
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> LuaResult<LuaFunction<'lua>> {
unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int {
let nargs = ffi::lua_gettop(state);
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(2)) as c_int;
check_stack(state, nbinds + 1);
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
ffi::lua_insert(state, 1);
for i in 0..nbinds {
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 3));
ffi::lua_insert(state, i + 2);
}
ffi::lua_call(state, nargs + nbinds, ffi::LUA_MULTRET);
ffi::lua_gettop(state)
}
let lua = self.0.lua;
unsafe {
stack_err_guard(lua.state, 0, || {
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
check_stack(lua.state, nargs + 2);
lua.push_ref(lua.state, &self.0);
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
for arg in args {
lua.push_value(lua.state, arg);
}
ffi::lua_pushcclosure(lua.state, bind_call_impl, nargs + 2);
Ok(LuaFunction(lua.pop_ref(lua.state)))
})
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum LuaThreadStatus {
Dead,
Active,
Error,
}
#[derive(Clone, Debug)]
pub struct LuaThread<'lua>(LuaRef<'lua>);
impl<'lua> LuaThread<'lua> {
pub fn resume<A, R>(&self, args: A) -> LuaResult<R>
where
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let lua = self.0.lua;
unsafe {
stack_err_guard(lua.state, 0, || {
check_stack(lua.state, 1);
lua.push_ref(lua.state, &self.0);
let thread_state = ffi::lua_tothread(lua.state, -1);
let status = ffi::lua_status(thread_state);
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
return Err(LuaError::CoroutineInactive);
}
ffi::lua_pop(lua.state, 1);
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
check_stack(thread_state, nargs);
for arg in args {
lua.push_value(thread_state, arg);
}
handle_error(
lua.state,
resume_with_traceback(thread_state, lua.state, nargs),
)?;
let nresults = ffi::lua_gettop(thread_state);
let mut results = LuaMultiValue::new();
for _ in 0..nresults {
results.push_front(lua.pop_value(thread_state));
}
R::from_lua_multi(results, lua)
})
}
}
pub fn status(&self) -> LuaThreadStatus {
let lua = self.0.lua;
unsafe {
stack_guard(lua.state, 0, || {
check_stack(lua.state, 1);
lua.push_ref(lua.state, &self.0);
let thread_state = ffi::lua_tothread(lua.state, -1);
ffi::lua_pop(lua.state, 1);
let status = ffi::lua_status(thread_state);
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
LuaThreadStatus::Error
} else if status == ffi::LUA_YIELD || ffi::lua_gettop(thread_state) > 0 {
LuaThreadStatus::Active
} else {
LuaThreadStatus::Dead
}
})
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum LuaMetaMethod {
Add,
Sub,
Mul,
Div,
Mod,
Pow,
Unm,
IDiv,
BAnd,
BOr,
BXor,
BNot,
Shl,
Shr,
Concat,
Len,
Eq,
Lt,
Le,
Index,
NewIndex,
Call,
ToString,
}
pub struct LuaUserDataMethods<T> {
methods: HashMap<String, LuaCallback>,
meta_methods: HashMap<LuaMetaMethod, LuaCallback>,
_type: PhantomData<T>,
}
impl<T: LuaUserDataType> LuaUserDataMethods<T> {
pub fn add_method<M>(&mut self, name: &str, method: M)
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
self.methods.insert(
name.to_owned(),
Self::box_method(method),
);
}
pub fn add_method_mut<M>(&mut self, name: &str, method: M)
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a mut T, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
self.methods.insert(
name.to_owned(),
Self::box_method_mut(method),
);
}
pub fn add_function<F>(&mut self, name: &str, function: F)
where F: 'static + for<'a, 'lua> FnMut(&'lua Lua, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
self.methods.insert(name.to_owned(), Box::new(function));
}
pub fn add_meta_method<M>(&mut self, meta: LuaMetaMethod, method: M)
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
self.meta_methods.insert(meta, Self::box_method(method));
}
pub fn add_meta_method_mut<M>(&mut self, meta: LuaMetaMethod, method: M)
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a mut T, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
self.meta_methods.insert(meta, Self::box_method_mut(method));
}
pub fn add_meta_function<F>(&mut self, meta: LuaMetaMethod, function: F)
where F: 'static + for<'a, 'lua> FnMut(&'lua Lua, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
self.meta_methods.insert(meta, Box::new(function));
}
fn box_method<M>(mut method: M) -> LuaCallback
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a T, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
Box::new(move |lua, mut args| if let Some(front) = args.pop_front() {
let userdata = LuaUserData::from_lua(front, lua)?;
let userdata = userdata.borrow::<T>()?;
method(lua, &userdata, args)
} else {
Err(LuaError::FromLuaConversionError(
"No userdata supplied as first argument to method"
.to_owned(),
))
})
}
fn box_method_mut<M>(mut method: M) -> LuaCallback
where M: 'static + for<'a, 'lua> FnMut(&'lua Lua, &'a mut T, LuaMultiValue<'lua>)
-> LuaResult<LuaMultiValue<'lua>>
{
Box::new(move |lua, mut args| if let Some(front) = args.pop_front() {
let userdata = LuaUserData::from_lua(front, lua)?;
let mut userdata = userdata.borrow_mut::<T>()?;
method(lua, &mut userdata, args)
} else {
Err(
LuaError::FromLuaConversionError(
"No userdata supplied as first argument to method".to_owned(),
).into(),
)
})
}
}
pub trait LuaUserDataType: 'static + Sized {
fn add_methods(_methods: &mut LuaUserDataMethods<Self>) {}
}
#[derive(Clone, Debug)]
pub struct LuaUserData<'lua>(LuaRef<'lua>);
impl<'lua> LuaUserData<'lua> {
pub fn is<T: LuaUserDataType>(&self) -> bool {
self.inspect(|_: &RefCell<T>| ()).is_some()
}
pub fn borrow<T: LuaUserDataType>(&self) -> LuaResult<Ref<T>> {
self.inspect(|cell| {
Ok(
cell.try_borrow().map_err(|_| LuaError::UserDataBorrowError)?,
)
}).ok_or(LuaError::UserDataTypeMismatch)?
}
pub fn borrow_mut<T: LuaUserDataType>(&self) -> LuaResult<RefMut<T>> {
self.inspect(|cell| {
Ok(cell.try_borrow_mut().map_err(
|_| LuaError::UserDataBorrowMutError,
)?)
}).ok_or(LuaError::UserDataTypeMismatch)?
}
fn inspect<'a, T, R, F>(&'a self, func: F) -> Option<R>
where
T: LuaUserDataType,
F: FnOnce(&'a RefCell<T>) -> R,
{
unsafe {
let lua = self.0.lua;
stack_guard(lua.state, 0, move || {
check_stack(lua.state, 3);
lua.push_ref(lua.state, &self.0);
let userdata = ffi::lua_touserdata(lua.state, -1);
lua_assert!(lua.state, !userdata.is_null());
lua_assert!(
lua.state,
ffi::lua_getmetatable(lua.state, -1) != 0,
"LuaUserData missing metatable"
);
ffi::lua_rawgeti(
lua.state,
ffi::LUA_REGISTRYINDEX,
lua.userdata_metatable::<T>() as ffi::lua_Integer,
);
if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
ffi::lua_pop(lua.state, 3);
None
} else {
let res = func(&*(userdata as *const RefCell<T>));
ffi::lua_pop(lua.state, 3);
Some(res)
}
})
}
}
}
pub struct Lua {
state: *mut ffi::lua_State,
main_state: *mut ffi::lua_State,
ephemeral: bool,
}
impl Drop for Lua {
fn drop(&mut self) {
unsafe {
if !self.ephemeral {
ffi::lua_close(self.state);
}
}
}
}
impl Lua {
pub fn new() -> Lua {
unsafe {
let state = ffi::luaL_newstate();
stack_guard(state, 0, || {
ffi::luaL_openlibs(state);
ffi::lua_pushlightuserdata(
state,
&LUA_USERDATA_REGISTRY_KEY as *const u8 as *mut c_void,
);
let registered_userdata = ffi::lua_newuserdata(
state,
mem::size_of::<RefCell<HashMap<TypeId, c_int>>>(),
) as *mut RefCell<HashMap<TypeId, c_int>>;
ptr::write(registered_userdata, RefCell::new(HashMap::new()));
ffi::lua_newtable(state);
push_string(state, "__gc");
ffi::lua_pushcfunction(state, destructor::<RefCell<HashMap<TypeId, c_int>>>);
ffi::lua_rawset(state, -3);
ffi::lua_setmetatable(state, -2);
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
ffi::lua_pushlightuserdata(
state,
&FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
);
ffi::lua_newtable(state);
push_string(state, "__gc");
ffi::lua_pushcfunction(state, destructor::<LuaCallback>);
ffi::lua_rawset(state, -3);
push_string(state, "__metatable");
ffi::lua_pushboolean(state, 0);
ffi::lua_rawset(state, -3);
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
push_string(state, "pcall");
ffi::lua_pushcfunction(state, safe_pcall);
ffi::lua_rawset(state, -3);
push_string(state, "xpcall");
ffi::lua_pushcfunction(state, safe_xpcall);
ffi::lua_rawset(state, -3);
ffi::lua_pop(state, 1);
});
Lua {
state,
main_state: state,
ephemeral: false,
}
}
}
pub fn load(&self, source: &str, name: Option<&str>) -> LuaResult<LuaFunction> {
unsafe {
stack_err_guard(self.state, 0, || {
handle_error(
self.state,
if let Some(name) = name {
let name = CString::new(name.to_owned()).map_err(|e| {
LuaError::ToLuaConversionError(e.to_string())
})?;
ffi::luaL_loadbuffer(
self.state,
source.as_ptr() as *const c_char,
source.len(),
name.as_ptr(),
)
} else {
ffi::luaL_loadbuffer(
self.state,
source.as_ptr() as *const c_char,
source.len(),
ptr::null(),
)
},
)?;
Ok(LuaFunction(self.pop_ref(self.state)))
})
}
}
pub fn exec<'lua, R: FromLuaMulti<'lua>>(
&'lua self,
source: &str,
name: Option<&str>,
) -> LuaResult<R> {
self.load(source, name)?.call(())
}
pub fn eval<'lua, R: FromLuaMulti<'lua>>(&'lua self, source: &str) -> LuaResult<R> {
unsafe {
stack_err_guard(self.state, 0, || {
let return_source = "return ".to_owned() + source;
let mut res = ffi::luaL_loadbuffer(
self.state,
return_source.as_ptr() as *const c_char,
return_source.len(),
ptr::null(),
);
if res == ffi::LUA_ERRSYNTAX {
ffi::lua_pop(self.state, 1);
res = ffi::luaL_loadbuffer(
self.state,
source.as_ptr() as *const c_char,
source.len(),
ptr::null(),
);
}
handle_error(self.state, res)?;
LuaFunction(self.pop_ref(self.state)).call(())
})
}
}
pub fn create_string(&self, s: &str) -> LuaString {
unsafe {
stack_guard(self.state, 0, || {
check_stack(self.state, 1);
ffi::lua_pushlstring(self.state, s.as_ptr() as *const c_char, s.len());
LuaString(self.pop_ref(self.state))
})
}
}
pub fn create_table(&self) -> LuaTable {
unsafe {
stack_guard(self.state, 0, || {
check_stack(self.state, 1);
ffi::lua_newtable(self.state);
LuaTable(self.pop_ref(self.state))
})
}
}
pub fn create_table_from<'lua, K, V, I>(&'lua self, cont: I) -> LuaResult<LuaTable>
where
K: ToLua<'lua>,
V: ToLua<'lua>,
I: IntoIterator<Item = (K, V)>,
{
unsafe {
stack_err_guard(self.state, 0, || {
check_stack(self.state, 3);
ffi::lua_newtable(self.state);
for (k, v) in cont {
self.push_value(self.state, k.to_lua(self)?);
self.push_value(self.state, v.to_lua(self)?);
ffi::lua_rawset(self.state, -3);
}
Ok(LuaTable(self.pop_ref(self.state)))
})
}
}
pub fn create_sequence_from<'lua, T, I>(&'lua self, cont: I) -> LuaResult<LuaTable>
where
T: ToLua<'lua>,
I: IntoIterator<Item = T>,
{
self.create_table_from(cont.into_iter().enumerate().map(|(k, v)| (k + 1, v)))
}
pub fn create_function<F>(&self, func: F) -> LuaFunction
where
F: 'static + for<'a> FnMut(&'a Lua, LuaMultiValue<'a>) -> LuaResult<LuaMultiValue<'a>>,
{
self.create_callback_function(Box::new(func))
}
pub fn create_thread<'lua>(&'lua self, func: LuaFunction<'lua>) -> LuaThread<'lua> {
unsafe {
stack_guard(self.state, 0, move || {
check_stack(self.state, 1);
let thread_state = ffi::lua_newthread(self.state);
self.push_ref(thread_state, &func.0);
LuaThread(self.pop_ref(self.state))
})
}
}
pub fn create_userdata<T>(&self, data: T) -> LuaUserData
where
T: LuaUserDataType,
{
unsafe {
stack_guard(self.state, 0, move || {
check_stack(self.state, 2);
let data = RefCell::new(data);
let data_userdata =
ffi::lua_newuserdata(self.state, mem::size_of::<RefCell<T>>()) as
*mut RefCell<T>;
ptr::write(data_userdata, data);
ffi::lua_rawgeti(
self.state,
ffi::LUA_REGISTRYINDEX,
self.userdata_metatable::<T>() as ffi::lua_Integer,
);
ffi::lua_setmetatable(self.state, -2);
LuaUserData(self.pop_ref(self.state))
})
}
}
pub fn globals(&self) -> LuaTable {
unsafe {
stack_guard(self.state, 0, move || {
check_stack(self.state, 1);
ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
LuaTable(self.pop_ref(self.state))
})
}
}
pub fn coerce_string<'lua>(&'lua self, v: LuaValue<'lua>) -> LuaResult<LuaString<'lua>> {
match v {
LuaValue::String(s) => Ok(s),
v => unsafe {
stack_guard(self.state, 0, || {
check_stack(self.state, 1);
self.push_value(self.state, v);
if ffi::lua_tostring(self.state, -1).is_null() {
ffi::lua_pop(self.state, 1);
Err(LuaError::FromLuaConversionError(
"cannot convert lua value to string".to_owned(),
))
} else {
Ok(LuaString(self.pop_ref(self.state)))
}
})
},
}
}
pub fn coerce_integer(&self, v: LuaValue) -> LuaResult<LuaInteger> {
match v {
LuaValue::Integer(i) => Ok(i),
v => unsafe {
stack_guard(self.state, 0, || {
check_stack(self.state, 1);
self.push_value(self.state, v);
let mut isint = 0;
let i = ffi::lua_tointegerx(self.state, -1, &mut isint);
ffi::lua_pop(self.state, 1);
if isint == 0 {
Err(LuaError::FromLuaConversionError(
"cannot convert lua value to integer".to_owned(),
))
} else {
Ok(i)
}
})
},
}
}
pub fn coerce_number(&self, v: LuaValue) -> LuaResult<LuaNumber> {
match v {
LuaValue::Number(n) => Ok(n),
v => unsafe {
stack_guard(self.state, 0, || {
check_stack(self.state, 1);
self.push_value(self.state, v);
let mut isnum = 0;
let n = ffi::lua_tonumberx(self.state, -1, &mut isnum);
ffi::lua_pop(self.state, 1);
if isnum == 0 {
Err(LuaError::FromLuaConversionError(
"cannot convert lua value to number".to_owned(),
))
} else {
Ok(n)
}
})
},
}
}
pub fn from<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> LuaResult<LuaValue<'lua>> {
t.to_lua(self)
}
pub fn to<'lua, T: FromLua<'lua>>(&'lua self, value: LuaValue<'lua>) -> LuaResult<T> {
T::from_lua(value, self)
}
pub fn pack<'lua, T: ToLuaMulti<'lua>>(&'lua self, t: T) -> LuaResult<LuaMultiValue<'lua>> {
t.to_lua_multi(self)
}
pub fn unpack<'lua, T: FromLuaMulti<'lua>>(
&'lua self,
value: LuaMultiValue<'lua>,
) -> LuaResult<T> {
T::from_lua_multi(value, self)
}
fn create_callback_function(&self, func: LuaCallback) -> LuaFunction {
unsafe extern "C" fn callback_call_impl(state: *mut ffi::lua_State) -> c_int {
callback_error(state, || {
let lua = Lua {
state: state,
main_state: main_state(state),
ephemeral: true,
};
let func = &mut *(ffi::lua_touserdata(state, ffi::lua_upvalueindex(1)) as
*mut LuaCallback);
let nargs = ffi::lua_gettop(state);
let mut args = LuaMultiValue::new();
for _ in 0..nargs {
args.push_front(lua.pop_value(state));
}
let results = func(&lua, args)?;
let nresults = results.len() as c_int;
for r in results {
lua.push_value(state, r);
}
Ok(nresults)
})
}
unsafe {
stack_guard(self.state, 0, move || {
check_stack(self.state, 2);
let func_userdata =
ffi::lua_newuserdata(self.state, mem::size_of::<LuaCallback>()) as
*mut LuaCallback;
ptr::write(func_userdata, func);
ffi::lua_pushlightuserdata(
self.state,
&FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
);
ffi::lua_gettable(self.state, ffi::LUA_REGISTRYINDEX);
ffi::lua_setmetatable(self.state, -2);
ffi::lua_pushcclosure(self.state, callback_call_impl, 1);
LuaFunction(self.pop_ref(self.state))
})
}
}
unsafe fn push_value(&self, state: *mut ffi::lua_State, value: LuaValue) {
match value {
LuaValue::Nil => {
ffi::lua_pushnil(state);
}
LuaValue::Boolean(b) => {
ffi::lua_pushboolean(state, if b { 1 } else { 0 });
}
LuaValue::LightUserData(ud) => {
ffi::lua_pushlightuserdata(state, ud.0);
}
LuaValue::Integer(i) => {
ffi::lua_pushinteger(state, i);
}
LuaValue::Number(n) => {
ffi::lua_pushnumber(state, n);
}
LuaValue::String(s) => {
self.push_ref(state, &s.0);
}
LuaValue::Table(t) => {
self.push_ref(state, &t.0);
}
LuaValue::Function(f) => {
self.push_ref(state, &f.0);
}
LuaValue::Thread(t) => {
self.push_ref(state, &t.0);
}
LuaValue::UserData(ud) => {
self.push_ref(state, &ud.0);
}
LuaValue::Error(e) => {
push_wrapped_error(state, e);
}
}
}
unsafe fn pop_value(&self, state: *mut ffi::lua_State) -> LuaValue {
match ffi::lua_type(state, -1) {
ffi::LUA_TNIL => {
ffi::lua_pop(state, 1);
LuaNil
}
ffi::LUA_TBOOLEAN => {
let b = LuaValue::Boolean(ffi::lua_toboolean(state, -1) != 0);
ffi::lua_pop(state, 1);
b
}
ffi::LUA_TLIGHTUSERDATA => {
let ud = LuaValue::LightUserData(LightUserData(ffi::lua_touserdata(state, -1)));
ffi::lua_pop(state, 1);
ud
}
ffi::LUA_TNUMBER => {
if ffi::lua_isinteger(state, -1) != 0 {
let i = LuaValue::Integer(ffi::lua_tointeger(state, -1));
ffi::lua_pop(state, 1);
i
} else {
let n = LuaValue::Number(ffi::lua_tonumber(state, -1));
ffi::lua_pop(state, 1);
n
}
}
ffi::LUA_TSTRING => LuaValue::String(LuaString(self.pop_ref(state))),
ffi::LUA_TTABLE => LuaValue::Table(LuaTable(self.pop_ref(state))),
ffi::LUA_TFUNCTION => LuaValue::Function(LuaFunction(self.pop_ref(state))),
ffi::LUA_TUSERDATA => {
if let Some(err) = pop_wrapped_error(state) {
LuaValue::Error(err)
} else {
LuaValue::UserData(LuaUserData(self.pop_ref(state)))
}
}
ffi::LUA_TTHREAD => LuaValue::Thread(LuaThread(self.pop_ref(state))),
_ => unreachable!("internal error: LUA_TNONE in pop_value"),
}
}
unsafe fn push_ref(&self, state: *mut ffi::lua_State, lref: &LuaRef) {
assert_eq!(
lref.lua.main_state,
self.main_state,
"Lua instance passed LuaValue created from a different Lua"
);
ffi::lua_rawgeti(
state,
ffi::LUA_REGISTRYINDEX,
lref.registry_id as ffi::lua_Integer,
);
}
unsafe fn pop_ref(&self, state: *mut ffi::lua_State) -> LuaRef {
let registry_id = ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
LuaRef {
lua: self,
registry_id: registry_id,
}
}
unsafe fn userdata_metatable<T: LuaUserDataType>(&self) -> c_int {
unsafe extern "C" fn meta_index_impl(state: *mut ffi::lua_State) -> c_int {
ffi::lua_pushvalue(state, -1);
ffi::lua_gettable(state, ffi::lua_upvalueindex(1));
if ffi::lua_isnil(state, -1) == 0 {
ffi::lua_insert(state, -3);
ffi::lua_pop(state, 2);
1
} else {
ffi::lua_pop(state, 1);
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(2));
ffi::lua_insert(state, -3);
ffi::lua_call(state, 2, 1);
1
}
}
stack_guard(self.state, 0, move || {
check_stack(self.state, 5);
ffi::lua_pushlightuserdata(
self.state,
&LUA_USERDATA_REGISTRY_KEY as *const u8 as *mut c_void,
);
ffi::lua_gettable(self.state, ffi::LUA_REGISTRYINDEX);
let registered_userdata = ffi::lua_touserdata(self.state, -1) as
*mut RefCell<HashMap<TypeId, c_int>>;
let mut map = (*registered_userdata).borrow_mut();
ffi::lua_pop(self.state, 1);
match map.entry(TypeId::of::<T>()) {
HashMapEntry::Occupied(entry) => *entry.get(),
HashMapEntry::Vacant(entry) => {
ffi::lua_newtable(self.state);
let mut methods = LuaUserDataMethods {
methods: HashMap::new(),
meta_methods: HashMap::new(),
_type: PhantomData,
};
T::add_methods(&mut methods);
let has_methods = !methods.methods.is_empty();
if has_methods {
push_string(self.state, "__index");
ffi::lua_newtable(self.state);
for (k, m) in methods.methods {
push_string(self.state, &k);
self.push_value(
self.state,
LuaValue::Function(self.create_callback_function(m)),
);
ffi::lua_rawset(self.state, -3);
}
ffi::lua_rawset(self.state, -3);
}
for (k, m) in methods.meta_methods {
if k == LuaMetaMethod::Index && has_methods {
push_string(self.state, "__index");
ffi::lua_pushvalue(self.state, -1);
ffi::lua_gettable(self.state, -3);
self.push_value(
self.state,
LuaValue::Function(self.create_callback_function(m)),
);
ffi::lua_pushcclosure(self.state, meta_index_impl, 2);
ffi::lua_rawset(self.state, -3);
} else {
let name = match k {
LuaMetaMethod::Add => "__add",
LuaMetaMethod::Sub => "__sub",
LuaMetaMethod::Mul => "__mul",
LuaMetaMethod::Div => "__div",
LuaMetaMethod::Mod => "__mod",
LuaMetaMethod::Pow => "__pow",
LuaMetaMethod::Unm => "__unm",
LuaMetaMethod::IDiv => "__idiv",
LuaMetaMethod::BAnd => "__band",
LuaMetaMethod::BOr => "__bor",
LuaMetaMethod::BXor => "__bxor",
LuaMetaMethod::BNot => "__bnot",
LuaMetaMethod::Shl => "__shl",
LuaMetaMethod::Shr => "__shr",
LuaMetaMethod::Concat => "__concat",
LuaMetaMethod::Len => "__len",
LuaMetaMethod::Eq => "__eq",
LuaMetaMethod::Lt => "__lt",
LuaMetaMethod::Le => "__le",
LuaMetaMethod::Index => "__index",
LuaMetaMethod::NewIndex => "__newIndex",
LuaMetaMethod::Call => "__call",
LuaMetaMethod::ToString => "__tostring",
};
push_string(self.state, name);
self.push_value(
self.state,
LuaValue::Function(self.create_callback_function(m)),
);
ffi::lua_rawset(self.state, -3);
}
}
push_string(self.state, "__gc");
ffi::lua_pushcfunction(self.state, destructor::<RefCell<T>>);
ffi::lua_rawset(self.state, -3);
push_string(self.state, "__metatable");
ffi::lua_pushboolean(self.state, 0);
ffi::lua_rawset(self.state, -3);
let id = ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX);
entry.insert(id);
id
}
}
})
}
}
static LUA_USERDATA_REGISTRY_KEY: u8 = 0;
static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0;