diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index cfad57f9..6ca6f59d 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -1,7 +1,7 @@ //! Module that defines the `call_fn` API of [`Engine`]. #![cfg(not(feature = "no_function"))] -use crate::eval::{EvalState, GlobalRuntimeState}; +use crate::eval::{Caches, GlobalRuntimeState}; use crate::types::dynamic::Variant; use crate::{ Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST, ERR, @@ -149,7 +149,7 @@ impl Engine { this_ptr: Option<&mut Dynamic>, arg_values: impl AsMut<[Dynamic]>, ) -> RhaiResult { - let state = &mut EvalState::new(); + let caches = &mut Caches::new(); let global = &mut GlobalRuntimeState::new(self); let statements = ast.statements(); @@ -157,7 +157,7 @@ impl Engine { let orig_scope_len = scope.len(); if eval_ast && !statements.is_empty() { - self.eval_global_statements(scope, global, state, statements, &[ast.as_ref()], 0)?; + self.eval_global_statements(scope, global, caches, statements, &[ast.as_ref()], 0)?; if rewind_scope { scope.rewind(orig_scope_len); @@ -181,7 +181,7 @@ impl Engine { self.call_script_fn( scope, global, - state, + caches, &[ast.as_ref()], &mut this_ptr, fn_def, diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index d3411fce..32be7d54 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -130,7 +130,7 @@ impl Deref for Expression<'_> { } } -impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_, '_, '_> { +impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_, '_> { /// Evaluate an [expression tree][Expression]. /// /// # WARNING - Low Level API @@ -141,7 +141,7 @@ impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_, '_, '_> { self.engine.eval_expr( self.scope, self.global, - self.state, + self.caches, self.lib, self.this_ptr, expr, diff --git a/src/api/eval.rs b/src/api/eval.rs index f83da77f..49e26243 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -1,6 +1,6 @@ //! Module that defines the public evaluation API of [`Engine`]. -use crate::eval::{EvalState, GlobalRuntimeState}; +use crate::eval::{Caches, GlobalRuntimeState}; use crate::parser::ParseState; use crate::types::dynamic::Variant; use crate::{ @@ -208,7 +208,7 @@ impl Engine { ast: &'a AST, level: usize, ) -> RhaiResult { - let mut state = EvalState::new(); + let mut caches = Caches::new(); global.source = ast.source_raw().clone(); #[cfg(not(feature = "no_module"))] @@ -231,6 +231,6 @@ impl Engine { } else { &lib[..] }; - self.eval_global_statements(scope, global, &mut state, statements, lib, level) + self.eval_global_statements(scope, global, &mut caches, statements, lib, level) } } diff --git a/src/api/run.rs b/src/api/run.rs index 25fd69c7..77097d29 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -1,6 +1,6 @@ //! Module that defines the public evaluation API of [`Engine`]. -use crate::eval::{EvalState, GlobalRuntimeState}; +use crate::eval::{Caches, GlobalRuntimeState}; use crate::parser::ParseState; use crate::{Engine, Module, RhaiResultOf, Scope, AST}; #[cfg(feature = "no_std")] @@ -43,7 +43,7 @@ impl Engine { /// Evaluate an [`AST`] with own scope, returning any error (if any). #[inline] pub fn run_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> { - let state = &mut EvalState::new(); + let caches = &mut Caches::new(); let global = &mut GlobalRuntimeState::new(self); global.source = ast.source_raw().clone(); @@ -63,7 +63,7 @@ impl Engine { } else { &lib }; - self.eval_global_statements(scope, global, state, statements, lib, 0)?; + self.eval_global_statements(scope, global, caches, statements, lib, 0)?; } Ok(()) } diff --git a/src/eval/cache.rs b/src/eval/cache.rs new file mode 100644 index 00000000..64c9e0fb --- /dev/null +++ b/src/eval/cache.rs @@ -0,0 +1,69 @@ +//! System caches. + +use crate::func::CallableFunction; +use crate::{Identifier, StaticVec}; +use std::collections::BTreeMap; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; + +/// _(internals)_ An entry in a function resolution cache. +/// Exported under the `internals` feature only. +#[derive(Debug, Clone)] +pub struct FnResolutionCacheEntry { + /// Function. + pub func: CallableFunction, + /// Optional source. + /// No source if the string is empty. + pub source: Identifier, +} + +/// _(internals)_ A function resolution cache. +/// Exported under the `internals` feature only. +/// +/// [`FnResolutionCacheEntry`] is [`Box`]ed in order to pack as many entries inside a single B-Tree +/// level as possible. +pub type FnResolutionCache = BTreeMap>>; + +/// _(internals)_ A type containing system-wide caches. +/// Exported under the `internals` feature only. +/// +/// The following caches are contained inside this type: +/// * A stack of [function resolution caches][FnResolutionCache] +#[derive(Debug, Clone)] +pub struct Caches(StaticVec); + +impl Caches { + /// Create an empty [`Caches`]. + #[inline(always)] + #[must_use] + pub const fn new() -> Self { + Self(StaticVec::new_const()) + } + /// Get the number of function resolution cache(s) in the stack. + #[inline(always)] + #[must_use] + pub fn fn_resolution_caches_len(&self) -> usize { + self.0.len() + } + /// Get a mutable reference to the current function resolution cache. + #[inline] + #[must_use] + pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache { + if self.0.is_empty() { + // Push a new function resolution cache if the stack is empty + self.push_fn_resolution_cache(); + } + self.0.last_mut().unwrap() + } + /// Push an empty function resolution cache onto the stack and make it current. + #[allow(dead_code)] + #[inline(always)] + pub fn push_fn_resolution_cache(&mut self) { + self.0.push(BTreeMap::new()); + } + /// Rewind the function resolution caches stack to a particular size. + #[inline(always)] + pub fn rewind_fn_resolution_caches(&mut self, len: usize) { + self.0.truncate(len); + } +} diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 56d3aba3..ee566328 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -1,7 +1,7 @@ //! Types to support chaining operations (i.e. indexing and dotting). #![cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -use super::{EvalState, GlobalRuntimeState, Target}; +use super::{Caches, GlobalRuntimeState, Target}; use crate::ast::{ASTFlags, Expr, OpAssignment}; use crate::types::dynamic::Union; use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR}; @@ -122,7 +122,7 @@ impl Engine { fn eval_dot_index_chain_helper( &self, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, target: &mut Target, @@ -155,7 +155,7 @@ impl Engine { if !_parent_options.contains(ASTFlags::BREAK) => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, state, lib, this_ptr, _parent, level)?; + self.run_debugger(scope, global, caches, lib, this_ptr, _parent, level)?; let mut idx_val_for_setter = idx_val.clone(); let idx_pos = x.lhs.start_position(); @@ -163,14 +163,14 @@ impl Engine { let (try_setter, result) = { let mut obj = self.get_indexed_mut( - global, state, lib, target, idx_val, idx_pos, false, true, level, + global, caches, lib, target, idx_val, idx_pos, false, true, level, )?; let is_obj_temp_val = obj.is_temp_value(); let obj_ptr = &mut obj; match self.eval_dot_index_chain_helper( - global, state, lib, this_ptr, obj_ptr, root, rhs, &x.rhs, *options, - idx_values, rhs_chain, level, new_val, + global, caches, lib, this_ptr, obj_ptr, root, rhs, &x.rhs, + *options, idx_values, rhs_chain, level, new_val, ) { Ok((result, true)) if is_obj_temp_val => { (Some(obj.take_or_clone()), (result, true)) @@ -185,7 +185,7 @@ impl Engine { let idx = &mut idx_val_for_setter; let new_val = &mut new_val; self.call_indexer_set( - global, state, lib, target, idx, new_val, is_ref_mut, level, + global, caches, lib, target, idx, new_val, is_ref_mut, level, ) .or_else(|e| match *e { ERR::ErrorIndexingType(..) => Ok((Dynamic::UNIT, false)), @@ -198,18 +198,18 @@ impl Engine { // xxx[rhs] op= new_val _ if new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, state, lib, this_ptr, _parent, level)?; + self.run_debugger(scope, global, caches, lib, this_ptr, _parent, level)?; let ((new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`"); let mut idx_val2 = idx_val.clone(); let try_setter = match self.get_indexed_mut( - global, state, lib, target, idx_val, pos, true, false, level, + global, caches, lib, target, idx_val, pos, true, false, level, ) { // Indexed value is not a temp value - update directly Ok(ref mut obj_ptr) => { self.eval_op_assignment( - global, state, lib, op_info, op_pos, obj_ptr, root, new_val, + global, caches, lib, op_info, op_pos, obj_ptr, root, new_val, level, ) .map_err(|err| err.fill_position(new_pos))?; @@ -232,12 +232,12 @@ impl Engine { let idx = &mut idx.clone(); // Call the index getter to get the current value if let Ok(val) = - self.call_indexer_get(global, state, lib, target, idx, level) + self.call_indexer_get(global, caches, lib, target, idx, level) { let mut res = val.into(); // Run the op-assignment self.eval_op_assignment( - global, state, lib, op_info, op_pos, &mut res, root, + global, caches, lib, op_info, op_pos, &mut res, root, new_val, level, ) .map_err(|err| err.fill_position(new_pos))?; @@ -251,7 +251,7 @@ impl Engine { // Try to call index setter let new_val = &mut new_val; self.call_indexer_set( - global, state, lib, target, idx, new_val, is_ref_mut, level, + global, caches, lib, target, idx, new_val, is_ref_mut, level, )?; } @@ -260,10 +260,10 @@ impl Engine { // xxx[rhs] _ => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, state, lib, this_ptr, _parent, level)?; + self.run_debugger(scope, global, caches, lib, this_ptr, _parent, level)?; self.get_indexed_mut( - global, state, lib, target, idx_val, pos, false, true, level, + global, caches, lib, target, idx_val, pos, false, true, level, ) .map(|v| (v.take_or_clone(), false)) } @@ -280,11 +280,11 @@ impl Engine { #[cfg(feature = "debugging")] let reset_debugger = self.run_debugger_with_reset( - scope, global, state, lib, this_ptr, rhs, level, + scope, global, caches, lib, this_ptr, rhs, level, )?; let result = self.make_method_call( - global, state, lib, name, *hashes, target, call_args, *pos, level, + global, caches, lib, name, *hashes, target, call_args, *pos, level, ); #[cfg(feature = "debugging")] @@ -303,16 +303,16 @@ impl Engine { // {xxx:map}.id op= ??? Expr::Property(x, pos) if target.is::() && new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, state, lib, this_ptr, rhs, level)?; + self.run_debugger(scope, global, caches, lib, this_ptr, rhs, level)?; let index = x.2.clone().into(); let ((new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`"); { let val_target = &mut self.get_indexed_mut( - global, state, lib, target, index, *pos, true, false, level, + global, caches, lib, target, index, *pos, true, false, level, )?; self.eval_op_assignment( - global, state, lib, op_info, op_pos, val_target, root, new_val, + global, caches, lib, op_info, op_pos, val_target, root, new_val, level, ) .map_err(|err| err.fill_position(new_pos))?; @@ -324,18 +324,18 @@ impl Engine { // {xxx:map}.id Expr::Property(x, pos) if target.is::() => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, state, lib, this_ptr, rhs, level)?; + self.run_debugger(scope, global, caches, lib, this_ptr, rhs, level)?; let index = x.2.clone().into(); let val = self.get_indexed_mut( - global, state, lib, target, index, *pos, false, false, level, + global, caches, lib, target, index, *pos, false, false, level, )?; Ok((val.take_or_clone(), false)) } // xxx.id op= ??? Expr::Property(x, pos) if new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, state, lib, this_ptr, rhs, level)?; + self.run_debugger(scope, global, caches, lib, this_ptr, rhs, level)?; let ((getter, hash_get), (setter, hash_set), name) = x.as_ref(); let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`"); @@ -345,15 +345,15 @@ impl Engine { let args = &mut [target.as_mut()]; let (mut orig_val, ..) = self .exec_fn_call( - None, global, state, lib, getter, hash, args, is_ref_mut, true, - *pos, level, + None, global, caches, lib, getter, hash, args, is_ref_mut, + true, *pos, level, ) .or_else(|err| match *err { // Try an indexer if property does not exist ERR::ErrorDotExpr(..) => { let mut prop = name.into(); self.call_indexer_get( - global, state, lib, target, &mut prop, level, + global, caches, lib, target, &mut prop, level, ) .map(|r| (r, false)) .map_err(|e| { @@ -370,7 +370,7 @@ impl Engine { let orig_val = &mut (&mut orig_val).into(); self.eval_op_assignment( - global, state, lib, op_info, op_pos, orig_val, root, new_val, + global, caches, lib, op_info, op_pos, orig_val, root, new_val, level, ) .map_err(|err| err.fill_position(new_pos))?; @@ -382,7 +382,7 @@ impl Engine { let hash = crate::ast::FnCallHashes::from_native(*hash_set); let args = &mut [target.as_mut(), &mut new_val]; self.exec_fn_call( - None, global, state, lib, setter, hash, args, is_ref_mut, true, *pos, + None, global, caches, lib, setter, hash, args, is_ref_mut, true, *pos, level, ) .or_else(|err| match *err { @@ -391,7 +391,7 @@ impl Engine { let idx = &mut name.into(); let new_val = &mut new_val; self.call_indexer_set( - global, state, lib, target, idx, new_val, is_ref_mut, level, + global, caches, lib, target, idx, new_val, is_ref_mut, level, ) .map_err(|e| match *e { ERR::ErrorIndexingType(..) => err, @@ -404,13 +404,13 @@ impl Engine { // xxx.id Expr::Property(x, pos) => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, state, lib, this_ptr, rhs, level)?; + self.run_debugger(scope, global, caches, lib, this_ptr, rhs, level)?; let ((getter, hash_get), _, name) = x.as_ref(); let hash = crate::ast::FnCallHashes::from_native(*hash_get); let args = &mut [target.as_mut()]; self.exec_fn_call( - None, global, state, lib, getter, hash, args, is_ref_mut, true, *pos, + None, global, caches, lib, getter, hash, args, is_ref_mut, true, *pos, level, ) .map_or_else( @@ -419,7 +419,7 @@ impl Engine { ERR::ErrorDotExpr(..) => { let mut prop = name.into(); self.call_indexer_get( - global, state, lib, target, &mut prop, level, + global, caches, lib, target, &mut prop, level, ) .map(|r| (r, false)) .map_err(|e| match *e { @@ -443,12 +443,12 @@ impl Engine { Expr::Property(ref p, pos) => { #[cfg(feature = "debugging")] self.run_debugger( - scope, global, state, lib, this_ptr, _node, level, + scope, global, caches, lib, this_ptr, _node, level, )?; let index = p.2.clone().into(); self.get_indexed_mut( - global, state, lib, target, index, pos, false, true, level, + global, caches, lib, target, index, pos, false, true, level, )? } // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr @@ -458,11 +458,11 @@ impl Engine { #[cfg(feature = "debugging")] let reset_debugger = self.run_debugger_with_reset( - scope, global, state, lib, this_ptr, _node, level, + scope, global, caches, lib, this_ptr, _node, level, )?; let result = self.make_method_call( - global, state, lib, name, *hashes, target, call_args, pos, + global, caches, lib, name, *hashes, target, call_args, pos, level, ); @@ -481,7 +481,7 @@ impl Engine { let rhs_chain = rhs.into(); self.eval_dot_index_chain_helper( - global, state, lib, this_ptr, val_target, root, rhs, &x.rhs, *options, + global, caches, lib, this_ptr, val_target, root, rhs, &x.rhs, *options, idx_values, rhs_chain, level, new_val, ) .map_err(|err| err.fill_position(*x_pos)) @@ -495,7 +495,7 @@ impl Engine { Expr::Property(ref p, pos) => { #[cfg(feature = "debugging")] self.run_debugger( - scope, global, state, lib, this_ptr, _node, level, + scope, global, caches, lib, this_ptr, _node, level, )?; let ((getter, hash_get), (setter, hash_set), name) = p.as_ref(); @@ -508,7 +508,7 @@ impl Engine { // Assume getters are always pure let (mut val, ..) = self .exec_fn_call( - None, global, state, lib, getter, hash_get, args, + None, global, caches, lib, getter, hash_get, args, is_ref_mut, true, pos, level, ) .or_else(|err| match *err { @@ -516,7 +516,7 @@ impl Engine { ERR::ErrorDotExpr(..) => { let mut prop = name.into(); self.call_indexer_get( - global, state, lib, target, &mut prop, level, + global, caches, lib, target, &mut prop, level, ) .map(|r| (r, false)) .map_err( @@ -533,7 +533,7 @@ impl Engine { let (result, may_be_changed) = self .eval_dot_index_chain_helper( - global, state, lib, this_ptr, val, root, rhs, &x.rhs, + global, caches, lib, this_ptr, val, root, rhs, &x.rhs, *options, idx_values, rhs_chain, level, new_val, ) .map_err(|err| err.fill_position(*x_pos))?; @@ -544,7 +544,7 @@ impl Engine { let mut arg_values = [target.as_mut(), val.as_mut()]; let args = &mut arg_values; self.exec_fn_call( - None, global, state, lib, setter, hash_set, args, + None, global, caches, lib, setter, hash_set, args, is_ref_mut, true, pos, level, ) .or_else( @@ -554,7 +554,7 @@ impl Engine { let idx = &mut name.into(); let new_val = val; self.call_indexer_set( - global, state, lib, target, idx, new_val, + global, caches, lib, target, idx, new_val, is_ref_mut, level, ) .or_else(|e| match *e { @@ -581,11 +581,11 @@ impl Engine { #[cfg(feature = "debugging")] let reset_debugger = self.run_debugger_with_reset( - scope, global, state, lib, this_ptr, _node, level, + scope, global, caches, lib, this_ptr, _node, level, )?; let result = self.make_method_call( - global, state, lib, name, *hashes, target, args, pos, level, + global, caches, lib, name, *hashes, target, args, pos, level, ); #[cfg(feature = "debugging")] @@ -595,8 +595,8 @@ impl Engine { let val = &mut val.into(); self.eval_dot_index_chain_helper( - global, state, lib, this_ptr, val, root, rhs, &x.rhs, *options, - idx_values, rhs_chain, level, new_val, + global, caches, lib, this_ptr, val, root, rhs, &x.rhs, + *options, idx_values, rhs_chain, level, new_val, ) .map_err(|err| err.fill_position(pos)) } @@ -620,7 +620,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, @@ -638,7 +638,7 @@ impl Engine { let idx_values = &mut StaticVec::new_const(); self.eval_dot_index_chain_arguments( - scope, global, state, lib, this_ptr, rhs, options, chain_type, idx_values, 0, level, + scope, global, caches, lib, this_ptr, rhs, options, chain_type, idx_values, 0, level, )?; let is_assignment = new_val.is_some(); @@ -647,19 +647,19 @@ impl Engine { // id.??? or id[???] Expr::Variable(x, .., var_pos) => { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, state, lib, this_ptr, lhs, level)?; + self.run_debugger(scope, global, caches, lib, this_ptr, lhs, level)?; #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut global.num_operations, *var_pos)?; let (mut target, ..) = - self.search_namespace(scope, global, state, lib, this_ptr, lhs, level)?; + self.search_namespace(scope, global, caches, lib, this_ptr, lhs, level)?; let obj_ptr = &mut target; let root = (x.3.as_str(), *var_pos); self.eval_dot_index_chain_helper( - global, state, lib, &mut None, obj_ptr, root, expr, rhs, options, idx_values, + global, caches, lib, &mut None, obj_ptr, root, expr, rhs, options, idx_values, chain_type, level, new_val, ) .map(|(v, ..)| v) @@ -669,11 +669,11 @@ impl Engine { _ if is_assignment => unreachable!("cannot assign to an expression"), // {expr}.??? or {expr}[???] expr => { - let value = self.eval_expr(scope, global, state, lib, this_ptr, expr, level)?; + let value = self.eval_expr(scope, global, caches, lib, this_ptr, expr, level)?; let obj_ptr = &mut value.into(); let root = ("", expr.start_position()); self.eval_dot_index_chain_helper( - global, state, lib, this_ptr, obj_ptr, root, expr, rhs, options, idx_values, + global, caches, lib, this_ptr, obj_ptr, root, expr, rhs, options, idx_values, chain_type, level, new_val, ) .map(|(v, ..)| if is_assignment { Dynamic::UNIT } else { v }) @@ -689,7 +689,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, @@ -713,7 +713,7 @@ impl Engine { (crate::FnArgsVec::with_capacity(args.len()), Position::NONE), |(mut values, mut pos), expr| { let (value, arg_pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)?; + self.get_arg_value(scope, global, caches, lib, this_ptr, expr, level)?; if values.is_empty() { pos = arg_pos; } @@ -758,7 +758,7 @@ impl Engine { (crate::FnArgsVec::with_capacity(args.len()), Position::NONE), |(mut values, mut pos), expr| { let (value, arg_pos) = self.get_arg_value( - scope, global, state, lib, this_ptr, expr, level, + scope, global, caches, lib, this_ptr, expr, level, )?; if values.is_empty() { pos = arg_pos @@ -779,7 +779,7 @@ impl Engine { } #[cfg(not(feature = "no_index"))] _ if _parent_chain_type == ChainType::Indexing => self - .eval_expr(scope, global, state, lib, this_ptr, lhs, level) + .eval_expr(scope, global, caches, lib, this_ptr, lhs, level) .map(|v| { super::ChainArgument::from_index_value( v.flatten(), @@ -793,7 +793,7 @@ impl Engine { let chain_type = expr.into(); self.eval_dot_index_chain_arguments( - scope, global, state, lib, this_ptr, rhs, *options, chain_type, idx_values, + scope, global, caches, lib, this_ptr, rhs, *options, chain_type, idx_values, size, level, )?; @@ -806,7 +806,7 @@ impl Engine { } #[cfg(not(feature = "no_index"))] _ if _parent_chain_type == ChainType::Indexing => idx_values.push( - self.eval_expr(scope, global, state, lib, this_ptr, expr, level) + self.eval_expr(scope, global, caches, lib, this_ptr, expr, level) .map(|v| { super::ChainArgument::from_index_value(v.flatten(), expr.start_position()) })?, @@ -823,7 +823,7 @@ impl Engine { fn call_indexer_get( &self, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], target: &mut Dynamic, idx: &mut Dynamic, @@ -835,7 +835,7 @@ impl Engine { let pos = Position::NONE; self.exec_fn_call( - None, global, state, lib, fn_name, hash_get, args, true, true, pos, level, + None, global, caches, lib, fn_name, hash_get, args, true, true, pos, level, ) .map(|(r, ..)| r) } @@ -846,7 +846,7 @@ impl Engine { fn call_indexer_set( &self, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], target: &mut Dynamic, idx: &mut Dynamic, @@ -860,7 +860,7 @@ impl Engine { let pos = Position::NONE; self.exec_fn_call( - None, global, state, lib, fn_name, hash_set, args, is_ref_mut, true, pos, level, + None, global, caches, lib, fn_name, hash_set, args, is_ref_mut, true, pos, level, ) } @@ -869,7 +869,7 @@ impl Engine { fn get_indexed_mut<'t>( &self, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], target: &'t mut Dynamic, mut idx: Dynamic, @@ -1064,7 +1064,7 @@ impl Engine { } _ if use_indexers => self - .call_indexer_get(global, state, lib, target, &mut idx, level) + .call_indexer_get(global, caches, lib, target, &mut idx, level) .map(Into::into), _ => Err(ERR::ErrorIndexingType( diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index e0dd361e..f79dd398 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -1,7 +1,7 @@ //! Module defining the debugging interface. #![cfg(feature = "debugging")] -use super::{EvalContext, EvalState, GlobalRuntimeState}; +use super::{Caches, EvalContext, GlobalRuntimeState}; use crate::ast::{ASTNode, Expr, Stmt}; use crate::{Dynamic, Engine, EvalAltResult, Identifier, Module, Position, RhaiResultOf, Scope}; #[cfg(feature = "no_std")] @@ -406,7 +406,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, node: impl Into>, @@ -414,7 +414,7 @@ impl Engine { ) -> RhaiResultOf<()> { if self.debugger.is_some() { if let Some(cmd) = - self.run_debugger_with_reset_raw(scope, global, state, lib, this_ptr, node, level)? + self.run_debugger_with_reset_raw(scope, global, caches, lib, this_ptr, node, level)? { global.debugger.status = cmd; } @@ -434,14 +434,14 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, node: impl Into>, level: usize, ) -> RhaiResultOf> { if self.debugger.is_some() { - self.run_debugger_with_reset_raw(scope, global, state, lib, this_ptr, node, level) + self.run_debugger_with_reset_raw(scope, global, caches, lib, this_ptr, node, level) } else { Ok(None) } @@ -458,7 +458,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, node: impl Into>, @@ -490,7 +490,7 @@ impl Engine { } }; - self.run_debugger_raw(scope, global, state, lib, this_ptr, node, event, level) + self.run_debugger_raw(scope, global, caches, lib, this_ptr, node, event, level) } /// Run the debugger callback unconditionally. /// @@ -504,7 +504,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, node: ASTNode<'a>, @@ -522,7 +522,7 @@ impl Engine { engine: self, scope, global, - state, + caches, lib, this_ptr, level, diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index f378076b..e05b9c60 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -1,21 +1,21 @@ //! Evaluation context. -use super::{EvalState, GlobalRuntimeState}; +use super::{Caches, GlobalRuntimeState}; use crate::{Dynamic, Engine, Module, Scope}; #[cfg(feature = "no_std")] use std::prelude::v1::*; /// Context of a script evaluation process. #[derive(Debug)] -pub struct EvalContext<'a, 'x, 'px, 'm, 'pm, 's, 'ps, 'b, 't, 'pt> { +pub struct EvalContext<'a, 'x, 'px, 'm, 'pm, 'c, 'b, 't, 'pt> { /// The current [`Engine`]. pub(crate) engine: &'a Engine, /// The current [`Scope`]. pub(crate) scope: &'x mut Scope<'px>, /// The current [`GlobalRuntimeState`]. pub(crate) global: &'m mut GlobalRuntimeState<'pm>, - /// The current [evaluation state][EvalState]. - pub(crate) state: &'s mut EvalState<'ps>, + /// The current [caches][Caches]. + pub(crate) caches: &'c mut Caches, /// The current stack of imported [modules][Module]. pub(crate) lib: &'b [&'b Module], /// The current bound `this` pointer, if any. @@ -24,7 +24,7 @@ pub struct EvalContext<'a, 'x, 'px, 'm, 'pm, 's, 'ps, 'b, 't, 'pt> { pub(crate) level: usize, } -impl<'x, 'px, 'm, 'pm, 'pt> EvalContext<'_, 'x, 'px, 'm, 'pm, '_, '_, '_, '_, 'pt> { +impl<'x, 'px, 'm, 'pm, 'pt> EvalContext<'_, 'x, 'px, 'm, 'pm, '_, '_, '_, 'pt> { /// The current [`Engine`]. #[inline(always)] #[must_use] diff --git a/src/eval/eval_state.rs b/src/eval/eval_state.rs deleted file mode 100644 index 13acd9b5..00000000 --- a/src/eval/eval_state.rs +++ /dev/null @@ -1,72 +0,0 @@ -//! Evaluation state. - -use crate::func::call::FnResolutionCache; -use crate::StaticVec; -#[cfg(feature = "no_std")] -use std::prelude::v1::*; -use std::{collections::BTreeMap, marker::PhantomData}; - -/// _(internals)_ A type that holds all the current states of the [`Engine`][crate::Engine]. -/// Exported under the `internals` feature only. -#[derive(Debug, Clone)] -pub struct EvalState<'a> { - /// Force a [`Scope`][crate::Scope] search by name. - /// - /// Normally, access to variables are parsed with a relative offset into the - /// [`Scope`][crate::Scope] to avoid a lookup. - /// - /// In some situation, e.g. after running an `eval` statement, or after a custom syntax - /// statement, subsequent offsets may become mis-aligned. - /// - /// When that happens, this flag is turned on. - pub always_search_scope: bool, - /// Level of the current scope. - /// - /// The global (root) level is zero, a new block (or function call) is one level higher, and so on. - pub scope_level: usize, - /// Stack of function resolution caches. - fn_resolution_caches: StaticVec, - /// Take care of the lifetime parameter - dummy: PhantomData<&'a ()>, -} - -impl EvalState<'_> { - /// Create a new [`EvalState`]. - #[inline(always)] - #[must_use] - pub fn new() -> Self { - Self { - always_search_scope: false, - scope_level: 0, - fn_resolution_caches: StaticVec::new_const(), - dummy: Default::default(), - } - } - /// Get the number of function resolution cache(s) in the stack. - #[inline(always)] - #[must_use] - pub fn fn_resolution_caches_len(&self) -> usize { - self.fn_resolution_caches.len() - } - /// Get a mutable reference to the current function resolution cache. - #[inline] - #[must_use] - pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache { - if self.fn_resolution_caches.is_empty() { - // Push a new function resolution cache if the stack is empty - self.push_fn_resolution_cache(); - } - self.fn_resolution_caches.last_mut().unwrap() - } - /// Push an empty function resolution cache onto the stack and make it current. - #[allow(dead_code)] - #[inline(always)] - pub fn push_fn_resolution_cache(&mut self) { - self.fn_resolution_caches.push(BTreeMap::new()); - } - /// Rewind the function resolution caches stack to a particular size. - #[inline(always)] - pub fn rewind_fn_resolution_caches(&mut self, len: usize) { - self.fn_resolution_caches.truncate(len); - } -} diff --git a/src/eval/expr.rs b/src/eval/expr.rs index c2d35b32..5d28f5c2 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -1,6 +1,6 @@ //! Module defining functions for evaluating an expression. -use super::{EvalContext, EvalState, GlobalRuntimeState, Target}; +use super::{Caches, EvalContext, GlobalRuntimeState, Target}; use crate::ast::{Expr, FnCallExpr, OpAssignment}; use crate::engine::{KEYWORD_THIS, OP_CONCAT}; use crate::types::dynamic::AccessMode; @@ -17,7 +17,6 @@ impl Engine { pub(crate) fn search_imports( &self, global: &GlobalRuntimeState, - state: &mut EvalState, namespace: &crate::ast::Namespace, ) -> Option> { assert!(!namespace.is_empty()); @@ -25,7 +24,7 @@ impl Engine { let root = namespace.root(); // Qualified - check if the root module is directly indexed - let index = if state.always_search_scope { + let index = if global.always_search_scope { None } else { namespace.index() @@ -48,7 +47,7 @@ impl Engine { &self, scope: &'s mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &'s mut Option<&mut Dynamic>, expr: &Expr, @@ -56,24 +55,24 @@ impl Engine { ) -> RhaiResultOf<(Target<'s>, Position)> { match expr { Expr::Variable(_, Some(_), _) => { - self.search_scope_only(scope, global, state, lib, this_ptr, expr, level) + self.search_scope_only(scope, global, caches, lib, this_ptr, expr, level) } Expr::Variable(v, None, _var_pos) => match v.as_ref() { // Normal variable access #[cfg(not(feature = "no_module"))] (_, ns, ..) if ns.is_empty() => { - self.search_scope_only(scope, global, state, lib, this_ptr, expr, level) + self.search_scope_only(scope, global, caches, lib, this_ptr, expr, level) } #[cfg(feature = "no_module")] (_, (), ..) => { - self.search_scope_only(scope, global, state, lib, this_ptr, expr, level) + self.search_scope_only(scope, global, caches, lib, this_ptr, expr, level) } // Qualified variable access #[cfg(not(feature = "no_module"))] (_, namespace, hash_var, var_name) => { // foo:bar::baz::VARIABLE - if let Some(module) = self.search_imports(global, state, namespace) { + if let Some(module) = self.search_imports(global, namespace) { return if let Some(mut target) = module.get_qualified_var(*hash_var) { // Module variables are constant target.set_access_mode(AccessMode::ReadOnly); @@ -131,7 +130,7 @@ impl Engine { &self, scope: &'s mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &'s mut Option<&mut Dynamic>, expr: &Expr, @@ -148,7 +147,7 @@ impl Engine { Err(ERR::ErrorUnboundThis(*pos).into()) } } - _ if state.always_search_scope => (0, expr.start_position()), + _ if global.always_search_scope => (0, expr.start_position()), Expr::Variable(.., Some(i), pos) => (i.get() as usize, *pos), Expr::Variable(v, None, pos) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos), _ => unreachable!("Expr::Variable expected but gets {:?}", expr), @@ -160,7 +159,7 @@ impl Engine { engine: self, scope, global, - state, + caches, lib, this_ptr, level, @@ -205,7 +204,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, expr: &FnCallExpr, @@ -228,7 +227,7 @@ impl Engine { let hash = hashes.native; return self.make_qualified_function_call( - scope, global, state, lib, this_ptr, namespace, name, args, hash, pos, level, + scope, global, caches, lib, this_ptr, namespace, name, args, hash, pos, level, ); } @@ -239,7 +238,7 @@ impl Engine { ); self.make_function_call( - scope, global, state, lib, this_ptr, name, first_arg, args, *hashes, *capture, pos, + scope, global, caches, lib, this_ptr, name, first_arg, args, *hashes, *capture, pos, level, ) } @@ -256,7 +255,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, @@ -270,13 +269,13 @@ impl Engine { if let Expr::FnCall(x, ..) = expr { #[cfg(feature = "debugging")] let reset_debugger = - self.run_debugger_with_reset(scope, global, state, lib, this_ptr, expr, level)?; + self.run_debugger_with_reset(scope, global, caches, lib, this_ptr, expr, level)?; #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut global.num_operations, expr.position())?; let result = - self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, x.pos, level); + self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level); #[cfg(feature = "debugging")] global.debugger.reset_status(reset_debugger); @@ -289,7 +288,7 @@ impl Engine { // will cost more than the mis-predicted `match` branch. if let Expr::Variable(x, index, var_pos) = expr { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, state, lib, this_ptr, expr, level)?; + self.run_debugger(scope, global, caches, lib, this_ptr, expr, level)?; #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut global.num_operations, expr.position())?; @@ -300,14 +299,14 @@ impl Engine { .cloned() .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()) } else { - self.search_namespace(scope, global, state, lib, this_ptr, expr, level) + self.search_namespace(scope, global, caches, lib, this_ptr, expr, level) .map(|(val, ..)| val.take_or_clone()) }; } #[cfg(feature = "debugging")] let reset_debugger = - self.run_debugger_with_reset(scope, global, state, lib, this_ptr, expr, level)?; + self.run_debugger_with_reset(scope, global, caches, lib, this_ptr, expr, level)?; #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut global.num_operations, expr.position())?; @@ -330,7 +329,7 @@ impl Engine { for expr in x.iter() { let item = - match self.eval_expr(scope, global, state, lib, this_ptr, expr, level) { + match self.eval_expr(scope, global, caches, lib, this_ptr, expr, level) { Ok(r) => r, err => { result = err; @@ -340,7 +339,7 @@ impl Engine { if let Err(err) = self.eval_op_assignment( global, - state, + caches, lib, Some(OpAssignment::new(OP_CONCAT)), expr.start_position(), @@ -367,7 +366,7 @@ impl Engine { for item_expr in x.iter() { let value = match self - .eval_expr(scope, global, state, lib, this_ptr, item_expr, level) + .eval_expr(scope, global, caches, lib, this_ptr, item_expr, level) { Ok(r) => r.flatten(), err => { @@ -405,7 +404,7 @@ impl Engine { for (key, value_expr) in x.0.iter() { let value = match self - .eval_expr(scope, global, state, lib, this_ptr, value_expr, level) + .eval_expr(scope, global, caches, lib, this_ptr, value_expr, level) { Ok(r) => r.flatten(), err => { @@ -431,7 +430,7 @@ impl Engine { Expr::And(x, ..) => { let lhs = self - .eval_expr(scope, global, state, lib, this_ptr, &x.lhs, level) + .eval_expr(scope, global, caches, lib, this_ptr, &x.lhs, level) .and_then(|v| { v.as_bool().map_err(|typ| { self.make_type_mismatch_err::(typ, x.lhs.position()) @@ -439,7 +438,7 @@ impl Engine { }); if let Ok(true) = lhs { - self.eval_expr(scope, global, state, lib, this_ptr, &x.rhs, level) + self.eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) .and_then(|v| { v.as_bool() .map_err(|typ| { @@ -454,7 +453,7 @@ impl Engine { Expr::Or(x, ..) => { let lhs = self - .eval_expr(scope, global, state, lib, this_ptr, &x.lhs, level) + .eval_expr(scope, global, caches, lib, this_ptr, &x.lhs, level) .and_then(|v| { v.as_bool().map_err(|typ| { self.make_type_mismatch_err::(typ, x.lhs.position()) @@ -462,7 +461,7 @@ impl Engine { }); if let Ok(false) = lhs { - self.eval_expr(scope, global, state, lib, this_ptr, &x.rhs, level) + self.eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) .and_then(|v| { v.as_bool() .map_err(|typ| { @@ -491,7 +490,7 @@ impl Engine { engine: self, scope, global, - state, + caches, lib, this_ptr, level, @@ -504,17 +503,17 @@ impl Engine { Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT), Expr::Stmt(x) => { - self.eval_stmt_block(scope, global, state, lib, this_ptr, x, true, level) + self.eval_stmt_block(scope, global, caches, lib, this_ptr, x, true, level) } #[cfg(not(feature = "no_index"))] Expr::Index(..) => { - self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None) + self.eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, None) } #[cfg(not(feature = "no_object"))] Expr::Dot(..) => { - self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None) + self.eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, None) } _ => unreachable!("expression cannot be evaluated: {:?}", expr), diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index a7d6aeb6..ae02a67d 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -30,16 +30,31 @@ pub struct GlobalRuntimeState<'a> { #[cfg(not(feature = "no_module"))] modules: crate::StaticVec>, /// Source of the current context. + /// /// No source if the string is empty. pub source: Identifier, /// Number of operations performed. pub num_operations: u64, /// Number of modules loaded. pub num_modules_loaded: usize, + /// Level of the current scope. + /// + /// The global (root) level is zero, a new block (or function call) is one level higher, and so on. + pub scope_level: usize, + /// Force a [`Scope`][crate::Scope] search by name. + /// + /// Normally, access to variables are parsed with a relative offset into the + /// [`Scope`][crate::Scope] to avoid a lookup. + /// + /// In some situation, e.g. after running an `eval` statement, or after a custom syntax + /// statement, subsequent offsets may become mis-aligned. + /// + /// When that happens, this flag is turned on. + pub always_search_scope: bool, /// Function call hashes to index getters and setters. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn_hash_indexing: (u64, u64), - /// Embedded [crate::Module][crate::Module] resolver. + /// Embedded [module][crate::Module] resolver. #[cfg(not(feature = "no_module"))] pub embedded_module_resolver: Option>, @@ -71,6 +86,8 @@ impl GlobalRuntimeState<'_> { source: Identifier::new_const(), num_operations: 0, num_modules_loaded: 0, + scope_level: 0, + always_search_scope: false, #[cfg(not(feature = "no_module"))] embedded_module_resolver: None, #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] @@ -92,7 +109,7 @@ impl GlobalRuntimeState<'_> { pub fn num_imports(&self) -> usize { self.keys.len() } - /// Get the globally-imported [crate::Module][crate::Module] at a particular index. + /// Get the globally-imported [module][crate::Module] at a particular index. /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] @@ -101,7 +118,7 @@ impl GlobalRuntimeState<'_> { pub fn get_shared_import(&self, index: usize) -> Option> { self.modules.get(index).cloned() } - /// Get a mutable reference to the globally-imported [crate::Module][crate::Module] at a + /// Get a mutable reference to the globally-imported [module][crate::Module] at a /// particular index. /// /// Not available under `no_module`. @@ -115,7 +132,7 @@ impl GlobalRuntimeState<'_> { ) -> Option<&mut crate::Shared> { self.modules.get_mut(index) } - /// Get the index of a globally-imported [crate::Module][crate::Module] by name. + /// Get the index of a globally-imported [module][crate::Module] by name. /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] @@ -132,7 +149,7 @@ impl GlobalRuntimeState<'_> { } }) } - /// Push an imported [crate::Module][crate::Module] onto the stack. + /// Push an imported [module][crate::Module] onto the stack. /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 0b87a2dd..ea11a5d8 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -1,13 +1,14 @@ +mod cache; mod chaining; mod data_check; mod debugger; mod eval_context; -mod eval_state; mod expr; mod global_state; mod stmt; mod target; +pub use cache::{Caches, FnResolutionCache, FnResolutionCacheEntry}; #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] pub use chaining::{ChainArgument, ChainType}; #[cfg(feature = "debugging")] @@ -16,7 +17,6 @@ pub use debugger::{ OnDebuggerCallback, OnDebuggingInit, }; pub use eval_context::EvalContext; -pub use eval_state::EvalState; #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] pub use global_state::GlobalConstants; diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 4e33f28a..20d473d1 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -1,6 +1,6 @@ //! Module defining functions for evaluating a statement. -use super::{EvalContext, EvalState, GlobalRuntimeState, Target}; +use super::{Caches, EvalContext, GlobalRuntimeState, Target}; use crate::api::events::VarDefInfo; use crate::ast::{ ASTFlags, BinaryExpr, Expr, Ident, OpAssignment, Stmt, SwitchCases, TryCatchBlock, @@ -25,7 +25,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, statements: &[Stmt], @@ -36,14 +36,14 @@ impl Engine { return Ok(Dynamic::UNIT); } - let orig_always_search_scope = state.always_search_scope; + let orig_always_search_scope = global.always_search_scope; let orig_scope_len = scope.len(); #[cfg(not(feature = "no_module"))] let orig_imports_len = global.num_imports(); - let orig_fn_resolution_caches_len = state.fn_resolution_caches_len(); + let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len(); if restore_orig_state { - state.scope_level += 1; + global.scope_level += 1; } let mut result = Ok(Dynamic::UNIT); @@ -55,7 +55,7 @@ impl Engine { result = self.eval_stmt( scope, global, - state, + caches, lib, this_ptr, stmt, @@ -76,34 +76,34 @@ impl Engine { .skip(imports_len) .any(|(.., m)| m.contains_indexed_global_functions()) { - if state.fn_resolution_caches_len() > orig_fn_resolution_caches_len { + 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 - state.fn_resolution_cache_mut().clear(); + caches.fn_resolution_cache_mut().clear(); } else if restore_orig_state { // When new module is imported with global functions, push a new cache - state.push_fn_resolution_cache(); + caches.push_fn_resolution_cache(); } else { // When the block is to be evaluated in-place, just clear the current cache - state.fn_resolution_cache_mut().clear(); + caches.fn_resolution_cache_mut().clear(); } } } } // If imports list is modified, pop the functions lookup cache - state.rewind_fn_resolution_caches(orig_fn_resolution_caches_len); + caches.rewind_fn_resolution_caches(orig_fn_resolution_caches_len); if restore_orig_state { scope.rewind(orig_scope_len); - state.scope_level -= 1; + 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 - state.always_search_scope = orig_always_search_scope; + global.always_search_scope = orig_always_search_scope; } result @@ -114,7 +114,7 @@ impl Engine { pub(crate) fn eval_op_assignment( &self, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], op_info: Option, op_pos: Position, @@ -157,7 +157,7 @@ impl Engine { let level = level + 1; match self.call_native_fn( - global, state, lib, op_assign, hash, args, true, true, op_pos, level, + global, caches, lib, op_assign, hash, args, true, true, op_pos, level, ) { Ok(_) => { #[cfg(not(feature = "unchecked"))] @@ -167,7 +167,7 @@ impl Engine { { // Expand to `var = var op rhs` let (value, ..) = self.call_native_fn( - global, state, lib, op, hash_op, args, true, false, op_pos, level, + global, caches, lib, op, hash_op, args, true, false, op_pos, level, )?; #[cfg(not(feature = "unchecked"))] @@ -197,7 +197,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, stmt: &Stmt, @@ -206,7 +206,7 @@ impl Engine { ) -> RhaiResult { #[cfg(feature = "debugging")] let reset_debugger = - self.run_debugger_with_reset(scope, global, state, lib, this_ptr, stmt, level)?; + self.run_debugger_with_reset(scope, global, caches, lib, this_ptr, stmt, level)?; // Coded this way for better branch prediction. // Popular branches are lifted out of the `match` statement into their own branches. @@ -217,7 +217,7 @@ impl Engine { self.inc_operations(&mut global.num_operations, stmt.position())?; let result = - self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, x.pos, level); + self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level); #[cfg(feature = "debugging")] global.debugger.reset_status(reset_debugger); @@ -236,12 +236,12 @@ impl Engine { let (op_info, BinaryExpr { lhs, rhs }) = x.as_ref(); let rhs_result = self - .eval_expr(scope, global, state, lib, this_ptr, rhs, level) + .eval_expr(scope, global, caches, lib, this_ptr, rhs, level) .map(Dynamic::flatten); if let Ok(rhs_val) = rhs_result { let search_result = - self.search_namespace(scope, global, state, lib, this_ptr, lhs, level); + self.search_namespace(scope, global, caches, lib, this_ptr, lhs, level); if let Ok(search_val) = search_result { let (mut lhs_ptr, pos) = search_val; @@ -261,7 +261,7 @@ impl Engine { let lhs_ptr = &mut lhs_ptr; self.eval_op_assignment( - global, state, lib, *op_info, *op_pos, lhs_ptr, root, rhs_val, level, + global, caches, lib, *op_info, *op_pos, lhs_ptr, root, rhs_val, level, ) .map_err(|err| err.fill_position(rhs.start_position())) .map(|_| Dynamic::UNIT) @@ -275,7 +275,7 @@ impl Engine { let (op_info, BinaryExpr { lhs, rhs }) = x.as_ref(); let rhs_result = self - .eval_expr(scope, global, state, lib, this_ptr, rhs, level) + .eval_expr(scope, global, caches, lib, this_ptr, rhs, level) .map(Dynamic::flatten); if let Ok(rhs_val) = rhs_result { @@ -291,14 +291,14 @@ impl Engine { #[cfg(not(feature = "no_index"))] Expr::Index(..) => self .eval_dot_index_chain( - scope, global, state, lib, this_ptr, lhs, level, _new_val, + scope, global, caches, lib, this_ptr, lhs, level, _new_val, ) .map(|_| Dynamic::UNIT), // dot_lhs.dot_rhs op= rhs #[cfg(not(feature = "no_object"))] Expr::Dot(..) => self .eval_dot_index_chain( - scope, global, state, lib, this_ptr, lhs, level, _new_val, + scope, global, caches, lib, this_ptr, lhs, level, _new_val, ) .map(|_| Dynamic::UNIT), _ => unreachable!("cannot assign to expression: {:?}", lhs), @@ -323,21 +323,21 @@ impl Engine { // Expression as statement Stmt::Expr(expr) => self - .eval_expr(scope, global, state, lib, this_ptr, expr, level) + .eval_expr(scope, global, caches, lib, this_ptr, expr, level) .map(Dynamic::flatten), // Block scope Stmt::Block(statements, ..) if statements.is_empty() => Ok(Dynamic::UNIT), - Stmt::Block(statements, ..) => { - self.eval_stmt_block(scope, global, state, lib, this_ptr, statements, true, level) - } + Stmt::Block(statements, ..) => self.eval_stmt_block( + scope, global, caches, lib, this_ptr, statements, true, level, + ), // If statement Stmt::If(x, ..) => { let (expr, if_block, else_block) = x.as_ref(); let guard_val = self - .eval_expr(scope, global, state, lib, this_ptr, expr, level) + .eval_expr(scope, global, caches, lib, this_ptr, expr, level) .and_then(|v| { v.as_bool().map_err(|typ| { self.make_type_mismatch_err::(typ, expr.position()) @@ -348,7 +348,7 @@ impl Engine { Ok(true) => { if !if_block.is_empty() { self.eval_stmt_block( - scope, global, state, lib, this_ptr, if_block, true, level, + scope, global, caches, lib, this_ptr, if_block, true, level, ) } else { Ok(Dynamic::UNIT) @@ -357,7 +357,7 @@ impl Engine { Ok(false) => { if !else_block.is_empty() { self.eval_stmt_block( - scope, global, state, lib, this_ptr, else_block, true, level, + scope, global, caches, lib, this_ptr, else_block, true, level, ) } else { Ok(Dynamic::UNIT) @@ -378,7 +378,8 @@ impl Engine { }, ) = x.as_ref(); - let value_result = self.eval_expr(scope, global, state, lib, this_ptr, expr, level); + let value_result = + self.eval_expr(scope, global, caches, lib, this_ptr, expr, level); if let Ok(value) = value_result { let stmt_block_result = if value.is_hashable() { @@ -392,15 +393,17 @@ impl Engine { .condition .as_ref() .map(|cond| { - self.eval_expr(scope, global, state, lib, this_ptr, cond, level) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::( - typ, - cond.position(), - ) - }) + self.eval_expr( + scope, global, caches, lib, this_ptr, cond, level, + ) + .and_then(|v| { + v.as_bool().map_err(|typ| { + self.make_type_mismatch_err::( + typ, + cond.position(), + ) }) + }) }) .unwrap_or(Ok(true)); @@ -425,7 +428,7 @@ impl Engine { .as_ref() .map(|cond| { self.eval_expr( - scope, global, state, lib, this_ptr, cond, level, + scope, global, caches, lib, this_ptr, cond, level, ) .and_then(|v| { v.as_bool().map_err(|typ| { @@ -460,7 +463,7 @@ impl Engine { if let Ok(Some(statements)) = stmt_block_result { if !statements.is_empty() { self.eval_stmt_block( - scope, global, state, lib, this_ptr, statements, true, level, + scope, global, caches, lib, this_ptr, statements, true, level, ) } else { Ok(Dynamic::UNIT) @@ -469,7 +472,7 @@ impl Engine { // Default match clause if !def_case.is_empty() { self.eval_stmt_block( - scope, global, state, lib, this_ptr, def_case, true, level, + scope, global, caches, lib, this_ptr, def_case, true, level, ) } else { Ok(Dynamic::UNIT) @@ -488,7 +491,7 @@ impl Engine { if !body.is_empty() { match self - .eval_stmt_block(scope, global, state, lib, this_ptr, body, true, level) + .eval_stmt_block(scope, global, caches, lib, this_ptr, body, true, level) { Ok(_) => (), Err(err) => match *err { @@ -508,7 +511,7 @@ impl Engine { let (expr, body) = x.as_ref(); let condition = self - .eval_expr(scope, global, state, lib, this_ptr, expr, level) + .eval_expr(scope, global, caches, lib, this_ptr, expr, level) .and_then(|v| { v.as_bool().map_err(|typ| { self.make_type_mismatch_err::(typ, expr.position()) @@ -519,9 +522,9 @@ impl Engine { Ok(false) => break Ok(Dynamic::UNIT), Ok(true) if body.is_empty() => (), Ok(true) => { - match self - .eval_stmt_block(scope, global, state, lib, this_ptr, body, true, level) - { + match self.eval_stmt_block( + scope, global, caches, lib, this_ptr, body, true, level, + ) { Ok(_) => (), Err(err) => match *err { ERR::LoopBreak(false, ..) => (), @@ -541,7 +544,7 @@ impl Engine { if !body.is_empty() { match self - .eval_stmt_block(scope, global, state, lib, this_ptr, body, true, level) + .eval_stmt_block(scope, global, caches, lib, this_ptr, body, true, level) { Ok(_) => (), Err(err) => match *err { @@ -553,7 +556,7 @@ impl Engine { } let condition = self - .eval_expr(scope, global, state, lib, this_ptr, &expr, level) + .eval_expr(scope, global, caches, lib, this_ptr, &expr, level) .and_then(|v| { v.as_bool().map_err(|typ| { self.make_type_mismatch_err::(typ, expr.position()) @@ -572,7 +575,7 @@ impl Engine { let (var_name, counter, expr, statements) = x.as_ref(); let iter_result = self - .eval_expr(scope, global, state, lib, this_ptr, expr, level) + .eval_expr(scope, global, caches, lib, this_ptr, expr, level) .map(Dynamic::flatten); if let Ok(iter_obj) = iter_result { @@ -671,7 +674,7 @@ impl Engine { } let result = self.eval_stmt_block( - scope, global, state, lib, this_ptr, statements, true, level, + scope, global, caches, lib, this_ptr, statements, true, level, ); match result { @@ -715,7 +718,7 @@ impl Engine { } = x.as_ref(); let result = self - .eval_stmt_block(scope, global, state, lib, this_ptr, try_block, true, level) + .eval_stmt_block(scope, global, caches, lib, this_ptr, try_block, true, level) .map(|_| Dynamic::UNIT); match result { @@ -767,7 +770,7 @@ impl Engine { let result = self.eval_stmt_block( scope, global, - state, + caches, lib, this_ptr, catch_block, @@ -794,7 +797,7 @@ impl Engine { // Throw value Stmt::Return(Some(expr), options, pos) if options.contains(ASTFlags::BREAK) => self - .eval_expr(scope, global, state, lib, this_ptr, expr, level) + .eval_expr(scope, global, caches, lib, this_ptr, expr, level) .and_then(|v| Err(ERR::ErrorRuntime(v.flatten(), *pos).into())), // Empty throw @@ -804,7 +807,7 @@ impl Engine { // Return value Stmt::Return(Some(expr), .., pos) => self - .eval_expr(scope, global, state, lib, this_ptr, expr, level) + .eval_expr(scope, global, caches, lib, this_ptr, expr, level) .and_then(|v| Err(ERR::Return(v.flatten(), *pos).into())), // Empty return @@ -828,7 +831,7 @@ impl Engine { // Check variable definition filter let result = if let Some(ref filter) = self.def_var_filter { let will_shadow = scope.contains(var_name); - let nesting_level = state.scope_level; + let nesting_level = global.scope_level; let is_const = access == AccessMode::ReadOnly; let info = VarDefInfo { name: var_name, @@ -840,7 +843,7 @@ impl Engine { engine: self, scope, global, - state, + caches, lib, this_ptr, level, @@ -864,7 +867,7 @@ impl Engine { } else { // Evaluate initial value let value_result = self - .eval_expr(scope, global, state, lib, this_ptr, expr, level) + .eval_expr(scope, global, caches, lib, this_ptr, expr, level) .map(Dynamic::flatten); if let Ok(mut value) = value_result { @@ -872,7 +875,7 @@ impl Engine { // Put global constants into global module #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_module"))] - if state.scope_level == 0 + if global.scope_level == 0 && access == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) { @@ -927,7 +930,7 @@ impl Engine { } let path_result = self - .eval_expr(scope, global, state, lib, this_ptr, &expr, level) + .eval_expr(scope, global, caches, lib, this_ptr, &expr, level) .and_then(|v| { let typ = v.type_name(); v.try_cast::().ok_or_else(|| { diff --git a/src/func/call.rs b/src/func/call.rs index 190a29c7..c74beef5 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -9,7 +9,7 @@ use crate::engine::{ KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; -use crate::eval::{EvalState, GlobalRuntimeState}; +use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::{ calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnArgsVec, FnPtr, Identifier, ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult, @@ -19,7 +19,6 @@ use crate::{ use std::prelude::v1::*; use std::{ any::{type_name, TypeId}, - collections::BTreeMap, convert::TryFrom, mem, }; @@ -128,24 +127,6 @@ pub fn ensure_no_data_race( Ok(()) } -/// _(internals)_ An entry in a function resolution cache. -/// Exported under the `internals` feature only. -#[derive(Debug, Clone)] -pub struct FnResolutionCacheEntry { - /// Function. - pub func: CallableFunction, - /// Optional source. - /// No source if the string is empty. - pub source: Identifier, -} - -/// _(internals)_ A function resolution cache. -/// Exported under the `internals` feature only. -/// -/// [`FnResolutionCacheEntry`] is [`Box`]ed in order to pack as many entries inside a single B-Tree -/// level as possible. -pub type FnResolutionCache = BTreeMap>>; - impl Engine { /// Generate the signature for a function call. #[inline] @@ -196,7 +177,7 @@ impl Engine { fn resolve_fn<'s>( &self, _global: &GlobalRuntimeState, - state: &'s mut EvalState, + state: &'s mut Caches, lib: &[&Module], fn_name: &str, hash_script: u64, @@ -344,7 +325,7 @@ impl Engine { pub(crate) fn call_native_fn( &self, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], name: &str, hash: u64, @@ -362,7 +343,7 @@ impl Engine { // Check if function access already in the cache let func = self.resolve_fn( global, - state, + caches, lib, name, hash, @@ -439,7 +420,7 @@ impl Engine { Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), }; match self - .run_debugger_raw(scope, global, state, lib, &mut None, node, event, level) + .run_debugger_raw(scope, global, caches, lib, &mut None, node, event, level) { Ok(_) => (), Err(err) => _result = Err(err), @@ -576,7 +557,7 @@ impl Engine { &self, _scope: Option<&mut Scope>, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], fn_name: &str, hashes: FnCallHashes, @@ -619,7 +600,7 @@ impl Engine { false } else { let hash_script = calc_fn_hash(fn_name.as_str(), num_params as usize); - self.has_script_fn(Some(global), state, lib, hash_script) + self.has_script_fn(Some(global), caches, lib, hash_script) } .into(), false, @@ -650,7 +631,7 @@ impl Engine { if let Some(FnResolutionCacheEntry { func, mut source }) = self .resolve_fn( global, - state, + caches, lib, fn_name, hashes.script, @@ -687,7 +668,7 @@ impl Engine { self.call_script_fn( scope, global, - state, + caches, lib, &mut Some(*first_arg), func, @@ -706,7 +687,7 @@ impl Engine { } let result = self.call_script_fn( - scope, global, state, lib, &mut None, func, args, true, pos, level, + scope, global, caches, lib, &mut None, func, args, true, pos, level, ); // Restore the original reference @@ -724,7 +705,7 @@ impl Engine { // Native function call let hash = hashes.native; self.call_native_fn( - global, state, lib, fn_name, hash, args, is_ref_mut, false, pos, level, + global, caches, lib, fn_name, hash, args, is_ref_mut, false, pos, level, ) } @@ -735,13 +716,13 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, statements: &[Stmt], lib: &[&Module], level: usize, ) -> RhaiResult { self.eval_stmt_block( - scope, global, state, lib, &mut None, statements, false, level, + scope, global, caches, lib, &mut None, statements, false, level, ) .or_else(|err| match *err { ERR::Return(out, ..) => Ok(out), @@ -757,7 +738,7 @@ impl Engine { pub(crate) fn make_method_call( &self, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], fn_name: &str, mut hash: FnCallHashes, @@ -786,7 +767,7 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - None, global, state, lib, fn_name, new_hash, &mut args, false, false, pos, + None, global, caches, lib, fn_name, new_hash, &mut args, false, false, pos, level, ) } @@ -822,7 +803,7 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - None, global, state, lib, fn_name, new_hash, &mut args, is_ref_mut, true, pos, + None, global, caches, lib, fn_name, new_hash, &mut args, is_ref_mut, true, pos, level, ) } @@ -892,7 +873,7 @@ impl Engine { args.extend(call_args.iter_mut()); self.exec_fn_call( - None, global, state, lib, fn_name, hash, &mut args, is_ref_mut, true, pos, + None, global, caches, lib, fn_name, hash, &mut args, is_ref_mut, true, pos, level, ) } @@ -914,7 +895,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, arg_expr: &Expr, @@ -924,7 +905,7 @@ impl Engine { if self.debugger.is_some() { if let Some(value) = arg_expr.get_literal_value() { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?; + self.run_debugger(scope, global, caches, lib, this_ptr, arg_expr, level)?; return Ok((value, arg_expr.start_position())); } } @@ -935,7 +916,7 @@ impl Engine { matches!(status, crate::eval::DebuggerStatus::FunctionExit(..)) }); - let result = self.eval_expr(scope, global, state, lib, this_ptr, arg_expr, level); + let result = self.eval_expr(scope, global, caches, lib, this_ptr, arg_expr, level); // Restore function exit status #[cfg(feature = "debugging")] @@ -949,7 +930,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, fn_name: &str, @@ -973,7 +954,7 @@ impl Engine { KEYWORD_FN_PTR_CALL if total_args >= 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?; + self.get_arg_value(scope, global, caches, lib, this_ptr, arg, level)?; if !arg_value.is::() { let typ = self.map_type_name(arg_value.type_name()); @@ -1006,7 +987,7 @@ impl Engine { KEYWORD_FN_PTR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?; + self.get_arg_value(scope, global, caches, lib, this_ptr, arg, level)?; // Fn - only in function call style return arg_value @@ -1021,7 +1002,7 @@ impl Engine { KEYWORD_FN_PTR_CURRY if total_args > 1 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, first, level)?; + self.get_arg_value(scope, global, caches, lib, this_ptr, first, level)?; if !arg_value.is::() { let typ = self.map_type_name(arg_value.type_name()); @@ -1033,7 +1014,7 @@ impl Engine { // Append the new curried arguments to the existing list. let fn_curry = a_expr.iter().try_fold(fn_curry, |mut curried, expr| { let (value, ..) = - self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)?; + self.get_arg_value(scope, global, caches, lib, this_ptr, expr, level)?; curried.push(value); Ok::<_, RhaiError>(curried) })?; @@ -1046,7 +1027,7 @@ impl Engine { crate::engine::KEYWORD_IS_SHARED if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, ..) = - self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?; + self.get_arg_value(scope, global, caches, lib, this_ptr, arg, level)?; return Ok(arg_value.is_shared().into()); } @@ -1055,14 +1036,14 @@ impl Engine { crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, first, level)?; + self.get_arg_value(scope, global, caches, lib, this_ptr, first, level)?; let fn_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; let (arg_value, arg_pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, &a_expr[0], level)?; + self.get_arg_value(scope, global, caches, lib, this_ptr, &a_expr[0], level)?; let num_params = arg_value .as_int() @@ -1072,7 +1053,7 @@ impl Engine { false } else { let hash_script = calc_fn_hash(&fn_name, num_params as usize); - self.has_script_fn(Some(global), state, lib, hash_script) + self.has_script_fn(Some(global), caches, lib, hash_script) } .into()); } @@ -1081,7 +1062,7 @@ impl Engine { KEYWORD_IS_DEF_VAR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?; + self.get_arg_value(scope, global, caches, lib, this_ptr, arg, level)?; let var_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; @@ -1094,14 +1075,14 @@ impl Engine { let orig_scope_len = scope.len(); let arg = first_arg.unwrap(); let (arg_value, pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?; + self.get_arg_value(scope, global, caches, lib, this_ptr, arg, level)?; let script = &arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; let result = self.eval_script_expr_in_place( scope, global, - state, + caches, lib, script, pos, @@ -1111,7 +1092,7 @@ impl Engine { // IMPORTANT! If the eval defines new variables in the current scope, // all variable offsets from this point on will be mis-aligned. if scope.len() != orig_scope_len { - state.always_search_scope = true; + global.always_search_scope = true; } return result.map_err(|err| { @@ -1143,7 +1124,7 @@ impl Engine { .map(|&v| v) .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value(scope, global, state, lib, this_ptr, expr, level) + self.get_arg_value(scope, global, caches, lib, this_ptr, expr, level) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1154,7 +1135,7 @@ impl Engine { return self .exec_fn_call( - scope, global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, + scope, global, caches, lib, name, hashes, &mut args, is_ref_mut, false, pos, level, ) .map(|(v, ..)| v); @@ -1171,16 +1152,16 @@ impl Engine { let first_expr = first_arg.unwrap(); #[cfg(feature = "debugging")] - self.run_debugger(scope, global, state, lib, this_ptr, first_expr, level)?; + self.run_debugger(scope, global, caches, lib, this_ptr, first_expr, level)?; // func(x, ...) -> x.func(...) a_expr.iter().try_for_each(|expr| { - self.get_arg_value(scope, global, state, lib, this_ptr, expr, level) + self.get_arg_value(scope, global, caches, lib, this_ptr, expr, level) .map(|(value, ..)| arg_values.push(value.flatten())) })?; let (mut target, _pos) = - self.search_namespace(scope, global, state, lib, this_ptr, first_expr, level)?; + self.search_namespace(scope, global, caches, lib, this_ptr, first_expr, level)?; if target.as_ref().is_read_only() { target = target.into_owned(); @@ -1210,7 +1191,7 @@ impl Engine { .into_iter() .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value(scope, global, state, lib, this_ptr, expr, level) + self.get_arg_value(scope, global, caches, lib, this_ptr, expr, level) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1219,7 +1200,7 @@ impl Engine { } self.exec_fn_call( - None, global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, level, + None, global, caches, lib, name, hashes, &mut args, is_ref_mut, false, pos, level, ) .map(|(v, ..)| v) } @@ -1230,7 +1211,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, namespace: &crate::ast::Namespace, @@ -1252,20 +1233,20 @@ impl Engine { // and avoid cloning the value if !args_expr.is_empty() && args_expr[0].is_variable_access(true) { #[cfg(feature = "debugging")] - self.run_debugger(scope, global, state, lib, this_ptr, &args_expr[0], level)?; + self.run_debugger(scope, global, caches, lib, this_ptr, &args_expr[0], level)?; // func(x, ...) -> x.func(...) arg_values.push(Dynamic::UNIT); args_expr.iter().skip(1).try_for_each(|expr| { - self.get_arg_value(scope, global, state, lib, this_ptr, expr, level) + self.get_arg_value(scope, global, caches, lib, this_ptr, expr, level) .map(|(value, ..)| arg_values.push(value.flatten())) })?; // Get target reference to first argument let first_arg = &args_expr[0]; let (target, _pos) = - self.search_scope_only(scope, global, state, lib, this_ptr, first_arg, level)?; + self.search_scope_only(scope, global, caches, lib, this_ptr, first_arg, level)?; #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut global.num_operations, _pos)?; @@ -1289,7 +1270,7 @@ impl Engine { } else { // func(..., ...) or func(mod::x, ...) args_expr.iter().try_for_each(|expr| { - self.get_arg_value(scope, global, state, lib, this_ptr, expr, level) + self.get_arg_value(scope, global, caches, lib, this_ptr, expr, level) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(arg_values.iter_mut()); @@ -1298,7 +1279,7 @@ impl Engine { // Search for the root namespace let module = self - .search_imports(global, state, namespace) + .search_imports(global, namespace) .ok_or_else(|| ERR::ErrorModuleNotFound(namespace.to_string(), namespace.position()))?; // First search script-defined functions in namespace (can override built-in) @@ -1370,7 +1351,7 @@ impl Engine { mem::swap(&mut global.source, &mut source); let result = self.call_script_fn( - new_scope, global, state, lib, &mut None, fn_def, &mut args, true, pos, level, + new_scope, global, caches, lib, &mut None, fn_def, &mut args, true, pos, level, ); global.source = source; @@ -1410,7 +1391,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], script: &str, _pos: Position, @@ -1448,6 +1429,6 @@ impl Engine { } // Evaluate the AST - self.eval_global_statements(scope, global, state, statements, lib, level) + self.eval_global_statements(scope, global, caches, statements, lib, level) } } diff --git a/src/func/native.rs b/src/func/native.rs index d3e1e3a7..8e1a27eb 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -3,7 +3,7 @@ use super::call::FnCallArgs; use crate::api::events::VarDefInfo; use crate::ast::FnCallHashes; -use crate::eval::{EvalState, GlobalRuntimeState}; +use crate::eval::{Caches, GlobalRuntimeState}; use crate::plugin::PluginFunction; use crate::tokenizer::{Token, TokenizeState}; use crate::types::dynamic::Variant; @@ -311,7 +311,7 @@ impl<'a> NativeCallContext<'a> { .global .cloned() .unwrap_or_else(|| GlobalRuntimeState::new(self.engine())); - let mut state = EvalState::new(); + let mut caches = Caches::new(); let fn_name = fn_name.as_ref(); let args_len = args.len(); @@ -330,7 +330,7 @@ impl<'a> NativeCallContext<'a> { .exec_fn_call( None, &mut global, - &mut state, + &mut caches, self.lib, fn_name, hash, diff --git a/src/func/script.rs b/src/func/script.rs index 97bcdaba..fb024849 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -3,7 +3,7 @@ use super::call::FnCallArgs; use crate::ast::ScriptFnDef; -use crate::eval::{EvalState, GlobalRuntimeState}; +use crate::eval::{Caches, GlobalRuntimeState}; use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, ERR}; use std::mem; #[cfg(feature = "no_std")] @@ -26,7 +26,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, fn_def: &ScriptFnDef, @@ -109,7 +109,7 @@ impl Engine { } // Merge in encapsulated environment, if any - let orig_fn_resolution_caches_len = state.fn_resolution_caches_len(); + let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len(); #[cfg(not(feature = "no_module"))] let mut lib_merged = crate::StaticVec::with_capacity(lib.len() + 1); @@ -128,7 +128,7 @@ impl Engine { if fn_lib.is_empty() { lib } else { - state.push_fn_resolution_cache(); + caches.push_fn_resolution_cache(); lib_merged.push(fn_lib.as_ref()); lib_merged.extend(lib.iter().cloned()); &lib_merged @@ -142,7 +142,7 @@ impl Engine { #[cfg(feature = "debugging")] { let node = crate::ast::Stmt::Noop(fn_def.body.position()); - self.run_debugger(scope, global, state, lib, this_ptr, &node, level)?; + self.run_debugger(scope, global, caches, lib, this_ptr, &node, level)?; } // Evaluate the function @@ -150,7 +150,7 @@ impl Engine { .eval_stmt_block( scope, global, - state, + caches, lib, this_ptr, &fn_def.body, @@ -193,7 +193,8 @@ impl Engine { Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r), Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), }; - match self.run_debugger_raw(scope, global, state, lib, this_ptr, node, event, level) + match self + .run_debugger_raw(scope, global, caches, lib, this_ptr, node, event, level) { Ok(_) => (), Err(err) => _result = Err(err), @@ -221,7 +222,7 @@ impl Engine { } // Restore state - state.rewind_fn_resolution_caches(orig_fn_resolution_caches_len); + caches.rewind_fn_resolution_caches(orig_fn_resolution_caches_len); _result } @@ -231,11 +232,11 @@ impl Engine { pub(crate) fn has_script_fn( &self, _global: Option<&GlobalRuntimeState>, - state: &mut EvalState, + caches: &mut Caches, lib: &[&Module], hash_script: u64, ) -> bool { - let cache = state.fn_resolution_cache_mut(); + let cache = caches.fn_resolution_cache_mut(); if let Some(result) = cache.get(&hash_script).map(|v| v.is_some()) { return result; diff --git a/src/lib.rs b/src/lib.rs index 5e6c444a..56958dd3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -294,10 +294,7 @@ pub use ast::EncapsulatedEnviron; pub use ast::FloatWrapper; #[cfg(feature = "internals")] -pub use eval::{EvalState, GlobalRuntimeState}; - -#[cfg(feature = "internals")] -pub use func::call::{FnResolutionCache, FnResolutionCacheEntry}; +pub use eval::{Caches, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState}; /// Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), which is a /// specialized [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored. diff --git a/src/optimizer.rs b/src/optimizer.rs index fe13e94f..f68d84d5 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -3,7 +3,7 @@ use crate::ast::{ASTFlags, Expr, OpAssignment, Stmt, StmtBlock, StmtBlockContainer, SwitchCases}; use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF}; -use crate::eval::{EvalState, GlobalRuntimeState}; +use crate::eval::{Caches, GlobalRuntimeState}; use crate::func::builtin::get_builtin_binary_op_fn; use crate::func::hashing::get_hasher; use crate::tokenizer::{Span, Token}; @@ -139,7 +139,7 @@ impl<'a> OptimizerState<'a> { self.engine .call_native_fn( &mut GlobalRuntimeState::new(&self.engine), - &mut EvalState::new(), + &mut Caches::new(), lib, fn_name, calc_fn_hash(&fn_name, arg_values.len()), diff --git a/src/parser.rs b/src/parser.rs index dd6659d4..db975a12 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -8,7 +8,7 @@ use crate::ast::{ OpAssignment, ScriptFnDef, Stmt, StmtBlock, StmtBlockContainer, SwitchCases, TryCatchBlock, }; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; -use crate::eval::{EvalState, GlobalRuntimeState}; +use crate::eval::{Caches, GlobalRuntimeState}; use crate::func::hashing::get_hasher; use crate::tokenizer::{ is_keyword_function, is_valid_function_name, is_valid_identifier, Token, TokenStream, @@ -2686,7 +2686,7 @@ impl Engine { engine: self, scope: &mut state.stack, global: &mut GlobalRuntimeState::new(self), - state: &mut EvalState::new(), + caches: &mut Caches::new(), lib: &[], this_ptr: &mut None, level,