From a6c2c00479c449fe0546ef8c7575b21fcdc015a7 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 22 Apr 2022 12:12:36 +0800 Subject: [PATCH 1/9] Allow variables in scope for strict vars. --- CHANGELOG.md | 2 +- src/parser.rs | 7 ++----- tests/options.rs | 6 +++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e47dc084..dd5f5ab6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ Bug fixes Script-breaking changes ----------------------- -* _Strict Variables Mode_ no longer returns an error when an undeclared variable matches a constant in the provided external `Scope`. +* _Strict Variables Mode_ no longer returns an error when an undeclared variable matches a variable/constant in the provided external `Scope`. Enhancements ------------ diff --git a/src/parser.rs b/src/parser.rs index e33ece2b..72664a54 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1301,10 +1301,7 @@ impl Engine { if settings.options.strict_var && !settings.is_closure_scope && index.is_none() - && !matches!( - state.scope.get_index(name), - Some((_, AccessMode::ReadOnly)) - ) + && !state.scope.contains(name) { // If the parent scope is not inside another capturing closure // then we can conclude that the captured variable doesn't exist. @@ -1450,7 +1447,7 @@ impl Engine { if settings.options.strict_var && index.is_none() - && !matches!(state.scope.get_index(&s), Some((_, AccessMode::ReadOnly))) + && !state.scope.contains(&s) { return Err( PERR::VariableUndefined(s.to_string()).into_err(settings.pos) diff --git a/tests/options.rs b/tests/options.rs index aa9b250d..5725a76f 100644 --- a/tests/options.rs +++ b/tests/options.rs @@ -57,7 +57,7 @@ fn test_options_strict_var() -> Result<(), Box> { let mut engine = Engine::new(); let mut scope = Scope::new(); - scope.push_constant("x", 42 as INT); + scope.push("x", 42 as INT); scope.push_constant("y", 0 as INT); engine.compile("let x = if y { z } else { w };")?; @@ -114,8 +114,8 @@ fn test_options_strict_var() -> Result<(), Box> { } #[cfg(not(feature = "no_optimize"))] assert_eq!( - engine.eval_with_scope::(&mut scope, "fn foo(z) { x * y + z } foo(1)")?, - 1 + engine.eval_with_scope::(&mut scope, "fn foo(z) { y + z } foo(x)")?, + 42 ); } From 2755d39cdf40c4d43216e2413e8de4d754691f37 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 22 Apr 2022 12:12:55 +0800 Subject: [PATCH 2/9] Return early if no type casting for `call_fn`. --- src/api/call_fn.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 6ca6f59d..2e5fedc1 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -4,9 +4,10 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::types::dynamic::Variant; use crate::{ - Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST, ERR, + reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST, + ERR, }; -use std::any::type_name; +use std::any::{type_name, TypeId}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -66,6 +67,15 @@ impl Engine { let result = self.call_fn_raw(scope, ast, true, true, name, None, arg_values)?; + // Bail out early if the return type needs no cast + if TypeId::of::() == TypeId::of::() { + return Ok(reify!(result => T)); + } + if TypeId::of::() == TypeId::of::<()>() { + return Ok(reify!(() => T)); + } + + // Cast return type let typ = self.map_type_name(result.type_name()); result.try_cast().ok_or_else(|| { From 71bc605fe61fff75d9b101d46b1e2c92ea0612d8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 22 Apr 2022 13:39:51 +0800 Subject: [PATCH 3/9] Add scope to FileModuleResolver. --- CHANGELOG.md | 1 + src/module/resolvers/file.rs | 46 ++++++++++++++++++++++++++++++++---- src/module/resolvers/stat.rs | 2 +- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd5f5ab6..a03e5fda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Enhancements * `Engine::parse_json` now natively handles nested JSON inputs (using a token remap filter) without needing to replace `{` with `#{`. * `to_json` is added to object maps to cheaply convert it to JSON format (`()` is mapped to `null`, all other data types must be supported by JSON) * A global function `format_map_as_json` is provided which is the same as `to_json` for object maps. +* `FileModuleResolver` now accepts a custom `Scope` to provide constants for optimization. Version 1.6.1 diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index 561dfaae..6fe16b48 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -45,11 +45,12 @@ pub const RHAI_SCRIPT_EXTENSION: &str = "rhai"; /// /// engine.set_module_resolver(resolver); /// ``` -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct FileModuleResolver { base_path: Option, extension: Identifier, cache_enabled: bool, + scope: Scope<'static>, #[cfg(not(feature = "sync"))] cache: std::cell::RefCell>>, @@ -57,6 +58,13 @@ pub struct FileModuleResolver { cache: std::sync::RwLock>>, } +impl Default for FileModuleResolver { + #[inline(always)] + fn default() -> Self { + Self::new() + } +} + impl FileModuleResolver { /// Create a new [`FileModuleResolver`] with the current directory as base path. /// @@ -126,6 +134,7 @@ impl FileModuleResolver { extension: extension.into(), cache_enabled: true, cache: BTreeMap::new().into(), + scope: Scope::new(), } } @@ -155,6 +164,7 @@ impl FileModuleResolver { extension: extension.into(), cache_enabled: true, cache: BTreeMap::new().into(), + scope: Scope::new(), } } @@ -185,6 +195,32 @@ impl FileModuleResolver { self } + /// Get a reference to the file module resolver's [scope][Scope]. + /// + /// The [scope][Scope] is used for compiling module scripts. + #[must_use] + #[inline(always)] + pub const fn scope(&self) -> &Scope { + &self.scope + } + + /// Set the file module resolver's [scope][Scope]. + /// + /// The [scope][Scope] is used for compiling module scripts. + #[inline(always)] + pub fn set_scope(&mut self, scope: Scope<'static>) { + self.scope = scope; + } + + /// Get a mutable reference to the file module resolver's [scope][Scope]. + /// + /// The [scope][Scope] is used for compiling module scripts. + #[must_use] + #[inline(always)] + pub fn scope_mut(&mut self) -> &mut Scope<'static> { + &mut self.scope + } + /// Enable/disable the cache. #[inline(always)] pub fn enable_cache(&mut self, enable: bool) -> &mut Self { @@ -281,10 +317,8 @@ impl FileModuleResolver { } } - let scope = Scope::new(); - let mut ast = engine - .compile_file(file_path.clone()) + .compile_file_with_scope(&self.scope, file_path.clone()) .map_err(|err| match *err { ERR::ErrorSystem(.., err) if err.is::() => { Box::new(ERR::ErrorModuleNotFound(path.to_string(), pos)) @@ -294,7 +328,9 @@ impl FileModuleResolver { ast.set_source(path); - let m: Shared = if let Some(global) = global { + let scope = Scope::new(); + + let m: Shared<_> = if let Some(global) = global { Module::eval_ast_as_new_raw(engine, scope, global, &ast) } else { Module::eval_ast_as_new(scope, &ast, engine) diff --git a/src/module/resolvers/stat.rs b/src/module/resolvers/stat.rs index be9110a7..3e2e9659 100644 --- a/src/module/resolvers/stat.rs +++ b/src/module/resolvers/stat.rs @@ -22,7 +22,7 @@ use std::{collections::btree_map::IntoIter, collections::BTreeMap, ops::AddAssig /// /// engine.set_module_resolver(resolver); /// ``` -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct StaticModuleResolver(BTreeMap>); impl StaticModuleResolver { From 5a7547911980a8ef5d35bb78e5c9828c9c88d3c4 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 23 Apr 2022 11:53:17 +0800 Subject: [PATCH 4/9] Fix bug in Scope cloning. --- CHANGELOG.md | 1 + src/types/scope.rs | 80 ++++++++++++++++++++++++++++++++++++---------- tests/var_scope.rs | 11 +++++++ 3 files changed, 75 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a03e5fda..8002c5a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Bug fixes --------- * Compound assignments now work properly with indexers. +* Cloning a `Scope` no longer turns all constants to mutable. Script-breaking changes ----------------------- diff --git a/src/types/scope.rs b/src/types/scope.rs index bae3f2a8..6a169bec 100644 --- a/src/types/scope.rs +++ b/src/types/scope.rs @@ -6,6 +6,7 @@ use smallvec::SmallVec; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ + fmt, iter::{Extend, FromIterator}, marker::PhantomData, }; @@ -59,7 +60,7 @@ const SCOPE_ENTRIES_INLINED: usize = 8; // direct indexing, by-passing the name altogether. // // [`Dynamic`] is reasonably small so packing it tightly improves cache performance. -#[derive(Debug, Clone, Hash, Default)] +#[derive(Debug, Hash, Default)] pub struct Scope<'a> { /// Current value of the entry. values: SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>, @@ -71,6 +72,51 @@ pub struct Scope<'a> { phantom: PhantomData<&'a ()>, } +impl fmt::Display for Scope<'_> { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for (i, (name, constant, value)) in self.iter_raw().enumerate() { + #[cfg(not(feature = "no_closure"))] + let value_is_shared = if value.is_shared() { " (shared)" } else { "" }; + #[cfg(feature = "no_closure")] + let value_is_shared = ""; + + write!( + f, + "[{}] {}{}{} = {:?}\n", + i + 1, + if constant { "const " } else { "" }, + name, + value_is_shared, + *value.read_lock::().unwrap(), + )?; + } + + Ok(()) + } +} + +impl Clone for Scope<'_> { + #[inline] + fn clone(&self) -> Self { + Self { + values: self + .values + .iter() + .map(|v| { + // Also copy the value's access mode (otherwise will turn to read-write) + let mut v2 = v.clone(); + v2.set_access_mode(v.access_mode()); + v2 + }) + .collect(), + names: self.names.clone(), + aliases: self.aliases.clone(), + phantom: self.phantom.clone(), + } + } +} + impl IntoIterator for Scope<'_> { type Item = (String, Dynamic, Vec); type IntoIter = Box>; @@ -551,24 +597,24 @@ impl Scope<'_> { #[must_use] pub fn clone_visible(&self) -> Self { let len = self.len(); + let mut scope = Self::new(); - self.names - .iter() - .rev() - .enumerate() - .fold(Self::new(), |mut entries, (index, name)| { - if entries.names.is_empty() || !entries.names.contains(name) { - let orig_value = &self.values[len - 1 - index]; - let alias = &self.aliases[len - 1 - index]; - let mut value = orig_value.clone(); - value.set_access_mode(orig_value.access_mode()); + self.names.iter().rev().enumerate().for_each(|(i, name)| { + if scope.names.contains(name) { + return; + } - entries.names.push(name.clone()); - entries.values.push(value); - entries.aliases.push(alias.clone()); - } - entries - }) + let v1 = &self.values[len - 1 - i]; + let alias = &self.aliases[len - 1 - i]; + let mut v2 = v1.clone(); + v2.set_access_mode(v1.access_mode()); + + scope.names.push(name.clone()); + scope.values.push(v2); + scope.aliases.push(alias.clone()); + }); + + scope } /// Get an iterator to entries in the [`Scope`]. #[inline] diff --git a/tests/var_scope.rs b/tests/var_scope.rs index 2f50c457..014ec42b 100644 --- a/tests/var_scope.rs +++ b/tests/var_scope.rs @@ -79,6 +79,17 @@ fn test_var_scope() -> Result<(), Box> { ); } + scope.clear(); + + scope.push("x", 42 as INT); + scope.push_constant("x", 42 as INT); + + let scope2 = scope.clone(); + let scope3 = scope.clone_visible(); + + assert_eq!(scope2.is_constant("x"), Some(true)); + assert_eq!(scope3.is_constant("x"), Some(true)); + Ok(()) } From c2a796f9c234e92b1f563bf23ac7b8be2ee6c5f6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 23 Apr 2022 11:53:34 +0800 Subject: [PATCH 5/9] Replace Scope display code. --- src/bin/rhai-dbg.rs | 41 ++--------------------------------------- src/bin/rhai-repl.rs | 23 +---------------------- 2 files changed, 3 insertions(+), 61 deletions(-) diff --git a/src/bin/rhai-dbg.rs b/src/bin/rhai-dbg.rs index 7656c31a..ffe0aae1 100644 --- a/src/bin/rhai-dbg.rs +++ b/src/bin/rhai-dbg.rs @@ -154,43 +154,6 @@ fn print_debug_help() { println!(); } -/// Display the current scope. -fn print_scope(scope: &Scope, dedup: bool) { - let flattened_clone; - let scope = if dedup { - flattened_clone = scope.clone_visible(); - &flattened_clone - } else { - scope - }; - - for (i, (name, constant, value)) in scope.iter_raw().enumerate() { - #[cfg(not(feature = "no_closure"))] - let value_is_shared = if value.is_shared() { " (shared)" } else { "" }; - #[cfg(feature = "no_closure")] - let value_is_shared = ""; - - if dedup { - println!( - "{}{}{} = {:?}", - if constant { "const " } else { "" }, - name, - value_is_shared, - *value.read_lock::().unwrap(), - ); - } else { - println!( - "[{}] {}{}{} = {:?}", - i + 1, - if constant { "const " } else { "" }, - name, - value_is_shared, - *value.read_lock::().unwrap(), - ); - } - } -} - // Load script to debug. fn load_script(engine: &Engine) -> (rhai::AST, String) { if let Some(filename) = env::args().skip(1).next() { @@ -365,7 +328,7 @@ fn debug_callback( [] | ["step" | "s"] => break Ok(DebuggerCommand::StepInto), ["over" | "o"] => break Ok(DebuggerCommand::StepOver), ["next" | "n"] => break Ok(DebuggerCommand::Next), - ["scope"] => print_scope(context.scope(), false), + ["scope"] => println!("{}", context.scope()), ["print" | "p", "this"] => { if let Some(value) = context.this_ptr() { println!("=> {:?}", value); @@ -381,7 +344,7 @@ fn debug_callback( } } ["print" | "p"] => { - print_scope(context.scope(), true); + println!("{}", context.scope().clone_visible()); if let Some(value) = context.this_ptr() { println!("this = {:?}", value); } diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index c0dfa093..0cf59289 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -108,27 +108,6 @@ fn print_keys() { println!(); } -/// Display the scope. -fn print_scope(scope: &Scope) { - for (i, (name, constant, value)) in scope.iter_raw().enumerate() { - #[cfg(not(feature = "no_closure"))] - let value_is_shared = if value.is_shared() { " (shared)" } else { "" }; - #[cfg(feature = "no_closure")] - let value_is_shared = ""; - - println!( - "[{}] {}{}{} = {:?}", - i + 1, - if constant { "const " } else { "" }, - name, - value_is_shared, - *value.read_lock::().unwrap(), - ) - } - - println!(); -} - // Load script files specified in the command line. #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_std"))] @@ -458,7 +437,7 @@ fn main() { continue; } "scope" => { - print_scope(&scope); + println!("{}", scope); continue; } #[cfg(not(feature = "no_optimize"))] From 63592ed6485dfe4d31b0cb0ff8183789ff46d6aa Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 23 Apr 2022 12:49:03 +0800 Subject: [PATCH 6/9] Remove Clone from FileModuleResolver. --- src/module/resolvers/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index 6fe16b48..5b6f232a 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -45,7 +45,7 @@ pub const RHAI_SCRIPT_EXTENSION: &str = "rhai"; /// /// engine.set_module_resolver(resolver); /// ``` -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct FileModuleResolver { base_path: Option, extension: Identifier, From d61f7fa7c2917d90990e2c837127797242f6663f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 23 Apr 2022 13:15:27 +0800 Subject: [PATCH 7/9] Add call_fn_with_global_raw. --- CHANGELOG.md | 1 + src/api/call_fn.rs | 83 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8002c5a7..c7f85b83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Enhancements * `to_json` is added to object maps to cheaply convert it to JSON format (`()` is mapped to `null`, all other data types must be supported by JSON) * A global function `format_map_as_json` is provided which is the same as `to_json` for object maps. * `FileModuleResolver` now accepts a custom `Scope` to provide constants for optimization. +* A new low-level method `Engine::call_fn_with_global_raw` is added to add speed to repeated function calls. Version 1.6.1 diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 2e5fedc1..838cad07 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -83,8 +83,9 @@ impl Engine { ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into() }) } - /// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments and the - /// following options: + /// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments. + /// + /// The following options are available: /// /// * whether to evaluate the [`AST`] to load necessary modules before calling the function /// * whether to rewind the [`Scope`] after the function call @@ -148,7 +149,7 @@ impl Engine { /// # Ok(()) /// # } /// ``` - #[inline] + #[inline(always)] pub fn call_fn_raw( &self, scope: &mut Scope, @@ -158,9 +159,83 @@ impl Engine { name: impl AsRef, this_ptr: Option<&mut Dynamic>, arg_values: impl AsMut<[Dynamic]>, + ) -> RhaiResult { + self.call_fn_internal( + scope, + &mut GlobalRuntimeState::new(self), + ast, + eval_ast, + rewind_scope, + name, + this_ptr, + arg_values, + ) + } + /// _(internals)_ Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments. + /// Exported under the `internals` feature only. + /// + /// The following options are available: + /// + /// * whether to evaluate the [`AST`] to load necessary modules before calling the function + /// * whether to rewind the [`Scope`] after the function call + /// * a value for binding to the `this` pointer (if any) + /// + /// Not available under `no_function`. + /// + /// # WARNING - Low Level API + /// + /// This function is very low level. + /// + /// A [`GlobalRuntimeState`] needs to be passed into the function, which can be created via + /// [`GlobalRuntimeState::new`]. This makes repeatedly calling particular functions + /// extremely efficient as the functions resolution cache inside the [`GlobalRuntimeState`] + /// is kept intact. + /// + /// # Arguments + /// + /// All the arguments are _consumed_, meaning that they're replaced by `()`. + /// This is to avoid unnecessarily cloning the arguments. + /// + /// Do not use the arguments after this call. If they are needed afterwards, clone them _before_ + /// calling this function. + #[cfg(feature = "internals")] + #[inline(always)] + pub fn call_fn_with_global_raw( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + ast: &AST, + eval_ast: bool, + rewind_scope: bool, + name: impl AsRef, + this_ptr: Option<&mut Dynamic>, + arg_values: impl AsMut<[Dynamic]>, + ) -> RhaiResult { + self.call_fn_internal( + scope, + global, + ast, + eval_ast, + rewind_scope, + name, + this_ptr, + arg_values, + ) + } + + /// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments. + fn call_fn_internal( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + ast: &AST, + eval_ast: bool, + rewind_scope: bool, + name: impl AsRef, + this_ptr: Option<&mut Dynamic>, + arg_values: impl AsMut<[Dynamic]>, ) -> RhaiResult { let caches = &mut Caches::new(); - let global = &mut GlobalRuntimeState::new(self); let statements = ast.statements(); From 0ef5c0ec54fa0a78ef507b8cb289da01e447ebd7 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 23 Apr 2022 13:28:26 +0800 Subject: [PATCH 8/9] Change to `call_fn_raw_raw`. --- CHANGELOG.md | 2 +- src/api/call_fn.rs | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7f85b83..9a297a01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ Enhancements * `to_json` is added to object maps to cheaply convert it to JSON format (`()` is mapped to `null`, all other data types must be supported by JSON) * A global function `format_map_as_json` is provided which is the same as `to_json` for object maps. * `FileModuleResolver` now accepts a custom `Scope` to provide constants for optimization. -* A new low-level method `Engine::call_fn_with_global_raw` is added to add speed to repeated function calls. +* A new low-level method `Engine::call_fn_raw_raw` is added to add speed to repeated function calls. Version 1.6.1 diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 838cad07..9d327c80 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -163,6 +163,7 @@ impl Engine { self.call_fn_internal( scope, &mut GlobalRuntimeState::new(self), + &mut Caches::new(), ast, eval_ast, rewind_scope, @@ -186,9 +187,9 @@ impl Engine { /// /// This function is very low level. /// - /// A [`GlobalRuntimeState`] needs to be passed into the function, which can be created via - /// [`GlobalRuntimeState::new`]. This makes repeatedly calling particular functions - /// extremely efficient as the functions resolution cache inside the [`GlobalRuntimeState`] + /// A [`GlobalRuntimeState`] and [`Caches`] need to be passed into the function, which can be + /// created via [`GlobalRuntimeState::new`] and [`Caches::new`]. + /// This makes repeatedly calling particular functions more efficient as the functions resolution cache /// is kept intact. /// /// # Arguments @@ -200,10 +201,11 @@ impl Engine { /// calling this function. #[cfg(feature = "internals")] #[inline(always)] - pub fn call_fn_with_global_raw( + pub fn call_fn_raw_raw( &self, scope: &mut Scope, global: &mut GlobalRuntimeState, + caches: &mut Caches, ast: &AST, eval_ast: bool, rewind_scope: bool, @@ -214,6 +216,7 @@ impl Engine { self.call_fn_internal( scope, global, + caches, ast, eval_ast, rewind_scope, @@ -228,6 +231,7 @@ impl Engine { &self, scope: &mut Scope, global: &mut GlobalRuntimeState, + caches: &mut Caches, ast: &AST, eval_ast: bool, rewind_scope: bool, @@ -235,8 +239,6 @@ impl Engine { this_ptr: Option<&mut Dynamic>, arg_values: impl AsMut<[Dynamic]>, ) -> RhaiResult { - let caches = &mut Caches::new(); - let statements = ast.statements(); let orig_scope_len = scope.len(); From 1d904f47583ae2d4c5a2c82212405bbb229d7356 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 23 Apr 2022 13:37:08 +0800 Subject: [PATCH 9/9] Add Engine::eval_statements_raw. --- CHANGELOG.md | 1 + src/api/eval.rs | 41 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a297a01..833c3b71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Enhancements * A global function `format_map_as_json` is provided which is the same as `to_json` for object maps. * `FileModuleResolver` now accepts a custom `Scope` to provide constants for optimization. * A new low-level method `Engine::call_fn_raw_raw` is added to add speed to repeated function calls. +* A new low-level method `Engine::eval_statements_raw` is added to evaluate a sequence of statements. Version 1.6.1 diff --git a/src/api/eval.rs b/src/api/eval.rs index 094fd117..4b23958a 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -6,9 +6,9 @@ use crate::types::dynamic::Variant; use crate::{ Dynamic, Engine, Module, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR, }; -use std::any::type_name; #[cfg(feature = "no_std")] use std::prelude::v1::*; +use std::{any::type_name, mem}; impl Engine { /// Evaluate a string. @@ -211,9 +211,10 @@ impl Engine { global.source = ast.source_raw().clone(); #[cfg(not(feature = "no_module"))] - { - global.embedded_module_resolver = ast.resolver().cloned(); - } + let orig_embedded_module_resolver = mem::replace( + &mut global.embedded_module_resolver, + ast.resolver().cloned(), + ); let statements = ast.statements(); @@ -230,6 +231,36 @@ impl Engine { } else { &lib[..] }; - self.eval_global_statements(scope, global, &mut caches, statements, lib, level) + + let result = + self.eval_global_statements(scope, global, &mut caches, statements, lib, level); + + #[cfg(not(feature = "no_module"))] + { + global.embedded_module_resolver = orig_embedded_module_resolver; + } + + result + } + /// _(internals)_ Evaluate a list of statements with no `this` pointer. + /// Exported under the `internals` feature only. + /// + /// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body. + /// + /// # WARNING - Low Level API + /// + /// This function is very low level. + #[cfg(feature = "internals")] + #[inline(always)] + pub fn eval_statements_raw( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + caches: &mut Caches, + statements: &[crate::ast::Stmt], + lib: &[&Module], + level: usize, + ) -> RhaiResult { + self.eval_global_statements(scope, global, caches, statements, lib, level) } }