Refactor and add state to debugger.
This commit is contained in:
parent
20baae71d4
commit
66af69aaff
@ -17,10 +17,7 @@ struct Handler {
|
||||
}
|
||||
|
||||
fn print_scope(scope: &Scope) {
|
||||
scope
|
||||
.iter_raw()
|
||||
.enumerate()
|
||||
.for_each(|(i, (name, constant, value))| {
|
||||
for (i, (name, constant, value)) in scope.iter_raw().enumerate() {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
||||
#[cfg(feature = "no_closure")]
|
||||
@ -34,7 +31,7 @@ fn print_scope(scope: &Scope) {
|
||||
value_is_shared,
|
||||
*value.read_lock::<Dynamic>().unwrap(),
|
||||
)
|
||||
});
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
|
@ -13,10 +13,7 @@ struct Handler {
|
||||
}
|
||||
|
||||
fn print_scope(scope: &Scope) {
|
||||
scope
|
||||
.iter_raw()
|
||||
.enumerate()
|
||||
.for_each(|(i, (name, constant, value))| {
|
||||
for (i, (name, constant, value)) in scope.iter_raw().enumerate() {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
||||
#[cfg(feature = "no_closure")]
|
||||
@ -30,7 +27,7 @@ fn print_scope(scope: &Scope) {
|
||||
value_is_shared,
|
||||
*value.read_lock::<Dynamic>().unwrap(),
|
||||
)
|
||||
});
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
|
@ -16,10 +16,7 @@ struct Handler {
|
||||
}
|
||||
|
||||
fn print_scope(scope: &Scope) {
|
||||
scope
|
||||
.iter_raw()
|
||||
.enumerate()
|
||||
.for_each(|(i, (name, constant, value))| {
|
||||
for (i, (name, constant, value)) in scope.iter_raw().enumerate() {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
||||
#[cfg(feature = "no_closure")]
|
||||
@ -33,7 +30,7 @@ fn print_scope(scope: &Scope) {
|
||||
value_is_shared,
|
||||
*value.read_lock::<Dynamic>().unwrap(),
|
||||
)
|
||||
});
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
|
@ -154,10 +154,7 @@ impl Engine {
|
||||
arg_values: impl AsMut<[Dynamic]>,
|
||||
) -> RhaiResult {
|
||||
let state = &mut EvalState::new();
|
||||
let global = &mut GlobalRuntimeState::new();
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
global.debugger.activate(self.debugger.is_some());
|
||||
let global = &mut GlobalRuntimeState::new(self);
|
||||
|
||||
let statements = ast.statements();
|
||||
|
||||
|
@ -184,10 +184,7 @@ impl Engine {
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
) -> RhaiResultOf<T> {
|
||||
let global = &mut GlobalRuntimeState::new();
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
global.debugger.activate(self.debugger.is_some());
|
||||
let global = &mut GlobalRuntimeState::new(self);
|
||||
|
||||
let result = self.eval_ast_with_scope_raw(scope, global, ast, 0)?;
|
||||
|
||||
|
@ -260,12 +260,13 @@ impl Engine {
|
||||
self.debug = Some(Box::new(callback));
|
||||
self
|
||||
}
|
||||
/// _(debugging)_ Register a callback for debugging.
|
||||
/// _(debugging)_ Register callbacks for debugging.
|
||||
/// Exported under the `debugging` feature only.
|
||||
#[cfg(feature = "debugging")]
|
||||
#[inline(always)]
|
||||
pub fn on_debugger(
|
||||
&mut self,
|
||||
init: impl Fn() -> Dynamic + SendSync + 'static,
|
||||
callback: impl Fn(
|
||||
&mut EvalContext,
|
||||
crate::ast::ASTNode,
|
||||
@ -275,7 +276,7 @@ impl Engine {
|
||||
+ SendSync
|
||||
+ 'static,
|
||||
) -> &mut Self {
|
||||
self.debugger = Some(Box::new(callback));
|
||||
self.debugger = Some((Box::new(init), Box::new(callback)));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -1037,9 +1037,9 @@ impl Engine {
|
||||
|
||||
signatures.extend(self.global_namespace().gen_fn_signatures());
|
||||
|
||||
self.global_sub_modules.iter().for_each(|(name, m)| {
|
||||
for (name, m) in &self.global_sub_modules {
|
||||
signatures.extend(m.gen_fn_signatures().map(|f| format!("{}::{}", name, f)))
|
||||
});
|
||||
}
|
||||
|
||||
signatures.extend(
|
||||
self.global_modules
|
||||
|
@ -45,9 +45,7 @@ impl Engine {
|
||||
#[inline]
|
||||
pub fn run_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> {
|
||||
let state = &mut EvalState::new();
|
||||
let global = &mut GlobalRuntimeState::new();
|
||||
#[cfg(feature = "debugging")]
|
||||
global.debugger.activate(self.debugger.is_some());
|
||||
let global = &mut GlobalRuntimeState::new(self);
|
||||
global.source = ast.source_raw().clone();
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
|
@ -1,5 +1,5 @@
|
||||
#[cfg(feature = "debugging")]
|
||||
use rhai::{Dynamic, Engine, EvalAltResult, Position, Scope};
|
||||
use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString, Position, Scope};
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
use rhai::debugger::DebuggerCommand;
|
||||
@ -121,10 +121,7 @@ fn print_scope(scope: &Scope, dedup: bool) {
|
||||
scope
|
||||
};
|
||||
|
||||
scope
|
||||
.iter_raw()
|
||||
.enumerate()
|
||||
.for_each(|(i, (name, constant, value))| {
|
||||
for (i, (name, constant, value)) in scope.iter_raw().enumerate() {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
||||
#[cfg(feature = "no_closure")]
|
||||
@ -148,7 +145,7 @@ fn print_scope(scope: &Scope, dedup: bool) {
|
||||
*value.read_lock::<Dynamic>().unwrap(),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
println!();
|
||||
}
|
||||
@ -233,33 +230,35 @@ fn main() {
|
||||
// Hook up debugger
|
||||
let lines: Vec<_> = script.trim().split('\n').map(|s| s.to_string()).collect();
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
let current_source = std::cell::RefCell::new(rhai::Identifier::new_const());
|
||||
#[cfg(feature = "sync")]
|
||||
let current_source = std::sync::RwLock::new(rhai::Identifier::new_const());
|
||||
engine.on_debugger(
|
||||
// Store the current source in the debugger state
|
||||
|| "".into(),
|
||||
// Main debugging interface
|
||||
move |context, node, source, pos| {
|
||||
{
|
||||
let current_source = &mut *context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.state_mut()
|
||||
.write_lock::<ImmutableString>()
|
||||
.unwrap();
|
||||
|
||||
engine.on_debugger(move |context, node, source, pos| {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
let current_source = &mut *current_source.borrow_mut();
|
||||
#[cfg(feature = "sync")]
|
||||
let current_source = &mut *current_source.write().unwrap();
|
||||
let src = source.unwrap_or("");
|
||||
|
||||
// Check source
|
||||
if let Some(src) = source {
|
||||
if src != current_source {
|
||||
println!(">>> Source => {}", src);
|
||||
println!(">>> Source => {}", source.unwrap_or("main script"));
|
||||
*current_source = src.into();
|
||||
}
|
||||
|
||||
if !src.is_empty() {
|
||||
// Print just a line number for imported modules
|
||||
println!("{} @ {:?}", src, pos);
|
||||
} else {
|
||||
// The root file has no source
|
||||
if !current_source.is_empty() {
|
||||
println!(">>> Source => main script.");
|
||||
}
|
||||
// Print the current source line
|
||||
print_source(&lines, pos, 0);
|
||||
}
|
||||
*current_source = source.unwrap_or("").into();
|
||||
}
|
||||
|
||||
// Read stdin for commands
|
||||
let mut input = String::new();
|
||||
@ -305,30 +304,32 @@ fn main() {
|
||||
}
|
||||
["print", ..] => print_scope(context.scope(), true),
|
||||
["imports", ..] => {
|
||||
context
|
||||
for (i, (name, module)) in context
|
||||
.global_runtime_state()
|
||||
.scan_imports_raw()
|
||||
.enumerate()
|
||||
.for_each(|(i, (name, module))| {
|
||||
{
|
||||
println!(
|
||||
"[{}] {} = {}",
|
||||
i + 1,
|
||||
name,
|
||||
module.id().unwrap_or("<unknown>")
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
println!();
|
||||
}
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
["backtrace", ..] => {
|
||||
context
|
||||
for frame in context
|
||||
.global_runtime_state()
|
||||
.debugger
|
||||
.call_stack()
|
||||
.iter()
|
||||
.rev()
|
||||
.for_each(|frame| println!("{}", frame));
|
||||
{
|
||||
println!("{}", frame)
|
||||
}
|
||||
}
|
||||
["clear", ..] => {
|
||||
context
|
||||
@ -338,13 +339,14 @@ fn main() {
|
||||
.clear();
|
||||
println!("All break-points cleared.");
|
||||
}
|
||||
["breakpoints", ..] => context
|
||||
["breakpoints", ..] => Iterator::for_each(
|
||||
context
|
||||
.global_runtime_state()
|
||||
.debugger
|
||||
.break_points()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.for_each(|(i, bp)| match bp {
|
||||
.enumerate(),
|
||||
|(i, bp)| match bp {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
rhai::debugger::BreakPoint::AtPosition { pos, .. } => {
|
||||
let line_num = format!("[{}] line ", i + 1);
|
||||
@ -352,7 +354,8 @@ fn main() {
|
||||
print_source(&lines, *pos, line_num.len());
|
||||
}
|
||||
_ => println!("[{}] {}", i + 1, bp),
|
||||
}),
|
||||
},
|
||||
),
|
||||
["enable", n, ..] => {
|
||||
if let Ok(n) = n.parse::<usize>() {
|
||||
let range = 1..=context
|
||||
@ -504,7 +507,9 @@ fn main() {
|
||||
.break_points_mut()
|
||||
.push(bp);
|
||||
}
|
||||
["throw"] => break Err(EvalAltResult::ErrorRuntime(Dynamic::UNIT, pos).into()),
|
||||
["throw"] => {
|
||||
break Err(EvalAltResult::ErrorRuntime(Dynamic::UNIT, pos).into())
|
||||
}
|
||||
["throw", _msg, ..] => {
|
||||
let msg = input.trim().splitn(2, ' ').skip(1).next().unwrap_or("");
|
||||
break Err(EvalAltResult::ErrorRuntime(msg.trim().into(), pos).into());
|
||||
@ -518,7 +523,8 @@ fn main() {
|
||||
Err(err) => panic!("input error: {}", err),
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Set a file module resolver without caching
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
|
@ -61,10 +61,7 @@ fn print_help() {
|
||||
|
||||
/// Display the scope.
|
||||
fn print_scope(scope: &Scope) {
|
||||
scope
|
||||
.iter_raw()
|
||||
.enumerate()
|
||||
.for_each(|(i, (name, constant, value))| {
|
||||
for (i, (name, constant, value)) in scope.iter_raw().enumerate() {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
||||
#[cfg(feature = "no_closure")]
|
||||
@ -78,7 +75,7 @@ fn print_scope(scope: &Scope) {
|
||||
value_is_shared,
|
||||
*value.read_lock::<Dynamic>().unwrap(),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
println!();
|
||||
}
|
||||
@ -282,13 +279,13 @@ fn main() {
|
||||
#[cfg(feature = "metadata")]
|
||||
"functions" => {
|
||||
// print a list of all registered functions
|
||||
engine
|
||||
.gen_fn_signatures(false)
|
||||
.into_iter()
|
||||
.for_each(|f| println!("{}", f));
|
||||
for f in engine.gen_fn_signatures(false) {
|
||||
println!("{}", f)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
main_ast.iter_functions().for_each(|f| println!("{}", f));
|
||||
for f in main_ast.iter_functions() {
|
||||
println!("{}", f)
|
||||
}
|
||||
|
||||
println!();
|
||||
continue;
|
||||
|
@ -140,7 +140,10 @@ pub struct Engine {
|
||||
|
||||
/// Callback closure for debugging.
|
||||
#[cfg(feature = "debugging")]
|
||||
pub(crate) debugger: Option<Box<crate::eval::OnDebuggerCallback>>,
|
||||
pub(crate) debugger: Option<(
|
||||
Box<crate::eval::OnDebuggingInit>,
|
||||
Box<crate::eval::OnDebuggerCallback>,
|
||||
)>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Engine {
|
||||
|
@ -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) => {
|
||||
if let Some(module) = self.search_imports(global, state, namespace) {
|
||||
// foo:bar::baz::VARIABLE
|
||||
if let Some(module) = self.search_imports(global, state, namespace) {
|
||||
return match module.get_qualified_var(*hash_var) {
|
||||
Ok(target) => {
|
||||
let mut target = target.clone();
|
||||
@ -89,19 +89,17 @@ 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));
|
||||
}
|
||||
}
|
||||
|
||||
return Err(ERR::ErrorVariableNotFound(
|
||||
format!(
|
||||
|
@ -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())
|
||||
|
@ -22,7 +22,7 @@ pub use hashing::{
|
||||
combine_hashes, get_hasher,
|
||||
};
|
||||
pub use native::{
|
||||
shared_make_mut, shared_take, shared_take_or_clone, shared_try_take, shared_write_lock, FnAny,
|
||||
locked_write, shared_make_mut, shared_take, shared_take_or_clone, shared_try_take, FnAny,
|
||||
FnPlugin, IteratorFn, Locked, NativeCallContext, SendSync, Shared,
|
||||
};
|
||||
pub use plugin::PluginFunction;
|
||||
|
@ -286,23 +286,29 @@ impl<'a> NativeCallContext<'a> {
|
||||
is_method_call: bool,
|
||||
args: &mut [&mut Dynamic],
|
||||
) -> RhaiResult {
|
||||
let mut global = self
|
||||
.global
|
||||
.cloned()
|
||||
.unwrap_or_else(|| GlobalRuntimeState::new(self.engine()));
|
||||
let mut state = EvalState::new();
|
||||
|
||||
let fn_name = fn_name.as_ref();
|
||||
let len = args.len();
|
||||
let args_len = args.len();
|
||||
|
||||
let hash = if is_method_call {
|
||||
FnCallHashes::from_all(
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
calc_fn_hash(fn_name, len - 1),
|
||||
calc_fn_hash(fn_name, len),
|
||||
calc_fn_hash(fn_name, args_len - 1),
|
||||
calc_fn_hash(fn_name, args_len),
|
||||
)
|
||||
} else {
|
||||
calc_fn_hash(fn_name, len).into()
|
||||
calc_fn_hash(fn_name, args_len).into()
|
||||
};
|
||||
|
||||
self.engine()
|
||||
.exec_fn_call(
|
||||
&mut self.global.cloned().unwrap_or_else(GlobalRuntimeState::new),
|
||||
&mut EvalState::new(),
|
||||
&mut global,
|
||||
&mut state,
|
||||
self.lib,
|
||||
fn_name,
|
||||
hash,
|
||||
@ -353,11 +359,11 @@ pub fn shared_take<T>(value: Shared<T>) -> T {
|
||||
shared_try_take(value).ok().expect("not shared")
|
||||
}
|
||||
|
||||
/// Lock a [`Shared`] resource.
|
||||
/// Lock a [`Locked`] resource.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub fn shared_write_lock<'a, T>(value: &'a Locked<T>) -> LockGuard<'a, T> {
|
||||
pub fn locked_write<'a, T>(value: &'a Locked<T>) -> LockGuard<'a, T> {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
return value.borrow_mut();
|
||||
|
||||
@ -365,20 +371,20 @@ pub fn shared_write_lock<'a, T>(value: &'a Locked<T>) -> LockGuard<'a, T> {
|
||||
return value.write().unwrap();
|
||||
}
|
||||
|
||||
/// A general function trail object.
|
||||
/// General function trail object.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult;
|
||||
/// A general function trail object.
|
||||
/// General function trail object.
|
||||
#[cfg(feature = "sync")]
|
||||
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + Send + Sync;
|
||||
|
||||
/// A trail object for built-in functions.
|
||||
/// Trail object for built-in functions.
|
||||
pub type FnBuiltin = fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult;
|
||||
|
||||
/// A standard function that gets an iterator from a type.
|
||||
/// Function that gets an iterator from a type.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type IteratorFn = dyn Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
||||
/// A standard function that gets an iterator from a type.
|
||||
/// Function that gets an iterator from a type.
|
||||
#[cfg(feature = "sync")]
|
||||
pub type IteratorFn = dyn Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + Send + Sync;
|
||||
|
||||
@ -387,40 +393,40 @@ pub type FnPlugin = dyn PluginFunction;
|
||||
#[cfg(feature = "sync")]
|
||||
pub type FnPlugin = dyn PluginFunction + Send + Sync;
|
||||
|
||||
/// A standard callback function for progress reporting.
|
||||
/// Callback function for progress reporting.
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type OnProgressCallback = dyn Fn(u64) -> Option<Dynamic>;
|
||||
/// A standard callback function for progress reporting.
|
||||
/// Callback function for progress reporting.
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[cfg(feature = "sync")]
|
||||
pub type OnProgressCallback = dyn Fn(u64) -> Option<Dynamic> + Send + Sync;
|
||||
|
||||
/// A standard callback function for printing.
|
||||
/// Callback function for printing.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type OnPrintCallback = dyn Fn(&str);
|
||||
/// A standard callback function for printing.
|
||||
/// Callback function for printing.
|
||||
#[cfg(feature = "sync")]
|
||||
pub type OnPrintCallback = dyn Fn(&str) + Send + Sync;
|
||||
|
||||
/// A standard callback function for debugging.
|
||||
/// Callback function for debugging.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type OnDebugCallback = dyn Fn(&str, Option<&str>, Position);
|
||||
/// A standard callback function for debugging.
|
||||
/// Callback function for debugging.
|
||||
#[cfg(feature = "sync")]
|
||||
pub type OnDebugCallback = dyn Fn(&str, Option<&str>, Position) + Send + Sync;
|
||||
|
||||
/// A standard callback function for mapping tokens during parsing.
|
||||
/// Callback function for mapping tokens during parsing.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type OnParseTokenCallback = dyn Fn(Token, Position, &TokenizeState) -> Token;
|
||||
/// A standard callback function for mapping tokens during parsing.
|
||||
/// Callback function for mapping tokens during parsing.
|
||||
#[cfg(feature = "sync")]
|
||||
pub type OnParseTokenCallback = dyn Fn(Token, Position, &TokenizeState) -> Token + Send + Sync;
|
||||
|
||||
/// A standard callback function for variable access.
|
||||
/// Callback function for variable access.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type OnVarCallback = dyn Fn(&str, usize, &EvalContext) -> RhaiResultOf<Option<Dynamic>>;
|
||||
/// A standard callback function for variable access.
|
||||
/// Callback function for variable access.
|
||||
#[cfg(feature = "sync")]
|
||||
pub type OnVarCallback =
|
||||
dyn Fn(&str, usize, &EvalContext) -> RhaiResultOf<Option<Dynamic>> + Send + Sync;
|
||||
|
@ -74,7 +74,7 @@ impl Engine {
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let orig_call_stack_len = global.debugger.call_stack_len();
|
||||
let orig_call_stack_len = global.debugger.call_stack().len();
|
||||
|
||||
// Put arguments into scope as variables
|
||||
scope.extend(fn_def.params.iter().cloned().zip(args.into_iter().map(|v| {
|
||||
@ -115,10 +115,9 @@ impl Engine {
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
if let Some(ref modules) = fn_def.global {
|
||||
modules
|
||||
.iter()
|
||||
.cloned()
|
||||
.for_each(|(n, m)| global.push_import(n, m));
|
||||
for (n, m) in modules.iter().cloned() {
|
||||
global.push_import(n, m)
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the function
|
||||
|
@ -1401,9 +1401,9 @@ impl Module {
|
||||
/// Sub-modules are flattened onto the root [`Module`], with higher level overriding lower level.
|
||||
#[inline]
|
||||
pub fn combine_flatten(&mut self, other: Self) -> &mut Self {
|
||||
other.modules.into_iter().for_each(|(_, m)| {
|
||||
for (_, m) in other.modules.into_iter() {
|
||||
self.combine_flatten(shared_take_or_clone(m));
|
||||
});
|
||||
}
|
||||
self.variables.extend(other.variables.into_iter());
|
||||
self.functions.extend(other.functions.into_iter());
|
||||
self.type_iterators.extend(other.type_iterators.into_iter());
|
||||
@ -1419,22 +1419,22 @@ impl Module {
|
||||
/// Only items not existing in this [`Module`] are added.
|
||||
#[inline]
|
||||
pub fn fill_with(&mut self, other: &Self) -> &mut Self {
|
||||
other.modules.iter().for_each(|(k, v)| {
|
||||
for (k, v) in &other.modules {
|
||||
if !self.modules.contains_key(k) {
|
||||
self.modules.insert(k.clone(), v.clone());
|
||||
}
|
||||
});
|
||||
other.variables.iter().for_each(|(k, v)| {
|
||||
}
|
||||
for (k, v) in &other.variables {
|
||||
if !self.variables.contains_key(k) {
|
||||
self.variables.insert(k.clone(), v.clone());
|
||||
}
|
||||
});
|
||||
other.functions.iter().for_each(|(&k, v)| {
|
||||
}
|
||||
for (&k, v) in &other.functions {
|
||||
self.functions.entry(k).or_insert_with(|| v.clone());
|
||||
});
|
||||
other.type_iterators.iter().for_each(|(&k, v)| {
|
||||
}
|
||||
for (&k, v) in &other.type_iterators {
|
||||
self.type_iterators.entry(k).or_insert(v.clone());
|
||||
});
|
||||
}
|
||||
self.all_functions.clear();
|
||||
self.all_variables.clear();
|
||||
self.all_type_iterators.clear();
|
||||
@ -1455,12 +1455,11 @@ impl Module {
|
||||
other: &Self,
|
||||
_filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool + Copy,
|
||||
) -> &mut Self {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
other.modules.iter().for_each(|(k, v)| {
|
||||
for (k, v) in &other.modules {
|
||||
let mut m = Self::new();
|
||||
m.merge_filtered(v, _filter);
|
||||
self.set_sub_module(k.clone(), m);
|
||||
});
|
||||
}
|
||||
#[cfg(feature = "no_function")]
|
||||
self.modules
|
||||
.extend(other.modules.iter().map(|(k, v)| (k.clone(), v.clone())));
|
||||
@ -1658,28 +1657,74 @@ impl Module {
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
pub fn eval_ast_as_new(
|
||||
scope: crate::Scope,
|
||||
ast: &crate::AST,
|
||||
engine: &crate::Engine,
|
||||
) -> RhaiResultOf<Self> {
|
||||
let global = &mut crate::eval::GlobalRuntimeState::new(engine);
|
||||
|
||||
Self::eval_ast_as_new_raw(engine, scope, global, ast)
|
||||
}
|
||||
/// Create a new [`Module`] by evaluating an [`AST`][crate::AST].
|
||||
///
|
||||
/// The entire [`AST`][crate::AST] is encapsulated into each function, allowing functions
|
||||
/// to cross-call each other. Functions in the global namespace, plus all functions
|
||||
/// defined in the [`Module`], are _merged_ into a _unified_ namespace before each call.
|
||||
/// Therefore, all functions will be found.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub(crate) fn eval_ast_as_new_raw(
|
||||
engine: &crate::Engine,
|
||||
scope: crate::Scope,
|
||||
global: &mut crate::eval::GlobalRuntimeState,
|
||||
ast: &crate::AST,
|
||||
) -> RhaiResultOf<Self> {
|
||||
let mut scope = scope;
|
||||
let mut global = crate::eval::GlobalRuntimeState::new();
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
global.debugger.activate(engine.debugger.is_some());
|
||||
|
||||
// Save global state
|
||||
let orig_imports_len = global.num_imports();
|
||||
let orig_source = global.source.clone();
|
||||
let orig_constants = std::mem::take(&mut global.constants);
|
||||
|
||||
// Run the script
|
||||
engine.eval_ast_with_scope_raw(&mut scope, &mut global, &ast, 0)?;
|
||||
let result = engine.eval_ast_with_scope_raw(&mut scope, global, &ast, 0);
|
||||
|
||||
// Create new module
|
||||
let mut module =
|
||||
scope
|
||||
.into_iter()
|
||||
.fold(Module::new(), |mut module, (_, value, mut aliases)| {
|
||||
let mut module = Module::new();
|
||||
|
||||
// Extra modules left become sub-modules
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let mut func_global = None;
|
||||
|
||||
if result.is_ok() {
|
||||
global
|
||||
.scan_imports_raw()
|
||||
.skip(orig_imports_len)
|
||||
.for_each(|(k, m)| {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if func_global.is_none() {
|
||||
func_global = Some(StaticVec::new());
|
||||
}
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
func_global
|
||||
.as_mut()
|
||||
.expect("`Some`")
|
||||
.push((k.clone(), m.clone()));
|
||||
|
||||
module.set_sub_module(k.clone(), m.clone());
|
||||
});
|
||||
}
|
||||
|
||||
// Restore global state
|
||||
global.constants = orig_constants;
|
||||
global.truncate_imports(orig_imports_len);
|
||||
global.source = orig_source;
|
||||
|
||||
result?;
|
||||
|
||||
// Variables with an alias left in the scope become module variables
|
||||
for (_, value, mut aliases) in scope {
|
||||
match aliases.len() {
|
||||
0 => (),
|
||||
1 => {
|
||||
@ -1688,30 +1733,14 @@ impl Module {
|
||||
}
|
||||
_ => {
|
||||
let last_alias = aliases.pop().unwrap();
|
||||
aliases.into_iter().for_each(|alias| {
|
||||
for alias in aliases {
|
||||
module.set_var(alias, value.clone());
|
||||
});
|
||||
}
|
||||
// Avoid cloning the last value
|
||||
module.set_var(last_alias, value);
|
||||
}
|
||||
}
|
||||
module
|
||||
});
|
||||
|
||||
// Extra modules left in the scope become sub-modules
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let mut func_global = None;
|
||||
|
||||
global.into_iter().skip(orig_imports_len).for_each(|kv| {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if func_global.is_none() {
|
||||
func_global = Some(StaticVec::new());
|
||||
}
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
func_global.as_mut().expect("`Some`").push(kv.clone());
|
||||
|
||||
module.set_sub_module(kv.0, kv.1);
|
||||
});
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let func_global = func_global.map(|v| v.into_boxed_slice());
|
||||
@ -1773,29 +1802,29 @@ impl Module {
|
||||
) -> bool {
|
||||
let mut contains_indexed_global_functions = false;
|
||||
|
||||
module.modules.iter().for_each(|(name, m)| {
|
||||
for (name, m) in &module.modules {
|
||||
// Index all the sub-modules first.
|
||||
path.push(name);
|
||||
if index_module(m, path, variables, functions, type_iterators) {
|
||||
contains_indexed_global_functions = true;
|
||||
}
|
||||
path.pop();
|
||||
});
|
||||
}
|
||||
|
||||
// Index all variables
|
||||
module.variables.iter().for_each(|(var_name, value)| {
|
||||
for (var_name, value) in &module.variables {
|
||||
let hash_var = crate::calc_qualified_var_hash(path.iter().copied(), var_name);
|
||||
variables.insert(hash_var, value.clone());
|
||||
});
|
||||
}
|
||||
|
||||
// Index type iterators
|
||||
module.type_iterators.iter().for_each(|(&type_id, func)| {
|
||||
for (&type_id, func) in &module.type_iterators {
|
||||
type_iterators.insert(type_id, func.clone());
|
||||
contains_indexed_global_functions = true;
|
||||
});
|
||||
}
|
||||
|
||||
// Index all Rust functions
|
||||
module.functions.iter().for_each(|(&hash, f)| {
|
||||
for (&hash, f) in &module.functions {
|
||||
match f.metadata.namespace {
|
||||
FnNamespace::Global => {
|
||||
// Flatten all functions with global namespace
|
||||
@ -1806,7 +1835,7 @@ impl Module {
|
||||
}
|
||||
match f.metadata.access {
|
||||
FnAccess::Public => (),
|
||||
FnAccess::Private => return, // Do not index private functions
|
||||
FnAccess::Private => continue, // Do not index private functions
|
||||
}
|
||||
|
||||
if !f.func.is_script() {
|
||||
@ -1824,7 +1853,7 @@ impl Module {
|
||||
);
|
||||
functions.insert(hash_qualified_script, f.func.clone());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
contains_indexed_global_functions
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ impl ModuleResolver for ModuleResolversCollection {
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Shared<Module>> {
|
||||
for resolver in self.0.iter() {
|
||||
for resolver in &self.0 {
|
||||
match resolver.resolve(engine, source_path, path, pos) {
|
||||
Ok(module) => return Ok(module),
|
||||
Err(err) => match *err {
|
||||
|
@ -1,9 +1,10 @@
|
||||
#![cfg(not(feature = "no_std"))]
|
||||
#![cfg(not(target_family = "wasm"))]
|
||||
|
||||
use crate::func::native::shared_write_lock;
|
||||
use crate::func::native::locked_write;
|
||||
use crate::{
|
||||
Engine, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Scope, Shared, ERR,
|
||||
Engine, GlobalRuntimeState, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Scope,
|
||||
Shared, ERR,
|
||||
};
|
||||
|
||||
use std::{
|
||||
@ -207,12 +208,12 @@ impl FileModuleResolver {
|
||||
|
||||
let file_path = self.get_file_path(path.as_ref(), source_path);
|
||||
|
||||
shared_write_lock(&self.cache).contains_key(&file_path)
|
||||
locked_write(&self.cache).contains_key(&file_path)
|
||||
}
|
||||
/// Empty the internal cache.
|
||||
#[inline]
|
||||
pub fn clear_cache(&mut self) -> &mut Self {
|
||||
shared_write_lock(&self.cache).clear();
|
||||
locked_write(&self.cache).clear();
|
||||
self
|
||||
}
|
||||
/// Remove the specified path from internal cache.
|
||||
@ -227,7 +228,7 @@ impl FileModuleResolver {
|
||||
) -> Option<Shared<Module>> {
|
||||
let file_path = self.get_file_path(path.as_ref(), source_path.as_ref().map(<_>::as_ref));
|
||||
|
||||
shared_write_lock(&self.cache)
|
||||
locked_write(&self.cache)
|
||||
.remove_entry(&file_path)
|
||||
.map(|(_, v)| v)
|
||||
}
|
||||
@ -252,24 +253,25 @@ impl FileModuleResolver {
|
||||
file_path.set_extension(self.extension.as_str()); // Force extension
|
||||
file_path
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleResolver for FileModuleResolver {
|
||||
fn resolve(
|
||||
/// Resolve a module based on a path.
|
||||
fn impl_resolve(
|
||||
&self,
|
||||
engine: &Engine,
|
||||
source_path: Option<&str>,
|
||||
global: Option<&mut GlobalRuntimeState>,
|
||||
source: Option<&str>,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Shared<Module>> {
|
||||
) -> Result<Shared<Module>, Box<crate::EvalAltResult>> {
|
||||
// Load relative paths from source if there is no base path specified
|
||||
let source_path =
|
||||
source_path.and_then(|p| Path::new(p).parent().map(|p| p.to_string_lossy()));
|
||||
let source_path = global
|
||||
.as_ref()
|
||||
.and_then(|g| g.source())
|
||||
.or(source)
|
||||
.and_then(|p| Path::new(p).parent().map(|p| p.to_string_lossy()));
|
||||
|
||||
// Construct the script file path
|
||||
let file_path = self.get_file_path(path, source_path.as_ref().map(|p| p.as_ref()));
|
||||
|
||||
// See if it is cached
|
||||
if self.is_cache_enabled() {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
let c = self.cache.borrow();
|
||||
@ -281,7 +283,6 @@ impl ModuleResolver for FileModuleResolver {
|
||||
}
|
||||
}
|
||||
|
||||
// Load the script file and compile it
|
||||
let scope = Scope::new();
|
||||
|
||||
let mut ast = engine
|
||||
@ -295,18 +296,43 @@ impl ModuleResolver for FileModuleResolver {
|
||||
|
||||
ast.set_source(path);
|
||||
|
||||
// Make a module from the AST
|
||||
let m: Shared<Module> = Module::eval_ast_as_new(scope, &ast, engine)
|
||||
let m: Shared<Module> = if let Some(global) = global {
|
||||
Module::eval_ast_as_new_raw(engine, scope, global, &ast)
|
||||
} else {
|
||||
Module::eval_ast_as_new(scope, &ast, engine)
|
||||
}
|
||||
.map_err(|err| Box::new(ERR::ErrorInModule(path.to_string(), err, pos)))?
|
||||
.into();
|
||||
|
||||
// Put it into the cache
|
||||
if self.is_cache_enabled() {
|
||||
shared_write_lock(&self.cache).insert(file_path, m.clone());
|
||||
locked_write(&self.cache).insert(file_path, m.clone());
|
||||
}
|
||||
|
||||
Ok(m)
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleResolver for FileModuleResolver {
|
||||
fn resolve_raw(
|
||||
&self,
|
||||
engine: &Engine,
|
||||
global: &mut GlobalRuntimeState,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Shared<Module>> {
|
||||
self.impl_resolve(engine, Some(global), None, path, pos)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn resolve(
|
||||
&self,
|
||||
engine: &Engine,
|
||||
source: Option<&str>,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Shared<Module>> {
|
||||
self.impl_resolve(engine, None, source, path, pos)
|
||||
}
|
||||
|
||||
/// Resolve an `AST` based on a path string.
|
||||
///
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::func::native::SendSync;
|
||||
use crate::{Engine, Module, Position, RhaiResultOf, Shared, AST};
|
||||
use crate::{Engine, GlobalRuntimeState, Module, Position, RhaiResultOf, Shared, AST};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@ -21,11 +21,26 @@ pub trait ModuleResolver: SendSync {
|
||||
fn resolve(
|
||||
&self,
|
||||
engine: &Engine,
|
||||
source_path: Option<&str>,
|
||||
source: Option<&str>,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Shared<Module>>;
|
||||
|
||||
/// Resolve a module based on a path string, given a [`GlobalRuntimeState`].
|
||||
///
|
||||
/// # WARNING - Low Level API
|
||||
///
|
||||
/// This function is very low level.
|
||||
fn resolve_raw(
|
||||
&self,
|
||||
engine: &Engine,
|
||||
global: &mut GlobalRuntimeState,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Shared<Module>> {
|
||||
self.resolve(engine, global.source(), path, pos)
|
||||
}
|
||||
|
||||
/// Resolve an `AST` based on a path string.
|
||||
///
|
||||
/// Returns [`None`] (default) if such resolution is not supported
|
||||
@ -40,7 +55,7 @@ pub trait ModuleResolver: SendSync {
|
||||
fn resolve_ast(
|
||||
&self,
|
||||
engine: &Engine,
|
||||
source_path: Option<&str>,
|
||||
source: Option<&str>,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> Option<RhaiResultOf<AST>> {
|
||||
|
@ -138,7 +138,7 @@ impl<'a> OptimizerState<'a> {
|
||||
|
||||
self.engine
|
||||
.call_native_fn(
|
||||
&mut GlobalRuntimeState::new(),
|
||||
&mut GlobalRuntimeState::new(&self.engine),
|
||||
&mut EvalState::new(),
|
||||
lib,
|
||||
fn_name,
|
||||
@ -237,7 +237,7 @@ fn optimize_stmt_block(
|
||||
});
|
||||
|
||||
// Optimize each statement in the block
|
||||
statements.iter_mut().for_each(|stmt| {
|
||||
for stmt in statements.iter_mut() {
|
||||
match stmt {
|
||||
Stmt::Var(value_expr, x, options, _) => {
|
||||
if options.contains(AST_OPTION_CONSTANT) {
|
||||
@ -260,7 +260,7 @@ fn optimize_stmt_block(
|
||||
// Optimize the statement
|
||||
_ => optimize_stmt(stmt, state, preserve_result),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Remove all pure statements except the last one
|
||||
let mut index = 0;
|
||||
@ -640,7 +640,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
// switch
|
||||
Stmt::Switch(match_expr, x, _) => {
|
||||
optimize_expr(match_expr, state, false);
|
||||
x.cases.values_mut().for_each(|block| {
|
||||
for block in x.cases.values_mut() {
|
||||
let statements = mem::take(&mut *block.statements);
|
||||
*block.statements =
|
||||
optimize_stmt_block(statements, state, preserve_result, true, false);
|
||||
@ -652,7 +652,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
_ => block.condition = Some(condition),
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Remove false cases
|
||||
while let Some((&key, _)) = x.cases.iter().find(|(_, block)| match block.condition {
|
||||
@ -1159,13 +1159,13 @@ fn optimize_top_level(
|
||||
);
|
||||
|
||||
// Add constants and variables from the scope
|
||||
scope.iter().for_each(|(name, constant, value)| {
|
||||
for (name, constant, value) in scope.iter() {
|
||||
if !constant {
|
||||
state.push_var(name, AccessMode::ReadWrite, None);
|
||||
} else {
|
||||
state.push_var(name, AccessMode::ReadOnly, Some(value));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
statements = optimize_stmt_block(statements, &mut state, true, false, true);
|
||||
statements
|
||||
@ -1191,9 +1191,8 @@ pub fn optimize_into_ast(
|
||||
// We only need the script library's signatures for optimization purposes
|
||||
let mut lib2 = crate::Module::new();
|
||||
|
||||
functions
|
||||
.iter()
|
||||
.map(|fn_def| crate::ast::ScriptFnDef {
|
||||
for fn_def in &functions {
|
||||
lib2.set_script_fn(crate::ast::ScriptFnDef {
|
||||
name: fn_def.name.clone(),
|
||||
access: fn_def.access,
|
||||
body: crate::ast::StmtBlock::NONE,
|
||||
@ -1204,33 +1203,25 @@ pub fn optimize_into_ast(
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(feature = "metadata")]
|
||||
comments: None,
|
||||
})
|
||||
.for_each(|fn_def| {
|
||||
lib2.set_script_fn(fn_def);
|
||||
});
|
||||
}
|
||||
|
||||
let lib2 = &[&lib2];
|
||||
|
||||
functions
|
||||
.into_iter()
|
||||
.map(|fn_def| {
|
||||
for fn_def in functions {
|
||||
let mut fn_def = crate::func::native::shared_take_or_clone(fn_def);
|
||||
|
||||
// Optimize the function body
|
||||
let body = mem::take(&mut *fn_def.body);
|
||||
|
||||
*fn_def.body =
|
||||
optimize_top_level(body, engine, scope, lib2, optimization_level);
|
||||
*fn_def.body = optimize_top_level(body, engine, scope, lib2, optimization_level);
|
||||
|
||||
fn_def
|
||||
})
|
||||
.for_each(|fn_def| {
|
||||
module.set_script_fn(fn_def);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
functions.into_iter().for_each(|fn_def| {
|
||||
for fn_def in functions {
|
||||
module.set_script_fn(fn_def);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module
|
||||
|
@ -189,7 +189,7 @@ fn collect_fn_metadata(
|
||||
.for_each(|(_, _, _, _, f)| {
|
||||
list.push(make_metadata(dict, Some(namespace.clone()), f).into())
|
||||
});
|
||||
module.iter_sub_modules().for_each(|(ns, m)| {
|
||||
for (ns, m) in module.iter_sub_modules() {
|
||||
let ns = format!(
|
||||
"{}{}{}",
|
||||
namespace,
|
||||
@ -197,11 +197,12 @@ fn collect_fn_metadata(
|
||||
ns
|
||||
);
|
||||
scan_module(list, dict, ns.into(), m.as_ref(), filter)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ctx.iter_imports_raw()
|
||||
.for_each(|(ns, m)| scan_module(&mut list, &dict, ns.clone(), m.as_ref(), filter));
|
||||
for (ns, m) in ctx.iter_imports_raw() {
|
||||
scan_module(&mut list, &dict, ns.clone(), m.as_ref(), filter)
|
||||
}
|
||||
}
|
||||
|
||||
list
|
||||
|
@ -3450,9 +3450,9 @@ impl Engine {
|
||||
{
|
||||
let mut m = crate::Module::new();
|
||||
|
||||
_lib.into_iter().for_each(|fn_def| {
|
||||
for fn_def in _lib {
|
||||
m.set_script_fn(fn_def);
|
||||
});
|
||||
}
|
||||
|
||||
return Ok(AST::new(statements, m));
|
||||
}
|
||||
|
@ -206,9 +206,9 @@ impl Engine {
|
||||
let _ast = ast;
|
||||
let mut global = ModuleMetadata::new();
|
||||
|
||||
self.global_sub_modules.iter().for_each(|(name, m)| {
|
||||
for (name, m) in &self.global_sub_modules {
|
||||
global.modules.insert(name, m.as_ref().into());
|
||||
});
|
||||
}
|
||||
|
||||
self.global_modules
|
||||
.iter()
|
||||
@ -221,11 +221,11 @@ impl Engine {
|
||||
});
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
_ast.shared_lib().iter_fn().for_each(|f| {
|
||||
for f in _ast.shared_lib().iter_fn() {
|
||||
let mut meta: FnMetadata = f.into();
|
||||
meta.namespace = FnNamespace::Global;
|
||||
global.functions.push(meta);
|
||||
});
|
||||
}
|
||||
|
||||
global.functions.sort();
|
||||
|
||||
|
@ -1119,18 +1119,18 @@ impl Dynamic {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(ref mut a, _, ref mut access) => {
|
||||
*access = typ;
|
||||
a.iter_mut().for_each(|v| {
|
||||
for v in a.iter_mut() {
|
||||
v.set_access_mode(typ);
|
||||
});
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Blob(_, _, ref mut access) => *access = typ,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(ref mut m, _, ref mut access) => {
|
||||
*access = typ;
|
||||
m.values_mut().for_each(|v| {
|
||||
for v in m.values_mut() {
|
||||
v.set_access_mode(typ);
|
||||
});
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
Union::TimeStamp(_, _, ref mut access) => *access = typ,
|
||||
@ -1708,14 +1708,14 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell, _, _) => {
|
||||
let value = crate::func::native::shared_write_lock(cell);
|
||||
let guard = crate::func::native::locked_write(cell);
|
||||
|
||||
if (*value).type_id() != TypeId::of::<T>()
|
||||
if (*guard).type_id() != TypeId::of::<T>()
|
||||
&& TypeId::of::<Dynamic>() != TypeId::of::<T>()
|
||||
{
|
||||
return None;
|
||||
} else {
|
||||
return Some(DynamicWriteLock(DynamicWriteLockInner::Guard(value)));
|
||||
return Some(DynamicWriteLock(DynamicWriteLockInner::Guard(guard)));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
|
@ -611,9 +611,9 @@ impl Scope<'_> {
|
||||
impl<K: Into<Identifier>> Extend<(K, Dynamic)> for Scope<'_> {
|
||||
#[inline]
|
||||
fn extend<T: IntoIterator<Item = (K, Dynamic)>>(&mut self, iter: T) {
|
||||
iter.into_iter().for_each(|(name, value)| {
|
||||
for (name, value) in iter {
|
||||
self.push_dynamic_value(name, AccessMode::ReadWrite, value);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -629,7 +629,7 @@ impl<K: Into<Identifier>> FromIterator<(K, Dynamic)> for Scope<'_> {
|
||||
impl<K: Into<Identifier>> Extend<(K, bool, Dynamic)> for Scope<'_> {
|
||||
#[inline]
|
||||
fn extend<T: IntoIterator<Item = (K, bool, Dynamic)>>(&mut self, iter: T) {
|
||||
iter.into_iter().for_each(|(name, is_constant, value)| {
|
||||
for (name, is_constant, value) in iter {
|
||||
self.push_dynamic_value(
|
||||
name,
|
||||
if is_constant {
|
||||
@ -639,7 +639,7 @@ impl<K: Into<Identifier>> Extend<(K, bool, Dynamic)> for Scope<'_> {
|
||||
},
|
||||
value,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,12 @@
|
||||
#![cfg(feature = "debugging")]
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
use rhai::{Dynamic, Engine, EvalAltResult, INT};
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use rhai::Array;
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
use rhai::Map;
|
||||
|
||||
#[test]
|
||||
fn test_debugging() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
@ -32,3 +35,44 @@ fn test_debugging() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
fn test_debugger_state() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.on_debugger(
|
||||
|| {
|
||||
// Say, use an object map for the debugger state
|
||||
let mut state = Map::new();
|
||||
// Initialize properties
|
||||
state.insert("hello".into(), (42 as INT).into());
|
||||
state.insert("foo".into(), false.into());
|
||||
Dynamic::from_map(state)
|
||||
},
|
||||
|context, _, _, _| {
|
||||
// Get global runtime state
|
||||
let global = context.global_runtime_state_mut();
|
||||
|
||||
// Get debugger
|
||||
let debugger = &mut global.debugger;
|
||||
|
||||
// Print debugger state - which is an object map
|
||||
println!("Current state = {}", debugger.state());
|
||||
|
||||
// Modify state
|
||||
let mut state = debugger.state_mut().write_lock::<Map>().unwrap();
|
||||
let hello = state.get("hello").unwrap().as_int().unwrap();
|
||||
state.insert("hello".into(), (hello + 1).into());
|
||||
state.insert("foo".into(), true.into());
|
||||
state.insert("something_new".into(), "hello, world!".into());
|
||||
|
||||
// Continue with debugging
|
||||
Ok(rhai::debugger::DebuggerCommand::StepInto)
|
||||
},
|
||||
);
|
||||
|
||||
engine.run("let x = 42;")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user