Add Function::info() to get information about functions.

Closes #149 and #7.
This commit is contained in:
Alex Orlenko 2022-04-17 22:43:41 +01:00
parent 790df77965
commit 5133a9837a
No known key found for this signature in database
GPG key ID: 4C150C250863B96D
6 changed files with 127 additions and 24 deletions

View file

@ -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<Vec<u8>>,
pub name_what: Option<Vec<u8>>,
pub what: Option<Vec<u8>>,
pub source: Option<Vec<u8>>,
pub short_src: Option<Vec<u8>>,
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

View file

@ -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())
}
}

View file

@ -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;

View file

@ -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,
};

View file

@ -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;

View file

@ -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(())
}