Add ability for debugger to throw errors.

This commit is contained in:
Stephen Chung
2022-01-25 21:31:35 +08:00
parent b4f679d35f
commit 3c2ac7f0c5
8 changed files with 78 additions and 50 deletions

View File

@@ -156,7 +156,7 @@ impl Engine {
if !_terminate_chaining =>
{
#[cfg(feature = "debugging")]
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();
@@ -204,7 +204,7 @@ impl Engine {
// xxx[rhs] op= new_val
_ if new_val.is_some() => {
#[cfg(feature = "debugging")]
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();
@@ -249,7 +249,7 @@ impl Engine {
// xxx[rhs]
_ => {
#[cfg(feature = "debugging")]
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,
@@ -270,7 +270,7 @@ impl Engine {
#[cfg(feature = "debugging")]
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,
@@ -292,7 +292,7 @@ impl Engine {
// {xxx:map}.id op= ???
Expr::Property(x) if target.is::<crate::Map>() && new_val.is_some() => {
#[cfg(feature = "debugging")]
self.run_debugger(scope, global, state, lib, this_ptr, rhs, level);
self.run_debugger(scope, global, state, lib, this_ptr, rhs, level)?;
let (name, pos) = &x.2;
let ((new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`");
@@ -313,7 +313,7 @@ impl Engine {
// {xxx:map}.id
Expr::Property(x) if target.is::<crate::Map>() => {
#[cfg(feature = "debugging")]
self.run_debugger(scope, global, state, lib, this_ptr, rhs, level);
self.run_debugger(scope, global, state, lib, this_ptr, rhs, level)?;
let (name, pos) = &x.2;
let index = name.into();
@@ -325,7 +325,7 @@ impl Engine {
// xxx.id op= ???
Expr::Property(x) if new_val.is_some() => {
#[cfg(feature = "debugging")]
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, pos)) = x.as_ref();
let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`");
@@ -404,7 +404,7 @@ impl Engine {
// xxx.id
Expr::Property(x) => {
#[cfg(feature = "debugging")]
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, pos)) = x.as_ref();
let hash = crate::ast::FnCallHashes::from_native(*hash_get);
@@ -446,7 +446,7 @@ impl Engine {
#[cfg(feature = "debugging")]
self.run_debugger(
scope, global, state, lib, this_ptr, _node, level,
);
)?;
let (name, pos) = &p.2;
let index = name.into();
@@ -462,7 +462,7 @@ impl Engine {
#[cfg(feature = "debugging")]
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,
@@ -499,7 +499,7 @@ impl Engine {
#[cfg(feature = "debugging")]
self.run_debugger(
scope, global, state, lib, this_ptr, _node, level,
);
)?;
let ((getter, hash_get), (setter, hash_set), (name, pos)) =
p.as_ref();
@@ -603,7 +603,7 @@ impl Engine {
#[cfg(feature = "debugging")]
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,
@@ -668,7 +668,7 @@ impl Engine {
// id.??? or id[???]
Expr::Variable(_, var_pos, x) => {
#[cfg(feature = "debugging")]
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)?;

View File

@@ -3,19 +3,21 @@
use super::{EvalContext, EvalState, GlobalRuntimeState};
use crate::ast::{ASTNode, Expr, Stmt};
use crate::{Dynamic, Engine, Identifier, Module, Position, Scope};
use crate::{Dynamic, Engine, Identifier, Module, Position, RhaiResultOf, Scope};
use std::fmt;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
/// A standard callback function for debugging.
#[cfg(not(feature = "sync"))]
pub type OnDebuggerCallback =
Box<dyn Fn(&mut EvalContext, ASTNode, Option<&str>, Position) -> DebuggerCommand + 'static>;
pub type OnDebuggerCallback = Box<
dyn Fn(&mut EvalContext, ASTNode, Option<&str>, Position) -> RhaiResultOf<DebuggerCommand>
+ 'static,
>;
/// A standard callback function for debugging.
#[cfg(feature = "sync")]
pub type OnDebuggerCallback = Box<
dyn Fn(&mut EvalContext, ASTNode, Option<&str>, Position) -> DebuggerCommand
dyn Fn(&mut EvalContext, ASTNode, Option<&str>, Position) -> RhaiResultOf<DebuggerCommand>
+ Send
+ Sync
+ 'static,
@@ -28,8 +30,10 @@ pub enum DebuggerCommand {
Continue,
// Step into the next expression, diving into functions.
StepInto,
// Run to the next statement, stepping over functions.
// Run to the next expression or statement, stepping over functions.
StepOver,
// Run to the next statement, skipping over functions.
Next,
}
/// A break-point for debugging.
@@ -318,12 +322,14 @@ impl Engine {
this_ptr: &mut Option<&mut Dynamic>,
node: impl Into<ASTNode<'a>>,
level: usize,
) {
) -> RhaiResultOf<()> {
if let Some(cmd) =
self.run_debugger_with_reset(scope, global, state, lib, this_ptr, node, level)
self.run_debugger_with_reset(scope, global, state, lib, this_ptr, node, level)?
{
global.debugger.set_status(cmd);
}
Ok(())
}
/// Run the debugger callback.
///
@@ -347,18 +353,18 @@ impl Engine {
this_ptr: &mut Option<&mut Dynamic>,
node: impl Into<ASTNode<'a>>,
level: usize,
) -> Option<DebuggerCommand> {
) -> RhaiResultOf<Option<DebuggerCommand>> {
if let Some(ref on_debugger) = self.debugger {
let node = node.into();
let stop = match global.debugger.status {
DebuggerCommand::Continue => false,
DebuggerCommand::StepOver => matches!(node, ASTNode::Stmt(_)),
DebuggerCommand::StepInto => true,
DebuggerCommand::Next => matches!(node, ASTNode::Stmt(_)),
DebuggerCommand::StepInto | DebuggerCommand::StepOver => true,
};
if !stop && !global.debugger.is_break_point(&global.source, node) {
return None;
return Ok(None);
}
let source = global.source.clone();
@@ -378,24 +384,28 @@ impl Engine {
level,
};
let command = on_debugger(&mut context, node, source, node.position());
let command = on_debugger(&mut context, node, source, node.position())?;
match command {
DebuggerCommand::Continue => {
global.debugger.set_status(DebuggerCommand::Continue);
None
Ok(None)
}
DebuggerCommand::Next => {
global.debugger.set_status(DebuggerCommand::Continue);
Ok(Some(DebuggerCommand::Next))
}
DebuggerCommand::StepInto => {
global.debugger.set_status(DebuggerCommand::StepInto);
None
Ok(None)
}
DebuggerCommand::StepOver => {
global.debugger.set_status(DebuggerCommand::Continue);
Some(DebuggerCommand::StepOver)
Ok(Some(DebuggerCommand::StepOver))
}
}
} else {
None
Ok(None)
}
}
}

View File

@@ -267,7 +267,7 @@ impl Engine {
if let Expr::FnCall(x, pos) = expr {
#[cfg(feature = "debugging")]
let reset_debugger =
self.run_debugger_with_reset(scope, global, state, lib, this_ptr, expr, level);
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())?;
@@ -286,7 +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")]
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())?;
@@ -304,7 +304,7 @@ impl Engine {
#[cfg(feature = "debugging")]
let reset_debugger =
self.run_debugger_with_reset(scope, global, state, lib, this_ptr, expr, level);
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())?;

View File

@@ -196,7 +196,7 @@ impl Engine {
) -> RhaiResult {
#[cfg(feature = "debugging")]
let reset_debugger =
self.run_debugger_with_reset(scope, global, state, lib, this_ptr, stmt, level);
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.