hlua_badtouch/
functions_write.rs

1use ffi;
2use libc;
3
4use AnyLuaValue;
5use AsLua;
6use AsMutLua;
7use LuaContext;
8use LuaRead;
9use Push;
10use PushGuard;
11use PushOne;
12use Void;
13
14use std::marker::PhantomData;
15use std::fmt::Display;
16use std::mem;
17use std::ptr;
18
19macro_rules! impl_function {
20    ($name:ident, $($p:ident),*) => (
21        /// Wraps a type that implements `FnMut` so that it can be used by hlua.
22        ///
23        /// This is needed because of a limitation in Rust's inferrence system. Even though in
24        /// practice functions and closures always have a fixed number of parameters, the `FnMut`
25        /// trait of Rust was designed so that it allows calling the same closure with a varying
26        /// number of parameters. The consequence however is that there is no way of inferring
27        /// with the trait alone many parameters a function or closure expects.
28        #[inline]
29        pub fn $name<Z, R $(, $p)*>(f: Z) -> Function<Z, ($($p,)*), R>
30            where Z: FnMut($($p),*) -> R
31        {
32            Function {
33                function: f,
34                marker: PhantomData,
35            }
36        }
37    )
38}
39
40impl_function!(function0,);
41impl_function!(function1, A);
42impl_function!(function2, A, B);
43impl_function!(function3, A, B, C);
44impl_function!(function4, A, B, C, D);
45impl_function!(function5, A, B, C, D, E);
46impl_function!(function6, A, B, C, D, E, F);
47impl_function!(function7, A, B, C, D, E, F, G);
48impl_function!(function8, A, B, C, D, E, F, G, H);
49impl_function!(function9, A, B, C, D, E, F, G, H, I);
50impl_function!(function10, A, B, C, D, E, F, G, H, I, J);
51
52/// Opaque type containing a Rust function or closure.
53///
54/// In order to build an instance of this struct, you need to use one of the `functionN` functions.
55/// There is one function for each possible number of parameter. For example if you have a function
56/// with two parameters, you must use [`function2`](fn.function2.html).
57/// Example:
58///
59/// ```
60/// let f: hlua::Function<_, _, _> = hlua::function2(move |a: i32, b: i32| { });
61/// ```
62///
63/// > **Note**: In practice you will never need to build an object of type `Function` as an
64/// > intermediary step. Instead you will most likely always immediately push the function, like
65/// > in the code below.
66///
67/// You can push a `Function` object like any other value:
68///
69/// ```
70/// use hlua::Lua;
71/// let mut lua = Lua::new();
72///
73/// lua.set("foo", hlua::function1(move |a: i32| -> i32 {
74///     a * 5
75/// }));
76/// ```
77///
78/// The function can then be called from Lua:
79///
80/// ```
81/// # use hlua::Lua;
82/// # let mut lua = Lua::new();
83/// # lua.set("foo", hlua::function1(move |a: i32| -> i32 { a * 5 }));
84/// lua.execute::<()>("a = foo(12)").unwrap();
85///
86/// assert_eq!(lua.get::<i32, _>("a").unwrap(), 60);
87/// ```
88///
89/// Remember that in Lua functions are regular variables, so you can do something like this
90/// for example:
91///
92/// ```
93/// # use hlua::Lua;
94/// # let mut lua = Lua::new();
95/// # lua.set("foo", hlua::function1(move |a: i32| -> i32 { a * 5 }));
96/// lua.execute::<()>("bar = foo; a = bar(12)").unwrap();
97/// ```
98///
99/// # Multiple return values
100///
101/// The Lua language supports functions that return multiple values at once.
102///
103/// In order to return multiple values from a Rust function, you can return a tuple. The elements
104/// of the tuple will be returned in order.
105///
106/// ```
107/// use hlua::Lua;
108/// let mut lua = Lua::new();
109///
110/// lua.set("values", hlua::function0(move || -> (i32, i32, i32) {
111///     (12, 24, 48)
112/// }));
113///
114/// lua.execute::<()>("a, b, c = values()").unwrap();
115///
116/// assert_eq!(lua.get::<i32, _>("a").unwrap(), 12);
117/// assert_eq!(lua.get::<i32, _>("b").unwrap(), 24);
118/// assert_eq!(lua.get::<i32, _>("c").unwrap(), 48);
119/// ```
120///
121/// # Using `Result`
122///
123/// If you want to return an error to the Lua script, you can use a `Result` that contains an
124/// `Err`. The error will be returned to Lua as two values: A `nil` value and the error message.
125///
126/// The error type of the `Result` must implement the `Display` trait, and will be turned into a
127/// Lua string.
128///
129/// ```
130/// use hlua::Lua;
131/// let mut lua = Lua::new();
132/// lua.openlibs();
133///
134/// lua.set("err", hlua::function0(move || -> Result<i32, &'static str> {
135///     Err("something wrong happened")
136/// }));
137///
138/// lua.execute::<()>(r#"
139///     res, err = err();
140///     assert(res == nil);
141///     assert(err == "something wrong happened");
142/// "#).unwrap();
143/// ```
144///
145/// This also allows easy use of `assert` to act like `.unwrap()` in Rust:
146///
147/// ```
148/// use hlua::Lua;
149/// let mut lua = Lua::new();
150/// lua.openlibs();
151///
152/// lua.set("err", hlua::function0(move || -> Result<i32, &'static str> {
153///     Err("something wrong happened")
154/// }));
155///
156/// let ret = lua.execute::<()>("res = assert(err())");
157/// assert!(ret.is_err());
158/// ```
159#[derive(Debug)]
160pub struct Function<F, P, R> {
161    function: F,
162    marker: PhantomData<(P, R)>,
163}
164
165/// Trait implemented on `Function` to mimic `FnMut`.
166///
167/// We could in theory use the `FnMut` trait instead of this one, but it is still unstable.
168pub trait FunctionExt<P> {
169    type Output;
170
171    fn call_mut(&mut self, params: P) -> Self::Output;
172}
173
174// Called when an object inside Lua is being dropped.
175#[inline]
176extern "C" fn closure_destructor_wrapper<T>(lua: *mut ffi::lua_State) -> libc::c_int {
177    unsafe {
178        let obj = ffi::lua_touserdata(lua, -1);
179        ptr::drop_in_place((obj as *mut u8) as *mut T);
180        0
181    }
182}
183
184macro_rules! impl_function_ext {
185    () => (
186        impl<Z, R> FunctionExt<()> for Function<Z, (), R> where Z: FnMut() -> R {
187            type Output = R;
188
189            #[allow(non_snake_case)]
190            #[inline]
191            fn call_mut(&mut self, _: ()) -> Self::Output {
192                (self.function)()
193            }
194        }
195
196        impl<'lua, L, Z, R> Push<L> for Function<Z, (), R>
197                where L: AsMutLua<'lua>,
198                      Z: 'lua + FnMut() -> R,
199                      R: for<'a> Push<&'a mut InsideCallback> + 'static
200        {
201            type Err = Void;      // TODO: use `!` instead (https://github.com/rust-lang/rust/issues/35121)
202
203            #[inline]
204            fn push_to_lua(self, mut lua: L) -> Result<PushGuard<L>, (Void, L)> {
205                unsafe {
206                    // pushing the function pointer as a userdata
207                    let lua_data = ffi::lua_newuserdata(lua.as_mut_lua().0,
208                                                        mem::size_of::<Z>() as libc::size_t);
209                    let lua_data: *mut Z = mem::transmute(lua_data);
210                    ptr::write(lua_data, self.function);
211
212                    let lua_raw = lua.as_mut_lua();
213
214                    // Creating a metatable.
215                    ffi::lua_newtable(lua.as_mut_lua().0);
216
217                    // Index "__gc" in the metatable calls the object's destructor.
218
219                    // TODO: Could use std::intrinsics::needs_drop to avoid that if not needed.
220                    // After some discussion on IRC, it would be acceptable to add a reexport in libcore
221                    // without going through the RFC process.
222                    {
223                        match "__gc".push_to_lua(&mut lua) {
224                            Ok(p) => p.forget(),
225                            Err(_) => unreachable!(),
226                        };
227
228                        ffi::lua_pushcfunction(lua.as_mut_lua().0, closure_destructor_wrapper::<Z>);
229                        ffi::lua_settable(lua.as_mut_lua().0, -3);
230                    }
231                    ffi::lua_setmetatable(lua_raw.0, -2);
232
233                    // pushing wrapper as a closure
234                    let wrapper: extern fn(*mut ffi::lua_State) -> libc::c_int = wrapper::<Self, _, R>;
235                    ffi::lua_pushcclosure(lua.as_mut_lua().0, wrapper, 1);
236                    let raw_lua = lua.as_lua();
237                    Ok(PushGuard { lua: lua, size: 1, raw_lua: raw_lua })
238                }
239            }
240        }
241
242        impl<'lua, L, Z, R> PushOne<L> for Function<Z, (), R>
243                where L: AsMutLua<'lua>,
244                      Z: 'lua + FnMut() -> R,
245                      R: for<'a> Push<&'a mut InsideCallback> + 'static
246        {
247        }
248    );
249
250    ($($p:ident),+) => (
251        impl<Z, R $(,$p)*> FunctionExt<($($p,)*)> for Function<Z, ($($p,)*), R> where Z: FnMut($($p),*) -> R {
252            type Output = R;
253
254            #[allow(non_snake_case)]
255            #[inline]
256            fn call_mut(&mut self, params: ($($p,)*)) -> Self::Output {
257                let ($($p,)*) = params;
258                (self.function)($($p),*)
259            }
260        }
261
262        impl<'lua, L, Z, R $(,$p: 'static)+> Push<L> for Function<Z, ($($p,)*), R>
263                where L: AsMutLua<'lua>,
264                      Z: 'lua + FnMut($($p),*) -> R,
265                      ($($p,)*): for<'p> LuaRead<&'p mut InsideCallback>,
266                      R: for<'a> Push<&'a mut InsideCallback> + 'static
267        {
268            type Err = Void;      // TODO: use `!` instead (https://github.com/rust-lang/rust/issues/35121)
269
270            #[inline]
271            fn push_to_lua(self, mut lua: L) -> Result<PushGuard<L>, (Void, L)> {
272                unsafe {
273                    // pushing the function pointer as a userdata
274                    let lua_data = ffi::lua_newuserdata(lua.as_mut_lua().0,
275                                                        mem::size_of::<Z>() as libc::size_t);
276                    let lua_data: *mut Z = mem::transmute(lua_data);
277                    ptr::write(lua_data, self.function);
278
279                    let lua_raw = lua.as_mut_lua();
280
281                    // Creating a metatable.
282                    ffi::lua_newtable(lua.as_mut_lua().0);
283
284                    // Index "__gc" in the metatable calls the object's destructor.
285
286                    // TODO: Could use std::intrinsics::needs_drop to avoid that if not needed.
287                    // After some discussion on IRC, it would be acceptable to add a reexport in libcore
288                    // without going through the RFC process.
289                    {
290                        match "__gc".push_to_lua(&mut lua) {
291                            Ok(p) => p.forget_internal(),
292                            Err(_) => unreachable!(),
293                        };
294
295                        ffi::lua_pushcfunction(lua.as_mut_lua().0, closure_destructor_wrapper::<Z>);
296                        ffi::lua_settable(lua.as_mut_lua().0, -3);
297                    }
298                    ffi::lua_setmetatable(lua_raw.0, -2);
299
300                    // pushing wrapper as a closure
301                    let wrapper: extern fn(*mut ffi::lua_State) -> libc::c_int = wrapper::<Self, _, R>;
302                    ffi::lua_pushcclosure(lua.as_mut_lua().0, wrapper, 1);
303                    let raw_lua = lua.as_lua();
304                    Ok(PushGuard { lua: lua, size: 1, raw_lua: raw_lua })
305                }
306            }
307        }
308
309        impl<'lua, L, Z, R $(,$p: 'static)+> PushOne<L> for Function<Z, ($($p,)*), R>
310                where L: AsMutLua<'lua>,
311                      Z: 'lua + FnMut($($p),*) -> R,
312                      ($($p,)*): for<'p> LuaRead<&'p mut InsideCallback>,
313                      R: for<'a> Push<&'a mut InsideCallback> + 'static
314        {
315        }
316    )
317}
318
319impl_function_ext!();
320impl_function_ext!(A);
321impl_function_ext!(A, B);
322impl_function_ext!(A, B, C);
323impl_function_ext!(A, B, C, D);
324impl_function_ext!(A, B, C, D, E);
325impl_function_ext!(A, B, C, D, E, F);
326impl_function_ext!(A, B, C, D, E, F, G);
327impl_function_ext!(A, B, C, D, E, F, G, H);
328impl_function_ext!(A, B, C, D, E, F, G, H, I);
329impl_function_ext!(A, B, C, D, E, F, G, H, I, J);
330
331/// Opaque type that represents the Lua context when inside a callback.
332///
333/// Some types (like `Result`) can only be returned from a callback and not written inside a
334/// Lua variable. This type is here to enforce this restriction.
335#[derive(Debug)]
336pub struct InsideCallback {
337    lua: LuaContext,
338}
339
340unsafe impl<'a, 'lua> AsLua<'lua> for &'a InsideCallback {
341    #[inline]
342    fn as_lua(&self) -> LuaContext {
343        self.lua
344    }
345}
346
347unsafe impl<'a, 'lua> AsLua<'lua> for &'a mut InsideCallback {
348    #[inline]
349    fn as_lua(&self) -> LuaContext {
350        self.lua
351    }
352}
353
354unsafe impl<'a, 'lua> AsMutLua<'lua> for &'a mut InsideCallback {
355    #[inline]
356    fn as_mut_lua(&mut self) -> LuaContext {
357        self.lua
358    }
359}
360
361impl<'a, T, E, P> Push<&'a mut InsideCallback> for Result<T, E>
362    where T: Push<&'a mut InsideCallback, Err = P> + for<'b> Push<&'b mut &'a mut InsideCallback, Err = P>,
363          E: Display
364{
365    type Err = P;
366
367    #[inline]
368    fn push_to_lua(self, lua: &'a mut InsideCallback) -> Result<PushGuard<&'a mut InsideCallback>, (P, &'a mut InsideCallback)> {
369        match self {
370            Ok(val) => val.push_to_lua(lua),
371            Err(val) => {
372                Ok((AnyLuaValue::LuaNil, format!("{}", val)).push_no_err(lua))
373            }
374        }
375    }
376}
377
378impl<'a, T, E, P> PushOne<&'a mut InsideCallback> for Result<T, E>
379    where T: PushOne<&'a mut InsideCallback, Err = P> + for<'b> PushOne<&'b mut &'a mut InsideCallback, Err = P>,
380          E: Display
381{
382}
383
384// this function is called when Lua wants to call one of our functions
385#[inline]
386extern "C" fn wrapper<T, P, R>(lua: *mut ffi::lua_State) -> libc::c_int
387    where T: FunctionExt<P, Output = R>,
388          P: for<'p> LuaRead<&'p mut InsideCallback> + 'static,
389          R: for<'p> Push<&'p mut InsideCallback>
390{
391    // loading the object that we want to call from the Lua context
392    let data_raw = unsafe { ffi::lua_touserdata(lua, ffi::lua_upvalueindex(1)) };
393    let data: &mut T = unsafe { mem::transmute(data_raw) };
394
395    // creating a temporary Lua context in order to pass it to push & read functions
396    let mut tmp_lua = InsideCallback { lua: LuaContext(lua) };
397
398    // trying to read the arguments
399    let arguments_count = unsafe { ffi::lua_gettop(lua) } as i32;
400    let args = match LuaRead::lua_read_at_position(&mut tmp_lua, -arguments_count as libc::c_int) {      // TODO: what if the user has the wrong params?
401        Err(_) => {
402            let err_msg = format!("wrong parameter types for callback function");
403            match err_msg.push_to_lua(&mut tmp_lua) {
404                Ok(p) => p.forget_internal(),
405                Err(_) => unreachable!(),
406            };
407            unsafe {
408                ffi::lua_error(lua);
409            }
410            unreachable!()
411        }
412        Ok(a) => a,
413    };
414
415    let ret_value = data.call_mut(args);
416
417    // pushing back the result of the function on the stack
418    let nb = match ret_value.push_to_lua(&mut tmp_lua) {
419        Ok(p) => p.forget_internal(),
420        Err(_) => panic!(),      // TODO: wrong
421    };
422    nb as libc::c_int
423}
424
425#[cfg(test)]
426mod tests {
427    use Lua;
428    use LuaError;
429    use function0;
430    use function1;
431    use function2;
432
433    use std::sync::Arc;
434
435    #[test]
436    fn simple_function() {
437        let mut lua = Lua::new();
438
439        fn ret5() -> i32 {
440            5
441        };
442        lua.set("ret5", function0(ret5));
443
444        let val: i32 = lua.execute("return ret5()").unwrap();
445        assert_eq!(val, 5);
446    }
447
448    #[test]
449    fn one_argument() {
450        let mut lua = Lua::new();
451
452        fn plus_one(val: i32) -> i32 {
453            val + 1
454        };
455        lua.set("plus_one", function1(plus_one));
456
457        let val: i32 = lua.execute("return plus_one(3)").unwrap();
458        assert_eq!(val, 4);
459    }
460
461    #[test]
462    fn two_arguments() {
463        let mut lua = Lua::new();
464
465        fn add(val1: i32, val2: i32) -> i32 {
466            val1 + val2
467        };
468        lua.set("add", function2(add));
469
470        let val: i32 = lua.execute("return add(3, 7)").unwrap();
471        assert_eq!(val, 10);
472    }
473
474    #[test]
475    fn wrong_arguments_types() {
476        let mut lua = Lua::new();
477
478        fn add(val1: i32, val2: i32) -> i32 {
479            val1 + val2
480        };
481        lua.set("add", function2(add));
482
483        match lua.execute::<i32>("return add(3, \"hello\")") {
484            Err(LuaError::ExecutionError(_)) => (),
485            _ => panic!(),
486        }
487    }
488
489    #[test]
490    fn return_result() {
491        let mut lua = Lua::new();
492        lua.openlibs();
493
494        fn always_fails() -> Result<i32, &'static str> {
495            Err("oops, problem")
496        };
497        lua.set("always_fails", function0(always_fails));
498
499        match lua.execute::<()>(r#"
500            local res, err = always_fails();
501            assert(res == nil);
502            assert(err == "oops, problem");
503        "#) {
504            Ok(()) => {}
505            Err(e) => panic!("{:?}", e),
506        }
507    }
508
509    #[test]
510    fn closures() {
511        let mut lua = Lua::new();
512
513        lua.set("add", function2(|a: i32, b: i32| a + b));
514        lua.set("sub", function2(|a: i32, b: i32| a - b));
515
516        let val1: i32 = lua.execute("return add(3, 7)").unwrap();
517        assert_eq!(val1, 10);
518
519        let val2: i32 = lua.execute("return sub(5, 2)").unwrap();
520        assert_eq!(val2, 3);
521    }
522
523    #[test]
524    fn closures_lifetime() {
525        fn t<F>(f: F)
526            where F: Fn(i32, i32) -> i32
527        {
528            let mut lua = Lua::new();
529
530            lua.set("add", function2(f));
531
532            let val1: i32 = lua.execute("return add(3, 7)").unwrap();
533            assert_eq!(val1, 10);
534        }
535
536        t(|a, b| a + b);
537    }
538
539    #[test]
540    fn closures_extern_access() {
541        let mut a = 5;
542
543        {
544            let mut lua = Lua::new();
545
546            lua.set("inc", function0(|| a += 1));
547            for _ in 0..15 {
548                lua.execute::<()>("inc()").unwrap();
549            }
550        }
551
552        assert_eq!(a, 20)
553    }
554
555    #[test]
556    fn closures_drop_env() {
557        static mut DID_DESTRUCTOR_RUN: bool = false;
558
559        #[derive(Debug)]
560        struct Foo { };
561        impl Drop for Foo {
562            fn drop(&mut self) {
563                unsafe {
564                    DID_DESTRUCTOR_RUN = true;
565                }
566            }
567        }
568        {
569            let foo = Arc::new(Foo { });
570
571            {
572                let mut lua = Lua::new();
573
574                lua.set("print_foo", function0(move || println!("{:?}", foo)));
575            }
576        }
577        assert_eq!(unsafe { DID_DESTRUCTOR_RUN }, true);
578    }
579}