From 0c79471fd317c3bb6c2c08a0a245ad99e941ee15 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 7 Nov 2022 16:18:59 +0800 Subject: [PATCH 1/7] 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/7] 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/7] 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/7] 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/7] 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/7] 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/7] 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.