From 0c79471fd317c3bb6c2c08a0a245ad99e941ee15 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 7 Nov 2022 16:18:59 +0800 Subject: [PATCH 1/9] Change lib to &[Shared] and remove dummy lifetimes. --- src/api/call_fn.rs | 6 +++--- src/api/eval.rs | 6 +++--- src/api/run.rs | 8 ++++---- src/eval/cache.rs | 8 ++------ src/eval/chaining.rs | 16 +++++++++------- src/eval/debugger.rs | 10 +++++----- src/eval/eval_context.rs | 24 ++++++++++++------------ src/eval/expr.rs | 12 ++++++------ src/eval/global_state.rs | 18 +++++++----------- src/eval/stmt.rs | 11 ++++++----- src/func/call.rs | 23 +++++++++++------------ src/func/mod.rs | 2 ++ src/func/script.rs | 10 +++++----- src/optimizer.rs | 20 ++++++++++---------- src/parser.rs | 2 +- src/types/fn_ptr.rs | 11 +++++++---- 16 files changed, 93 insertions(+), 94 deletions(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 1a9e2347..32510f60 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -4,8 +4,8 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::types::dynamic::Variant; use crate::{ - reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST, - ERR, + reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, Shared, StaticVec, + AST, ERR, }; use std::any::{type_name, TypeId}; #[cfg(feature = "no_std")] @@ -248,7 +248,7 @@ impl Engine { arg_values: &mut [Dynamic], ) -> RhaiResult { let statements = ast.statements(); - let lib = &[ast.as_ref()]; + let lib = &[AsRef::>::as_ref(ast).clone()]; let mut this_ptr = this_ptr; let orig_scope_len = scope.len(); diff --git a/src/api/eval.rs b/src/api/eval.rs index ffef8623..02f21831 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -195,7 +195,7 @@ impl Engine { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let lib = &[ #[cfg(not(feature = "no_function"))] - ast.as_ref(), + AsRef::>::as_ref(ast).clone(), ]; let node = &crate::ast::Stmt::Noop(Position::NONE); self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; @@ -234,7 +234,7 @@ impl Engine { let mut _lib = &[ #[cfg(not(feature = "no_function"))] - ast.as_ref(), + AsRef::>::as_ref(ast).clone(), ][..]; #[cfg(not(feature = "no_function"))] if !ast.has_functions() { @@ -264,7 +264,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&crate::Module], + lib: &[crate::Shared], level: usize, scope: &mut Scope, statements: &[crate::ast::Stmt], diff --git a/src/api/run.rs b/src/api/run.rs index effdd4b6..96faee2e 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -2,7 +2,7 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::parser::ParseState; -use crate::{Engine, Module, RhaiResultOf, Scope, AST}; +use crate::{Engine, RhaiResultOf, Scope, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -124,9 +124,9 @@ impl Engine { if !statements.is_empty() { let lib = [ #[cfg(not(feature = "no_function"))] - ast.as_ref(), + AsRef::>::as_ref(ast).clone(), ]; - let lib = if lib.first().map_or(true, |m: &&Module| m.is_empty()) { + let lib = if lib.first().map_or(true, |m| m.is_empty()) { &lib[0..0] } else { &lib @@ -139,7 +139,7 @@ impl Engine { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let lib = &[ #[cfg(not(feature = "no_function"))] - ast.as_ref(), + AsRef::>::as_ref(ast).clone(), ]; let node = &crate::ast::Stmt::Noop(crate::Position::NONE); self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; diff --git a/src/eval/cache.rs b/src/eval/cache.rs index b71eb8cf..ee4f07a3 100644 --- a/src/eval/cache.rs +++ b/src/eval/cache.rs @@ -3,7 +3,6 @@ use crate::func::{CallableFunction, StraightHashMap}; use crate::types::BloomFilterU64; use crate::{ImmutableString, StaticVec}; -use std::marker::PhantomData; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -45,21 +44,18 @@ impl FnResolutionCache { /// The following caches are contained inside this type: /// * A stack of [function resolution caches][FnResolutionCache] #[derive(Debug, Clone)] -pub struct Caches<'a> { +pub struct Caches { /// Stack of [function resolution caches][FnResolutionCache]. stack: StaticVec, - /// Take care of the lifetime parameter. - dummy: PhantomData<&'a ()>, } -impl Caches<'_> { +impl Caches { /// Create an empty [`Caches`]. #[inline(always)] #[must_use] pub const fn new() -> Self { Self { stack: StaticVec::new_const(), - dummy: PhantomData, } } /// Get the number of function resolution cache(s) in the stack. diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index ecc40fc7..cf51a465 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -4,7 +4,9 @@ use super::{Caches, GlobalRuntimeState, Target}; use crate::ast::{ASTFlags, Expr, OpAssignment}; use crate::types::dynamic::Union; -use crate::{Dynamic, Engine, FnArgsVec, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR}; +use crate::{ + Dynamic, Engine, FnArgsVec, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, ERR, +}; use std::hash::Hash; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -40,7 +42,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, this_ptr: &mut Option<&mut Dynamic>, target: &mut Target, @@ -555,7 +557,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -646,7 +648,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -758,7 +760,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, target: &mut Dynamic, idx: &mut Dynamic, @@ -781,7 +783,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, target: &mut Dynamic, idx: &mut Dynamic, @@ -805,7 +807,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, target: &'t mut Dynamic, idx: &mut Dynamic, diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 9b516fdf..3048e5bc 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -4,7 +4,7 @@ use super::{Caches, EvalContext, GlobalRuntimeState}; use crate::ast::{ASTNode, Expr, Stmt}; use crate::{ - Dynamic, Engine, EvalAltResult, ImmutableString, Module, Position, RhaiResultOf, Scope, + Dynamic, Engine, EvalAltResult, ImmutableString, Module, Position, RhaiResultOf, Scope, Shared, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -413,7 +413,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -440,7 +440,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -463,7 +463,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -510,7 +510,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index 172720cd..99456ce1 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -1,39 +1,39 @@ //! Evaluation context. use super::{Caches, GlobalRuntimeState}; -use crate::{Dynamic, Engine, Module, Scope}; +use crate::{Dynamic, Engine, Module, Scope, Shared}; #[cfg(feature = "no_std")] use std::prelude::v1::*; /// Context of a script evaluation process. #[derive(Debug)] #[allow(dead_code)] -pub struct EvalContext<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> { +pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { /// The current [`Engine`]. engine: &'a Engine, /// The current [`Scope`]. scope: &'s mut Scope<'ps>, /// The current [`GlobalRuntimeState`]. - global: &'g mut GlobalRuntimeState<'pg>, + global: &'g mut GlobalRuntimeState, /// The current [caches][Caches], if available. - caches: Option<&'c mut Caches<'pc>>, + caches: Option<&'c mut Caches>, /// The current stack of imported [modules][Module]. - lib: &'a [&'a Module], + lib: &'a [Shared], /// The current bound `this` pointer, if any. this_ptr: &'t mut Option<&'pt mut Dynamic>, /// The current nesting level of function calls. level: usize, } -impl<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> { +impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { /// Create a new [`EvalContext`]. #[inline(always)] #[must_use] pub fn new( engine: &'a Engine, - global: &'g mut GlobalRuntimeState<'pg>, - caches: Option<&'c mut Caches<'pc>>, - lib: &'a [&'a Module], + global: &'g mut GlobalRuntimeState, + caches: Option<&'c mut Caches>, + lib: &'a [Shared], level: usize, scope: &'s mut Scope<'ps>, this_ptr: &'t mut Option<&'pt mut Dynamic>, @@ -104,20 +104,20 @@ impl<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'pg, ' #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub fn global_runtime_state_mut(&mut self) -> &mut &'g mut GlobalRuntimeState<'pg> { + pub fn global_runtime_state_mut(&mut self) -> &mut &'g mut GlobalRuntimeState { &mut self.global } /// Get an iterator over the namespaces containing definition of all script-defined functions. #[inline] pub fn iter_namespaces(&self) -> impl Iterator { - self.lib.iter().copied() + self.lib.iter().map(|m| m.as_ref()) } /// _(internals)_ The current set of namespaces containing definitions of all script-defined functions. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub const fn namespaces(&self) -> &[&Module] { + pub const fn namespaces(&self) -> &[Shared] { self.lib } /// The current bound `this` pointer, if any. diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 0a9558da..afa47ce4 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -4,7 +4,7 @@ use super::{Caches, EvalContext, GlobalRuntimeState, Target}; use crate::ast::{Expr, OpAssignment}; use crate::engine::{KEYWORD_THIS, OP_CONCAT}; use crate::types::dynamic::AccessMode; -use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR}; +use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, ERR}; use std::num::NonZeroUsize; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -18,7 +18,7 @@ impl Engine { &self, global: &GlobalRuntimeState, namespace: &crate::ast::Namespace, - ) -> Option> { + ) -> Option> { assert!(!namespace.is_empty()); let root = namespace.root(); @@ -51,7 +51,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &'s mut Scope, this_ptr: &'s mut Option<&mut Dynamic>, @@ -137,7 +137,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &'s mut Scope, this_ptr: &'s mut Option<&mut Dynamic>, @@ -160,7 +160,7 @@ impl Engine { Expr::Variable(v, None, pos) if lib .iter() - .flat_map(|&m| m.iter_script_fn()) + .flat_map(|m| m.iter_script_fn()) .any(|(_, _, f, ..)| f == v.3.as_str()) => { let val: Dynamic = @@ -221,7 +221,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index db4c5332..935434ef 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -1,9 +1,9 @@ //! Global runtime state. use crate::{Dynamic, Engine, ImmutableString}; +use std::fmt; #[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::{fmt, marker::PhantomData}; /// Collection of globally-defined constants. #[cfg(not(feature = "no_module"))] @@ -22,7 +22,7 @@ pub type GlobalConstants = // Most usage will be looking up a particular key from the list and then getting the module that // corresponds to that key. #[derive(Clone)] -pub struct GlobalRuntimeState<'a> { +pub struct GlobalRuntimeState { /// Names of imported [modules][crate::Module]. #[cfg(not(feature = "no_module"))] imports: crate::StaticVec, @@ -70,11 +70,9 @@ pub struct GlobalRuntimeState<'a> { /// Debugging interface. #[cfg(feature = "debugging")] pub debugger: super::Debugger, - /// Take care of the lifetime parameter. - dummy: PhantomData<&'a ()>, } -impl GlobalRuntimeState<'_> { +impl GlobalRuntimeState { /// Create a new [`GlobalRuntimeState`] based on an [`Engine`]. #[inline(always)] #[must_use] @@ -112,8 +110,6 @@ impl GlobalRuntimeState<'_> { None => Dynamic::UNIT, }, ), - - dummy: PhantomData::default(), } } /// Get the length of the stack of globally-imported [modules][crate::Module]. @@ -319,7 +315,7 @@ impl GlobalRuntimeState<'_> { } #[cfg(not(feature = "no_module"))] -impl IntoIterator for GlobalRuntimeState<'_> { +impl IntoIterator for GlobalRuntimeState { type Item = (ImmutableString, crate::Shared); type IntoIter = std::iter::Rev< std::iter::Zip< @@ -334,7 +330,7 @@ impl IntoIterator for GlobalRuntimeState<'_> { } #[cfg(not(feature = "no_module"))] -impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> { +impl<'a> IntoIterator for &'a GlobalRuntimeState { type Item = (&'a ImmutableString, &'a crate::Shared); type IntoIter = std::iter::Rev< std::iter::Zip< @@ -350,7 +346,7 @@ impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> { #[cfg(not(feature = "no_module"))] impl, M: Into>> Extend<(K, M)> - for GlobalRuntimeState<'_> + for GlobalRuntimeState { #[inline] fn extend>(&mut self, iter: T) { @@ -361,7 +357,7 @@ impl, M: Into>> Extend<(K, } } -impl fmt::Debug for GlobalRuntimeState<'_> { +impl fmt::Debug for GlobalRuntimeState { #[cold] #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 5bf03093..9ccde0a0 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -8,7 +8,8 @@ use crate::ast::{ use crate::func::{get_builtin_op_assignment_fn, get_hasher}; use crate::types::dynamic::{AccessMode, Union}; use crate::{ - Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR, INT, + Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, + ERR, INT, }; use std::hash::{Hash, Hasher}; #[cfg(feature = "no_std")] @@ -27,7 +28,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -112,7 +113,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, op_info: &OpAssignment, target: &mut Target, @@ -196,7 +197,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -1018,7 +1019,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, statements: &[Stmt], diff --git a/src/func/call.rs b/src/func/call.rs index 817a8d7b..ff95e3a4 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -11,7 +11,7 @@ use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::tokenizer::{is_valid_function_name, Token}; use crate::{ calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, Module, - OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, ERR, + OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, ERR, }; #[cfg(feature = "no_std")] use hashbrown::hash_map::Entry; @@ -166,7 +166,7 @@ impl Engine { _global: &GlobalRuntimeState, caches: &'s mut Caches, local_entry: &'s mut Option, - lib: &[&Module], + lib: &[Shared], op_token: Option<&Token>, hash_base: u64, args: Option<&mut FnCallArgs>, @@ -193,8 +193,7 @@ impl Engine { loop { let func = lib .iter() - .copied() - .chain(self.global_modules.iter().map(|m| m.as_ref())) + .chain(self.global_modules.iter()) .find_map(|m| m.get_fn(hash).map(|f| (f, m.id_raw()))); #[cfg(not(feature = "no_module"))] @@ -323,7 +322,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, name: &str, op_token: Option<&Token>, @@ -538,7 +537,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, _scope: Option<&mut Scope>, fn_name: &str, @@ -705,7 +704,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -742,7 +741,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, fn_name: &str, mut hash: FnCallHashes, @@ -967,7 +966,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -1258,7 +1257,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -1442,7 +1441,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, script: &str, @@ -1487,7 +1486,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, diff --git a/src/func/mod.rs b/src/func/mod.rs index 6120764d..d5659854 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -22,6 +22,8 @@ pub use callable_function::CallableFunction; #[cfg(not(feature = "no_function"))] pub use func::Func; pub use hashing::{calc_fn_hash, calc_fn_hash_full, calc_var_hash, get_hasher, StraightHashMap}; +#[cfg(feature = "internals")] +pub use native::NativeCallContextStore; pub use native::{ locked_read, locked_write, shared_get_mut, shared_make_mut, shared_take, shared_take_or_clone, shared_try_take, FnAny, FnPlugin, IteratorFn, Locked, NativeCallContext, SendSync, Shared, diff --git a/src/func/script.rs b/src/func/script.rs index b80d74ae..06fa916a 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -4,7 +4,7 @@ use super::call::FnCallArgs; use crate::ast::ScriptFnDef; use crate::eval::{Caches, GlobalRuntimeState}; -use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, ERR}; +use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, Shared, ERR}; use std::mem; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -26,7 +26,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -127,8 +127,8 @@ impl Engine { lib } else { caches.push_fn_resolution_cache(); - lib_merged.push(&**fn_lib); - lib_merged.extend(lib.iter().copied()); + lib_merged.push(fn_lib.clone()); + lib_merged.extend(lib.iter().cloned()); &lib_merged }, Some(mem::replace(&mut global.constants, constants.clone())), @@ -230,7 +230,7 @@ impl Engine { &self, _global: Option<&GlobalRuntimeState>, caches: &mut Caches, - lib: &[&Module], + lib: &[Shared], hash_script: u64, ) -> bool { let cache = caches.fn_resolution_cache_mut(); diff --git a/src/optimizer.rs b/src/optimizer.rs index 4428c6da..aa204777 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -56,12 +56,12 @@ struct OptimizerState<'a> { /// An [`Engine`] instance for eager function evaluation. engine: &'a Engine, /// The global runtime state. - global: GlobalRuntimeState<'a>, + global: GlobalRuntimeState, /// Function resolution caches. - caches: Caches<'a>, + caches: Caches, /// [Module][crate::Module] containing script-defined functions. #[cfg(not(feature = "no_function"))] - lib: &'a [&'a crate::Module], + lib: &'a [crate::Shared], /// Optimization level. optimization_level: OptimizationLevel, } @@ -71,7 +71,7 @@ impl<'a> OptimizerState<'a> { #[inline(always)] pub fn new( engine: &'a Engine, - #[cfg(not(feature = "no_function"))] lib: &'a [&'a crate::Module], + #[cfg(not(feature = "no_function"))] lib: &'a [crate::Shared], optimization_level: OptimizationLevel, ) -> Self { Self { @@ -1189,7 +1189,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { => { // First search for script-defined functions (can override built-in) #[cfg(not(feature = "no_function"))] - let has_script_fn = !x.hashes.is_native_only() && state.lib.iter().find_map(|&m| m.get_script_fn(&x.name, x.args.len())).is_some(); + let has_script_fn = !x.hashes.is_native_only() && state.lib.iter().find_map(|m| m.get_script_fn(&x.name, x.args.len())).is_some(); #[cfg(feature = "no_function")] let has_script_fn = false; @@ -1263,7 +1263,7 @@ fn optimize_top_level( statements: StmtBlockContainer, engine: &Engine, scope: &Scope, - #[cfg(not(feature = "no_function"))] lib: &[&crate::Module], + #[cfg(not(feature = "no_function"))] lib: &[crate::Shared], optimization_level: OptimizationLevel, ) -> StmtBlockContainer { let mut statements = statements; @@ -1317,7 +1317,7 @@ pub fn optimize_into_ast( let mut statements = statements; #[cfg(not(feature = "no_function"))] - let lib = { + let lib: crate::Shared<_> = { let mut module = crate::Module::new(); if optimization_level != OptimizationLevel::None { @@ -1338,7 +1338,7 @@ pub fn optimize_into_ast( }); } - let lib2 = &[&lib2]; + let lib2 = &[lib2.into()]; for fn_def in functions { let mut fn_def = crate::func::shared_take_or_clone(fn_def); @@ -1356,7 +1356,7 @@ pub fn optimize_into_ast( } } - module + module.into() }; statements.shrink_to_fit(); @@ -1369,7 +1369,7 @@ pub fn optimize_into_ast( engine, scope, #[cfg(not(feature = "no_function"))] - &[&lib], + &[lib.clone()], optimization_level, ), }, diff --git a/src/parser.rs b/src/parser.rs index f0c3843f..ee107089 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -55,7 +55,7 @@ pub struct ParseState<'e> { /// External [scope][Scope] with constants. pub scope: &'e Scope<'e>, /// Global runtime state. - pub global: GlobalRuntimeState<'e>, + pub global: GlobalRuntimeState, /// Encapsulates a local stack with variable names to simulate an actual runtime scope. pub stack: Scope<'e>, /// Size of the local variables stack upon entry of the current block scope. diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 5938b7a6..810dd5a8 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -3,8 +3,8 @@ use crate::tokenizer::is_valid_function_name; use crate::types::dynamic::Variant; use crate::{ - Dynamic, Engine, FuncArgs, ImmutableString, Module, NativeCallContext, Position, RhaiError, - RhaiResult, RhaiResultOf, StaticVec, AST, ERR, + Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError, RhaiResult, + RhaiResultOf, StaticVec, AST, ERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -152,13 +152,16 @@ impl FnPtr { let lib = [ #[cfg(not(feature = "no_function"))] - _ast.as_ref(), + AsRef::>::as_ref(ast).clone(), ]; - let lib = if lib.first().map_or(true, |m: &&Module| m.is_empty()) { + let lib = if lib.first().map_or(true, |m| m.is_empty()) { &lib[0..0] } else { &lib }; + #[cfg(feature = "no_function")] + let lib = &[]; + #[allow(deprecated)] let ctx = NativeCallContext::new(engine, self.fn_name(), lib); From b4529b6a6400c7cb0976e4e8a457f0bf5511616f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 7 Nov 2022 16:19:10 +0800 Subject: [PATCH 2/9] Add storage API for NativeCallContext. --- CHANGELOG.md | 5 +++ src/func/native.rs | 84 ++++++++++++++++++++++++++++++++++++++++------ tests/modules.rs | 41 +++------------------- 3 files changed, 83 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f73ee408..3a606c13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,11 @@ New features * `Scope` is now serializable and deserializable via `serde`. +### Store and recreate `NativeCallContext` + +* A convenient API is added to store a `NativeCallContext` into a new `NativeCallContextStore` type. +* This allows a `NativeCallContext` to be stored and recreated later on. + ### Call native Rust functions in `NativeCallContext` * `NativeCallContext::call_native_fn` is added to call registered native Rust functions only. diff --git a/src/func/native.rs b/src/func/native.rs index 912f9d79..63823ecd 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -72,22 +72,51 @@ pub struct NativeCallContext<'a> { /// Function source, if any. source: Option<&'a str>, /// The current [`GlobalRuntimeState`], if any. - global: Option<&'a GlobalRuntimeState<'a>>, + global: Option<&'a GlobalRuntimeState>, /// The current stack of loaded [modules][Module]. - lib: &'a [&'a Module], + lib: &'a [Shared], /// [Position] of the function call. pos: Position, /// The current nesting level of function calls. level: usize, } +/// _(internals)_ Context of a native Rust function call. +/// Exported under the `internals` feature only. +#[cfg(feature = "internals")] +#[derive(Debug, Clone)] +pub struct NativeCallContextStore { + /// Name of function called. + pub fn_name: String, + /// Function source, if any. + pub source: Option, + /// The current [`GlobalRuntimeState`], if any. + pub global: GlobalRuntimeState, + /// The current stack of loaded [modules][Module]. + pub lib: StaticVec>, + /// [Position] of the function call. + pub pos: Position, + /// The current nesting level of function calls. + pub level: usize, +} + +#[cfg(feature = "internals")] +impl NativeCallContextStore { + /// Create a [`NativeCallContext`] from a [`NativeCallContextClone`]. + #[inline(always)] + #[must_use] + pub fn create_context<'a>(&'a self, engine: &'a Engine) -> NativeCallContext<'a> { + NativeCallContext::from_stored_data(engine, self) + } +} + impl<'a> From<( &'a Engine, &'a str, Option<&'a str>, - &'a GlobalRuntimeState<'a>, - &'a [&Module], + &'a GlobalRuntimeState, + &'a [Shared], Position, usize, )> for NativeCallContext<'a> @@ -99,7 +128,7 @@ impl<'a> &'a str, Option<&'a str>, &'a GlobalRuntimeState, - &'a [&Module], + &'a [Shared], Position, usize, ), @@ -116,9 +145,9 @@ impl<'a> } } -impl<'a> From<(&'a Engine, &'a str, &'a [&'a Module])> for NativeCallContext<'a> { +impl<'a> From<(&'a Engine, &'a str, &'a [Shared])> for NativeCallContext<'a> { #[inline(always)] - fn from(value: (&'a Engine, &'a str, &'a [&Module])) -> Self { + fn from(value: (&'a Engine, &'a str, &'a [Shared])) -> Self { Self { engine: value.0, fn_name: value.1, @@ -140,7 +169,7 @@ impl<'a> NativeCallContext<'a> { )] #[inline(always)] #[must_use] - pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self { + pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [Shared]) -> Self { Self { engine, fn_name, @@ -164,7 +193,7 @@ impl<'a> NativeCallContext<'a> { fn_name: &'a str, source: Option<&'a str>, global: &'a GlobalRuntimeState, - lib: &'a [&Module], + lib: &'a [Shared], pos: Position, level: usize, ) -> Self { @@ -178,6 +207,39 @@ impl<'a> NativeCallContext<'a> { level, } } + + /// _(internals)_ Create a [`NativeCallContext`] from a [`NativeCallContextClone`]. + /// Exported under the `internals` feature only. + #[cfg(feature = "internals")] + #[inline] + #[must_use] + pub fn from_stored_data(engine: &'a Engine, context: &'a NativeCallContextStore) -> Self { + Self { + engine, + fn_name: &context.fn_name, + source: context.source.as_ref().map(String::as_str), + global: Some(&context.global), + lib: &context.lib, + pos: context.pos, + level: context.level, + } + } + /// _(internals)_ Store this [`NativeCallContext`] into a [`NativeCallContextClone`]. + /// Exported under the `internals` feature only. + #[cfg(feature = "internals")] + #[inline] + #[must_use] + pub fn store_data(&self) -> NativeCallContextStore { + NativeCallContextStore { + fn_name: self.fn_name.to_string(), + source: self.source.map(|s| s.to_string()), + global: self.global.unwrap().clone(), + lib: self.lib.iter().cloned().collect(), + pos: self.pos, + level: self.level, + } + } + /// The current [`Engine`]. #[inline(always)] #[must_use] @@ -246,14 +308,14 @@ impl<'a> NativeCallContext<'a> { /// in reverse order (i.e. parent namespaces are iterated after child namespaces). #[inline] pub fn iter_namespaces(&self) -> impl Iterator { - self.lib.iter().copied() + self.lib.iter().map(|m| m.as_ref()) } /// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub const fn namespaces(&self) -> &[&Module] { + pub const fn namespaces(&self) -> &[Shared] { self.lib } /// Call a function inside the call context with the provided arguments. diff --git a/tests/modules.rs b/tests/modules.rs index f18d9aa7..babc862a 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -2,7 +2,7 @@ use rhai::{ module_resolvers::{DummyModuleResolver, StaticModuleResolver}, Dynamic, Engine, EvalAltResult, FnNamespace, FnPtr, ImmutableString, Module, NativeCallContext, - ParseError, ParseErrorType, Scope, Shared, INT, + ParseError, ParseErrorType, Scope, INT, }; #[test] @@ -546,44 +546,13 @@ fn test_module_context() -> Result<(), Box> { engine.register_fn( "calc", |context: NativeCallContext, fp: FnPtr| -> Result> { - // Store fields for later use let engine = context.engine(); - let fn_name = context.fn_name().to_string(); - let source = context.source().map(|s| s.to_string()); - let global = context.global_runtime_state().unwrap().clone(); - let pos = context.position(); - let call_level = context.call_level(); - // Store the paths of the stack of call modules up to this point - let modules_list: Vec = context - .iter_namespaces() - .map(|m| m.id().unwrap_or("testing")) - .filter(|id| !id.is_empty()) - .map(|id| id.to_string()) - .collect(); + // Store context for later use - requires the 'internals' feature + let context_data = context.store_data(); - // Recreate the 'NativeCallContext' - requires the 'internals' feature - let mut libraries = Vec::>::new(); - - for path in modules_list { - // Recreate the stack of call modules by resolving each path with - // the module resolver. - let module = engine.module_resolver().resolve(engine, None, &path, pos)?; - - libraries.push(module); - } - - let lib: Vec<&Module> = libraries.iter().map(|m| m.as_ref()).collect(); - - let new_context = NativeCallContext::new_with_all_fields( - engine, - &fn_name, - source.as_ref().map(String::as_str), - &global, - &lib, - pos, - call_level, - ); + // Recreate the 'NativeCallContext' + let new_context = context_data.create_context(engine); fp.call_within_context(&new_context, (41 as INT,)) }, From cba914db95bb6b5a6c176dab9a152a0186e5215a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 7 Nov 2022 16:34:25 +0800 Subject: [PATCH 3/9] Fix no_function build. --- src/api/run.rs | 6 +++--- src/types/fn_ptr.rs | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/api/run.rs b/src/api/run.rs index 96faee2e..34f1d106 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -122,12 +122,12 @@ impl Engine { let statements = ast.statements(); if !statements.is_empty() { - let lib = [ + let lib: &[crate::Shared] = &[ #[cfg(not(feature = "no_function"))] - AsRef::>::as_ref(ast).clone(), + AsRef::>::as_ref(ast).clone(), ]; let lib = if lib.first().map_or(true, |m| m.is_empty()) { - &lib[0..0] + &[][..] } else { &lib }; diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 810dd5a8..b7ac5468 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -150,17 +150,15 @@ impl FnPtr { let mut arg_values = crate::StaticVec::new_const(); args.parse(&mut arg_values); - let lib = [ + let lib: &[crate::Shared] = &[ #[cfg(not(feature = "no_function"))] - AsRef::>::as_ref(ast).clone(), + AsRef::>::as_ref(ast).clone(), ]; let lib = if lib.first().map_or(true, |m| m.is_empty()) { - &lib[0..0] + &[][..] } else { &lib }; - #[cfg(feature = "no_function")] - let lib = &[]; #[allow(deprecated)] let ctx = NativeCallContext::new(engine, self.fn_name(), lib); From f4e290135368eecf6c1da08e41c4a47665ad34a8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 8 Nov 2022 11:52:46 +0800 Subject: [PATCH 4/9] Use RestoreOnDrop. --- src/api/call_fn.rs | 14 +- src/api/eval.rs | 22 +- src/eval/chaining.rs | 93 ++--- src/eval/expr.rs | 99 ++--- src/eval/stmt.rs | 816 ++++++++++++++++++------------------------ src/func/call.rs | 145 ++++---- src/types/interner.rs | 69 ++-- src/types/mod.rs | 2 + src/types/restore.rs | 57 +++ 9 files changed, 618 insertions(+), 699 deletions(-) create mode 100644 src/types/restore.rs diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 32510f60..2057d9b7 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -3,6 +3,7 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::types::dynamic::Variant; +use crate::types::RestoreOnDrop; use crate::{ reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, Shared, StaticVec, AST, ERR, @@ -258,6 +259,10 @@ impl Engine { &mut global.embedded_module_resolver, ast.resolver().cloned(), ); + #[cfg(not(feature = "no_module"))] + let global = &mut *RestoreOnDrop::new(global, move |g| { + g.embedded_module_resolver = orig_embedded_module_resolver + }); let result = if eval_ast && !statements.is_empty() { let r = self.eval_global_statements(global, caches, lib, 0, scope, statements); @@ -293,14 +298,7 @@ impl Engine { } else { Err(ERR::ErrorFunctionNotFound(name.into(), Position::NONE).into()) } - }); - - #[cfg(not(feature = "no_module"))] - { - global.embedded_module_resolver = orig_embedded_module_resolver; - } - - let result = result?; + })?; #[cfg(feature = "debugging")] if self.debugger.is_some() { diff --git a/src/api/eval.rs b/src/api/eval.rs index 02f21831..294bf89a 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -3,6 +3,7 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::parser::ParseState; use crate::types::dynamic::Variant; +use crate::types::RestoreOnDrop; use crate::{ Dynamic, Engine, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR, }; @@ -225,6 +226,10 @@ impl Engine { &mut global.embedded_module_resolver, ast.resolver().cloned(), ); + #[cfg(not(feature = "no_module"))] + let global = &mut *RestoreOnDrop::new(global, move |g| { + g.embedded_module_resolver = orig_embedded_module_resolver + }); let statements = ast.statements(); @@ -232,23 +237,12 @@ impl Engine { return Ok(Dynamic::UNIT); } - let mut _lib = &[ + let lib = &[ #[cfg(not(feature = "no_function"))] AsRef::>::as_ref(ast).clone(), - ][..]; - #[cfg(not(feature = "no_function"))] - if !ast.has_functions() { - _lib = &[]; - } + ]; - let result = self.eval_global_statements(global, caches, _lib, level, scope, statements); - - #[cfg(not(feature = "no_module"))] - { - global.embedded_module_resolver = orig_embedded_module_resolver; - } - - result + self.eval_global_statements(global, caches, lib, level, scope, statements) } /// _(internals)_ Evaluate a list of statements with no `this` pointer. /// Exported under the `internals` feature only. diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index cf51a465..7d7560fc 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -4,6 +4,7 @@ use super::{Caches, GlobalRuntimeState, Target}; use crate::ast::{ASTFlags, Expr, OpAssignment}; use crate::types::dynamic::Union; +use crate::types::RestoreOnDrop; use crate::{ Dynamic, Engine, FnArgsVec, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, ERR, }; @@ -200,29 +201,30 @@ impl Engine { // xxx.fn_name(arg_expr_list) Expr::MethodCall(x, pos) if !x.is_qualified() && new_val.is_none() => { #[cfg(feature = "debugging")] - let reset_debugger = self.run_debugger_with_reset( + let reset = self.run_debugger_with_reset( global, caches, lib, level, scope, this_ptr, rhs, )?; + #[cfg(feature = "debugging")] + let global = &mut *RestoreOnDrop::new(global, move |g| { + g.debugger.reset_status(reset) + }); let crate::ast::FnCallExpr { name, hashes, args, .. } = &**x; + // Truncate the index values upon exit let offset = idx_values.len() - args.len(); + let idx_values = + &mut *RestoreOnDrop::new(idx_values, move |v| v.truncate(offset)); + let call_args = &mut idx_values[offset..]; let pos1 = args.get(0).map_or(Position::NONE, Expr::position); - let result = self.make_method_call( + self.make_method_call( global, caches, lib, level, name, *hashes, target, call_args, pos1, *pos, - ); - - idx_values.truncate(offset); - - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - result + ) } // xxx.fn_name(...) = ??? Expr::MethodCall(..) if new_val.is_some() => { @@ -378,29 +380,33 @@ impl Engine { // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr Expr::MethodCall(ref x, pos) if !x.is_qualified() => { #[cfg(feature = "debugging")] - let reset_debugger = self.run_debugger_with_reset( + let reset = self.run_debugger_with_reset( global, caches, lib, level, scope, this_ptr, _node, )?; + #[cfg(feature = "debugging")] + let global = &mut *RestoreOnDrop::new(global, move |g| { + g.debugger.reset_status(reset) + }); let crate::ast::FnCallExpr { name, hashes, args, .. } = &**x; + // Truncate the index values upon exit let offset = idx_values.len() - args.len(); + let idx_values = &mut *RestoreOnDrop::new(idx_values, move |v| { + v.truncate(offset) + }); + let call_args = &mut idx_values[offset..]; let pos1 = args.get(0).map_or(Position::NONE, Expr::position); - let result = self.make_method_call( + self.make_method_call( global, caches, lib, level, name, *hashes, target, call_args, pos1, pos, - ); - - idx_values.truncate(offset); - - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - result?.0.into() + )? + .0 + .into() } // {xxx:map}.module::fn_name(...) - syntax error Expr::MethodCall(..) => unreachable!( @@ -504,32 +510,39 @@ impl Engine { } // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr Expr::MethodCall(ref f, pos) if !f.is_qualified() => { - #[cfg(feature = "debugging")] - let reset_debugger = self.run_debugger_with_reset( - global, caches, lib, level, scope, this_ptr, _node, - )?; + let val = { + #[cfg(feature = "debugging")] + let reset = self.run_debugger_with_reset( + global, caches, lib, level, scope, this_ptr, _node, + )?; + #[cfg(feature = "debugging")] + let global = &mut *RestoreOnDrop::new(global, move |g| { + g.debugger.reset_status(reset) + }); - let crate::ast::FnCallExpr { - name, hashes, args, .. - } = &**f; - let rhs_chain = rhs.into(); + let crate::ast::FnCallExpr { + name, hashes, args, .. + } = &**f; - let offset = idx_values.len() - args.len(); - let call_args = &mut idx_values[offset..]; - let pos1 = args.get(0).map_or(Position::NONE, Expr::position); + // Truncate the index values upon exit + let offset = idx_values.len() - args.len(); + let idx_values = + &mut *RestoreOnDrop::new(idx_values, move |v| { + v.truncate(offset) + }); - let result = self.make_method_call( - global, caches, lib, level, name, *hashes, target, call_args, - pos1, pos, - ); + let call_args = &mut idx_values[offset..]; + let pos1 = args.get(0).map_or(Position::NONE, Expr::position); - idx_values.truncate(offset); + self.make_method_call( + global, caches, lib, level, name, *hashes, target, + call_args, pos1, pos, + )? + .0 + }; - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - let (val, _) = &mut result?; let val = &mut val.into(); + let rhs_chain = rhs.into(); self.eval_dot_index_chain_helper( global, caches, lib, level, this_ptr, val, root, rhs, *options, diff --git a/src/eval/expr.rs b/src/eval/expr.rs index afa47ce4..269ab992 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -4,6 +4,7 @@ use super::{Caches, EvalContext, GlobalRuntimeState, Target}; use crate::ast::{Expr, OpAssignment}; use crate::engine::{KEYWORD_THIS, OP_CONCAT}; use crate::types::dynamic::AccessMode; +use crate::types::RestoreOnDrop; use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, ERR}; use std::num::NonZeroUsize; #[cfg(feature = "no_std")] @@ -234,18 +235,14 @@ impl Engine { // binary operators are also function calls. if let Expr::FnCall(x, pos) = expr { #[cfg(feature = "debugging")] - let reset_debugger = + let reset = self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; + #[cfg(feature = "debugging")] + let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); self.track_operation(global, expr.position())?; - let result = - self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); - - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - return result; + return self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); } // Then variable access. @@ -269,12 +266,14 @@ impl Engine { } #[cfg(feature = "debugging")] - let reset_debugger = + let reset = self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; + #[cfg(feature = "debugging")] + let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); self.track_operation(global, expr.position())?; - let result = match expr { + match expr { // Constants Expr::DynamicConstant(x, ..) => Ok(x.as_ref().clone()), Expr::IntegerConstant(x, ..) => Ok((*x).into()), @@ -374,60 +373,33 @@ impl Engine { .map(Into::into) } - Expr::And(x, ..) => { - let lhs = self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::(typ, x.lhs.position()) - }) - }); + Expr::And(x, ..) => Ok((self + .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)? + .as_bool() + .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? + && self + .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs)? + .as_bool() + .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) + .into()), - match lhs { - Ok(true) => self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs) - .and_then(|v| { - v.as_bool() - .map_err(|typ| { - self.make_type_mismatch_err::(typ, x.rhs.position()) - }) - .map(Into::into) - }), - _ => lhs.map(Into::into), - } - } - - Expr::Or(x, ..) => { - let lhs = self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::(typ, x.lhs.position()) - }) - }); - - match lhs { - Ok(false) => self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs) - .and_then(|v| { - v.as_bool() - .map_err(|typ| { - self.make_type_mismatch_err::(typ, x.rhs.position()) - }) - .map(Into::into) - }), - _ => lhs.map(Into::into), - } - } + Expr::Or(x, ..) => Ok((self + .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)? + .as_bool() + .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? + || self + .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs)? + .as_bool() + .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) + .into()), Expr::Coalesce(x, ..) => { - let lhs = self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs); + let value = self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)?; - match lhs { - Ok(value) if value.is::<()>() => { - self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs) - } - _ => lhs, + if value.is::<()>() { + self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs) + } else { + Ok(value) } } @@ -467,11 +439,6 @@ impl Engine { .eval_dot_index_chain(global, caches, lib, level, scope, this_ptr, expr, &mut None), _ => unreachable!("expression cannot be evaluated: {:?}", expr), - }; - - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - result + } } } diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 9ccde0a0..2841ecba 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -7,6 +7,7 @@ use crate::ast::{ }; use crate::func::{get_builtin_op_assignment_fn, get_hasher}; use crate::types::dynamic::{AccessMode, Union}; +use crate::types::RestoreOnDrop; use crate::{ Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, ERR, INT, @@ -39,17 +40,39 @@ impl Engine { return Ok(Dynamic::UNIT); } - let orig_always_search_scope = global.always_search_scope; + // Restore scope at end of block if necessary let orig_scope_len = scope.len(); + let scope = &mut *RestoreOnDrop::new_if(restore_orig_state, scope, move |s| { + s.rewind(orig_scope_len); + }); + + // Restore global state at end of block if necessary + let orig_always_search_scope = global.always_search_scope; #[cfg(not(feature = "no_module"))] let orig_imports_len = global.num_imports(); - let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len(); if restore_orig_state { global.scope_level += 1; } - let result = statements.iter().try_fold(Dynamic::UNIT, |_, stmt| { + let global = &mut *RestoreOnDrop::new_if(restore_orig_state, global, move |g| { + g.scope_level -= 1; + #[cfg(not(feature = "no_module"))] + g.truncate_imports(orig_imports_len); + + // The impact of new local variables goes away at the end of a block + // because any new variables introduced will go out of scope + g.always_search_scope = orig_always_search_scope; + }); + + // Pop new function resolution caches at end of block + let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len(); + let caches = &mut *RestoreOnDrop::new(caches, move |c| { + c.rewind_fn_resolution_caches(orig_fn_resolution_caches_len) + }); + + // Run the statements + statements.iter().try_fold(Dynamic::UNIT, |_, stmt| { #[cfg(not(feature = "no_module"))] let imports_len = global.num_imports(); @@ -89,23 +112,7 @@ impl Engine { } Ok(result) - }); - - // If imports list is modified, pop the functions lookup cache - caches.rewind_fn_resolution_caches(orig_fn_resolution_caches_len); - - if restore_orig_state { - scope.rewind(orig_scope_len); - global.scope_level -= 1; - #[cfg(not(feature = "no_module"))] - global.truncate_imports(orig_imports_len); - - // The impact of new local variables goes away at the end of a block - // because any new variables introduced will go out of scope - global.always_search_scope = orig_always_search_scope; - } - - result + }) } /// Evaluate an op-assignment statement. @@ -205,8 +212,10 @@ impl Engine { rewind_scope: bool, ) -> RhaiResult { #[cfg(feature = "debugging")] - let reset_debugger = + let reset = self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, stmt)?; + #[cfg(feature = "debugging")] + let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); // Coded this way for better branch prediction. // Popular branches are lifted out of the `match` statement into their own branches. @@ -215,13 +224,7 @@ impl Engine { if let Stmt::FnCall(x, pos) = stmt { self.track_operation(global, stmt.position())?; - let result = - self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); - - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - return result; + return self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); } // Then assignments. @@ -232,108 +235,85 @@ impl Engine { self.track_operation(global, stmt.position())?; - let result = if let Expr::Variable(x, ..) = lhs { - let rhs_result = self - .eval_expr(global, caches, lib, level, scope, this_ptr, rhs) - .map(Dynamic::flatten); + if let Expr::Variable(x, ..) = lhs { + let rhs_val = self + .eval_expr(global, caches, lib, level, scope, this_ptr, rhs)? + .flatten(); - if let Ok(rhs_val) = rhs_result { - let search_result = - self.search_namespace(global, caches, lib, level, scope, this_ptr, lhs); + let search_val = + self.search_namespace(global, caches, lib, level, scope, this_ptr, lhs)?; - if let Ok(search_val) = search_result { - let (mut lhs_ptr, pos) = search_val; + let (mut lhs_ptr, pos) = search_val; - let var_name = x.3.as_str(); + let var_name = x.3.as_str(); - #[cfg(not(feature = "no_closure"))] - // Also handle case where target is a `Dynamic` shared value - // (returned by a variable resolver, for example) - let is_temp_result = !lhs_ptr.is_ref() && !lhs_ptr.is_shared(); - #[cfg(feature = "no_closure")] - let is_temp_result = !lhs_ptr.is_ref(); + #[cfg(not(feature = "no_closure"))] + // Also handle case where target is a `Dynamic` shared value + // (returned by a variable resolver, for example) + let is_temp_result = !lhs_ptr.is_ref() && !lhs_ptr.is_shared(); + #[cfg(feature = "no_closure")] + let is_temp_result = !lhs_ptr.is_ref(); - // Cannot assign to temp result from expression - if is_temp_result { - return Err( - ERR::ErrorAssignmentToConstant(var_name.to_string(), pos).into() - ); - } - - self.track_operation(global, pos)?; - - let root = (var_name, pos); - let lhs_ptr = &mut lhs_ptr; - - self.eval_op_assignment( - global, caches, lib, level, op_info, lhs_ptr, root, rhs_val, - ) - .map(|_| Dynamic::UNIT) - } else { - search_result.map(|_| Dynamic::UNIT) - } - } else { - rhs_result + // Cannot assign to temp result from expression + if is_temp_result { + return Err(ERR::ErrorAssignmentToConstant(var_name.to_string(), pos).into()); } + + self.track_operation(global, pos)?; + + let root = (var_name, pos); + let lhs_ptr = &mut lhs_ptr; + + return self + .eval_op_assignment(global, caches, lib, level, op_info, lhs_ptr, root, rhs_val) + .map(|_| Dynamic::UNIT); } else { let (op_info, BinaryExpr { lhs, rhs }) = &**x; - let rhs_result = self.eval_expr(global, caches, lib, level, scope, this_ptr, rhs); + let rhs_val = self.eval_expr(global, caches, lib, level, scope, this_ptr, rhs)?; - if let Ok(rhs_val) = rhs_result { - // Check if the result is a string. If so, intern it. - #[cfg(not(feature = "no_closure"))] - let is_string = !rhs_val.is_shared() && rhs_val.is::(); - #[cfg(feature = "no_closure")] - let is_string = rhs_val.is::(); + // Check if the result is a string. If so, intern it. + #[cfg(not(feature = "no_closure"))] + let is_string = !rhs_val.is_shared() && rhs_val.is::(); + #[cfg(feature = "no_closure")] + let is_string = rhs_val.is::(); - let rhs_val = if is_string { - self.get_interned_string( - rhs_val.into_immutable_string().expect("`ImmutableString`"), - ) - .into() - } else { - rhs_val.flatten() - }; - - let _new_val = &mut Some((rhs_val, op_info)); - - // Must be either `var[index] op= val` or `var.prop op= val` - match lhs { - // name op= rhs (handled above) - Expr::Variable(..) => { - unreachable!("Expr::Variable case is already handled") - } - // idx_lhs[idx_expr] op= rhs - #[cfg(not(feature = "no_index"))] - Expr::Index(..) => self - .eval_dot_index_chain( - global, caches, lib, level, scope, this_ptr, lhs, _new_val, - ) - .map(|_| Dynamic::UNIT), - // dot_lhs.dot_rhs op= rhs - #[cfg(not(feature = "no_object"))] - Expr::Dot(..) => self - .eval_dot_index_chain( - global, caches, lib, level, scope, this_ptr, lhs, _new_val, - ) - .map(|_| Dynamic::UNIT), - _ => unreachable!("cannot assign to expression: {:?}", lhs), - } + let rhs_val = if is_string { + self.get_interned_string( + rhs_val.into_immutable_string().expect("`ImmutableString`"), + ) + .into() } else { - rhs_result + rhs_val.flatten() + }; + + let _new_val = &mut Some((rhs_val, op_info)); + + // Must be either `var[index] op= val` or `var.prop op= val` + return match lhs { + // name op= rhs (handled above) + Expr::Variable(..) => { + unreachable!("Expr::Variable case is already handled") + } + // idx_lhs[idx_expr] op= rhs + #[cfg(not(feature = "no_index"))] + Expr::Index(..) => self.eval_dot_index_chain( + global, caches, lib, level, scope, this_ptr, lhs, _new_val, + ), + // dot_lhs.dot_rhs op= rhs + #[cfg(not(feature = "no_object"))] + Expr::Dot(..) => self.eval_dot_index_chain( + global, caches, lib, level, scope, this_ptr, lhs, _new_val, + ), + _ => unreachable!("cannot assign to expression: {:?}", lhs), } - }; - - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - return result; + .map(|_| Dynamic::UNIT); + } } self.track_operation(global, stmt.position())?; - let result = match stmt { + match stmt { // No-op Stmt::Noop(..) => Ok(Dynamic::UNIT), @@ -385,100 +365,69 @@ impl Engine { }, ) = &**x; - let value_result = - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr); + let mut result = None; - if let Ok(value) = value_result { - let expr_result = if value.is_hashable() { - let hasher = &mut get_hasher(); - value.hash(hasher); - let hash = hasher.finish(); + let value = self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)?; - // First check hashes - if let Some(case_blocks_list) = cases.get(&hash) { - assert!(!case_blocks_list.is_empty()); + if value.is_hashable() { + let hasher = &mut get_hasher(); + value.hash(hasher); + let hash = hasher.finish(); - let mut result = Ok(None); + // First check hashes + if let Some(case_blocks_list) = cases.get(&hash) { + assert!(!case_blocks_list.is_empty()); - for &index in case_blocks_list { - let block = &expressions[index]; + for &index in case_blocks_list { + let block = &expressions[index]; - let cond_result = match block.condition { - Expr::BoolConstant(b, ..) => Ok(b), - ref c => self - .eval_expr(global, caches, lib, level, scope, this_ptr, c) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::( - typ, - c.position(), - ) - }) - }), - }; + let cond_result = match block.condition { + Expr::BoolConstant(b, ..) => b, + ref c => self + .eval_expr(global, caches, lib, level, scope, this_ptr, c)? + .as_bool() + .map_err(|typ| { + self.make_type_mismatch_err::(typ, c.position()) + })?, + }; - match cond_result { - Ok(true) => result = Ok(Some(&block.expr)), - Ok(false) => continue, - _ => result = cond_result.map(|_| None), - } + if cond_result { + result = Some(&block.expr); break; } - - result - } else if value.is::() && !ranges.is_empty() { - // Then check integer ranges - let value = value.as_int().expect("`INT`"); - let mut result = Ok(None); - - for r in ranges.iter().filter(|r| r.contains(value)) { - let block = &expressions[r.index()]; - - let cond_result = match block.condition { - Expr::BoolConstant(b, ..) => Ok(b), - ref c => self - .eval_expr(global, caches, lib, level, scope, this_ptr, c) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::( - typ, - c.position(), - ) - }) - }), - }; - - match cond_result { - Ok(true) => result = Ok(Some(&block.expr)), - Ok(false) => continue, - _ => result = cond_result.map(|_| None), - } - break; - } - - result - } else { - // Nothing matches - Ok(None) } - } else { - // Non-hashable - Ok(None) - }; + } else if value.is::() && !ranges.is_empty() { + // Then check integer ranges + let value = value.as_int().expect("`INT`"); - if let Ok(Some(expr)) = expr_result { - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) - } else if let Ok(None) = expr_result { - // Default match clause - def_case.as_ref().map_or(Ok(Dynamic::UNIT), |&index| { - let def_expr = &expressions[index].expr; - self.eval_expr(global, caches, lib, level, scope, this_ptr, def_expr) - }) - } else { - expr_result.map(|_| Dynamic::UNIT) + for r in ranges.iter().filter(|r| r.contains(value)) { + let block = &expressions[r.index()]; + + let cond_result = match block.condition { + Expr::BoolConstant(b, ..) => b, + ref c => self + .eval_expr(global, caches, lib, level, scope, this_ptr, c)? + .as_bool() + .map_err(|typ| { + self.make_type_mismatch_err::(typ, c.position()) + })?, + }; + + if cond_result { + result = Some(&block.expr); + break; + } + } } + } + + if let Some(expr) = result { + self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) } else { - value_result + def_case.as_ref().map_or(Ok(Dynamic::UNIT), |&index| { + let def_expr = &expressions[index].expr; + self.eval_expr(global, caches, lib, level, scope, this_ptr, def_expr) + }) } } @@ -512,29 +461,24 @@ impl Engine { loop { let condition = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::(typ, expr.position()) - }) - }); + .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .as_bool() + .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; - match condition { - Ok(false) => break Ok(Dynamic::UNIT), - Ok(true) if body.is_empty() => (), - Ok(true) => { - match self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, body, true, - ) { - Ok(_) => (), - Err(err) => match *err { - ERR::LoopBreak(false, ..) => (), - ERR::LoopBreak(true, value, ..) => break Ok(value), - _ => break Err(err), - }, + if !condition { + break Ok(Dynamic::UNIT); + } + + if !body.is_empty() { + if let Err(err) = self.eval_stmt_block( + global, caches, lib, level, scope, this_ptr, body, true, + ) { + match *err { + ERR::LoopBreak(false, ..) => (), + ERR::LoopBreak(true, value, ..) => break Ok(value), + _ => break Err(err), } } - err => break err.map(|_| Dynamic::UNIT), } } } @@ -546,30 +490,24 @@ impl Engine { loop { if !body.is_empty() { - match self.eval_stmt_block( + if let Err(err) = self.eval_stmt_block( global, caches, lib, level, scope, this_ptr, body, true, ) { - Ok(_) => (), - Err(err) => match *err { + match *err { ERR::LoopBreak(false, ..) => continue, ERR::LoopBreak(true, value, ..) => break Ok(value), _ => break Err(err), - }, + } } } let condition = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::(typ, expr.position()) - }) - }); + .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .as_bool() + .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; - match condition { - Ok(condition) if condition ^ is_while => break Ok(Dynamic::UNIT), - Ok(_) => (), - err => break err.map(|_| Dynamic::UNIT), + if condition ^ is_while { + break Ok(Dynamic::UNIT); } } } @@ -578,102 +516,99 @@ impl Engine { Stmt::For(x, ..) => { let (var_name, counter, expr, statements) = &**x; - let iter_result = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) - .map(Dynamic::flatten); + let iter_obj = self + .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .flatten(); - if let Ok(iter_obj) = iter_result { - let iter_type = iter_obj.type_id(); + let iter_type = iter_obj.type_id(); - // lib should only contain scripts, so technically they cannot have iterators + // lib should only contain scripts, so technically they cannot have iterators - // Search order: - // 1) Global namespace - functions registered via Engine::register_XXX - // 2) Global modules - packages - // 3) Imported modules - functions marked with global namespace - // 4) Global sub-modules - functions marked with global namespace - let func = self - .global_modules - .iter() - .find_map(|m| m.get_iter(iter_type)); + // Search order: + // 1) Global namespace - functions registered via Engine::register_XXX + // 2) Global modules - packages + // 3) Imported modules - functions marked with global namespace + // 4) Global sub-modules - functions marked with global namespace + let func = self + .global_modules + .iter() + .find_map(|m| m.get_iter(iter_type)); - #[cfg(not(feature = "no_module"))] - let func = func.or_else(|| global.get_iter(iter_type)).or_else(|| { - self.global_sub_modules - .values() - .find_map(|m| m.get_qualified_iter(iter_type)) + #[cfg(not(feature = "no_module"))] + let func = func.or_else(|| global.get_iter(iter_type)).or_else(|| { + self.global_sub_modules + .values() + .find_map(|m| m.get_qualified_iter(iter_type)) + }); + + if let Some(func) = func { + // Restore scope at end of statement + let orig_scope_len = scope.len(); + let scope = &mut *RestoreOnDrop::new(scope, move |s| { + s.rewind(orig_scope_len); }); - if let Some(func) = func { - // Add the loop variables - let orig_scope_len = scope.len(); - let counter_index = if counter.is_empty() { - usize::MAX - } else { - scope.push(counter.name.clone(), 0 as INT); - scope.len() - 1 - }; - - scope.push(var_name.name.clone(), ()); - let index = scope.len() - 1; - - let loop_result = func(iter_obj) - .enumerate() - .try_fold(Dynamic::UNIT, |_, (x, iter_value)| { - // Increment counter - if counter_index < usize::MAX { - // As the variable increments from 0, this should always work - // since any overflow will first be caught below. - let index_value = x as INT; - - #[cfg(not(feature = "unchecked"))] - if index_value > crate::MAX_USIZE_INT { - return Err(ERR::ErrorArithmetic( - format!("for-loop counter overflow: {x}"), - counter.pos, - ) - .into()); - } - - *scope.get_mut_by_index(counter_index).write_lock().unwrap() = - Dynamic::from_int(index_value); - } - - let value = match iter_value { - Ok(v) => v.flatten(), - Err(err) => return Err(err.fill_position(expr.position())), - }; - - *scope.get_mut_by_index(index).write_lock().unwrap() = value; - - self.track_operation(global, statements.position())?; - - if statements.is_empty() { - return Ok(Dynamic::UNIT); - } - - self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, statements, true, - ) - .map(|_| Dynamic::UNIT) - .or_else(|err| match *err { - ERR::LoopBreak(false, ..) => Ok(Dynamic::UNIT), - _ => Err(err), - }) - }) - .or_else(|err| match *err { - ERR::LoopBreak(true, value, ..) => Ok(value), - _ => Err(err), - }); - - scope.rewind(orig_scope_len); - - loop_result + // Add the loop variables + let counter_index = if counter.is_empty() { + usize::MAX } else { - Err(ERR::ErrorFor(expr.start_position()).into()) - } + scope.push(counter.name.clone(), 0 as INT); + scope.len() - 1 + }; + + scope.push(var_name.name.clone(), ()); + let index = scope.len() - 1; + + func(iter_obj) + .enumerate() + .try_fold(Dynamic::UNIT, |_, (x, iter_value)| { + // Increment counter + if counter_index < usize::MAX { + // As the variable increments from 0, this should always work + // since any overflow will first be caught below. + let index_value = x as INT; + + #[cfg(not(feature = "unchecked"))] + if index_value > crate::MAX_USIZE_INT { + return Err(ERR::ErrorArithmetic( + format!("for-loop counter overflow: {x}"), + counter.pos, + ) + .into()); + } + + *scope.get_mut_by_index(counter_index).write_lock().unwrap() = + Dynamic::from_int(index_value); + } + + let value = match iter_value { + Ok(v) => v.flatten(), + Err(err) => return Err(err.fill_position(expr.position())), + }; + + *scope.get_mut_by_index(index).write_lock().unwrap() = value; + + self.track_operation(global, statements.position())?; + + if statements.is_empty() { + return Ok(Dynamic::UNIT); + } + + self.eval_stmt_block( + global, caches, lib, level, scope, this_ptr, statements, true, + ) + .map(|_| Dynamic::UNIT) + .or_else(|err| match *err { + ERR::LoopBreak(false, ..) => Ok(Dynamic::UNIT), + _ => Err(err), + }) + }) + .or_else(|err| match *err { + ERR::LoopBreak(true, value, ..) => Ok(value), + _ => Err(err), + }) } else { - iter_result + Err(ERR::ErrorFor(expr.start_position()).into()) } } @@ -700,12 +635,12 @@ impl Engine { catch_block, } = &**x; - let result = self + match self .eval_stmt_block(global, caches, lib, level, scope, this_ptr, try_block, true) - .map(|_| Dynamic::UNIT); - - match result { - Ok(_) => result, + { + Ok(_) => self.eval_stmt_block( + global, caches, lib, level, scope, this_ptr, try_block, true, + ), Err(err) if err.is_pseudo_error() => Err(err), Err(err) if !err.is_catchable() => Err(err), Err(mut err) => { @@ -744,13 +679,18 @@ impl Engine { } }; + // Restore scope at end of block let orig_scope_len = scope.len(); + let scope = + &mut *RestoreOnDrop::new_if(!catch_var.is_empty(), scope, move |s| { + s.rewind(orig_scope_len); + }); if !catch_var.is_empty() { scope.push(catch_var.clone(), err_value); } - let result = self.eval_stmt_block( + self.eval_stmt_block( global, caches, lib, @@ -759,21 +699,16 @@ impl Engine { this_ptr, catch_block, true, - ); - - scope.rewind(orig_scope_len); - - match result { - Ok(_) => Ok(Dynamic::UNIT), - Err(result_err) => match *result_err { - // Re-throw exception - ERR::ErrorRuntime(Dynamic(Union::Unit(..)), pos) => { - err.set_position(pos); - Err(err) - } - _ => Err(result_err), - }, - } + ) + .map(|_| Dynamic::UNIT) + .map_err(|result_err| match *result_err { + // Re-throw exception + ERR::ErrorRuntime(Dynamic(Union::Unit(..)), pos) => { + err.set_position(pos); + err + } + _ => result_err, + }) } } } @@ -812,7 +747,7 @@ impl Engine { let export = options.contains(ASTFlags::EXPORTED); // Check variable definition filter - let result = if let Some(ref filter) = self.def_var_filter { + if let Some(ref filter) = self.def_var_filter { let will_shadow = scope.contains(var_name); let nesting_level = global.scope_level; let is_const = access == AccessMode::ReadOnly; @@ -824,74 +759,56 @@ impl Engine { }; let context = EvalContext::new(self, global, None, lib, level, scope, this_ptr); - match filter(true, info, context) { - Ok(true) => None, - Ok(false) => { - Some(Err( - ERR::ErrorForbiddenVariable(var_name.to_string(), *pos).into() - )) - } - err @ Err(_) => Some(err), + if !filter(true, info, context)? { + return Err(ERR::ErrorForbiddenVariable(var_name.to_string(), *pos).into()); } + } + + // Evaluate initial value + let mut value = self + .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .flatten(); + + let _alias = if !rewind_scope { + // Put global constants into global module + #[cfg(not(feature = "no_function"))] + #[cfg(not(feature = "no_module"))] + if global.scope_level == 0 + && access == AccessMode::ReadOnly + && lib.iter().any(|m| !m.is_empty()) + { + crate::func::locked_write(global.constants.get_or_insert_with(|| { + crate::Shared::new( + crate::Locked::new(std::collections::BTreeMap::new()), + ) + })) + .insert(var_name.name.clone(), value.clone()); + } + + if export { + Some(var_name) + } else { + None + } + } else if export { + unreachable!("exported variable not on global level"); } else { None }; - if let Some(result) = result { - result.map(|_| Dynamic::UNIT) + if let Some(index) = index { + value.set_access_mode(access); + *scope.get_mut_by_index(scope.len() - index.get()) = value; } else { - // Evaluate initial value - let value_result = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) - .map(Dynamic::flatten); - - if let Ok(mut value) = value_result { - let _alias = if !rewind_scope { - // Put global constants into global module - #[cfg(not(feature = "no_function"))] - #[cfg(not(feature = "no_module"))] - if global.scope_level == 0 - && access == AccessMode::ReadOnly - && lib.iter().any(|m| !m.is_empty()) - { - crate::func::locked_write(global.constants.get_or_insert_with( - || { - crate::Shared::new(crate::Locked::new( - std::collections::BTreeMap::new(), - )) - }, - )) - .insert(var_name.name.clone(), value.clone()); - } - - if export { - Some(var_name) - } else { - None - } - } else if export { - unreachable!("exported variable not on global level"); - } else { - None - }; - - if let Some(index) = index { - value.set_access_mode(access); - *scope.get_mut_by_index(scope.len() - index.get()) = value; - } else { - scope.push_entry(var_name.name.clone(), access, value); - } - - #[cfg(not(feature = "no_module"))] - if let Some(alias) = _alias { - scope.add_alias_by_index(scope.len() - 1, alias.name.as_str().into()); - } - - Ok(Dynamic::UNIT) - } else { - value_result - } + scope.push_entry(var_name.name.clone(), access, value); } + + #[cfg(not(feature = "no_module"))] + if let Some(alias) = _alias { + scope.add_alias_by_index(scope.len() - 1, alias.name.as_str().into()); + } + + Ok(Dynamic::UNIT) } // Import statement @@ -904,66 +821,52 @@ impl Engine { return Err(ERR::ErrorTooManyModules(*_pos).into()); } - let path_result = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) - .and_then(|v| { - let typ = v.type_name(); - v.try_cast::().ok_or_else(|| { - self.make_type_mismatch_err::( - typ, - expr.position(), - ) - }) - }); + let v = self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)?; + let typ = v.type_name(); + let path = v.try_cast::().ok_or_else(|| { + self.make_type_mismatch_err::(typ, expr.position()) + })?; - if let Ok(path) = path_result { - use crate::ModuleResolver; + use crate::ModuleResolver; - let path_pos = expr.start_position(); + let path_pos = expr.start_position(); - let resolver = global.embedded_module_resolver.clone(); + let resolver = global.embedded_module_resolver.clone(); - let module_result = resolver - .as_ref() - .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(|| { - Some( - self.module_resolver - .resolve_raw(self, global, &path, path_pos), - ) - }) - .unwrap_or_else(|| { - Err(ERR::ErrorModuleNotFound(path.to_string(), path_pos).into()) - }); + let module = resolver + .as_ref() + .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(|| { + Some( + self.module_resolver + .resolve_raw(self, global, &path, path_pos), + ) + }) + .unwrap_or_else(|| { + Err(ERR::ErrorModuleNotFound(path.to_string(), path_pos).into()) + })?; - if let Ok(module) = module_result { - let (export, must_be_indexed) = if !export.is_empty() { - (export.name.clone(), true) - } else { - (self.get_interned_string(""), false) - }; - - if !must_be_indexed || module.is_indexed() { - global.push_import(export, module); - } else { - // Index the module (making a clone copy if necessary) if it is not indexed - let mut m = crate::func::shared_take_or_clone(module); - m.build_index(); - global.push_import(export, m); - } - - global.num_modules_loaded += 1; - - Ok(Dynamic::UNIT) - } else { - module_result.map(|_| Dynamic::UNIT) - } + let (export, must_be_indexed) = if !export.is_empty() { + (export.name.clone(), true) } else { - path_result.map(|_| Dynamic::UNIT) + (self.get_interned_string(""), false) + }; + + if !must_be_indexed || module.is_indexed() { + global.push_import(export, module); + } else { + // Index the module (making a clone copy if necessary) if it is not indexed + let mut m = crate::func::shared_take_or_clone(module); + m.build_index(); + global.push_import(export, m); } + + global.num_modules_loaded += 1; + + Ok(Dynamic::UNIT) } // Export statement @@ -1004,12 +907,7 @@ impl Engine { } _ => unreachable!("statement cannot be evaluated: {:?}", stmt), - }; - - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); - - result + } } /// Evaluate a list of statements with no `this` pointer. diff --git a/src/func/call.rs b/src/func/call.rs index ff95e3a4..6c7ddf75 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -9,6 +9,7 @@ use crate::engine::{ }; use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::tokenizer::{is_valid_function_name, Token}; +use crate::types::RestoreOnDrop; use crate::{ calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, ERR, @@ -88,9 +89,11 @@ impl<'a> ArgBackup<'a> { /// If `change_first_arg_to_copy` has been called, this function **MUST** be called _BEFORE_ /// exiting the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak. #[inline(always)] - pub fn restore_first_arg(mut self, args: &mut FnCallArgs<'a>) { + pub fn restore_first_arg(&mut self, args: &mut FnCallArgs<'a>) { if let Some(p) = self.orig_mut.take() { args[0] = p; + } else { + unreachable!("`Some`"); } } } @@ -327,7 +330,7 @@ impl Engine { name: &str, op_token: Option<&Token>, hash: u64, - args: &mut FnCallArgs, + mut args: &mut FnCallArgs, is_ref_mut: bool, pos: Position, ) -> RhaiResultOf<(Dynamic, bool)> { @@ -354,48 +357,45 @@ impl Engine { #[cfg(feature = "debugging")] let orig_call_stack_len = global.debugger.call_stack().len(); - let mut _result = if let Some(FnResolutionCacheEntry { func, source }) = func { - assert!(func.is_native()); + let FnResolutionCacheEntry { func, source } = func.unwrap(); + assert!(func.is_native()); - let mut backup = ArgBackup::new(); + let backup = &mut ArgBackup::new(); - // Calling pure function but the first argument is a reference? - if is_ref_mut && func.is_pure() && !args.is_empty() { - // Clone the first argument - backup.change_first_arg_to_copy(args); - } + // Calling pure function but the first argument is a reference? + let swap = is_ref_mut && func.is_pure() && !args.is_empty(); - #[cfg(feature = "debugging")] - if self.debugger.is_some() { - global.debugger.push_call_stack_frame( - self.get_interned_string(name), - args.iter().map(|v| (*v).clone()).collect(), - source.clone().or_else(|| global.source.clone()), - pos, - ); - } + if swap { + // Clone the first argument + backup.change_first_arg_to_copy(args); + } - // Run external function - let src = source.as_ref().map(|s| s.as_str()); - let context = (self, name, src, &*global, lib, pos, level).into(); + let args = + &mut *RestoreOnDrop::new_if(swap, &mut args, move |a| backup.restore_first_arg(a)); - let result = if func.is_plugin_fn() { - let f = func.get_plugin_fn().unwrap(); - if !f.is_pure() && !args.is_empty() && args[0].is_read_only() { - Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into()) - } else { - f.call(context, args) - } + #[cfg(feature = "debugging")] + if self.debugger.is_some() { + global.debugger.push_call_stack_frame( + self.get_interned_string(name), + args.iter().map(|v| (*v).clone()).collect(), + source.clone().or_else(|| global.source.clone()), + pos, + ); + } + + // Run external function + let src = source.as_ref().map(|s| s.as_str()); + let context = (self, name, src, &*global, lib, pos, level).into(); + + let mut _result = if func.is_plugin_fn() { + let f = func.get_plugin_fn().unwrap(); + if !f.is_pure() && !args.is_empty() && args[0].is_read_only() { + Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into()) } else { - func.get_native_fn().unwrap()(context, args) - }; - - // Restore the original reference - backup.restore_first_arg(args); - - result + f.call(context, args) + } } else { - unreachable!("`Some`"); + func.get_native_fn().unwrap()(context, args) }; #[cfg(feature = "debugging")] @@ -413,11 +413,11 @@ impl Engine { Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r), Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), }; - match self + + if let Err(err) = self .run_debugger_raw(global, caches, lib, level, scope, &mut None, node, event) { - Ok(_) => (), - Err(err) => _result = Err(err), + _result = Err(err); } } @@ -543,7 +543,7 @@ impl Engine { fn_name: &str, op_token: Option<&Token>, hashes: FnCallHashes, - args: &mut FnCallArgs, + mut args: &mut FnCallArgs, is_ref_mut: bool, _is_method_call: bool, pos: Position, @@ -613,19 +613,11 @@ impl Engine { #[cfg(not(feature = "no_function"))] if !hashes.is_native_only() { // Script-defined function call? + let hash = hashes.script(); let local_entry = &mut None; if let Some(FnResolutionCacheEntry { func, ref source }) = self - .resolve_fn( - global, - caches, - local_entry, - lib, - None, - hashes.script(), - None, - false, - ) + .resolve_fn(global, caches, local_entry, lib, None, hash, None, false) .cloned() { // Script function call @@ -647,8 +639,9 @@ impl Engine { }; let orig_source = mem::replace(&mut global.source, source.clone()); + let global = &mut *RestoreOnDrop::new(global, move |g| g.source = orig_source); - let result = if _is_method_call { + return if _is_method_call { // Method call of script function - map first argument to `this` let (first_arg, rest_args) = args.split_first_mut().unwrap(); @@ -666,27 +659,24 @@ impl Engine { ) } else { // Normal call of script function - let mut backup = ArgBackup::new(); + let backup = &mut ArgBackup::new(); // The first argument is a reference? - if is_ref_mut && !args.is_empty() { + let swap = is_ref_mut && !args.is_empty(); + + if swap { backup.change_first_arg_to_copy(args); } - let result = self.call_script_fn( + let args = &mut *RestoreOnDrop::new_if(swap, &mut args, move |a| { + backup.restore_first_arg(a) + }); + + self.call_script_fn( global, caches, lib, level, scope, &mut None, func, args, true, pos, - ); - - // Restore the original reference - backup.restore_first_arg(args); - - result - }; - - // Restore the original source - global.source = orig_source; - - return result.map(|r| (r, false)); + ) + } + .map(|r| (r, false)); } } @@ -722,17 +712,14 @@ impl Engine { // Do not match function exit for arguments #[cfg(feature = "debugging")] - let reset_debugger = global.debugger.clear_status_if(|status| { + let reset = global.debugger.clear_status_if(|status| { matches!(status, crate::eval::DebuggerStatus::FunctionExit(..)) }); - - let result = self.eval_expr(global, caches, lib, level, scope, this_ptr, arg_expr); - - // Restore function exit status #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); + let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); - result.map(|r| (r, arg_expr.start_position())) + self.eval_expr(global, caches, lib, level, scope, this_ptr, arg_expr) + .map(|r| (r, arg_expr.start_position())) } /// Call a dot method. @@ -1389,15 +1376,13 @@ impl Engine { Some(f) if f.is_script() => { let fn_def = f.get_script_fn_def().expect("script-defined function"); let new_scope = &mut Scope::new(); + let orig_source = mem::replace(&mut global.source, module.id_raw().cloned()); + let global = &mut *RestoreOnDrop::new(global, move |g| g.source = orig_source); - let result = self.call_script_fn( + self.call_script_fn( global, caches, lib, level, new_scope, &mut None, fn_def, &mut args, true, pos, - ); - - global.source = orig_source; - - result + ) } Some(f) if f.is_plugin_fn() => { diff --git a/src/types/interner.rs b/src/types/interner.rs index 4b7d387e..43a2d395 100644 --- a/src/types/interner.rs +++ b/src/types/interner.rs @@ -1,3 +1,5 @@ +//! A strings interner type. + use super::BloomFilterU64; use crate::func::{hashing::get_hasher, StraightHashMap}; use crate::ImmutableString; @@ -20,10 +22,8 @@ pub const MAX_INTERNED_STRINGS: usize = 1024; /// Maximum length of strings interned. pub const MAX_STRING_LEN: usize = 24; -/// _(internals)_ A factory of identifiers from text strings. +/// _(internals)_ A cache for interned strings. /// Exported under the `internals` feature only. -/// -/// Normal identifiers, property getters and setters are interned separately. pub struct StringsInterner<'a> { /// Maximum number of strings interned. pub capacity: usize, @@ -103,43 +103,48 @@ impl StringsInterner<'_> { if value.strong_count() > 1 { return value; } - e.insert(value).clone() } }; - // If the interner is over capacity, remove the longest entry that has the lowest count - if self.cache.len() > self.capacity { - // Throttle: leave some buffer to grow when shrinking the cache. - // We leave at least two entries, one for the empty string, and one for the string - // that has just been inserted. - let max = if self.capacity < 5 { - 2 - } else { - self.capacity - 3 - }; - - while self.cache.len() > max { - let (_, _, n) = self - .cache - .iter() - .fold((0, usize::MAX, 0), |(x, c, n), (&k, v)| { - if k != hash - && (v.strong_count() < c || (v.strong_count() == c && v.len() > x)) - { - (v.len(), v.strong_count(), k) - } else { - (x, c, n) - } - }); - - self.cache.remove(&n); - } - } + // Throttle the cache upon exit + self.throttle_cache(hash); result } + /// If the interner is over capacity, remove the longest entry that has the lowest count + fn throttle_cache(&mut self, hash: u64) { + if self.cache.len() <= self.capacity { + return; + } + + // Leave some buffer to grow when shrinking the cache. + // We leave at least two entries, one for the empty string, and one for the string + // that has just been inserted. + let max = if self.capacity < 5 { + 2 + } else { + self.capacity - 3 + }; + + while self.cache.len() > max { + let (_, _, n) = self + .cache + .iter() + .fold((0, usize::MAX, 0), |(x, c, n), (&k, v)| { + if k != hash && (v.strong_count() < c || (v.strong_count() == c && v.len() > x)) + { + (v.len(), v.strong_count(), k) + } else { + (x, c, n) + } + }); + + self.cache.remove(&n); + } + } + /// Number of strings interned. #[inline(always)] #[must_use] diff --git a/src/types/mod.rs b/src/types/mod.rs index b6ca3802..1fbf33d3 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -8,6 +8,7 @@ pub mod fn_ptr; pub mod immutable_string; pub mod interner; pub mod parse_error; +pub mod restore; pub mod scope; pub mod variant; @@ -21,5 +22,6 @@ pub use fn_ptr::FnPtr; pub use immutable_string::ImmutableString; pub use interner::StringsInterner; pub use parse_error::{LexError, ParseError, ParseErrorType}; +pub use restore::RestoreOnDrop; pub use scope::Scope; pub use variant::Variant; diff --git a/src/types/restore.rs b/src/types/restore.rs new file mode 100644 index 00000000..d2e6cd87 --- /dev/null +++ b/src/types/restore.rs @@ -0,0 +1,57 @@ +//! Facility to run state restoration logic at the end of scope. + +use std::ops::{Deref, DerefMut}; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; + +/// Run custom restoration logic upon the end of scope. +#[must_use] +pub struct RestoreOnDrop<'a, T, R: FnOnce(&mut T)> { + value: &'a mut T, + restore: Option, +} + +impl<'a, T, R: FnOnce(&mut T)> RestoreOnDrop<'a, T, R> { + /// Create a new [`RestoreOnDrop`] that runs restoration logic at the end of scope only when + /// `need_restore` is `true`. + #[inline(always)] + pub fn new_if(need_restore: bool, value: &'a mut T, restore: R) -> Self { + Self { + value, + restore: if need_restore { Some(restore) } else { None }, + } + } + /// Create a new [`RestoreOnDrop`] that runs restoration logic at the end of scope. + #[inline(always)] + pub fn new(value: &'a mut T, restore: R) -> Self { + Self { + value, + restore: Some(restore), + } + } +} + +impl<'a, T, R: FnOnce(&mut T)> Drop for RestoreOnDrop<'a, T, R> { + #[inline(always)] + fn drop(&mut self) { + if let Some(restore) = self.restore.take() { + restore(self.value); + } + } +} + +impl<'a, T, R: FnOnce(&mut T)> Deref for RestoreOnDrop<'a, T, R> { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.value + } +} + +impl<'a, T, R: FnOnce(&mut T)> DerefMut for RestoreOnDrop<'a, T, R> { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + self.value + } +} From 6053aa164158bb95686d3403cb51743fc794f7be Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 8 Nov 2022 15:01:40 +0800 Subject: [PATCH 5/9] Clean up types. --- src/api/call_fn.rs | 8 +- src/api/eval.rs | 8 +- src/api/register.rs | 11 ++- src/api/run.rs | 8 +- src/ast/ast.rs | 22 ++--- src/ast/script_fn.rs | 4 +- src/engine.rs | 7 +- src/eval/chaining.rs | 26 +++--- src/eval/debugger.rs | 13 ++- src/eval/eval_context.rs | 23 ++--- src/eval/expr.rs | 23 +++-- src/eval/global_state.rs | 24 +++-- src/eval/stmt.rs | 144 ++++++++++++++--------------- src/func/call.rs | 36 ++++---- src/func/native.rs | 22 ++--- src/func/script.rs | 10 +- src/lib.rs | 3 + src/module/mod.rs | 10 +- src/module/resolvers/collection.rs | 4 +- src/module/resolvers/dummy.rs | 4 +- src/module/resolvers/file.rs | 13 +-- src/module/resolvers/mod.rs | 6 +- src/module/resolvers/stat.rs | 23 ++--- src/optimizer.rs | 6 +- src/packages/mod.rs | 4 +- src/parser.rs | 10 +- src/types/fn_ptr.rs | 6 +- src/types/restore.rs | 18 +++- 28 files changed, 248 insertions(+), 248 deletions(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 2057d9b7..b467e54d 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -5,8 +5,8 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::types::dynamic::Variant; use crate::types::RestoreOnDrop; use crate::{ - reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, Shared, StaticVec, - AST, ERR, + reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, + StaticVec, AST, ERR, }; use std::any::{type_name, TypeId}; #[cfg(feature = "no_std")] @@ -249,7 +249,7 @@ impl Engine { arg_values: &mut [Dynamic], ) -> RhaiResult { let statements = ast.statements(); - let lib = &[AsRef::>::as_ref(ast).clone()]; + let lib = &[AsRef::::as_ref(ast).clone()]; let mut this_ptr = this_ptr; let orig_scope_len = scope.len(); @@ -260,7 +260,7 @@ impl Engine { ast.resolver().cloned(), ); #[cfg(not(feature = "no_module"))] - let global = &mut *RestoreOnDrop::new(global, move |g| { + let global = &mut *RestoreOnDrop::lock(global, move |g| { g.embedded_module_resolver = orig_embedded_module_resolver }); diff --git a/src/api/eval.rs b/src/api/eval.rs index 294bf89a..28bcdcbd 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -196,7 +196,7 @@ impl Engine { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let lib = &[ #[cfg(not(feature = "no_function"))] - AsRef::>::as_ref(ast).clone(), + AsRef::::as_ref(ast).clone(), ]; let node = &crate::ast::Stmt::Noop(Position::NONE); self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; @@ -227,7 +227,7 @@ impl Engine { ast.resolver().cloned(), ); #[cfg(not(feature = "no_module"))] - let global = &mut *RestoreOnDrop::new(global, move |g| { + let global = &mut *RestoreOnDrop::lock(global, move |g| { g.embedded_module_resolver = orig_embedded_module_resolver }); @@ -239,7 +239,7 @@ impl Engine { let lib = &[ #[cfg(not(feature = "no_function"))] - AsRef::>::as_ref(ast).clone(), + AsRef::::as_ref(ast).clone(), ]; self.eval_global_statements(global, caches, lib, level, scope, statements) @@ -258,7 +258,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[crate::Shared], + lib: &[crate::SharedModule], level: usize, scope: &mut Scope, statements: &[crate::ast::Stmt], diff --git a/src/api/register.rs b/src/api/register.rs index ce5c0c4a..9e2f7490 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -4,6 +4,7 @@ use crate::func::{FnCallArgs, RegisterNativeFunction, SendSync}; use crate::types::dynamic::Variant; use crate::{ Engine, FnAccess, FnNamespace, Identifier, Module, NativeCallContext, RhaiResultOf, Shared, + SharedModule, }; use std::any::{type_name, TypeId}; #[cfg(feature = "no_std")] @@ -636,7 +637,7 @@ impl Engine { /// When searching for functions, modules loaded later are preferred. In other words, loaded /// modules are searched in reverse order. #[inline(always)] - pub fn register_global_module(&mut self, module: Shared) -> &mut Self { + pub fn register_global_module(&mut self, module: SharedModule) -> &mut Self { // Insert the module into the front. // The first module is always the global namespace. self.global_modules.insert(1, module); @@ -661,7 +662,7 @@ impl Engine { /// let mut module = Module::new(); /// module.set_native_fn("calc", |x: i64| Ok(x + 1)); /// - /// let module: Shared = module.into(); + /// let module: SharedModule = module.into(); /// /// engine /// // Register the module as a fixed sub-module @@ -680,12 +681,12 @@ impl Engine { pub fn register_static_module( &mut self, name: impl AsRef, - module: Shared, + module: SharedModule, ) -> &mut Self { fn register_static_module_raw( - root: &mut std::collections::BTreeMap>, + root: &mut std::collections::BTreeMap, name: &str, - module: Shared, + module: SharedModule, ) { let separator = crate::tokenizer::Token::DoubleColon.syntax(); let separator = separator.as_ref(); diff --git a/src/api/run.rs b/src/api/run.rs index 34f1d106..90c6abf2 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -2,7 +2,7 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::parser::ParseState; -use crate::{Engine, RhaiResultOf, Scope, AST}; +use crate::{Engine, RhaiResultOf, Scope, SharedModule, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -122,9 +122,9 @@ impl Engine { let statements = ast.statements(); if !statements.is_empty() { - let lib: &[crate::Shared] = &[ + let lib: &[SharedModule] = &[ #[cfg(not(feature = "no_function"))] - AsRef::>::as_ref(ast).clone(), + AsRef::::as_ref(ast).clone(), ]; let lib = if lib.first().map_or(true, |m| m.is_empty()) { &[][..] @@ -139,7 +139,7 @@ impl Engine { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let lib = &[ #[cfg(not(feature = "no_function"))] - AsRef::>::as_ref(ast).clone(), + AsRef::::as_ref(ast).clone(), ]; let node = &crate::ast::Stmt::Noop(crate::Position::NONE); self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; diff --git a/src/ast/ast.rs b/src/ast/ast.rs index 9c186e67..76277e7b 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -28,7 +28,7 @@ pub struct AST { body: StmtBlock, /// Script-defined functions. #[cfg(not(feature = "no_function"))] - lib: crate::Shared, + lib: crate::SharedModule, /// Embedded module resolver, if any. #[cfg(not(feature = "no_module"))] resolver: Option>, @@ -74,7 +74,7 @@ impl AST { #[must_use] pub(crate) fn new( statements: impl IntoIterator, - #[cfg(not(feature = "no_function"))] functions: impl Into>, + #[cfg(not(feature = "no_function"))] functions: impl Into, ) -> Self { Self { source: None, @@ -94,7 +94,7 @@ impl AST { #[must_use] pub fn new( statements: impl IntoIterator, - #[cfg(not(feature = "no_function"))] functions: impl Into>, + #[cfg(not(feature = "no_function"))] functions: impl Into, ) -> Self { Self { source: None, @@ -113,7 +113,7 @@ impl AST { #[must_use] pub(crate) fn new_with_source( statements: impl IntoIterator, - #[cfg(not(feature = "no_function"))] functions: impl Into>, + #[cfg(not(feature = "no_function"))] functions: impl Into, source: impl Into, ) -> Self { let mut ast = Self::new( @@ -131,7 +131,7 @@ impl AST { #[must_use] pub fn new_with_source( statements: impl IntoIterator, - #[cfg(not(feature = "no_function"))] functions: impl Into>, + #[cfg(not(feature = "no_function"))] functions: impl Into, source: impl Into, ) -> Self { let mut ast = Self::new( @@ -267,7 +267,7 @@ impl AST { #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] - pub(crate) const fn shared_lib(&self) -> &crate::Shared { + pub(crate) const fn shared_lib(&self) -> &SharedModule { &self.lib } /// _(internals)_ Get the internal shared [`Module`][crate::Module] containing all script-defined functions. @@ -278,7 +278,7 @@ impl AST { #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] - pub const fn shared_lib(&self) -> &crate::Shared { + pub const fn shared_lib(&self) -> &crate::SharedModule { &self.lib } /// Get the embedded [module resolver][crate::ModuleResolver]. @@ -957,19 +957,19 @@ impl AsRef for AST { } #[cfg(not(feature = "no_function"))] -impl Borrow> for AST { +impl Borrow for AST { #[inline(always)] #[must_use] - fn borrow(&self) -> &crate::Shared { + fn borrow(&self) -> &crate::SharedModule { self.shared_lib() } } #[cfg(not(feature = "no_function"))] -impl AsRef> for AST { +impl AsRef for AST { #[inline(always)] #[must_use] - fn as_ref(&self) -> &crate::Shared { + fn as_ref(&self) -> &crate::SharedModule { self.shared_lib() } } diff --git a/src/ast/script_fn.rs b/src/ast/script_fn.rs index d40d6a5a..743bf2f5 100644 --- a/src/ast/script_fn.rs +++ b/src/ast/script_fn.rs @@ -20,9 +20,9 @@ use std::{fmt, hash::Hash}; #[derive(Debug, Clone)] pub struct EncapsulatedEnviron { /// Functions defined within the same [`AST`][crate::AST]. - pub lib: crate::Shared, + pub lib: crate::SharedModule, /// Imported [modules][crate::Module]. - pub imports: Box<[(ImmutableString, crate::Shared)]>, + pub imports: Box<[(ImmutableString, crate::SharedModule)]>, /// Globally-defined constants. pub constants: Option, } diff --git a/src/engine.rs b/src/engine.rs index 0afadf75..8d5df823 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -9,7 +9,8 @@ use crate::packages::{Package, StandardPackage}; use crate::tokenizer::Token; use crate::types::StringsInterner; use crate::{ - Dynamic, Identifier, ImmutableString, Locked, Module, OptimizationLevel, Shared, StaticVec, + Dynamic, Identifier, ImmutableString, Locked, Module, OptimizationLevel, SharedModule, + StaticVec, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -91,10 +92,10 @@ pub const OP_INCLUSIVE_RANGE: &str = Token::InclusiveRange.literal_syntax(); /// ``` pub struct Engine { /// A collection of all modules loaded into the global namespace of the Engine. - pub(crate) global_modules: StaticVec>, + pub(crate) global_modules: StaticVec, /// A collection of all sub-modules directly loaded into the Engine. #[cfg(not(feature = "no_module"))] - pub(crate) global_sub_modules: std::collections::BTreeMap>, + pub(crate) global_sub_modules: std::collections::BTreeMap, /// A module resolution service. #[cfg(not(feature = "no_module"))] diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 7d7560fc..021dfdc3 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -6,7 +6,7 @@ use crate::ast::{ASTFlags, Expr, OpAssignment}; use crate::types::dynamic::Union; use crate::types::RestoreOnDrop; use crate::{ - Dynamic, Engine, FnArgsVec, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, ERR, + Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR, }; use std::hash::Hash; #[cfg(feature = "no_std")] @@ -43,7 +43,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, this_ptr: &mut Option<&mut Dynamic>, target: &mut Target, @@ -205,7 +205,7 @@ impl Engine { global, caches, lib, level, scope, this_ptr, rhs, )?; #[cfg(feature = "debugging")] - let global = &mut *RestoreOnDrop::new(global, move |g| { + let global = &mut *RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) }); @@ -216,7 +216,7 @@ impl Engine { // Truncate the index values upon exit let offset = idx_values.len() - args.len(); let idx_values = - &mut *RestoreOnDrop::new(idx_values, move |v| v.truncate(offset)); + &mut *RestoreOnDrop::lock(idx_values, move |v| v.truncate(offset)); let call_args = &mut idx_values[offset..]; let pos1 = args.get(0).map_or(Position::NONE, Expr::position); @@ -384,7 +384,7 @@ impl Engine { global, caches, lib, level, scope, this_ptr, _node, )?; #[cfg(feature = "debugging")] - let global = &mut *RestoreOnDrop::new(global, move |g| { + let global = &mut *RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) }); @@ -394,7 +394,7 @@ impl Engine { // Truncate the index values upon exit let offset = idx_values.len() - args.len(); - let idx_values = &mut *RestoreOnDrop::new(idx_values, move |v| { + let idx_values = &mut *RestoreOnDrop::lock(idx_values, move |v| { v.truncate(offset) }); @@ -516,7 +516,7 @@ impl Engine { global, caches, lib, level, scope, this_ptr, _node, )?; #[cfg(feature = "debugging")] - let global = &mut *RestoreOnDrop::new(global, move |g| { + let global = &mut *RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) }); @@ -527,7 +527,7 @@ impl Engine { // Truncate the index values upon exit let offset = idx_values.len() - args.len(); let idx_values = - &mut *RestoreOnDrop::new(idx_values, move |v| { + &mut *RestoreOnDrop::lock(idx_values, move |v| { v.truncate(offset) }); @@ -570,7 +570,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -661,7 +661,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -773,7 +773,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, target: &mut Dynamic, idx: &mut Dynamic, @@ -796,7 +796,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, target: &mut Dynamic, idx: &mut Dynamic, @@ -820,7 +820,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, target: &'t mut Dynamic, idx: &mut Dynamic, diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 3048e5bc..8c2d94e5 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -4,7 +4,7 @@ use super::{Caches, EvalContext, GlobalRuntimeState}; use crate::ast::{ASTNode, Expr, Stmt}; use crate::{ - Dynamic, Engine, EvalAltResult, ImmutableString, Module, Position, RhaiResultOf, Scope, Shared, + Dynamic, Engine, EvalAltResult, ImmutableString, Position, RhaiResultOf, Scope, SharedModule, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -413,7 +413,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -440,7 +440,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -463,7 +463,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -510,7 +510,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -519,8 +519,7 @@ impl Engine { ) -> Result, Box> { let src = global.source_raw().cloned(); let src = src.as_ref().map(|s| s.as_str()); - let context = - crate::EvalContext::new(self, global, Some(caches), lib, level, scope, this_ptr); + let context = crate::EvalContext::new(self, global, caches, lib, level, scope, this_ptr); if let Some((.., ref on_debugger)) = self.debugger { let command = on_debugger(context, event, node, src, node.position())?; diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index 99456ce1..dc50a63c 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -1,7 +1,7 @@ //! Evaluation context. use super::{Caches, GlobalRuntimeState}; -use crate::{Dynamic, Engine, Module, Scope, Shared}; +use crate::{Dynamic, Engine, Module, Scope, SharedModule}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -16,9 +16,9 @@ pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { /// The current [`GlobalRuntimeState`]. global: &'g mut GlobalRuntimeState, /// The current [caches][Caches], if available. - caches: Option<&'c mut Caches>, + caches: &'c mut Caches, /// The current stack of imported [modules][Module]. - lib: &'a [Shared], + lib: &'a [SharedModule], /// The current bound `this` pointer, if any. this_ptr: &'t mut Option<&'pt mut Dynamic>, /// The current nesting level of function calls. @@ -32,8 +32,8 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { pub fn new( engine: &'a Engine, global: &'g mut GlobalRuntimeState, - caches: Option<&'c mut Caches>, - lib: &'a [Shared], + caches: &'c mut Caches, + lib: &'a [SharedModule], level: usize, scope: &'s mut Scope<'ps>, this_ptr: &'t mut Option<&'pt mut Dynamic>, @@ -117,7 +117,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub const fn namespaces(&self) -> &[Shared] { + pub const fn namespaces(&self) -> &[SharedModule] { self.lib } /// The current bound `this` pointer, if any. @@ -173,17 +173,10 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { ) -> crate::RhaiResult { let expr: &crate::ast::Expr = expr; - let mut new_caches = Caches::new(); - - let caches = match self.caches.as_mut() { - Some(c) => c, - None => &mut new_caches, - }; - match expr { crate::ast::Expr::Stmt(statements) => self.engine.eval_stmt_block( self.global, - caches, + self.caches, self.lib, self.level, self.scope, @@ -193,7 +186,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { ), _ => self.engine.eval_expr( self.global, - caches, + self.caches, self.lib, self.level, self.scope, diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 269ab992..89f0d4a7 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -4,8 +4,7 @@ use super::{Caches, EvalContext, GlobalRuntimeState, Target}; use crate::ast::{Expr, OpAssignment}; use crate::engine::{KEYWORD_THIS, OP_CONCAT}; use crate::types::dynamic::AccessMode; -use crate::types::RestoreOnDrop; -use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, ERR}; +use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR}; use std::num::NonZeroUsize; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -19,7 +18,7 @@ impl Engine { &self, global: &GlobalRuntimeState, namespace: &crate::ast::Namespace, - ) -> Option> { + ) -> Option { assert!(!namespace.is_empty()); let root = namespace.root(); @@ -52,7 +51,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &'s mut Scope, this_ptr: &'s mut Option<&mut Dynamic>, @@ -138,7 +137,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &'s mut Scope, this_ptr: &'s mut Option<&mut Dynamic>, @@ -174,7 +173,7 @@ impl Engine { // Check the variable resolver, if any if let Some(ref resolve_var) = self.resolve_var { - let context = EvalContext::new(self, global, Some(caches), lib, level, scope, this_ptr); + let context = EvalContext::new(self, global, caches, lib, level, scope, this_ptr); let var_name = expr.get_variable_name(true).expect("`Expr::Variable`"); match resolve_var(var_name, index, context) { Ok(Some(mut result)) => { @@ -222,7 +221,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -238,7 +237,9 @@ impl Engine { let reset = self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; #[cfg(feature = "debugging")] - let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); + let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { + g.debugger.reset_status(reset) + }); self.track_operation(global, expr.position())?; @@ -269,7 +270,9 @@ impl Engine { let reset = self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; #[cfg(feature = "debugging")] - let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); + let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { + g.debugger.reset_status(reset) + }); self.track_operation(global, expr.position())?; @@ -418,7 +421,7 @@ impl Engine { )) })?; let mut context = - EvalContext::new(self, global, Some(caches), lib, level, scope, this_ptr); + EvalContext::new(self, global, caches, lib, level, scope, this_ptr); let result = (custom_def.func)(&mut context, &expressions, &custom.state); diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 935434ef..413dee13 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -28,7 +28,7 @@ pub struct GlobalRuntimeState { imports: crate::StaticVec, /// Stack of imported [modules][crate::Module]. #[cfg(not(feature = "no_module"))] - modules: crate::StaticVec>, + modules: crate::StaticVec, /// Source of the current context. /// /// No source if the string is empty. @@ -127,7 +127,7 @@ impl GlobalRuntimeState { #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] - pub fn get_shared_import(&self, index: usize) -> Option> { + pub fn get_shared_import(&self, index: usize) -> Option { self.modules.get(index).cloned() } /// Get a mutable reference to the globally-imported [module][crate::Module] at a @@ -141,7 +141,7 @@ impl GlobalRuntimeState { pub(crate) fn get_shared_import_mut( &mut self, index: usize, - ) -> Option<&mut crate::Shared> { + ) -> Option<&mut crate::SharedModule> { self.modules.get_mut(index) } /// Get the index of a globally-imported [module][crate::Module] by name. @@ -165,7 +165,7 @@ impl GlobalRuntimeState { pub fn push_import( &mut self, name: impl Into, - module: impl Into>, + module: impl Into, ) { self.imports.push(name.into()); self.modules.push(module.into()); @@ -198,7 +198,7 @@ impl GlobalRuntimeState { #[inline] pub(crate) fn iter_imports_raw( &self, - ) -> impl Iterator)> { + ) -> impl Iterator { self.imports.iter().zip(self.modules.iter()).rev() } /// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order. @@ -208,7 +208,7 @@ impl GlobalRuntimeState { #[inline] pub fn scan_imports_raw( &self, - ) -> impl Iterator)> { + ) -> impl Iterator { self.imports.iter().zip(self.modules.iter()) } /// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of @@ -316,11 +316,11 @@ impl GlobalRuntimeState { #[cfg(not(feature = "no_module"))] impl IntoIterator for GlobalRuntimeState { - type Item = (ImmutableString, crate::Shared); + type Item = (ImmutableString, crate::SharedModule); type IntoIter = std::iter::Rev< std::iter::Zip< smallvec::IntoIter<[ImmutableString; crate::STATIC_VEC_INLINE_SIZE]>, - smallvec::IntoIter<[crate::Shared; crate::STATIC_VEC_INLINE_SIZE]>, + smallvec::IntoIter<[crate::SharedModule; crate::STATIC_VEC_INLINE_SIZE]>, >, >; @@ -331,11 +331,11 @@ impl IntoIterator for GlobalRuntimeState { #[cfg(not(feature = "no_module"))] impl<'a> IntoIterator for &'a GlobalRuntimeState { - type Item = (&'a ImmutableString, &'a crate::Shared); + type Item = (&'a ImmutableString, &'a crate::SharedModule); type IntoIter = std::iter::Rev< std::iter::Zip< std::slice::Iter<'a, ImmutableString>, - std::slice::Iter<'a, crate::Shared>, + std::slice::Iter<'a, crate::SharedModule>, >, >; @@ -345,9 +345,7 @@ impl<'a> IntoIterator for &'a GlobalRuntimeState { } #[cfg(not(feature = "no_module"))] -impl, M: Into>> Extend<(K, M)> - for GlobalRuntimeState -{ +impl, M: Into> Extend<(K, M)> for GlobalRuntimeState { #[inline] fn extend>(&mut self, iter: T) { for (k, m) in iter { diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 2841ecba..1e232ddc 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -8,10 +8,7 @@ use crate::ast::{ use crate::func::{get_builtin_op_assignment_fn, get_hasher}; use crate::types::dynamic::{AccessMode, Union}; use crate::types::RestoreOnDrop; -use crate::{ - Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, - ERR, INT, -}; +use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR, INT}; use std::hash::{Hash, Hasher}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -29,7 +26,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -42,7 +39,7 @@ impl Engine { // Restore scope at end of block if necessary let orig_scope_len = scope.len(); - let scope = &mut *RestoreOnDrop::new_if(restore_orig_state, scope, move |s| { + let scope = &mut *RestoreOnDrop::lock_if(restore_orig_state, scope, move |s| { s.rewind(orig_scope_len); }); @@ -55,7 +52,7 @@ impl Engine { global.scope_level += 1; } - let global = &mut *RestoreOnDrop::new_if(restore_orig_state, global, move |g| { + let global = &mut *RestoreOnDrop::lock_if(restore_orig_state, global, move |g| { g.scope_level -= 1; #[cfg(not(feature = "no_module"))] g.truncate_imports(orig_imports_len); @@ -67,7 +64,7 @@ impl Engine { // Pop new function resolution caches at end of block let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len(); - let caches = &mut *RestoreOnDrop::new(caches, move |c| { + let caches = &mut *RestoreOnDrop::lock(caches, move |c| { c.rewind_fn_resolution_caches(orig_fn_resolution_caches_len) }); @@ -96,10 +93,11 @@ impl Engine { .skip(imports_len) .any(|(.., m)| m.contains_indexed_global_functions()) { + // Different scenarios where the cache must be cleared - notice that this is + // expensive as all function resolutions must start again if caches.fn_resolution_caches_len() > orig_fn_resolution_caches_len { // When new module is imported with global functions and there is already - // a new cache, clear it - notice that this is expensive as all function - // resolutions must start again + // a new cache, just clear it caches.fn_resolution_cache_mut().clear(); } else if restore_orig_state { // When new module is imported with global functions, push a new cache @@ -120,20 +118,18 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, op_info: &OpAssignment, target: &mut Target, root: (&str, Position), - new_val: Dynamic, + mut new_val: Dynamic, ) -> RhaiResultOf<()> { + // Assignment to constant variable? if target.is_read_only() { - // Assignment to constant variable return Err(ERR::ErrorAssignmentToConstant(root.0.to_string(), root.1).into()); } - let mut new_val = new_val; - if op_info.is_op_assignment() { let OpAssignment { hash_op_assign, @@ -204,7 +200,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -215,7 +211,7 @@ impl Engine { let reset = self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, stmt)?; #[cfg(feature = "debugging")] - let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); + let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset)); // Coded this way for better branch prediction. // Popular branches are lifted out of the `match` statement into their own branches. @@ -240,11 +236,9 @@ impl Engine { .eval_expr(global, caches, lib, level, scope, this_ptr, rhs)? .flatten(); - let search_val = + let (mut lhs_ptr, pos) = self.search_namespace(global, caches, lib, level, scope, this_ptr, lhs)?; - let (mut lhs_ptr, pos) = search_val; - let var_name = x.3.as_str(); #[cfg(not(feature = "no_closure"))] @@ -267,16 +261,17 @@ impl Engine { return self .eval_op_assignment(global, caches, lib, level, op_info, lhs_ptr, root, rhs_val) .map(|_| Dynamic::UNIT); - } else { - let (op_info, BinaryExpr { lhs, rhs }) = &**x; + } + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + { let rhs_val = self.eval_expr(global, caches, lib, level, scope, this_ptr, rhs)?; // Check if the result is a string. If so, intern it. #[cfg(not(feature = "no_closure"))] - let is_string = !rhs_val.is_shared() && rhs_val.is::(); + let is_string = !rhs_val.is_shared() && rhs_val.is::(); #[cfg(feature = "no_closure")] - let is_string = rhs_val.is::(); + let is_string = rhs_val.is::(); let rhs_val = if is_string { self.get_interned_string( @@ -333,23 +328,20 @@ impl Engine { let (expr, if_block, else_block) = &**x; let guard_val = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::(typ, expr.position()) - }) - }); + .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .as_bool() + .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; - match guard_val { - Ok(true) if if_block.is_empty() => Ok(Dynamic::UNIT), - Ok(true) => self.eval_stmt_block( + if guard_val && !if_block.is_empty() { + self.eval_stmt_block( global, caches, lib, level, scope, this_ptr, if_block, true, - ), - Ok(false) if else_block.is_empty() => Ok(Dynamic::UNIT), - Ok(false) => self.eval_stmt_block( + ) + } else if !guard_val && !else_block.is_empty() { + self.eval_stmt_block( global, caches, lib, level, scope, this_ptr, else_block, true, - ), - err => err.map(Into::into), + ) + } else { + Ok(Dynamic::UNIT) } } @@ -421,14 +413,11 @@ impl Engine { } } - if let Some(expr) = result { - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) - } else { - def_case.as_ref().map_or(Ok(Dynamic::UNIT), |&index| { - let def_expr = &expressions[index].expr; - self.eval_expr(global, caches, lib, level, scope, this_ptr, def_expr) + result + .or_else(|| def_case.as_ref().map(|&index| &expressions[index].expr)) + .map_or(Ok(Dynamic::UNIT), |expr| { + self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) }) - } } // Loop @@ -439,17 +428,16 @@ impl Engine { loop { self.track_operation(global, body.position())?; } - } else { - loop { - match self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, body, true, - ) { - Ok(_) => (), - Err(err) => match *err { - ERR::LoopBreak(false, ..) => (), - ERR::LoopBreak(true, value, ..) => break Ok(value), - _ => break Err(err), - }, + } + + loop { + if let Err(err) = self + .eval_stmt_block(global, caches, lib, level, scope, this_ptr, body, true) + { + match *err { + ERR::LoopBreak(false, ..) => (), + ERR::LoopBreak(true, value, ..) => break Ok(value), + _ => break Err(err), } } } @@ -469,15 +457,17 @@ impl Engine { break Ok(Dynamic::UNIT); } - if !body.is_empty() { - if let Err(err) = self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, body, true, - ) { - match *err { - ERR::LoopBreak(false, ..) => (), - ERR::LoopBreak(true, value, ..) => break Ok(value), - _ => break Err(err), - } + if body.is_empty() { + continue; + } + + if let Err(err) = self + .eval_stmt_block(global, caches, lib, level, scope, this_ptr, body, true) + { + match *err { + ERR::LoopBreak(false, ..) => (), + ERR::LoopBreak(true, value, ..) => break Ok(value), + _ => break Err(err), } } } @@ -544,7 +534,7 @@ impl Engine { if let Some(func) = func { // Restore scope at end of statement let orig_scope_len = scope.len(); - let scope = &mut *RestoreOnDrop::new(scope, move |s| { + let scope = &mut *RestoreOnDrop::lock(scope, move |s| { s.rewind(orig_scope_len); }); @@ -616,12 +606,13 @@ impl Engine { Stmt::BreakLoop(expr, options, pos) => { let is_break = options.contains(ASTFlags::BREAK); - if let Some(ref expr) = expr { - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) - .and_then(|v| ERR::LoopBreak(is_break, v, *pos).into()) + let value = if let Some(ref expr) = expr { + self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)? } else { - Err(ERR::LoopBreak(is_break, Dynamic::UNIT, *pos).into()) - } + Dynamic::UNIT + }; + + Err(ERR::LoopBreak(is_break, value, *pos).into()) } // Try/Catch statement @@ -638,9 +629,7 @@ impl Engine { match self .eval_stmt_block(global, caches, lib, level, scope, this_ptr, try_block, true) { - Ok(_) => self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, try_block, true, - ), + r @ Ok(_) => r, Err(err) if err.is_pseudo_error() => Err(err), Err(err) if !err.is_catchable() => Err(err), Err(mut err) => { @@ -682,7 +671,7 @@ impl Engine { // Restore scope at end of block let orig_scope_len = scope.len(); let scope = - &mut *RestoreOnDrop::new_if(!catch_var.is_empty(), scope, move |s| { + &mut *RestoreOnDrop::lock_if(!catch_var.is_empty(), scope, move |s| { s.rewind(orig_scope_len); }); @@ -757,7 +746,8 @@ impl Engine { nesting_level, will_shadow, }; - let context = EvalContext::new(self, global, None, lib, level, scope, this_ptr); + let context = + EvalContext::new(self, global, caches, lib, level, scope, this_ptr); if !filter(true, info, context)? { return Err(ERR::ErrorForbiddenVariable(var_name.to_string(), *pos).into()); @@ -917,7 +907,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, statements: &[Stmt], diff --git a/src/func/call.rs b/src/func/call.rs index 6c7ddf75..d7f0102d 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -11,8 +11,8 @@ use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::tokenizer::{is_valid_function_name, Token}; use crate::types::RestoreOnDrop; use crate::{ - calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, Module, - OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, ERR, + calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, SharedModule, + OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, ERR, }; #[cfg(feature = "no_std")] use hashbrown::hash_map::Entry; @@ -169,7 +169,7 @@ impl Engine { _global: &GlobalRuntimeState, caches: &'s mut Caches, local_entry: &'s mut Option, - lib: &[Shared], + lib: &[SharedModule], op_token: Option<&Token>, hash_base: u64, args: Option<&mut FnCallArgs>, @@ -325,7 +325,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, name: &str, op_token: Option<&Token>, @@ -371,7 +371,7 @@ impl Engine { } let args = - &mut *RestoreOnDrop::new_if(swap, &mut args, move |a| backup.restore_first_arg(a)); + &mut *RestoreOnDrop::lock_if(swap, &mut args, move |a| backup.restore_first_arg(a)); #[cfg(feature = "debugging")] if self.debugger.is_some() { @@ -537,7 +537,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, _scope: Option<&mut Scope>, fn_name: &str, @@ -585,7 +585,7 @@ impl Engine { } else { let hash_script = calc_fn_hash(None, fn_name.as_str(), num_params as usize); - self.has_script_fn(Some(global), caches, lib, hash_script) + self.has_script_fn(global, caches, lib, hash_script) } .into(), false, @@ -639,7 +639,7 @@ impl Engine { }; let orig_source = mem::replace(&mut global.source, source.clone()); - let global = &mut *RestoreOnDrop::new(global, move |g| g.source = orig_source); + let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source); return if _is_method_call { // Method call of script function - map first argument to `this` @@ -668,7 +668,7 @@ impl Engine { backup.change_first_arg_to_copy(args); } - let args = &mut *RestoreOnDrop::new_if(swap, &mut args, move |a| { + let args = &mut *RestoreOnDrop::lock_if(swap, &mut args, move |a| { backup.restore_first_arg(a) }); @@ -694,7 +694,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -716,7 +716,7 @@ impl Engine { matches!(status, crate::eval::DebuggerStatus::FunctionExit(..)) }); #[cfg(feature = "debugging")] - let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset)); + let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset)); self.eval_expr(global, caches, lib, level, scope, this_ptr, arg_expr) .map(|r| (r, arg_expr.start_position())) @@ -728,7 +728,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, fn_name: &str, mut hash: FnCallHashes, @@ -953,7 +953,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -1087,7 +1087,7 @@ impl Engine { false } else { let hash_script = calc_fn_hash(None, &fn_name, num_params as usize); - self.has_script_fn(Some(global), caches, lib, hash_script) + self.has_script_fn(global, caches, lib, hash_script) } .into()); } @@ -1244,7 +1244,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -1378,7 +1378,7 @@ impl Engine { let new_scope = &mut Scope::new(); let orig_source = mem::replace(&mut global.source, module.id_raw().cloned()); - let global = &mut *RestoreOnDrop::new(global, move |g| g.source = orig_source); + let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source); self.call_script_fn( global, caches, lib, level, new_scope, &mut None, fn_def, &mut args, true, pos, @@ -1426,7 +1426,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, script: &str, @@ -1471,7 +1471,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, diff --git a/src/func/native.rs b/src/func/native.rs index 63823ecd..65f5119d 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -8,7 +8,7 @@ use crate::tokenizer::{is_valid_function_name, Token, TokenizeState}; use crate::types::dynamic::Variant; use crate::{ calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Module, Position, RhaiResult, - RhaiResultOf, StaticVec, VarDefInfo, ERR, + RhaiResultOf, SharedModule, StaticVec, VarDefInfo, ERR, }; use std::any::type_name; #[cfg(feature = "no_std")] @@ -74,7 +74,7 @@ pub struct NativeCallContext<'a> { /// The current [`GlobalRuntimeState`], if any. global: Option<&'a GlobalRuntimeState>, /// The current stack of loaded [modules][Module]. - lib: &'a [Shared], + lib: &'a [SharedModule], /// [Position] of the function call. pos: Position, /// The current nesting level of function calls. @@ -93,7 +93,7 @@ pub struct NativeCallContextStore { /// The current [`GlobalRuntimeState`], if any. pub global: GlobalRuntimeState, /// The current stack of loaded [modules][Module]. - pub lib: StaticVec>, + pub lib: StaticVec, /// [Position] of the function call. pub pos: Position, /// The current nesting level of function calls. @@ -116,7 +116,7 @@ impl<'a> &'a str, Option<&'a str>, &'a GlobalRuntimeState, - &'a [Shared], + &'a [SharedModule], Position, usize, )> for NativeCallContext<'a> @@ -128,7 +128,7 @@ impl<'a> &'a str, Option<&'a str>, &'a GlobalRuntimeState, - &'a [Shared], + &'a [SharedModule], Position, usize, ), @@ -145,9 +145,9 @@ impl<'a> } } -impl<'a> From<(&'a Engine, &'a str, &'a [Shared])> for NativeCallContext<'a> { +impl<'a> From<(&'a Engine, &'a str, &'a [SharedModule])> for NativeCallContext<'a> { #[inline(always)] - fn from(value: (&'a Engine, &'a str, &'a [Shared])) -> Self { + fn from(value: (&'a Engine, &'a str, &'a [SharedModule])) -> Self { Self { engine: value.0, fn_name: value.1, @@ -169,7 +169,7 @@ impl<'a> NativeCallContext<'a> { )] #[inline(always)] #[must_use] - pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [Shared]) -> Self { + pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [SharedModule]) -> Self { Self { engine, fn_name, @@ -193,7 +193,7 @@ impl<'a> NativeCallContext<'a> { fn_name: &'a str, source: Option<&'a str>, global: &'a GlobalRuntimeState, - lib: &'a [Shared], + lib: &'a [SharedModule], pos: Position, level: usize, ) -> Self { @@ -291,7 +291,7 @@ impl<'a> NativeCallContext<'a> { #[inline] pub(crate) fn iter_imports_raw( &self, - ) -> impl Iterator)> { + ) -> impl Iterator { self.global.iter().flat_map(|&g| g.iter_imports_raw()) } /// _(internals)_ The current [`GlobalRuntimeState`], if any. @@ -315,7 +315,7 @@ impl<'a> NativeCallContext<'a> { #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub const fn namespaces(&self) -> &[Shared] { + pub const fn namespaces(&self) -> &[SharedModule] { self.lib } /// Call a function inside the call context with the provided arguments. diff --git a/src/func/script.rs b/src/func/script.rs index 06fa916a..f8553165 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -4,7 +4,7 @@ use super::call::FnCallArgs; use crate::ast::ScriptFnDef; use crate::eval::{Caches, GlobalRuntimeState}; -use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, Shared, ERR}; +use crate::{Dynamic, Engine, Position, RhaiError, RhaiResult, Scope, SharedModule, ERR}; use std::mem; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -26,7 +26,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], level: usize, scope: &mut Scope, this_ptr: &mut Option<&mut Dynamic>, @@ -228,9 +228,9 @@ impl Engine { #[must_use] pub(crate) fn has_script_fn( &self, - _global: Option<&GlobalRuntimeState>, + _global: &GlobalRuntimeState, caches: &mut Caches, - lib: &[Shared], + lib: &[SharedModule], hash_script: u64, ) -> bool { let cache = caches.fn_resolution_cache_mut(); @@ -247,7 +247,7 @@ impl Engine { #[cfg(not(feature = "no_module"))] let result = result || // Then check imported modules - _global.map_or(false, |m| m.contains_qualified_fn(hash_script)) + _global.contains_qualified_fn(hash_script) // Then check sub-modules || self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script)); diff --git a/src/lib.rs b/src/lib.rs index 12f36850..4fdb59f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -240,6 +240,9 @@ pub use func::Locked; use func::{calc_fn_hash, calc_fn_hash_full, calc_var_hash}; +/// A shared [`Module`]. +type SharedModule = Shared; + pub use rhai_codegen::*; pub use func::{plugin, FuncArgs}; diff --git a/src/module/mod.rs b/src/module/mod.rs index 6cb59198..0d78e0e4 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -10,7 +10,7 @@ use crate::func::{ use crate::types::{dynamic::Variant, BloomFilterU64, CustomTypesCollection}; use crate::{ calc_fn_hash, calc_fn_hash_full, Dynamic, Identifier, ImmutableString, NativeCallContext, - RhaiResultOf, Shared, SmartString, StaticVec, + RhaiResultOf, Shared, SharedModule, SmartString, StaticVec, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -172,7 +172,7 @@ pub struct Module { /// Custom types. custom_types: Option, /// Sub-modules. - modules: Option>>, + modules: Option>, /// [`Module`] variables. variables: Option>, /// Flattened collection of all [`Module`] variables, including those in sub-modules. @@ -754,7 +754,7 @@ impl Module { #[cfg(not(feature = "no_module"))] #[inline] #[must_use] - pub(crate) fn get_sub_modules_mut(&mut self) -> &mut BTreeMap> { + pub(crate) fn get_sub_modules_mut(&mut self) -> &mut BTreeMap { // We must assume that the user has changed the sub-modules // (otherwise why take a mutable reference?) self.all_functions = None; @@ -822,7 +822,7 @@ impl Module { pub fn set_sub_module( &mut self, name: impl Into, - sub_module: impl Into>, + sub_module: impl Into, ) -> &mut Self { self.modules .get_or_insert_with(|| Default::default()) @@ -1830,7 +1830,7 @@ impl Module { /// Get an iterator to the sub-modules in the [`Module`]. #[inline] - pub fn iter_sub_modules(&self) -> impl Iterator)> { + pub fn iter_sub_modules(&self) -> impl Iterator { self.modules .iter() .flat_map(|m| m.iter().map(|(k, m)| (k.as_str(), m))) diff --git a/src/module/resolvers/collection.rs b/src/module/resolvers/collection.rs index 8a61d715..5310e6a8 100644 --- a/src/module/resolvers/collection.rs +++ b/src/module/resolvers/collection.rs @@ -1,5 +1,5 @@ use crate::{ - Engine, Module, ModuleResolver, Position, RhaiResultOf, Shared, StaticVec, ERR, + Engine, ModuleResolver, Position, RhaiResultOf, SharedModule, StaticVec, ERR, STATIC_VEC_INLINE_SIZE, }; #[cfg(feature = "no_std")] @@ -138,7 +138,7 @@ impl ModuleResolver for ModuleResolversCollection { source_path: Option<&str>, path: &str, pos: Position, - ) -> RhaiResultOf> { + ) -> RhaiResultOf { for resolver in &self.0 { match resolver.resolve(engine, source_path, path, pos) { Ok(module) => return Ok(module), diff --git a/src/module/resolvers/dummy.rs b/src/module/resolvers/dummy.rs index f5782ebc..4da90566 100644 --- a/src/module/resolvers/dummy.rs +++ b/src/module/resolvers/dummy.rs @@ -1,4 +1,4 @@ -use crate::{Engine, Module, ModuleResolver, Position, RhaiResultOf, Shared, ERR}; +use crate::{Engine, ModuleResolver, Position, RhaiResultOf, SharedModule, ERR}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -45,7 +45,7 @@ impl ModuleResolver for DummyModuleResolver { _: Option<&str>, path: &str, pos: Position, - ) -> RhaiResultOf> { + ) -> RhaiResultOf { Err(ERR::ErrorModuleNotFound(path.into(), pos).into()) } } diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index d68ddfd1..ee56ac18 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -4,7 +4,8 @@ use crate::eval::GlobalRuntimeState; use crate::func::{locked_read, locked_write}; use crate::{ - Engine, Identifier, Locked, Module, ModuleResolver, Position, RhaiResultOf, Scope, Shared, ERR, + Engine, Identifier, Locked, Module, ModuleResolver, Position, RhaiResultOf, Scope, Shared, + SharedModule, ERR, }; use std::{ @@ -51,7 +52,7 @@ pub struct FileModuleResolver { extension: Identifier, cache_enabled: bool, scope: Scope<'static>, - cache: Locked>>, + cache: Locked>, } impl Default for FileModuleResolver { @@ -258,7 +259,7 @@ impl FileModuleResolver { /// The next time this path is resolved, the script file will be loaded once again. #[inline] #[must_use] - pub fn clear_cache_for_path(&mut self, path: impl AsRef) -> Option> { + pub fn clear_cache_for_path(&mut self, path: impl AsRef) -> Option { locked_write(&self.cache) .remove_entry(path.as_ref()) .map(|(.., v)| v) @@ -293,7 +294,7 @@ impl FileModuleResolver { source: Option<&str>, path: &str, pos: Position, - ) -> Result, Box> { + ) -> Result> { // Load relative paths from source if there is no base path specified let source_path = global .as_ref() @@ -344,7 +345,7 @@ impl ModuleResolver for FileModuleResolver { global: &mut GlobalRuntimeState, path: &str, pos: Position, - ) -> RhaiResultOf> { + ) -> RhaiResultOf { self.impl_resolve(engine, Some(global), None, path, pos) } @@ -355,7 +356,7 @@ impl ModuleResolver for FileModuleResolver { source: Option<&str>, path: &str, pos: Position, - ) -> RhaiResultOf> { + ) -> RhaiResultOf { self.impl_resolve(engine, None, source, path, pos) } diff --git a/src/module/resolvers/mod.rs b/src/module/resolvers/mod.rs index a7f2d5c4..6316a845 100644 --- a/src/module/resolvers/mod.rs +++ b/src/module/resolvers/mod.rs @@ -1,6 +1,6 @@ use crate::eval::GlobalRuntimeState; use crate::func::SendSync; -use crate::{Engine, Module, Position, RhaiResultOf, Shared, AST}; +use crate::{Engine, Position, RhaiResultOf, SharedModule, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -25,7 +25,7 @@ pub trait ModuleResolver: SendSync { source: Option<&str>, path: &str, pos: Position, - ) -> RhaiResultOf>; + ) -> RhaiResultOf; /// Resolve a module based on a path string, given a [`GlobalRuntimeState`]. /// @@ -38,7 +38,7 @@ pub trait ModuleResolver: SendSync { global: &mut GlobalRuntimeState, path: &str, pos: Position, - ) -> RhaiResultOf> { + ) -> RhaiResultOf { self.resolve(engine, global.source(), path, pos) } diff --git a/src/module/resolvers/stat.rs b/src/module/resolvers/stat.rs index 8338a43a..bd2b2638 100644 --- a/src/module/resolvers/stat.rs +++ b/src/module/resolvers/stat.rs @@ -1,5 +1,6 @@ use crate::{ - Engine, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Shared, SmartString, ERR, + Engine, Identifier, Module, ModuleResolver, Position, RhaiResultOf, SharedModule, SmartString, + ERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -27,7 +28,7 @@ use std::{ /// engine.set_module_resolver(resolver); /// ``` #[derive(Debug, Clone, Default)] -pub struct StaticModuleResolver(BTreeMap>); +pub struct StaticModuleResolver(BTreeMap); impl StaticModuleResolver { /// Create a new [`StaticModuleResolver`]. @@ -65,7 +66,7 @@ impl StaticModuleResolver { } /// Remove a [module][Module] given its path. #[inline(always)] - pub fn remove(&mut self, path: &str) -> Option> { + pub fn remove(&mut self, path: &str) -> Option { self.0.remove(path) } /// Does the path exist? @@ -80,12 +81,12 @@ impl StaticModuleResolver { } /// Get an iterator of all the [modules][Module]. #[inline] - pub fn iter(&self) -> impl Iterator)> { + pub fn iter(&self) -> impl Iterator { self.0.iter().map(|(k, v)| (k.as_str(), v)) } /// Get a mutable iterator of all the [modules][Module]. #[inline] - pub fn iter_mut(&mut self) -> impl Iterator)> { + pub fn iter_mut(&mut self) -> impl Iterator { self.0.iter_mut().map(|(k, v)| (k.as_str(), v)) } /// Get an iterator of all the [module][Module] paths. @@ -95,7 +96,7 @@ impl StaticModuleResolver { } /// Get an iterator of all the [modules][Module]. #[inline(always)] - pub fn values(&self) -> impl Iterator> { + pub fn values(&self) -> impl Iterator { self.0.values() } /// Remove all [modules][Module]. @@ -130,8 +131,8 @@ impl StaticModuleResolver { } impl IntoIterator for StaticModuleResolver { - type Item = (Identifier, Shared); - type IntoIter = IntoIter>; + type Item = (Identifier, SharedModule); + type IntoIter = IntoIter; #[inline(always)] #[must_use] @@ -141,8 +142,8 @@ impl IntoIterator for StaticModuleResolver { } impl<'a> IntoIterator for &'a StaticModuleResolver { - type Item = (&'a Identifier, &'a Shared); - type IntoIter = Iter<'a, SmartString, Shared>; + type Item = (&'a Identifier, &'a SharedModule); + type IntoIter = Iter<'a, SmartString, SharedModule>; #[inline(always)] fn into_iter(self) -> Self::IntoIter { @@ -158,7 +159,7 @@ impl ModuleResolver for StaticModuleResolver { _: Option<&str>, path: &str, pos: Position, - ) -> RhaiResultOf> { + ) -> RhaiResultOf { self.0 .get(path) .cloned() diff --git a/src/optimizer.rs b/src/optimizer.rs index aa204777..b9804024 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -61,7 +61,7 @@ struct OptimizerState<'a> { caches: Caches, /// [Module][crate::Module] containing script-defined functions. #[cfg(not(feature = "no_function"))] - lib: &'a [crate::Shared], + lib: &'a [crate::SharedModule], /// Optimization level. optimization_level: OptimizationLevel, } @@ -71,7 +71,7 @@ impl<'a> OptimizerState<'a> { #[inline(always)] pub fn new( engine: &'a Engine, - #[cfg(not(feature = "no_function"))] lib: &'a [crate::Shared], + #[cfg(not(feature = "no_function"))] lib: &'a [crate::SharedModule], optimization_level: OptimizationLevel, ) -> Self { Self { @@ -1263,7 +1263,7 @@ fn optimize_top_level( statements: StmtBlockContainer, engine: &Engine, scope: &Scope, - #[cfg(not(feature = "no_function"))] lib: &[crate::Shared], + #[cfg(not(feature = "no_function"))] lib: &[crate::SharedModule], optimization_level: OptimizationLevel, ) -> StmtBlockContainer { let mut statements = statements; diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 499a1762..dfa3d8d1 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -1,6 +1,6 @@ //! Module containing all built-in _packages_ available to Rhai, plus facilities to define custom packages. -use crate::{Engine, Module, Shared}; +use crate::{Engine, Module, SharedModule}; pub(crate) mod arithmetic; pub(crate) mod array_basic; @@ -99,7 +99,7 @@ pub trait Package { /// Get a reference to a shared module from this package. #[must_use] - fn as_shared_module(&self) -> Shared; + fn as_shared_module(&self) -> SharedModule; } /// Macro that makes it easy to define a _package_ (which is basically a shared [module][Module]) diff --git a/src/parser.rs b/src/parser.rs index ee107089..fd315bad 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -8,7 +8,7 @@ use crate::ast::{ SwitchCasesCollection, TryCatchBlock, }; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; -use crate::eval::GlobalRuntimeState; +use crate::eval::{Caches, GlobalRuntimeState}; use crate::func::{hashing::get_hasher, StraightHashMap}; use crate::tokenizer::{ is_keyword_function, is_valid_function_name, is_valid_identifier, Token, TokenStream, @@ -2906,15 +2906,17 @@ impl Engine { nesting_level: level, will_shadow, }; - let mut this_ptr = None; + let caches = &mut Caches::new(); + let this_ptr = &mut None; + let context = EvalContext::new( self, &mut state.global, - None, + caches, &[], level, &mut state.stack, - &mut this_ptr, + this_ptr, ); match filter(false, info, context) { diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index b7ac5468..85355d4b 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -4,7 +4,7 @@ use crate::tokenizer::is_valid_function_name; use crate::types::dynamic::Variant; use crate::{ Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError, RhaiResult, - RhaiResultOf, StaticVec, AST, ERR, + RhaiResultOf, SharedModule, StaticVec, AST, ERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -150,9 +150,9 @@ impl FnPtr { let mut arg_values = crate::StaticVec::new_const(); args.parse(&mut arg_values); - let lib: &[crate::Shared] = &[ + let lib: &[SharedModule] = &[ #[cfg(not(feature = "no_function"))] - AsRef::>::as_ref(ast).clone(), + AsRef::::as_ref(ast).clone(), ]; let lib = if lib.first().map_or(true, |m| m.is_empty()) { &[][..] diff --git a/src/types/restore.rs b/src/types/restore.rs index d2e6cd87..6d0ad595 100644 --- a/src/types/restore.rs +++ b/src/types/restore.rs @@ -12,18 +12,26 @@ pub struct RestoreOnDrop<'a, T, R: FnOnce(&mut T)> { } impl<'a, T, R: FnOnce(&mut T)> RestoreOnDrop<'a, T, R> { - /// Create a new [`RestoreOnDrop`] that runs restoration logic at the end of scope only when - /// `need_restore` is `true`. + /// Create a new [`RestoreOnDrop`] that locks a mutable reference and runs restoration logic at + /// the end of scope only when `need_restore` is `true`. + /// + /// Beware that the end of scope means the end of its lifetime, not necessarily waiting until + /// the current block scope is exited. #[inline(always)] - pub fn new_if(need_restore: bool, value: &'a mut T, restore: R) -> Self { + pub fn lock_if(need_restore: bool, value: &'a mut T, restore: R) -> Self { Self { value, restore: if need_restore { Some(restore) } else { None }, } } - /// Create a new [`RestoreOnDrop`] that runs restoration logic at the end of scope. + + /// Create a new [`RestoreOnDrop`] that locks a mutable reference and runs restoration logic at + /// the end of scope. + /// + /// Beware that the end of scope means the end of its lifetime, not necessarily waiting until + /// the current block scope is exited. #[inline(always)] - pub fn new(value: &'a mut T, restore: R) -> Self { + pub fn lock(value: &'a mut T, restore: R) -> Self { Self { value, restore: Some(restore), From 8f128f37f0dc6f702a81966e68fa049a32571207 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 8 Nov 2022 16:16:42 +0800 Subject: [PATCH 6/9] Add Dynamic::NULL to simplify this pointer binding. --- src/api/call_fn.rs | 4 +++- src/api/eval.rs | 4 +++- src/api/run.rs | 3 ++- src/eval/chaining.rs | 9 +++++---- src/eval/debugger.rs | 8 ++++---- src/eval/eval_context.rs | 30 +++++++++++++++++++----------- src/eval/expr.rs | 24 +++++++++++++----------- src/eval/stmt.rs | 8 +++++--- src/func/call.rs | 33 ++++++++++++++------------------- src/func/script.rs | 2 +- src/optimizer.rs | 33 ++++++++++++++------------------- src/parser.rs | 4 ++-- src/serde/de.rs | 2 ++ src/serde/serialize.rs | 2 ++ src/types/dynamic.rs | 37 +++++++++++++++++++++++++++++++++++++ unreachable!() | 0 16 files changed, 126 insertions(+), 77 deletions(-) create mode 100644 unreachable!() diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index b467e54d..42f418e4 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -250,7 +250,9 @@ impl Engine { ) -> RhaiResult { let statements = ast.statements(); let lib = &[AsRef::::as_ref(ast).clone()]; - let mut this_ptr = this_ptr; + + let mut no_this_ptr = Dynamic::NULL; + let mut this_ptr = this_ptr.unwrap_or(&mut no_this_ptr); let orig_scope_len = scope.len(); diff --git a/src/api/eval.rs b/src/api/eval.rs index 28bcdcbd..8770996d 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -198,8 +198,10 @@ impl Engine { #[cfg(not(feature = "no_function"))] AsRef::::as_ref(ast).clone(), ]; + let mut this = Dynamic::NULL; let node = &crate::ast::Stmt::Noop(Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; + + self.run_debugger(global, caches, lib, 0, scope, &mut this, node)?; } let typ = self.map_type_name(result.type_name()); diff --git a/src/api/run.rs b/src/api/run.rs index 90c6abf2..f1c25bb4 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -141,8 +141,9 @@ impl Engine { #[cfg(not(feature = "no_function"))] AsRef::::as_ref(ast).clone(), ]; + let mut this = crate::Dynamic::NULL; let node = &crate::ast::Stmt::Noop(crate::Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut None, node)?; + self.run_debugger(global, caches, lib, 0, scope, &mut this, node)?; } Ok(()) diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 021dfdc3..a37fd16e 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -45,7 +45,7 @@ impl Engine { caches: &mut Caches, lib: &[SharedModule], level: usize, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, target: &mut Target, root: (&str, Position), _parent: &Expr, @@ -573,7 +573,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, expr: &Expr, new_val: &mut Option<(Dynamic, &OpAssignment)>, ) -> RhaiResult { @@ -630,9 +630,10 @@ impl Engine { let obj_ptr = &mut target; let root = (x.3.as_str(), *var_pos); + let mut this = Dynamic::NULL; self.eval_dot_index_chain_helper( - global, caches, lib, level, &mut None, obj_ptr, root, expr, options, rhs, + global, caches, lib, level, &mut this, obj_ptr, root, expr, options, rhs, idx_values, chain_type, new_val, ) } @@ -664,7 +665,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, expr: &Expr, parent_options: ASTFlags, parent_chain_type: ChainType, diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 8c2d94e5..124b3547 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -416,7 +416,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf<()> { if self.debugger.is_some() { @@ -443,7 +443,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf> { if self.debugger.is_some() { @@ -466,7 +466,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf> { let node = node.into(); @@ -513,7 +513,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, node: ASTNode<'a>, event: DebuggerEvent, ) -> Result, Box> { diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index dc50a63c..ed5d6193 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -8,7 +8,7 @@ use std::prelude::v1::*; /// Context of a script evaluation process. #[derive(Debug)] #[allow(dead_code)] -pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { +pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> { /// The current [`Engine`]. engine: &'a Engine, /// The current [`Scope`]. @@ -20,12 +20,12 @@ pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { /// The current stack of imported [modules][Module]. lib: &'a [SharedModule], /// The current bound `this` pointer, if any. - this_ptr: &'t mut Option<&'pt mut Dynamic>, + this_ptr: &'t mut Dynamic, /// The current nesting level of function calls. level: usize, } -impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { +impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { /// Create a new [`EvalContext`]. #[inline(always)] #[must_use] @@ -36,7 +36,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { lib: &'a [SharedModule], level: usize, scope: &'s mut Scope<'ps>, - this_ptr: &'t mut Option<&'pt mut Dynamic>, + this_ptr: &'t mut Dynamic, ) -> Self { Self { engine, @@ -104,8 +104,8 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub fn global_runtime_state_mut(&mut self) -> &mut &'g mut GlobalRuntimeState { - &mut self.global + pub fn global_runtime_state_mut(&mut self) -> &mut GlobalRuntimeState { + self.global } /// Get an iterator over the namespaces containing definition of all script-defined functions. #[inline] @@ -121,16 +121,24 @@ impl<'a, 's, 'ps, 'g, 'c, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'c, 't, 'pt> { self.lib } /// The current bound `this` pointer, if any. - #[inline(always)] + #[inline] #[must_use] pub fn this_ptr(&self) -> Option<&Dynamic> { - self.this_ptr.as_ref().map(|v| &**v) + if self.this_ptr.is_null() { + None + } else { + Some(self.this_ptr) + } } /// Mutable reference to the current bound `this` pointer, if any. - #[inline(always)] + #[inline] #[must_use] - pub fn this_ptr_mut(&mut self) -> &mut Option<&'pt mut Dynamic> { - self.this_ptr + pub fn this_ptr_mut(&mut self) -> Option<&mut Dynamic> { + if self.this_ptr.is_null() { + None + } else { + Some(self.this_ptr) + } } /// The current nesting level of function calls. #[inline(always)] diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 89f0d4a7..55467124 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -54,7 +54,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &'s mut Scope, - this_ptr: &'s mut Option<&mut Dynamic>, + this_ptr: &'s mut Dynamic, expr: &Expr, ) -> RhaiResultOf<(Target<'s>, Position)> { match expr { @@ -140,7 +140,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &'s mut Scope, - this_ptr: &'s mut Option<&mut Dynamic>, + this_ptr: &'s mut Dynamic, expr: &Expr, ) -> RhaiResultOf<(Target<'s>, Position)> { // Make sure that the pointer indirection is taken only when absolutely necessary. @@ -148,10 +148,11 @@ impl Engine { let (index, var_pos) = match expr { // Check if the variable is `this` Expr::Variable(v, None, pos) if v.0.is_none() && v.3 == KEYWORD_THIS => { - return this_ptr.as_mut().map_or_else( - || Err(ERR::ErrorUnboundThis(*pos).into()), - |val| Ok(((*val).into(), *pos)), - ) + return if this_ptr.is_null() { + Err(ERR::ErrorUnboundThis(*pos).into()) + } else { + Ok((this_ptr.into(), *pos)) + }; } _ if global.always_search_scope => (0, expr.start_position()), Expr::Variable(.., Some(i), pos) => (i.get() as usize, *pos), @@ -224,7 +225,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, expr: &Expr, ) -> RhaiResult { // Coded this way for better branch prediction. @@ -256,10 +257,11 @@ impl Engine { self.track_operation(global, expr.position())?; return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS { - this_ptr - .as_deref() - .cloned() - .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()) + if this_ptr.is_null() { + ERR::ErrorUnboundThis(*var_pos).into() + } else { + Ok(this_ptr.clone()) + } } else { self.search_namespace(global, caches, lib, level, scope, this_ptr, expr) .map(|(val, ..)| val.take_or_clone()) diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 1e232ddc..0f064711 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -29,7 +29,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, statements: &[Stmt], restore_orig_state: bool, ) -> RhaiResult { @@ -203,7 +203,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, stmt: &Stmt, rewind_scope: bool, ) -> RhaiResult { @@ -912,8 +912,10 @@ impl Engine { scope: &mut Scope, statements: &[Stmt], ) -> RhaiResult { + let mut this = Dynamic::NULL; + self.eval_stmt_block( - global, caches, lib, level, scope, &mut None, statements, false, + global, caches, lib, level, scope, &mut this, statements, false, ) .or_else(|err| match *err { ERR::Return(out, ..) => Ok(out), diff --git a/src/func/call.rs b/src/func/call.rs index d7f0102d..8bc4cfe3 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -11,8 +11,8 @@ use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::tokenizer::{is_valid_function_name, Token}; use crate::types::RestoreOnDrop; use crate::{ - calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, SharedModule, - OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, ERR, + calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, + OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR, }; #[cfg(feature = "no_std")] use hashbrown::hash_map::Entry; @@ -407,6 +407,7 @@ impl Engine { }; if trigger { let scope = &mut &mut Scope::new(); + let mut this = Dynamic::NULL; let node = crate::ast::Stmt::Noop(pos); let node = (&node).into(); let event = match _result { @@ -415,7 +416,7 @@ impl Engine { }; if let Err(err) = self - .run_debugger_raw(global, caches, lib, level, scope, &mut None, node, event) + .run_debugger_raw(global, caches, lib, level, scope, &mut this, node, event) { _result = Err(err); } @@ -646,16 +647,7 @@ impl Engine { let (first_arg, rest_args) = args.split_first_mut().unwrap(); self.call_script_fn( - global, - caches, - lib, - level, - scope, - &mut Some(*first_arg), - func, - rest_args, - true, - pos, + global, caches, lib, level, scope, first_arg, func, rest_args, true, pos, ) } else { // Normal call of script function @@ -672,8 +664,10 @@ impl Engine { backup.restore_first_arg(a) }); + let mut this = Dynamic::NULL; + self.call_script_fn( - global, caches, lib, level, scope, &mut None, func, args, true, pos, + global, caches, lib, level, scope, &mut this, func, args, true, pos, ) } .map(|r| (r, false)); @@ -697,7 +691,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, arg_expr: &Expr, ) -> RhaiResultOf<(Dynamic, Position)> { // Literal values @@ -956,7 +950,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, fn_name: &str, op_token: Option<&Token>, first_arg: Option<&Expr>, @@ -1247,7 +1241,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, namespace: &crate::ast::Namespace, fn_name: &str, args_expr: &[Expr], @@ -1376,12 +1370,13 @@ impl Engine { Some(f) if f.is_script() => { let fn_def = f.get_script_fn_def().expect("script-defined function"); let new_scope = &mut Scope::new(); + let mut this = Dynamic::NULL; let orig_source = mem::replace(&mut global.source, module.id_raw().cloned()); let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source); self.call_script_fn( - global, caches, lib, level, new_scope, &mut None, fn_def, &mut args, true, pos, + global, caches, lib, level, new_scope, &mut this, fn_def, &mut args, true, pos, ) } @@ -1474,7 +1469,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, expr: &FnCallExpr, pos: Position, ) -> RhaiResult { diff --git a/src/func/script.rs b/src/func/script.rs index f8553165..ae4a0f77 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -29,7 +29,7 @@ impl Engine { lib: &[SharedModule], level: usize, scope: &mut Scope, - this_ptr: &mut Option<&mut Dynamic>, + this_ptr: &mut Dynamic, fn_def: &ScriptFnDef, args: &mut FnCallArgs, rewind_scope: bool, diff --git a/src/optimizer.rs b/src/optimizer.rs index b9804024..7ad765c1 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -50,7 +50,7 @@ struct OptimizerState<'a> { /// Has the [`AST`] been changed during this pass? changed: bool, /// Collection of constants to use for eager function evaluations. - variables: StaticVec<(Identifier, AccessMode, Option)>, + variables: StaticVec<(Identifier, AccessMode, Dynamic)>, /// Activate constants propagation? propagate_constants: bool, /// An [`Engine`] instance for eager function evaluation. @@ -108,12 +108,7 @@ impl<'a> OptimizerState<'a> { } /// Add a new variable to the list. #[inline(always)] - pub fn push_var( - &mut self, - name: impl Into, - access: AccessMode, - value: Option, - ) { + pub fn push_var(&mut self, name: impl Into, access: AccessMode, value: Dynamic) { self.variables.push((name.into(), access, value)); } /// Look up a constant from the list. @@ -127,7 +122,8 @@ impl<'a> OptimizerState<'a> { if n == name { return match access { AccessMode::ReadWrite => None, - AccessMode::ReadOnly => value.as_ref(), + AccessMode::ReadOnly if value.is_null() => None, + AccessMode::ReadOnly => Some(value), }; } } @@ -141,7 +137,7 @@ impl<'a> OptimizerState<'a> { fn_name: &str, op_token: Option<&Token>, arg_values: &mut [Dynamic], - ) -> Option { + ) -> Dynamic { #[cfg(not(feature = "no_function"))] let lib = self.lib; #[cfg(feature = "no_function")] @@ -160,8 +156,7 @@ impl<'a> OptimizerState<'a> { false, Position::NONE, ) - .ok() - .map(|(v, ..)| v) + .map_or(Dynamic::NULL, |(v, ..)| v) } } @@ -271,13 +266,13 @@ fn optimize_stmt_block( state.push_var( x.0.as_str(), AccessMode::ReadOnly, - x.1.get_literal_value(), + x.1.get_literal_value().unwrap_or(Dynamic::NULL), ); } } else { // Add variables into the state optimize_expr(&mut x.1, state, false); - state.push_var(x.0.as_str(), AccessMode::ReadWrite, None); + state.push_var(x.0.as_str(), AccessMode::ReadWrite, Dynamic::NULL); } } // Optimize the statement @@ -1197,13 +1192,13 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { let arg_values = &mut x.args.iter().map(Expr::get_literal_value).collect::>>().unwrap(); let result = match x.name.as_str() { - KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()), + KEYWORD_TYPE_OF if arg_values.len() == 1 => state.engine.map_type_name(arg_values[0].type_name()).into(), #[cfg(not(feature = "no_closure"))] - crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE), + crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Dynamic::FALSE, _ => state.call_fn_with_constant_arguments(&x.name, x.op_token.as_ref(), arg_values) }; - if let Some(result) = result { + if !result.is_null() { state.set_dirty(); *expr = Expr::from_dynamic(result, *pos); return; @@ -1289,15 +1284,15 @@ fn optimize_top_level( .rev() .flat_map(|m| m.iter_var()) { - state.push_var(name, AccessMode::ReadOnly, Some(value.clone())); + state.push_var(name, AccessMode::ReadOnly, value.clone()); } // Add constants and variables from the scope for (name, constant, value) in scope.iter() { if constant { - state.push_var(name, AccessMode::ReadOnly, Some(value)); + state.push_var(name, AccessMode::ReadOnly, value); } else { - state.push_var(name, AccessMode::ReadWrite, None); + state.push_var(name, AccessMode::ReadWrite, Dynamic::NULL); } } diff --git a/src/parser.rs b/src/parser.rs index fd315bad..03d5b16b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2907,7 +2907,7 @@ impl Engine { will_shadow, }; let caches = &mut Caches::new(); - let this_ptr = &mut None; + let mut this = Dynamic::NULL; let context = EvalContext::new( self, @@ -2916,7 +2916,7 @@ impl Engine { &[], level, &mut state.stack, - this_ptr, + &mut this, ); match filter(false, info, context) { diff --git a/src/serde/de.rs b/src/serde/de.rs index fcb26a9d..42d4739f 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -125,6 +125,8 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { fn deserialize_any>(self, visitor: V) -> RhaiResultOf { match self.0 .0 { + Union::Null => unreachable!(), + Union::Unit(..) => self.deserialize_unit(visitor), Union::Bool(..) => self.deserialize_bool(visitor), Union::Str(..) => self.deserialize_str(visitor), diff --git a/src/serde/serialize.rs b/src/serde/serialize.rs index 424de0d5..0e9c481f 100644 --- a/src/serde/serialize.rs +++ b/src/serde/serialize.rs @@ -15,6 +15,8 @@ use crate::types::dynamic::Variant; impl Serialize for Dynamic { fn serialize(&self, ser: S) -> Result { match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => ser.serialize_unit(), Union::Bool(x, ..) => ser.serialize_bool(x), Union::Str(ref s, ..) => ser.serialize_str(s.as_str()), diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index c53b8ea3..eeee966a 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -55,6 +55,9 @@ pub struct Dynamic(pub(crate) Union); /// /// Most variants are boxed to reduce the size. pub enum Union { + /// An error value which should not exist. + Null, + /// The Unit value - (). Unit((), Tag, AccessMode), /// A boolean value. @@ -178,6 +181,8 @@ impl Dynamic { #[must_use] pub const fn tag(&self) -> Tag { match self.0 { + Union::Null => unreachable!(), + Union::Unit(_, tag, _) | Union::Bool(_, tag, _) | Union::Str(_, tag, _) @@ -203,6 +208,8 @@ impl Dynamic { /// Attach arbitrary data to this [`Dynamic`]. pub fn set_tag(&mut self, value: Tag) -> &mut Self { match self.0 { + Union::Null => unreachable!(), + Union::Unit(_, ref mut tag, _) | Union::Bool(_, ref mut tag, _) | Union::Str(_, ref mut tag, _) @@ -226,6 +233,12 @@ impl Dynamic { } self } + /// Is this [`Dynamic`] null? + #[inline(always)] + #[must_use] + pub(crate) const fn is_null(&self) -> bool { + matches!(self.0, Union::Null) + } /// Does this [`Dynamic`] hold a variant data type instead of one of the supported system /// primitive types? #[inline(always)] @@ -307,6 +320,8 @@ impl Dynamic { #[must_use] pub fn type_id(&self) -> TypeId { match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => TypeId::of::<()>(), Union::Bool(..) => TypeId::of::(), Union::Str(..) => TypeId::of::(), @@ -341,6 +356,8 @@ impl Dynamic { #[must_use] pub fn type_name(&self) -> &'static str { match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => "()", Union::Bool(..) => "bool", Union::Str(..) => "string", @@ -385,6 +402,8 @@ impl Hash for Dynamic { mem::discriminant(&self.0).hash(state); match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => (), Union::Bool(ref b, ..) => b.hash(state), Union::Str(ref s, ..) => s.hash(state), @@ -416,6 +435,8 @@ impl Hash for Dynamic { impl fmt::Display for Dynamic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { + Union::Null => unreachable!(), + Union::Unit(..) => Ok(()), Union::Bool(ref v, ..) => fmt::Display::fmt(v, f), Union::Str(ref v, ..) => fmt::Display::fmt(v, f), @@ -509,6 +530,8 @@ impl fmt::Debug for Dynamic { #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { + Union::Null => unreachable!(), + Union::Unit(ref v, ..) => fmt::Debug::fmt(v, f), Union::Bool(ref v, ..) => fmt::Debug::fmt(v, f), Union::Str(ref v, ..) => fmt::Debug::fmt(v, f), @@ -619,6 +642,8 @@ impl Clone for Dynamic { /// The cloned copy is marked read-write even if the original is read-only. fn clone(&self) -> Self { match self.0 { + Union::Null => unreachable!(), + Union::Unit(v, tag, ..) => Self(Union::Unit(v, tag, ReadWrite)), Union::Bool(v, tag, ..) => Self(Union::Bool(v, tag, ReadWrite)), Union::Str(ref v, tag, ..) => Self(Union::Str(v.clone(), tag, ReadWrite)), @@ -666,6 +691,9 @@ use std::f32::consts as FloatConstants; use std::f64::consts as FloatConstants; impl Dynamic { + /// A [`Dynamic`] containing a `null`. + pub(crate) const NULL: Self = Self(Union::Null); + /// A [`Dynamic`] containing a `()`. pub const UNIT: Self = Self(Union::Unit((), DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing a `true`. @@ -888,6 +916,8 @@ impl Dynamic { #[must_use] pub(crate) const fn access_mode(&self) -> AccessMode { match self.0 { + Union::Null => unreachable!(), + Union::Unit(.., access) | Union::Bool(.., access) | Union::Str(.., access) @@ -913,6 +943,8 @@ impl Dynamic { /// Set the [`AccessMode`] for this [`Dynamic`]. pub(crate) fn set_access_mode(&mut self, typ: AccessMode) -> &mut Self { match self.0 { + Union::Null => unreachable!(), + Union::Unit(.., ref mut access) | Union::Bool(.., ref mut access) | Union::Str(.., ref mut access) @@ -1107,6 +1139,7 @@ impl Dynamic { let _access = self.access_mode(); match self.0 { + Union::Null => unreachable!(), Union::Shared(..) => self, _ => Self(Union::Shared( crate::Locked::new(self).into(), @@ -1151,6 +1184,8 @@ impl Dynamic { reify!(self, |v: T| return Some(v)); match self.0 { + Union::Null => unreachable!(), + Union::Int(v, ..) => reify!(v => Option), #[cfg(not(feature = "no_float"))] Union::Float(v, ..) => reify!(*v => Option), @@ -1485,6 +1520,7 @@ impl Dynamic { } match self.0 { + Union::Null => unreachable!(), Union::Variant(ref v, ..) => (***v).as_any().downcast_ref::(), #[cfg(not(feature = "no_closure"))] Union::Shared(..) => None, @@ -1583,6 +1619,7 @@ impl Dynamic { } match self.0 { + Union::Null => unreachable!(), Union::Variant(ref mut v, ..) => (***v).as_any_mut().downcast_mut::(), #[cfg(not(feature = "no_closure"))] Union::Shared(..) => None, diff --git a/unreachable!() b/unreachable!() new file mode 100644 index 00000000..e69de29b From 5bae4d8a19f396038b84fcbcc0741db9e30c45a3 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 8 Nov 2022 16:37:35 +0800 Subject: [PATCH 7/9] Fix builds. --- src/api/register.rs | 2 +- src/ast/ast.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/register.rs b/src/api/register.rs index 9e2f7490..a335faa1 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -662,7 +662,7 @@ impl Engine { /// let mut module = Module::new(); /// module.set_native_fn("calc", |x: i64| Ok(x + 1)); /// - /// let module: SharedModule = module.into(); + /// let module: Shared = module.into(); /// /// engine /// // Register the module as a fixed sub-module diff --git a/src/ast/ast.rs b/src/ast/ast.rs index 76277e7b..c1cfbaed 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -267,7 +267,7 @@ impl AST { #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] - pub(crate) const fn shared_lib(&self) -> &SharedModule { + pub(crate) const fn shared_lib(&self) -> &crate::SharedModule { &self.lib } /// _(internals)_ Get the internal shared [`Module`][crate::Module] containing all script-defined functions. From e93923b3b6a41c47554d2a3759aa5519c0ccc0bd Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 8 Nov 2022 21:28:20 +0800 Subject: [PATCH 8/9] Move level into GlobalRuntimeState. --- CHANGELOG.md | 11 +- src/api/call_fn.rs | 5 +- src/api/deprecated.rs | 22 +++- src/api/eval.rs | 12 +- src/api/run.rs | 4 +- src/eval/chaining.rs | 185 ++++++++++++--------------- src/eval/debugger.rs | 18 ++- src/eval/eval_context.rs | 8 +- src/eval/expr.rs | 68 +++++----- src/eval/global_state.rs | 3 + src/eval/stmt.rs | 254 ++++++++++++++++++-------------------- src/func/call.rs | 109 ++++++++-------- src/func/native.rs | 74 ++--------- src/func/script.rs | 12 +- src/module/mod.rs | 2 +- src/optimizer.rs | 3 +- src/packages/debugging.rs | 90 ++++++-------- src/parser.rs | 5 +- src/tests.rs | 4 +- src/types/fn_ptr.rs | 13 +- unreachable!() | 0 21 files changed, 409 insertions(+), 493 deletions(-) delete mode 100644 unreachable!() diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a606c13..4f785f8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Rhai Release Notes Version 1.11.0 ============== +Speed Improvements +------------------ + +* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%. + Bug fixes --------- @@ -12,10 +17,10 @@ Bug fixes * Functions marked `global` in `import`ed modules with no alias names now work properly. * Incorrect loop optimizations that are too aggressive (e.g. unrolling a `do { ... } until true` with a `break` statement inside) and cause crashes are removed. -Speed Improvements ------------------- +Breaking changes +---------------- -* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%. +* `NativeCallContext::new` is completely deprecated and unimplemented (always panics) in favor of new API's. New features ------------ diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 42f418e4..6ba9fcf3 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -267,7 +267,7 @@ impl Engine { }); let result = if eval_ast && !statements.is_empty() { - let r = self.eval_global_statements(global, caches, lib, 0, scope, statements); + let r = self.eval_global_statements(global, caches, lib, scope, statements); if rewind_scope { scope.rewind(orig_scope_len); @@ -289,7 +289,6 @@ impl Engine { global, caches, lib, - 0, scope, &mut this_ptr, fn_def, @@ -306,7 +305,7 @@ impl Engine { if self.debugger.is_some() { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let node = &crate::ast::Stmt::Noop(Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut this_ptr, node)?; + self.run_debugger(global, caches, lib, scope, &mut this_ptr, node)?; } Ok(result) diff --git a/src/api/deprecated.rs b/src/api/deprecated.rs index 6b6ad977..cda495db 100644 --- a/src/api/deprecated.rs +++ b/src/api/deprecated.rs @@ -4,7 +4,7 @@ use crate::func::RegisterNativeFunction; use crate::types::dynamic::Variant; use crate::{ Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, NativeCallContext, - Position, RhaiResult, RhaiResultOf, Scope, AST, + Position, RhaiResult, RhaiResultOf, Scope, SharedModule, AST, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -354,6 +354,26 @@ impl Dynamic { } impl NativeCallContext<'_> { + /// Create a new [`NativeCallContext`]. + /// + /// # Unimplemented + /// + /// This method is deprecated. It is no longer implemented and always panics. + /// + /// Use [`FnPtr::call`] to call a function pointer directly. + /// + /// This method will be removed in the next major version. + #[deprecated( + since = "1.3.0", + note = "use `FnPtr::call` to call a function pointer directly." + )] + #[inline(always)] + #[must_use] + #[allow(unused_variables)] + pub fn new(engine: &Engine, fn_name: &str, lib: &[SharedModule]) -> Self { + unimplemented!("`NativeCallContext::new` is deprecated"); + } + /// Call a function inside the call context. /// /// # Deprecated diff --git a/src/api/eval.rs b/src/api/eval.rs index 8770996d..829c69b0 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -189,7 +189,7 @@ impl Engine { let global = &mut GlobalRuntimeState::new(self); let caches = &mut Caches::new(); - let result = self.eval_ast_with_scope_raw(global, caches, 0, scope, ast)?; + let result = self.eval_ast_with_scope_raw(global, caches, scope, ast)?; #[cfg(feature = "debugging")] if self.debugger.is_some() { @@ -201,7 +201,7 @@ impl Engine { let mut this = Dynamic::NULL; let node = &crate::ast::Stmt::Noop(Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut this, node)?; + self.run_debugger(global, caches, lib, scope, &mut this, node)?; } let typ = self.map_type_name(result.type_name()); @@ -217,7 +217,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - level: usize, + scope: &mut Scope, ast: &'a AST, ) -> RhaiResult { @@ -244,7 +244,7 @@ impl Engine { AsRef::::as_ref(ast).clone(), ]; - self.eval_global_statements(global, caches, lib, level, scope, statements) + self.eval_global_statements(global, caches, lib, scope, statements) } /// _(internals)_ Evaluate a list of statements with no `this` pointer. /// Exported under the `internals` feature only. @@ -261,11 +261,11 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[crate::SharedModule], - level: usize, + scope: &mut Scope, statements: &[crate::ast::Stmt], ) -> RhaiResult { - self.eval_global_statements(global, caches, lib, level, scope, statements) + self.eval_global_statements(global, caches, lib, scope, statements) } } diff --git a/src/api/run.rs b/src/api/run.rs index f1c25bb4..551b5e98 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -131,7 +131,7 @@ impl Engine { } else { &lib }; - self.eval_global_statements(global, caches, lib, 0, scope, statements)?; + self.eval_global_statements(global, caches, lib, scope, statements)?; } #[cfg(feature = "debugging")] @@ -143,7 +143,7 @@ impl Engine { ]; let mut this = crate::Dynamic::NULL; let node = &crate::ast::Stmt::Noop(crate::Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut this, node)?; + self.run_debugger(global, caches, lib, scope, &mut this, node)?; } Ok(()) diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index a37fd16e..ca813d86 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -44,7 +44,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, this_ptr: &mut Dynamic, target: &mut Target, root: (&str, Position), @@ -76,7 +75,7 @@ impl Engine { if !parent_options.contains(ASTFlags::BREAK) => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, _parent)?; + self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?; let idx_val = &mut idx_values.pop().unwrap(); let mut idx_val_for_setter = idx_val.clone(); @@ -85,13 +84,13 @@ impl Engine { let (try_setter, result) = { let mut obj = self.get_indexed_mut( - global, caches, lib, level, target, idx_val, idx_pos, false, true, + global, caches, lib, target, idx_val, idx_pos, false, true, )?; let is_obj_temp_val = obj.is_temp_value(); let obj_ptr = &mut obj; match self.eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, obj_ptr, root, rhs, *options, + global, caches, lib, this_ptr, obj_ptr, root, rhs, *options, &x.rhs, idx_values, rhs_chain, new_val, ) { Ok((result, true)) if is_obj_temp_val => { @@ -107,7 +106,7 @@ impl Engine { let idx = &mut idx_val_for_setter; let new_val = &mut new_val; self.call_indexer_set( - global, caches, lib, level, target, idx, new_val, is_ref_mut, + global, caches, lib, target, idx, new_val, is_ref_mut, ) .or_else(|e| match *e { ERR::ErrorIndexingType(..) => Ok((Dynamic::UNIT, false)), @@ -120,19 +119,19 @@ impl Engine { // xxx[rhs] op= new_val _ if new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, _parent)?; + self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?; let (new_val, op_info) = new_val.take().expect("`Some`"); let idx_val = &mut idx_values.pop().unwrap(); let idx = &mut idx_val.clone(); - let try_setter = match self.get_indexed_mut( - global, caches, lib, level, target, idx, pos, true, false, - ) { + let try_setter = match self + .get_indexed_mut(global, caches, lib, target, idx, pos, true, false) + { // Indexed value is not a temp value - update directly Ok(ref mut obj_ptr) => { self.eval_op_assignment( - global, caches, lib, level, op_info, obj_ptr, root, new_val, + global, caches, lib, op_info, obj_ptr, root, new_val, )?; self.check_data_size(obj_ptr, op_info.pos)?; None @@ -151,13 +150,12 @@ impl Engine { // Call the index getter to get the current value if let Ok(val) = - self.call_indexer_get(global, caches, lib, level, target, idx) + self.call_indexer_get(global, caches, lib, target, idx) { let mut val = val.into(); // Run the op-assignment self.eval_op_assignment( - global, caches, lib, level, op_info, &mut val, root, - new_val, + global, caches, lib, op_info, &mut val, root, new_val, )?; // Replace new value new_val = val.take_or_clone(); @@ -169,7 +167,7 @@ impl Engine { let new_val = &mut new_val; self.call_indexer_set( - global, caches, lib, level, target, idx_val, new_val, is_ref_mut, + global, caches, lib, target, idx_val, new_val, is_ref_mut, )?; } @@ -178,14 +176,12 @@ impl Engine { // xxx[rhs] _ => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, _parent)?; + self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?; let idx_val = &mut idx_values.pop().unwrap(); - self.get_indexed_mut( - global, caches, lib, level, target, idx_val, pos, false, true, - ) - .map(|v| (v.take_or_clone(), false)) + self.get_indexed_mut(global, caches, lib, target, idx_val, pos, false, true) + .map(|v| (v.take_or_clone(), false)) } } } @@ -201,9 +197,8 @@ impl Engine { // xxx.fn_name(arg_expr_list) Expr::MethodCall(x, pos) if !x.is_qualified() && new_val.is_none() => { #[cfg(feature = "debugging")] - let reset = self.run_debugger_with_reset( - global, caches, lib, level, scope, this_ptr, rhs, - )?; + let reset = self + .run_debugger_with_reset(global, caches, lib, scope, this_ptr, rhs)?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) @@ -222,8 +217,7 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); self.make_method_call( - global, caches, lib, level, name, *hashes, target, call_args, pos1, - *pos, + global, caches, lib, name, *hashes, target, call_args, pos1, *pos, ) } // xxx.fn_name(...) = ??? @@ -237,16 +231,16 @@ impl Engine { // {xxx:map}.id op= ??? Expr::Property(x, pos) if target.is::() && new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; let index = &mut x.2.clone().into(); let (new_val, op_info) = new_val.take().expect("`Some`"); { let val_target = &mut self.get_indexed_mut( - global, caches, lib, level, target, index, *pos, true, false, + global, caches, lib, target, index, *pos, true, false, )?; self.eval_op_assignment( - global, caches, lib, level, op_info, val_target, root, new_val, + global, caches, lib, op_info, val_target, root, new_val, )?; } self.check_data_size(target.source(), op_info.pos)?; @@ -255,18 +249,18 @@ impl Engine { // {xxx:map}.id Expr::Property(x, pos) if target.is::() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; let index = &mut x.2.clone().into(); let val = self.get_indexed_mut( - global, caches, lib, level, target, index, *pos, false, false, + global, caches, lib, target, index, *pos, false, false, )?; Ok((val.take_or_clone(), false)) } // xxx.id op= ??? Expr::Property(x, pos) if new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; let ((getter, hash_get), (setter, hash_set), name) = &**x; let (mut new_val, op_info) = new_val.take().expect("`Some`"); @@ -275,15 +269,15 @@ impl Engine { let args = &mut [target.as_mut()]; let (mut orig_val, ..) = self .exec_native_fn_call( - global, caches, lib, level, getter, None, *hash_get, args, - is_ref_mut, *pos, + global, caches, lib, getter, None, *hash_get, args, is_ref_mut, + *pos, ) .or_else(|err| match *err { // Try an indexer if property does not exist ERR::ErrorDotExpr(..) => { let mut prop = name.into(); self.call_indexer_get( - global, caches, lib, level, target, &mut prop, + global, caches, lib, target, &mut prop, ) .map(|r| (r, false)) .map_err(|e| { @@ -300,7 +294,7 @@ impl Engine { let orig_val = &mut (&mut orig_val).into(); self.eval_op_assignment( - global, caches, lib, level, op_info, orig_val, root, new_val, + global, caches, lib, op_info, orig_val, root, new_val, )?; } @@ -309,8 +303,7 @@ impl Engine { let args = &mut [target.as_mut(), &mut new_val]; self.exec_native_fn_call( - global, caches, lib, level, setter, None, *hash_set, args, is_ref_mut, - *pos, + global, caches, lib, setter, None, *hash_set, args, is_ref_mut, *pos, ) .or_else(|err| match *err { // Try an indexer if property does not exist @@ -318,7 +311,7 @@ impl Engine { let idx = &mut name.into(); let new_val = &mut new_val; self.call_indexer_set( - global, caches, lib, level, target, idx, new_val, is_ref_mut, + global, caches, lib, target, idx, new_val, is_ref_mut, ) .map_err(|e| match *e { ERR::ErrorIndexingType(..) => err, @@ -331,27 +324,24 @@ impl Engine { // xxx.id Expr::Property(x, pos) => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; let ((getter, hash_get), _, name) = &**x; let args = &mut [target.as_mut()]; self.exec_native_fn_call( - global, caches, lib, level, getter, None, *hash_get, args, is_ref_mut, - *pos, + global, caches, lib, getter, None, *hash_get, args, is_ref_mut, *pos, ) .map_or_else( |err| match *err { // Try an indexer if property does not exist ERR::ErrorDotExpr(..) => { let mut prop = name.into(); - self.call_indexer_get( - global, caches, lib, level, target, &mut prop, - ) - .map(|r| (r, false)) - .map_err(|e| match *e { - ERR::ErrorIndexingType(..) => err, - _ => e, - }) + self.call_indexer_get(global, caches, lib, target, &mut prop) + .map(|r| (r, false)) + .map_err(|e| match *e { + ERR::ErrorIndexingType(..) => err, + _ => e, + }) } _ => Err(err), }, @@ -368,20 +358,18 @@ impl Engine { let val_target = &mut match x.lhs { Expr::Property(ref p, pos) => { #[cfg(feature = "debugging")] - self.run_debugger( - global, caches, lib, level, scope, this_ptr, _node, - )?; + self.run_debugger(global, caches, lib, scope, this_ptr, _node)?; let index = &mut p.2.clone().into(); self.get_indexed_mut( - global, caches, lib, level, target, index, pos, false, true, + global, caches, lib, target, index, pos, false, true, )? } // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr Expr::MethodCall(ref x, pos) if !x.is_qualified() => { #[cfg(feature = "debugging")] let reset = self.run_debugger_with_reset( - global, caches, lib, level, scope, this_ptr, _node, + global, caches, lib, scope, this_ptr, _node, )?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| { @@ -402,8 +390,8 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); self.make_method_call( - global, caches, lib, level, name, *hashes, target, call_args, - pos1, pos, + global, caches, lib, name, *hashes, target, call_args, pos1, + pos, )? .0 .into() @@ -418,8 +406,8 @@ impl Engine { let rhs_chain = rhs.into(); self.eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, val_target, root, rhs, *options, - &x.rhs, idx_values, rhs_chain, new_val, + global, caches, lib, this_ptr, val_target, root, rhs, *options, &x.rhs, + idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(*x_pos)) } @@ -431,9 +419,7 @@ impl Engine { // xxx.prop[expr] | xxx.prop.expr Expr::Property(ref p, pos) => { #[cfg(feature = "debugging")] - self.run_debugger( - global, caches, lib, level, scope, this_ptr, _node, - )?; + self.run_debugger(global, caches, lib, scope, this_ptr, _node)?; let ((getter, hash_get), (setter, hash_set), name) = &**p; let rhs_chain = rhs.into(); @@ -443,7 +429,7 @@ impl Engine { // Assume getters are always pure let (mut val, ..) = self .exec_native_fn_call( - global, caches, lib, level, getter, None, *hash_get, args, + global, caches, lib, getter, None, *hash_get, args, is_ref_mut, pos, ) .or_else(|err| match *err { @@ -451,7 +437,7 @@ impl Engine { ERR::ErrorDotExpr(..) => { let mut prop = name.into(); self.call_indexer_get( - global, caches, lib, level, target, &mut prop, + global, caches, lib, target, &mut prop, ) .map(|r| (r, false)) .map_err( @@ -468,8 +454,8 @@ impl Engine { let (result, may_be_changed) = self .eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, val, root, rhs, - *options, &x.rhs, idx_values, rhs_chain, new_val, + global, caches, lib, this_ptr, val, root, rhs, *options, + &x.rhs, idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(*x_pos))?; @@ -479,7 +465,7 @@ impl Engine { let mut arg_values = [target.as_mut(), val.as_mut()]; let args = &mut arg_values; self.exec_native_fn_call( - global, caches, lib, level, setter, None, *hash_set, args, + global, caches, lib, setter, None, *hash_set, args, is_ref_mut, pos, ) .or_else( @@ -489,8 +475,8 @@ impl Engine { let idx = &mut name.into(); let new_val = val; self.call_indexer_set( - global, caches, lib, level, target, idx, - new_val, is_ref_mut, + global, caches, lib, target, idx, new_val, + is_ref_mut, ) .or_else(|e| match *e { // If there is no setter, no need to feed it @@ -513,7 +499,7 @@ impl Engine { let val = { #[cfg(feature = "debugging")] let reset = self.run_debugger_with_reset( - global, caches, lib, level, scope, this_ptr, _node, + global, caches, lib, scope, this_ptr, _node, )?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| { @@ -535,8 +521,8 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); self.make_method_call( - global, caches, lib, level, name, *hashes, target, - call_args, pos1, pos, + global, caches, lib, name, *hashes, target, call_args, + pos1, pos, )? .0 }; @@ -545,7 +531,7 @@ impl Engine { let rhs_chain = rhs.into(); self.eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, val, root, rhs, *options, + global, caches, lib, this_ptr, val, root, rhs, *options, &x.rhs, idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(pos)) @@ -571,7 +557,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, expr: &Expr, @@ -612,8 +597,7 @@ impl Engine { // All other patterns - evaluate the arguments chain _ => { self.eval_dot_index_chain_arguments( - global, caches, lib, level, scope, this_ptr, rhs, options, chain_type, - idx_values, + global, caches, lib, scope, this_ptr, rhs, options, chain_type, idx_values, )?; } } @@ -622,19 +606,19 @@ impl Engine { // id.??? or id[???] Expr::Variable(x, .., var_pos) => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, lhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, lhs)?; self.track_operation(global, *var_pos)?; let (mut target, ..) = - self.search_namespace(global, caches, lib, level, scope, this_ptr, lhs)?; + self.search_namespace(global, caches, lib, scope, this_ptr, lhs)?; let obj_ptr = &mut target; let root = (x.3.as_str(), *var_pos); let mut this = Dynamic::NULL; self.eval_dot_index_chain_helper( - global, caches, lib, level, &mut this, obj_ptr, root, expr, options, rhs, - idx_values, chain_type, new_val, + global, caches, lib, &mut this, obj_ptr, root, expr, options, rhs, idx_values, + chain_type, new_val, ) } // {expr}.??? = ??? or {expr}[???] = ??? @@ -642,14 +626,14 @@ impl Engine { // {expr}.??? or {expr}[???] expr => { let value = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .flatten(); let obj_ptr = &mut value.into(); let root = ("", expr.start_position()); self.eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, obj_ptr, root, expr, options, rhs, - idx_values, chain_type, new_val, + global, caches, lib, this_ptr, obj_ptr, root, expr, options, rhs, idx_values, + chain_type, new_val, ) } } @@ -663,7 +647,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, expr: &Expr, @@ -680,7 +663,7 @@ impl Engine { { for arg_expr in &x.args { idx_values.push( - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg_expr)? + self.get_arg_value(global, caches, lib, scope, this_ptr, arg_expr)? .0 .flatten(), ); @@ -714,11 +697,9 @@ impl Engine { { for arg_expr in &x.args { _arg_values.push( - self.get_arg_value( - global, caches, lib, level, scope, this_ptr, arg_expr, - )? - .0 - .flatten(), + self.get_arg_value(global, caches, lib, scope, this_ptr, arg_expr)? + .0 + .flatten(), ); } } @@ -733,7 +714,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] _ if parent_chain_type == ChainType::Indexing => { _arg_values.push( - self.eval_expr(global, caches, lib, level, scope, this_ptr, lhs)? + self.eval_expr(global, caches, lib, scope, this_ptr, lhs)? .flatten(), ); } @@ -744,8 +725,7 @@ impl Engine { let chain_type = expr.into(); self.eval_dot_index_chain_arguments( - global, caches, lib, level, scope, this_ptr, rhs, *options, chain_type, - idx_values, + global, caches, lib, scope, this_ptr, rhs, *options, chain_type, idx_values, )?; if !_arg_values.is_empty() { @@ -759,7 +739,7 @@ impl Engine { } #[cfg(not(feature = "no_index"))] _ if parent_chain_type == ChainType::Indexing => idx_values.push( - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + self.eval_expr(global, caches, lib, scope, this_ptr, expr)? .flatten(), ), _ => unreachable!("unknown chained expression: {:?}", expr), @@ -775,7 +755,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, target: &mut Dynamic, idx: &mut Dynamic, ) -> RhaiResultOf { @@ -783,12 +762,12 @@ impl Engine { let hash = global.hash_idx_get(); let fn_name = crate::engine::FN_IDX_GET; let pos = Position::NONE; - let level = level + 1; - self.exec_native_fn_call( - global, caches, lib, level, fn_name, None, hash, args, true, pos, - ) - .map(|(r, ..)| r) + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); + + self.exec_native_fn_call(global, caches, lib, fn_name, None, hash, args, true, pos) + .map(|(r, ..)| r) } /// Call a set indexer. @@ -798,7 +777,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, target: &mut Dynamic, idx: &mut Dynamic, new_val: &mut Dynamic, @@ -808,10 +786,12 @@ impl Engine { let args = &mut [target, idx, new_val]; let fn_name = crate::engine::FN_IDX_SET; let pos = Position::NONE; - let level = level + 1; + + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); self.exec_native_fn_call( - global, caches, lib, level, fn_name, None, hash, args, is_ref_mut, pos, + global, caches, lib, fn_name, None, hash, args, is_ref_mut, pos, ) } @@ -822,7 +802,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, target: &'t mut Dynamic, idx: &mut Dynamic, idx_pos: Position, @@ -1031,7 +1010,7 @@ impl Engine { } _ if use_indexers => self - .call_indexer_get(global, caches, lib, level, target, idx) + .call_indexer_get(global, caches, lib, target, idx) .map(Into::into), _ => Err(ERR::ErrorIndexingType( diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 124b3547..6eed7279 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -414,14 +414,13 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf<()> { if self.debugger.is_some() { if let Some(cmd) = - self.run_debugger_with_reset_raw(global, caches, lib, level, scope, this_ptr, node)? + self.run_debugger_with_reset_raw(global, caches, lib, scope, this_ptr, node)? { global.debugger.status = cmd; } @@ -441,13 +440,12 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf> { if self.debugger.is_some() { - self.run_debugger_with_reset_raw(global, caches, lib, level, scope, this_ptr, node) + self.run_debugger_with_reset_raw(global, caches, lib, scope, this_ptr, node) } else { Ok(None) } @@ -464,7 +462,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, node: impl Into>, @@ -497,7 +494,7 @@ impl Engine { }, }; - self.run_debugger_raw(global, caches, lib, level, scope, this_ptr, node, event) + self.run_debugger_raw(global, caches, lib, scope, this_ptr, node, event) } /// Run the debugger callback unconditionally. /// @@ -511,7 +508,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, node: ASTNode<'a>, @@ -519,7 +515,7 @@ impl Engine { ) -> Result, Box> { let src = global.source_raw().cloned(); let src = src.as_ref().map(|s| s.as_str()); - let context = crate::EvalContext::new(self, global, caches, lib, level, scope, this_ptr); + let context = crate::EvalContext::new(self, global, caches, lib, scope, this_ptr); if let Some((.., ref on_debugger)) = self.debugger { let command = on_debugger(context, event, node, src, node.position())?; @@ -545,12 +541,12 @@ impl Engine { // Bump a level if it is a function call let level = match node { ASTNode::Expr(Expr::FnCall(..)) | ASTNode::Stmt(Stmt::FnCall(..)) => { - level + 1 + global.level + 1 } ASTNode::Stmt(Stmt::Expr(e)) if matches!(**e, Expr::FnCall(..)) => { - level + 1 + global.level + 1 } - _ => level, + _ => global.level, }; global.debugger.status = DebuggerStatus::FunctionExit(level); Ok(None) diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index ed5d6193..f8f2fd1a 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -21,8 +21,6 @@ pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> { lib: &'a [SharedModule], /// The current bound `this` pointer, if any. this_ptr: &'t mut Dynamic, - /// The current nesting level of function calls. - level: usize, } impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { @@ -34,7 +32,6 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { global: &'g mut GlobalRuntimeState, caches: &'c mut Caches, lib: &'a [SharedModule], - level: usize, scope: &'s mut Scope<'ps>, this_ptr: &'t mut Dynamic, ) -> Self { @@ -45,7 +42,6 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { caches, lib, this_ptr, - level, } } /// The current [`Engine`]. @@ -144,7 +140,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { #[inline(always)] #[must_use] pub const fn call_level(&self) -> usize { - self.level + self.global.level } /// Evaluate an [expression tree][crate::Expression] within this [evaluation context][`EvalContext`]. @@ -186,7 +182,6 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { self.global, self.caches, self.lib, - self.level, self.scope, self.this_ptr, statements, @@ -196,7 +191,6 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { self.global, self.caches, self.lib, - self.level, self.scope, self.this_ptr, expr, diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 55467124..0c18772a 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -52,25 +52,23 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, + scope: &'s mut Scope, this_ptr: &'s mut Dynamic, expr: &Expr, ) -> RhaiResultOf<(Target<'s>, Position)> { match expr { Expr::Variable(_, Some(_), _) => { - self.search_scope_only(global, caches, lib, level, scope, this_ptr, expr) + self.search_scope_only(global, caches, lib, scope, this_ptr, expr) } Expr::Variable(v, None, _var_pos) => match &**v { // Normal variable access #[cfg(not(feature = "no_module"))] (_, ns, ..) if ns.is_empty() => { - self.search_scope_only(global, caches, lib, level, scope, this_ptr, expr) + self.search_scope_only(global, caches, lib, scope, this_ptr, expr) } #[cfg(feature = "no_module")] - (_, (), ..) => { - self.search_scope_only(global, caches, lib, level, scope, this_ptr, expr) - } + (_, (), ..) => self.search_scope_only(global, caches, lib, scope, this_ptr, expr), // Qualified variable access #[cfg(not(feature = "no_module"))] @@ -138,7 +136,7 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, + scope: &'s mut Scope, this_ptr: &'s mut Dynamic, expr: &Expr, @@ -174,7 +172,7 @@ impl Engine { // Check the variable resolver, if any if let Some(ref resolve_var) = self.resolve_var { - let context = EvalContext::new(self, global, caches, lib, level, scope, this_ptr); + let context = EvalContext::new(self, global, caches, lib, scope, this_ptr); let var_name = expr.get_variable_name(true).expect("`Expr::Variable`"); match resolve_var(var_name, index, context) { Ok(Some(mut result)) => { @@ -223,7 +221,7 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, + scope: &mut Scope, this_ptr: &mut Dynamic, expr: &Expr, @@ -235,8 +233,7 @@ impl Engine { // binary operators are also function calls. if let Expr::FnCall(x, pos) = expr { #[cfg(feature = "debugging")] - let reset = - self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; + let reset = self.run_debugger_with_reset(global, caches, lib, scope, this_ptr, expr)?; #[cfg(feature = "debugging")] let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) @@ -244,7 +241,7 @@ impl Engine { self.track_operation(global, expr.position())?; - return self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); + return self.eval_fn_call_expr(global, caches, lib, scope, this_ptr, x, *pos); } // Then variable access. @@ -252,7 +249,7 @@ impl Engine { // will cost more than the mis-predicted `match` branch. if let Expr::Variable(x, index, var_pos) = expr { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, expr)?; + self.run_debugger(global, caches, lib, scope, this_ptr, expr)?; self.track_operation(global, expr.position())?; @@ -263,14 +260,13 @@ impl Engine { Ok(this_ptr.clone()) } } else { - self.search_namespace(global, caches, lib, level, scope, this_ptr, expr) + self.search_namespace(global, caches, lib, scope, this_ptr, expr) .map(|(val, ..)| val.take_or_clone()) }; } #[cfg(feature = "debugging")] - let reset = - self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; + let reset = self.run_debugger_with_reset(global, caches, lib, scope, this_ptr, expr)?; #[cfg(feature = "debugging")] let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) @@ -300,14 +296,11 @@ impl Engine { let result = x .iter() .try_for_each(|expr| { - let item = - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)?; + let item = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?; op_info.pos = expr.start_position(); - self.eval_op_assignment( - global, caches, lib, level, &op_info, target, root, item, - ) + self.eval_op_assignment(global, caches, lib, &op_info, target, root, item) }) .map(|_| concat.take_or_clone()); @@ -324,7 +317,7 @@ impl Engine { crate::Array::with_capacity(x.len()), |mut array, item_expr| { let value = self - .eval_expr(global, caches, lib, level, scope, this_ptr, item_expr)? + .eval_expr(global, caches, lib, scope, this_ptr, item_expr)? .flatten(); #[cfg(not(feature = "unchecked"))] @@ -356,7 +349,7 @@ impl Engine { x.0.iter() .try_fold(x.1.clone(), |mut map, (key, value_expr)| { let value = self - .eval_expr(global, caches, lib, level, scope, this_ptr, value_expr)? + .eval_expr(global, caches, lib, scope, this_ptr, value_expr)? .flatten(); #[cfg(not(feature = "unchecked"))] @@ -379,30 +372,30 @@ impl Engine { } Expr::And(x, ..) => Ok((self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)? + .eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? && self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs)? + .eval_expr(global, caches, lib, scope, this_ptr, &x.rhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) .into()), Expr::Or(x, ..) => Ok((self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)? + .eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? || self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs)? + .eval_expr(global, caches, lib, scope, this_ptr, &x.rhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) .into()), Expr::Coalesce(x, ..) => { - let value = self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)?; + let value = self.eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)?; if value.is::<()>() { - self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs) + self.eval_expr(global, caches, lib, scope, this_ptr, &x.rhs) } else { Ok(value) } @@ -422,8 +415,7 @@ impl Engine { *pos, )) })?; - let mut context = - EvalContext::new(self, global, caches, lib, level, scope, this_ptr); + let mut context = EvalContext::new(self, global, caches, lib, scope, this_ptr); let result = (custom_def.func)(&mut context, &expressions, &custom.state); @@ -431,17 +423,17 @@ impl Engine { } Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT), - Expr::Stmt(x) => { - self.eval_stmt_block(global, caches, lib, level, scope, this_ptr, x, true) - } + Expr::Stmt(x) => self.eval_stmt_block(global, caches, lib, scope, this_ptr, x, true), #[cfg(not(feature = "no_index"))] - Expr::Index(..) => self - .eval_dot_index_chain(global, caches, lib, level, scope, this_ptr, expr, &mut None), + Expr::Index(..) => { + self.eval_dot_index_chain(global, caches, lib, scope, this_ptr, expr, &mut None) + } #[cfg(not(feature = "no_object"))] - Expr::Dot(..) => self - .eval_dot_index_chain(global, caches, lib, level, scope, this_ptr, expr, &mut None), + Expr::Dot(..) => { + self.eval_dot_index_chain(global, caches, lib, scope, this_ptr, expr, &mut None) + } _ => unreachable!("expression cannot be evaluated: {:?}", expr), } diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 413dee13..94dcb837 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -38,6 +38,8 @@ pub struct GlobalRuntimeState { /// Number of modules loaded. #[cfg(not(feature = "no_module"))] pub num_modules_loaded: usize, + /// The current nesting level of function calls. + pub level: usize, /// Level of the current scope. /// /// The global (root) level is zero, a new block (or function call) is one level higher, and so on. @@ -87,6 +89,7 @@ impl GlobalRuntimeState { #[cfg(not(feature = "no_module"))] num_modules_loaded: 0, scope_level: 0, + level: 0, always_search_scope: false, #[cfg(not(feature = "no_module"))] embedded_module_resolver: None, diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 0f064711..faea3923 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -27,7 +27,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, statements: &[Stmt], @@ -54,6 +53,7 @@ impl Engine { let global = &mut *RestoreOnDrop::lock_if(restore_orig_state, global, move |g| { g.scope_level -= 1; + #[cfg(not(feature = "no_module"))] g.truncate_imports(orig_imports_len); @@ -77,7 +77,6 @@ impl Engine { global, caches, lib, - level, scope, this_ptr, stmt, @@ -119,7 +118,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, op_info: &OpAssignment, target: &mut Target, root: (&str, Position), @@ -143,14 +141,17 @@ impl Engine { let hash = *hash_op_assign; let args = &mut [&mut *lock_guard, &mut new_val]; - let level = level + 1; if self.fast_operators() { if let Some(func) = get_builtin_op_assignment_fn(op_assign_token, args[0], args[1]) { // Built-in found let op = op_assign_token.literal_syntax(); - let context = (self, op, None, &*global, lib, *op_pos, level).into(); + + global.level += 1; + let global = &*RestoreOnDrop::lock(global, move |g| g.level -= 1); + + let context = (self, op, None, global, lib, *op_pos).into(); return func(context, args).map(|_| ()); } } @@ -160,7 +161,7 @@ impl Engine { let token = Some(op_assign_token); match self.exec_native_fn_call( - global, caches, lib, level, op_assign, token, hash, args, true, *op_pos, + global, caches, lib, op_assign, token, hash, args, true, *op_pos, ) { Ok(_) => (), Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) => @@ -170,7 +171,7 @@ impl Engine { *args[0] = self .exec_native_fn_call( - global, caches, lib, level, op, token, *hash_op, args, true, *op_pos, + global, caches, lib, op, token, *hash_op, args, true, *op_pos, ) .map_err(|err| err.fill_position(op_info.pos))? .0 @@ -201,15 +202,13 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, stmt: &Stmt, rewind_scope: bool, ) -> RhaiResult { #[cfg(feature = "debugging")] - let reset = - self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, stmt)?; + let reset = self.run_debugger_with_reset(global, caches, lib, scope, this_ptr, stmt)?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset)); @@ -220,7 +219,7 @@ impl Engine { if let Stmt::FnCall(x, pos) = stmt { self.track_operation(global, stmt.position())?; - return self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); + return self.eval_fn_call_expr(global, caches, lib, scope, this_ptr, x, *pos); } // Then assignments. @@ -233,11 +232,11 @@ impl Engine { if let Expr::Variable(x, ..) = lhs { let rhs_val = self - .eval_expr(global, caches, lib, level, scope, this_ptr, rhs)? + .eval_expr(global, caches, lib, scope, this_ptr, rhs)? .flatten(); let (mut lhs_ptr, pos) = - self.search_namespace(global, caches, lib, level, scope, this_ptr, lhs)?; + self.search_namespace(global, caches, lib, scope, this_ptr, lhs)?; let var_name = x.3.as_str(); @@ -259,13 +258,13 @@ impl Engine { let lhs_ptr = &mut lhs_ptr; return self - .eval_op_assignment(global, caches, lib, level, op_info, lhs_ptr, root, rhs_val) + .eval_op_assignment(global, caches, lib, op_info, lhs_ptr, root, rhs_val) .map(|_| Dynamic::UNIT); } #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] { - let rhs_val = self.eval_expr(global, caches, lib, level, scope, this_ptr, rhs)?; + let rhs_val = self.eval_expr(global, caches, lib, scope, this_ptr, rhs)?; // Check if the result is a string. If so, intern it. #[cfg(not(feature = "no_closure"))] @@ -292,14 +291,12 @@ impl Engine { } // idx_lhs[idx_expr] op= rhs #[cfg(not(feature = "no_index"))] - Expr::Index(..) => self.eval_dot_index_chain( - global, caches, lib, level, scope, this_ptr, lhs, _new_val, - ), + Expr::Index(..) => self + .eval_dot_index_chain(global, caches, lib, scope, this_ptr, lhs, _new_val), // dot_lhs.dot_rhs op= rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(..) => self.eval_dot_index_chain( - global, caches, lib, level, scope, this_ptr, lhs, _new_val, - ), + Expr::Dot(..) => self + .eval_dot_index_chain(global, caches, lib, scope, this_ptr, lhs, _new_val), _ => unreachable!("cannot assign to expression: {:?}", lhs), } .map(|_| Dynamic::UNIT); @@ -314,32 +311,28 @@ impl Engine { // Expression as statement Stmt::Expr(expr) => self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) + .eval_expr(global, caches, lib, scope, this_ptr, expr) .map(Dynamic::flatten), // Block scope Stmt::Block(statements, ..) if statements.is_empty() => Ok(Dynamic::UNIT), - Stmt::Block(statements, ..) => self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, statements, true, - ), + Stmt::Block(statements, ..) => { + self.eval_stmt_block(global, caches, lib, scope, this_ptr, statements, true) + } // If statement Stmt::If(x, ..) => { let (expr, if_block, else_block) = &**x; let guard_val = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; if guard_val && !if_block.is_empty() { - self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, if_block, true, - ) + self.eval_stmt_block(global, caches, lib, scope, this_ptr, if_block, true) } else if !guard_val && !else_block.is_empty() { - self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, else_block, true, - ) + self.eval_stmt_block(global, caches, lib, scope, this_ptr, else_block, true) } else { Ok(Dynamic::UNIT) } @@ -359,7 +352,7 @@ impl Engine { let mut result = None; - let value = self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)?; + let value = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?; if value.is_hashable() { let hasher = &mut get_hasher(); @@ -376,7 +369,7 @@ impl Engine { let cond_result = match block.condition { Expr::BoolConstant(b, ..) => b, ref c => self - .eval_expr(global, caches, lib, level, scope, this_ptr, c)? + .eval_expr(global, caches, lib, scope, this_ptr, c)? .as_bool() .map_err(|typ| { self.make_type_mismatch_err::(typ, c.position()) @@ -398,7 +391,7 @@ impl Engine { let cond_result = match block.condition { Expr::BoolConstant(b, ..) => b, ref c => self - .eval_expr(global, caches, lib, level, scope, this_ptr, c)? + .eval_expr(global, caches, lib, scope, this_ptr, c)? .as_bool() .map_err(|typ| { self.make_type_mismatch_err::(typ, c.position()) @@ -416,7 +409,7 @@ impl Engine { result .or_else(|| def_case.as_ref().map(|&index| &expressions[index].expr)) .map_or(Ok(Dynamic::UNIT), |expr| { - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) + self.eval_expr(global, caches, lib, scope, this_ptr, expr) }) } @@ -431,8 +424,8 @@ impl Engine { } loop { - if let Err(err) = self - .eval_stmt_block(global, caches, lib, level, scope, this_ptr, body, true) + if let Err(err) = + self.eval_stmt_block(global, caches, lib, scope, this_ptr, body, true) { match *err { ERR::LoopBreak(false, ..) => (), @@ -449,7 +442,7 @@ impl Engine { loop { let condition = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; @@ -461,8 +454,8 @@ impl Engine { continue; } - if let Err(err) = self - .eval_stmt_block(global, caches, lib, level, scope, this_ptr, body, true) + if let Err(err) = + self.eval_stmt_block(global, caches, lib, scope, this_ptr, body, true) { match *err { ERR::LoopBreak(false, ..) => (), @@ -480,9 +473,9 @@ impl Engine { loop { if !body.is_empty() { - if let Err(err) = self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, body, true, - ) { + if let Err(err) = + self.eval_stmt_block(global, caches, lib, scope, this_ptr, body, true) + { match *err { ERR::LoopBreak(false, ..) => continue, ERR::LoopBreak(true, value, ..) => break Ok(value), @@ -492,7 +485,7 @@ impl Engine { } let condition = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; @@ -507,7 +500,7 @@ impl Engine { let (var_name, counter, expr, statements) = &**x; let iter_obj = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .flatten(); let iter_type = iter_obj.type_id(); @@ -531,75 +524,77 @@ impl Engine { .find_map(|m| m.get_qualified_iter(iter_type)) }); - if let Some(func) = func { - // Restore scope at end of statement - let orig_scope_len = scope.len(); - let scope = &mut *RestoreOnDrop::lock(scope, move |s| { - s.rewind(orig_scope_len); - }); + let func = func.ok_or_else(|| ERR::ErrorFor(expr.start_position()))?; - // Add the loop variables - let counter_index = if counter.is_empty() { - usize::MAX - } else { - scope.push(counter.name.clone(), 0 as INT); - scope.len() - 1 - }; + // Restore scope at end of statement + let orig_scope_len = scope.len(); + let scope = &mut *RestoreOnDrop::lock(scope, move |s| { + s.rewind(orig_scope_len); + }); - scope.push(var_name.name.clone(), ()); - let index = scope.len() - 1; - - func(iter_obj) - .enumerate() - .try_fold(Dynamic::UNIT, |_, (x, iter_value)| { - // Increment counter - if counter_index < usize::MAX { - // As the variable increments from 0, this should always work - // since any overflow will first be caught below. - let index_value = x as INT; - - #[cfg(not(feature = "unchecked"))] - if index_value > crate::MAX_USIZE_INT { - return Err(ERR::ErrorArithmetic( - format!("for-loop counter overflow: {x}"), - counter.pos, - ) - .into()); - } - - *scope.get_mut_by_index(counter_index).write_lock().unwrap() = - Dynamic::from_int(index_value); - } - - let value = match iter_value { - Ok(v) => v.flatten(), - Err(err) => return Err(err.fill_position(expr.position())), - }; - - *scope.get_mut_by_index(index).write_lock().unwrap() = value; - - self.track_operation(global, statements.position())?; - - if statements.is_empty() { - return Ok(Dynamic::UNIT); - } - - self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, statements, true, - ) - .map(|_| Dynamic::UNIT) - .or_else(|err| match *err { - ERR::LoopBreak(false, ..) => Ok(Dynamic::UNIT), - _ => Err(err), - }) - }) - .or_else(|err| match *err { - ERR::LoopBreak(true, value, ..) => Ok(value), - _ => Err(err), - }) + // Add the loop variables + let counter_index = if counter.is_empty() { + usize::MAX } else { - Err(ERR::ErrorFor(expr.start_position()).into()) + scope.push(counter.name.clone(), 0 as INT); + scope.len() - 1 + }; + + scope.push(var_name.name.clone(), ()); + let index = scope.len() - 1; + + let mut result = Dynamic::UNIT; + + for (x, iter_value) in func(iter_obj).enumerate() { + // Increment counter + if counter_index < usize::MAX { + // As the variable increments from 0, this should always work + // since any overflow will first be caught below. + let index_value = x as INT; + + #[cfg(not(feature = "unchecked"))] + if index_value > crate::MAX_USIZE_INT { + return Err(ERR::ErrorArithmetic( + format!("for-loop counter overflow: {x}"), + counter.pos, + ) + .into()); + } + + *scope.get_mut_by_index(counter_index).write_lock().unwrap() = + Dynamic::from_int(index_value); + } + + // Set loop value + let value = iter_value + .map_err(|err| err.fill_position(expr.position()))? + .flatten(); + + *scope.get_mut_by_index(index).write_lock().unwrap() = value; + + // Run block + self.track_operation(global, statements.position())?; + + if statements.is_empty() { + continue; + } + + match self + .eval_stmt_block(global, caches, lib, scope, this_ptr, statements, true) + { + Ok(_) => (), + Err(err) => match *err { + ERR::LoopBreak(false, ..) => (), + ERR::LoopBreak(true, value, ..) => { + result = value; + break; + } + _ => return Err(err), + }, + } } + + Ok(result) } // Continue/Break statement @@ -607,7 +602,7 @@ impl Engine { let is_break = options.contains(ASTFlags::BREAK); let value = if let Some(ref expr) = expr { - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + self.eval_expr(global, caches, lib, scope, this_ptr, expr)? } else { Dynamic::UNIT }; @@ -626,9 +621,7 @@ impl Engine { catch_block, } = &**x; - match self - .eval_stmt_block(global, caches, lib, level, scope, this_ptr, try_block, true) - { + match self.eval_stmt_block(global, caches, lib, scope, this_ptr, try_block, true) { r @ Ok(_) => r, Err(err) if err.is_pseudo_error() => Err(err), Err(err) if !err.is_catchable() => Err(err), @@ -683,7 +676,6 @@ impl Engine { global, caches, lib, - level, scope, this_ptr, catch_block, @@ -704,7 +696,7 @@ impl Engine { // Throw value Stmt::Return(Some(expr), options, pos) if options.contains(ASTFlags::BREAK) => self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) + .eval_expr(global, caches, lib, scope, this_ptr, expr) .and_then(|v| Err(ERR::ErrorRuntime(v.flatten(), *pos).into())), // Empty throw @@ -714,7 +706,7 @@ impl Engine { // Return value Stmt::Return(Some(expr), .., pos) => self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) + .eval_expr(global, caches, lib, scope, this_ptr, expr) .and_then(|v| Err(ERR::Return(v.flatten(), *pos).into())), // Empty return @@ -746,8 +738,7 @@ impl Engine { nesting_level, will_shadow, }; - let context = - EvalContext::new(self, global, caches, lib, level, scope, this_ptr); + let context = EvalContext::new(self, global, caches, lib, scope, this_ptr); if !filter(true, info, context)? { return Err(ERR::ErrorForbiddenVariable(var_name.to_string(), *pos).into()); @@ -756,7 +747,7 @@ impl Engine { // Evaluate initial value let mut value = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .flatten(); let _alias = if !rewind_scope { @@ -811,7 +802,7 @@ impl Engine { return Err(ERR::ErrorTooManyModules(*_pos).into()); } - let v = self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)?; + let v = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?; let typ = v.type_name(); let path = v.try_cast::().ok_or_else(|| { self.make_type_mismatch_err::(typ, expr.position()) @@ -908,21 +899,18 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, statements: &[Stmt], ) -> RhaiResult { let mut this = Dynamic::NULL; - self.eval_stmt_block( - global, caches, lib, level, scope, &mut this, statements, false, - ) - .or_else(|err| match *err { - ERR::Return(out, ..) => Ok(out), - ERR::LoopBreak(..) => { - unreachable!("no outer loop scope to break out of") - } - _ => Err(err), - }) + self.eval_stmt_block(global, caches, lib, scope, &mut this, statements, false) + .or_else(|err| match *err { + ERR::Return(out, ..) => Ok(out), + ERR::LoopBreak(..) => { + unreachable!("no outer loop scope to break out of") + } + _ => Err(err), + }) } } diff --git a/src/func/call.rs b/src/func/call.rs index 8bc4cfe3..38020404 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -326,7 +326,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, name: &str, op_token: Option<&Token>, hash: u64, @@ -385,7 +384,7 @@ impl Engine { // Run external function let src = source.as_ref().map(|s| s.as_str()); - let context = (self, name, src, &*global, lib, pos, level).into(); + let context = (self, name, src, &*global, lib, pos).into(); let mut _result = if func.is_plugin_fn() { let f = func.get_plugin_fn().unwrap(); @@ -401,7 +400,7 @@ impl Engine { #[cfg(feature = "debugging")] { let trigger = match global.debugger.status { - crate::eval::DebuggerStatus::FunctionExit(n) => n >= level, + crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level, crate::eval::DebuggerStatus::Next(.., true) => true, _ => false, }; @@ -415,8 +414,8 @@ impl Engine { Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), }; - if let Err(err) = self - .run_debugger_raw(global, caches, lib, level, scope, &mut this, node, event) + if let Err(err) = + self.run_debugger_raw(global, caches, lib, scope, &mut this, node, event) { _result = Err(err); } @@ -539,7 +538,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, _scope: Option<&mut Scope>, fn_name: &str, op_token: Option<&Token>, @@ -561,7 +559,8 @@ impl Engine { #[cfg(not(feature = "no_closure"))] ensure_no_data_race(fn_name, args, is_ref_mut)?; - let level = level + 1; + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); // These may be redirected from method style calls. if hashes.is_native_only() { @@ -647,7 +646,7 @@ impl Engine { let (first_arg, rest_args) = args.split_first_mut().unwrap(); self.call_script_fn( - global, caches, lib, level, scope, first_arg, func, rest_args, true, pos, + global, caches, lib, scope, first_arg, func, rest_args, true, pos, ) } else { // Normal call of script function @@ -667,7 +666,7 @@ impl Engine { let mut this = Dynamic::NULL; self.call_script_fn( - global, caches, lib, level, scope, &mut this, func, args, true, pos, + global, caches, lib, scope, &mut this, func, args, true, pos, ) } .map(|r| (r, false)); @@ -678,7 +677,7 @@ impl Engine { let hash = hashes.native(); self.exec_native_fn_call( - global, caches, lib, level, fn_name, op_token, hash, args, is_ref_mut, pos, + global, caches, lib, fn_name, op_token, hash, args, is_ref_mut, pos, ) } @@ -689,7 +688,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, arg_expr: &Expr, @@ -699,7 +697,7 @@ impl Engine { self.track_operation(global, arg_expr.start_position())?; #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, arg_expr)?; + self.run_debugger(global, caches, lib, scope, this_ptr, arg_expr)?; return Ok((value, arg_expr.start_position())); } @@ -712,7 +710,7 @@ impl Engine { #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset)); - self.eval_expr(global, caches, lib, level, scope, this_ptr, arg_expr) + self.eval_expr(global, caches, lib, scope, this_ptr, arg_expr) .map(|r| (r, arg_expr.start_position())) } @@ -723,7 +721,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, fn_name: &str, mut hash: FnCallHashes, target: &mut crate::eval::Target, @@ -764,7 +761,6 @@ impl Engine { global, caches, lib, - level, None, fn_name, None, @@ -820,7 +816,6 @@ impl Engine { global, caches, lib, - level, None, &fn_name, None, @@ -921,7 +916,6 @@ impl Engine { global, caches, lib, - level, None, fn_name, None, @@ -948,7 +942,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, fn_name: &str, @@ -974,7 +967,7 @@ impl Engine { KEYWORD_FN_PTR_CALL if total_args >= 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; if !arg_value.is::() { let typ = self.map_type_name(arg_value.type_name()); @@ -1015,7 +1008,7 @@ impl Engine { KEYWORD_FN_PTR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; // Fn - only in function call style return arg_value @@ -1030,7 +1023,7 @@ impl Engine { KEYWORD_FN_PTR_CURRY if total_args > 1 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, first)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, first)?; if !arg_value.is::() { let typ = self.map_type_name(arg_value.type_name()); @@ -1042,7 +1035,7 @@ impl Engine { // Append the new curried arguments to the existing list. let fn_curry = a_expr.iter().try_fold(fn_curry, |mut curried, expr| { let (value, ..) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, expr)?; curried.push(value); Ok::<_, RhaiError>(curried) })?; @@ -1055,7 +1048,7 @@ impl Engine { crate::engine::KEYWORD_IS_SHARED if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, ..) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; return Ok(arg_value.is_shared().into()); } @@ -1064,14 +1057,14 @@ impl Engine { crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, first)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, first)?; let fn_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, &a_expr[0])?; + self.get_arg_value(global, caches, lib, scope, this_ptr, &a_expr[0])?; let num_params = arg_value .as_int() @@ -1090,7 +1083,7 @@ impl Engine { KEYWORD_IS_DEF_VAR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; let var_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; @@ -1105,12 +1098,15 @@ impl Engine { let orig_imports_len = global.num_imports(); let arg = first_arg.unwrap(); let (arg_value, pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; let s = &arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; - let result = - self.eval_script_expr_in_place(global, caches, lib, level + 1, scope, s, pos); + + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); + + let result = self.eval_script_expr_in_place(global, caches, lib, scope, s, pos); // IMPORTANT! If the eval defines new variables in the current scope, // all variable offsets from this point on will be mis-aligned. @@ -1152,7 +1148,7 @@ impl Engine { .copied() .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1163,8 +1159,8 @@ impl Engine { return self .exec_fn_call( - global, caches, lib, level, scope, name, op_token, hashes, &mut args, - is_ref_mut, false, pos, + global, caches, lib, scope, name, op_token, hashes, &mut args, is_ref_mut, + false, pos, ) .map(|(v, ..)| v); } @@ -1180,16 +1176,16 @@ impl Engine { let first_expr = first_arg.unwrap(); #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, first_expr)?; + self.run_debugger(global, caches, lib, scope, this_ptr, first_expr)?; // func(x, ...) -> x.func(...) a_expr.iter().try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; let (mut target, _pos) = - self.search_namespace(global, caches, lib, level, scope, this_ptr, first_expr)?; + self.search_namespace(global, caches, lib, scope, this_ptr, first_expr)?; if target.is_read_only() { target = target.into_owned(); @@ -1216,7 +1212,7 @@ impl Engine { .into_iter() .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1226,8 +1222,7 @@ impl Engine { } self.exec_fn_call( - global, caches, lib, level, None, name, op_token, hashes, &mut args, is_ref_mut, false, - pos, + global, caches, lib, None, name, op_token, hashes, &mut args, is_ref_mut, false, pos, ) .map(|(v, ..)| v) } @@ -1239,7 +1234,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, namespace: &crate::ast::Namespace, @@ -1260,20 +1254,20 @@ impl Engine { // and avoid cloning the value if !args_expr.is_empty() && args_expr[0].is_variable_access(true) { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, &args_expr[0])?; + self.run_debugger(global, caches, lib, scope, this_ptr, &args_expr[0])?; // func(x, ...) -> x.func(...) arg_values.push(Dynamic::UNIT); args_expr.iter().skip(1).try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; // Get target reference to first argument let first_arg = &args_expr[0]; let (target, _pos) = - self.search_scope_only(global, caches, lib, level, scope, this_ptr, first_arg)?; + self.search_scope_only(global, caches, lib, scope, this_ptr, first_arg)?; self.track_operation(global, _pos)?; @@ -1296,7 +1290,7 @@ impl Engine { } else { // func(..., ...) or func(mod::x, ...) args_expr.iter().try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(arg_values.iter_mut()); @@ -1363,7 +1357,8 @@ impl Engine { } } - let level = level + 1; + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); match func { #[cfg(not(feature = "no_function"))] @@ -1376,12 +1371,12 @@ impl Engine { let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source); self.call_script_fn( - global, caches, lib, level, new_scope, &mut this, fn_def, &mut args, true, pos, + global, caches, lib, new_scope, &mut this, fn_def, &mut args, true, pos, ) } Some(f) if f.is_plugin_fn() => { - let context = (self, fn_name, module.id(), &*global, lib, pos, level).into(); + let context = (self, fn_name, module.id(), &*global, lib, pos).into(); let f = f.get_plugin_fn().expect("plugin function"); let result = if !f.is_pure() && !args.is_empty() && args[0].is_read_only() { Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into()) @@ -1393,7 +1388,7 @@ impl Engine { Some(f) if f.is_native() => { let func = f.get_native_fn().expect("native function"); - let context = (self, fn_name, module.id(), &*global, lib, pos, level).into(); + let context = (self, fn_name, module.id(), &*global, lib, pos).into(); let result = func(context, &mut args); self.check_return_value(result, pos) } @@ -1422,7 +1417,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, script: &str, _pos: Position, @@ -1458,7 +1452,7 @@ impl Engine { } // Evaluate the AST - self.eval_global_statements(global, caches, lib, level, scope, statements) + self.eval_global_statements(global, caches, lib, scope, statements) } /// Evaluate a function call expression. @@ -1467,7 +1461,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, expr: &FnCallExpr, @@ -1489,12 +1482,12 @@ impl Engine { // Short-circuit native binary operator call if under Fast Operators mode if op_token.is_some() && self.fast_operators() && args.len() == 2 { let mut lhs = self - .get_arg_value(global, caches, lib, level, scope, this_ptr, &args[0])? + .get_arg_value(global, caches, lib, scope, this_ptr, &args[0])? .0 .flatten(); let mut rhs = self - .get_arg_value(global, caches, lib, level, scope, this_ptr, &args[1])? + .get_arg_value(global, caches, lib, scope, this_ptr, &args[1])? .0 .flatten(); @@ -1504,14 +1497,16 @@ impl Engine { get_builtin_binary_op_fn(op_token.as_ref().unwrap(), operands[0], operands[1]) { // Built-in found - let context = (self, name.as_str(), None, &*global, lib, pos, level + 1).into(); + global.level += 1; + let global = &*RestoreOnDrop::lock(global, move |g| g.level -= 1); + + let context = (self, name.as_str(), None, global, lib, pos).into(); return func(context, operands); } return self .exec_fn_call( - global, caches, lib, level, None, name, op_token, *hashes, operands, false, - false, pos, + global, caches, lib, None, name, op_token, *hashes, operands, false, false, pos, ) .map(|(v, ..)| v); } @@ -1522,7 +1517,7 @@ impl Engine { let hash = hashes.native(); return self.make_qualified_function_call( - global, caches, lib, level, scope, this_ptr, namespace, name, args, hash, pos, + global, caches, lib, scope, this_ptr, namespace, name, args, hash, pos, ); } @@ -1533,7 +1528,7 @@ impl Engine { ); self.make_function_call( - global, caches, lib, level, scope, this_ptr, name, op_token, first_arg, args, *hashes, + global, caches, lib, scope, this_ptr, name, op_token, first_arg, args, *hashes, *capture, pos, ) } diff --git a/src/func/native.rs b/src/func/native.rs index 65f5119d..de74f6c6 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -72,13 +72,11 @@ pub struct NativeCallContext<'a> { /// Function source, if any. source: Option<&'a str>, /// The current [`GlobalRuntimeState`], if any. - global: Option<&'a GlobalRuntimeState>, + global: &'a GlobalRuntimeState, /// The current stack of loaded [modules][Module]. lib: &'a [SharedModule], /// [Position] of the function call. pos: Position, - /// The current nesting level of function calls. - level: usize, } /// _(internals)_ Context of a native Rust function call. @@ -96,8 +94,6 @@ pub struct NativeCallContextStore { pub lib: StaticVec, /// [Position] of the function call. pub pos: Position, - /// The current nesting level of function calls. - pub level: usize, } #[cfg(feature = "internals")] @@ -118,7 +114,6 @@ impl<'a> &'a GlobalRuntimeState, &'a [SharedModule], Position, - usize, )> for NativeCallContext<'a> { #[inline(always)] @@ -130,56 +125,20 @@ impl<'a> &'a GlobalRuntimeState, &'a [SharedModule], Position, - usize, ), ) -> Self { Self { engine: value.0, fn_name: value.1, source: value.2, - global: Some(value.3), + global: value.3, lib: value.4, pos: value.5, - level: value.6, - } - } -} - -impl<'a> From<(&'a Engine, &'a str, &'a [SharedModule])> for NativeCallContext<'a> { - #[inline(always)] - fn from(value: (&'a Engine, &'a str, &'a [SharedModule])) -> Self { - Self { - engine: value.0, - fn_name: value.1, - source: None, - global: None, - lib: value.2, - pos: Position::NONE, - level: 0, } } } impl<'a> NativeCallContext<'a> { - /// _(internals)_ Create a new [`NativeCallContext`]. - /// Exported under the `internals` feature only. - #[deprecated( - since = "1.3.0", - note = "`NativeCallContext::new` will be moved under `internals`. Use `FnPtr::call` to call a function pointer directly." - )] - #[inline(always)] - #[must_use] - pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [SharedModule]) -> Self { - Self { - engine, - fn_name, - source: None, - global: None, - lib, - pos: Position::NONE, - level: 0, - } - } /// _(internals)_ Create a new [`NativeCallContext`]. /// Exported under the `internals` feature only. /// @@ -195,16 +154,14 @@ impl<'a> NativeCallContext<'a> { global: &'a GlobalRuntimeState, lib: &'a [SharedModule], pos: Position, - level: usize, ) -> Self { Self { engine, fn_name, source, - global: Some(global), + global, lib, pos, - level, } } @@ -218,10 +175,9 @@ impl<'a> NativeCallContext<'a> { engine, fn_name: &context.fn_name, source: context.source.as_ref().map(String::as_str), - global: Some(&context.global), + global: &context.global, lib: &context.lib, pos: context.pos, - level: context.level, } } /// _(internals)_ Store this [`NativeCallContext`] into a [`NativeCallContextClone`]. @@ -233,10 +189,9 @@ impl<'a> NativeCallContext<'a> { NativeCallContextStore { fn_name: self.fn_name.to_string(), source: self.source.map(|s| s.to_string()), - global: self.global.unwrap().clone(), + global: self.global.clone(), lib: self.lib.iter().cloned().collect(), pos: self.pos, - level: self.level, } } @@ -262,7 +217,7 @@ impl<'a> NativeCallContext<'a> { #[inline(always)] #[must_use] pub const fn call_level(&self) -> usize { - self.level + self.global.level } /// The current source. #[inline(always)] @@ -274,7 +229,7 @@ impl<'a> NativeCallContext<'a> { #[inline(always)] #[must_use] pub fn tag(&self) -> Option<&Dynamic> { - self.global.as_ref().map(|g| &g.tag) + Some(&self.global.tag) } /// Get an iterator over the current set of modules imported via `import` statements /// in reverse order. @@ -283,7 +238,7 @@ impl<'a> NativeCallContext<'a> { #[cfg(not(feature = "no_module"))] #[inline] pub fn iter_imports(&self) -> impl Iterator { - self.global.iter().flat_map(|&g| g.iter_imports()) + self.global.iter_imports() } /// Get an iterator over the current set of modules imported via `import` statements in reverse order. #[cfg(not(feature = "no_module"))] @@ -292,7 +247,7 @@ impl<'a> NativeCallContext<'a> { pub(crate) fn iter_imports_raw( &self, ) -> impl Iterator { - self.global.iter().flat_map(|&g| g.iter_imports_raw()) + self.global.iter_imports_raw() } /// _(internals)_ The current [`GlobalRuntimeState`], if any. /// Exported under the `internals` feature only. @@ -301,7 +256,7 @@ impl<'a> NativeCallContext<'a> { #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub const fn global_runtime_state(&self) -> Option<&GlobalRuntimeState> { + pub const fn global_runtime_state(&self) -> &GlobalRuntimeState { self.global } /// Get an iterator over the namespaces containing definitions of all script-defined functions @@ -437,10 +392,7 @@ impl<'a> NativeCallContext<'a> { is_method_call: bool, args: &mut [&mut Dynamic], ) -> RhaiResult { - let global = &mut self - .global - .cloned() - .unwrap_or_else(|| GlobalRuntimeState::new(self.engine())); + let mut global = &mut self.global.clone(); let caches = &mut Caches::new(); let fn_name = fn_name.as_ref(); @@ -448,6 +400,8 @@ impl<'a> NativeCallContext<'a> { let op_token = op_token.as_ref(); let args_len = args.len(); + global.level += 1; + if native_only { return self .engine() @@ -455,7 +409,6 @@ impl<'a> NativeCallContext<'a> { global, caches, self.lib, - self.level + 1, fn_name, op_token, calc_fn_hash(None, fn_name, args_len), @@ -483,7 +436,6 @@ impl<'a> NativeCallContext<'a> { global, caches, self.lib, - self.level + 1, None, fn_name, op_token, diff --git a/src/func/script.rs b/src/func/script.rs index ae4a0f77..db3fa542 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -27,7 +27,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, fn_def: &ScriptFnDef, @@ -66,7 +65,7 @@ impl Engine { self.track_operation(global, pos)?; // Check for stack overflow - if level > self.max_call_levels() { + if global.level > self.max_call_levels() { return Err(ERR::ErrorStackOverflow(pos).into()); } @@ -140,7 +139,7 @@ impl Engine { #[cfg(feature = "debugging")] { let node = crate::ast::Stmt::Noop(fn_def.body.position()); - self.run_debugger(global, caches, lib, level, scope, this_ptr, &node)?; + self.run_debugger(global, caches, lib, scope, this_ptr, &node)?; } // Evaluate the function @@ -149,7 +148,6 @@ impl Engine { global, caches, lib, - level, scope, this_ptr, &fn_def.body, @@ -179,7 +177,7 @@ impl Engine { #[cfg(feature = "debugging")] { let trigger = match global.debugger.status { - crate::eval::DebuggerStatus::FunctionExit(n) => n >= level, + crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level, crate::eval::DebuggerStatus::Next(.., true) => true, _ => false, }; @@ -190,9 +188,7 @@ impl Engine { Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r), Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), }; - match self - .run_debugger_raw(global, caches, lib, level, scope, this_ptr, node, event) - { + match self.run_debugger_raw(global, caches, lib, scope, this_ptr, node, event) { Ok(_) => (), Err(err) => _result = Err(err), } diff --git a/src/module/mod.rs b/src/module/mod.rs index 0d78e0e4..a8676655 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1986,7 +1986,7 @@ impl Module { // Run the script let caches = &mut crate::eval::Caches::new(); - let result = engine.eval_ast_with_scope_raw(global, caches, 0, &mut scope, ast); + let result = engine.eval_ast_with_scope_raw(global, caches, &mut scope, ast); // Create new module let mut module = Module::new(); diff --git a/src/optimizer.rs b/src/optimizer.rs index 7ad765c1..9bf57005 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -148,7 +148,6 @@ impl<'a> OptimizerState<'a> { &mut self.global, &mut self.caches, lib, - 0, fn_name, op_token, calc_fn_hash(None, fn_name, arg_values.len()), @@ -1144,7 +1143,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { #[cfg(feature = "no_function")] let lib = &[][..]; - let context = (state.engine, x.name.as_str(), lib).into(); + let context = (state.engine, x.name.as_str(),None, &state.global, lib, *pos).into(); let (first, second) = arg_values.split_first_mut().unwrap(); (f)(context, &mut [ first, &mut second[0] ]).ok() }) { diff --git a/src/packages/debugging.rs b/src/packages/debugging.rs index 8a800c1b..4e3cbc35 100644 --- a/src/packages/debugging.rs +++ b/src/packages/debugging.rs @@ -33,57 +33,47 @@ mod debugging_functions { #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_index"))] pub fn back_trace(ctx: NativeCallContext) -> Array { - if let Some(global) = ctx.global_runtime_state() { - global - .debugger - .call_stack() - .iter() - .rev() - .filter(|crate::debugger::CallStackFrame { fn_name, args, .. }| { - fn_name.as_str() != "back_trace" || !args.is_empty() - }) - .map( - |frame @ crate::debugger::CallStackFrame { - fn_name: _fn_name, - args: _args, - source: _source, - pos: _pos, - }| { - let display = frame.to_string(); + ctx.global_runtime_state() + .debugger + .call_stack() + .iter() + .rev() + .filter(|crate::debugger::CallStackFrame { fn_name, args, .. }| { + fn_name.as_str() != "back_trace" || !args.is_empty() + }) + .map( + |frame @ crate::debugger::CallStackFrame { + fn_name: _fn_name, + args: _args, + source: _source, + pos: _pos, + }| { + let display = frame.to_string(); - #[cfg(not(feature = "no_object"))] - { - let mut map = Map::new(); - map.insert("display".into(), display.into()); - map.insert("fn_name".into(), _fn_name.into()); - if !_args.is_empty() { - map.insert( - "args".into(), - Dynamic::from_array(_args.clone().to_vec()), - ); - } - if let Some(source) = _source { - map.insert("source".into(), source.into()); - } - if !_pos.is_none() { - map.insert( - "line".into(), - (_pos.line().unwrap() as crate::INT).into(), - ); - map.insert( - "position".into(), - (_pos.position().unwrap_or(0) as crate::INT).into(), - ); - } - Dynamic::from_map(map) + #[cfg(not(feature = "no_object"))] + { + let mut map = Map::new(); + map.insert("display".into(), display.into()); + map.insert("fn_name".into(), _fn_name.into()); + if !_args.is_empty() { + map.insert("args".into(), Dynamic::from_array(_args.clone().to_vec())); } - #[cfg(feature = "no_object")] - display.into() - }, - ) - .collect() - } else { - Array::new() - } + if let Some(source) = _source { + map.insert("source".into(), source.into()); + } + if !_pos.is_none() { + map.insert("line".into(), (_pos.line().unwrap() as crate::INT).into()); + map.insert( + "position".into(), + (_pos.position().unwrap_or(0) as crate::INT).into(), + ); + } + Dynamic::from_map(map) + } + #[cfg(feature = "no_object")] + display.into() + }, + ) + .collect() } } diff --git a/src/parser.rs b/src/parser.rs index 03d5b16b..fed55ac3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2898,12 +2898,12 @@ impl Engine { if let Some(ref filter) = self.def_var_filter { let will_shadow = state.stack.iter().any(|(v, ..)| v == name); - let level = settings.level; + state.global.level = settings.level; let is_const = access == AccessMode::ReadOnly; let info = VarDefInfo { name: &name, is_const, - nesting_level: level, + nesting_level: state.global.level, will_shadow, }; let caches = &mut Caches::new(); @@ -2914,7 +2914,6 @@ impl Engine { &mut state.global, caches, &[], - level, &mut state.stack, &mut this, ); diff --git a/src/tests.rs b/src/tests.rs index 8bbef0d4..6e36c977 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -45,9 +45,9 @@ fn check_struct_sizes() { assert_eq!( size_of::(), if cfg!(feature = "no_position") { - 72 + 64 } else { - 80 + 72 } ); } diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 85355d4b..3348d6c2 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -1,5 +1,6 @@ //! The `FnPtr` type. +use crate::eval::GlobalRuntimeState; use crate::tokenizer::is_valid_function_name; use crate::types::dynamic::Variant; use crate::{ @@ -160,8 +161,16 @@ impl FnPtr { &lib }; - #[allow(deprecated)] - let ctx = NativeCallContext::new(engine, self.fn_name(), lib); + let global = &mut GlobalRuntimeState::new(engine); + + let ctx = NativeCallContext::new_with_all_fields( + engine, + self.fn_name(), + None, + global, + lib, + Position::NONE, + ); let result = self.call_raw(&ctx, None, arg_values)?; diff --git a/unreachable!() b/unreachable!() deleted file mode 100644 index e69de29b..00000000 From ae1e19c98a1123795cc4cbdc5ea3c61d6fa10fd4 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 8 Nov 2022 21:41:00 +0800 Subject: [PATCH 9/9] Fix builds. --- src/api/call_fn.rs | 15 +++++++-------- src/api/eval.rs | 3 +-- src/eval/stmt.rs | 3 +-- src/types/fn_ptr.rs | 11 ++--------- 4 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 6ba9fcf3..1a78cae1 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -3,7 +3,6 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::types::dynamic::Variant; -use crate::types::RestoreOnDrop; use crate::{ reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, StaticVec, AST, ERR, @@ -252,7 +251,7 @@ impl Engine { let lib = &[AsRef::::as_ref(ast).clone()]; let mut no_this_ptr = Dynamic::NULL; - let mut this_ptr = this_ptr.unwrap_or(&mut no_this_ptr); + let this_ptr = this_ptr.unwrap_or(&mut no_this_ptr); let orig_scope_len = scope.len(); @@ -262,7 +261,7 @@ impl Engine { ast.resolver().cloned(), ); #[cfg(not(feature = "no_module"))] - let global = &mut *RestoreOnDrop::lock(global, move |g| { + let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { g.embedded_module_resolver = orig_embedded_module_resolver }); @@ -278,11 +277,11 @@ impl Engine { Ok(Dynamic::UNIT) } .and_then(|_| { - let mut args: StaticVec<_> = arg_values.iter_mut().collect(); + let args = &mut arg_values.iter_mut().collect::>(); // Check for data race. #[cfg(not(feature = "no_closure"))] - crate::func::ensure_no_data_race(name, &args, false).map(|_| Dynamic::UNIT)?; + crate::func::ensure_no_data_race(name, args, false).map(|_| Dynamic::UNIT)?; if let Some(fn_def) = ast.shared_lib().get_script_fn(name, args.len()) { self.call_script_fn( @@ -290,9 +289,9 @@ impl Engine { caches, lib, scope, - &mut this_ptr, + this_ptr, fn_def, - &mut args, + args, rewind_scope, Position::NONE, ) @@ -305,7 +304,7 @@ impl Engine { if self.debugger.is_some() { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let node = &crate::ast::Stmt::Noop(Position::NONE); - self.run_debugger(global, caches, lib, scope, &mut this_ptr, node)?; + self.run_debugger(global, caches, lib, scope, this_ptr, node)?; } Ok(result) diff --git a/src/api/eval.rs b/src/api/eval.rs index 829c69b0..10cf9040 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -3,7 +3,6 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::parser::ParseState; use crate::types::dynamic::Variant; -use crate::types::RestoreOnDrop; use crate::{ Dynamic, Engine, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR, }; @@ -229,7 +228,7 @@ impl Engine { ast.resolver().cloned(), ); #[cfg(not(feature = "no_module"))] - let global = &mut *RestoreOnDrop::lock(global, move |g| { + let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { g.embedded_module_resolver = orig_embedded_module_resolver }); diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index faea3923..9a086ed9 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -730,12 +730,11 @@ impl Engine { // Check variable definition filter if let Some(ref filter) = self.def_var_filter { let will_shadow = scope.contains(var_name); - let nesting_level = global.scope_level; let is_const = access == AccessMode::ReadOnly; let info = VarDefInfo { name: var_name, is_const, - nesting_level, + nesting_level: global.scope_level, will_shadow, }; let context = EvalContext::new(self, global, caches, lib, scope, this_ptr); diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 3348d6c2..5d087ef2 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -161,16 +161,9 @@ impl FnPtr { &lib }; - let global = &mut GlobalRuntimeState::new(engine); + let global = &GlobalRuntimeState::new(engine); - let ctx = NativeCallContext::new_with_all_fields( - engine, - self.fn_name(), - None, - global, - lib, - Position::NONE, - ); + let ctx = (engine, self.fn_name(), None, global, lib, Position::NONE).into(); let result = self.call_raw(&ctx, None, arg_values)?;