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) {
|
fn print_scope(scope: &Scope) {
|
||||||
scope
|
for (i, (name, constant, value)) in scope.iter_raw().enumerate() {
|
||||||
.iter_raw()
|
|
||||||
.enumerate()
|
|
||||||
.for_each(|(i, (name, constant, value))| {
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
@ -34,7 +31,7 @@ fn print_scope(scope: &Scope) {
|
|||||||
value_is_shared,
|
value_is_shared,
|
||||||
*value.read_lock::<Dynamic>().unwrap(),
|
*value.read_lock::<Dynamic>().unwrap(),
|
||||||
)
|
)
|
||||||
});
|
}
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,10 +13,7 @@ struct Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn print_scope(scope: &Scope) {
|
fn print_scope(scope: &Scope) {
|
||||||
scope
|
for (i, (name, constant, value)) in scope.iter_raw().enumerate() {
|
||||||
.iter_raw()
|
|
||||||
.enumerate()
|
|
||||||
.for_each(|(i, (name, constant, value))| {
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
@ -30,7 +27,7 @@ fn print_scope(scope: &Scope) {
|
|||||||
value_is_shared,
|
value_is_shared,
|
||||||
*value.read_lock::<Dynamic>().unwrap(),
|
*value.read_lock::<Dynamic>().unwrap(),
|
||||||
)
|
)
|
||||||
});
|
}
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,10 +16,7 @@ struct Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn print_scope(scope: &Scope) {
|
fn print_scope(scope: &Scope) {
|
||||||
scope
|
for (i, (name, constant, value)) in scope.iter_raw().enumerate() {
|
||||||
.iter_raw()
|
|
||||||
.enumerate()
|
|
||||||
.for_each(|(i, (name, constant, value))| {
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
@ -33,7 +30,7 @@ fn print_scope(scope: &Scope) {
|
|||||||
value_is_shared,
|
value_is_shared,
|
||||||
*value.read_lock::<Dynamic>().unwrap(),
|
*value.read_lock::<Dynamic>().unwrap(),
|
||||||
)
|
)
|
||||||
});
|
}
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,10 +154,7 @@ impl Engine {
|
|||||||
arg_values: impl AsMut<[Dynamic]>,
|
arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let state = &mut EvalState::new();
|
let state = &mut EvalState::new();
|
||||||
let global = &mut GlobalRuntimeState::new();
|
let global = &mut GlobalRuntimeState::new(self);
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
|
||||||
global.debugger.activate(self.debugger.is_some());
|
|
||||||
|
|
||||||
let statements = ast.statements();
|
let statements = ast.statements();
|
||||||
|
|
||||||
|
@ -184,10 +184,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
) -> RhaiResultOf<T> {
|
) -> RhaiResultOf<T> {
|
||||||
let global = &mut GlobalRuntimeState::new();
|
let global = &mut GlobalRuntimeState::new(self);
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
|
||||||
global.debugger.activate(self.debugger.is_some());
|
|
||||||
|
|
||||||
let result = self.eval_ast_with_scope_raw(scope, global, ast, 0)?;
|
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.debug = Some(Box::new(callback));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// _(debugging)_ Register a callback for debugging.
|
/// _(debugging)_ Register callbacks for debugging.
|
||||||
/// Exported under the `debugging` feature only.
|
/// Exported under the `debugging` feature only.
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn on_debugger(
|
pub fn on_debugger(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
init: impl Fn() -> Dynamic + SendSync + 'static,
|
||||||
callback: impl Fn(
|
callback: impl Fn(
|
||||||
&mut EvalContext,
|
&mut EvalContext,
|
||||||
crate::ast::ASTNode,
|
crate::ast::ASTNode,
|
||||||
@ -275,7 +276,7 @@ impl Engine {
|
|||||||
+ SendSync
|
+ SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.debugger = Some(Box::new(callback));
|
self.debugger = Some((Box::new(init), Box::new(callback)));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1037,9 +1037,9 @@ impl Engine {
|
|||||||
|
|
||||||
signatures.extend(self.global_namespace().gen_fn_signatures());
|
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(m.gen_fn_signatures().map(|f| format!("{}::{}", name, f)))
|
||||||
});
|
}
|
||||||
|
|
||||||
signatures.extend(
|
signatures.extend(
|
||||||
self.global_modules
|
self.global_modules
|
||||||
|
@ -45,9 +45,7 @@ impl Engine {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn run_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> {
|
pub fn run_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> {
|
||||||
let state = &mut EvalState::new();
|
let state = &mut EvalState::new();
|
||||||
let global = &mut GlobalRuntimeState::new();
|
let global = &mut GlobalRuntimeState::new(self);
|
||||||
#[cfg(feature = "debugging")]
|
|
||||||
global.debugger.activate(self.debugger.is_some());
|
|
||||||
global.source = ast.source_raw().clone();
|
global.source = ast.source_raw().clone();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
use rhai::{Dynamic, Engine, EvalAltResult, Position, Scope};
|
use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString, Position, Scope};
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
use rhai::debugger::DebuggerCommand;
|
use rhai::debugger::DebuggerCommand;
|
||||||
@ -121,10 +121,7 @@ fn print_scope(scope: &Scope, dedup: bool) {
|
|||||||
scope
|
scope
|
||||||
};
|
};
|
||||||
|
|
||||||
scope
|
for (i, (name, constant, value)) in scope.iter_raw().enumerate() {
|
||||||
.iter_raw()
|
|
||||||
.enumerate()
|
|
||||||
.for_each(|(i, (name, constant, value))| {
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
@ -148,7 +145,7 @@ fn print_scope(scope: &Scope, dedup: bool) {
|
|||||||
*value.read_lock::<Dynamic>().unwrap(),
|
*value.read_lock::<Dynamic>().unwrap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
@ -233,33 +230,35 @@ fn main() {
|
|||||||
// Hook up debugger
|
// Hook up debugger
|
||||||
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();
|
||||||
|
|
||||||
#[cfg(not(feature = "sync"))]
|
engine.on_debugger(
|
||||||
let current_source = std::cell::RefCell::new(rhai::Identifier::new_const());
|
// Store the current source in the debugger state
|
||||||
#[cfg(feature = "sync")]
|
|| "".into(),
|
||||||
let current_source = std::sync::RwLock::new(rhai::Identifier::new_const());
|
// 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| {
|
let src = source.unwrap_or("");
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
let current_source = &mut *current_source.borrow_mut();
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
let current_source = &mut *current_source.write().unwrap();
|
|
||||||
|
|
||||||
// Check source
|
// Check source
|
||||||
if let Some(src) = source {
|
|
||||||
if src != current_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
|
// Print just a line number for imported modules
|
||||||
println!("{} @ {:?}", src, pos);
|
println!("{} @ {:?}", src, pos);
|
||||||
} else {
|
} else {
|
||||||
// The root file has no source
|
|
||||||
if !current_source.is_empty() {
|
|
||||||
println!(">>> Source => main script.");
|
|
||||||
}
|
|
||||||
// Print the current source line
|
// Print the current source line
|
||||||
print_source(&lines, pos, 0);
|
print_source(&lines, pos, 0);
|
||||||
}
|
}
|
||||||
*current_source = source.unwrap_or("").into();
|
}
|
||||||
|
|
||||||
// Read stdin for commands
|
// Read stdin for commands
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
@ -305,30 +304,32 @@ fn main() {
|
|||||||
}
|
}
|
||||||
["print", ..] => print_scope(context.scope(), true),
|
["print", ..] => print_scope(context.scope(), true),
|
||||||
["imports", ..] => {
|
["imports", ..] => {
|
||||||
context
|
for (i, (name, module)) in context
|
||||||
.global_runtime_state()
|
.global_runtime_state()
|
||||||
.scan_imports_raw()
|
.scan_imports_raw()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.for_each(|(i, (name, module))| {
|
{
|
||||||
println!(
|
println!(
|
||||||
"[{}] {} = {}",
|
"[{}] {} = {}",
|
||||||
i + 1,
|
i + 1,
|
||||||
name,
|
name,
|
||||||
module.id().unwrap_or("<unknown>")
|
module.id().unwrap_or("<unknown>")
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
["backtrace", ..] => {
|
["backtrace", ..] => {
|
||||||
context
|
for frame in context
|
||||||
.global_runtime_state()
|
.global_runtime_state()
|
||||||
.debugger
|
.debugger
|
||||||
.call_stack()
|
.call_stack()
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.for_each(|frame| println!("{}", frame));
|
{
|
||||||
|
println!("{}", frame)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
["clear", ..] => {
|
["clear", ..] => {
|
||||||
context
|
context
|
||||||
@ -338,13 +339,14 @@ fn main() {
|
|||||||
.clear();
|
.clear();
|
||||||
println!("All break-points cleared.");
|
println!("All break-points cleared.");
|
||||||
}
|
}
|
||||||
["breakpoints", ..] => context
|
["breakpoints", ..] => Iterator::for_each(
|
||||||
|
context
|
||||||
.global_runtime_state()
|
.global_runtime_state()
|
||||||
.debugger
|
.debugger
|
||||||
.break_points()
|
.break_points()
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate(),
|
||||||
.for_each(|(i, bp)| match bp {
|
|(i, bp)| match bp {
|
||||||
#[cfg(not(feature = "no_position"))]
|
#[cfg(not(feature = "no_position"))]
|
||||||
rhai::debugger::BreakPoint::AtPosition { pos, .. } => {
|
rhai::debugger::BreakPoint::AtPosition { pos, .. } => {
|
||||||
let line_num = format!("[{}] line ", i + 1);
|
let line_num = format!("[{}] line ", i + 1);
|
||||||
@ -352,7 +354,8 @@ fn main() {
|
|||||||
print_source(&lines, *pos, line_num.len());
|
print_source(&lines, *pos, line_num.len());
|
||||||
}
|
}
|
||||||
_ => println!("[{}] {}", 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>() {
|
||||||
let range = 1..=context
|
let range = 1..=context
|
||||||
@ -504,7 +507,9 @@ fn main() {
|
|||||||
.break_points_mut()
|
.break_points_mut()
|
||||||
.push(bp);
|
.push(bp);
|
||||||
}
|
}
|
||||||
["throw"] => break Err(EvalAltResult::ErrorRuntime(Dynamic::UNIT, pos).into()),
|
["throw"] => {
|
||||||
|
break Err(EvalAltResult::ErrorRuntime(Dynamic::UNIT, pos).into())
|
||||||
|
}
|
||||||
["throw", _msg, ..] => {
|
["throw", _msg, ..] => {
|
||||||
let msg = input.trim().splitn(2, ' ').skip(1).next().unwrap_or("");
|
let msg = input.trim().splitn(2, ' ').skip(1).next().unwrap_or("");
|
||||||
break Err(EvalAltResult::ErrorRuntime(msg.trim().into(), pos).into());
|
break Err(EvalAltResult::ErrorRuntime(msg.trim().into(), pos).into());
|
||||||
@ -518,7 +523,8 @@ fn main() {
|
|||||||
Err(err) => panic!("input error: {}", err),
|
Err(err) => panic!("input error: {}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Set a file module resolver without caching
|
// Set a file module resolver without caching
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
@ -61,10 +61,7 @@ fn print_help() {
|
|||||||
|
|
||||||
/// Display the scope.
|
/// Display the scope.
|
||||||
fn print_scope(scope: &Scope) {
|
fn print_scope(scope: &Scope) {
|
||||||
scope
|
for (i, (name, constant, value)) in scope.iter_raw().enumerate() {
|
||||||
.iter_raw()
|
|
||||||
.enumerate()
|
|
||||||
.for_each(|(i, (name, constant, value))| {
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
@ -78,7 +75,7 @@ fn print_scope(scope: &Scope) {
|
|||||||
value_is_shared,
|
value_is_shared,
|
||||||
*value.read_lock::<Dynamic>().unwrap(),
|
*value.read_lock::<Dynamic>().unwrap(),
|
||||||
)
|
)
|
||||||
});
|
}
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
@ -282,13 +279,13 @@ fn main() {
|
|||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
"functions" => {
|
"functions" => {
|
||||||
// print a list of all registered functions
|
// print a list of all registered functions
|
||||||
engine
|
for f in engine.gen_fn_signatures(false) {
|
||||||
.gen_fn_signatures(false)
|
println!("{}", f)
|
||||||
.into_iter()
|
}
|
||||||
.for_each(|f| println!("{}", f));
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
for f in main_ast.iter_functions() {
|
||||||
main_ast.iter_functions().for_each(|f| println!("{}", f));
|
println!("{}", f)
|
||||||
|
}
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
continue;
|
continue;
|
||||||
|
@ -140,7 +140,10 @@ pub struct Engine {
|
|||||||
|
|
||||||
/// Callback closure for debugging.
|
/// Callback closure for debugging.
|
||||||
#[cfg(feature = "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 {
|
impl fmt::Debug for Engine {
|
||||||
|
@ -8,11 +8,18 @@ use std::fmt;
|
|||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
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"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type OnDebuggerCallback =
|
pub type OnDebuggerCallback =
|
||||||
dyn Fn(&mut EvalContext, ASTNode, Option<&str>, Position) -> RhaiResultOf<DebuggerCommand>;
|
dyn Fn(&mut EvalContext, ASTNode, Option<&str>, Position) -> RhaiResultOf<DebuggerCommand>;
|
||||||
/// A standard callback function for debugging.
|
/// Callback function for debugging.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type OnDebuggerCallback = dyn Fn(&mut EvalContext, ASTNode, Option<&str>, Position) -> RhaiResultOf<DebuggerCommand>
|
pub type OnDebuggerCallback = dyn Fn(&mut EvalContext, ASTNode, Option<&str>, Position) -> RhaiResultOf<DebuggerCommand>
|
||||||
+ Send
|
+ Send
|
||||||
@ -192,6 +199,8 @@ impl fmt::Display for CallStackFrame {
|
|||||||
pub struct Debugger {
|
pub struct Debugger {
|
||||||
/// The current status command.
|
/// The current status command.
|
||||||
status: DebuggerCommand,
|
status: DebuggerCommand,
|
||||||
|
/// The current state.
|
||||||
|
state: Dynamic,
|
||||||
/// The current set of break-points.
|
/// The current set of break-points.
|
||||||
break_points: Vec<BreakPoint>,
|
break_points: Vec<BreakPoint>,
|
||||||
/// The current function call stack.
|
/// The current function call stack.
|
||||||
@ -200,28 +209,44 @@ pub struct Debugger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Debugger {
|
impl Debugger {
|
||||||
/// Create a new [`Debugger`].
|
/// Create a new [`Debugger`] based on an [`Engine`].
|
||||||
pub const fn new() -> Self {
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(engine: &Engine) -> Self {
|
||||||
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(),
|
break_points: Vec::new(),
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
call_stack: Vec::new(),
|
call_stack: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the function call stack depth.
|
/// Get a reference to the current state.
|
||||||
///
|
|
||||||
/// Not available under `no_function`.
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn call_stack_len(&self) -> usize {
|
#[must_use]
|
||||||
self.call_stack.len()
|
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.
|
/// Get the current call stack.
|
||||||
///
|
///
|
||||||
/// Not available under `no_function`.
|
/// Not available under `no_function`.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
pub fn call_stack(&self) -> &[CallStackFrame] {
|
pub fn call_stack(&self) -> &[CallStackFrame] {
|
||||||
&self.call_stack
|
&self.call_stack
|
||||||
}
|
}
|
||||||
@ -258,10 +283,11 @@ impl Debugger {
|
|||||||
pub fn status(&self) -> DebuggerCommand {
|
pub fn status(&self) -> DebuggerCommand {
|
||||||
self.status
|
self.status
|
||||||
}
|
}
|
||||||
/// Set the status of this [`Debugger`].
|
/// Get a mutable reference to the current status of this [`Debugger`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_status(&mut self, status: DebuggerCommand) {
|
#[must_use]
|
||||||
self.status = status;
|
pub fn status_mut(&mut self) -> &mut DebuggerCommand {
|
||||||
|
&mut self.status
|
||||||
}
|
}
|
||||||
/// Set the status of this [`Debugger`].
|
/// Set the status of this [`Debugger`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -270,17 +296,8 @@ impl Debugger {
|
|||||||
self.status = cmd;
|
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?
|
/// Does a particular [`AST` Node][ASTNode] trigger a break-point?
|
||||||
|
#[must_use]
|
||||||
pub fn is_break_point(&self, src: &str, node: ASTNode) -> bool {
|
pub fn is_break_point(&self, src: &str, node: ASTNode) -> bool {
|
||||||
let _src = src;
|
let _src = src;
|
||||||
|
|
||||||
@ -347,7 +364,7 @@ impl Engine {
|
|||||||
if let Some(cmd) =
|
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);
|
global.debugger.status = cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -375,7 +392,7 @@ impl Engine {
|
|||||||
node: impl Into<ASTNode<'a>>,
|
node: impl Into<ASTNode<'a>>,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResultOf<Option<DebuggerCommand>> {
|
) -> RhaiResultOf<Option<DebuggerCommand>> {
|
||||||
if let Some(ref on_debugger) = self.debugger {
|
if let Some((_, ref on_debugger)) = self.debugger {
|
||||||
let node = node.into();
|
let node = node.into();
|
||||||
|
|
||||||
// Skip transitive nodes
|
// Skip transitive nodes
|
||||||
@ -415,19 +432,19 @@ impl Engine {
|
|||||||
|
|
||||||
match command {
|
match command {
|
||||||
DebuggerCommand::Continue => {
|
DebuggerCommand::Continue => {
|
||||||
global.debugger.set_status(DebuggerCommand::Continue);
|
global.debugger.status = DebuggerCommand::Continue;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
DebuggerCommand::Next => {
|
DebuggerCommand::Next => {
|
||||||
global.debugger.set_status(DebuggerCommand::Continue);
|
global.debugger.status = DebuggerCommand::Continue;
|
||||||
Ok(Some(DebuggerCommand::Next))
|
Ok(Some(DebuggerCommand::Next))
|
||||||
}
|
}
|
||||||
DebuggerCommand::StepInto => {
|
DebuggerCommand::StepInto => {
|
||||||
global.debugger.set_status(DebuggerCommand::StepInto);
|
global.debugger.status = DebuggerCommand::StepInto;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
DebuggerCommand::StepOver => {
|
DebuggerCommand::StepOver => {
|
||||||
global.debugger.set_status(DebuggerCommand::Continue);
|
global.debugger.status = DebuggerCommand::Continue;
|
||||||
Ok(Some(DebuggerCommand::StepOver))
|
Ok(Some(DebuggerCommand::StepOver))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,8 +64,8 @@ impl Engine {
|
|||||||
// Qualified variable access
|
// Qualified variable access
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(_, Some((namespace, hash_var)), var_name) => {
|
(_, Some((namespace, hash_var)), var_name) => {
|
||||||
if let Some(module) = self.search_imports(global, state, namespace) {
|
|
||||||
// foo:bar::baz::VARIABLE
|
// foo:bar::baz::VARIABLE
|
||||||
|
if let Some(module) = self.search_imports(global, state, namespace) {
|
||||||
return match module.get_qualified_var(*hash_var) {
|
return match module.get_qualified_var(*hash_var) {
|
||||||
Ok(target) => {
|
Ok(target) => {
|
||||||
let mut target = target.clone();
|
let mut target = target.clone();
|
||||||
@ -89,19 +89,17 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// global::VARIABLE
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
if namespace.len() == 1 && namespace[0].name == crate::engine::KEYWORD_GLOBAL {
|
if namespace.len() == 1 && namespace[0].name == crate::engine::KEYWORD_GLOBAL {
|
||||||
// global::VARIABLE
|
let mut guard = crate::func::native::locked_write(&global.constants);
|
||||||
let global_constants = global.constants_mut();
|
|
||||||
|
|
||||||
if let Some(mut guard) = global_constants {
|
|
||||||
if let Some(value) = guard.get_mut(var_name) {
|
if let Some(value) = guard.get_mut(var_name) {
|
||||||
let mut target: Target = value.clone().into();
|
let mut target: Target = value.clone().into();
|
||||||
// Module variables are constant
|
// Module variables are constant
|
||||||
target.set_access_mode(AccessMode::ReadOnly);
|
target.set_access_mode(AccessMode::ReadOnly);
|
||||||
return Ok((target.into(), *_var_pos));
|
return Ok((target.into(), *_var_pos));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return Err(ERR::ErrorVariableNotFound(
|
return Err(ERR::ErrorVariableNotFound(
|
||||||
format!(
|
format!(
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
//! Global runtime state.
|
//! Global runtime state.
|
||||||
|
|
||||||
use crate::func::{CallableFunction, IteratorFn};
|
use crate::func::{CallableFunction, IteratorFn};
|
||||||
use crate::{Identifier, Module, Shared, StaticVec};
|
use crate::{Engine, Identifier, Module, Shared, StaticVec};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
fmt,
|
fmt,
|
||||||
iter::{FromIterator, Rev, Zip},
|
iter::{Rev, Zip},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -44,8 +44,7 @@ pub struct GlobalRuntimeState<'a> {
|
|||||||
/// Cache of globally-defined constants.
|
/// Cache of globally-defined constants.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
constants:
|
pub(crate) constants: crate::Locked<std::collections::BTreeMap<Identifier, crate::Dynamic>>,
|
||||||
Option<Shared<crate::Locked<std::collections::BTreeMap<Identifier, crate::Dynamic>>>>,
|
|
||||||
/// Debugging interface.
|
/// Debugging interface.
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
pub debugger: super::Debugger,
|
pub debugger: super::Debugger,
|
||||||
@ -53,18 +52,11 @@ pub struct GlobalRuntimeState<'a> {
|
|||||||
dummy: PhantomData<&'a ()>,
|
dummy: PhantomData<&'a ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for GlobalRuntimeState<'_> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlobalRuntimeState<'_> {
|
impl GlobalRuntimeState<'_> {
|
||||||
/// Create a new [`GlobalRuntimeState`].
|
/// Create a new [`GlobalRuntimeState`] based on an [`Engine`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new(engine: &Engine) -> Self {
|
||||||
Self {
|
Self {
|
||||||
keys: StaticVec::new_const(),
|
keys: StaticVec::new_const(),
|
||||||
modules: StaticVec::new_const(),
|
modules: StaticVec::new_const(),
|
||||||
@ -77,9 +69,9 @@ impl GlobalRuntimeState<'_> {
|
|||||||
fn_hash_indexing: (0, 0),
|
fn_hash_indexing: (0, 0),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
constants: None,
|
constants: std::collections::BTreeMap::new().into(),
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
debugger: crate::eval::Debugger::new(),
|
debugger: crate::eval::Debugger::new(engine),
|
||||||
dummy: PhantomData::default(),
|
dummy: PhantomData::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,33 +185,6 @@ impl GlobalRuntimeState<'_> {
|
|||||||
s => Some(s),
|
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.
|
/// Get the pre-calculated index getter hash.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[must_use]
|
#[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<'_> {
|
impl<K: Into<Identifier>, M: Into<Shared<Module>>> Extend<(K, M)> for GlobalRuntimeState<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extend<T: IntoIterator<Item = (K, M)>>(&mut self, iter: T) {
|
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.keys.push(k.into());
|
||||||
self.modules.push(m.into());
|
self.modules.push(m.into());
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ pub use chaining::{ChainArgument, ChainType};
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub use debugger::CallStackFrame;
|
pub use debugger::CallStackFrame;
|
||||||
#[cfg(feature = "debugging")]
|
#[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_context::EvalContext;
|
||||||
pub use eval_state::EvalState;
|
pub use eval_state::EvalState;
|
||||||
pub use global_state::GlobalRuntimeState;
|
pub use global_state::GlobalRuntimeState;
|
||||||
|
@ -818,8 +818,8 @@ impl Engine {
|
|||||||
&& entry_type == AccessMode::ReadOnly
|
&& entry_type == AccessMode::ReadOnly
|
||||||
&& lib.iter().any(|&m| !m.is_empty())
|
&& lib.iter().any(|&m| !m.is_empty())
|
||||||
{
|
{
|
||||||
// Add a global constant if at top level and there are functions
|
crate::func::native::locked_write(&global.constants)
|
||||||
global.set_constant(var_name.clone(), value.clone());
|
.insert(var_name.clone(), value.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if export {
|
||||||
@ -869,23 +869,20 @@ impl Engine {
|
|||||||
if let Ok(path) = path_result {
|
if let Ok(path) = path_result {
|
||||||
use crate::ModuleResolver;
|
use crate::ModuleResolver;
|
||||||
|
|
||||||
let source = match global.source.as_str() {
|
|
||||||
"" => None,
|
|
||||||
s => Some(s),
|
|
||||||
};
|
|
||||||
let path_pos = expr.position();
|
let path_pos = expr.position();
|
||||||
|
|
||||||
let module_result = global
|
let resolver = global.embedded_module_resolver.clone();
|
||||||
.embedded_module_resolver
|
|
||||||
|
let module_result = resolver
|
||||||
.as_ref()
|
.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,
|
Err(err) if matches!(*err, ERR::ErrorModuleNotFound(_, _)) => None,
|
||||||
result => Some(result),
|
result => Some(result),
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
self.module_resolver
|
self.module_resolver
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|r| r.resolve(self, source, &path, path_pos))
|
.map(|r| r.resolve_raw(self, global, &path, path_pos))
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
Err(ERR::ErrorModuleNotFound(path.to_string(), path_pos).into())
|
Err(ERR::ErrorModuleNotFound(path.to_string(), path_pos).into())
|
||||||
|
@ -22,7 +22,7 @@ pub use hashing::{
|
|||||||
combine_hashes, get_hasher,
|
combine_hashes, get_hasher,
|
||||||
};
|
};
|
||||||
pub use native::{
|
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,
|
FnPlugin, IteratorFn, Locked, NativeCallContext, SendSync, Shared,
|
||||||
};
|
};
|
||||||
pub use plugin::PluginFunction;
|
pub use plugin::PluginFunction;
|
||||||
|
@ -286,23 +286,29 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
is_method_call: bool,
|
is_method_call: bool,
|
||||||
args: &mut [&mut Dynamic],
|
args: &mut [&mut Dynamic],
|
||||||
) -> RhaiResult {
|
) -> 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 fn_name = fn_name.as_ref();
|
||||||
let len = args.len();
|
let args_len = args.len();
|
||||||
|
|
||||||
let hash = if is_method_call {
|
let hash = if is_method_call {
|
||||||
FnCallHashes::from_all(
|
FnCallHashes::from_all(
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
calc_fn_hash(fn_name, len - 1),
|
calc_fn_hash(fn_name, args_len - 1),
|
||||||
calc_fn_hash(fn_name, len),
|
calc_fn_hash(fn_name, args_len),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
calc_fn_hash(fn_name, len).into()
|
calc_fn_hash(fn_name, args_len).into()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.engine()
|
self.engine()
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
&mut self.global.cloned().unwrap_or_else(GlobalRuntimeState::new),
|
&mut global,
|
||||||
&mut EvalState::new(),
|
&mut state,
|
||||||
self.lib,
|
self.lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
hash,
|
hash,
|
||||||
@ -353,11 +359,11 @@ pub fn shared_take<T>(value: Shared<T>) -> T {
|
|||||||
shared_try_take(value).ok().expect("not shared")
|
shared_try_take(value).ok().expect("not shared")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lock a [`Shared`] resource.
|
/// Lock a [`Locked`] resource.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[allow(dead_code)]
|
#[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"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
return value.borrow_mut();
|
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();
|
return value.write().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A general function trail object.
|
/// General function trail object.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult;
|
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult;
|
||||||
/// A general function trail object.
|
/// General function trail object.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + Send + 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;
|
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"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type IteratorFn = dyn Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
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")]
|
#[cfg(feature = "sync")]
|
||||||
pub type IteratorFn = dyn Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + Send + 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")]
|
#[cfg(feature = "sync")]
|
||||||
pub type FnPlugin = dyn PluginFunction + Send + 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 = "unchecked"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type OnProgressCallback = dyn Fn(u64) -> Option<Dynamic>;
|
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(not(feature = "unchecked"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type OnProgressCallback = dyn Fn(u64) -> Option<Dynamic> + Send + 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"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type OnPrintCallback = dyn Fn(&str);
|
pub type OnPrintCallback = dyn Fn(&str);
|
||||||
/// A standard callback function for printing.
|
/// Callback function for printing.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type OnPrintCallback = dyn Fn(&str) + Send + Sync;
|
pub type OnPrintCallback = dyn Fn(&str) + Send + Sync;
|
||||||
|
|
||||||
/// A standard callback function for debugging.
|
/// Callback function for debugging.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type OnDebugCallback = dyn Fn(&str, Option<&str>, Position);
|
pub type OnDebugCallback = dyn Fn(&str, Option<&str>, Position);
|
||||||
/// A standard callback function for debugging.
|
/// Callback function for debugging.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type OnDebugCallback = dyn Fn(&str, Option<&str>, Position) + Send + 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"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type OnParseTokenCallback = dyn Fn(Token, Position, &TokenizeState) -> Token;
|
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")]
|
#[cfg(feature = "sync")]
|
||||||
pub type OnParseTokenCallback = dyn Fn(Token, Position, &TokenizeState) -> Token + Send + 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"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type OnVarCallback = dyn Fn(&str, usize, &EvalContext) -> RhaiResultOf<Option<Dynamic>>;
|
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")]
|
#[cfg(feature = "sync")]
|
||||||
pub type OnVarCallback =
|
pub type OnVarCallback =
|
||||||
dyn Fn(&str, usize, &EvalContext) -> RhaiResultOf<Option<Dynamic>> + Send + Sync;
|
dyn Fn(&str, usize, &EvalContext) -> RhaiResultOf<Option<Dynamic>> + Send + Sync;
|
||||||
|
@ -74,7 +74,7 @@ impl Engine {
|
|||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[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
|
// Put arguments into scope as variables
|
||||||
scope.extend(fn_def.params.iter().cloned().zip(args.into_iter().map(|v| {
|
scope.extend(fn_def.params.iter().cloned().zip(args.into_iter().map(|v| {
|
||||||
@ -115,10 +115,9 @@ impl Engine {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
if let Some(ref modules) = fn_def.global {
|
if let Some(ref modules) = fn_def.global {
|
||||||
modules
|
for (n, m) in modules.iter().cloned() {
|
||||||
.iter()
|
global.push_import(n, m)
|
||||||
.cloned()
|
}
|
||||||
.for_each(|(n, m)| global.push_import(n, m));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the function
|
// Evaluate the function
|
||||||
|
@ -1401,9 +1401,9 @@ impl Module {
|
|||||||
/// Sub-modules are flattened onto the root [`Module`], with higher level overriding lower level.
|
/// Sub-modules are flattened onto the root [`Module`], with higher level overriding lower level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn combine_flatten(&mut self, other: Self) -> &mut Self {
|
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.combine_flatten(shared_take_or_clone(m));
|
||||||
});
|
}
|
||||||
self.variables.extend(other.variables.into_iter());
|
self.variables.extend(other.variables.into_iter());
|
||||||
self.functions.extend(other.functions.into_iter());
|
self.functions.extend(other.functions.into_iter());
|
||||||
self.type_iterators.extend(other.type_iterators.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.
|
/// Only items not existing in this [`Module`] are added.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn fill_with(&mut self, other: &Self) -> &mut Self {
|
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) {
|
if !self.modules.contains_key(k) {
|
||||||
self.modules.insert(k.clone(), v.clone());
|
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) {
|
if !self.variables.contains_key(k) {
|
||||||
self.variables.insert(k.clone(), v.clone());
|
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());
|
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.type_iterators.entry(k).or_insert(v.clone());
|
||||||
});
|
}
|
||||||
self.all_functions.clear();
|
self.all_functions.clear();
|
||||||
self.all_variables.clear();
|
self.all_variables.clear();
|
||||||
self.all_type_iterators.clear();
|
self.all_type_iterators.clear();
|
||||||
@ -1455,12 +1455,11 @@ impl Module {
|
|||||||
other: &Self,
|
other: &Self,
|
||||||
_filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool + Copy,
|
_filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool + Copy,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
#[cfg(not(feature = "no_function"))]
|
for (k, v) in &other.modules {
|
||||||
other.modules.iter().for_each(|(k, v)| {
|
|
||||||
let mut m = Self::new();
|
let mut m = Self::new();
|
||||||
m.merge_filtered(v, _filter);
|
m.merge_filtered(v, _filter);
|
||||||
self.set_sub_module(k.clone(), m);
|
self.set_sub_module(k.clone(), m);
|
||||||
});
|
}
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
self.modules
|
self.modules
|
||||||
.extend(other.modules.iter().map(|(k, v)| (k.clone(), v.clone())));
|
.extend(other.modules.iter().map(|(k, v)| (k.clone(), v.clone())));
|
||||||
@ -1658,28 +1657,74 @@ impl Module {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn eval_ast_as_new(
|
pub fn eval_ast_as_new(
|
||||||
scope: crate::Scope,
|
scope: crate::Scope,
|
||||||
ast: &crate::AST,
|
ast: &crate::AST,
|
||||||
engine: &crate::Engine,
|
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> {
|
) -> RhaiResultOf<Self> {
|
||||||
let mut scope = scope;
|
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_imports_len = global.num_imports();
|
||||||
|
let orig_source = global.source.clone();
|
||||||
|
let orig_constants = std::mem::take(&mut global.constants);
|
||||||
|
|
||||||
// Run the script
|
// 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
|
// Create new module
|
||||||
let mut module =
|
let mut module = Module::new();
|
||||||
scope
|
|
||||||
.into_iter()
|
// Extra modules left become sub-modules
|
||||||
.fold(Module::new(), |mut module, (_, value, mut aliases)| {
|
#[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
|
// Variables with an alias left in the scope become module variables
|
||||||
|
for (_, value, mut aliases) in scope {
|
||||||
match aliases.len() {
|
match aliases.len() {
|
||||||
0 => (),
|
0 => (),
|
||||||
1 => {
|
1 => {
|
||||||
@ -1688,30 +1733,14 @@ impl Module {
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let last_alias = aliases.pop().unwrap();
|
let last_alias = aliases.pop().unwrap();
|
||||||
aliases.into_iter().for_each(|alias| {
|
for alias in aliases {
|
||||||
module.set_var(alias, value.clone());
|
module.set_var(alias, value.clone());
|
||||||
});
|
}
|
||||||
// Avoid cloning the last value
|
// Avoid cloning the last value
|
||||||
module.set_var(last_alias, 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"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let func_global = func_global.map(|v| v.into_boxed_slice());
|
let func_global = func_global.map(|v| v.into_boxed_slice());
|
||||||
@ -1773,29 +1802,29 @@ impl Module {
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
let mut contains_indexed_global_functions = false;
|
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.
|
// Index all the sub-modules first.
|
||||||
path.push(name);
|
path.push(name);
|
||||||
if index_module(m, path, variables, functions, type_iterators) {
|
if index_module(m, path, variables, functions, type_iterators) {
|
||||||
contains_indexed_global_functions = true;
|
contains_indexed_global_functions = true;
|
||||||
}
|
}
|
||||||
path.pop();
|
path.pop();
|
||||||
});
|
}
|
||||||
|
|
||||||
// Index all variables
|
// 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);
|
let hash_var = crate::calc_qualified_var_hash(path.iter().copied(), var_name);
|
||||||
variables.insert(hash_var, value.clone());
|
variables.insert(hash_var, value.clone());
|
||||||
});
|
}
|
||||||
|
|
||||||
// Index type iterators
|
// 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());
|
type_iterators.insert(type_id, func.clone());
|
||||||
contains_indexed_global_functions = true;
|
contains_indexed_global_functions = true;
|
||||||
});
|
}
|
||||||
|
|
||||||
// Index all Rust functions
|
// Index all Rust functions
|
||||||
module.functions.iter().for_each(|(&hash, f)| {
|
for (&hash, f) in &module.functions {
|
||||||
match f.metadata.namespace {
|
match f.metadata.namespace {
|
||||||
FnNamespace::Global => {
|
FnNamespace::Global => {
|
||||||
// Flatten all functions with global namespace
|
// Flatten all functions with global namespace
|
||||||
@ -1806,7 +1835,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
match f.metadata.access {
|
match f.metadata.access {
|
||||||
FnAccess::Public => (),
|
FnAccess::Public => (),
|
||||||
FnAccess::Private => return, // Do not index private functions
|
FnAccess::Private => continue, // Do not index private functions
|
||||||
}
|
}
|
||||||
|
|
||||||
if !f.func.is_script() {
|
if !f.func.is_script() {
|
||||||
@ -1824,7 +1853,7 @@ impl Module {
|
|||||||
);
|
);
|
||||||
functions.insert(hash_qualified_script, f.func.clone());
|
functions.insert(hash_qualified_script, f.func.clone());
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
contains_indexed_global_functions
|
contains_indexed_global_functions
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ impl ModuleResolver for ModuleResolversCollection {
|
|||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResultOf<Shared<Module>> {
|
) -> RhaiResultOf<Shared<Module>> {
|
||||||
for resolver in self.0.iter() {
|
for resolver in &self.0 {
|
||||||
match resolver.resolve(engine, source_path, path, pos) {
|
match resolver.resolve(engine, source_path, path, pos) {
|
||||||
Ok(module) => return Ok(module),
|
Ok(module) => return Ok(module),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
#![cfg(not(feature = "no_std"))]
|
#![cfg(not(feature = "no_std"))]
|
||||||
#![cfg(not(target_family = "wasm"))]
|
#![cfg(not(target_family = "wasm"))]
|
||||||
|
|
||||||
use crate::func::native::shared_write_lock;
|
use crate::func::native::locked_write;
|
||||||
use crate::{
|
use crate::{
|
||||||
Engine, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Scope, Shared, ERR,
|
Engine, GlobalRuntimeState, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Scope,
|
||||||
|
Shared, ERR,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@ -207,12 +208,12 @@ impl FileModuleResolver {
|
|||||||
|
|
||||||
let file_path = self.get_file_path(path.as_ref(), source_path);
|
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.
|
/// Empty the internal cache.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn clear_cache(&mut self) -> &mut Self {
|
pub fn clear_cache(&mut self) -> &mut Self {
|
||||||
shared_write_lock(&self.cache).clear();
|
locked_write(&self.cache).clear();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Remove the specified path from internal cache.
|
/// Remove the specified path from internal cache.
|
||||||
@ -227,7 +228,7 @@ impl FileModuleResolver {
|
|||||||
) -> Option<Shared<Module>> {
|
) -> Option<Shared<Module>> {
|
||||||
let file_path = self.get_file_path(path.as_ref(), source_path.as_ref().map(<_>::as_ref));
|
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)
|
.remove_entry(&file_path)
|
||||||
.map(|(_, v)| v)
|
.map(|(_, v)| v)
|
||||||
}
|
}
|
||||||
@ -252,24 +253,25 @@ impl FileModuleResolver {
|
|||||||
file_path.set_extension(self.extension.as_str()); // Force extension
|
file_path.set_extension(self.extension.as_str()); // Force extension
|
||||||
file_path
|
file_path
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleResolver for FileModuleResolver {
|
/// Resolve a module based on a path.
|
||||||
fn resolve(
|
fn impl_resolve(
|
||||||
&self,
|
&self,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
source_path: Option<&str>,
|
global: Option<&mut GlobalRuntimeState>,
|
||||||
|
source: Option<&str>,
|
||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResultOf<Shared<Module>> {
|
) -> Result<Shared<Module>, Box<crate::EvalAltResult>> {
|
||||||
// Load relative paths from source if there is no base path specified
|
// Load relative paths from source if there is no base path specified
|
||||||
let source_path =
|
let source_path = global
|
||||||
source_path.and_then(|p| Path::new(p).parent().map(|p| p.to_string_lossy()));
|
.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()));
|
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() {
|
if self.is_cache_enabled() {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
let c = self.cache.borrow();
|
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 scope = Scope::new();
|
||||||
|
|
||||||
let mut ast = engine
|
let mut ast = engine
|
||||||
@ -295,18 +296,43 @@ impl ModuleResolver for FileModuleResolver {
|
|||||||
|
|
||||||
ast.set_source(path);
|
ast.set_source(path);
|
||||||
|
|
||||||
// Make a module from the AST
|
let m: Shared<Module> = if let Some(global) = global {
|
||||||
let m: Shared<Module> = Module::eval_ast_as_new(scope, &ast, engine)
|
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)))?
|
.map_err(|err| Box::new(ERR::ErrorInModule(path.to_string(), err, pos)))?
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
// Put it into the cache
|
|
||||||
if self.is_cache_enabled() {
|
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)
|
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.
|
/// Resolve an `AST` based on a path string.
|
||||||
///
|
///
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::func::native::SendSync;
|
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")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
@ -21,11 +21,26 @@ pub trait ModuleResolver: SendSync {
|
|||||||
fn resolve(
|
fn resolve(
|
||||||
&self,
|
&self,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
source_path: Option<&str>,
|
source: Option<&str>,
|
||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResultOf<Shared<Module>>;
|
) -> 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.
|
/// Resolve an `AST` based on a path string.
|
||||||
///
|
///
|
||||||
/// Returns [`None`] (default) if such resolution is not supported
|
/// Returns [`None`] (default) if such resolution is not supported
|
||||||
@ -40,7 +55,7 @@ pub trait ModuleResolver: SendSync {
|
|||||||
fn resolve_ast(
|
fn resolve_ast(
|
||||||
&self,
|
&self,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
source_path: Option<&str>,
|
source: Option<&str>,
|
||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Option<RhaiResultOf<AST>> {
|
) -> Option<RhaiResultOf<AST>> {
|
||||||
|
@ -138,7 +138,7 @@ impl<'a> OptimizerState<'a> {
|
|||||||
|
|
||||||
self.engine
|
self.engine
|
||||||
.call_native_fn(
|
.call_native_fn(
|
||||||
&mut GlobalRuntimeState::new(),
|
&mut GlobalRuntimeState::new(&self.engine),
|
||||||
&mut EvalState::new(),
|
&mut EvalState::new(),
|
||||||
lib,
|
lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
@ -237,7 +237,7 @@ fn optimize_stmt_block(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Optimize each statement in the block
|
// Optimize each statement in the block
|
||||||
statements.iter_mut().for_each(|stmt| {
|
for stmt in statements.iter_mut() {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Var(value_expr, x, options, _) => {
|
Stmt::Var(value_expr, x, options, _) => {
|
||||||
if options.contains(AST_OPTION_CONSTANT) {
|
if options.contains(AST_OPTION_CONSTANT) {
|
||||||
@ -260,7 +260,7 @@ fn optimize_stmt_block(
|
|||||||
// Optimize the statement
|
// Optimize the statement
|
||||||
_ => optimize_stmt(stmt, state, preserve_result),
|
_ => optimize_stmt(stmt, state, preserve_result),
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// Remove all pure statements except the last one
|
// Remove all pure statements except the last one
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
@ -640,7 +640,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
// switch
|
// switch
|
||||||
Stmt::Switch(match_expr, x, _) => {
|
Stmt::Switch(match_expr, x, _) => {
|
||||||
optimize_expr(match_expr, state, false);
|
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);
|
let statements = mem::take(&mut *block.statements);
|
||||||
*block.statements =
|
*block.statements =
|
||||||
optimize_stmt_block(statements, state, preserve_result, true, false);
|
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),
|
_ => block.condition = Some(condition),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// Remove false cases
|
// Remove false cases
|
||||||
while let Some((&key, _)) = x.cases.iter().find(|(_, block)| match block.condition {
|
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
|
// Add constants and variables from the scope
|
||||||
scope.iter().for_each(|(name, constant, value)| {
|
for (name, constant, value) in scope.iter() {
|
||||||
if !constant {
|
if !constant {
|
||||||
state.push_var(name, AccessMode::ReadWrite, None);
|
state.push_var(name, AccessMode::ReadWrite, None);
|
||||||
} else {
|
} else {
|
||||||
state.push_var(name, AccessMode::ReadOnly, Some(value));
|
state.push_var(name, AccessMode::ReadOnly, Some(value));
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
statements = optimize_stmt_block(statements, &mut state, true, false, true);
|
statements = optimize_stmt_block(statements, &mut state, true, false, true);
|
||||||
statements
|
statements
|
||||||
@ -1191,9 +1191,8 @@ pub fn optimize_into_ast(
|
|||||||
// We only need the script library's signatures for optimization purposes
|
// We only need the script library's signatures for optimization purposes
|
||||||
let mut lib2 = crate::Module::new();
|
let mut lib2 = crate::Module::new();
|
||||||
|
|
||||||
functions
|
for fn_def in &functions {
|
||||||
.iter()
|
lib2.set_script_fn(crate::ast::ScriptFnDef {
|
||||||
.map(|fn_def| crate::ast::ScriptFnDef {
|
|
||||||
name: fn_def.name.clone(),
|
name: fn_def.name.clone(),
|
||||||
access: fn_def.access,
|
access: fn_def.access,
|
||||||
body: crate::ast::StmtBlock::NONE,
|
body: crate::ast::StmtBlock::NONE,
|
||||||
@ -1204,33 +1203,25 @@ pub fn optimize_into_ast(
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
comments: None,
|
comments: None,
|
||||||
})
|
|
||||||
.for_each(|fn_def| {
|
|
||||||
lib2.set_script_fn(fn_def);
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let lib2 = &[&lib2];
|
let lib2 = &[&lib2];
|
||||||
|
|
||||||
functions
|
for fn_def in functions {
|
||||||
.into_iter()
|
|
||||||
.map(|fn_def| {
|
|
||||||
let mut fn_def = crate::func::native::shared_take_or_clone(fn_def);
|
let mut fn_def = crate::func::native::shared_take_or_clone(fn_def);
|
||||||
|
|
||||||
// Optimize the function body
|
// Optimize the function body
|
||||||
let body = mem::take(&mut *fn_def.body);
|
let body = mem::take(&mut *fn_def.body);
|
||||||
|
|
||||||
*fn_def.body =
|
*fn_def.body = optimize_top_level(body, engine, scope, lib2, optimization_level);
|
||||||
optimize_top_level(body, engine, scope, lib2, optimization_level);
|
|
||||||
|
|
||||||
fn_def
|
|
||||||
})
|
|
||||||
.for_each(|fn_def| {
|
|
||||||
module.set_script_fn(fn_def);
|
module.set_script_fn(fn_def);
|
||||||
});
|
}
|
||||||
} else {
|
} else {
|
||||||
functions.into_iter().for_each(|fn_def| {
|
for fn_def in functions {
|
||||||
module.set_script_fn(fn_def);
|
module.set_script_fn(fn_def);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module
|
module
|
||||||
|
@ -189,7 +189,7 @@ fn collect_fn_metadata(
|
|||||||
.for_each(|(_, _, _, _, f)| {
|
.for_each(|(_, _, _, _, f)| {
|
||||||
list.push(make_metadata(dict, Some(namespace.clone()), f).into())
|
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!(
|
let ns = format!(
|
||||||
"{}{}{}",
|
"{}{}{}",
|
||||||
namespace,
|
namespace,
|
||||||
@ -197,11 +197,12 @@ fn collect_fn_metadata(
|
|||||||
ns
|
ns
|
||||||
);
|
);
|
||||||
scan_module(list, dict, ns.into(), m.as_ref(), filter)
|
scan_module(list, dict, ns.into(), m.as_ref(), filter)
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.iter_imports_raw()
|
for (ns, m) in ctx.iter_imports_raw() {
|
||||||
.for_each(|(ns, m)| scan_module(&mut list, &dict, ns.clone(), m.as_ref(), filter));
|
scan_module(&mut list, &dict, ns.clone(), m.as_ref(), filter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list
|
list
|
||||||
|
@ -3450,9 +3450,9 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
let mut m = crate::Module::new();
|
let mut m = crate::Module::new();
|
||||||
|
|
||||||
_lib.into_iter().for_each(|fn_def| {
|
for fn_def in _lib {
|
||||||
m.set_script_fn(fn_def);
|
m.set_script_fn(fn_def);
|
||||||
});
|
}
|
||||||
|
|
||||||
return Ok(AST::new(statements, m));
|
return Ok(AST::new(statements, m));
|
||||||
}
|
}
|
||||||
|
@ -206,9 +206,9 @@ impl Engine {
|
|||||||
let _ast = ast;
|
let _ast = ast;
|
||||||
let mut global = ModuleMetadata::new();
|
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());
|
global.modules.insert(name, m.as_ref().into());
|
||||||
});
|
}
|
||||||
|
|
||||||
self.global_modules
|
self.global_modules
|
||||||
.iter()
|
.iter()
|
||||||
@ -221,11 +221,11 @@ impl Engine {
|
|||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[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();
|
let mut meta: FnMetadata = f.into();
|
||||||
meta.namespace = FnNamespace::Global;
|
meta.namespace = FnNamespace::Global;
|
||||||
global.functions.push(meta);
|
global.functions.push(meta);
|
||||||
});
|
}
|
||||||
|
|
||||||
global.functions.sort();
|
global.functions.sort();
|
||||||
|
|
||||||
|
@ -1119,18 +1119,18 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(ref mut a, _, ref mut access) => {
|
Union::Array(ref mut a, _, ref mut access) => {
|
||||||
*access = typ;
|
*access = typ;
|
||||||
a.iter_mut().for_each(|v| {
|
for v in a.iter_mut() {
|
||||||
v.set_access_mode(typ);
|
v.set_access_mode(typ);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Blob(_, _, ref mut access) => *access = typ,
|
Union::Blob(_, _, ref mut access) => *access = typ,
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(ref mut m, _, ref mut access) => {
|
Union::Map(ref mut m, _, ref mut access) => {
|
||||||
*access = typ;
|
*access = typ;
|
||||||
m.values_mut().for_each(|v| {
|
for v in m.values_mut() {
|
||||||
v.set_access_mode(typ);
|
v.set_access_mode(typ);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::TimeStamp(_, _, ref mut access) => *access = typ,
|
Union::TimeStamp(_, _, ref mut access) => *access = typ,
|
||||||
@ -1708,14 +1708,14 @@ impl Dynamic {
|
|||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell, _, _) => {
|
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>()
|
&& TypeId::of::<Dynamic>() != TypeId::of::<T>()
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
} else {
|
} 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<'_> {
|
impl<K: Into<Identifier>> Extend<(K, Dynamic)> for Scope<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extend<T: IntoIterator<Item = (K, Dynamic)>>(&mut self, iter: T) {
|
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);
|
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<'_> {
|
impl<K: Into<Identifier>> Extend<(K, bool, Dynamic)> for Scope<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extend<T: IntoIterator<Item = (K, bool, Dynamic)>>(&mut self, iter: T) {
|
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(
|
self.push_dynamic_value(
|
||||||
name,
|
name,
|
||||||
if is_constant {
|
if is_constant {
|
||||||
@ -639,7 +639,7 @@ impl<K: Into<Identifier>> Extend<(K, bool, Dynamic)> for Scope<'_> {
|
|||||||
},
|
},
|
||||||
value,
|
value,
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
#![cfg(feature = "debugging")]
|
#![cfg(feature = "debugging")]
|
||||||
use rhai::{Engine, EvalAltResult, INT};
|
use rhai::{Dynamic, Engine, EvalAltResult, INT};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use rhai::Array;
|
use rhai::Array;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
use rhai::Map;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_debugging() -> Result<(), Box<EvalAltResult>> {
|
fn test_debugging() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
@ -32,3 +35,44 @@ fn test_debugging() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
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