diff --git a/hlua/src/functions_write.rs b/hlua/src/functions_write.rs index eda775a..47bf84d 100644 --- a/hlua/src/functions_write.rs +++ b/hlua/src/functions_write.rs @@ -170,6 +170,16 @@ pub trait FunctionExt

{ fn call_mut(&mut self, params: P) -> Self::Output; } +// Called when an object inside Lua is being dropped. +#[inline] +extern "C" fn closure_destructor_wrapper(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 FunctionExt<()> for Function where Z: FnMut() -> R { @@ -198,6 +208,27 @@ macro_rules! impl_function_ext { let lua_data: *mut Z = mem::transmute(lua_data); ptr::write(lua_data, self.function); + let lua_raw = lua.as_mut_lua(); + + // Creating a metatable. + ffi::lua_newtable(lua.as_mut_lua().0); + + // Index "__gc" in the metatable calls the object's destructor. + + // TODO: Could use std::intrinsics::needs_drop to avoid that if not needed. + // After some discussion on IRC, it would be acceptable to add a reexport in libcore + // without going through the RFC process. + { + match "__gc".push_to_lua(&mut lua) { + Ok(p) => p.forget(), + Err(_) => unreachable!(), + }; + + ffi::lua_pushcfunction(lua.as_mut_lua().0, closure_destructor_wrapper::); + ffi::lua_settable(lua.as_mut_lua().0, -3); + } + ffi::lua_setmetatable(lua_raw.0, -2); + // pushing wrapper as a closure let wrapper: extern fn(*mut ffi::lua_State) -> libc::c_int = wrapper::; ffi::lua_pushcclosure(lua.as_mut_lua().0, wrapper, 1); @@ -244,6 +275,27 @@ macro_rules! impl_function_ext { let lua_data: *mut Z = mem::transmute(lua_data); ptr::write(lua_data, self.function); + let lua_raw = lua.as_mut_lua(); + + // Creating a metatable. + ffi::lua_newtable(lua.as_mut_lua().0); + + // Index "__gc" in the metatable calls the object's destructor. + + // TODO: Could use std::intrinsics::needs_drop to avoid that if not needed. + // After some discussion on IRC, it would be acceptable to add a reexport in libcore + // without going through the RFC process. + { + match "__gc".push_to_lua(&mut lua) { + Ok(p) => p.forget(), + Err(_) => unreachable!(), + }; + + ffi::lua_pushcfunction(lua.as_mut_lua().0, closure_destructor_wrapper::); + ffi::lua_settable(lua.as_mut_lua().0, -3); + } + ffi::lua_setmetatable(lua_raw.0, -2); + // pushing wrapper as a closure let wrapper: extern fn(*mut ffi::lua_State) -> libc::c_int = wrapper::; ffi::lua_pushcclosure(lua.as_mut_lua().0, wrapper, 1); @@ -376,6 +428,8 @@ mod tests { use function1; use function2; + use std::sync::Arc; + #[test] fn simple_function() { let mut lua = Lua::new(); @@ -496,4 +550,28 @@ mod tests { 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); + } }