tlua/
functions_write.rs

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