Enable Lua::inspect_stack for Luau

This commit is contained in:
Alex Orlenko 2022-02-25 23:51:51 +00:00
parent cab92f4ea2
commit 3e5f8e7bb8
No known key found for this signature in database
GPG key ID: 4C150C250863B96D
7 changed files with 139 additions and 27 deletions

View file

@ -7,8 +7,8 @@ use std::mem;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use super::lua::*;
use super::lauxlib::*;
use super::lua::*;
#[inline(always)]
unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) {

View file

@ -397,6 +397,15 @@ const LUA_IDSIZE: usize = 256;
/// Type for functions to be called on debug events.
pub type lua_Hook = unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug);
pub type lua_Coverage = unsafe extern "C" fn(
context: *mut c_void,
function: *const c_char,
linedefined: c_int,
depth: c_int,
hits: *const c_int,
size: usize,
);
extern "C" {
pub fn lua_getinfo(
L: *mut lua_State,
@ -413,7 +422,14 @@ extern "C" {
pub fn lua_singlestep(L: *mut lua_State, enabled: c_int);
pub fn lua_breakpoint(L: *mut lua_State, funcindex: c_int, line: c_int, enabled: c_int);
// TODO: lua_Coverage, lua_getcoverage
pub fn lua_getcoverage(
L: *mut lua_State,
funcindex: c_int,
context: *mut c_void,
callback: lua_Coverage,
);
pub fn lua_debugtrace(L: *mut lua_State) -> *const c_char;
}
#[repr(C)]
@ -435,4 +451,31 @@ pub struct lua_Debug {
// These are shared between all coroutines.
//
// TODO: lua_Callbacks, lua_callbacks
#[repr(C)]
pub struct lua_Callbacks {
/// arbitrary userdata pointer that is never overwritten by Luau
pub userdata: *mut c_void,
/// gets called at safepoints (loop back edges, call/ret, gc) if set
pub interrupt: Option<unsafe extern "C" fn(L: *mut lua_State, gc: c_int)>,
/// gets called when an unprotected error is raised (if longjmp is used)
pub panic: Option<unsafe extern "C" fn(L: *mut lua_State, errcode: c_int)>,
/// gets called when L is created (LP == parent) or destroyed (LP == NULL)
pub userthread: Option<unsafe extern "C" fn(LP: *mut lua_State, L: *mut lua_State)>,
/// gets called when a string is created; returned atom can be retrieved via tostringatom
pub useratom: Option<unsafe extern "C" fn(s: *const c_char, l: usize) -> i16>,
/// gets called when BREAK instruction is encountered
pub debugbreak: Option<unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
/// gets called after each instruction in single step mode
pub debugstep: Option<unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
/// gets called when thread execution is interrupted by break in another thread
pub debuginterrupt: Option<unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
/// gets called when protected call results in an error
pub debugprotectederror: Option<unsafe extern "C" fn(L: *mut lua_State)>,
}
extern "C" {
pub fn lua_callbacks(L: *mut lua_State) -> *mut lua_Callbacks;
}

View file

@ -1,5 +1,6 @@
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};
@ -18,9 +19,12 @@ use crate::lua::Lua;
pub struct Debug<'lua> {
lua: &'lua Lua,
ar: ActivationRecord,
#[cfg(feature = "luau")]
level: c_int,
}
impl<'lua> Debug<'lua> {
#[cfg(not(feature = "luau"))]
pub(crate) fn new(lua: &'lua Lua, ar: *mut lua_Debug) -> Self {
Debug {
lua,
@ -28,10 +32,12 @@ impl<'lua> Debug<'lua> {
}
}
pub(crate) fn new_owned(lua: &'lua Lua, ar: lua_Debug) -> Self {
pub(crate) fn new_owned(lua: &'lua Lua, _level: c_int, ar: lua_Debug) -> Self {
Debug {
lua,
ar: ActivationRecord::Owned(UnsafeCell::new(ar)),
#[cfg(feature = "luau")]
level: _level,
}
}
@ -41,6 +47,7 @@ impl<'lua> Debug<'lua> {
/// from a function that did a tail call.
///
/// [Lua 5.1]: https://www.lua.org/manual/5.1/manual.html#pdf-LUA_HOOKTAILRET
#[cfg(not(feature = "luau"))]
pub fn event(&self) -> DebugEvent {
unsafe {
match (*self.ar.get()).event {
@ -57,13 +64,23 @@ impl<'lua> Debug<'lua> {
/// Corresponds to the `n` what mask.
pub fn names(&self) -> DebugNames<'lua> {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.lua.state, cstr!("n"), self.ar.get()) != 0,
"lua_getinfo failed with `n`"
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state, self.level, cstr!("n"), self.ar.get()) != 0,
"lua_getinfo failed with `n`"
);
DebugNames {
name: ptr_to_str((*self.ar.get()).name),
#[cfg(not(feature = "luau"))]
name_what: ptr_to_str((*self.ar.get()).namewhat),
#[cfg(feature = "luau")]
name_what: None,
}
}
}
@ -71,14 +88,22 @@ impl<'lua> Debug<'lua> {
/// Corresponds to the `S` what mask.
pub fn source(&self) -> DebugSource<'lua> {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.lua.state, cstr!("S"), self.ar.get()) != 0,
"lua_getinfo failed with `S`"
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state, self.level, cstr!("s"), self.ar.get()) != 0,
"lua_getinfo failed with `s`"
);
DebugSource {
source: ptr_to_str((*self.ar.get()).source),
short_src: ptr_to_str((*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),
}
@ -88,16 +113,24 @@ impl<'lua> Debug<'lua> {
/// Corresponds to the `l` what mask. Returns the current line.
pub fn curr_line(&self) -> i32 {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.lua.state, cstr!("l"), self.ar.get()) != 0,
"lua_getinfo failed with `l`"
);
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state, self.level, cstr!("l"), self.ar.get()) != 0,
"lua_getinfo failed with `l`"
);
(*self.ar.get()).currentline as i32
}
}
/// Corresponds to the `t` what mask. Returns true if the hook is in a function tail call, false
/// otherwise.
#[cfg(not(feature = "luau"))]
pub fn is_tail_call(&self) -> bool {
unsafe {
mlua_assert!(
@ -111,22 +144,38 @@ impl<'lua> Debug<'lua> {
/// Corresponds to the `u` what mask.
pub fn stack(&self) -> DebugStack {
unsafe {
#[cfg(not(feature = "luau"))]
mlua_assert!(
ffi::lua_getinfo(self.lua.state, cstr!("u"), self.ar.get()) != 0,
"lua_getinfo failed with `u`"
);
DebugStack {
#[cfg(feature = "luau")]
mlua_assert!(
ffi::lua_getinfo(self.lua.state, self.level, cstr!("a"), self.ar.get()) != 0,
"lua_getinfo failed with `a`"
);
#[cfg(not(feature = "luau"))]
let stack = DebugStack {
num_ups: (*self.ar.get()).nups as i32,
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
num_params: (*self.ar.get()).nparams as i32,
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
is_vararg: (*self.ar.get()).isvararg != 0,
}
};
#[cfg(feature = "luau")]
let stack = DebugStack {
num_ups: (*self.ar.get()).nupvals as i32,
num_params: (*self.ar.get()).nparams as i32,
is_vararg: (*self.ar.get()).isvararg != 0,
};
stack
}
}
}
enum ActivationRecord {
#[cfg(not(feature = "luau"))]
Borrowed(*mut lua_Debug),
Owned(UnsafeCell<lua_Debug>),
}
@ -135,6 +184,7 @@ impl ActivationRecord {
#[inline]
fn get(&self) -> *mut lua_Debug {
match self {
#[cfg(not(feature = "luau"))]
ActivationRecord::Borrowed(x) => *x,
ActivationRecord::Owned(x) => x.get(),
}
@ -163,6 +213,7 @@ 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]>,
}
@ -170,15 +221,26 @@ pub struct DebugSource<'a> {
#[derive(Copy, Clone, Debug)]
pub struct DebugStack {
pub num_ups: i32,
/// Requires `feature = "lua54/lua53/lua52"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
/// Requires `feature = "lua54/lua53/lua52/luau"`
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luau"
))]
pub num_params: i32,
/// Requires `feature = "lua54/lua53/lua52"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
/// Requires `feature = "lua54/lua53/lua52/luau"`
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luau"
))]
pub is_vararg: bool,
}
/// Determines when a hook function will be called by Lua.
#[cfg(not(feature = "luau"))]
#[derive(Clone, Copy, Debug, Default)]
pub struct HookTriggers {
/// Before a function call.
@ -196,6 +258,7 @@ pub struct HookTriggers {
pub every_nth_instruction: Option<u32>,
}
#[cfg(not(feature = "luau"))]
impl HookTriggers {
/// Returns a new instance of `HookTriggers` with [`on_calls`] trigger set.
///
@ -262,6 +325,7 @@ impl HookTriggers {
}
}
#[cfg(not(feature = "luau"))]
impl BitOr for HookTriggers {
type Output = Self;
@ -276,6 +340,7 @@ impl BitOr for HookTriggers {
}
}
#[cfg(not(feature = "luau"))]
impl BitOrAssign for HookTriggers {
fn bitor_assign(&mut self, rhs: Self) {
*self = *self | rhs;

View file

@ -85,7 +85,6 @@ mod conversion;
mod error;
mod ffi;
mod function;
#[cfg(not(feature = "luau"))]
mod hook;
mod lua;
mod multi;
@ -105,8 +104,7 @@ pub use crate::{ffi::lua_CFunction, ffi::lua_State};
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
pub use crate::function::Function;
#[cfg(not(feature = "luau"))]
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack, HookTriggers};
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
pub use crate::lua::{AsChunk, Chunk, ChunkMode, GCMode, Lua, LuaOptions};
pub use crate::multi::Variadic;
pub use crate::scope::Scope;
@ -120,6 +118,9 @@ pub use crate::userdata::{
};
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
#[cfg(not(feature = "luau"))]
pub use crate::hook::HookTriggers;
#[cfg(feature = "async")]
pub use crate::thread::AsyncThread;

View file

@ -14,16 +14,15 @@ use rustc_hash::FxHashMap;
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
#[cfg(not(feature = "luau"))]
use crate::hook::{Debug, HookTriggers};
use crate::hook::Debug;
use crate::scope::Scope;
use crate::stdlib::StdLib;
use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{
Callback, CallbackUpvalue, DestructedUserdataMT, /*HookCallback,*/ Integer, LightUserData,
LuaRef, MaybeSend, Number, RegistryKey,
Callback, CallbackUpvalue, DestructedUserdataMT, Integer, LightUserData, LuaRef, MaybeSend,
Number, RegistryKey,
};
use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
@ -36,9 +35,6 @@ use crate::util::{
};
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
#[cfg(not(feature = "luau"))]
use crate::types::HookCallback;
#[cfg(not(feature = "lua54"))]
use crate::util::push_userdata;
#[cfg(feature = "lua54")]
@ -47,6 +43,9 @@ use {
std::ffi::CStr,
};
#[cfg(not(feature = "luau"))]
use crate::{hook::HookTriggers, types::HookCallback};
#[cfg(not(feature = "send"))]
use std::rc::Rc;
@ -933,14 +932,19 @@ impl Lua {
/// function that has called level `n` (except for tail calls, which do not count in the stack).
///
/// [`Debug`]: crate::hook::Debug
#[cfg(not(feature = "luau"))]
pub fn inspect_stack(&self, level: usize) -> Option<Debug> {
unsafe {
let mut ar: ffi::lua_Debug = mem::zeroed();
if ffi::lua_getstack(self.state, level as c_int, &mut ar) == 0 {
let level = level as c_int;
#[cfg(not(feature = "luau"))]
if ffi::lua_getstack(self.state, level, &mut ar) == 0 {
return None;
}
Some(Debug::new_owned(self, ar))
#[cfg(feature = "luau")]
if ffi::lua_getinfo(self.state, level, cstr!(""), &mut ar) == 0 {
return None;
}
Some(Debug::new_owned(self, level, ar))
}
}

View file

@ -17,8 +17,8 @@ use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
};
use crate::util::{
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, push_userdata,
rawset_field, take_userdata, StackGuard,
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, rawset_field,
take_userdata, StackGuard,
};
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
@ -352,7 +352,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
})?;
#[cfg(feature = "luau")]
let ud_ptr = {
push_userdata::<UserDataCell<Rc<RefCell<T>>>>(
crate::util::push_userdata::<UserDataCell<Rc<RefCell<T>>>>(
lua.state,
UserDataCell::new(data.clone()),
)?;

View file

@ -1169,7 +1169,6 @@ fn test_load_from_function() -> Result<()> {
Ok(())
}
#[cfg(not(feature = "luau"))]
#[test]
fn test_inspect_stack() -> Result<()> {
let lua = Lua::new();