Make Debug interface more user friendly
- use String instead of Vec<u8> - update docs - unify fields between lua5.x/luau - line numbers are `usize`
This commit is contained in:
parent
85f17a269d
commit
b3b8d79446
|
@ -10,7 +10,8 @@ use crate::memory::MemoryState;
|
|||
use crate::table::Table;
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, error_traceback, pop_error, ptr_to_cstr_bytes, StackGuard,
|
||||
assert_stack, check_stack, error_traceback, linenumber_to_usize, pop_error, ptr_to_lossy_str,
|
||||
ptr_to_str, StackGuard,
|
||||
};
|
||||
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
|
||||
|
@ -52,24 +53,22 @@ impl OwnedFunction {
|
|||
/// [`Lua Debug Interface`]: https://www.lua.org/manual/5.4/manual.html#4.7
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FunctionInfo {
|
||||
/// A (reasonable) name of the function.
|
||||
/// A (reasonable) name of the function (`None` if the name cannot be found).
|
||||
pub name: Option<String>,
|
||||
/// Explains the `name` field ("global", "local", "method", "field", "upvalue", or "").
|
||||
/// Explains the `name` field (can be `global`/`local`/`method`/`field`/`upvalue`/etc).
|
||||
///
|
||||
/// Always `None` for Luau.
|
||||
pub name_what: Option<String>,
|
||||
/// A string "Lua" if the function is a Lua function, "C" if it is a C function, "main" if it is the main part of a chunk.
|
||||
pub what: Option<String>,
|
||||
/// The source of the chunk that created the function.
|
||||
pub source: Option<Vec<u8>>,
|
||||
/// A "printable" version of source, to be used in error messages.
|
||||
pub short_src: Option<Vec<u8>>,
|
||||
pub name_what: Option<&'static str>,
|
||||
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is the main part of a chunk.
|
||||
pub what: &'static str,
|
||||
/// Source of the chunk that created the function.
|
||||
pub source: Option<String>,
|
||||
/// A "printable" version of `source`, to be used in error messages.
|
||||
pub short_src: Option<String>,
|
||||
/// The line number where the definition of the function starts.
|
||||
pub line_defined: i32,
|
||||
/// The line number where the definition of the function ends.
|
||||
///
|
||||
/// Always `-1` for Luau.
|
||||
pub last_line_defined: i32,
|
||||
pub line_defined: Option<usize>,
|
||||
/// The line number where the definition of the function ends (not set by Luau).
|
||||
pub last_line_defined: Option<usize>,
|
||||
}
|
||||
|
||||
/// Luau function coverage snapshot.
|
||||
|
@ -391,23 +390,25 @@ impl<'lua> Function<'lua> {
|
|||
mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
|
||||
|
||||
FunctionInfo {
|
||||
name: ptr_to_cstr_bytes(ar.name).map(|s| String::from_utf8_lossy(s).into_owned()),
|
||||
name: ptr_to_lossy_str(ar.name).map(|s| s.into_owned()),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
name_what: ptr_to_cstr_bytes(ar.namewhat)
|
||||
.map(|s| String::from_utf8_lossy(s).into_owned()),
|
||||
name_what: match ptr_to_str(ar.namewhat) {
|
||||
Some("") => None,
|
||||
val => val,
|
||||
},
|
||||
#[cfg(feature = "luau")]
|
||||
name_what: None,
|
||||
what: ptr_to_cstr_bytes(ar.what).map(|s| String::from_utf8_lossy(s).into_owned()),
|
||||
source: ptr_to_cstr_bytes(ar.source).map(|s| s.to_vec()),
|
||||
what: ptr_to_str(ar.what).unwrap_or("main"),
|
||||
source: ptr_to_lossy_str(ar.source).map(|s| s.into_owned()),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
short_src: ptr_to_cstr_bytes(ar.short_src.as_ptr()).map(|s| s.to_vec()),
|
||||
short_src: ptr_to_lossy_str(ar.short_src.as_ptr()).map(|s| s.into_owned()),
|
||||
#[cfg(feature = "luau")]
|
||||
short_src: ptr_to_cstr_bytes(ar.short_src).map(|s| s.to_vec()),
|
||||
line_defined: ar.linedefined,
|
||||
short_src: ptr_to_lossy_str(ar.short_src).map(|s| s.into_owned()),
|
||||
line_defined: linenumber_to_usize(ar.linedefined),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: ar.lastlinedefined,
|
||||
last_line_defined: linenumber_to_usize(ar.lastlinedefined),
|
||||
#[cfg(feature = "luau")]
|
||||
last_line_defined: -1,
|
||||
last_line_defined: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
48
src/hook.rs
48
src/hook.rs
|
@ -1,3 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::cell::UnsafeCell;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use std::ops::{BitOr, BitOrAssign};
|
||||
|
@ -6,7 +7,7 @@ use std::os::raw::c_int;
|
|||
use ffi::lua_Debug;
|
||||
|
||||
use crate::lua::Lua;
|
||||
use crate::util::ptr_to_cstr_bytes;
|
||||
use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
|
||||
|
||||
/// Contains information about currently executing Lua code.
|
||||
///
|
||||
|
@ -78,9 +79,12 @@ impl<'lua> Debug<'lua> {
|
|||
);
|
||||
|
||||
DebugNames {
|
||||
name: ptr_to_cstr_bytes((*self.ar.get()).name),
|
||||
name: ptr_to_lossy_str((*self.ar.get()).name),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
name_what: ptr_to_cstr_bytes((*self.ar.get()).namewhat),
|
||||
name_what: match ptr_to_str((*self.ar.get()).namewhat) {
|
||||
Some("") => None,
|
||||
val => val,
|
||||
},
|
||||
#[cfg(feature = "luau")]
|
||||
name_what: None,
|
||||
}
|
||||
|
@ -102,15 +106,17 @@ impl<'lua> Debug<'lua> {
|
|||
);
|
||||
|
||||
DebugSource {
|
||||
source: ptr_to_cstr_bytes((*self.ar.get()).source),
|
||||
source: ptr_to_lossy_str((*self.ar.get()).source),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src.as_ptr()),
|
||||
short_src: ptr_to_lossy_str((*self.ar.get()).short_src.as_ptr()),
|
||||
#[cfg(feature = "luau")]
|
||||
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src),
|
||||
line_defined: (*self.ar.get()).linedefined,
|
||||
short_src: ptr_to_lossy_str((*self.ar.get()).short_src),
|
||||
line_defined: linenumber_to_usize((*self.ar.get()).linedefined),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: (*self.ar.get()).lastlinedefined,
|
||||
what: ptr_to_cstr_bytes((*self.ar.get()).what),
|
||||
last_line_defined: linenumber_to_usize((*self.ar.get()).lastlinedefined),
|
||||
#[cfg(feature = "luau")]
|
||||
last_line_defined: None,
|
||||
what: ptr_to_str((*self.ar.get()).what).unwrap_or("main"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -210,18 +216,26 @@ pub enum DebugEvent {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DebugNames<'a> {
|
||||
pub name: Option<&'a [u8]>,
|
||||
pub name_what: Option<&'a [u8]>,
|
||||
/// A (reasonable) name of the function (`None` if the name cannot be found).
|
||||
pub name: Option<Cow<'a, str>>,
|
||||
/// Explains the `name` field (can be `global`/`local`/`method`/`field`/`upvalue`/etc).
|
||||
///
|
||||
/// Always `None` for Luau.
|
||||
pub name_what: Option<&'static str>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DebugSource<'a> {
|
||||
pub source: Option<&'a [u8]>,
|
||||
pub short_src: Option<&'a [u8]>,
|
||||
pub line_defined: i32,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub last_line_defined: i32,
|
||||
pub what: Option<&'a [u8]>,
|
||||
/// Source of the chunk that created the function.
|
||||
pub source: Option<Cow<'a, str>>,
|
||||
/// A "printable" version of `source`, to be used in error messages.
|
||||
pub short_src: Option<Cow<'a, str>>,
|
||||
/// The line number where the definition of the function starts.
|
||||
pub line_defined: Option<usize>,
|
||||
/// The line number where the definition of the function ends (not set by Luau).
|
||||
pub last_line_defined: Option<usize>,
|
||||
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is the main part of a chunk.
|
||||
pub what: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use std::any::{Any, TypeId};
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt::Write;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
use std::sync::Arc;
|
||||
use std::{mem, ptr, slice};
|
||||
use std::{mem, ptr, slice, str};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
@ -1066,11 +1067,25 @@ 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]> {
|
||||
pub(crate) unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a str> {
|
||||
if input.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(CStr::from_ptr(input).to_bytes())
|
||||
str::from_utf8(CStr::from_ptr(input).to_bytes()).ok()
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn ptr_to_lossy_str<'a>(input: *const c_char) -> Option<Cow<'a, str>> {
|
||||
if input.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(String::from_utf8_lossy(CStr::from_ptr(input).to_bytes()))
|
||||
}
|
||||
|
||||
pub(crate) fn linenumber_to_usize(n: c_int) -> Option<usize> {
|
||||
match n {
|
||||
n if n < 0 => None,
|
||||
n => Some(n as usize),
|
||||
}
|
||||
}
|
||||
|
||||
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
||||
|
|
|
@ -196,37 +196,37 @@ fn test_function_info() -> Result<()> {
|
|||
let function1_info = function1.info();
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(function1_info.name.as_deref(), Some("function1"));
|
||||
assert_eq!(function1_info.source.as_deref(), Some(b"source1".as_ref()));
|
||||
assert_eq!(function1_info.line_defined, 2);
|
||||
assert_eq!(function1_info.source.as_deref(), Some("source1"));
|
||||
assert_eq!(function1_info.line_defined, Some(2));
|
||||
#[cfg(not(feature = "luau"))]
|
||||
assert_eq!(function1_info.last_line_defined, 4);
|
||||
assert_eq!(function1_info.last_line_defined, Some(4));
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(function1_info.last_line_defined, -1);
|
||||
assert_eq!(function1_info.what.as_deref(), Some("Lua"));
|
||||
assert_eq!(function1_info.last_line_defined, None);
|
||||
assert_eq!(function1_info.what, "Lua");
|
||||
|
||||
let function2_info = function2.info();
|
||||
assert_eq!(function2_info.name, None);
|
||||
assert_eq!(function2_info.source.as_deref(), Some(b"source1".as_ref()));
|
||||
assert_eq!(function2_info.line_defined, 3);
|
||||
assert_eq!(function2_info.source.as_deref(), Some("source1"));
|
||||
assert_eq!(function2_info.line_defined, Some(3));
|
||||
#[cfg(not(feature = "luau"))]
|
||||
assert_eq!(function2_info.last_line_defined, 3);
|
||||
assert_eq!(function2_info.last_line_defined, Some(3));
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(function2_info.last_line_defined, -1);
|
||||
assert_eq!(function2_info.what.as_deref(), Some("Lua"));
|
||||
assert_eq!(function2_info.last_line_defined, None);
|
||||
assert_eq!(function2_info.what, "Lua");
|
||||
|
||||
let function3_info = function3.info();
|
||||
assert_eq!(function3_info.name, None);
|
||||
assert_eq!(function3_info.source.as_deref(), Some(b"=[C]".as_ref()));
|
||||
assert_eq!(function3_info.line_defined, -1);
|
||||
assert_eq!(function3_info.last_line_defined, -1);
|
||||
assert_eq!(function3_info.what.as_deref(), Some("C"));
|
||||
assert_eq!(function3_info.source.as_deref(), Some("=[C]"));
|
||||
assert_eq!(function3_info.line_defined, None);
|
||||
assert_eq!(function3_info.last_line_defined, None);
|
||||
assert_eq!(function3_info.what, "C");
|
||||
|
||||
let print_info = globals.get::<_, Function>("print")?.info();
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(print_info.name.as_deref(), Some("print"));
|
||||
assert_eq!(print_info.source.as_deref(), Some(b"=[C]".as_ref()));
|
||||
assert_eq!(print_info.what.as_deref(), Some("C"));
|
||||
assert_eq!(print_info.line_defined, -1);
|
||||
assert_eq!(print_info.source.as_deref(), Some("=[C]"));
|
||||
assert_eq!(print_info.what, "C");
|
||||
assert_eq!(print_info.line_defined, None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
use std::str;
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
|
@ -61,9 +60,8 @@ fn test_function_calls() -> Result<()> {
|
|||
assert_eq!(debug.event(), DebugEvent::Call);
|
||||
let names = debug.names();
|
||||
let source = debug.source();
|
||||
let name = names.name.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||
let what = source.what.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||
hook_output.lock().unwrap().push((name, what));
|
||||
let name = names.name.map(|s| s.into_owned());
|
||||
hook_output.lock().unwrap().push((name, source.what));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
@ -80,18 +78,12 @@ fn test_function_calls() -> Result<()> {
|
|||
if cfg!(feature = "luajit") && lua.load("jit.version_num").eval::<i64>()? >= 20100 {
|
||||
assert_eq!(
|
||||
*output,
|
||||
vec![
|
||||
(None, Some("main".to_string())),
|
||||
(Some("len".to_string()), Some("Lua".to_string()))
|
||||
]
|
||||
vec![(None, "main"), (Some("len".to_string()), "Lua")]
|
||||
);
|
||||
} else {
|
||||
assert_eq!(
|
||||
*output,
|
||||
vec![
|
||||
(None, Some("main".to_string())),
|
||||
(Some("len".to_string()), Some("C".to_string()))
|
||||
]
|
||||
vec![(None, "main"), (Some("len".to_string()), "C")]
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1226,8 +1226,8 @@ fn test_inspect_stack() -> Result<()> {
|
|||
|
||||
let logline = lua.create_function(|lua, msg: StdString| {
|
||||
let debug = lua.inspect_stack(1).unwrap(); // caller
|
||||
let source = debug.source().short_src.map(core::str::from_utf8);
|
||||
let source = source.transpose().unwrap().unwrap_or("?");
|
||||
let source = debug.source().short_src;
|
||||
let source = source.as_deref().unwrap_or("?");
|
||||
let line = debug.curr_line();
|
||||
Ok(format!("{}:{} {}", source, line, msg))
|
||||
})?;
|
||||
|
|
Loading…
Reference in New Issue