diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ea21d8c..87f55f4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ Enhancements * `SmartString` now uses `LazyCompact` instead of `Compact` to minimize allocations. * Added `pop` for strings. * Added `ImmutableString::ptr_eq` to test if two strings point to the same allocation. +* The `serde` feature of `SmartString` is turned on under `metadata` to make `Map` serializable. ### `Scope` API diff --git a/Cargo.toml b/Cargo.toml index ef48ed62..52e61ab6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,7 @@ no_closure = [] # no automatic sharing and capture of anonymous no_module = [] # no modules internals = [] # expose internal data structures unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers. -metadata = ["serde", "serde_json", "rhai_codegen/metadata"] # enable exporting functions metadata +metadata = ["serde", "serde_json", "rhai_codegen/metadata", "smartstring/serde"] # enable exporting functions metadata no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"] diff --git a/README.md b/README.md index e11cf72f..7aa96bfb 100644 --- a/README.md +++ b/README.md @@ -79,8 +79,8 @@ The [`scripts`](https://github.com/rhaiscript/rhai/tree/master/scripts) subdirec Below is the standard _Fibonacci_ example for scripting languages: ```js -// This Rhai script calculates the n-th Fibonacci number using a really dumb algorithm -// to test the speed of the scripting engine. +// This Rhai script calculates the n-th Fibonacci number using a +// really dumb algorithm to test the speed of the scripting engine. const TARGET = 28; const REPEAT = 5; diff --git a/src/engine.rs b/src/engine.rs index b0aa1400..477e3f6e 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -3,7 +3,7 @@ use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, Stmt, AST_OPTION_FLAGS::*}; use crate::custom_syntax::CustomSyntax; use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant}; -use crate::fn_hash::{calc_fn_hash, get_hasher}; +use crate::fn_hash::get_hasher; use crate::fn_native::{ CallableFunction, IteratorFn, OnDebugCallback, OnParseTokenCallback, OnPrintCallback, OnVarCallback, @@ -682,6 +682,9 @@ pub struct EvalState { /// Embedded module resolver. #[cfg(not(feature = "no_module"))] pub embedded_module_resolver: Option>, + /// Function call hashes to FN_IDX_GET and FN_IDX_SET + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + fn_hash_indexing: (u64, u64), } impl EvalState { @@ -698,6 +701,8 @@ impl EvalState { #[cfg(not(feature = "no_module"))] embedded_module_resolver: None, fn_resolution_caches: StaticVec::new(), + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + fn_hash_indexing: (0, 0), } } /// Is the state currently at global (root) level? @@ -735,6 +740,32 @@ impl EvalState { .pop() .expect("at least one function resolution cache"); } + /// Get the pre-calculated index getter hash. + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + #[must_use] + pub fn fn_hash_idx_get(&mut self) -> u64 { + if self.fn_hash_indexing != (0, 0) { + self.fn_hash_indexing.0 + } else { + let n1 = crate::calc_fn_hash(FN_IDX_GET, 2); + let n2 = crate::calc_fn_hash(FN_IDX_SET, 3); + self.fn_hash_indexing = (n1, n2); + n1 + } + } + /// Get the pre-calculated index setter hash. + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + #[must_use] + pub fn fn_hash_idx_set(&mut self) -> u64 { + if self.fn_hash_indexing != (0, 0) { + self.fn_hash_indexing.1 + } else { + let n1 = crate::calc_fn_hash(FN_IDX_GET, 2); + let n2 = crate::calc_fn_hash(FN_IDX_SET, 3); + self.fn_hash_indexing = (n1, n2); + n2 + } + } } /// _(internals)_ A type containing all the limits imposed by the [`Engine`]. @@ -804,31 +835,6 @@ impl Default for Limits { } } -/// A type containing useful constants for the [`Engine`]. -#[derive(Debug)] -pub struct GlobalConstants { - /// An empty [`ImmutableString`] for cloning purposes. - pub(crate) empty_string: ImmutableString, - /// Function call hash to FN_IDX_GET - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - pub(crate) fn_hash_idx_get: u64, - /// Function call hash to FN_IDX_SET - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - pub(crate) fn_hash_idx_set: u64, -} - -impl Default for GlobalConstants { - fn default() -> Self { - Self { - empty_string: Default::default(), - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - fn_hash_idx_get: calc_fn_hash(FN_IDX_GET, 2), - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - fn_hash_idx_set: calc_fn_hash(FN_IDX_SET, 3), - } - } -} - /// Context of a script evaluation process. #[derive(Debug)] pub struct EvalContext<'a, 'x, 'px, 'm, 's, 'b, 't, 'pt> { @@ -950,8 +956,8 @@ pub struct Engine { /// A map mapping type names to pretty-print names. pub(crate) type_names: BTreeMap>, - /// Useful constants - pub(crate) constants: GlobalConstants, + /// An empty [`ImmutableString`] for cloning purposes. + pub(crate) empty_string: ImmutableString, /// A set of symbols to disable. pub(crate) disabled_symbols: BTreeSet, @@ -1081,7 +1087,7 @@ impl Engine { module_resolver: None, type_names: Default::default(), - constants: Default::default(), + empty_string: Default::default(), disabled_symbols: Default::default(), custom_keywords: Default::default(), custom_syntax: Default::default(), @@ -1116,7 +1122,7 @@ impl Engine { #[inline(always)] #[must_use] pub fn const_empty_string(&self) -> ImmutableString { - self.constants.empty_string.clone() + self.empty_string.clone() } /// Search for a module within an imports stack. @@ -1328,8 +1334,7 @@ impl Engine { if let Some(mut new_val) = try_setter { // Try to call index setter if value is changed - let hash_set = - FnCallHashes::from_native(self.constants.fn_hash_idx_set); + let hash_set = FnCallHashes::from_native(state.fn_hash_idx_set()); let args = &mut [target, &mut idx_val_for_setter, &mut new_val]; if let Err(err) = self.exec_fn_call( @@ -1376,8 +1381,7 @@ impl Engine { if let Some(mut new_val) = try_setter { // Try to call index setter - let hash_set = - FnCallHashes::from_native(self.constants.fn_hash_idx_set); + let hash_set = FnCallHashes::from_native(state.fn_hash_idx_set()); let args = &mut [target, &mut idx_val_for_setter, &mut new_val]; self.exec_fn_call( @@ -1508,8 +1512,7 @@ impl Engine { // Try an indexer if property does not exist EvalAltResult::ErrorDotExpr(_, _) => { let args = &mut [target, &mut name.into(), &mut new_val]; - let hash_set = - FnCallHashes::from_native(self.constants.fn_hash_idx_set); + let hash_set = FnCallHashes::from_native(state.fn_hash_idx_set()); let pos = Position::NONE; self.exec_fn_call( @@ -1668,7 +1671,7 @@ impl Engine { let args = &mut [target.as_mut(), &mut name.into(), val]; let hash_set = FnCallHashes::from_native( - self.constants.fn_hash_idx_set, + state.fn_hash_idx_set(), ); self.exec_fn_call( mods, state, lib, FN_IDX_SET, hash_set, args, @@ -1956,12 +1959,11 @@ impl Engine { - index .checked_abs() .ok_or_else(|| { - Box::new(EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos)) + EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos).into() }) .and_then(|n| { if n as usize > arr_len { - Err(EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos) - .into()) + EvalAltResult::ErrorArrayBounds(arr_len, index, idx_pos).into() } else { Ok(n as usize) } @@ -2072,7 +2074,7 @@ impl Engine { _ if use_indexers => { let args = &mut [target, &mut idx]; - let hash_get = FnCallHashes::from_native(self.constants.fn_hash_idx_get); + let hash_get = FnCallHashes::from_native(state.fn_hash_idx_get()); let idx_pos = Position::NONE; self.exec_fn_call( @@ -3096,7 +3098,7 @@ impl Engine { // Concentrate all empty strings into one instance to save memory if let Dynamic(crate::dynamic::Union::Str(s, _, _)) = r { if s.is_empty() { - if !s.ptr_eq(&self.constants.empty_string) { + if !s.ptr_eq(&self.empty_string) { *s = self.const_empty_string(); } return result; diff --git a/src/error.rs b/src/error.rs index 83ed5dd1..adc7cf15 100644 --- a/src/error.rs +++ b/src/error.rs @@ -225,10 +225,7 @@ impl> From for EvalAltResult { impl> From for Box { #[inline(always)] fn from(err: T) -> Self { - Box::new(EvalAltResult::ErrorRuntime( - err.as_ref().to_string().into(), - Position::NONE, - )) + EvalAltResult::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE).into() } } diff --git a/src/fn_call.rs b/src/fn_call.rs index a3e97465..61911f74 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -681,12 +681,12 @@ impl Engine { return Ok(( if num_params < 0 { - Dynamic::FALSE + false } else { let hash_script = calc_fn_hash(fn_name.as_str(), num_params as usize); self.has_script_fn(Some(mods), state, lib, hash_script) - .into() - }, + } + .into(), false, )); } @@ -1189,12 +1189,12 @@ impl Engine { .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; return Ok(if num_params < 0 { - Dynamic::FALSE + false } else { let hash_script = calc_fn_hash(&fn_name, num_params as usize); self.has_script_fn(Some(mods), state, lib, hash_script) - .into() - }); + } + .into()); } // Handle is_def_var()