hlua_badtouch/
userdata.rs

1use std::any::{Any, TypeId};
2use std::marker::PhantomData;
3use std::ops::{Deref, DerefMut};
4use std::mem;
5use std::ptr;
6
7use ffi;
8use libc;
9
10use AsLua;
11use AsMutLua;
12use Push;
13use PushGuard;
14use LuaContext;
15use LuaRead;
16
17use InsideCallback;
18use LuaTable;
19
20// Called when an object inside Lua is being dropped.
21#[inline]
22extern "C" fn destructor_wrapper<T>(lua: *mut ffi::lua_State) -> libc::c_int {
23    unsafe {
24        let obj = ffi::lua_touserdata(lua, -1);
25        ptr::drop_in_place(obj as *mut TypeId);
26        ptr::drop_in_place((obj as *mut u8).offset(mem::size_of::<TypeId>() as isize) as *mut T);
27        0
28    }
29}
30
31/// Pushes an object as a user data.
32///
33/// In Lua, a user data is anything that is not recognized by Lua. When the script attempts to
34/// copy a user data, instead only a reference to the data is copied.
35///
36/// The way a Lua script can use the user data depends on the content of the **metatable**, which
37/// is a Lua table linked to the object.
38///
39/// [See this link for more infos.](http://www.lua.org/manual/5.2/manual.html#2.4)
40///
41/// # About the Drop trait
42///
43/// When the Lua context detects that a userdata is no longer needed it calls the function at the
44/// `__gc` index in the userdata's metatable, if any. The hlua library will automatically fill this
45/// index with a function that invokes the `Drop` trait of the userdata.
46///
47/// You can replace the function if you wish so, although you are strongly discouraged to do it.
48/// It is no unsafe to leak data in Rust, so there is no safety issue in doing so.
49///
50/// # Arguments
51///
52///  - `metatable`: Function that fills the metatable of the object.
53///
54#[inline]
55pub fn push_userdata<'lua, L, T, F>(data: T, mut lua: L, metatable: F) -> PushGuard<L>
56    where F: FnOnce(LuaTable<&mut PushGuard<&mut L>>),
57          L: AsMutLua<'lua>,
58          T: Send + 'static + Any
59{
60    unsafe {
61        let typeid = TypeId::of::<T>();
62
63        let lua_data = {
64            let tot_size = mem::size_of_val(&typeid) + mem::size_of_val(&data);
65            ffi::lua_newuserdata(lua.as_mut_lua().0, tot_size as libc::size_t)
66        };
67
68        // We check the alignment requirements.
69        debug_assert_eq!(lua_data as usize % mem::align_of_val(&data), 0);
70        // Since the size of a `TypeId` should always be a usize, this assert should pass every
71        // time as well.
72        debug_assert_eq!(mem::size_of_val(&typeid) % mem::align_of_val(&data), 0);
73
74        // We write the `TypeId` first, and the data right next to it.
75        ptr::write(lua_data as *mut TypeId, typeid);
76        let data_loc = (lua_data as *const u8).offset(mem::size_of_val(&typeid) as isize);
77        ptr::write(data_loc as *mut _, data);
78
79        let lua_raw = lua.as_mut_lua();
80
81        // Creating a metatable.
82        ffi::lua_newtable(lua.as_mut_lua().0);
83
84        // Index "__gc" in the metatable calls the object's destructor.
85
86        // TODO: Could use std::intrinsics::needs_drop to avoid that if not needed.
87        // After some discussion on IRC, it would be acceptable to add a reexport in libcore
88        // without going through the RFC process.
89        {
90            match "__gc".push_to_lua(&mut lua) {
91                Ok(p) => p.forget(),
92                Err(_) => unreachable!(),
93            };
94
95            ffi::lua_pushcfunction(lua.as_mut_lua().0, destructor_wrapper::<T>);
96            ffi::lua_settable(lua.as_mut_lua().0, -3);
97        }
98
99        // Calling the metatable closure.
100        {
101            let raw_lua = lua.as_lua();
102            let mut guard = PushGuard {
103                lua: &mut lua,
104                size: 1,
105                raw_lua: raw_lua,
106            };
107            metatable(LuaRead::lua_read(&mut guard).ok().unwrap());
108            guard.forget();
109        }
110
111        ffi::lua_setmetatable(lua_raw.0, -2);
112    }
113
114    let raw_lua = lua.as_lua();
115    PushGuard {
116        lua: lua,
117        size: 1,
118        raw_lua: raw_lua,
119    }
120}
121
122///
123#[inline]
124pub fn read_userdata<'t, 'c, T>(lua: &'c mut InsideCallback,
125                                index: i32)
126                                -> Result<&'t mut T, &'c mut InsideCallback>
127    where T: 'static + Any
128{
129    unsafe {
130        let data_ptr = ffi::lua_touserdata(lua.as_lua().0, index);
131        if data_ptr.is_null() {
132            return Err(lua);
133        }
134
135        let actual_typeid = data_ptr as *const TypeId;
136        if *actual_typeid != TypeId::of::<T>() {
137            return Err(lua);
138        }
139
140        let data = (data_ptr as *const u8).offset(mem::size_of::<TypeId>() as isize);
141        Ok(&mut *(data as *mut T))
142    }
143}
144
145/// Represents a user data located inside the Lua context.
146#[derive(Debug)]
147pub struct UserdataOnStack<T, L> {
148    variable: L,
149    index: i32,
150    marker: PhantomData<T>,
151}
152
153impl<'lua, T, L> LuaRead<L> for UserdataOnStack<T, L>
154    where L: AsMutLua<'lua>,
155          T: 'lua + Any
156{
157    #[inline]
158    fn lua_read_at_position(lua: L, index: i32) -> Result<UserdataOnStack<T, L>, L> {
159        unsafe {
160            let data_ptr = ffi::lua_touserdata(lua.as_lua().0, index);
161            if data_ptr.is_null() {
162                return Err(lua);
163            }
164
165            let actual_typeid = data_ptr as *const TypeId;
166            if *actual_typeid != TypeId::of::<T>() {
167                return Err(lua);
168            }
169
170            Ok(UserdataOnStack {
171                variable: lua,
172                index: index,
173                marker: PhantomData,
174            })
175        }
176    }
177}
178
179unsafe impl<'lua, T, L> AsLua<'lua> for UserdataOnStack<T, L>
180    where L: AsLua<'lua>,
181          T: 'lua + Any
182{
183    #[inline]
184    fn as_lua(&self) -> LuaContext {
185        self.variable.as_lua()
186    }
187}
188
189unsafe impl<'lua, T, L> AsMutLua<'lua> for UserdataOnStack<T, L>
190    where L: AsMutLua<'lua>,
191          T: 'lua + Any
192{
193    #[inline]
194    fn as_mut_lua(&mut self) -> LuaContext {
195        self.variable.as_mut_lua()
196    }
197}
198
199impl<'lua, T, L> Deref for UserdataOnStack<T, L>
200    where L: AsLua<'lua>,
201          T: 'lua + Any
202{
203    type Target = T;
204
205    #[inline]
206    fn deref(&self) -> &T {
207        unsafe {
208            let base = ffi::lua_touserdata(self.variable.as_lua().0, self.index);
209            let data = (base as *const u8).offset(mem::size_of::<TypeId>() as isize);
210            &*(data as *const T)
211        }
212    }
213}
214
215impl<'lua, T, L> DerefMut for UserdataOnStack<T, L>
216    where L: AsMutLua<'lua>,
217          T: 'lua + Any
218{
219    #[inline]
220    fn deref_mut(&mut self) -> &mut T {
221        unsafe {
222            let base = ffi::lua_touserdata(self.variable.as_mut_lua().0, self.index);
223            let data = (base as *const u8).offset(mem::size_of::<TypeId>() as isize);
224            &mut *(data as *mut T)
225        }
226    }
227}