1use std::cell::RefCell;
2use std::os::raw::{c_int, c_void};
3use std::{mem, ptr, slice};
4
5use crate::error::{Error, Result};
6use crate::state::Lua;
7use crate::table::Table;
8use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut};
9use crate::types::{Callback, LuaType, MaybeSend, ValueRef};
10use crate::util::{
11 assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str, StackGuard,
12};
13use crate::value::Value;
14
15#[cfg(feature = "async")]
16use {
17 crate::thread::AsyncThread,
18 crate::traits::LuaNativeAsyncFn,
19 crate::types::AsyncCallback,
20 std::future::{self, Future},
21 std::pin::{pin, Pin},
22 std::task::{Context, Poll},
23};
24
25#[derive(Clone, Debug, PartialEq)]
27pub struct Function(pub(crate) ValueRef);
28
29#[derive(Clone, Debug)]
35pub struct FunctionInfo {
36 pub name: Option<String>,
38 pub name_what: Option<&'static str>,
42 pub what: &'static str,
45 pub source: Option<String>,
47 pub short_src: Option<String>,
49 pub line_defined: Option<usize>,
51 pub last_line_defined: Option<usize>,
53}
54
55#[cfg(any(feature = "luau", doc))]
57#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
58#[derive(Clone, Debug, PartialEq, Eq)]
59pub struct CoverageInfo {
60 pub function: Option<String>,
61 pub line_defined: i32,
62 pub depth: i32,
63 pub hits: Vec<i32>,
64}
65
66impl Function {
67 pub fn call<R: FromLuaMulti>(&self, args: impl IntoLuaMulti) -> Result<R> {
108 let lua = self.0.lua.lock();
109 let state = lua.state();
110 unsafe {
111 let _sg = StackGuard::new(state);
112 check_stack(state, 2)?;
113
114 lua.push_error_traceback();
116 let stack_start = ffi::lua_gettop(state);
117 lua.push_ref(&self.0);
119 let nargs = args.push_into_stack_multi(&lua)?;
120 let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start);
122 if ret != ffi::LUA_OK {
123 return Err(pop_error(state, ret));
124 }
125 let nresults = ffi::lua_gettop(state) - stack_start;
127 R::from_stack_multi(nresults, &lua)
128 }
129 }
130
131 #[cfg(feature = "async")]
159 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
160 pub fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
161 where
162 R: FromLuaMulti,
163 {
164 let lua = self.0.lua.lock();
165 AsyncCallFuture(unsafe {
166 lua.create_recycled_thread(self).and_then(|th| {
167 let mut th = th.into_async(args)?;
168 th.set_recyclable(true);
169 Ok(th)
170 })
171 })
172 }
173
174 pub fn bind(&self, args: impl IntoLuaMulti) -> Result<Function> {
202 unsafe extern "C-unwind" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
203 let nargs = ffi::lua_gettop(state);
204 let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int;
205 ffi::luaL_checkstack(state, nbinds, ptr::null());
206
207 for i in 0..nbinds {
208 ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 2));
209 }
210 if nargs > 0 {
211 ffi::lua_rotate(state, 1, nbinds);
212 }
213
214 nargs + nbinds
215 }
216
217 let lua = self.0.lua.lock();
218 let state = lua.state();
219
220 let args = args.into_lua_multi(lua.lua())?;
221 let nargs = args.len() as c_int;
222
223 if nargs == 0 {
224 return Ok(self.clone());
225 }
226
227 if nargs + 1 > ffi::LUA_MAX_UPVALUES {
228 return Err(Error::BindError);
229 }
230
231 let args_wrapper = unsafe {
232 let _sg = StackGuard::new(state);
233 check_stack(state, nargs + 3)?;
234
235 ffi::lua_pushinteger(state, nargs as ffi::lua_Integer);
236 for arg in &args {
237 lua.push_value(arg)?;
238 }
239 protect_lua!(state, nargs + 1, 1, fn(state) {
240 ffi::lua_pushcclosure(state, args_wrapper_impl, ffi::lua_gettop(state));
241 })?;
242
243 Function(lua.pop_ref())
244 };
245
246 let lua = lua.lua();
247 lua.load(
248 r#"
249 local func, args_wrapper = ...
250 return function(...)
251 return func(args_wrapper(...))
252 end
253 "#,
254 )
255 .try_cache()
256 .set_name("=__mlua_bind")
257 .call((self, args_wrapper))
258 }
259
260 pub fn environment(&self) -> Option<Table> {
266 let lua = self.0.lua.lock();
267 let state = lua.state();
268 unsafe {
269 let _sg = StackGuard::new(state);
270 assert_stack(state, 1);
271
272 lua.push_ref(&self.0);
273 if ffi::lua_iscfunction(state, -1) != 0 {
274 return None;
275 }
276
277 #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
278 ffi::lua_getfenv(state, -1);
279 #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
280 for i in 1..=255 {
281 match ffi::lua_getupvalue(state, -1, i) {
283 s if s.is_null() => break,
284 s if std::ffi::CStr::from_ptr(s as _) == c"_ENV" => break,
285 _ => ffi::lua_pop(state, 1),
286 }
287 }
288
289 if ffi::lua_type(state, -1) != ffi::LUA_TTABLE {
290 return None;
291 }
292 Some(Table(lua.pop_ref()))
293 }
294 }
295
296 pub fn set_environment(&self, env: Table) -> Result<bool> {
303 let lua = self.0.lua.lock();
304 let state = lua.state();
305 unsafe {
306 let _sg = StackGuard::new(state);
307 check_stack(state, 2)?;
308
309 lua.push_ref(&self.0);
310 if ffi::lua_iscfunction(state, -1) != 0 {
311 return Ok(false);
312 }
313
314 #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
315 {
316 lua.push_ref(&env.0);
317 ffi::lua_setfenv(state, -2);
318 }
319 #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
320 for i in 1..=255 {
321 match ffi::lua_getupvalue(state, -1, i) {
322 s if s.is_null() => return Ok(false),
323 s if std::ffi::CStr::from_ptr(s as _) == c"_ENV" => {
324 ffi::lua_pop(state, 1);
325 let f_with_env = lua
327 .lua()
328 .load("return _ENV")
329 .set_environment(env)
330 .try_cache()
331 .into_function()?;
332 lua.push_ref(&f_with_env.0);
333 ffi::lua_upvaluejoin(state, -2, i, -1, 1);
334 break;
335 }
336 _ => ffi::lua_pop(state, 1),
337 }
338 }
339
340 Ok(true)
341 }
342 }
343
344 pub fn info(&self) -> FunctionInfo {
350 let lua = self.0.lua.lock();
351 let state = lua.state();
352 unsafe {
353 let _sg = StackGuard::new(state);
354 assert_stack(state, 1);
355
356 let mut ar: ffi::lua_Debug = mem::zeroed();
357 lua.push_ref(&self.0);
358 #[cfg(not(feature = "luau"))]
359 let res = ffi::lua_getinfo(state, cstr!(">Sn"), &mut ar);
360 #[cfg(feature = "luau")]
361 let res = ffi::lua_getinfo(state, -1, cstr!("sn"), &mut ar);
362 mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
363
364 FunctionInfo {
365 name: ptr_to_lossy_str(ar.name).map(|s| s.into_owned()),
366 #[cfg(not(feature = "luau"))]
367 name_what: match ptr_to_str(ar.namewhat) {
368 Some("") => None,
369 val => val,
370 },
371 #[cfg(feature = "luau")]
372 name_what: None,
373 what: ptr_to_str(ar.what).unwrap_or("main"),
374 source: ptr_to_lossy_str(ar.source).map(|s| s.into_owned()),
375 #[cfg(not(feature = "luau"))]
376 short_src: ptr_to_lossy_str(ar.short_src.as_ptr()).map(|s| s.into_owned()),
377 #[cfg(feature = "luau")]
378 short_src: ptr_to_lossy_str(ar.short_src).map(|s| s.into_owned()),
379 line_defined: linenumber_to_usize(ar.linedefined),
380 #[cfg(not(feature = "luau"))]
381 last_line_defined: linenumber_to_usize(ar.lastlinedefined),
382 #[cfg(feature = "luau")]
383 last_line_defined: None,
384 }
385 }
386 }
387
388 #[cfg(not(feature = "luau"))]
397 #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
398 pub fn dump(&self, strip: bool) -> Vec<u8> {
399 unsafe extern "C-unwind" fn writer(
400 _state: *mut ffi::lua_State,
401 buf: *const c_void,
402 buf_len: usize,
403 data: *mut c_void,
404 ) -> c_int {
405 let data = &mut *(data as *mut Vec<u8>);
406 let buf = slice::from_raw_parts(buf as *const u8, buf_len);
407 data.extend_from_slice(buf);
408 0
409 }
410
411 let lua = self.0.lua.lock();
412 let state = lua.state();
413 let mut data: Vec<u8> = Vec::new();
414 unsafe {
415 let _sg = StackGuard::new(state);
416 assert_stack(state, 1);
417
418 lua.push_ref(&self.0);
419 let data_ptr = &mut data as *mut Vec<u8> as *mut c_void;
420 ffi::lua_dump(state, writer, data_ptr, strip as i32);
421 ffi::lua_pop(state, 1);
422 }
423
424 data
425 }
426
427 #[cfg(any(feature = "luau", doc))]
436 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
437 pub fn coverage<F>(&self, func: F)
438 where
439 F: FnMut(CoverageInfo),
440 {
441 use std::ffi::CStr;
442 use std::os::raw::c_char;
443
444 unsafe extern "C-unwind" fn callback<F: FnMut(CoverageInfo)>(
445 data: *mut c_void,
446 function: *const c_char,
447 line_defined: c_int,
448 depth: c_int,
449 hits: *const c_int,
450 size: usize,
451 ) {
452 let function = if !function.is_null() {
453 Some(CStr::from_ptr(function).to_string_lossy().to_string())
454 } else {
455 None
456 };
457 let rust_callback = &*(data as *const RefCell<F>);
458 if let Ok(mut rust_callback) = rust_callback.try_borrow_mut() {
459 rust_callback(CoverageInfo {
461 function,
462 line_defined,
463 depth,
464 hits: slice::from_raw_parts(hits, size).to_vec(),
465 });
466 }
467 }
468
469 let lua = self.0.lua.lock();
470 let state = lua.state();
471 unsafe {
472 let _sg = StackGuard::new(state);
473 assert_stack(state, 1);
474
475 lua.push_ref(&self.0);
476 let func = RefCell::new(func);
477 let func_ptr = &func as *const RefCell<F> as *mut c_void;
478 ffi::lua_getcoverage(state, -1, func_ptr, callback::<F>);
479 }
480 }
481
482 #[inline]
488 pub fn to_pointer(&self) -> *const c_void {
489 self.0.to_pointer()
490 }
491
492 #[cfg(any(feature = "luau", doc))]
498 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
499 pub fn deep_clone(&self) -> Result<Self> {
500 let lua = self.0.lua.lock();
501 let state = lua.state();
502 unsafe {
503 let _sg = StackGuard::new(state);
504 check_stack(state, 2)?;
505
506 lua.push_ref(&self.0);
507 if ffi::lua_iscfunction(state, -1) != 0 {
508 return Ok(self.clone());
509 }
510
511 if lua.unlikely_memory_error() {
512 ffi::lua_clonefunction(state, -1);
513 } else {
514 protect_lua!(state, 1, 1, fn(state) ffi::lua_clonefunction(state, -1))?;
515 }
516 Ok(Function(lua.pop_ref()))
517 }
518 }
519}
520
521struct WrappedFunction(pub(crate) Callback);
522
523#[cfg(feature = "async")]
524struct WrappedAsyncFunction(pub(crate) AsyncCallback);
525
526impl Function {
527 #[inline]
530 pub fn wrap<F, A, R>(func: F) -> impl IntoLua
531 where
532 F: LuaNativeFn<A, Output = Result<R>> + MaybeSend + 'static,
533 A: FromLuaMulti,
534 R: IntoLuaMulti,
535 {
536 WrappedFunction(Box::new(move |lua, nargs| unsafe {
537 let args = A::from_stack_args(nargs, 1, None, lua)?;
538 func.call(args)?.push_into_stack_multi(lua)
539 }))
540 }
541
542 pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua
544 where
545 F: LuaNativeFnMut<A, Output = Result<R>> + MaybeSend + 'static,
546 A: FromLuaMulti,
547 R: IntoLuaMulti,
548 {
549 let func = RefCell::new(func);
550 WrappedFunction(Box::new(move |lua, nargs| unsafe {
551 let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
552 let args = A::from_stack_args(nargs, 1, None, lua)?;
553 func.call(args)?.push_into_stack_multi(lua)
554 }))
555 }
556
557 #[inline]
563 pub fn wrap_raw<F, A>(func: F) -> impl IntoLua
564 where
565 F: LuaNativeFn<A> + MaybeSend + 'static,
566 A: FromLuaMulti,
567 {
568 WrappedFunction(Box::new(move |lua, nargs| unsafe {
569 let args = A::from_stack_args(nargs, 1, None, lua)?;
570 func.call(args).push_into_stack_multi(lua)
571 }))
572 }
573
574 #[inline]
579 pub fn wrap_raw_mut<F, A>(func: F) -> impl IntoLua
580 where
581 F: LuaNativeFnMut<A> + MaybeSend + 'static,
582 A: FromLuaMulti,
583 {
584 let func = RefCell::new(func);
585 WrappedFunction(Box::new(move |lua, nargs| unsafe {
586 let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
587 let args = A::from_stack_args(nargs, 1, None, lua)?;
588 func.call(args).push_into_stack_multi(lua)
589 }))
590 }
591
592 #[cfg(feature = "async")]
595 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
596 pub fn wrap_async<F, A, R>(func: F) -> impl IntoLua
597 where
598 F: LuaNativeAsyncFn<A, Output = Result<R>> + MaybeSend + 'static,
599 A: FromLuaMulti,
600 R: IntoLuaMulti,
601 {
602 WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
603 let args = match A::from_stack_args(nargs, 1, None, rawlua) {
604 Ok(args) => args,
605 Err(e) => return Box::pin(future::ready(Err(e))),
606 };
607 let lua = rawlua.lua();
608 let fut = func.call(args);
609 Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
610 }))
611 }
612
613 #[cfg(feature = "async")]
619 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
620 pub fn wrap_raw_async<F, A>(func: F) -> impl IntoLua
621 where
622 F: LuaNativeAsyncFn<A> + MaybeSend + 'static,
623 A: FromLuaMulti,
624 {
625 WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
626 let args = match A::from_stack_args(nargs, 1, None, rawlua) {
627 Ok(args) => args,
628 Err(e) => return Box::pin(future::ready(Err(e))),
629 };
630 let lua = rawlua.lua();
631 let fut = func.call(args);
632 Box::pin(async move { fut.await.push_into_stack_multi(lua.raw_lua()) })
633 }))
634 }
635}
636
637impl IntoLua for WrappedFunction {
638 #[inline]
639 fn into_lua(self, lua: &Lua) -> Result<Value> {
640 lua.lock().create_callback(self.0).map(Value::Function)
641 }
642}
643
644#[cfg(feature = "async")]
645impl IntoLua for WrappedAsyncFunction {
646 #[inline]
647 fn into_lua(self, lua: &Lua) -> Result<Value> {
648 lua.lock().create_async_callback(self.0).map(Value::Function)
649 }
650}
651
652impl LuaType for Function {
653 const TYPE_ID: c_int = ffi::LUA_TFUNCTION;
654}
655
656#[cfg(feature = "async")]
657#[must_use = "futures do nothing unless you `.await` or poll them"]
658pub struct AsyncCallFuture<R: FromLuaMulti>(Result<AsyncThread<R>>);
659
660#[cfg(feature = "async")]
661impl<R: FromLuaMulti> AsyncCallFuture<R> {
662 pub(crate) fn error(err: Error) -> Self {
663 AsyncCallFuture(Err(err))
664 }
665}
666
667#[cfg(feature = "async")]
668impl<R: FromLuaMulti> Future for AsyncCallFuture<R> {
669 type Output = Result<R>;
670
671 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
672 let this = self.get_mut();
673 match &mut this.0 {
674 Ok(thread) => pin!(thread).poll(cx),
675 Err(err) => Poll::Ready(Err(err.clone())),
676 }
677 }
678}
679
680#[cfg(test)]
681mod assertions {
682 use super::*;
683
684 #[cfg(not(feature = "send"))]
685 static_assertions::assert_not_impl_any!(Function: Send);
686 #[cfg(feature = "send")]
687 static_assertions::assert_impl_all!(Function: Send, Sync);
688
689 #[cfg(all(feature = "async", feature = "send"))]
690 static_assertions::assert_impl_all!(AsyncCallFuture<()>: Send);
691}