diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 8cd99b15..aada9039 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -156,9 +156,7 @@ impl Engine { if !_terminate_chaining => { #[cfg(feature = "debugging")] - if self.debugger.is_some() { - self.run_debugger(scope, global, state, lib, this_ptr, _parent, level)?; - } + self.run_debugger(scope, global, state, lib, this_ptr, _parent, level)?; let mut idx_val_for_setter = idx_val.clone(); let idx_pos = x.lhs.position(); @@ -206,9 +204,7 @@ impl Engine { // xxx[rhs] op= new_val _ if new_val.is_some() => { #[cfg(feature = "debugging")] - if self.debugger.is_some() { - self.run_debugger(scope, global, state, lib, this_ptr, _parent, level)?; - } + self.run_debugger(scope, global, state, lib, this_ptr, _parent, level)?; let ((new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`"); let mut idx_val_for_setter = idx_val.clone(); @@ -254,9 +250,7 @@ impl Engine { // xxx[rhs] _ => { #[cfg(feature = "debugging")] - if self.debugger.is_some() { - self.run_debugger(scope, global, state, lib, this_ptr, _parent, level)?; - } + self.run_debugger(scope, global, state, lib, this_ptr, _parent, level)?; self.get_indexed_mut( global, state, lib, target, idx_val, pos, false, true, level, @@ -275,13 +269,9 @@ impl Engine { let call_args = &mut idx_val.into_fn_call_args(); #[cfg(feature = "debugging")] - let reset_debugger = if self.debugger.is_some() { - self.run_debugger_with_reset( - scope, global, state, lib, this_ptr, rhs, level, - )? - } else { - None - }; + let reset_debugger = self.run_debugger_with_reset( + scope, global, state, lib, this_ptr, rhs, level, + )?; let result = self.make_method_call( global, state, lib, name, *hashes, target, call_args, *pos, level, @@ -303,9 +293,7 @@ impl Engine { // {xxx:map}.id op= ??? Expr::Property(x, pos) if target.is::() && new_val.is_some() => { #[cfg(feature = "debugging")] - if self.debugger.is_some() { - self.run_debugger(scope, global, state, lib, this_ptr, rhs, level)?; - } + self.run_debugger(scope, global, state, lib, this_ptr, rhs, level)?; let index = x.2.clone().into(); let ((new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`"); @@ -326,9 +314,7 @@ impl Engine { // {xxx:map}.id Expr::Property(x, pos) if target.is::() => { #[cfg(feature = "debugging")] - if self.debugger.is_some() { - self.run_debugger(scope, global, state, lib, this_ptr, rhs, level)?; - } + self.run_debugger(scope, global, state, lib, this_ptr, rhs, level)?; let index = x.2.clone().into(); let val = self.get_indexed_mut( @@ -339,9 +325,7 @@ impl Engine { // xxx.id op= ??? Expr::Property(x, pos) if new_val.is_some() => { #[cfg(feature = "debugging")] - if self.debugger.is_some() { - self.run_debugger(scope, global, state, lib, this_ptr, rhs, level)?; - } + self.run_debugger(scope, global, state, lib, this_ptr, rhs, level)?; let ((getter, hash_get), (setter, hash_set), name) = x.as_ref(); let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`"); @@ -421,9 +405,7 @@ impl Engine { // xxx.id Expr::Property(x, pos) => { #[cfg(feature = "debugging")] - if self.debugger.is_some() { - self.run_debugger(scope, global, state, lib, this_ptr, rhs, level)?; - } + self.run_debugger(scope, global, state, lib, this_ptr, rhs, level)?; let ((getter, hash_get), _, name) = x.as_ref(); let hash = crate::ast::FnCallHashes::from_native(*hash_get); @@ -463,11 +445,9 @@ impl Engine { let val_target = &mut match x.lhs { Expr::Property(ref p, pos) => { #[cfg(feature = "debugging")] - if self.debugger.is_some() { - self.run_debugger( - scope, global, state, lib, this_ptr, _node, level, - )?; - } + self.run_debugger( + scope, global, state, lib, this_ptr, _node, level, + )?; let index = p.2.clone().into(); self.get_indexed_mut( @@ -480,13 +460,9 @@ impl Engine { let call_args = &mut idx_val.into_fn_call_args(); #[cfg(feature = "debugging")] - let reset_debugger = if self.debugger.is_some() { - self.run_debugger_with_reset( - scope, global, state, lib, this_ptr, _node, level, - )? - } else { - None - }; + let reset_debugger = self.run_debugger_with_reset( + scope, global, state, lib, this_ptr, _node, level, + )?; let result = self.make_method_call( global, state, lib, name, *hashes, target, call_args, pos, @@ -521,11 +497,9 @@ impl Engine { // xxx.prop[expr] | xxx.prop.expr Expr::Property(ref p, pos) => { #[cfg(feature = "debugging")] - if self.debugger.is_some() { - self.run_debugger( - scope, global, state, lib, this_ptr, _node, level, - )?; - } + self.run_debugger( + scope, global, state, lib, this_ptr, _node, level, + )?; let ((getter, hash_get), (setter, hash_set), name) = p.as_ref(); let rhs_chain = rhs.into(); @@ -626,13 +600,9 @@ impl Engine { let args = &mut idx_val.into_fn_call_args(); #[cfg(feature = "debugging")] - let reset_debugger = if self.debugger.is_some() { - self.run_debugger_with_reset( - scope, global, state, lib, this_ptr, _node, level, - )? - } else { - None - }; + let reset_debugger = self.run_debugger_with_reset( + scope, global, state, lib, this_ptr, _node, level, + )?; let result = self.make_method_call( global, state, lib, name, *hashes, target, args, pos, level, @@ -697,9 +667,7 @@ impl Engine { // id.??? or id[???] Expr::Variable(_, var_pos, x) => { #[cfg(feature = "debugging")] - if self.debugger.is_some() { - self.run_debugger(scope, global, state, lib, this_ptr, lhs, level)?; - } + self.run_debugger(scope, global, state, lib, this_ptr, lhs, level)?; #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut global.num_operations, *var_pos)?; diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index f6889c17..d585439b 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -4,9 +4,9 @@ use super::{EvalContext, EvalState, GlobalRuntimeState}; use crate::ast::{ASTNode, Expr, Stmt}; use crate::{Dynamic, Engine, EvalAltResult, Identifier, Module, Position, RhaiResultOf, Scope}; -use std::fmt; #[cfg(feature = "no_std")] use std::prelude::v1::*; +use std::{fmt, mem}; /// Callback function to initialize the debugger. #[cfg(not(feature = "sync"))] @@ -319,11 +319,25 @@ impl Debugger { pos, }); } - /// Set the status of this [`Debugger`]. + /// Change the current status to [`CONTINUE`][DebuggerStatus::CONTINUE] and return the previous status. + pub(crate) fn clear_status_if( + &mut self, + filter: impl Fn(&DebuggerStatus) -> bool, + ) -> Option { + if filter(&self.status) { + Some(mem::replace(&mut self.status, DebuggerStatus::CONTINUE)) + } else { + None + } + } + /// Override the status of this [`Debugger`] if it is [`Some`] the current status is + /// [`CONTINUE`][DebuggerStatus::CONTINUE]. #[inline(always)] pub(crate) fn reset_status(&mut self, status: Option) { - if let Some(cmd) = status { - self.status = cmd; + if self.status == DebuggerStatus::CONTINUE { + if let Some(cmd) = status { + self.status = cmd; + } } } /// Returns the first break-point triggered by a particular [`AST` Node][ASTNode]. @@ -383,7 +397,7 @@ impl Debugger { } impl Engine { - /// Run the debugger callback. + /// Run the debugger callback if there is a debugging interface registered. #[inline(always)] pub(crate) fn run_debugger<'a>( &self, @@ -395,14 +409,40 @@ impl Engine { node: impl Into>, level: usize, ) -> RhaiResultOf<()> { - if let Some(cmd) = - self.run_debugger_with_reset(scope, global, state, lib, this_ptr, node, level)? - { - global.debugger.status = cmd; + if self.debugger.is_some() { + if let Some(cmd) = + self.run_debugger_with_reset_raw(scope, global, state, lib, this_ptr, node, level)? + { + global.debugger.status = cmd; + } } Ok(()) } + /// Run the debugger callback if there is a debugging interface registered. + /// + /// Returns `Some` if the debugger needs to be reactivated at the end of the block, statement or + /// function call. + /// + /// It is up to the [`Engine`] to reactivate the debugger. + #[inline(always)] + #[must_use] + pub(crate) fn run_debugger_with_reset<'a>( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + state: &mut EvalState, + lib: &[&Module], + this_ptr: &mut Option<&mut Dynamic>, + node: impl Into>, + level: usize, + ) -> RhaiResultOf> { + if self.debugger.is_some() { + self.run_debugger_with_reset_raw(scope, global, state, lib, this_ptr, node, level) + } else { + Ok(None) + } + } /// Run the debugger callback. /// /// Returns `Some` if the debugger needs to be reactivated at the end of the block, statement or @@ -411,7 +451,7 @@ impl Engine { /// It is up to the [`Engine`] to reactivate the debugger. #[inline] #[must_use] - pub(crate) fn run_debugger_with_reset<'a>( + pub(crate) fn run_debugger_with_reset_raw<'a>( &self, scope: &mut Scope, global: &mut GlobalRuntimeState, @@ -513,7 +553,6 @@ impl Engine { | ASTNode::Stmt(Stmt::Expr(Expr::FnCall(_, _))) => context.call_level() + 1, _ => context.call_level(), }; - println!("Set FunctionExit to {}", level); global.debugger.status = DebuggerStatus::FunctionExit(level); Ok(None) } diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 79f27d92..520f8283 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -266,11 +266,8 @@ impl Engine { // binary operators are also function calls. if let Expr::FnCall(x, pos) = expr { #[cfg(feature = "debugging")] - let reset_debugger = if self.debugger.is_some() { - self.run_debugger_with_reset(scope, global, state, lib, this_ptr, expr, level)? - } else { - None - }; + let reset_debugger = + self.run_debugger_with_reset(scope, global, state, lib, this_ptr, expr, level)?; #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut global.num_operations, expr.position())?; @@ -289,9 +286,7 @@ impl Engine { // will cost more than the mis-predicted `match` branch. if let Expr::Variable(index, var_pos, x) = expr { #[cfg(feature = "debugging")] - if self.debugger.is_some() { - self.run_debugger(scope, global, state, lib, this_ptr, expr, level)?; - } + self.run_debugger(scope, global, state, lib, this_ptr, expr, level)?; #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut global.num_operations, expr.position())?; @@ -308,11 +303,8 @@ impl Engine { } #[cfg(feature = "debugging")] - let reset_debugger = if self.debugger.is_some() { - self.run_debugger_with_reset(scope, global, state, lib, this_ptr, expr, level)? - } else { - None - }; + let reset_debugger = + self.run_debugger_with_reset(scope, global, state, lib, this_ptr, expr, level)?; #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut global.num_operations, expr.position())?; diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index bb4645da..065e251a 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -204,11 +204,8 @@ impl Engine { level: usize, ) -> RhaiResult { #[cfg(feature = "debugging")] - let reset_debugger = if self.debugger.is_some() { - self.run_debugger_with_reset(scope, global, state, lib, this_ptr, stmt, level)? - } else { - None - }; + let reset_debugger = + self.run_debugger_with_reset(scope, global, state, lib, this_ptr, stmt, level)?; // Coded this way for better branch prediction. // Popular branches are lifted out of the `match` statement into their own branches. diff --git a/src/func/call.rs b/src/func/call.rs index 240b050c..ab719c14 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -956,35 +956,24 @@ impl Engine { Ok(( if let Expr::Stack(slot, _) = arg_expr { #[cfg(feature = "debugging")] - if self.debugger.is_some() { - self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?; - } + self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?; constants[*slot].clone() } else if let Some(value) = arg_expr.get_literal_value() { #[cfg(feature = "debugging")] - if self.debugger.is_some() { - self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?; - } + self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?; value } else { // Do not match function exit for arguments #[cfg(feature = "debugging")] - let reset_debugger = match global.debugger.status { - crate::eval::DebuggerStatus::FunctionExit(_) => { - Some(std::mem::take(&mut global.debugger.status)) - } - _ => None, - }; + let reset_debugger = global.debugger.clear_status_if(|status| { + matches!(status, crate::eval::DebuggerStatus::FunctionExit(_)) + }); let result = self.eval_expr(scope, global, state, lib, this_ptr, arg_expr, level); - // Restore function exit if status is not active + // Restore function exit status #[cfg(feature = "debugging")] - if self.debugger.is_some() - && global.debugger.status == crate::eval::DebuggerStatus::CONTINUE - { - global.debugger.reset_status(reset_debugger); - } + global.debugger.reset_status(reset_debugger); result? }, @@ -1229,9 +1218,7 @@ impl Engine { let first_expr = first_arg.unwrap(); #[cfg(feature = "debugging")] - if self.debugger.is_some() { - self.run_debugger(scope, global, state, lib, this_ptr, first_expr, level)?; - } + self.run_debugger(scope, global, state, lib, this_ptr, first_expr, level)?; // func(x, ...) -> x.func(...) a_expr.iter().try_for_each(|expr| { @@ -1315,10 +1302,7 @@ impl Engine { // &mut first argument and avoid cloning the value if !args_expr.is_empty() && args_expr[0].is_variable_access(true) { #[cfg(feature = "debugging")] - if self.debugger.is_some() { - let node = &args_expr[0]; - self.run_debugger(scope, global, state, lib, this_ptr, node, level)?; - } + self.run_debugger(scope, global, state, lib, this_ptr, &args_expr[0], level)?; // func(x, ...) -> x.func(...) arg_values.push(Dynamic::UNIT); diff --git a/src/func/script.rs b/src/func/script.rs index 66132cc3..77397f1e 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -142,8 +142,7 @@ impl Engine { }; #[cfg(feature = "debugging")] - if self.debugger.is_some() { - println!("Level = {}", level); + { let node = crate::ast::Stmt::Noop(fn_def.body.position()); self.run_debugger(scope, global, state, lib, this_ptr, &node, level)?; }