diff --git a/src/engine.rs b/src/engine.rs index 2b0e6569..ff25fa34 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1712,7 +1712,7 @@ impl Engine { // Statement block Expr::Stmt(x, _) => { - self.eval_stmt_block(scope, mods, state, lib, this_ptr, x.as_ref(), level) + self.eval_stmt_block(scope, mods, state, lib, this_ptr, x.as_ref(), true, level) } // lhs[idx_expr] @@ -1856,45 +1856,49 @@ impl Engine { lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, statements: impl IntoIterator, + restore: bool, level: usize, ) -> Result> { - let mut has_imports = false; + let mut _has_imports = false; let prev_always_search = state.always_search; let prev_scope_len = scope.len(); let prev_mods_len = mods.len(); - state.scope_level += 1; - let result = statements - .into_iter() - .try_fold(Default::default(), |_, stmt| { - match stmt { - #[cfg(not(feature = "no_module"))] - Stmt::Import(_, _, _) => { - // When imports list is modified, clear the functions lookup cache - if has_imports { - state.functions_caches.last_mut().map(|c| c.clear()); - } else { - state.functions_caches.push(Default::default()); - } - has_imports = true; - } - _ => (), - } - - self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) - }); - - scope.rewind(prev_scope_len); - if has_imports { - // If imports list is modified, pop the functions lookup cache - state.functions_caches.pop(); + if restore { + state.scope_level += 1; } - mods.truncate(prev_mods_len); - state.scope_level -= 1; - // 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 = prev_always_search; + let result = statements.into_iter().try_fold(Dynamic::UNIT, |_, stmt| { + #[cfg(not(feature = "no_module"))] + match stmt { + Stmt::Import(_, _, _) => { + // When imports list is modified, clear the functions lookup cache + if _has_imports { + state.functions_caches.last_mut().map(|c| c.clear()); + } else if restore { + state.functions_caches.push(Default::default()); + _has_imports = true; + } + } + _ => (), + } + + self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) + }); + + if restore { + scope.rewind(prev_scope_len); + if _has_imports { + // If imports list is modified, pop the functions lookup cache + state.functions_caches.pop(); + } + mods.truncate(prev_mods_len); + state.scope_level -= 1; + + // 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 = prev_always_search; + } result } @@ -2088,7 +2092,7 @@ impl Engine { // Block scope Stmt::Block(statements, _) => { - self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, level) + self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, true, level) } // If statement diff --git a/src/engine_api.rs b/src/engine_api.rs index b861c06c..26268ba8 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1533,7 +1533,9 @@ impl Engine { resolver: ast.resolver(), ..Default::default() }; - self.eval_statements_raw(scope, mods, state, ast.statements(), &[ast.lib()], level) + let statements = ast.statements(); + let lib = &[ast.lib()]; + self.eval_global_statements(scope, mods, state, statements, lib, level) } /// Evaluate a file, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. @@ -1602,7 +1604,9 @@ impl Engine { resolver: ast.resolver(), ..Default::default() }; - self.eval_statements_raw(scope, mods, state, ast.statements(), &[ast.lib()], 0)?; + let statements = ast.statements(); + let lib = &[ast.lib()]; + self.eval_global_statements(scope, mods, state, statements, lib, 0)?; Ok(()) } /// Call a script function defined in an [`AST`] with multiple arguments. diff --git a/src/fn_call.rs b/src/fn_call.rs index 15307458..9b56b55d 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -768,10 +768,10 @@ impl Engine { } } - /// Evaluate a list of statements with an empty state and no `this` pointer. + /// Evaluate a list of statements with no `this` pointer. /// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body. #[inline] - pub(crate) fn eval_statements_raw<'a>( + pub(crate) fn eval_global_statements<'a>( &self, scope: &mut Scope, mods: &mut Imports, @@ -780,11 +780,7 @@ impl Engine { lib: &[&Module], level: usize, ) -> Result> { - statements - .into_iter() - .try_fold(Dynamic::UNIT, |_, stmt| { - self.eval_stmt(scope, mods, state, lib, &mut None, stmt, level) - }) + self.eval_stmt_block(scope, mods, state, lib, &mut None, statements, false, level) .or_else(|err| match *err { EvalAltResult::Return(out, _) => Ok(out), EvalAltResult::LoopBreak(_, _) => { @@ -826,14 +822,14 @@ impl Engine { } // Evaluate the AST - let mut new_state = State { + let new_state = &mut State { source: state.source.clone(), operations: state.operations, ..Default::default() }; let result = - self.eval_statements_raw(scope, mods, &mut new_state, ast.statements(), lib, level); + self.eval_global_statements(scope, mods, new_state, ast.statements(), lib, level); state.operations = new_state.operations; result diff --git a/tests/eval.rs b/tests/eval.rs index 568a5e5b..c59ccfff 100644 --- a/tests/eval.rs +++ b/tests/eval.rs @@ -4,14 +4,7 @@ use rhai::{Engine, EvalAltResult, LexError, ParseErrorType, RegisterFn, Scope, I fn test_eval() -> Result<(), Box> { let engine = Engine::new(); - assert_eq!( - engine.eval::( - r#" - eval("40 + 2") - "# - )?, - 42 - ); + assert_eq!(engine.eval::(r#"eval("40 + 2")"#)?, 42); Ok(()) } @@ -62,7 +55,7 @@ fn test_eval_function() -> Result<(), Box> { script += "x + y"; eval(script) + x + y - "# + "# )?, 84 );