Refactor and add state to debugger.
This commit is contained in:
@@ -8,11 +8,18 @@ use std::fmt;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
/// A standard callback function for debugging.
|
||||
/// Callback function to initialize the debugger.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type OnDebuggingInit = dyn Fn() -> Dynamic;
|
||||
/// Callback function to initialize the debugger.
|
||||
#[cfg(feature = "sync")]
|
||||
pub type OnDebuggingInit = dyn Fn() -> Dynamic + Send + Sync;
|
||||
|
||||
/// Callback function for debugging.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type OnDebuggerCallback =
|
||||
dyn Fn(&mut EvalContext, ASTNode, Option<&str>, Position) -> RhaiResultOf<DebuggerCommand>;
|
||||
/// A standard callback function for debugging.
|
||||
/// Callback function for debugging.
|
||||
#[cfg(feature = "sync")]
|
||||
pub type OnDebuggerCallback = dyn Fn(&mut EvalContext, ASTNode, Option<&str>, Position) -> RhaiResultOf<DebuggerCommand>
|
||||
+ Send
|
||||
@@ -192,6 +199,8 @@ impl fmt::Display for CallStackFrame {
|
||||
pub struct Debugger {
|
||||
/// The current status command.
|
||||
status: DebuggerCommand,
|
||||
/// The current state.
|
||||
state: Dynamic,
|
||||
/// The current set of break-points.
|
||||
break_points: Vec<BreakPoint>,
|
||||
/// The current function call stack.
|
||||
@@ -200,28 +209,44 @@ pub struct Debugger {
|
||||
}
|
||||
|
||||
impl Debugger {
|
||||
/// Create a new [`Debugger`].
|
||||
pub const fn new() -> Self {
|
||||
/// Create a new [`Debugger`] based on an [`Engine`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new(engine: &Engine) -> Self {
|
||||
Self {
|
||||
status: DebuggerCommand::Continue,
|
||||
status: if engine.debugger.is_some() {
|
||||
DebuggerCommand::StepInto
|
||||
} else {
|
||||
DebuggerCommand::Continue
|
||||
},
|
||||
state: if let Some((ref init, _)) = engine.debugger {
|
||||
init()
|
||||
} else {
|
||||
Dynamic::UNIT
|
||||
},
|
||||
break_points: Vec::new(),
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
call_stack: Vec::new(),
|
||||
}
|
||||
}
|
||||
/// Get the function call stack depth.
|
||||
///
|
||||
/// Not available under `no_function`.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
/// Get a reference to the current state.
|
||||
#[inline(always)]
|
||||
pub fn call_stack_len(&self) -> usize {
|
||||
self.call_stack.len()
|
||||
#[must_use]
|
||||
pub fn state(&self) -> &Dynamic {
|
||||
&self.state
|
||||
}
|
||||
/// Get a mutable reference to the current state.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn state_mut(&mut self) -> &mut Dynamic {
|
||||
&mut self.state
|
||||
}
|
||||
/// Get the current call stack.
|
||||
///
|
||||
/// Not available under `no_function`.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn call_stack(&self) -> &[CallStackFrame] {
|
||||
&self.call_stack
|
||||
}
|
||||
@@ -258,10 +283,11 @@ impl Debugger {
|
||||
pub fn status(&self) -> DebuggerCommand {
|
||||
self.status
|
||||
}
|
||||
/// Set the status of this [`Debugger`].
|
||||
/// Get a mutable reference to the current status of this [`Debugger`].
|
||||
#[inline(always)]
|
||||
pub fn set_status(&mut self, status: DebuggerCommand) {
|
||||
self.status = status;
|
||||
#[must_use]
|
||||
pub fn status_mut(&mut self) -> &mut DebuggerCommand {
|
||||
&mut self.status
|
||||
}
|
||||
/// Set the status of this [`Debugger`].
|
||||
#[inline(always)]
|
||||
@@ -270,17 +296,8 @@ impl Debugger {
|
||||
self.status = cmd;
|
||||
}
|
||||
}
|
||||
/// Activate: set the status of this [`Debugger`] to [`DebuggerCommand::StepInto`].
|
||||
/// Deactivate: set the status of this [`Debugger`] to [`DebuggerCommand::Continue`].
|
||||
#[inline(always)]
|
||||
pub fn activate(&mut self, active: bool) {
|
||||
if active {
|
||||
self.set_status(DebuggerCommand::StepInto);
|
||||
} else {
|
||||
self.set_status(DebuggerCommand::Continue);
|
||||
}
|
||||
}
|
||||
/// Does a particular [`AST` Node][ASTNode] trigger a break-point?
|
||||
#[must_use]
|
||||
pub fn is_break_point(&self, src: &str, node: ASTNode) -> bool {
|
||||
let _src = src;
|
||||
|
||||
@@ -347,7 +364,7 @@ impl Engine {
|
||||
if let Some(cmd) =
|
||||
self.run_debugger_with_reset(scope, global, state, lib, this_ptr, node, level)?
|
||||
{
|
||||
global.debugger.set_status(cmd);
|
||||
global.debugger.status = cmd;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -375,7 +392,7 @@ impl Engine {
|
||||
node: impl Into<ASTNode<'a>>,
|
||||
level: usize,
|
||||
) -> RhaiResultOf<Option<DebuggerCommand>> {
|
||||
if let Some(ref on_debugger) = self.debugger {
|
||||
if let Some((_, ref on_debugger)) = self.debugger {
|
||||
let node = node.into();
|
||||
|
||||
// Skip transitive nodes
|
||||
@@ -415,19 +432,19 @@ impl Engine {
|
||||
|
||||
match command {
|
||||
DebuggerCommand::Continue => {
|
||||
global.debugger.set_status(DebuggerCommand::Continue);
|
||||
global.debugger.status = DebuggerCommand::Continue;
|
||||
Ok(None)
|
||||
}
|
||||
DebuggerCommand::Next => {
|
||||
global.debugger.set_status(DebuggerCommand::Continue);
|
||||
global.debugger.status = DebuggerCommand::Continue;
|
||||
Ok(Some(DebuggerCommand::Next))
|
||||
}
|
||||
DebuggerCommand::StepInto => {
|
||||
global.debugger.set_status(DebuggerCommand::StepInto);
|
||||
global.debugger.status = DebuggerCommand::StepInto;
|
||||
Ok(None)
|
||||
}
|
||||
DebuggerCommand::StepOver => {
|
||||
global.debugger.set_status(DebuggerCommand::Continue);
|
||||
global.debugger.status = DebuggerCommand::Continue;
|
||||
Ok(Some(DebuggerCommand::StepOver))
|
||||
}
|
||||
}
|
||||
|
@@ -64,8 +64,8 @@ impl Engine {
|
||||
// Qualified variable access
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
(_, Some((namespace, hash_var)), var_name) => {
|
||||
// foo:bar::baz::VARIABLE
|
||||
if let Some(module) = self.search_imports(global, state, namespace) {
|
||||
// foo:bar::baz::VARIABLE
|
||||
return match module.get_qualified_var(*hash_var) {
|
||||
Ok(target) => {
|
||||
let mut target = target.clone();
|
||||
@@ -89,18 +89,16 @@ impl Engine {
|
||||
};
|
||||
}
|
||||
|
||||
// global::VARIABLE
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if namespace.len() == 1 && namespace[0].name == crate::engine::KEYWORD_GLOBAL {
|
||||
// global::VARIABLE
|
||||
let global_constants = global.constants_mut();
|
||||
let mut guard = crate::func::native::locked_write(&global.constants);
|
||||
|
||||
if let Some(mut guard) = global_constants {
|
||||
if let Some(value) = guard.get_mut(var_name) {
|
||||
let mut target: Target = value.clone().into();
|
||||
// Module variables are constant
|
||||
target.set_access_mode(AccessMode::ReadOnly);
|
||||
return Ok((target.into(), *_var_pos));
|
||||
}
|
||||
if let Some(value) = guard.get_mut(var_name) {
|
||||
let mut target: Target = value.clone().into();
|
||||
// Module variables are constant
|
||||
target.set_access_mode(AccessMode::ReadOnly);
|
||||
return Ok((target.into(), *_var_pos));
|
||||
}
|
||||
|
||||
return Err(ERR::ErrorVariableNotFound(
|
||||
|
@@ -1,13 +1,13 @@
|
||||
//! Global runtime state.
|
||||
|
||||
use crate::func::{CallableFunction, IteratorFn};
|
||||
use crate::{Identifier, Module, Shared, StaticVec};
|
||||
use crate::{Engine, Identifier, Module, Shared, StaticVec};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
any::TypeId,
|
||||
fmt,
|
||||
iter::{FromIterator, Rev, Zip},
|
||||
iter::{Rev, Zip},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
@@ -44,8 +44,7 @@ pub struct GlobalRuntimeState<'a> {
|
||||
/// Cache of globally-defined constants.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
constants:
|
||||
Option<Shared<crate::Locked<std::collections::BTreeMap<Identifier, crate::Dynamic>>>>,
|
||||
pub(crate) constants: crate::Locked<std::collections::BTreeMap<Identifier, crate::Dynamic>>,
|
||||
/// Debugging interface.
|
||||
#[cfg(feature = "debugging")]
|
||||
pub debugger: super::Debugger,
|
||||
@@ -53,18 +52,11 @@ pub struct GlobalRuntimeState<'a> {
|
||||
dummy: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl Default for GlobalRuntimeState<'_> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl GlobalRuntimeState<'_> {
|
||||
/// Create a new [`GlobalRuntimeState`].
|
||||
/// Create a new [`GlobalRuntimeState`] based on an [`Engine`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
pub fn new(engine: &Engine) -> Self {
|
||||
Self {
|
||||
keys: StaticVec::new_const(),
|
||||
modules: StaticVec::new_const(),
|
||||
@@ -77,9 +69,9 @@ impl GlobalRuntimeState<'_> {
|
||||
fn_hash_indexing: (0, 0),
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
constants: None,
|
||||
constants: std::collections::BTreeMap::new().into(),
|
||||
#[cfg(feature = "debugging")]
|
||||
debugger: crate::eval::Debugger::new(),
|
||||
debugger: crate::eval::Debugger::new(engine),
|
||||
dummy: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
@@ -193,33 +185,6 @@ impl GlobalRuntimeState<'_> {
|
||||
s => Some(s),
|
||||
}
|
||||
}
|
||||
/// Get a mutable reference to the cache of globally-defined constants.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[must_use]
|
||||
pub(crate) fn constants_mut<'a>(
|
||||
&'a mut self,
|
||||
) -> Option<
|
||||
impl std::ops::DerefMut<Target = std::collections::BTreeMap<Identifier, crate::Dynamic>> + 'a,
|
||||
> {
|
||||
if let Some(ref global_constants) = self.constants {
|
||||
Some(crate::func::native::shared_write_lock(global_constants))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Set a constant into the cache of globally-defined constants.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub(crate) fn set_constant(&mut self, name: impl Into<Identifier>, value: crate::Dynamic) {
|
||||
if self.constants.is_none() {
|
||||
let dict: crate::Locked<_> = std::collections::BTreeMap::new().into();
|
||||
self.constants = Some(dict.into());
|
||||
}
|
||||
|
||||
crate::func::native::shared_write_lock(self.constants.as_mut().expect("`Some`"))
|
||||
.insert(name.into(), value);
|
||||
}
|
||||
/// Get the pre-calculated index getter hash.
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[must_use]
|
||||
@@ -262,22 +227,13 @@ impl IntoIterator for GlobalRuntimeState<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Into<Identifier>, M: Into<Shared<Module>>> FromIterator<(K, M)> for GlobalRuntimeState<'_> {
|
||||
#[inline]
|
||||
fn from_iter<T: IntoIterator<Item = (K, M)>>(iter: T) -> Self {
|
||||
let mut lib = Self::new();
|
||||
lib.extend(iter);
|
||||
lib
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Into<Identifier>, M: Into<Shared<Module>>> Extend<(K, M)> for GlobalRuntimeState<'_> {
|
||||
#[inline]
|
||||
fn extend<T: IntoIterator<Item = (K, M)>>(&mut self, iter: T) {
|
||||
iter.into_iter().for_each(|(k, m)| {
|
||||
for (k, m) in iter {
|
||||
self.keys.push(k.into());
|
||||
self.modules.push(m.into());
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -14,7 +14,7 @@ pub use chaining::{ChainArgument, ChainType};
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use debugger::CallStackFrame;
|
||||
#[cfg(feature = "debugging")]
|
||||
pub use debugger::{BreakPoint, Debugger, DebuggerCommand, OnDebuggerCallback};
|
||||
pub use debugger::{BreakPoint, Debugger, DebuggerCommand, OnDebuggerCallback, OnDebuggingInit};
|
||||
pub use eval_context::EvalContext;
|
||||
pub use eval_state::EvalState;
|
||||
pub use global_state::GlobalRuntimeState;
|
||||
|
@@ -818,8 +818,8 @@ impl Engine {
|
||||
&& entry_type == AccessMode::ReadOnly
|
||||
&& lib.iter().any(|&m| !m.is_empty())
|
||||
{
|
||||
// Add a global constant if at top level and there are functions
|
||||
global.set_constant(var_name.clone(), value.clone());
|
||||
crate::func::native::locked_write(&global.constants)
|
||||
.insert(var_name.clone(), value.clone());
|
||||
}
|
||||
|
||||
if export {
|
||||
@@ -869,23 +869,20 @@ impl Engine {
|
||||
if let Ok(path) = path_result {
|
||||
use crate::ModuleResolver;
|
||||
|
||||
let source = match global.source.as_str() {
|
||||
"" => None,
|
||||
s => Some(s),
|
||||
};
|
||||
let path_pos = expr.position();
|
||||
|
||||
let module_result = global
|
||||
.embedded_module_resolver
|
||||
let resolver = global.embedded_module_resolver.clone();
|
||||
|
||||
let module_result = resolver
|
||||
.as_ref()
|
||||
.and_then(|r| match r.resolve(self, source, &path, path_pos) {
|
||||
.and_then(|r| match r.resolve_raw(self, global, &path, path_pos) {
|
||||
Err(err) if matches!(*err, ERR::ErrorModuleNotFound(_, _)) => None,
|
||||
result => Some(result),
|
||||
})
|
||||
.or_else(|| {
|
||||
self.module_resolver
|
||||
.as_ref()
|
||||
.map(|r| r.resolve(self, source, &path, path_pos))
|
||||
.map(|r| r.resolve_raw(self, global, &path, path_pos))
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
Err(ERR::ErrorModuleNotFound(path.to_string(), path_pos).into())
|
||||
|
Reference in New Issue
Block a user