pm_rlua/
userdata.rs

1use std::any::{Any, TypeId};
2use std::boxed::Box;
3use std::ffi::CString;
4use std::marker::PhantomData;
5use std::mem;
6use std::ptr;
7
8use libc;
9use td_clua;
10use td_clua::lua_State;
11use Lua;
12use LuaPush;
13use LuaRead;
14use LuaTable;
15
16// Called when an object inside Lua is being dropped.
17#[inline]
18extern "C" fn destructor_wrapper<T>(lua: *mut td_clua::lua_State) -> libc::c_int {
19    unsafe {
20        let obj = td_clua::lua_touserdata(lua, -1);
21        ptr::drop_in_place(obj as *mut T);
22        0
23    }
24}
25
26extern "C" fn constructor_wrapper<T>(lua: *mut td_clua::lua_State) -> libc::c_int
27where
28    T: NewStruct + Any,
29{
30    let t = T::new();
31    let lua_data_raw =
32        unsafe { td_clua::lua_newuserdata(lua, mem::size_of::<T>() as libc::size_t) };
33    unsafe {
34        ptr::write(lua_data_raw as *mut _, t);
35    }
36    let typeid = CString::new(T::name()).unwrap();
37    unsafe {
38        td_clua::lua_getglobal(lua, typeid.as_ptr());
39        td_clua::lua_setmetatable(lua, -2);
40    }
41    1
42}
43
44// constructor direct create light object,
45// in rust we alloc the memory, avoid copy the memory
46// in lua we get the object, we must free the memory
47extern "C" fn constructor_light_wrapper<T>(lua: *mut td_clua::lua_State) -> libc::c_int
48where
49    T: NewStruct + Any,
50{
51    let t = Box::into_raw(Box::new(T::new()));
52    push_lightuserdata(unsafe { &mut *t }, lua, |_| {});
53    let typeid = CString::new(T::name()).unwrap();
54    unsafe {
55        td_clua::lua_getglobal(lua, typeid.as_ptr());
56        td_clua::lua_setmetatable(lua, -2);
57    }
58    1
59}
60
61/// Pushes an object as a user data.
62///
63/// In Lua, a user data is anything that is not recognized by Lua. When the script attempts to
64/// copy a user data, instead only a reference to the data is copied.
65///
66/// The way a Lua script can use the user data depends on the content of the **metatable**, which
67/// is a Lua table linked to the object.
68///
69/// # Arguments
70///
71///  - `metatable`: Function that fills the metatable of the object.
72///
73pub fn push_userdata<'a, T, F>(data: T, lua: *mut td_clua::lua_State, mut metatable: F) -> i32
74where
75    F: FnMut(LuaTable),
76    T: 'a + Any,
77{
78    let typeid = format!("{:?}", TypeId::of::<T>());
79    let lua_data_raw =
80        unsafe { td_clua::lua_newuserdata(lua, mem::size_of::<T>() as libc::size_t) };
81
82    // creating a metatable
83    unsafe {
84        ptr::write(lua_data_raw as *mut _, data);
85
86        td_clua::lua_newtable(lua);
87
88        // index "__typeid" corresponds to the hash of the TypeId of T
89        "__typeid".push_to_lua(lua);
90        typeid.push_to_lua(lua);
91        td_clua::lua_settable(lua, -3);
92
93        // index "__gc" call the object's destructor
94        {
95            "__gc".push_to_lua(lua);
96
97            td_clua::lua_pushcfunction(lua, destructor_wrapper::<T>);
98
99            td_clua::lua_settable(lua, -3);
100        }
101
102        // calling the metatable closure
103        {
104            metatable(LuaRead::lua_read(lua).unwrap());
105        }
106
107        td_clua::lua_setmetatable(lua, -2);
108    }
109
110    1
111}
112
113/// Pushes an object as a user data.
114///
115/// In Lua, a user data is anything that is not recognized by Lua. When the script attempts to
116/// copy a user data, instead only a reference to the data is copied.
117///
118/// The way a Lua script can use the user data depends on the content of the **metatable**, which
119/// is a Lua table linked to the object.
120///
121/// # Arguments
122///
123///  - `metatable`: Function that fills the metatable of the object.
124///
125pub fn push_lightuserdata<'a, T, F>(
126    data: &'a mut T,
127    lua: *mut td_clua::lua_State,
128    mut metatable: F,
129) -> i32
130where
131    F: FnMut(LuaTable),
132    T: 'a + Any,
133{
134    let typeid = format!("{:?}", TypeId::of::<T>());
135    unsafe {
136        td_clua::lua_pushlightuserdata(lua, mem::transmute(data));
137    };
138
139    // creating a metatable
140    unsafe {
141        td_clua::lua_newtable(lua);
142
143        // index "__typeid" corresponds to the hash of the TypeId of T
144        "__typeid".push_to_lua(lua);
145        typeid.push_to_lua(lua);
146        td_clua::lua_settable(lua, -3);
147
148        // calling the metatable closure
149        {
150            metatable(LuaRead::lua_read(lua).unwrap());
151        }
152
153        td_clua::lua_setmetatable(lua, -2);
154    }
155
156    1
157}
158
159///
160pub fn read_userdata<'t, T>(lua: *mut td_clua::lua_State, index: i32) -> Option<&'t mut T>
161where
162    T: 'static + Any,
163{
164    unsafe {
165        let expected_typeid = format!("{:?}", TypeId::of::<T>());
166        let data_ptr = td_clua::lua_touserdata(lua, index);
167        if data_ptr.is_null() {
168            return None;
169        }
170        if td_clua::lua_getmetatable(lua, index) == 0 {
171            return None;
172        }
173
174        "__typeid".push_to_lua(lua);
175        td_clua::lua_gettable(lua, -2);
176        match <String as LuaRead>::lua_read(lua) {
177            Some(ref val) if val == &expected_typeid => {}
178            _ => {
179                return None;
180            }
181        }
182        td_clua::lua_pop(lua, 2);
183        Some(&mut *(data_ptr as *mut T))
184    }
185}
186
187pub trait NewStruct {
188    fn new() -> Self;
189    fn name() -> &'static str;
190}
191
192pub struct LuaStruct<T> {
193    lua: *mut lua_State,
194    light: bool,
195    marker: PhantomData<T>,
196}
197
198impl<T> LuaStruct<T>
199where
200    T: NewStruct + Any,
201{
202    pub fn new(lua: *mut lua_State) -> LuaStruct<T> {
203        LuaStruct {
204            lua: lua,
205            light: false,
206            marker: PhantomData,
207        }
208    }
209
210    pub fn new_light(lua: *mut lua_State) -> LuaStruct<T> {
211        LuaStruct {
212            lua: lua,
213            light: true,
214            marker: PhantomData,
215        }
216    }
217
218    pub fn ensure_matetable(&mut self) {
219        let name = T::name();
220        let mut lua = Lua::from_existing_state(self.lua, false);
221
222        match lua.query::<LuaTable, _>(name.clone()) {
223            Some(_) => {}
224            None => unsafe {
225                td_clua::lua_newtable(self.lua);
226
227                let typeid = format!("{:?}", TypeId::of::<T>());
228                // index "__name" corresponds to the hash of the TypeId of T
229                "__typeid".push_to_lua(self.lua);
230                typeid.push_to_lua(self.lua);
231                td_clua::lua_settable(self.lua, -3);
232
233                // index "__gc" call the object's destructor
234                if !self.light {
235                    "__gc".push_to_lua(self.lua);
236
237                    td_clua::lua_pushcfunction(self.lua, destructor_wrapper::<T>);
238
239                    td_clua::lua_settable(self.lua, -3);
240                }
241
242                "__index".push_to_lua(self.lua);
243                td_clua::lua_newtable(self.lua);
244                td_clua::lua_rawset(self.lua, -3);
245
246                let name = CString::new(name).unwrap();
247                td_clua::lua_setglobal(self.lua, name.as_ptr());
248            },
249        }
250    }
251
252    pub fn create(&mut self) -> &mut LuaStruct<T> {
253        self.ensure_matetable();
254        unsafe {
255            let typeid = CString::new(T::name()).unwrap();
256            td_clua::lua_getglobal(self.lua, typeid.as_ptr());
257            if td_clua::lua_istable(self.lua, -1) {
258                td_clua::lua_newtable(self.lua);
259                "__call".push_to_lua(self.lua);
260
261                if self.light {
262                    td_clua::lua_pushcfunction(self.lua, constructor_light_wrapper::<T>);
263                    td_clua::lua_settable(self.lua, -3);
264                } else {
265                    td_clua::lua_pushcfunction(self.lua, constructor_wrapper::<T>);
266                    td_clua::lua_settable(self.lua, -3);
267                }
268
269                td_clua::lua_setmetatable(self.lua, -2);
270            }
271            td_clua::lua_pop(self.lua, 1);
272        }
273        self
274    }
275
276    pub fn def<P>(&mut self, name: &str, param: P) -> &mut LuaStruct<T>
277    where
278        P: LuaPush,
279    {
280        let tname = T::name();
281        let mut lua = Lua::from_existing_state(self.lua, false);
282        if let Some(mut table) = lua.query::<LuaTable, _>(tname.clone()) {
283            match table.query::<LuaTable, _>("__index") {
284                Some(mut index) => {
285                    index.set(name, param);
286                }
287                None => {
288                    let mut index = table.empty_table("__index");
289                    index.set(name, param);
290                }
291            };
292        };
293        self
294    }
295
296    pub fn register(
297        &mut self,
298        name: &str,
299        func: extern "C" fn(*mut td_clua::lua_State) -> libc::c_int,
300    ) -> &mut LuaStruct<T> {
301        let tname = T::name();
302        let mut lua = Lua::from_existing_state(self.lua, false);
303        if let Some(mut table) = lua.query::<LuaTable, _>(tname.clone()) {
304            match table.query::<LuaTable, _>("__index") {
305                Some(mut index) => {
306                    index.register(name, func);
307                }
308                None => {
309                    let mut index = table.empty_table("__index");
310                    index.register(name, func);
311                }
312            };
313        };
314        self
315    }
316}