Add Start/End to DebuggerEvent.
This commit is contained in:
parent
34dfe841cd
commit
2889ca0988
13
CHANGELOG.md
13
CHANGELOG.md
@ -15,17 +15,22 @@ Script-breaking changes
|
||||
|
||||
* _Strict Variables Mode_ no longer returns an error when an undeclared variable matches a variable/constant in the provided external `Scope`.
|
||||
|
||||
New API
|
||||
-------
|
||||
|
||||
* `Module::eval_ast_as_new_raw` is made public as a low-level API.
|
||||
* `format_map_as_json` is provided globally, which is the same as `to_json` for object maps.
|
||||
* `Engine::call_fn_raw_raw` is added to add speed to repeated function calls.
|
||||
* `Engine::eval_statements_raw` is added to evaluate a sequence of statements.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
* `Module::eval_ast_as_new_raw` is made public as a low-level API.
|
||||
* Improper `switch` case condition syntax is now caught at parse time.
|
||||
* `Engine::parse_json` now natively handles nested JSON inputs (using a token remap filter) without needing to replace `{` with `#{`.
|
||||
* `to_json` is added to object maps to cheaply convert it to JSON format (`()` is mapped to `null`, all other data types must be supported by JSON)
|
||||
* A global function `format_map_as_json` is provided which is the same as `to_json` for object maps.
|
||||
* `FileModuleResolver` now accepts a custom `Scope` to provide constants for optimization.
|
||||
* A new low-level method `Engine::call_fn_raw_raw` is added to add speed to repeated function calls.
|
||||
* A new low-level method `Engine::eval_statements_raw` is added to evaluate a sequence of statements.
|
||||
* New variants, `Start` and `End`, are added to `DebuggerEvent` triggered at the start/end of script evaluation.
|
||||
|
||||
|
||||
Version 1.6.1
|
||||
|
@ -255,26 +255,36 @@ impl Engine {
|
||||
let mut this_ptr = this_ptr;
|
||||
let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
||||
|
||||
// Check for data race.
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
crate::func::call::ensure_no_data_race(name, &mut args, false)?;
|
||||
|
||||
let lib = &[ast.as_ref()];
|
||||
let fn_def = ast
|
||||
.shared_lib()
|
||||
.get_script_fn(name, args.len())
|
||||
.ok_or_else(|| ERR::ErrorFunctionNotFound(name.into(), Position::NONE))?;
|
||||
|
||||
// Check for data race.
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
crate::func::call::ensure_no_data_race(name, &mut args, false)?;
|
||||
|
||||
self.call_script_fn(
|
||||
let result = self.call_script_fn(
|
||||
scope,
|
||||
global,
|
||||
caches,
|
||||
&[ast.as_ref()],
|
||||
lib,
|
||||
&mut this_ptr,
|
||||
fn_def,
|
||||
&mut args,
|
||||
rewind_scope,
|
||||
Position::NONE,
|
||||
0,
|
||||
)
|
||||
)?;
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
global.debugger.status = crate::eval::DebuggerStatus::Terminate;
|
||||
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||
self.run_debugger(scope, global, lib, &mut this_ptr, node, 0)?;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
@ -191,6 +191,14 @@ impl Engine {
|
||||
|
||||
let result = self.eval_ast_with_scope_raw(scope, global, ast, 0)?;
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
global.debugger.status = crate::eval::DebuggerStatus::Terminate;
|
||||
let lib = &[ast.as_ref()];
|
||||
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||
self.run_debugger(scope, global, lib, &mut None, node, 0)?;
|
||||
}
|
||||
|
||||
let typ = self.map_type_name(result.type_name());
|
||||
|
||||
result.try_cast::<T>().ok_or_else(|| {
|
||||
|
@ -64,6 +64,15 @@ impl Engine {
|
||||
};
|
||||
self.eval_global_statements(scope, global, caches, statements, lib, 0)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
global.debugger.status = crate::eval::DebuggerStatus::Terminate;
|
||||
let lib = &[ast.as_ref()];
|
||||
let node = &crate::ast::Stmt::Noop(crate::Position::NONE);
|
||||
self.run_debugger(scope, global, lib, &mut None, node, 0)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -848,6 +848,7 @@ impl AsRef<crate::Shared<crate::Module>> for AST {
|
||||
/// _(internals)_ An [`AST`] node, consisting of either an [`Expr`] or a [`Stmt`].
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Debug, Clone, Copy, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum ASTNode<'a> {
|
||||
/// A statement ([`Stmt`]).
|
||||
Stmt(&'a Stmt),
|
||||
|
@ -348,6 +348,7 @@ impl FloatWrapper<crate::FLOAT> {
|
||||
/// _(internals)_ An expression sub-tree.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Clone, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum Expr {
|
||||
/// Dynamic constant.
|
||||
///
|
||||
|
@ -366,6 +366,7 @@ impl Extend<Stmt> for StmtBlock {
|
||||
/// _(internals)_ A statement.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum Stmt {
|
||||
/// No-op.
|
||||
Noop(Position),
|
||||
|
@ -68,7 +68,10 @@ fn print_current_source(
|
||||
.unwrap();
|
||||
let src = source.unwrap_or("");
|
||||
if src != current_source {
|
||||
println!(">>> Source => {}", source.unwrap_or("main script"));
|
||||
println!(
|
||||
"\x1b[34m>>> Source => {}\x1b[39m",
|
||||
source.unwrap_or("main script")
|
||||
);
|
||||
*current_source = src.into();
|
||||
}
|
||||
if !src.is_empty() {
|
||||
@ -236,6 +239,8 @@ fn debug_callback(
|
||||
) -> Result<DebuggerCommand, Box<EvalAltResult>> {
|
||||
// Check event
|
||||
match event {
|
||||
DebuggerEvent::Start => println!("\x1b[32m! Script start\x1b[39m"),
|
||||
DebuggerEvent::End => println!("\x1b[31m! Script end\x1b[39m"),
|
||||
DebuggerEvent::Step => (),
|
||||
DebuggerEvent::BreakPoint(n) => {
|
||||
match context.global_runtime_state().debugger.break_points()[n] {
|
||||
@ -249,6 +254,7 @@ fn debug_callback(
|
||||
BreakPoint::AtProperty { ref name, .. } => {
|
||||
println!("! Property {} accessed.", name)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
DebuggerEvent::FunctionExitWithValue(r) => {
|
||||
@ -277,6 +283,7 @@ fn debug_callback(
|
||||
err
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
// Print current source line
|
||||
@ -640,4 +647,6 @@ fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("Script terminated. Bye!");
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ pub type OnDebuggerCallback = dyn Fn(
|
||||
|
||||
/// A command for the debugger on the next iteration.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum DebuggerCommand {
|
||||
// Continue normal execution.
|
||||
Continue,
|
||||
@ -60,29 +61,31 @@ impl Default for DebuggerCommand {
|
||||
|
||||
/// The debugger status.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum DebuggerStatus {
|
||||
// Script evaluation starts.
|
||||
Init,
|
||||
// Stop at the next statement or expression.
|
||||
Next(bool, bool),
|
||||
// Run to the end of the current level of function call.
|
||||
FunctionExit(usize),
|
||||
}
|
||||
|
||||
impl Default for DebuggerStatus {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self::CONTINUE
|
||||
}
|
||||
// Script evaluation ends.
|
||||
Terminate,
|
||||
}
|
||||
|
||||
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);
|
||||
pub const INTO: Self = Self::Next(false, true);
|
||||
}
|
||||
|
||||
/// A event that triggers the debugger.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub enum DebuggerEvent<'a> {
|
||||
// Script evaluation starts.
|
||||
Start,
|
||||
// Break on next step.
|
||||
Step,
|
||||
// Break on break-point.
|
||||
@ -91,10 +94,13 @@ pub enum DebuggerEvent<'a> {
|
||||
FunctionExitWithValue(&'a Dynamic),
|
||||
// Return from a function with a value.
|
||||
FunctionExitWithError(&'a EvalAltResult),
|
||||
// Script evaluation ends.
|
||||
End,
|
||||
}
|
||||
|
||||
/// A break-point for debugging.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum BreakPoint {
|
||||
/// Break at a particular position under a particular source.
|
||||
///
|
||||
@ -264,7 +270,7 @@ impl Debugger {
|
||||
pub fn new(engine: &Engine) -> Self {
|
||||
Self {
|
||||
status: if engine.debugger.is_some() {
|
||||
DebuggerStatus::STEP
|
||||
DebuggerStatus::Init
|
||||
} else {
|
||||
DebuggerStatus::CONTINUE
|
||||
},
|
||||
@ -469,22 +475,27 @@ impl Engine {
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let stop = match global.debugger.status {
|
||||
DebuggerStatus::Next(false, false) => false,
|
||||
DebuggerStatus::Next(true, false) => matches!(node, ASTNode::Stmt(..)),
|
||||
DebuggerStatus::Next(false, true) => matches!(node, ASTNode::Expr(..)),
|
||||
DebuggerStatus::Next(true, true) => true,
|
||||
DebuggerStatus::FunctionExit(..) => false,
|
||||
let event = match global.debugger.status {
|
||||
DebuggerStatus::Init => Some(DebuggerEvent::Start),
|
||||
DebuggerStatus::CONTINUE => None,
|
||||
DebuggerStatus::NEXT if matches!(node, ASTNode::Stmt(..)) => Some(DebuggerEvent::Step),
|
||||
DebuggerStatus::NEXT => None,
|
||||
DebuggerStatus::INTO if matches!(node, ASTNode::Expr(..)) => Some(DebuggerEvent::Step),
|
||||
DebuggerStatus::INTO => None,
|
||||
DebuggerStatus::STEP => Some(DebuggerEvent::Step),
|
||||
DebuggerStatus::FunctionExit(..) => None,
|
||||
DebuggerStatus::Terminate => Some(DebuggerEvent::End),
|
||||
};
|
||||
|
||||
let event = if stop {
|
||||
DebuggerEvent::Step
|
||||
} else {
|
||||
let event = match event {
|
||||
Some(e) => e,
|
||||
None => {
|
||||
if let Some(bp) = global.debugger.is_break_point(&global.source, node) {
|
||||
DebuggerEvent::BreakPoint(bp)
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.run_debugger_raw(scope, global, lib, this_ptr, node, event, level)
|
||||
|
@ -10,6 +10,7 @@ use std::prelude::v1::*;
|
||||
|
||||
/// A type encapsulating a function callable by Rhai.
|
||||
#[derive(Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum CallableFunction {
|
||||
/// A pure native Rust function with all arguments passed by value.
|
||||
Pure(Shared<FnAny>),
|
||||
|
@ -1,7 +1,6 @@
|
||||
//! Module defining interfaces to native-Rust functions.
|
||||
|
||||
use super::call::FnCallArgs;
|
||||
use crate::api::events::VarDefInfo;
|
||||
use crate::ast::FnCallHashes;
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::plugin::PluginFunction;
|
||||
@ -9,7 +8,7 @@ use crate::tokenizer::{Token, TokenizeState};
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Module, Position, RhaiResult,
|
||||
RhaiResultOf, StaticVec, ERR,
|
||||
RhaiResultOf, StaticVec, VarDefInfo, ERR,
|
||||
};
|
||||
use std::any::type_name;
|
||||
#[cfg(feature = "no_std")]
|
||||
|
@ -121,9 +121,11 @@ impl Engine {
|
||||
ref constants,
|
||||
}) = fn_def.environ
|
||||
{
|
||||
for (n, m) in imports.iter().cloned() {
|
||||
global.push_import(n, m)
|
||||
}
|
||||
imports
|
||||
.iter()
|
||||
.cloned()
|
||||
.for_each(|(n, m)| global.push_import(n, m));
|
||||
|
||||
(
|
||||
if fn_lib.is_empty() {
|
||||
lib
|
||||
@ -167,7 +169,6 @@ impl Engine {
|
||||
} else {
|
||||
format!("{} @ '{}' < {}", name, src, fn_def.name)
|
||||
};
|
||||
|
||||
make_error(fn_name, fn_def, global, err, pos)
|
||||
}
|
||||
// System errors are passed straight-through
|
||||
|
@ -349,6 +349,7 @@ impl fmt::Debug for Span {
|
||||
/// _(internals)_ A Rhai language token.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum Token {
|
||||
/// An `INT` constant.
|
||||
IntegerConstant(INT),
|
||||
|
Loading…
Reference in New Issue
Block a user