1use std::cell::RefCell;
83use std::os::raw::{c_int, c_void};
84use std::{mem, ptr, slice};
85
86use crate::error::{Error, Result};
87use crate::state::Lua;
88use crate::table::Table;
89use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut};
90use crate::types::{Callback, LuaType, MaybeSend, ValueRef};
91use crate::util::{
92 StackGuard, assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str,
93};
94use crate::value::Value;
95
96#[cfg(feature = "async")]
97use {
98 crate::thread::AsyncThread,
99 crate::traits::LuaNativeAsyncFn,
100 crate::types::AsyncCallback,
101 std::future::{self, Future},
102 std::pin::{Pin, pin},
103 std::task::{Context, Poll},
104};
105
106#[derive(Clone, Debug, PartialEq)]
108pub struct Function(pub(crate) ValueRef);
109
110#[derive(Clone, Debug)]
116#[non_exhaustive]
117pub struct FunctionInfo {
118 pub name: Option<String>,
120 pub name_what: Option<&'static str>,
124 pub what: &'static str,
127 pub source: Option<String>,
129 pub short_src: Option<String>,
131 pub line_defined: Option<usize>,
133 pub last_line_defined: Option<usize>,
135 pub num_upvalues: u8,
137 #[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))]
139 #[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))]
140 pub num_params: u8,
141 #[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))]
143 #[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))]
144 pub is_vararg: bool,
145}
146
147#[cfg(any(feature = "luau", doc))]
149#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
150#[derive(Clone, Debug, PartialEq, Eq)]
151pub struct CoverageInfo {
152 pub function: Option<String>,
153 pub line_defined: i32,
154 pub depth: i32,
155 pub hits: Vec<i32>,
156}
157
158impl Function {
159 pub fn call<R: FromLuaMulti>(&self, args: impl IntoLuaMulti) -> Result<R> {
200 let lua = self.0.lua.lock();
201 let state = lua.state();
202 unsafe {
203 let _sg = StackGuard::new(state);
204 check_stack(state, 2)?;
205
206 lua.push_error_traceback();
208 let stack_start = ffi::lua_gettop(state);
209 lua.push_ref(&self.0);
211 let nargs = args.push_into_stack_multi(&lua)?;
212 let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start);
214 if ret != ffi::LUA_OK {
215 return Err(pop_error(state, ret));
216 }
217 let nresults = ffi::lua_gettop(state) - stack_start;
219 R::from_stack_multi(nresults, &lua)
220 }
221 }
222
223 #[cfg(feature = "async")]
251 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
252 pub fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
253 where
254 R: FromLuaMulti,
255 {
256 let lua = self.0.lua.lock();
257 AsyncCallFuture(unsafe {
258 lua.create_recycled_thread(self).and_then(|th| {
259 let mut th = th.into_async(args)?;
260 th.set_recyclable(true);
261 Ok(th)
262 })
263 })
264 }
265
266 pub fn bind(&self, args: impl IntoLuaMulti) -> Result<Function> {
294 unsafe extern "C-unwind" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
295 let nargs = ffi::lua_gettop(state);
296 let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int;
297 ffi::luaL_checkstack(state, nbinds, ptr::null());
298
299 for i in 0..nbinds {
300 ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 2));
301 }
302 if nargs > 0 {
303 ffi::lua_rotate(state, 1, nbinds);
304 }
305
306 nargs + nbinds
307 }
308
309 let lua = self.0.lua.lock();
310 let state = lua.state();
311
312 let args = args.into_lua_multi(lua.lua())?;
313 let nargs = args.len() as c_int;
314
315 if nargs == 0 {
316 return Ok(self.clone());
317 }
318
319 if nargs + 1 > ffi::LUA_MAX_UPVALUES {
320 return Err(Error::BindError);
321 }
322
323 let args_wrapper = unsafe {
324 let _sg = StackGuard::new(state);
325 check_stack(state, nargs + 3)?;
326
327 ffi::lua_pushinteger(state, nargs as ffi::lua_Integer);
328 for arg in &args {
329 lua.push_value(arg)?;
330 }
331 protect_lua!(state, nargs + 1, 1, fn(state) {
332 ffi::lua_pushcclosure(state, args_wrapper_impl, ffi::lua_gettop(state));
333 })?;
334
335 Function(lua.pop_ref())
336 };
337
338 let lua = lua.lua();
339 lua.load(
340 r#"
341 local func, args_wrapper = ...
342 return function(...)
343 return func(args_wrapper(...))
344 end
345 "#,
346 )
347 .try_cache()
348 .set_name("=__mlua_bind")
349 .call((self, args_wrapper))
350 }
351
352 pub fn environment(&self) -> Option<Table> {
358 let lua = self.0.lua.lock();
359 let state = lua.state();
360 unsafe {
361 let _sg = StackGuard::new(state);
362 assert_stack(state, 1);
363
364 lua.push_ref(&self.0);
365 if ffi::lua_iscfunction(state, -1) != 0 {
366 return None;
367 }
368
369 #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
370 ffi::lua_getfenv(state, -1);
371 #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
372 for i in 1..=255 {
373 match ffi::lua_getupvalue(state, -1, i) {
375 s if s.is_null() => break,
376 s if std::ffi::CStr::from_ptr(s as _) == c"_ENV" => break,
377 _ => ffi::lua_pop(state, 1),
378 }
379 }
380
381 if ffi::lua_type(state, -1) != ffi::LUA_TTABLE {
382 return None;
383 }
384 Some(Table(lua.pop_ref()))
385 }
386 }
387
388 pub fn set_environment(&self, env: Table) -> Result<bool> {
395 let lua = self.0.lua.lock();
396 let state = lua.state();
397 unsafe {
398 let _sg = StackGuard::new(state);
399 check_stack(state, 2)?;
400
401 lua.push_ref(&self.0);
402 if ffi::lua_iscfunction(state, -1) != 0 {
403 return Ok(false);
404 }
405
406 #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
407 {
408 lua.push_ref(&env.0);
409 ffi::lua_setfenv(state, -2);
410 }
411 #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
412 for i in 1..=255 {
413 match ffi::lua_getupvalue(state, -1, i) {
414 s if s.is_null() => return Ok(false),
415 s if std::ffi::CStr::from_ptr(s as _) == c"_ENV" => {
416 ffi::lua_pop(state, 1);
417 let f_with_env = lua
419 .lua()
420 .load("return _ENV")
421 .set_environment(env)
422 .try_cache()
423 .into_function()?;
424 lua.push_ref(&f_with_env.0);
425 ffi::lua_upvaluejoin(state, -2, i, -1, 1);
426 break;
427 }
428 _ => ffi::lua_pop(state, 1),
429 }
430 }
431
432 Ok(true)
433 }
434 }
435
436 pub fn info(&self) -> FunctionInfo {
443 let lua = self.0.lua.lock();
444 let state = lua.state();
445 unsafe {
446 let _sg = StackGuard::new(state);
447 assert_stack(state, 1);
448
449 let mut ar: ffi::lua_Debug = mem::zeroed();
450 lua.push_ref(&self.0);
451
452 #[cfg(not(feature = "luau"))]
453 let res = ffi::lua_getinfo(state, cstr!(">Snu"), &mut ar);
454 #[cfg(not(feature = "luau"))]
455 mlua_assert!(res != 0, "lua_getinfo failed with `>Snu`");
456
457 #[cfg(feature = "luau")]
458 let res = ffi::lua_getinfo(state, -1, cstr!("snau"), &mut ar);
459 #[cfg(feature = "luau")]
460 mlua_assert!(res != 0, "lua_getinfo failed with `snau`");
461
462 FunctionInfo {
463 name: ptr_to_lossy_str(ar.name).map(|s| s.into_owned()),
464 #[cfg(not(feature = "luau"))]
465 name_what: match ptr_to_str(ar.namewhat) {
466 Some("") => None,
467 val => val,
468 },
469 #[cfg(feature = "luau")]
470 name_what: None,
471 what: ptr_to_str(ar.what).unwrap_or("main"),
472 source: ptr_to_lossy_str(ar.source).map(|s| s.into_owned()),
473 #[cfg(not(feature = "luau"))]
474 short_src: ptr_to_lossy_str(ar.short_src.as_ptr()).map(|s| s.into_owned()),
475 #[cfg(feature = "luau")]
476 short_src: ptr_to_lossy_str(ar.short_src).map(|s| s.into_owned()),
477 line_defined: linenumber_to_usize(ar.linedefined),
478 #[cfg(not(feature = "luau"))]
479 last_line_defined: linenumber_to_usize(ar.lastlinedefined),
480 #[cfg(feature = "luau")]
481 last_line_defined: None,
482 #[cfg(not(feature = "luau"))]
483 num_upvalues: ar.nups as _,
484 #[cfg(feature = "luau")]
485 num_upvalues: ar.nupvals,
486 #[cfg(not(any(feature = "lua51", feature = "luajit")))]
487 num_params: ar.nparams,
488 #[cfg(not(any(feature = "lua51", feature = "luajit")))]
489 is_vararg: ar.isvararg != 0,
490 }
491 }
492 }
493
494 #[cfg(not(feature = "luau"))]
503 #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
504 pub fn dump(&self, strip: bool) -> Vec<u8> {
505 unsafe extern "C-unwind" fn writer(
506 _state: *mut ffi::lua_State,
507 buf: *const c_void,
508 buf_len: usize,
509 data_ptr: *mut c_void,
510 ) -> c_int {
511 if !data_ptr.is_null() && buf_len > 0 {
513 let data = &mut *(data_ptr as *mut Vec<u8>);
514 let buf = slice::from_raw_parts(buf as *const u8, buf_len);
515 data.extend_from_slice(buf);
516 }
517 0
518 }
519
520 let lua = self.0.lua.lock();
521 let state = lua.state();
522 let mut data: Vec<u8> = Vec::new();
523 unsafe {
524 let _sg = StackGuard::new(state);
525 assert_stack(state, 1);
526
527 lua.push_ref(&self.0);
528 let data_ptr = &mut data as *mut Vec<u8> as *mut c_void;
529 ffi::lua_dump(state, writer, data_ptr, strip as i32);
530 ffi::lua_pop(state, 1);
531 }
532
533 data
534 }
535
536 #[cfg(any(feature = "luau", doc))]
545 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
546 pub fn coverage<F>(&self, func: F)
547 where
548 F: FnMut(CoverageInfo),
549 {
550 use std::ffi::CStr;
551 use std::os::raw::c_char;
552
553 unsafe extern "C-unwind" fn callback<F: FnMut(CoverageInfo)>(
554 data: *mut c_void,
555 function: *const c_char,
556 line_defined: c_int,
557 depth: c_int,
558 hits: *const c_int,
559 size: usize,
560 ) {
561 let function = if !function.is_null() {
562 Some(CStr::from_ptr(function).to_string_lossy().to_string())
563 } else {
564 None
565 };
566 let rust_callback = &*(data as *const RefCell<F>);
567 if let Ok(mut rust_callback) = rust_callback.try_borrow_mut() {
568 rust_callback(CoverageInfo {
570 function,
571 line_defined,
572 depth,
573 hits: slice::from_raw_parts(hits, size).to_vec(),
574 });
575 }
576 }
577
578 let lua = self.0.lua.lock();
579 let state = lua.state();
580 unsafe {
581 let _sg = StackGuard::new(state);
582 assert_stack(state, 1);
583
584 lua.push_ref(&self.0);
585 let func = RefCell::new(func);
586 let func_ptr = &func as *const RefCell<F> as *mut c_void;
587 ffi::lua_getcoverage(state, -1, func_ptr, callback::<F>);
588 }
589 }
590
591 #[inline]
597 pub fn to_pointer(&self) -> *const c_void {
598 self.0.to_pointer()
599 }
600
601 #[cfg(any(feature = "luau", doc))]
607 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
608 pub fn deep_clone(&self) -> Result<Self> {
609 let lua = self.0.lua.lock();
610 let state = lua.state();
611 unsafe {
612 let _sg = StackGuard::new(state);
613 check_stack(state, 2)?;
614
615 lua.push_ref(&self.0);
616 if ffi::lua_iscfunction(state, -1) != 0 {
617 return Ok(self.clone());
618 }
619
620 if lua.unlikely_memory_error() {
621 ffi::lua_clonefunction(state, -1);
622 } else {
623 protect_lua!(state, 1, 1, fn(state) ffi::lua_clonefunction(state, -1))?;
624 }
625 Ok(Function(lua.pop_ref()))
626 }
627 }
628}
629
630struct WrappedFunction(pub(crate) Callback);
631
632#[cfg(feature = "async")]
633struct WrappedAsyncFunction(pub(crate) AsyncCallback);
634
635impl Function {
636 #[inline]
639 pub fn wrap<F, A, R>(func: F) -> impl IntoLua
640 where
641 F: LuaNativeFn<A, Output = Result<R>> + MaybeSend + 'static,
642 A: FromLuaMulti,
643 R: IntoLuaMulti,
644 {
645 WrappedFunction(Box::new(move |lua, nargs| unsafe {
646 let args = A::from_stack_args(nargs, 1, None, lua)?;
647 func.call(args)?.push_into_stack_multi(lua)
648 }))
649 }
650
651 pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua
653 where
654 F: LuaNativeFnMut<A, Output = Result<R>> + MaybeSend + 'static,
655 A: FromLuaMulti,
656 R: IntoLuaMulti,
657 {
658 let func = RefCell::new(func);
659 WrappedFunction(Box::new(move |lua, nargs| unsafe {
660 let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
661 let args = A::from_stack_args(nargs, 1, None, lua)?;
662 func.call(args)?.push_into_stack_multi(lua)
663 }))
664 }
665
666 #[inline]
672 pub fn wrap_raw<F, A>(func: F) -> impl IntoLua
673 where
674 F: LuaNativeFn<A> + MaybeSend + 'static,
675 A: FromLuaMulti,
676 {
677 WrappedFunction(Box::new(move |lua, nargs| unsafe {
678 let args = A::from_stack_args(nargs, 1, None, lua)?;
679 func.call(args).push_into_stack_multi(lua)
680 }))
681 }
682
683 #[inline]
688 pub fn wrap_raw_mut<F, A>(func: F) -> impl IntoLua
689 where
690 F: LuaNativeFnMut<A> + MaybeSend + 'static,
691 A: FromLuaMulti,
692 {
693 let func = RefCell::new(func);
694 WrappedFunction(Box::new(move |lua, nargs| unsafe {
695 let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
696 let args = A::from_stack_args(nargs, 1, None, lua)?;
697 func.call(args).push_into_stack_multi(lua)
698 }))
699 }
700
701 #[cfg(feature = "async")]
704 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
705 pub fn wrap_async<F, A, R>(func: F) -> impl IntoLua
706 where
707 F: LuaNativeAsyncFn<A, Output = Result<R>> + MaybeSend + 'static,
708 A: FromLuaMulti,
709 R: IntoLuaMulti,
710 {
711 WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
712 let args = match A::from_stack_args(nargs, 1, None, rawlua) {
713 Ok(args) => args,
714 Err(e) => return Box::pin(future::ready(Err(e))),
715 };
716 let lua = rawlua.lua();
717 let fut = func.call(args);
718 Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
719 }))
720 }
721
722 #[cfg(feature = "async")]
728 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
729 pub fn wrap_raw_async<F, A>(func: F) -> impl IntoLua
730 where
731 F: LuaNativeAsyncFn<A> + MaybeSend + 'static,
732 A: FromLuaMulti,
733 {
734 WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
735 let args = match A::from_stack_args(nargs, 1, None, rawlua) {
736 Ok(args) => args,
737 Err(e) => return Box::pin(future::ready(Err(e))),
738 };
739 let lua = rawlua.lua();
740 let fut = func.call(args);
741 Box::pin(async move { fut.await.push_into_stack_multi(lua.raw_lua()) })
742 }))
743 }
744}
745
746impl IntoLua for WrappedFunction {
747 #[inline]
748 fn into_lua(self, lua: &Lua) -> Result<Value> {
749 lua.lock().create_callback(self.0).map(Value::Function)
750 }
751}
752
753#[cfg(feature = "async")]
754impl IntoLua for WrappedAsyncFunction {
755 #[inline]
756 fn into_lua(self, lua: &Lua) -> Result<Value> {
757 lua.lock().create_async_callback(self.0).map(Value::Function)
758 }
759}
760
761impl LuaType for Function {
762 const TYPE_ID: c_int = ffi::LUA_TFUNCTION;
763}
764
765#[cfg(feature = "async")]
767#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
768#[must_use = "futures do nothing unless you `.await` or poll them"]
769pub struct AsyncCallFuture<R: FromLuaMulti>(Result<AsyncThread<R>>);
770
771#[cfg(feature = "async")]
772impl<R: FromLuaMulti> AsyncCallFuture<R> {
773 pub(crate) fn error(err: Error) -> Self {
774 AsyncCallFuture(Err(err))
775 }
776}
777
778#[cfg(feature = "async")]
779impl<R: FromLuaMulti> Future for AsyncCallFuture<R> {
780 type Output = Result<R>;
781
782 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
783 let this = self.get_mut();
784 match &mut this.0 {
785 Ok(thread) => pin!(thread).poll(cx),
786 Err(err) => Poll::Ready(Err(err.clone())),
787 }
788 }
789}
790
791#[cfg(test)]
792mod assertions {
793 use super::*;
794
795 #[cfg(not(feature = "send"))]
796 static_assertions::assert_not_impl_any!(Function: Send);
797 #[cfg(feature = "send")]
798 static_assertions::assert_impl_all!(Function: Send, Sync);
799
800 #[cfg(all(feature = "async", feature = "send"))]
801 static_assertions::assert_impl_all!(AsyncCallFuture<()>: Send);
802}