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 #[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#[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
186pub 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; #[inline]
226 fn push_into_lua(self, lua: L) -> Result<PushGuard<L>, (Void, L)> {
227 unsafe {
228 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 ffi::lua_newtable(lua.as_lua());
235
236 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 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; fn push_to_lua(&self, lua: L) -> Result<PushGuard<L>, (Void, L)> {
280 unsafe {
281 let ud = ffi::lua_newuserdata(lua.as_lua(), mem::size_of::<Self>() as _);
283 ptr::write(ud.cast(), *self);
284
285 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#[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#[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
381impl<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#[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
454extern "C-unwind" fn wrapper<T, A, R>(lua: LuaState) -> libc::c_int
456where
457 T: FnMutExt<A, Output = R>,
458 A: for<'p> LuaRead<&'p InsideCallback> + 'static,
460 R: PushInto<InsideCallback>,
461{
462 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 let tmp_lua = InsideCallback(lua.as_lua());
468
469 let arguments_count = unsafe { ffi::lua_gettop(lua) };
471 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 let nb = match ret_value.push_into_lua(tmp_lua) {
491 Ok(p) => p.forget_internal(),
492 Err(_) => panic!(), };
494 nb as _
495}
496
497#[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}