From 8322e62c181783184e4ef12c945a4d7ba486fd33 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 2 Feb 2022 22:42:33 +0800 Subject: [PATCH] Fix function exit trigger and add function enter trigger. --- CHANGELOG.md | 2 +- src/api/call_fn.rs | 2 +- src/eval/chaining.rs | 38 +++++++++++------------ src/eval/debugger.rs | 45 ++++++++++++++++++--------- src/eval/expr.rs | 4 +-- src/eval/stmt.rs | 1 + src/func/call.rs | 73 ++++++++++++++++++++++++++++---------------- src/func/native.rs | 2 +- src/func/script.rs | 16 ++++++++-- 9 files changed, 115 insertions(+), 68 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0de82a1d..42f989ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -576,7 +576,7 @@ Breaking changes New features ------------ -* Line continuation (via `\`) and multi-line literal strings (wrapped with \`) support are added. +* Line continuation (via `\`) and multi-line literal strings (wrapped with `` ` ``) support are added. * Rhai scripts can now start with a shebang `#!` which is ignored. Enhancements diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index e501c68d..ca41552d 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -190,8 +190,8 @@ impl Engine { &mut this_ptr, fn_def, &mut args, - Position::NONE, rewind_scope, + Position::NONE, 0, ); diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 16daa9e5..8cd99b15 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -191,8 +191,8 @@ impl Engine { let fn_name = crate::engine::FN_IDX_SET; if let Err(err) = self.exec_fn_call( - global, state, lib, fn_name, hash_set, args, is_ref_mut, true, - root_pos, None, level, + None, global, state, lib, fn_name, hash_set, args, is_ref_mut, + true, root_pos, level, ) { // Just ignore if there is no index setter if !matches!(*err, ERR::ErrorFunctionNotFound(_, _)) { @@ -244,8 +244,8 @@ impl Engine { let fn_name = crate::engine::FN_IDX_SET; self.exec_fn_call( - global, state, lib, fn_name, hash_set, args, is_ref_mut, true, - root_pos, None, level, + None, global, state, lib, fn_name, hash_set, args, is_ref_mut, + true, root_pos, level, )?; } @@ -351,8 +351,8 @@ impl Engine { let args = &mut [target.as_mut()]; let (mut orig_val, _) = self .exec_fn_call( - global, state, lib, getter, hash, args, is_ref_mut, true, *pos, - None, level, + None, global, state, lib, getter, hash, args, is_ref_mut, true, + *pos, level, ) .or_else(|err| match *err { // Try an indexer if property does not exist @@ -392,7 +392,7 @@ impl Engine { let hash = crate::ast::FnCallHashes::from_native(*hash_set); let args = &mut [target.as_mut(), &mut new_val]; self.exec_fn_call( - global, state, lib, setter, hash, args, is_ref_mut, true, *pos, None, + None, global, state, lib, setter, hash, args, is_ref_mut, true, *pos, level, ) .or_else(|err| match *err { @@ -405,8 +405,8 @@ impl Engine { let pos = Position::NONE; self.exec_fn_call( - global, state, lib, fn_name, hash_set, args, is_ref_mut, true, - pos, None, level, + None, global, state, lib, fn_name, hash_set, args, is_ref_mut, + true, pos, level, ) .map_err( |idx_err| match *idx_err { @@ -429,7 +429,7 @@ impl Engine { let hash = crate::ast::FnCallHashes::from_native(*hash_get); let args = &mut [target.as_mut()]; self.exec_fn_call( - global, state, lib, getter, hash, args, is_ref_mut, true, *pos, None, + None, global, state, lib, getter, hash, args, is_ref_mut, true, *pos, level, ) .map_or_else( @@ -537,8 +537,8 @@ impl Engine { // Assume getters are always pure let (mut val, _) = self .exec_fn_call( - global, state, lib, getter, hash_get, args, is_ref_mut, - true, pos, None, level, + None, global, state, lib, getter, hash_get, args, + is_ref_mut, true, pos, level, ) .or_else(|err| match *err { // Try an indexer if property does not exist @@ -585,8 +585,8 @@ impl Engine { let mut arg_values = [target.as_mut(), val]; let args = &mut arg_values; self.exec_fn_call( - global, state, lib, setter, hash_set, args, is_ref_mut, - true, pos, None, level, + None, global, state, lib, setter, hash_set, args, + is_ref_mut, true, pos, level, ) .or_else( |err| match *err { @@ -600,8 +600,8 @@ impl Engine { global.hash_idx_set(), ); self.exec_fn_call( - global, state, lib, fn_name, hash_set, args, - is_ref_mut, true, pos, None, level, + None, global, state, lib, fn_name, hash_set, + args, is_ref_mut, true, pos, level, ) .or_else(|idx_err| match *idx_err { ERR::ErrorIndexingType(_, _) => { @@ -767,7 +767,7 @@ impl Engine { (crate::FnArgsVec::with_capacity(args.len()), Position::NONE), |(mut values, mut pos), expr| -> RhaiResultOf<_> { let (value, arg_pos) = self.get_arg_value( - scope, global, state, lib, this_ptr, level, expr, constants, + scope, global, state, lib, this_ptr, expr, constants, level, )?; if values.is_empty() { pos = arg_pos; @@ -813,7 +813,7 @@ impl Engine { (crate::FnArgsVec::with_capacity(args.len()), Position::NONE), |(mut values, mut pos), expr| -> RhaiResultOf<_> { let (value, arg_pos) = self.get_arg_value( - scope, global, state, lib, this_ptr, level, expr, constants, + scope, global, state, lib, this_ptr, expr, constants, level, )?; if values.is_empty() { pos = arg_pos @@ -1079,7 +1079,7 @@ impl Engine { let pos = Position::NONE; self.exec_fn_call( - global, state, lib, fn_name, hash_get, args, true, true, pos, None, level, + None, global, state, lib, fn_name, hash_get, args, true, true, pos, level, ) .map(|(v, _)| v.into()) } diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index d8a29d9e..f6889c17 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -51,6 +51,13 @@ pub enum DebuggerCommand { FunctionExit, } +impl Default for DebuggerCommand { + #[inline(always)] + fn default() -> Self { + Self::Continue + } +} + /// The debugger status. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum DebuggerStatus { @@ -60,6 +67,19 @@ pub enum DebuggerStatus { FunctionExit(usize), } +impl Default for DebuggerStatus { + #[inline(always)] + fn default() -> Self { + Self::CONTINUE + } +} + +impl DebuggerStatus { + pub const CONTINUE: Self = Self::Next(false, false); + pub const STEP: Self = Self::Next(true, true); + pub const NEXT: Self = Self::Next(true, false); +} + /// A event that triggers the debugger. #[derive(Debug, Clone, Copy)] pub enum DebuggerEvent<'a> { @@ -231,7 +251,7 @@ impl fmt::Display for CallStackFrame { #[derive(Debug, Clone, Hash)] pub struct Debugger { /// The current status command. - status: DebuggerStatus, + pub(crate) status: DebuggerStatus, /// The current state. state: Dynamic, /// The current set of break-points. @@ -247,9 +267,9 @@ impl Debugger { pub fn new(engine: &Engine) -> Self { Self { status: if engine.debugger.is_some() { - DebuggerStatus::Next(true, true) + DebuggerStatus::STEP } else { - DebuggerStatus::Next(false, false) + DebuggerStatus::CONTINUE }, state: if let Some((ref init, _)) = engine.debugger { init() @@ -299,12 +319,6 @@ impl Debugger { pos, }); } - /// Get the current status of this [`Debugger`]. - #[inline(always)] - #[must_use] - pub(crate) fn status(&self) -> DebuggerStatus { - self.status - } /// Set the status of this [`Debugger`]. #[inline(always)] pub(crate) fn reset_status(&mut self, status: Option) { @@ -476,19 +490,19 @@ impl Engine { match command { DebuggerCommand::Continue => { - global.debugger.status = DebuggerStatus::Next(false, false); + global.debugger.status = DebuggerStatus::CONTINUE; Ok(None) } DebuggerCommand::Next => { - global.debugger.status = DebuggerStatus::Next(false, false); - Ok(Some(DebuggerStatus::Next(true, false))) + global.debugger.status = DebuggerStatus::CONTINUE; + Ok(Some(DebuggerStatus::NEXT)) } DebuggerCommand::StepOver => { - global.debugger.status = DebuggerStatus::Next(false, false); - Ok(Some(DebuggerStatus::Next(true, true))) + global.debugger.status = DebuggerStatus::CONTINUE; + Ok(Some(DebuggerStatus::STEP)) } DebuggerCommand::StepInto => { - global.debugger.status = DebuggerStatus::Next(true, true); + global.debugger.status = DebuggerStatus::STEP; Ok(None) } DebuggerCommand::FunctionExit => { @@ -499,6 +513,7 @@ 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 3b6015b7..79f27d92 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -236,8 +236,8 @@ impl Engine { ); self.make_function_call( - scope, global, state, lib, this_ptr, name, first_arg, args, constants, *hashes, pos, - *capture, level, + scope, global, state, lib, this_ptr, name, first_arg, args, constants, *hashes, + *capture, pos, level, ) } diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 2adb7829..bb4645da 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -153,6 +153,7 @@ impl Engine { let hash = hash_op_assign; let args = &mut [lhs_ptr_inner, &mut new_val]; + let level = level + 1; match self.call_native_fn( global, state, lib, op_assign, hash, args, true, true, op_pos, level, diff --git a/src/func/call.rs b/src/func/call.rs index cd0d9614..240b050c 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -427,7 +427,7 @@ impl Engine { #[cfg(feature = "debugging")] if self.debugger.is_some() { - match global.debugger.status() { + match global.debugger.status { crate::eval::DebuggerStatus::FunctionExit(n) if n >= level => { let scope = &mut &mut Scope::new(); let node = crate::ast::Stmt::Noop(pos); @@ -591,6 +591,7 @@ impl Engine { /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! pub(crate) fn exec_fn_call( &self, + scope: Option<&mut Scope>, global: &mut GlobalRuntimeState, state: &mut EvalState, lib: &[&Module], @@ -600,7 +601,6 @@ impl Engine { is_ref_mut: bool, is_method_call: bool, pos: Position, - scope: Option<&mut Scope>, level: usize, ) -> RhaiResultOf<(Dynamic, bool)> { fn no_method_err(name: &str, pos: Position) -> RhaiResultOf<(Dynamic, bool)> { @@ -711,8 +711,8 @@ impl Engine { &mut Some(*first_arg), func, rest_args, - pos, true, + pos, level, ); @@ -730,7 +730,7 @@ impl Engine { } let result = self.call_script_fn( - scope, global, state, lib, &mut None, func, args, pos, true, level, + scope, global, state, lib, &mut None, func, args, true, pos, level, ); // Restore the original reference @@ -812,7 +812,7 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - global, state, lib, fn_name, new_hash, &mut args, false, false, pos, None, + None, global, state, lib, fn_name, new_hash, &mut args, false, false, pos, level, ) } @@ -852,7 +852,7 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - global, state, lib, fn_name, new_hash, &mut args, is_ref_mut, true, pos, None, + None, global, state, lib, fn_name, new_hash, &mut args, is_ref_mut, true, pos, level, ) } @@ -924,7 +924,7 @@ impl Engine { args.extend(call_args.iter_mut()); self.exec_fn_call( - global, state, lib, fn_name, hash, &mut args, is_ref_mut, true, pos, None, + None, global, state, lib, fn_name, hash, &mut args, is_ref_mut, true, pos, level, ) } @@ -949,9 +949,9 @@ impl Engine { state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, - level: usize, arg_expr: &Expr, constants: &[Dynamic], + level: usize, ) -> RhaiResultOf<(Dynamic, Position)> { Ok(( if let Expr::Stack(slot, _) = arg_expr { @@ -967,7 +967,26 @@ impl Engine { } value } else { - self.eval_expr(scope, global, state, lib, this_ptr, arg_expr, level)? + // 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 result = self.eval_expr(scope, global, state, lib, this_ptr, arg_expr, level); + + // Restore function exit if status is not active + #[cfg(feature = "debugging")] + if self.debugger.is_some() + && global.debugger.status == crate::eval::DebuggerStatus::CONTINUE + { + global.debugger.reset_status(reset_debugger); + } + + result? }, arg_expr.position(), )) @@ -986,8 +1005,8 @@ impl Engine { args_expr: &[Expr], constants: &[Dynamic], hashes: FnCallHashes, - pos: Position, capture_scope: bool, + pos: Position, level: usize, ) -> RhaiResult { let mut first_arg = first_arg; @@ -1003,7 +1022,7 @@ impl Engine { KEYWORD_FN_PTR_CALL if total_args >= 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?; + self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; if !arg_value.is::() { return Err(self.make_type_mismatch_err::( @@ -1038,7 +1057,7 @@ impl Engine { KEYWORD_FN_PTR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?; + self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; // Fn - only in function call style return arg_value @@ -1053,7 +1072,7 @@ impl Engine { KEYWORD_FN_PTR_CURRY if total_args > 1 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = self - .get_arg_value(scope, global, state, lib, this_ptr, level, first, constants)?; + .get_arg_value(scope, global, state, lib, this_ptr, first, constants, level)?; if !arg_value.is::() { return Err(self.make_type_mismatch_err::( @@ -1070,7 +1089,7 @@ impl Engine { .iter() .try_fold(fn_curry, |mut curried, expr| -> RhaiResultOf<_> { let (value, _) = self.get_arg_value( - scope, global, state, lib, this_ptr, level, expr, constants, + scope, global, state, lib, this_ptr, expr, constants, level, )?; curried.push(value); Ok(curried) @@ -1084,7 +1103,7 @@ impl Engine { crate::engine::KEYWORD_IS_SHARED if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, _) = - self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?; + self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; return Ok(arg_value.is_shared().into()); } @@ -1093,14 +1112,14 @@ impl Engine { crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = self - .get_arg_value(scope, global, state, lib, this_ptr, level, first, constants)?; + .get_arg_value(scope, global, state, lib, this_ptr, first, constants, level)?; let fn_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; let (arg_value, arg_pos) = self.get_arg_value( - scope, global, state, lib, this_ptr, level, &a_expr[0], constants, + scope, global, state, lib, this_ptr, &a_expr[0], constants, level, )?; let num_params = arg_value @@ -1120,7 +1139,7 @@ impl Engine { KEYWORD_IS_DEF_VAR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?; + self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; let var_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; @@ -1133,7 +1152,7 @@ impl Engine { let orig_scope_len = scope.len(); let arg = first_arg.unwrap(); let (arg_value, pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?; + self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; let script = &arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; @@ -1182,7 +1201,7 @@ impl Engine { .map(|&v| v) .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants) + self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) .map(|(value, _)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1193,7 +1212,7 @@ impl Engine { return self .exec_fn_call( - global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, scope, + scope, global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, level, ) .map(|(v, _)| v); @@ -1216,7 +1235,7 @@ impl Engine { // func(x, ...) -> x.func(...) a_expr.iter().try_for_each(|expr| { - self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants) + self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) .map(|(value, _)| arg_values.push(value.flatten())) })?; @@ -1252,7 +1271,7 @@ impl Engine { .chain(a_expr.iter()) .try_for_each(|expr| { self.get_arg_value( - scope, global, state, lib, this_ptr, level, expr, constants, + scope, global, state, lib, this_ptr, expr, constants, level, ) .map(|(value, _)| arg_values.push(value.flatten())) })?; @@ -1262,7 +1281,7 @@ impl Engine { } self.exec_fn_call( - global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, None, level, + None, global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, level, ) .map(|(v, _)| v) } @@ -1305,7 +1324,7 @@ impl Engine { arg_values.push(Dynamic::UNIT); args_expr.iter().skip(1).try_for_each(|expr| { - self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants) + self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) .map(|(value, _)| arg_values.push(value.flatten())) })?; @@ -1335,7 +1354,7 @@ impl Engine { } else { // func(..., ...) or func(mod::x, ...) args_expr.iter().try_for_each(|expr| { - self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants) + self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) .map(|(value, _)| arg_values.push(value.flatten())) })?; args.extend(arg_values.iter_mut()); @@ -1385,7 +1404,7 @@ impl Engine { mem::swap(&mut global.source, &mut source); let result = self.call_script_fn( - new_scope, global, state, lib, &mut None, fn_def, &mut args, pos, true, + new_scope, global, state, lib, &mut None, fn_def, &mut args, true, pos, level, ); diff --git a/src/func/native.rs b/src/func/native.rs index c5787386..43de5bcb 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -321,6 +321,7 @@ impl<'a> NativeCallContext<'a> { self.engine() .exec_fn_call( + None, &mut global, &mut state, self.lib, @@ -330,7 +331,6 @@ impl<'a> NativeCallContext<'a> { is_ref_mut, is_method_call, Position::NONE, - None, self.level + 1, ) .map(|(r, _)| r) diff --git a/src/func/script.rs b/src/func/script.rs index 9c10e76e..66132cc3 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -31,8 +31,8 @@ impl Engine { this_ptr: &mut Option<&mut Dynamic>, fn_def: &ScriptFnDef, args: &mut FnCallArgs, - pos: Position, rewind_scope: bool, + pos: Position, level: usize, ) -> RhaiResult { #[inline(never)] @@ -73,6 +73,11 @@ impl Engine { return Err(ERR::ErrorStackOverflow(pos).into()); } + #[cfg(feature = "debugging")] + if self.debugger.is_none() && fn_def.body.is_empty() { + return Ok(Dynamic::UNIT); + } + #[cfg(not(feature = "debugging"))] if fn_def.body.is_empty() { return Ok(Dynamic::UNIT); } @@ -136,6 +141,13 @@ impl Engine { (lib, None) }; + #[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)?; + } + // Evaluate the function let mut _result = self .eval_stmt_block( @@ -172,7 +184,7 @@ impl Engine { #[cfg(feature = "debugging")] if self.debugger.is_some() { - match global.debugger.status() { + match global.debugger.status { crate::eval::DebuggerStatus::FunctionExit(n) if n >= level => { let node = crate::ast::Stmt::Noop(pos); let node = (&node).into();