Add property breakpoints.

This commit is contained in:
Stephen Chung 2022-01-25 18:21:05 +08:00
parent cc64ae3939
commit b4f679d35f
2 changed files with 53 additions and 14 deletions

View File

@ -12,7 +12,7 @@ use std::{
}; };
/// Pretty-print source line. /// Pretty-print source line.
fn print_source(lines: &[String], pos: Position) { fn print_source(lines: &[String], pos: Position, offset: usize) {
let line_no = if lines.len() > 1 { let line_no = if lines.len() > 1 {
if pos.is_none() { if pos.is_none() {
"".to_string() "".to_string()
@ -32,7 +32,9 @@ fn print_source(lines: &[String], pos: Position) {
println!("{}{}", line_no, lines[pos.line().unwrap() - 1]); println!("{}{}", line_no, lines[pos.line().unwrap() - 1]);
// Display position marker // Display position marker
println!("{0:>1$}", "^", line_no.len() + pos.position().unwrap(),); if let Some(pos) = pos.position() {
println!("{0:>1$}", "^", line_no.len() + pos + offset);
}
} }
} }
@ -83,6 +85,7 @@ fn print_debug_help() {
println!("clear => delete all break-points"); println!("clear => delete all break-points");
println!("break => set a new break-point at the current position"); println!("break => set a new break-point at the current position");
println!("break <line#> => set a new break-point at a line number"); println!("break <line#> => set a new break-point at a line number");
println!("break .<prop> => set a new break-point for a property access");
println!("break <func> => set a new break-point for a function call"); println!("break <func> => set a new break-point for a function call");
println!( println!(
"break <func> <#args> => set a new break-point for a function call with #args arguments" "break <func> <#args> => set a new break-point for a function call with #args arguments"
@ -198,7 +201,7 @@ fn main() {
let lines: Vec<_> = script.trim().split('\n').map(|s| s.to_string()).collect(); let lines: Vec<_> = script.trim().split('\n').map(|s| s.to_string()).collect();
engine.on_debugger(move |context, node, source, pos| { engine.on_debugger(move |context, node, source, pos| {
print_source(&lines, pos); print_source(&lines, pos, 0);
let mut input = String::new(); let mut input = String::new();
@ -210,7 +213,12 @@ fn main() {
match stdin().read_line(&mut input) { match stdin().read_line(&mut input) {
Ok(0) => break DebuggerCommand::Continue, Ok(0) => break DebuggerCommand::Continue,
Ok(_) => match input.trim_end().split(' ').collect::<Vec<_>>().as_slice() { Ok(_) => match input
.trim_end()
.split_whitespace()
.collect::<Vec<_>>()
.as_slice()
{
["help", ..] => print_debug_help(), ["help", ..] => print_debug_help(),
["exit", ..] | ["quit", ..] | ["kill", ..] => { ["exit", ..] | ["quit", ..] | ["kill", ..] => {
println!("Script terminated. Bye!"); println!("Script terminated. Bye!");
@ -221,7 +229,7 @@ fn main() {
println!(); println!();
} }
["continue", ..] => break DebuggerCommand::Continue, ["continue", ..] => break DebuggerCommand::Continue,
[""] | ["step", ..] => break DebuggerCommand::StepInto, [] | ["step", ..] => break DebuggerCommand::StepInto,
["next", ..] => break DebuggerCommand::StepOver, ["next", ..] => break DebuggerCommand::StepOver,
["scope", ..] => print_scope(context.scope()), ["scope", ..] => print_scope(context.scope()),
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -250,10 +258,11 @@ fn main() {
.enumerate() .enumerate()
.for_each(|(i, bp)| match bp { .for_each(|(i, bp)| match bp {
BreakPoint::AtPosition { pos, .. } => { BreakPoint::AtPosition { pos, .. } => {
println!("[{}]", i + 1); let line_num = format!("[{}] line ", i + 1);
print_source(&lines, *pos); print!("{}", line_num);
print_source(&lines, *pos, line_num.len());
} }
_ => println!("[{}]\n{}", i + 1, bp), _ => println!("[{}] {}", i + 1, bp),
}), }),
["enable", n, ..] => { ["enable", n, ..] => {
if let Ok(n) = n.parse::<usize>() { if let Ok(n) = n.parse::<usize>() {
@ -340,7 +349,19 @@ fn main() {
} }
} }
["break", param] => { ["break", param] => {
if let Ok(n) = param.parse::<usize>() { if param.starts_with('.') && param.len() > 1 {
// Property name
let bp = rhai::debugger::BreakPoint::AtProperty {
name: param[1..].into(),
enabled: true,
};
println!("Break-point added for {}", bp);
context
.global_runtime_state_mut()
.debugger
.break_points_mut()
.push(bp);
} else if let Ok(n) = param.parse::<usize>() {
// Numeric parameter // Numeric parameter
let range = 1..=lines.len(); let range = 1..=lines.len();
if range.contains(&n) { if range.contains(&n) {
@ -385,7 +406,7 @@ fn main() {
.break_points_mut() .break_points_mut()
.push(bp); .push(bp);
} }
cmd => eprintln!("Invalid debugger command: '{}'", cmd[0]), [cmd, ..] => eprintln!("Invalid debugger command: '{}'", cmd),
}, },
Err(err) => panic!("input error: {}", err), Err(err) => panic!("input error: {}", err),
} }

View File

@ -53,6 +53,8 @@ pub enum BreakPoint {
args: usize, args: usize,
enabled: bool, enabled: bool,
}, },
/// Break at a particular property .
AtProperty { name: Identifier, enabled: bool },
} }
impl fmt::Display for BreakPoint { impl fmt::Display for BreakPoint {
@ -102,6 +104,16 @@ impl fmt::Display for BreakPoint {
} }
Ok(()) Ok(())
} }
Self::AtProperty {
name: prop,
enabled,
} => {
write!(f, ".{}", prop)?;
if !*enabled {
f.write_str(" (disabled)")?;
}
Ok(())
}
} }
} }
} }
@ -113,7 +125,9 @@ impl BreakPoint {
match self { match self {
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
Self::AtPosition { enabled, .. } => *enabled, Self::AtPosition { enabled, .. } => *enabled,
Self::AtFunctionName { enabled, .. } | Self::AtFunctionCall { enabled, .. } => *enabled, Self::AtFunctionName { enabled, .. }
| Self::AtFunctionCall { enabled, .. }
| Self::AtProperty { enabled, .. } => *enabled,
} }
} }
/// Enable/disable this [`BreakPoint`]. /// Enable/disable this [`BreakPoint`].
@ -122,9 +136,9 @@ impl BreakPoint {
match self { match self {
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
Self::AtPosition { enabled, .. } => *enabled = value, Self::AtPosition { enabled, .. } => *enabled = value,
Self::AtFunctionName { enabled, .. } | Self::AtFunctionCall { enabled, .. } => { Self::AtFunctionName { enabled, .. }
*enabled = value | Self::AtFunctionCall { enabled, .. }
} | Self::AtProperty { enabled, .. } => *enabled = value,
} }
} }
} }
@ -272,6 +286,10 @@ impl Debugger {
} }
_ => false, _ => false,
}, },
BreakPoint::AtProperty { name, .. } => match node {
ASTNode::Expr(Expr::Property(x)) => (x.2).0 == *name,
_ => false,
},
}) })
} }
/// Get a slice of all [`BreakPoint`]'s. /// Get a slice of all [`BreakPoint`]'s.