use ffi;
use libc;
use AnyLuaValue;
use AsLua;
use AsMutLua;
use LuaContext;
use LuaRead;
use Push;
use PushGuard;
use PushOne;
use Void;
use std::marker::PhantomData;
use std::fmt::Display;
use std::mem;
use std::ptr;
macro_rules! impl_function {
($name:ident, $($p:ident),*) => (
#[inline]
pub fn $name<Z, R $(, $p)*>(f: Z) -> Function<Z, ($($p,)*), R>
where Z: FnMut($($p),*) -> R
{
Function {
function: f,
marker: PhantomData,
}
}
)
}
impl_function!(function0,);
impl_function!(function1, A);
impl_function!(function2, A, B);
impl_function!(function3, A, B, C);
impl_function!(function4, A, B, C, D);
impl_function!(function5, A, B, C, D, E);
impl_function!(function6, A, B, C, D, E, F);
impl_function!(function7, A, B, C, D, E, F, G);
impl_function!(function8, A, B, C, D, E, F, G, H);
impl_function!(function9, A, B, C, D, E, F, G, H, I);
impl_function!(function10, A, B, C, D, E, F, G, H, I, J);
#[derive(Debug)]
pub struct Function<F, P, R> {
function: F,
marker: PhantomData<(P, R)>,
}
pub trait FunctionExt<P> {
type Output;
fn call_mut(&mut self, params: P) -> Self::Output;
}
#[inline]
extern "C" fn closure_destructor_wrapper<T>(lua: *mut ffi::lua_State) -> libc::c_int {
unsafe {
let obj = ffi::lua_touserdata(lua, -1);
ptr::drop_in_place((obj as *mut u8) as *mut T);
0
}
}
macro_rules! impl_function_ext {
() => (
impl<Z, R> FunctionExt<()> for Function<Z, (), R> where Z: FnMut() -> R {
type Output = R;
#[allow(non_snake_case)]
#[inline]
fn call_mut(&mut self, _: ()) -> Self::Output {
(self.function)()
}
}
impl<'lua, L, Z, R> Push<L> for Function<Z, (), R>
where L: AsMutLua<'lua>,
Z: 'lua + FnMut() -> R,
R: for<'a> Push<&'a mut InsideCallback> + 'static
{
type Err = Void;
#[inline]
fn push_to_lua(self, mut lua: L) -> Result<PushGuard<L>, (Void, L)> {
unsafe {
let lua_data = ffi::lua_newuserdata(lua.as_mut_lua().0,
mem::size_of::<Z>() as libc::size_t);
let lua_data: *mut Z = mem::transmute(lua_data);
ptr::write(lua_data, self.function);
let lua_raw = lua.as_mut_lua();
ffi::lua_newtable(lua.as_mut_lua().0);
{
match "__gc".push_to_lua(&mut lua) {
Ok(p) => p.forget(),
Err(_) => unreachable!(),
};
ffi::lua_pushcfunction(lua.as_mut_lua().0, closure_destructor_wrapper::<Z>);
ffi::lua_settable(lua.as_mut_lua().0, -3);
}
ffi::lua_setmetatable(lua_raw.0, -2);
let wrapper: extern fn(*mut ffi::lua_State) -> libc::c_int = wrapper::<Self, _, R>;
ffi::lua_pushcclosure(lua.as_mut_lua().0, wrapper, 1);
let raw_lua = lua.as_lua();
Ok(PushGuard { lua: lua, size: 1, raw_lua: raw_lua })
}
}
}
impl<'lua, L, Z, R> PushOne<L> for Function<Z, (), R>
where L: AsMutLua<'lua>,
Z: 'lua + FnMut() -> R,
R: for<'a> Push<&'a mut InsideCallback> + 'static
{
}
);
($($p:ident),+) => (
impl<Z, R $(,$p)*> FunctionExt<($($p,)*)> for Function<Z, ($($p,)*), R> where Z: FnMut($($p),*) -> R {
type Output = R;
#[allow(non_snake_case)]
#[inline]
fn call_mut(&mut self, params: ($($p,)*)) -> Self::Output {
let ($($p,)*) = params;
(self.function)($($p),*)
}
}
impl<'lua, L, Z, R $(,$p: 'static)+> Push<L> for Function<Z, ($($p,)*), R>
where L: AsMutLua<'lua>,
Z: 'lua + FnMut($($p),*) -> R,
($($p,)*): for<'p> LuaRead<&'p mut InsideCallback>,
R: for<'a> Push<&'a mut InsideCallback> + 'static
{
type Err = Void;
#[inline]
fn push_to_lua(self, mut lua: L) -> Result<PushGuard<L>, (Void, L)> {
unsafe {
let lua_data = ffi::lua_newuserdata(lua.as_mut_lua().0,
mem::size_of::<Z>() as libc::size_t);
let lua_data: *mut Z = mem::transmute(lua_data);
ptr::write(lua_data, self.function);
let lua_raw = lua.as_mut_lua();
ffi::lua_newtable(lua.as_mut_lua().0);
{
match "__gc".push_to_lua(&mut lua) {
Ok(p) => p.forget_internal(),
Err(_) => unreachable!(),
};
ffi::lua_pushcfunction(lua.as_mut_lua().0, closure_destructor_wrapper::<Z>);
ffi::lua_settable(lua.as_mut_lua().0, -3);
}
ffi::lua_setmetatable(lua_raw.0, -2);
let wrapper: extern fn(*mut ffi::lua_State) -> libc::c_int = wrapper::<Self, _, R>;
ffi::lua_pushcclosure(lua.as_mut_lua().0, wrapper, 1);
let raw_lua = lua.as_lua();
Ok(PushGuard { lua: lua, size: 1, raw_lua: raw_lua })
}
}
}
impl<'lua, L, Z, R $(,$p: 'static)+> PushOne<L> for Function<Z, ($($p,)*), R>
where L: AsMutLua<'lua>,
Z: 'lua + FnMut($($p),*) -> R,
($($p,)*): for<'p> LuaRead<&'p mut InsideCallback>,
R: for<'a> Push<&'a mut InsideCallback> + 'static
{
}
)
}
impl_function_ext!();
impl_function_ext!(A);
impl_function_ext!(A, B);
impl_function_ext!(A, B, C);
impl_function_ext!(A, B, C, D);
impl_function_ext!(A, B, C, D, E);
impl_function_ext!(A, B, C, D, E, F);
impl_function_ext!(A, B, C, D, E, F, G);
impl_function_ext!(A, B, C, D, E, F, G, H);
impl_function_ext!(A, B, C, D, E, F, G, H, I);
impl_function_ext!(A, B, C, D, E, F, G, H, I, J);
#[derive(Debug)]
pub struct InsideCallback {
lua: LuaContext,
}
unsafe impl<'a, 'lua> AsLua<'lua> for &'a InsideCallback {
#[inline]
fn as_lua(&self) -> LuaContext {
self.lua
}
}
unsafe impl<'a, 'lua> AsLua<'lua> for &'a mut InsideCallback {
#[inline]
fn as_lua(&self) -> LuaContext {
self.lua
}
}
unsafe impl<'a, 'lua> AsMutLua<'lua> for &'a mut InsideCallback {
#[inline]
fn as_mut_lua(&mut self) -> LuaContext {
self.lua
}
}
impl<'a, T, E, P> Push<&'a mut InsideCallback> for Result<T, E>
where T: Push<&'a mut InsideCallback, Err = P> + for<'b> Push<&'b mut &'a mut InsideCallback, Err = P>,
E: Display
{
type Err = P;
#[inline]
fn push_to_lua(self, lua: &'a mut InsideCallback) -> Result<PushGuard<&'a mut InsideCallback>, (P, &'a mut InsideCallback)> {
match self {
Ok(val) => val.push_to_lua(lua),
Err(val) => {
Ok((AnyLuaValue::LuaNil, format!("{}", val)).push_no_err(lua))
}
}
}
}
impl<'a, T, E, P> PushOne<&'a mut InsideCallback> for Result<T, E>
where T: PushOne<&'a mut InsideCallback, Err = P> + for<'b> PushOne<&'b mut &'a mut InsideCallback, Err = P>,
E: Display
{
}
#[inline]
extern "C" fn wrapper<T, P, R>(lua: *mut ffi::lua_State) -> libc::c_int
where T: FunctionExt<P, Output = R>,
P: for<'p> LuaRead<&'p mut InsideCallback> + 'static,
R: for<'p> Push<&'p mut InsideCallback>
{
let data_raw = unsafe { ffi::lua_touserdata(lua, ffi::lua_upvalueindex(1)) };
let data: &mut T = unsafe { mem::transmute(data_raw) };
let mut tmp_lua = InsideCallback { lua: LuaContext(lua) };
let arguments_count = unsafe { ffi::lua_gettop(lua) } as i32;
let args = match LuaRead::lua_read_at_position(&mut tmp_lua, -arguments_count as libc::c_int) { Err(_) => {
let err_msg = format!("wrong parameter types for callback function");
match err_msg.push_to_lua(&mut tmp_lua) {
Ok(p) => p.forget_internal(),
Err(_) => unreachable!(),
};
unsafe {
ffi::lua_error(lua);
}
unreachable!()
}
Ok(a) => a,
};
let ret_value = data.call_mut(args);
let nb = match ret_value.push_to_lua(&mut tmp_lua) {
Ok(p) => p.forget_internal(),
Err(_) => panic!(), };
nb as libc::c_int
}
#[cfg(test)]
mod tests {
use Lua;
use LuaError;
use function0;
use function1;
use function2;
use std::sync::Arc;
#[test]
fn simple_function() {
let mut lua = Lua::new();
fn ret5() -> i32 {
5
};
lua.set("ret5", function0(ret5));
let val: i32 = lua.execute("return ret5()").unwrap();
assert_eq!(val, 5);
}
#[test]
fn one_argument() {
let mut lua = Lua::new();
fn plus_one(val: i32) -> i32 {
val + 1
};
lua.set("plus_one", function1(plus_one));
let val: i32 = lua.execute("return plus_one(3)").unwrap();
assert_eq!(val, 4);
}
#[test]
fn two_arguments() {
let mut lua = Lua::new();
fn add(val1: i32, val2: i32) -> i32 {
val1 + val2
};
lua.set("add", function2(add));
let val: i32 = lua.execute("return add(3, 7)").unwrap();
assert_eq!(val, 10);
}
#[test]
fn wrong_arguments_types() {
let mut lua = Lua::new();
fn add(val1: i32, val2: i32) -> i32 {
val1 + val2
};
lua.set("add", function2(add));
match lua.execute::<i32>("return add(3, \"hello\")") {
Err(LuaError::ExecutionError(_)) => (),
_ => panic!(),
}
}
#[test]
fn return_result() {
let mut lua = Lua::new();
lua.openlibs();
fn always_fails() -> Result<i32, &'static str> {
Err("oops, problem")
};
lua.set("always_fails", function0(always_fails));
match lua.execute::<()>(r#"
local res, err = always_fails();
assert(res == nil);
assert(err == "oops, problem");
"#) {
Ok(()) => {}
Err(e) => panic!("{:?}", e),
}
}
#[test]
fn closures() {
let mut lua = Lua::new();
lua.set("add", function2(|a: i32, b: i32| a + b));
lua.set("sub", function2(|a: i32, b: i32| a - b));
let val1: i32 = lua.execute("return add(3, 7)").unwrap();
assert_eq!(val1, 10);
let val2: i32 = lua.execute("return sub(5, 2)").unwrap();
assert_eq!(val2, 3);
}
#[test]
fn closures_lifetime() {
fn t<F>(f: F)
where F: Fn(i32, i32) -> i32
{
let mut lua = Lua::new();
lua.set("add", function2(f));
let val1: i32 = lua.execute("return add(3, 7)").unwrap();
assert_eq!(val1, 10);
}
t(|a, b| a + b);
}
#[test]
fn closures_extern_access() {
let mut a = 5;
{
let mut lua = Lua::new();
lua.set("inc", function0(|| a += 1));
for _ in 0..15 {
lua.execute::<()>("inc()").unwrap();
}
}
assert_eq!(a, 20)
}
#[test]
fn closures_drop_env() {
static mut DID_DESTRUCTOR_RUN: bool = false;
#[derive(Debug)]
struct Foo { };
impl Drop for Foo {
fn drop(&mut self) {
unsafe {
DID_DESTRUCTOR_RUN = true;
}
}
}
{
let foo = Arc::new(Foo { });
{
let mut lua = Lua::new();
lua.set("print_foo", function0(move || println!("{:?}", foo)));
}
}
assert_eq!(unsafe { DID_DESTRUCTOR_RUN }, true);
}
}