1use std::cell::RefCell;
77use std::os::raw::{c_int, c_void};
78use std::result::Result as StdResult;
79use std::{mem, ptr, slice};
80
81use crate::error::{Error, ExternalError, ExternalResult, Result};
82use crate::state::Lua;
83use crate::table::Table;
84use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti};
85use crate::types::{Callback, LuaType, MaybeSend, ValueRef};
86use crate::util::{
87 StackGuard, assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str,
88};
89use crate::value::Value;
90
91#[cfg(feature = "async")]
92use {
93 crate::thread::AsyncThread,
94 crate::types::AsyncCallback,
95 std::future::{self, Future},
96 std::pin::{Pin, pin},
97 std::task::{Context, Poll},
98};
99
100#[derive(Clone, Debug, PartialEq)]
102pub struct Function(pub(crate) ValueRef);
103
104#[derive(Clone, Debug)]
110#[non_exhaustive]
111pub struct FunctionInfo {
112 pub name: Option<String>,
114 pub name_what: Option<&'static str>,
118 pub what: &'static str,
121 pub source: Option<String>,
123 pub short_src: Option<String>,
125 pub line_defined: Option<usize>,
127 pub last_line_defined: Option<usize>,
129 pub num_upvalues: u8,
131 #[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))]
133 #[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))]
134 pub num_params: u8,
135 #[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))]
137 #[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))]
138 pub is_vararg: bool,
139}
140
141#[cfg(any(feature = "luau", doc))]
143#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
144#[derive(Clone, Debug, PartialEq, Eq)]
145pub struct CoverageInfo {
146 pub function: Option<String>,
147 pub line_defined: i32,
148 pub depth: i32,
149 pub hits: Vec<i32>,
150}
151
152impl Function {
153 pub fn call<R: FromLuaMulti>(&self, args: impl IntoLuaMulti) -> Result<R> {
194 let lua = self.0.lua.lock();
195 let state = lua.state();
196 unsafe {
197 let _sg = StackGuard::new(state);
198 check_stack(state, 2)?;
199
200 lua.push_error_traceback();
202 let stack_start = ffi::lua_gettop(state);
203 lua.push_ref(&self.0);
205 let nargs = args.push_into_stack_multi(&lua)?;
206 let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start);
208 if ret != ffi::LUA_OK {
209 return Err(pop_error(state, ret));
210 }
211 let nresults = ffi::lua_gettop(state) - stack_start;
213 R::from_stack_multi(nresults, &lua)
214 }
215 }
216
217 #[cfg(feature = "async")]
245 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
246 pub fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
247 where
248 R: FromLuaMulti,
249 {
250 let lua = self.0.lua.lock();
251 AsyncCallFuture(unsafe {
252 lua.create_recycled_thread(self).and_then(|th| {
253 let mut th = th.into_async(args)?;
254 th.set_recyclable(true);
255 lua.update_thread_ownership(th.thread(), Some(lua.state()));
256 Ok(th)
257 })
258 })
259 }
260
261 pub fn bind(&self, args: impl IntoLuaMulti) -> Result<Function> {
289 unsafe extern "C-unwind" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
290 let nargs = ffi::lua_gettop(state);
291 let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int;
292 ffi::luaL_checkstack(state, nbinds, ptr::null());
293
294 for i in 0..nbinds {
295 ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 2));
296 }
297 if nargs > 0 {
298 ffi::lua_rotate(state, 1, nbinds);
299 }
300
301 nargs + nbinds
302 }
303
304 let lua = self.0.lua.lock();
305 let state = lua.state();
306
307 let args = args.into_lua_multi(lua.lua())?;
308 let nargs = args.len() as c_int;
309
310 if nargs == 0 {
311 return Ok(self.clone());
312 }
313
314 if nargs + 1 > ffi::LUA_MAX_UPVALUES {
315 return Err(Error::BindError);
316 }
317
318 let args_wrapper = unsafe {
319 let _sg = StackGuard::new(state);
320 check_stack(state, nargs + 3)?;
321
322 ffi::lua_pushinteger(state, nargs as ffi::lua_Integer);
323 for arg in &args {
324 lua.push_value(arg)?;
325 }
326 protect_lua!(state, nargs + 1, 1, fn(state) {
327 ffi::lua_pushcclosure(state, args_wrapper_impl, ffi::lua_gettop(state));
328 })?;
329
330 Function(lua.try_pop_ref()?)
331 };
332
333 let lua = lua.lua();
334 lua.load(
335 r#"
336 local func, args_wrapper = ...
337 return function(...)
338 return func(args_wrapper(...))
339 end
340 "#,
341 )
342 .try_cache()
343 .set_name("=__mlua_bind")
344 .call((self, args_wrapper))
345 }
346
347 pub fn environment(&self) -> Option<Table> {
353 let lua = self.0.lua.lock();
354 let state = lua.state();
355 unsafe {
356 let _sg = StackGuard::new(state);
357 assert_stack(state, 1);
358
359 lua.push_ref(&self.0);
360 if ffi::lua_iscfunction(state, -1) != 0 {
361 return None;
362 }
363
364 #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
365 ffi::lua_getfenv(state, -1);
366 #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
367 for i in 1..=255 {
368 match ffi::lua_getupvalue(state, -1, i) {
370 s if s.is_null() => break,
371 s if std::ffi::CStr::from_ptr(s as _) == c"_ENV" => break,
372 _ => ffi::lua_pop(state, 1),
373 }
374 }
375
376 if ffi::lua_type(state, -1) != ffi::LUA_TTABLE {
377 return None;
378 }
379 Some(Table(lua.pop_ref()))
380 }
381 }
382
383 pub fn set_environment(&self, env: Table) -> Result<bool> {
390 let lua = self.0.lua.lock();
391 let state = lua.state();
392 unsafe {
393 let _sg = StackGuard::new(state);
394 check_stack(state, 2)?;
395
396 lua.push_ref(&self.0);
397 if ffi::lua_iscfunction(state, -1) != 0 {
398 return Ok(false);
399 }
400
401 #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
402 {
403 lua.push_ref(&env.0);
404 ffi::lua_setfenv(state, -2);
405 }
406 #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
407 for i in 1..=255 {
408 match ffi::lua_getupvalue(state, -1, i) {
409 s if s.is_null() => return Ok(false),
410 s if std::ffi::CStr::from_ptr(s as _) == c"_ENV" => {
411 ffi::lua_pop(state, 1);
412 let f_with_env = lua
414 .lua()
415 .load("return _ENV")
416 .set_environment(env)
417 .try_cache()
418 .into_function()?;
419 lua.push_ref(&f_with_env.0);
420 ffi::lua_upvaluejoin(state, -2, i, -1, 1);
421 break;
422 }
423 _ => ffi::lua_pop(state, 1),
424 }
425 }
426
427 Ok(true)
428 }
429 }
430
431 pub fn info(&self) -> FunctionInfo {
438 let lua = self.0.lua.lock();
439 let state = lua.state();
440 unsafe {
441 let _sg = StackGuard::new(state);
442 assert_stack(state, 1);
443
444 let mut ar: ffi::lua_Debug = mem::zeroed();
445 lua.push_ref(&self.0);
446
447 #[cfg(not(feature = "luau"))]
448 let res = ffi::lua_getinfo(state, cstr!(">Snu"), &mut ar);
449 #[cfg(not(feature = "luau"))]
450 mlua_assert!(res != 0, "lua_getinfo failed with `>Snu`");
451
452 #[cfg(feature = "luau")]
453 let res = ffi::lua_getinfo(state, -1, cstr!("snau"), &mut ar);
454 #[cfg(feature = "luau")]
455 mlua_assert!(res != 0, "lua_getinfo failed with `snau`");
456
457 FunctionInfo {
458 name: ptr_to_lossy_str(ar.name).map(|s| s.into_owned()),
459 #[cfg(not(feature = "luau"))]
460 name_what: ptr_to_str(ar.namewhat).filter(|s| !s.is_empty()),
461 #[cfg(feature = "luau")]
462 name_what: None,
463 what: ptr_to_str(ar.what).unwrap_or("main"),
464 source: ptr_to_lossy_str(ar.source).map(|s| s.into_owned()),
465 #[cfg(not(feature = "luau"))]
466 short_src: ptr_to_lossy_str(ar.short_src.as_ptr()).map(|s| s.into_owned()),
467 #[cfg(feature = "luau")]
468 short_src: ptr_to_lossy_str(ar.short_src).map(|s| s.into_owned()),
469 line_defined: linenumber_to_usize(ar.linedefined),
470 #[cfg(not(feature = "luau"))]
471 last_line_defined: linenumber_to_usize(ar.lastlinedefined),
472 #[cfg(feature = "luau")]
473 last_line_defined: None,
474 #[cfg(not(feature = "luau"))]
475 num_upvalues: ar.nups as _,
476 #[cfg(feature = "luau")]
477 num_upvalues: ar.nupvals,
478 #[cfg(not(any(feature = "lua51", feature = "luajit")))]
479 num_params: ar.nparams,
480 #[cfg(not(any(feature = "lua51", feature = "luajit")))]
481 is_vararg: ar.isvararg != 0,
482 }
483 }
484 }
485
486 #[cfg(not(feature = "luau"))]
495 #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
496 pub fn dump(&self, strip: bool) -> Vec<u8> {
497 self.try_dump(strip).expect("cannot dump function")
498 }
499
500 #[cfg(not(feature = "luau"))]
502 pub(crate) fn try_dump(&self, strip: bool) -> Result<Vec<u8>> {
503 unsafe extern "C-unwind" fn writer(
504 _state: *mut ffi::lua_State,
505 buf: *const c_void,
506 buf_len: usize,
507 data_ptr: *mut c_void,
508 ) -> c_int {
509 if !data_ptr.is_null() && buf_len > 0 {
511 let data = &mut *(data_ptr as *mut Vec<u8>);
512 let buf = slice::from_raw_parts(buf as *const u8, buf_len);
513 data.extend_from_slice(buf);
514 }
515 0
516 }
517
518 let lua = self.0.lua.lock();
519 let state = lua.state();
520 let mut data: Vec<u8> = Vec::new();
521 unsafe {
522 let _sg = StackGuard::new(state);
523 let protect = cfg!(feature = "lua55") && !lua.unlikely_memory_error();
525 check_stack(state, if protect { 4 } else { 1 })?;
526
527 lua.push_ref(&self.0);
528 if ffi::lua_iscfunction(state, -1) != 0 {
529 return Ok(data);
530 }
531 let data_ptr = &mut data as *mut Vec<u8> as *mut c_void;
532 let status = protect_lua_mem!(state, if protect, 1, 0, |state| {
533 ffi::lua_dump(state, writer, data_ptr, strip as i32)
534 })?;
535 if status != 0 {
536 return Err(pop_error(state, status));
537 }
538 }
539
540 Ok(data)
541 }
542
543 #[cfg(any(feature = "luau", doc))]
552 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
553 pub fn coverage<F>(&self, func: F)
554 where
555 F: FnMut(CoverageInfo),
556 {
557 use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
558
559 unsafe extern "C-unwind" fn callback<F: FnMut(CoverageInfo)>(
560 data: *mut c_void,
561 function: *const std::os::raw::c_char,
562 line_defined: c_int,
563 depth: c_int,
564 hits: *const c_int,
565 size: usize,
566 ) {
567 let rust_callback = &*(data as *const RefCell<(F, std::thread::Result<()>)>);
568 if let Ok(mut rust_callback) = rust_callback.try_borrow_mut() {
569 let (func, result) = &mut *rust_callback;
570 if result.is_ok() {
571 *result = catch_unwind(AssertUnwindSafe(|| {
572 func(CoverageInfo {
573 function: ptr_to_lossy_str(function).map(|s| s.into_owned()),
574 line_defined,
575 depth,
576 hits: slice::from_raw_parts(hits, size).to_vec(),
577 });
578 }));
579 }
580 }
581 }
582
583 let lua = self.0.lua.lock();
584 let state = lua.state();
585 unsafe {
586 let _sg = StackGuard::new(state);
587 assert_stack(state, 1);
588
589 lua.push_ref(&self.0);
590 let func: RefCell<(F, std::thread::Result<()>)> = RefCell::new((func, Ok(())));
591 let func_ptr = &func as *const _ as *mut c_void;
592 ffi::lua_getcoverage(state, -1, func_ptr, callback::<F>);
593 func.into_inner().1.unwrap_or_else(|panic| resume_unwind(panic));
595 }
596 }
597
598 #[inline]
604 pub fn to_pointer(&self) -> *const c_void {
605 self.0.to_pointer()
606 }
607
608 #[cfg(any(feature = "luau", doc))]
614 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
615 pub fn deep_clone(&self) -> Result<Self> {
616 let lua = self.0.lua.lock();
617 let state = lua.state();
618 unsafe {
619 let _sg = StackGuard::new(state);
620 check_stack(state, 2)?;
621
622 lua.push_ref(&self.0);
623 if ffi::lua_iscfunction(state, -1) != 0 {
624 return Ok(self.clone());
625 }
626
627 protect_lua_mem!(lua, 1, 1, fn(state) ffi::lua_clonefunction(state, -1))?;
628 Ok(Function(lua.try_pop_ref()?))
629 }
630 }
631}
632
633struct WrappedFunction(pub(crate) Callback);
634
635#[cfg(feature = "async")]
636struct WrappedAsyncFunction(pub(crate) AsyncCallback);
637
638impl Function {
639 #[inline]
642 pub fn wrap<F, A, R, E>(func: F) -> impl IntoLua
643 where
644 F: LuaNativeFn<A, Output = StdResult<R, E>> + MaybeSend + 'static,
645 A: FromLuaMulti,
646 R: IntoLuaMulti,
647 E: ExternalError,
648 {
649 WrappedFunction(Box::new(move |lua, nargs| unsafe {
650 let args = A::from_stack_args(nargs, 1, None, lua)?;
651 func.call(args).into_lua_err()?.push_into_stack_multi(lua)
652 }))
653 }
654
655 pub fn wrap_mut<F, A, R, E>(func: F) -> impl IntoLua
657 where
658 F: LuaNativeFnMut<A, Output = StdResult<R, E>> + MaybeSend + 'static,
659 A: FromLuaMulti,
660 R: IntoLuaMulti,
661 E: ExternalError,
662 {
663 let func = RefCell::new(func);
664 WrappedFunction(Box::new(move |lua, nargs| unsafe {
665 let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
666 let args = A::from_stack_args(nargs, 1, None, lua)?;
667 func.call(args).into_lua_err()?.push_into_stack_multi(lua)
668 }))
669 }
670
671 #[inline]
677 pub fn wrap_raw<F, A>(func: F) -> impl IntoLua
678 where
679 F: LuaNativeFn<A> + MaybeSend + 'static,
680 F::Output: IntoLuaMulti,
681 A: FromLuaMulti,
682 {
683 WrappedFunction(Box::new(move |lua, nargs| unsafe {
684 let args = A::from_stack_args(nargs, 1, None, lua)?;
685 func.call(args).push_into_stack_multi(lua)
686 }))
687 }
688
689 #[inline]
694 pub fn wrap_raw_mut<F, A>(func: F) -> impl IntoLua
695 where
696 F: LuaNativeFnMut<A> + MaybeSend + 'static,
697 F::Output: IntoLuaMulti,
698 A: FromLuaMulti,
699 {
700 let func = RefCell::new(func);
701 WrappedFunction(Box::new(move |lua, nargs| unsafe {
702 let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
703 let args = A::from_stack_args(nargs, 1, None, lua)?;
704 func.call(args).push_into_stack_multi(lua)
705 }))
706 }
707
708 #[cfg(feature = "async")]
711 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
712 pub fn wrap_async<F, A, R, E>(func: F) -> impl IntoLua
713 where
714 F: LuaNativeAsyncFn<A, Output = StdResult<R, E>> + MaybeSend + 'static,
715 A: FromLuaMulti,
716 R: IntoLuaMulti,
717 E: ExternalError,
718 {
719 WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
720 let args = match A::from_stack_args(nargs, 1, None, rawlua) {
721 Ok(args) => args,
722 Err(e) => return Box::pin(future::ready(Err(e))),
723 };
724 let lua = rawlua.lua();
725 let fut = func.call(args);
726 Box::pin(async move { fut.await.into_lua_err()?.push_into_stack_multi(lua.raw_lua()) })
727 }))
728 }
729
730 #[cfg(feature = "async")]
736 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
737 pub fn wrap_raw_async<F, A>(func: F) -> impl IntoLua
738 where
739 F: LuaNativeAsyncFn<A> + MaybeSend + 'static,
740 F::Output: IntoLuaMulti,
741 A: FromLuaMulti,
742 {
743 WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
744 let args = match A::from_stack_args(nargs, 1, None, rawlua) {
745 Ok(args) => args,
746 Err(e) => return Box::pin(future::ready(Err(e))),
747 };
748 let lua = rawlua.lua();
749 let fut = func.call(args);
750 Box::pin(async move { fut.await.push_into_stack_multi(lua.raw_lua()) })
751 }))
752 }
753}
754
755impl IntoLua for WrappedFunction {
756 #[inline]
757 fn into_lua(self, lua: &Lua) -> Result<Value> {
758 lua.lock().create_callback(self.0).map(Value::Function)
759 }
760}
761
762#[cfg(feature = "async")]
763impl IntoLua for WrappedAsyncFunction {
764 #[inline]
765 fn into_lua(self, lua: &Lua) -> Result<Value> {
766 lua.lock().create_async_callback(self.0).map(Value::Function)
767 }
768}
769
770impl LuaType for Function {
771 const TYPE_ID: c_int = ffi::LUA_TFUNCTION;
772}
773
774#[cfg(feature = "async")]
776#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
777#[must_use = "futures do nothing unless you `.await` or poll them"]
778pub struct AsyncCallFuture<R: FromLuaMulti>(Result<AsyncThread<R>>);
779
780#[cfg(feature = "async")]
781impl<R: FromLuaMulti> AsyncCallFuture<R> {
782 pub(crate) fn error(err: Error) -> Self {
783 AsyncCallFuture(Err(err))
784 }
785}
786
787#[cfg(feature = "async")]
788impl<R: FromLuaMulti> Future for AsyncCallFuture<R> {
789 type Output = Result<R>;
790
791 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
792 let this = self.get_mut();
793 match &mut this.0 {
794 Ok(thread) => pin!(thread).poll(cx),
795 Err(err) => Poll::Ready(Err(err.clone())),
796 }
797 }
798}
799
800pub trait LuaNativeFn<A: FromLuaMulti> {
802 type Output;
803
804 fn call(&self, args: A) -> Self::Output;
805}
806
807pub trait LuaNativeFnMut<A: FromLuaMulti> {
809 type Output;
810
811 fn call(&mut self, args: A) -> Self::Output;
812}
813
814#[cfg(feature = "async")]
816#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
817pub trait LuaNativeAsyncFn<A: FromLuaMulti> {
818 type Output;
819
820 fn call(&self, args: A) -> impl Future<Output = Self::Output> + MaybeSend + 'static;
821}
822
823macro_rules! impl_lua_native_fn {
824 ($($A:ident),*) => {
825 impl<FN, $($A,)* R> LuaNativeFn<($($A,)*)> for FN
826 where
827 FN: Fn($($A,)*) -> R + MaybeSend + 'static,
828 ($($A,)*): FromLuaMulti,
829 {
830 type Output = R;
831
832 #[allow(non_snake_case)]
833 fn call(&self, args: ($($A,)*)) -> Self::Output {
834 let ($($A,)*) = args;
835 self($($A,)*)
836 }
837 }
838
839 impl<FN, $($A,)* R> LuaNativeFnMut<($($A,)*)> for FN
840 where
841 FN: FnMut($($A,)*) -> R + MaybeSend + 'static,
842 ($($A,)*): FromLuaMulti,
843 {
844 type Output = R;
845
846 #[allow(non_snake_case)]
847 fn call(&mut self, args: ($($A,)*)) -> Self::Output {
848 let ($($A,)*) = args;
849 self($($A,)*)
850 }
851 }
852
853 #[cfg(feature = "async")]
854 impl<FN, $($A,)* Fut, R> LuaNativeAsyncFn<($($A,)*)> for FN
855 where
856 FN: Fn($($A,)*) -> Fut + MaybeSend + 'static,
857 ($($A,)*): FromLuaMulti,
858 Fut: Future<Output = R> + MaybeSend + 'static,
859 {
860 type Output = R;
861
862 #[allow(non_snake_case)]
863 fn call(&self, args: ($($A,)*)) -> impl Future<Output = Self::Output> + MaybeSend + 'static {
864 let ($($A,)*) = args;
865 self($($A,)*)
866 }
867 }
868 };
869}
870
871impl_lua_native_fn!();
872impl_lua_native_fn!(A);
873impl_lua_native_fn!(A, B);
874impl_lua_native_fn!(A, B, C);
875impl_lua_native_fn!(A, B, C, D);
876impl_lua_native_fn!(A, B, C, D, E);
877impl_lua_native_fn!(A, B, C, D, E, F);
878impl_lua_native_fn!(A, B, C, D, E, F, G);
879impl_lua_native_fn!(A, B, C, D, E, F, G, H);
880impl_lua_native_fn!(A, B, C, D, E, F, G, H, I);
881impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J);
882impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K);
883impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L);
884impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M);
885impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
886impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
887impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
888
889#[cfg(test)]
890mod assertions {
891 use super::*;
892
893 #[cfg(not(feature = "send"))]
894 static_assertions::assert_not_impl_any!(Function: Send);
895 #[cfg(feature = "send")]
896 static_assertions::assert_impl_all!(Function: Send, Sync);
897
898 #[cfg(all(feature = "async", feature = "send"))]
899 static_assertions::assert_impl_all!(AsyncCallFuture<()>: Send);
900}