From aee35e5f20800bb17793ece03b26ff4008224ce6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 25 Jan 2022 14:32:42 +0800 Subject: [PATCH] Add DebuggingPackage. --- CHANGELOG.md | 1 + src/packages/debugging.rs | 80 +++++++++++++++++++++++++++++++++++++++ src/packages/mod.rs | 3 ++ src/packages/pkg_core.rs | 3 ++ tests/debugging.rs | 34 +++++++++++++++++ 5 files changed, 121 insertions(+) create mode 100644 src/packages/debugging.rs create mode 100644 tests/debugging.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ebe85c55..112423ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ New features * A debugging interface is added. * A new bin tool, `rhai-dbg` (aka _The Rhai Debugger_), is added to showcase the debugging interface. +* A new package, `DebuggingPackage`, is added which contains the `stack_trace` function to get the current call stack anywhere in a script. Version 1.4.2 diff --git a/src/packages/debugging.rs b/src/packages/debugging.rs new file mode 100644 index 00000000..7a1139f4 --- /dev/null +++ b/src/packages/debugging.rs @@ -0,0 +1,80 @@ +#![cfg(feature = "debugging")] + +use crate::def_package; +use crate::plugin::*; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; + +#[cfg(not(feature = "no_function"))] +use crate::{Dynamic, NativeCallContext, INT}; + +#[cfg(not(feature = "no_function"))] +#[cfg(not(feature = "no_index"))] +use crate::Array; + +#[cfg(not(feature = "no_function"))] +#[cfg(not(feature = "no_object"))] +use crate::Map; + +def_package! { + /// Package of basic debugging utilities. + crate::DebuggingPackage => |lib| { + lib.standard = true; + + combine_with_exported_module!(lib, "debugging", debugging_functions); + } +} + +#[export_module] +mod debugging_functions { + #[cfg(not(feature = "no_function"))] + #[cfg(not(feature = "no_index"))] + pub fn stack_trace(ctx: NativeCallContext) -> Array { + if let Some(global) = ctx.global_runtime_state() { + global + .debugger + .call_stack() + .iter() + .rev() + .map( + |frame @ crate::debugger::CallStackFrame { + fn_name, + args, + source, + pos, + }| { + let call = frame.to_string(); + + #[cfg(not(feature = "no_object"))] + { + let mut map = Map::new(); + map.insert("call".into(), call.into()); + map.insert("fn_name".into(), fn_name.into()); + if !args.is_empty() { + map.insert( + "args".into(), + Dynamic::from_array(args.clone().to_vec()), + ); + } + if !source.is_empty() { + map.insert("source".into(), source.into()); + } + if !pos.is_none() { + map.insert("line".into(), (pos.line().unwrap() as INT).into()); + map.insert( + "pos".into(), + (pos.position().unwrap_or(0) as INT).into(), + ); + } + Dynamic::from_map(map) + } + #[cfg(feature = "no_object")] + call.into() + }, + ) + .collect() + } else { + Array::new() + } + } +} diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 0b8bc6f3..f56fafe9 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -6,6 +6,7 @@ pub(crate) mod arithmetic; pub(crate) mod array_basic; mod bit_field; pub(crate) mod blob_basic; +mod debugging; mod fn_basic; mod iter_basic; mod lang_core; @@ -24,6 +25,8 @@ pub use array_basic::BasicArrayPackage; pub use bit_field::BitFieldPackage; #[cfg(not(feature = "no_index"))] pub use blob_basic::BasicBlobPackage; +#[cfg(feature = "debugging")] +pub use debugging::DebuggingPackage; pub use fn_basic::BasicFnPackage; pub use iter_basic::BasicIteratorPackage; pub use lang_core::LanguageCorePackage; diff --git a/src/packages/pkg_core.rs b/src/packages/pkg_core.rs index c006b0a1..94d59c7f 100644 --- a/src/packages/pkg_core.rs +++ b/src/packages/pkg_core.rs @@ -13,6 +13,7 @@ def_package! { /// * [`BasicStringPackage`][super::BasicStringPackage] /// * [`BasicIteratorPackage`][super::BasicIteratorPackage] /// * [`BasicFnPackage`][super::BasicFnPackage] + /// * [`DebuggingPackage`][super::DebuggingPackage] crate::CorePackage => |lib| { lib.standard = true; @@ -21,5 +22,7 @@ def_package! { super::BasicStringPackage::init(lib); super::BasicIteratorPackage::init(lib); super::BasicFnPackage::init(lib); + #[cfg(feature = "debugging")] + super::DebuggingPackage::init(lib); } } diff --git a/tests/debugging.rs b/tests/debugging.rs new file mode 100644 index 00000000..55b03786 --- /dev/null +++ b/tests/debugging.rs @@ -0,0 +1,34 @@ +#![cfg(feature = "debugging")] +use rhai::{Engine, EvalAltResult, INT}; + +#[cfg(not(feature = "no_index"))] +use rhai::Array; + +#[test] +fn test_debugging() -> Result<(), Box> { + let engine = Engine::new(); + + #[cfg(not(feature = "no_function"))] + #[cfg(not(feature = "no_index"))] + { + let r = engine.eval::( + " + fn foo(x) { + if x >= 5 { + stack_trace() + } else { + foo(x+1) + } + } + + foo(0) + ", + )?; + + assert_eq!(r.len(), 6); + + assert_eq!(engine.eval::("len(stack_trace())")?, 0); + } + + Ok(()) +}