diff --git a/src/function.rs b/src/function.rs index 6341cec..d1878d7 100644 --- a/src/function.rs +++ b/src/function.rs @@ -1,10 +1,13 @@ +use std::mem; use std::os::raw::c_int; use std::ptr; use crate::error::{Error, Result}; use crate::ffi; use crate::types::LuaRef; -use crate::util::{assert_stack, check_stack, error_traceback, pop_error, StackGuard}; +use crate::util::{ + assert_stack, check_stack, error_traceback, pop_error, ptr_to_cstr_bytes, StackGuard, +}; use crate::value::{FromLuaMulti, ToLuaMulti}; #[cfg(feature = "async")] @@ -14,6 +17,18 @@ use {futures_core::future::LocalBoxFuture, futures_util::future}; #[derive(Clone, Debug)] pub struct Function<'lua>(pub(crate) LuaRef<'lua>); +#[derive(Clone, Debug)] +pub struct FunctionInfo { + pub name: Option>, + pub name_what: Option>, + pub what: Option>, + pub source: Option>, + pub short_src: Option>, + pub line_defined: i32, + #[cfg(not(feature = "luau"))] + pub last_line_defined: i32, +} + impl<'lua> Function<'lua> { /// Calls the function, passing `args` as function arguments. /// @@ -209,6 +224,41 @@ impl<'lua> Function<'lua> { } } + /// Returns information about the function. + /// + /// Corresponds to the `>Sn` what mask for [`lua_getinfo`] when applied to the function. + /// + /// [`lua_getinfo`]: https://www.lua.org/manual/5.4/manual.html#lua_getinfo + pub fn info(&self) -> FunctionInfo { + let lua = self.0.lua; + unsafe { + let _sg = StackGuard::new(lua.state); + assert_stack(lua.state, 1); + + let mut ar: ffi::lua_Debug = mem::zeroed(); + lua.push_ref(&self.0); + #[cfg(not(feature = "luau"))] + let res = ffi::lua_getinfo(lua.state, cstr!(">Sn"), &mut ar); + #[cfg(feature = "luau")] + let res = ffi::lua_getinfo(lua.state, -1, cstr!("sn"), &mut ar); + mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`"); + + FunctionInfo { + name: ptr_to_cstr_bytes(ar.name).map(|s| s.to_vec()), + #[cfg(not(feature = "luau"))] + name_what: ptr_to_cstr_bytes(ar.namewhat).map(|s| s.to_vec()), + #[cfg(feature = "luau")] + name_what: None, + what: ptr_to_cstr_bytes(ar.what).map(|s| s.to_vec()), + source: ptr_to_cstr_bytes(ar.source).map(|s| s.to_vec()), + short_src: ptr_to_cstr_bytes(&ar.short_src as *const _).map(|s| s.to_vec()), + line_defined: ar.linedefined as i32, + #[cfg(not(feature = "luau"))] + last_line_defined: ar.lastlinedefined as i32, + } + } + } + /// Dumps the function as a binary chunk. /// /// If `strip` is true, the binary representation may not include all debug information diff --git a/src/hook.rs b/src/hook.rs index 44d4136..c9e3714 100644 --- a/src/hook.rs +++ b/src/hook.rs @@ -1,11 +1,11 @@ use std::cell::UnsafeCell; -use std::ffi::CStr; #[cfg(not(feature = "luau"))] use std::ops::{BitOr, BitOrAssign}; -use std::os::raw::{c_char, c_int}; +use std::os::raw::c_int; use crate::ffi::{self, lua_Debug}; use crate::lua::Lua; +use crate::util::ptr_to_cstr_bytes; /// Contains information about currently executing Lua code. /// @@ -77,9 +77,9 @@ impl<'lua> Debug<'lua> { ); DebugNames { - name: ptr_to_str((*self.ar.get()).name), + name: ptr_to_cstr_bytes((*self.ar.get()).name), #[cfg(not(feature = "luau"))] - name_what: ptr_to_str((*self.ar.get()).namewhat), + name_what: ptr_to_cstr_bytes((*self.ar.get()).namewhat), #[cfg(feature = "luau")] name_what: None, } @@ -101,12 +101,12 @@ impl<'lua> Debug<'lua> { ); DebugSource { - source: ptr_to_str((*self.ar.get()).source), - short_src: ptr_to_str((*self.ar.get()).short_src.as_ptr()), + source: ptr_to_cstr_bytes((*self.ar.get()).source), + short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src.as_ptr()), line_defined: (*self.ar.get()).linedefined as i32, #[cfg(not(feature = "luau"))] last_line_defined: (*self.ar.get()).lastlinedefined as i32, - what: ptr_to_str((*self.ar.get()).what), + what: ptr_to_cstr_bytes((*self.ar.get()).what), } } } @@ -349,11 +349,3 @@ impl BitOrAssign for HookTriggers { *self = *self | rhs; } } - -unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a [u8]> { - if input.is_null() { - None - } else { - Some(CStr::from_ptr(input).to_bytes()) - } -} diff --git a/src/lib.rs b/src/lib.rs index 50b2553..79bc9cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,7 +108,7 @@ pub use crate::{ffi::lua_CFunction, ffi::lua_State}; pub use crate::chunk::{AsChunk, Chunk, ChunkMode}; pub use crate::error::{Error, ExternalError, ExternalResult, Result}; -pub use crate::function::Function; +pub use crate::function::{Function, FunctionInfo}; pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack}; pub use crate::lua::{GCMode, Lua, LuaOptions}; pub use crate::multi::Variadic; diff --git a/src/prelude.rs b/src/prelude.rs index 1dea012..308f352 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -4,13 +4,13 @@ pub use crate::{ AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError, ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti, - Function as LuaFunction, GCMode as LuaGCMode, Integer as LuaInteger, - LightUserData as LuaLightUserData, Lua, LuaOptions, MetaMethod as LuaMetaMethod, - MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey, - Result as LuaResult, StdLib as LuaStdLib, String as LuaString, Table as LuaTable, - TableExt as LuaTableExt, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, - Thread as LuaThread, ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti, - UserData as LuaUserData, UserDataFields as LuaUserDataFields, + Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode, + Integer as LuaInteger, LightUserData as LuaLightUserData, Lua, LuaOptions, + MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, + RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib, String as LuaString, + Table as LuaTable, TableExt as LuaTableExt, TablePairs as LuaTablePairs, + TableSequence as LuaTableSequence, Thread as LuaThread, ThreadStatus as LuaThreadStatus, ToLua, + ToLuaMulti, UserData as LuaUserData, UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable, UserDataMethods as LuaUserDataMethods, Value as LuaValue, }; diff --git a/src/util.rs b/src/util.rs index 66a60db..063a5c6 100644 --- a/src/util.rs +++ b/src/util.rs @@ -992,6 +992,13 @@ pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_Stat ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key); } +pub(crate) unsafe fn ptr_to_cstr_bytes<'a>(input: *const c_char) -> Option<&'a [u8]> { + if input.is_null() { + return None; + } + Some(CStr::from_ptr(input).to_bytes()) +} + static DESTRUCTED_USERDATA_METATABLE: u8 = 0; static ERROR_PRINT_BUFFER_KEY: u8 = 0; static USERDATA_METATABLE_INDEX: u8 = 0; diff --git a/tests/function.rs b/tests/function.rs index f245c37..4045d50 100644 --- a/tests/function.rs +++ b/tests/function.rs @@ -107,3 +107,57 @@ fn test_dump() -> Result<()> { Ok(()) } + +#[test] +fn test_function_info() -> Result<()> { + let lua = Lua::new(); + + let globals = lua.globals(); + lua.load( + r#" + function function1() + return function() end + end + "#, + ) + .set_name("source1")? + .exec()?; + + let function1 = globals.get::<_, Function>("function1")?; + let function2 = function1.call::<_, Function>(())?; + let function3 = lua.create_function(|_, ()| Ok(()))?; + + let function1_info = function1.info(); + #[cfg(feature = "luau")] + assert_eq!(function1_info.name, Some(b"function1".to_vec())); + assert_eq!(function1_info.source, Some(b"source1".to_vec())); + assert_eq!(function1_info.line_defined, 2); + #[cfg(not(feature = "luau"))] + assert_eq!(function1_info.last_line_defined, 4); + assert_eq!(function1_info.what, Some(b"Lua".to_vec())); + + let function2_info = function2.info(); + assert_eq!(function2_info.name, None); + assert_eq!(function2_info.source, Some(b"source1".to_vec())); + assert_eq!(function2_info.line_defined, 3); + #[cfg(not(feature = "luau"))] + assert_eq!(function2_info.last_line_defined, 3); + assert_eq!(function2_info.what, Some(b"Lua".to_vec())); + + let function3_info = function3.info(); + assert_eq!(function3_info.name, None); + assert_eq!(function3_info.source, Some(b"=[C]".to_vec())); + assert_eq!(function3_info.line_defined, -1); + #[cfg(not(feature = "luau"))] + assert_eq!(function3_info.last_line_defined, -1); + assert_eq!(function3_info.what, Some(b"C".to_vec())); + + let print_info = globals.get::<_, Function>("print")?.info(); + #[cfg(feature = "luau")] + assert_eq!(print_info.name, Some(b"print".to_vec())); + assert_eq!(print_info.source, Some(b"=[C]".to_vec())); + assert_eq!(print_info.what, Some(b"C".to_vec())); + assert_eq!(print_info.line_defined, -1); + + Ok(()) +}