diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f785f8c..1772d4f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Bug fixes * `import` statements inside `eval` no longer cause errors in subsequent code. * Functions marked `global` in `import`ed modules with no alias names now work properly. * Incorrect loop optimizations that are too aggressive (e.g. unrolling a `do { ... } until true` with a `break` statement inside) and cause crashes are removed. +* `Dynamic::is` now works properly for shared values. Breaking changes ---------------- @@ -25,6 +26,11 @@ Breaking changes New features ------------ +### `Dynamic` detection API + +* New methods are added to `Dynamic` in the form of `is_XXX()` where `XXX` is a type (e.g. `is_int`, `is_unit`, `is_bool`, `is_array`). +* This new API is to make it easier to detect the data type, instead of having to call `is::()`. + ### Loop expressions * Loops (such as `loop`, `do`, `while` and `for`) can now act as _expressions_, with the `break` statement returning an optional value. @@ -33,13 +39,13 @@ New features ### Static hashing -* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via a static function `rhai::config::hashing::set_ahash_seed` or an environment variable, in order to force static (i.e. deterministic) hashes for function signatures. +* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via a static function `rhai::config::hashing::set_ahash_seed` or an environment variable (`RHAI_AHASH_SEED`), in order to force static (i.e. deterministic) hashes for function signatures. * This is necessary when using Rhai across shared-library boundaries. * A build script is used to extract the environment variable (`RHAI_AHASH_SEED`, if any) and splice it into the source code before compilation. -### No Timestamps +### `no_time` for no timestamps -* A new feature, `no_time`, is added to disable support timestamps. +* A new feature, `no_time`, is added to disable support for timestamps. * This may be necessary when building for architectures without time support, such as raw WASM. ### Serializable `Scope` diff --git a/Cargo.toml b/Cargo.toml index 4db19d3b..0efb84c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,8 +19,7 @@ categories = ["no-std", "embedded", "wasm", "parser-implementations"] [dependencies] smallvec = { version = "1.7", default-features = false, features = ["union", "const_new", "const_generics"] } -# 0.8.1 pulls in `getrandom/js` which breaks no-std -ahash = { version = "=0.8.0", default-features = false, features = ["compile-time-rng"] } +ahash = { version = "0.8.2", default-features = false, features = ["compile-time-rng"] } num-traits = { version = "0.2", default-features = false } bitflags = { version = "1", default-features = false } smartstring = { version = "1", default-features = false } @@ -28,7 +27,7 @@ rhai_codegen = { version = "1.4.1", path = "codegen", default-features = false } no-std-compat = { version = "0.4", default-features = false, features = ["alloc"], optional = true } libm = { version = "0.2", default-features = false, optional = true } -hashbrown = { version = "0.12", optional = true } +hashbrown = { version = "0.13", optional = true } core-error = { version = "0.0", default-features = false, features = ["alloc"], optional = true } serde = { version = "1.0", default-features = false, features = ["derive", "alloc"], optional = true } serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true } diff --git a/src/api/build_type.rs b/src/api/build_type.rs index 9ce59ba4..2ebab673 100644 --- a/src/api/build_type.rs +++ b/src/api/build_type.rs @@ -99,6 +99,10 @@ impl Engine { /// /// To define a pretty-print name, call [`with_name`][`TypeBuilder::with_name`], /// to use [`Engine::register_type_with_name`] instead. +/// +/// # WARNING - Volatile Type +/// +/// This type is volatile and may change in the future. #[deprecated = "This type is NOT deprecated, but it is considered volatile and may change in the future."] pub struct TypeBuilder<'a, T: Variant + Clone> { engine: &'a mut Engine, diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 1a78cae1..335be57d 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -3,9 +3,10 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::types::dynamic::Variant; +use crate::types::RestoreOnDrop; use crate::{ - reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, - StaticVec, AST, ERR, + reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST, + ERR, }; use std::any::{type_name, TypeId}; #[cfg(feature = "no_std")] @@ -248,31 +249,28 @@ impl Engine { arg_values: &mut [Dynamic], ) -> RhaiResult { let statements = ast.statements(); - let lib = &[AsRef::::as_ref(ast).clone()]; + + let orig_lib_len = global.lib.len(); + + #[cfg(not(feature = "no_function"))] + global.lib.push(ast.shared_lib().clone()); let mut no_this_ptr = Dynamic::NULL; let this_ptr = this_ptr.unwrap_or(&mut no_this_ptr); - let orig_scope_len = scope.len(); - #[cfg(not(feature = "no_module"))] let orig_embedded_module_resolver = std::mem::replace( &mut global.embedded_module_resolver, ast.resolver().cloned(), ); - #[cfg(not(feature = "no_module"))] - let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { - g.embedded_module_resolver = orig_embedded_module_resolver - }); let result = if eval_ast && !statements.is_empty() { - let r = self.eval_global_statements(global, caches, lib, scope, statements); + let orig_scope_len = scope.len(); + let scope = &mut *RestoreOnDrop::lock_if(rewind_scope, scope, move |s| { + s.rewind(orig_scope_len); + }); - if rewind_scope { - scope.rewind(orig_scope_len); - } - - r + self.eval_global_statements(global, caches, scope, statements) } else { Ok(Dynamic::UNIT) } @@ -287,7 +285,6 @@ impl Engine { self.call_script_fn( global, caches, - lib, scope, this_ptr, fn_def, @@ -298,15 +295,21 @@ impl Engine { } else { Err(ERR::ErrorFunctionNotFound(name.into(), Position::NONE).into()) } - })?; + }); #[cfg(feature = "debugging")] if self.debugger.is_some() { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let node = &crate::ast::Stmt::Noop(Position::NONE); - self.run_debugger(global, caches, lib, scope, this_ptr, node)?; + self.run_debugger(global, caches, scope, this_ptr, node)?; } - Ok(result) + #[cfg(not(feature = "no_module"))] + { + global.embedded_module_resolver = orig_embedded_module_resolver; + } + global.lib.truncate(orig_lib_len); + + result } } diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index a12ea593..b884cc24 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -89,6 +89,7 @@ impl Expression<'_> { /// # WARNING - Low Level API /// /// This function is _extremely_ low level. It evaluates an expression from an [`AST`][crate::AST]. + #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."] #[inline(always)] pub fn eval_with_context_raw( &self, diff --git a/src/api/eval.rs b/src/api/eval.rs index 10cf9040..2c5ffb70 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -6,9 +6,9 @@ use crate::types::dynamic::Variant; use crate::{ Dynamic, Engine, 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 as a script, returning the result value or an error. @@ -190,19 +190,6 @@ impl Engine { let result = self.eval_ast_with_scope_raw(global, caches, scope, ast)?; - #[cfg(feature = "debugging")] - if self.debugger.is_some() { - global.debugger.status = crate::eval::DebuggerStatus::Terminate; - let lib = &[ - #[cfg(not(feature = "no_function"))] - AsRef::::as_ref(ast).clone(), - ]; - let mut this = Dynamic::NULL; - let node = &crate::ast::Stmt::Noop(Position::NONE); - - self.run_debugger(global, caches, lib, scope, &mut this, node)?; - } - let typ = self.map_type_name(result.type_name()); result.try_cast::().ok_or_else(|| { @@ -216,21 +203,22 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - scope: &mut Scope, ast: &'a AST, ) -> RhaiResult { - global.source = ast.source_raw().cloned(); + let orig_source = mem::replace(&mut global.source, ast.source_raw().cloned()); + + #[cfg(not(feature = "no_function"))] + let orig_lib_len = global.lib.len(); + + #[cfg(not(feature = "no_function"))] + global.lib.push(ast.shared_lib().clone()); #[cfg(not(feature = "no_module"))] - let orig_embedded_module_resolver = std::mem::replace( + let orig_embedded_module_resolver = mem::replace( &mut global.embedded_module_resolver, ast.resolver().cloned(), ); - #[cfg(not(feature = "no_module"))] - let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { - g.embedded_module_resolver = orig_embedded_module_resolver - }); let statements = ast.statements(); @@ -238,12 +226,28 @@ impl Engine { return Ok(Dynamic::UNIT); } - let lib = &[ - #[cfg(not(feature = "no_function"))] - AsRef::::as_ref(ast).clone(), - ]; + let result = self.eval_global_statements(global, caches, scope, statements); - self.eval_global_statements(global, caches, lib, scope, statements) + #[cfg(feature = "debugging")] + if self.debugger.is_some() { + global.debugger.status = crate::eval::DebuggerStatus::Terminate; + let mut this = Dynamic::NULL; + let node = &crate::ast::Stmt::Noop(Position::NONE); + + self.run_debugger(global, caches, scope, &mut this, node)?; + } + + #[cfg(not(feature = "no_module"))] + { + global.embedded_module_resolver = orig_embedded_module_resolver; + } + + #[cfg(not(feature = "no_function"))] + global.lib.truncate(orig_lib_len); + + global.source = orig_source; + + result } /// _(internals)_ Evaluate a list of statements with no `this` pointer. /// Exported under the `internals` feature only. @@ -259,12 +263,10 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[crate::SharedModule], - scope: &mut Scope, statements: &[crate::ast::Stmt], ) -> RhaiResult { - self.eval_global_statements(global, caches, lib, scope, statements) + self.eval_global_statements(global, caches, scope, statements) } } diff --git a/src/api/json.rs b/src/api/json.rs index c22d682c..4365a4a8 100644 --- a/src/api/json.rs +++ b/src/api/json.rs @@ -166,7 +166,7 @@ pub fn format_map_as_json(map: &Map) -> String { if let Some(val) = value.read_lock::() { result.push_str(&format_map_as_json(&*val)); - } else if value.is::<()>() { + } else if value.is_unit() { result.push_str("null"); } else { write!(result, "{:?}", value).unwrap(); diff --git a/src/api/optimize.rs b/src/api/optimize.rs index f790a803..0baccba6 100644 --- a/src/api/optimize.rs +++ b/src/api/optimize.rs @@ -52,7 +52,7 @@ impl Engine { let mut ast = ast; #[cfg(not(feature = "no_function"))] - let lib = ast + let functions = ast .shared_lib() .iter_fn() .filter(|f| f.func.is_script()) @@ -64,7 +64,7 @@ impl Engine { scope, ast.take_statements(), #[cfg(not(feature = "no_function"))] - lib, + functions, optimization_level, ); diff --git a/src/api/run.rs b/src/api/run.rs index 551b5e98..a83a3a73 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -2,7 +2,7 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::parser::ParseState; -use crate::{Engine, RhaiResultOf, Scope, SharedModule, AST}; +use crate::{Engine, RhaiResultOf, Scope, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -115,6 +115,9 @@ impl Engine { let global = &mut GlobalRuntimeState::new(self); global.source = ast.source_raw().cloned(); + #[cfg(not(feature = "no_function"))] + global.lib.push(ast.shared_lib().clone()); + #[cfg(not(feature = "no_module"))] { global.embedded_module_resolver = ast.resolver().cloned(); @@ -122,28 +125,15 @@ impl Engine { let statements = ast.statements(); if !statements.is_empty() { - let lib: &[SharedModule] = &[ - #[cfg(not(feature = "no_function"))] - AsRef::::as_ref(ast).clone(), - ]; - let lib = if lib.first().map_or(true, |m| m.is_empty()) { - &[][..] - } else { - &lib - }; - self.eval_global_statements(global, caches, lib, scope, statements)?; + self.eval_global_statements(global, caches, scope, statements)?; } #[cfg(feature = "debugging")] if self.debugger.is_some() { global.debugger.status = crate::eval::DebuggerStatus::Terminate; - let lib = &[ - #[cfg(not(feature = "no_function"))] - AsRef::::as_ref(ast).clone(), - ]; let mut this = crate::Dynamic::NULL; let node = &crate::ast::Stmt::Noop(crate::Position::NONE); - self.run_debugger(global, caches, lib, scope, &mut this, node)?; + self.run_debugger(global, caches, scope, &mut this, node)?; } Ok(()) diff --git a/src/ast/ast.rs b/src/ast/ast.rs index c1cfbaed..71b07669 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -57,12 +57,11 @@ impl fmt::Debug for AST { fp.field("body", &self.body.as_slice()); #[cfg(not(feature = "no_function"))] - if !self.lib.is_empty() { - for (.., fn_def) in self.lib.iter_script_fn() { - let sig = fn_def.to_string(); - fp.field(&sig, &fn_def.body.as_slice()); - } + for (.., fn_def) in self.lib.iter_script_fn() { + let sig = fn_def.to_string(); + fp.field(&sig, &fn_def.body.as_slice()); } + fp.finish() } } @@ -739,7 +738,7 @@ impl AST { &mut self, filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool, ) -> &mut Self { - if !self.lib.is_empty() { + if self.has_functions() { crate::func::shared_make_mut(&mut self.lib).retain_script_functions(filter); } self diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 163fdbc9..1d39171d 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -20,16 +20,6 @@ use std::{ num::{NonZeroU64, NonZeroU8, NonZeroUsize}, }; -#[cfg(not(feature = "no_float"))] -use std::{ - hash::Hasher, - ops::{Deref, DerefMut}, - str::FromStr, -}; - -#[cfg(not(feature = "no_float"))] -use num_traits::float::FloatCore as Float; - /// _(internals)_ A binary expression. /// Exported under the `internals` feature only. #[derive(Debug, Clone, Hash)] @@ -273,120 +263,6 @@ impl FnCallExpr { } } -/// A type that wraps a floating-point number and implements [`Hash`]. -/// -/// Not available under `no_float`. -#[cfg(not(feature = "no_float"))] -#[derive(Clone, Copy, PartialEq, PartialOrd)] -pub struct FloatWrapper(F); - -#[cfg(not(feature = "no_float"))] -impl Hash for FloatWrapper { - #[inline] - fn hash(&self, state: &mut H) { - self.0.to_ne_bytes().hash(state); - } -} - -#[cfg(not(feature = "no_float"))] -impl AsRef for FloatWrapper { - #[inline(always)] - #[must_use] - fn as_ref(&self) -> &F { - &self.0 - } -} - -#[cfg(not(feature = "no_float"))] -impl AsMut for FloatWrapper { - #[inline(always)] - #[must_use] - fn as_mut(&mut self) -> &mut F { - &mut self.0 - } -} - -#[cfg(not(feature = "no_float"))] -impl Deref for FloatWrapper { - type Target = F; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -#[cfg(not(feature = "no_float"))] -impl DerefMut for FloatWrapper { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -#[cfg(not(feature = "no_float"))] -impl fmt::Debug for FloatWrapper { - #[cold] - #[inline(never)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } -} - -#[cfg(not(feature = "no_float"))] -impl> fmt::Display for FloatWrapper { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let abs = self.0.abs(); - if abs.is_zero() { - f.write_str("0.0") - } else if abs > Self::MAX_NATURAL_FLOAT_FOR_DISPLAY.into() - || abs < Self::MIN_NATURAL_FLOAT_FOR_DISPLAY.into() - { - write!(f, "{:e}", self.0) - } else { - fmt::Display::fmt(&self.0, f)?; - if abs.fract().is_zero() { - f.write_str(".0")?; - } - Ok(()) - } - } -} - -#[cfg(not(feature = "no_float"))] -impl From for FloatWrapper { - #[inline(always)] - fn from(value: F) -> Self { - Self::new(value) - } -} - -#[cfg(not(feature = "no_float"))] -impl FromStr for FloatWrapper { - type Err = ::Err; - - #[inline] - fn from_str(s: &str) -> Result { - F::from_str(s).map(Into::into) - } -} - -#[cfg(not(feature = "no_float"))] -impl FloatWrapper { - /// Maximum floating-point number for natural display before switching to scientific notation. - pub const MAX_NATURAL_FLOAT_FOR_DISPLAY: f32 = 10_000_000_000_000.0; - - /// Minimum floating-point number for natural display before switching to scientific notation. - pub const MIN_NATURAL_FLOAT_FOR_DISPLAY: f32 = 0.000_000_000_000_1; - - /// Create a new [`FloatWrapper`]. - #[inline(always)] - #[must_use] - pub const fn new(value: F) -> Self { - Self(value) - } -} - /// _(internals)_ An expression sub-tree. /// Exported under the `internals` feature only. #[derive(Clone, Hash)] @@ -405,7 +281,7 @@ pub enum Expr { IntegerConstant(INT, Position), /// Floating-point constant. #[cfg(not(feature = "no_float"))] - FloatConstant(FloatWrapper, Position), + FloatConstant(crate::types::FloatWrapper, Position), /// Character constant. CharConstant(char, Position), /// [String][ImmutableString] constant. diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 00719a31..695f0786 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -26,9 +26,6 @@ pub use stmt::{ SwitchCasesCollection, TryCatchBlock, }; -#[cfg(not(feature = "no_float"))] -pub use expr::FloatWrapper; - /// _(internals)_ Placeholder for a script-defined function. /// Exported under the `internals` feature only. #[cfg(feature = "no_function")] diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index 71801d82..260f7de8 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -559,7 +559,7 @@ fn main() { // Evaluate engine.eval_ast_with_scope::(&mut scope, &main_ast) }) { - Ok(result) if !result.is::<()>() => { + Ok(result) if !result.is_unit() => { println!("=> {result:?}"); println!(); } diff --git a/src/config/hashing.rs b/src/config/hashing.rs index dd93e812..671b6664 100644 --- a/src/config/hashing.rs +++ b/src/config/hashing.rs @@ -94,7 +94,7 @@ fn hokmalock(address: usize) -> &'static HokmaLock { &RECORDS[address % LEN] } -// Safety: lol, there is a reason its called "SusLock" +// Safety: lol, there is a reason its called `SusLock` #[must_use] struct SusLock where @@ -118,36 +118,39 @@ where } } + #[inline(always)] + #[must_use] + pub fn is_initialized(&self) -> bool { + self.initialized.load(Ordering::SeqCst) + } + #[must_use] pub fn get(&self) -> Option<&'static T> { if self.initialized.load(Ordering::SeqCst) { let hokma = hokmalock(unsafe { mem::transmute(self.data.get()) }); // we forgo the optimistic read, because we don't really care let guard = hokma.write(); - let val = { - let cast: *const T = self.data.get().cast(); - unsafe { mem::transmute::<*const T, &'static T>(cast) } - }; + let cast: *const T = self.data.get().cast(); + let val = unsafe { mem::transmute::<*const T, &'static T>(cast) }; guard.the_price_of_silence(); Some(val) } else { - return None; + None } } #[must_use] - pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&'static T> { + pub fn get_or_init(&self, f: impl FnOnce() -> T) -> &'static T { if !self.initialized.load(Ordering::SeqCst) { - let value = f(); self.initialized.store(true, Ordering::SeqCst); let hokma = hokmalock(unsafe { mem::transmute(self.data.get()) }); hokma.write(); unsafe { - self.data.get().write(MaybeUninit::new(value)); + self.data.get().write(MaybeUninit::new(f())); } } - self.get() + self.get().unwrap() } pub fn set(&self, value: T) -> Result<(), T> { @@ -217,10 +220,9 @@ pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]> #[inline] #[must_use] pub fn get_ahash_seed() -> &'static Option<[u64; 4]> { - const NONE: &'static Option<[u64; 4]> = &None; - - match AHASH_SEED.get_or_init(|| hashing_env::AHASH_SEED) { - Some(ash) => ash, - None => NONE, + if !AHASH_SEED.is_initialized() { + return &hashing_env::AHASH_SEED; } + + AHASH_SEED.get().unwrap_or(&hashing_env::AHASH_SEED) } diff --git a/src/eval/cache.rs b/src/eval/cache.rs index ee4f07a3..96160d7e 100644 --- a/src/eval/cache.rs +++ b/src/eval/cache.rs @@ -44,44 +44,39 @@ impl FnResolutionCache { /// The following caches are contained inside this type: /// * A stack of [function resolution caches][FnResolutionCache] #[derive(Debug, Clone)] -pub struct Caches { - /// Stack of [function resolution caches][FnResolutionCache]. - stack: StaticVec, -} +pub struct Caches(StaticVec); impl Caches { /// Create an empty [`Caches`]. #[inline(always)] #[must_use] pub const fn new() -> Self { - Self { - stack: StaticVec::new_const(), - } + 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.stack.len() + 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.stack.is_empty() { + if self.0.is_empty() { // Push a new function resolution cache if the stack is empty self.push_fn_resolution_cache(); } - self.stack.last_mut().unwrap() + self.0.last_mut().unwrap() } /// Push an empty function resolution cache onto the stack and make it current. #[inline(always)] pub fn push_fn_resolution_cache(&mut self) { - self.stack.push(Default::default()); + self.0.push(Default::default()); } /// Rewind the function resolution caches stack to a particular size. #[inline(always)] pub fn rewind_fn_resolution_caches(&mut self, len: usize) { - self.stack.truncate(len); + self.0.truncate(len); } } diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index ca813d86..5c3fa792 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -5,9 +5,7 @@ use super::{Caches, GlobalRuntimeState, Target}; use crate::ast::{ASTFlags, Expr, OpAssignment}; use crate::types::dynamic::Union; use crate::types::RestoreOnDrop; -use crate::{ - Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR, -}; +use crate::{Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR}; use std::hash::Hash; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -43,7 +41,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], this_ptr: &mut Dynamic, target: &mut Target, root: (&str, Position), @@ -63,7 +60,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] ChainType::Indexing => { // Check for existence with the null conditional operator - if parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() { + if parent_options.contains(ASTFlags::NEGATED) && target.is_unit() { return Ok((Dynamic::UNIT, false)); } @@ -75,7 +72,7 @@ impl Engine { if !parent_options.contains(ASTFlags::BREAK) => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?; + self.run_debugger(global, caches, scope, this_ptr, _parent)?; let idx_val = &mut idx_values.pop().unwrap(); let mut idx_val_for_setter = idx_val.clone(); @@ -84,14 +81,14 @@ impl Engine { let (try_setter, result) = { let mut obj = self.get_indexed_mut( - global, caches, lib, target, idx_val, idx_pos, false, true, + global, caches, target, idx_val, idx_pos, false, true, )?; let is_obj_temp_val = obj.is_temp_value(); let obj_ptr = &mut obj; match self.eval_dot_index_chain_helper( - global, caches, lib, this_ptr, obj_ptr, root, rhs, *options, - &x.rhs, idx_values, rhs_chain, new_val, + global, caches, this_ptr, obj_ptr, root, rhs, *options, &x.rhs, + idx_values, rhs_chain, new_val, ) { Ok((result, true)) if is_obj_temp_val => { (Some(obj.take_or_clone()), (result, true)) @@ -105,13 +102,11 @@ impl Engine { // Try to call index setter if value is changed let idx = &mut idx_val_for_setter; let new_val = &mut new_val; - self.call_indexer_set( - global, caches, lib, target, idx, new_val, is_ref_mut, - ) - .or_else(|e| match *e { - ERR::ErrorIndexingType(..) => Ok((Dynamic::UNIT, false)), - _ => Err(e), - })?; + self.call_indexer_set(global, caches, target, idx, new_val, is_ref_mut) + .or_else(|e| match *e { + ERR::ErrorIndexingType(..) => Ok((Dynamic::UNIT, false)), + _ => Err(e), + })?; } Ok(result) @@ -119,19 +114,19 @@ impl Engine { // xxx[rhs] op= new_val _ if new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?; + self.run_debugger(global, caches, scope, this_ptr, _parent)?; let (new_val, op_info) = new_val.take().expect("`Some`"); let idx_val = &mut idx_values.pop().unwrap(); let idx = &mut idx_val.clone(); let try_setter = match self - .get_indexed_mut(global, caches, lib, target, idx, pos, true, false) + .get_indexed_mut(global, caches, target, idx, pos, true, false) { // Indexed value is not a temp value - update directly Ok(ref mut obj_ptr) => { self.eval_op_assignment( - global, caches, lib, op_info, obj_ptr, root, new_val, + global, caches, op_info, obj_ptr, root, new_val, )?; self.check_data_size(obj_ptr, op_info.pos)?; None @@ -149,13 +144,12 @@ impl Engine { let idx = &mut idx_val.clone(); // Call the index getter to get the current value - if let Ok(val) = - self.call_indexer_get(global, caches, lib, target, idx) + if let Ok(val) = self.call_indexer_get(global, caches, target, idx) { let mut val = val.into(); // Run the op-assignment self.eval_op_assignment( - global, caches, lib, op_info, &mut val, root, new_val, + global, caches, op_info, &mut val, root, new_val, )?; // Replace new value new_val = val.take_or_clone(); @@ -167,7 +161,7 @@ impl Engine { let new_val = &mut new_val; self.call_indexer_set( - global, caches, lib, target, idx_val, new_val, is_ref_mut, + global, caches, target, idx_val, new_val, is_ref_mut, )?; } @@ -176,11 +170,11 @@ impl Engine { // xxx[rhs] _ => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?; + self.run_debugger(global, caches, scope, this_ptr, _parent)?; let idx_val = &mut idx_values.pop().unwrap(); - self.get_indexed_mut(global, caches, lib, target, idx_val, pos, false, true) + self.get_indexed_mut(global, caches, target, idx_val, pos, false, true) .map(|v| (v.take_or_clone(), false)) } } @@ -189,7 +183,7 @@ impl Engine { #[cfg(not(feature = "no_object"))] ChainType::Dotting => { // Check for existence with the Elvis operator - if parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() { + if parent_options.contains(ASTFlags::NEGATED) && target.is_unit() { return Ok((Dynamic::UNIT, false)); } @@ -197,8 +191,8 @@ impl Engine { // xxx.fn_name(arg_expr_list) Expr::MethodCall(x, pos) if !x.is_qualified() && new_val.is_none() => { #[cfg(feature = "debugging")] - let reset = self - .run_debugger_with_reset(global, caches, lib, scope, this_ptr, rhs)?; + let reset = + self.run_debugger_with_reset(global, caches, scope, this_ptr, rhs)?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) @@ -217,7 +211,7 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); self.make_method_call( - global, caches, lib, name, *hashes, target, call_args, pos1, *pos, + global, caches, name, *hashes, target, call_args, pos1, *pos, ) } // xxx.fn_name(...) = ??? @@ -229,38 +223,37 @@ impl Engine { unreachable!("function call in dot chain should not be namespace-qualified") } // {xxx:map}.id op= ??? - Expr::Property(x, pos) if target.is::() && new_val.is_some() => { + Expr::Property(x, pos) if target.is_map() && new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, scope, this_ptr, rhs)?; let index = &mut x.2.clone().into(); let (new_val, op_info) = new_val.take().expect("`Some`"); { let val_target = &mut self.get_indexed_mut( - global, caches, lib, target, index, *pos, true, false, + global, caches, target, index, *pos, true, false, )?; self.eval_op_assignment( - global, caches, lib, op_info, val_target, root, new_val, + global, caches, op_info, val_target, root, new_val, )?; } self.check_data_size(target.source(), op_info.pos)?; Ok((Dynamic::UNIT, true)) } // {xxx:map}.id - Expr::Property(x, pos) if target.is::() => { + Expr::Property(x, pos) if target.is_map() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, scope, this_ptr, rhs)?; let index = &mut x.2.clone().into(); - let val = self.get_indexed_mut( - global, caches, lib, target, index, *pos, false, false, - )?; + let val = self + .get_indexed_mut(global, caches, target, index, *pos, false, false)?; Ok((val.take_or_clone(), false)) } // xxx.id op= ??? Expr::Property(x, pos) if new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, scope, this_ptr, rhs)?; let ((getter, hash_get), (setter, hash_set), name) = &**x; let (mut new_val, op_info) = new_val.take().expect("`Some`"); @@ -269,23 +262,18 @@ impl Engine { let args = &mut [target.as_mut()]; let (mut orig_val, ..) = self .exec_native_fn_call( - global, caches, lib, getter, None, *hash_get, args, is_ref_mut, - *pos, + global, caches, getter, None, *hash_get, args, is_ref_mut, *pos, ) .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, caches, lib, target, &mut prop, - ) - .map(|r| (r, false)) - .map_err(|e| { - match *e { + self.call_indexer_get(global, caches, target, &mut prop) + .map(|r| (r, false)) + .map_err(|e| match *e { ERR::ErrorIndexingType(..) => err, _ => e, - } - }) + }) } _ => Err(err), })?; @@ -294,7 +282,7 @@ impl Engine { let orig_val = &mut (&mut orig_val).into(); self.eval_op_assignment( - global, caches, lib, op_info, orig_val, root, new_val, + global, caches, op_info, orig_val, root, new_val, )?; } @@ -303,7 +291,7 @@ impl Engine { let args = &mut [target.as_mut(), &mut new_val]; self.exec_native_fn_call( - global, caches, lib, setter, None, *hash_set, args, is_ref_mut, *pos, + global, caches, setter, None, *hash_set, args, is_ref_mut, *pos, ) .or_else(|err| match *err { // Try an indexer if property does not exist @@ -311,7 +299,7 @@ impl Engine { let idx = &mut name.into(); let new_val = &mut new_val; self.call_indexer_set( - global, caches, lib, target, idx, new_val, is_ref_mut, + global, caches, target, idx, new_val, is_ref_mut, ) .map_err(|e| match *e { ERR::ErrorIndexingType(..) => err, @@ -324,19 +312,19 @@ impl Engine { // xxx.id Expr::Property(x, pos) => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, scope, this_ptr, rhs)?; let ((getter, hash_get), _, name) = &**x; let args = &mut [target.as_mut()]; self.exec_native_fn_call( - global, caches, lib, getter, None, *hash_get, args, is_ref_mut, *pos, + global, caches, getter, None, *hash_get, args, is_ref_mut, *pos, ) .map_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, caches, lib, target, &mut prop) + self.call_indexer_get(global, caches, target, &mut prop) .map(|r| (r, false)) .map_err(|e| match *e { ERR::ErrorIndexingType(..) => err, @@ -351,25 +339,25 @@ impl Engine { } // {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr Expr::Index(x, options, x_pos) | Expr::Dot(x, options, x_pos) - if target.is::() => + if target.is_map() => { let _node = &x.lhs; let val_target = &mut match x.lhs { Expr::Property(ref p, pos) => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, scope, this_ptr, _node)?; + self.run_debugger(global, caches, scope, this_ptr, _node)?; let index = &mut p.2.clone().into(); self.get_indexed_mut( - global, caches, lib, target, index, pos, false, true, + global, caches, target, index, pos, false, true, )? } // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr Expr::MethodCall(ref x, pos) if !x.is_qualified() => { #[cfg(feature = "debugging")] let reset = self.run_debugger_with_reset( - global, caches, lib, scope, this_ptr, _node, + global, caches, scope, this_ptr, _node, )?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| { @@ -390,8 +378,7 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); self.make_method_call( - global, caches, lib, name, *hashes, target, call_args, pos1, - pos, + global, caches, name, *hashes, target, call_args, pos1, pos, )? .0 .into() @@ -406,7 +393,7 @@ impl Engine { let rhs_chain = rhs.into(); self.eval_dot_index_chain_helper( - global, caches, lib, this_ptr, val_target, root, rhs, *options, &x.rhs, + global, caches, this_ptr, val_target, root, rhs, *options, &x.rhs, idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(*x_pos)) @@ -419,7 +406,7 @@ impl Engine { // xxx.prop[expr] | xxx.prop.expr Expr::Property(ref p, pos) => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, scope, this_ptr, _node)?; + self.run_debugger(global, caches, scope, this_ptr, _node)?; let ((getter, hash_get), (setter, hash_set), name) = &**p; let rhs_chain = rhs.into(); @@ -429,23 +416,19 @@ impl Engine { // Assume getters are always pure let (mut val, ..) = self .exec_native_fn_call( - global, caches, lib, getter, None, *hash_get, args, - is_ref_mut, pos, + global, caches, getter, None, *hash_get, args, is_ref_mut, + pos, ) .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, caches, lib, target, &mut prop, - ) - .map(|r| (r, false)) - .map_err( - |e| match *e { + self.call_indexer_get(global, caches, target, &mut prop) + .map(|r| (r, false)) + .map_err(|e| match *e { ERR::ErrorIndexingType(..) => err, _ => e, - }, - ) + }) } _ => Err(err), })?; @@ -454,8 +437,8 @@ impl Engine { let (result, may_be_changed) = self .eval_dot_index_chain_helper( - global, caches, lib, this_ptr, val, root, rhs, *options, - &x.rhs, idx_values, rhs_chain, new_val, + global, caches, this_ptr, val, root, rhs, *options, &x.rhs, + idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(*x_pos))?; @@ -465,8 +448,8 @@ impl Engine { let mut arg_values = [target.as_mut(), val.as_mut()]; let args = &mut arg_values; self.exec_native_fn_call( - global, caches, lib, setter, None, *hash_set, args, - is_ref_mut, pos, + global, caches, setter, None, *hash_set, args, is_ref_mut, + pos, ) .or_else( |err| match *err { @@ -475,7 +458,7 @@ impl Engine { let idx = &mut name.into(); let new_val = val; self.call_indexer_set( - global, caches, lib, target, idx, new_val, + global, caches, target, idx, new_val, is_ref_mut, ) .or_else(|e| match *e { @@ -499,7 +482,7 @@ impl Engine { let val = { #[cfg(feature = "debugging")] let reset = self.run_debugger_with_reset( - global, caches, lib, scope, this_ptr, _node, + global, caches, scope, this_ptr, _node, )?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| { @@ -521,8 +504,7 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); self.make_method_call( - global, caches, lib, name, *hashes, target, call_args, - pos1, pos, + global, caches, name, *hashes, target, call_args, pos1, pos, )? .0 }; @@ -531,8 +513,8 @@ impl Engine { let rhs_chain = rhs.into(); self.eval_dot_index_chain_helper( - global, caches, lib, this_ptr, val, root, rhs, *options, - &x.rhs, idx_values, rhs_chain, new_val, + global, caches, this_ptr, val, root, rhs, *options, &x.rhs, + idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(pos)) } @@ -556,7 +538,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, this_ptr: &mut Dynamic, expr: &Expr, @@ -597,7 +578,7 @@ impl Engine { // All other patterns - evaluate the arguments chain _ => { self.eval_dot_index_chain_arguments( - global, caches, lib, scope, this_ptr, rhs, options, chain_type, idx_values, + global, caches, scope, this_ptr, rhs, options, chain_type, idx_values, )?; } } @@ -606,18 +587,18 @@ impl Engine { // id.??? or id[???] Expr::Variable(x, .., var_pos) => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, scope, this_ptr, lhs)?; + self.run_debugger(global, caches, scope, this_ptr, lhs)?; self.track_operation(global, *var_pos)?; let (mut target, ..) = - self.search_namespace(global, caches, lib, scope, this_ptr, lhs)?; + self.search_namespace(global, caches, scope, this_ptr, lhs)?; let obj_ptr = &mut target; let root = (x.3.as_str(), *var_pos); let mut this = Dynamic::NULL; self.eval_dot_index_chain_helper( - global, caches, lib, &mut this, obj_ptr, root, expr, options, rhs, idx_values, + global, caches, &mut this, obj_ptr, root, expr, options, rhs, idx_values, chain_type, new_val, ) } @@ -626,13 +607,13 @@ impl Engine { // {expr}.??? or {expr}[???] expr => { let value = self - .eval_expr(global, caches, lib, scope, this_ptr, expr)? + .eval_expr(global, caches, scope, this_ptr, expr)? .flatten(); let obj_ptr = &mut value.into(); let root = ("", expr.start_position()); self.eval_dot_index_chain_helper( - global, caches, lib, this_ptr, obj_ptr, root, expr, options, rhs, idx_values, + global, caches, this_ptr, obj_ptr, root, expr, options, rhs, idx_values, chain_type, new_val, ) } @@ -646,7 +627,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, this_ptr: &mut Dynamic, expr: &Expr, @@ -663,7 +643,7 @@ impl Engine { { for arg_expr in &x.args { idx_values.push( - self.get_arg_value(global, caches, lib, scope, this_ptr, arg_expr)? + self.get_arg_value(global, caches, scope, this_ptr, arg_expr)? .0 .flatten(), ); @@ -697,7 +677,7 @@ impl Engine { { for arg_expr in &x.args { _arg_values.push( - self.get_arg_value(global, caches, lib, scope, this_ptr, arg_expr)? + self.get_arg_value(global, caches, scope, this_ptr, arg_expr)? .0 .flatten(), ); @@ -714,7 +694,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] _ if parent_chain_type == ChainType::Indexing => { _arg_values.push( - self.eval_expr(global, caches, lib, scope, this_ptr, lhs)? + self.eval_expr(global, caches, scope, this_ptr, lhs)? .flatten(), ); } @@ -725,7 +705,7 @@ impl Engine { let chain_type = expr.into(); self.eval_dot_index_chain_arguments( - global, caches, lib, scope, this_ptr, rhs, *options, chain_type, idx_values, + global, caches, scope, this_ptr, rhs, *options, chain_type, idx_values, )?; if !_arg_values.is_empty() { @@ -739,7 +719,7 @@ impl Engine { } #[cfg(not(feature = "no_index"))] _ if parent_chain_type == ChainType::Indexing => idx_values.push( - self.eval_expr(global, caches, lib, scope, this_ptr, expr)? + self.eval_expr(global, caches, scope, this_ptr, expr)? .flatten(), ), _ => unreachable!("unknown chained expression: {:?}", expr), @@ -754,7 +734,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], target: &mut Dynamic, idx: &mut Dynamic, ) -> RhaiResultOf { @@ -763,10 +742,11 @@ impl Engine { let fn_name = crate::engine::FN_IDX_GET; let pos = Position::NONE; + let orig_level = global.level; global.level += 1; - let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level); - self.exec_native_fn_call(global, caches, lib, fn_name, None, hash, args, true, pos) + self.exec_native_fn_call(global, caches, fn_name, None, hash, args, true, pos) .map(|(r, ..)| r) } @@ -776,7 +756,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], target: &mut Dynamic, idx: &mut Dynamic, new_val: &mut Dynamic, @@ -787,12 +766,11 @@ impl Engine { let fn_name = crate::engine::FN_IDX_SET; let pos = Position::NONE; + let orig_level = global.level; global.level += 1; - let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level); - self.exec_native_fn_call( - global, caches, lib, fn_name, None, hash, args, is_ref_mut, pos, - ) + self.exec_native_fn_call(global, caches, fn_name, None, hash, args, is_ref_mut, pos) } /// Get the value at the indexed position of a base type. @@ -801,7 +779,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], target: &'t mut Dynamic, idx: &mut Dynamic, idx_pos: Position, @@ -1010,7 +987,7 @@ impl Engine { } _ if use_indexers => self - .call_indexer_get(global, caches, lib, target, idx) + .call_indexer_get(global, caches, target, idx) .map(Into::into), _ => Err(ERR::ErrorIndexingType( diff --git a/src/eval/data_check.rs b/src/eval/data_check.rs index abda0e45..bce0b87b 100644 --- a/src/eval/data_check.rs +++ b/src/eval/data_check.rs @@ -147,15 +147,11 @@ impl Engine { return Err(ERR::ErrorTooManyOperations(pos).into()); } - // Report progress - only in steps - if let Some(ref progress) = self.progress { - if let Some(token) = progress(num_operations) { - // Terminate script if progress returns a termination token - return Err(ERR::ErrorTerminated(token, pos).into()); - } - } - - Ok(()) + // Report progress + self.progress + .as_ref() + .and_then(|p| p(num_operations)) + .map_or(Ok(()), |token| Err(ERR::ErrorTerminated(token, pos).into())) } /// Check a result to ensure that it is valid. diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 6eed7279..bf74af3d 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -3,9 +3,7 @@ use super::{Caches, EvalContext, GlobalRuntimeState}; use crate::ast::{ASTNode, Expr, Stmt}; -use crate::{ - Dynamic, Engine, EvalAltResult, ImmutableString, Position, RhaiResultOf, Scope, SharedModule, -}; +use crate::{Dynamic, Engine, EvalAltResult, ImmutableString, Position, RhaiResultOf, Scope}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{fmt, iter::repeat, mem}; @@ -413,14 +411,13 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf<()> { if self.debugger.is_some() { if let Some(cmd) = - self.run_debugger_with_reset_raw(global, caches, lib, scope, this_ptr, node)? + self.run_debugger_with_reset_raw(global, caches, scope, this_ptr, node)? { global.debugger.status = cmd; } @@ -439,13 +436,12 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf> { if self.debugger.is_some() { - self.run_debugger_with_reset_raw(global, caches, lib, scope, this_ptr, node) + self.run_debugger_with_reset_raw(global, caches, scope, this_ptr, node) } else { Ok(None) } @@ -461,7 +457,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, this_ptr: &mut Dynamic, node: impl Into>, @@ -494,7 +489,7 @@ impl Engine { }, }; - self.run_debugger_raw(global, caches, lib, scope, this_ptr, node, event) + self.run_debugger_raw(global, caches, scope, this_ptr, node, event) } /// Run the debugger callback unconditionally. /// @@ -507,7 +502,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, this_ptr: &mut Dynamic, node: ASTNode<'a>, @@ -515,7 +509,7 @@ impl Engine { ) -> Result, Box> { let src = global.source_raw().cloned(); let src = src.as_ref().map(|s| s.as_str()); - let context = crate::EvalContext::new(self, global, caches, lib, scope, this_ptr); + let context = crate::EvalContext::new(self, global, caches, scope, this_ptr); if let Some((.., ref on_debugger)) = self.debugger { let command = on_debugger(context, event, node, src, node.position())?; diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index f8f2fd1a..82b3750b 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -1,7 +1,7 @@ //! Evaluation context. use super::{Caches, GlobalRuntimeState}; -use crate::{Dynamic, Engine, Module, Scope, SharedModule}; +use crate::{Dynamic, Engine, Module, Scope}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -11,14 +11,12 @@ use std::prelude::v1::*; pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> { /// The current [`Engine`]. engine: &'a Engine, - /// The current [`Scope`]. - scope: &'s mut Scope<'ps>, /// The current [`GlobalRuntimeState`]. global: &'g mut GlobalRuntimeState, /// The current [caches][Caches], if available. caches: &'c mut Caches, - /// The current stack of imported [modules][Module]. - lib: &'a [SharedModule], + /// The current [`Scope`]. + scope: &'s mut Scope<'ps>, /// The current bound `this` pointer, if any. this_ptr: &'t mut Dynamic, } @@ -31,16 +29,14 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { engine: &'a Engine, global: &'g mut GlobalRuntimeState, caches: &'c mut Caches, - lib: &'a [SharedModule], scope: &'s mut Scope<'ps>, this_ptr: &'t mut Dynamic, ) -> Self { Self { engine, - scope, global, caches, - lib, + scope, this_ptr, } } @@ -104,17 +100,23 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { self.global } /// Get an iterator over the namespaces containing definition of all script-defined functions. + /// + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] #[inline] pub fn iter_namespaces(&self) -> impl Iterator { - self.lib.iter().map(|m| m.as_ref()) + self.global.lib.iter().map(|m| m.as_ref()) } /// _(internals)_ The current set of namespaces containing definitions of all script-defined functions. /// Exported under the `internals` feature only. + /// + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub const fn namespaces(&self) -> &[SharedModule] { - self.lib + pub fn namespaces(&self) -> &[crate::SharedModule] { + &self.global.lib } /// The current bound `this` pointer, if any. #[inline] @@ -181,20 +183,14 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { crate::ast::Expr::Stmt(statements) => self.engine.eval_stmt_block( self.global, self.caches, - self.lib, self.scope, self.this_ptr, statements, rewind_scope, ), - _ => self.engine.eval_expr( - self.global, - self.caches, - self.lib, - self.scope, - self.this_ptr, - expr, - ), + _ => self + .engine + .eval_expr(self.global, self.caches, self.scope, self.this_ptr, expr), } } } diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 0c18772a..58e45d8a 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -51,24 +51,22 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], - scope: &'s mut Scope, this_ptr: &'s mut Dynamic, expr: &Expr, ) -> RhaiResultOf<(Target<'s>, Position)> { match expr { Expr::Variable(_, Some(_), _) => { - self.search_scope_only(global, caches, lib, scope, this_ptr, expr) + self.search_scope_only(global, caches, scope, this_ptr, expr) } Expr::Variable(v, None, _var_pos) => match &**v { // Normal variable access #[cfg(not(feature = "no_module"))] (_, ns, ..) if ns.is_empty() => { - self.search_scope_only(global, caches, lib, scope, this_ptr, expr) + self.search_scope_only(global, caches, scope, this_ptr, expr) } #[cfg(feature = "no_module")] - (_, (), ..) => self.search_scope_only(global, caches, lib, scope, this_ptr, expr), + (_, (), ..) => self.search_scope_only(global, caches, scope, this_ptr, expr), // Qualified variable access #[cfg(not(feature = "no_module"))] @@ -135,8 +133,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], - scope: &'s mut Scope, this_ptr: &'s mut Dynamic, expr: &Expr, @@ -157,7 +153,8 @@ impl Engine { // Scripted function with the same name #[cfg(not(feature = "no_function"))] Expr::Variable(v, None, pos) - if lib + if global + .lib .iter() .flat_map(|m| m.iter_script_fn()) .any(|(_, _, f, ..)| f == v.3.as_str()) => @@ -172,7 +169,7 @@ impl Engine { // Check the variable resolver, if any if let Some(ref resolve_var) = self.resolve_var { - let context = EvalContext::new(self, global, caches, lib, scope, this_ptr); + let context = EvalContext::new(self, global, caches, scope, this_ptr); let var_name = expr.get_variable_name(true).expect("`Expr::Variable`"); match resolve_var(var_name, index, context) { Ok(Some(mut result)) => { @@ -220,8 +217,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], - scope: &mut Scope, this_ptr: &mut Dynamic, expr: &Expr, @@ -233,7 +228,7 @@ impl Engine { // binary operators are also function calls. if let Expr::FnCall(x, pos) = expr { #[cfg(feature = "debugging")] - let reset = self.run_debugger_with_reset(global, caches, lib, scope, this_ptr, expr)?; + let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?; #[cfg(feature = "debugging")] let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) @@ -241,7 +236,7 @@ impl Engine { self.track_operation(global, expr.position())?; - return self.eval_fn_call_expr(global, caches, lib, scope, this_ptr, x, *pos); + return self.eval_fn_call_expr(global, caches, scope, this_ptr, x, *pos); } // Then variable access. @@ -249,7 +244,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(global, caches, lib, scope, this_ptr, expr)?; + self.run_debugger(global, caches, scope, this_ptr, expr)?; self.track_operation(global, expr.position())?; @@ -260,13 +255,13 @@ impl Engine { Ok(this_ptr.clone()) } } else { - self.search_namespace(global, caches, lib, scope, this_ptr, expr) + self.search_namespace(global, caches, scope, this_ptr, expr) .map(|(val, ..)| val.take_or_clone()) }; } #[cfg(feature = "debugging")] - let reset = self.run_debugger_with_reset(global, caches, lib, scope, this_ptr, expr)?; + let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?; #[cfg(feature = "debugging")] let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) @@ -287,7 +282,7 @@ impl Engine { // `... ${...} ...` Expr::InterpolatedString(x, _) => { - let mut concat = self.get_interned_string("").into(); + let mut concat = self.const_empty_string().into(); let target = &mut concat; let mut op_info = OpAssignment::new_op_assignment(OP_CONCAT, Position::NONE); @@ -296,11 +291,13 @@ impl Engine { let result = x .iter() .try_for_each(|expr| { - let item = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?; + let item = self + .eval_expr(global, caches, scope, this_ptr, expr)? + .flatten(); op_info.pos = expr.start_position(); - self.eval_op_assignment(global, caches, lib, &op_info, target, root, item) + self.eval_op_assignment(global, caches, &op_info, target, root, item) }) .map(|_| concat.take_or_clone()); @@ -317,7 +314,7 @@ impl Engine { crate::Array::with_capacity(x.len()), |mut array, item_expr| { let value = self - .eval_expr(global, caches, lib, scope, this_ptr, item_expr)? + .eval_expr(global, caches, scope, this_ptr, item_expr)? .flatten(); #[cfg(not(feature = "unchecked"))] @@ -349,7 +346,7 @@ impl Engine { x.0.iter() .try_fold(x.1.clone(), |mut map, (key, value_expr)| { let value = self - .eval_expr(global, caches, lib, scope, this_ptr, value_expr)? + .eval_expr(global, caches, scope, this_ptr, value_expr)? .flatten(); #[cfg(not(feature = "unchecked"))] @@ -372,30 +369,30 @@ impl Engine { } Expr::And(x, ..) => Ok((self - .eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)? + .eval_expr(global, caches, scope, this_ptr, &x.lhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? && self - .eval_expr(global, caches, lib, scope, this_ptr, &x.rhs)? + .eval_expr(global, caches, scope, this_ptr, &x.rhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) .into()), Expr::Or(x, ..) => Ok((self - .eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)? + .eval_expr(global, caches, scope, this_ptr, &x.lhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? || self - .eval_expr(global, caches, lib, scope, this_ptr, &x.rhs)? + .eval_expr(global, caches, scope, this_ptr, &x.rhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) .into()), Expr::Coalesce(x, ..) => { - let value = self.eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)?; + let value = self.eval_expr(global, caches, scope, this_ptr, &x.lhs)?; - if value.is::<()>() { - self.eval_expr(global, caches, lib, scope, this_ptr, &x.rhs) + if value.is_unit() { + self.eval_expr(global, caches, scope, this_ptr, &x.rhs) } else { Ok(value) } @@ -415,7 +412,7 @@ impl Engine { *pos, )) })?; - let mut context = EvalContext::new(self, global, caches, lib, scope, this_ptr); + let mut context = EvalContext::new(self, global, caches, scope, this_ptr); let result = (custom_def.func)(&mut context, &expressions, &custom.state); @@ -423,16 +420,16 @@ impl Engine { } Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT), - Expr::Stmt(x) => self.eval_stmt_block(global, caches, lib, scope, this_ptr, x, true), + Expr::Stmt(x) => self.eval_stmt_block(global, caches, scope, this_ptr, x, true), #[cfg(not(feature = "no_index"))] Expr::Index(..) => { - self.eval_dot_index_chain(global, caches, lib, scope, this_ptr, expr, &mut None) + self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, &mut None) } #[cfg(not(feature = "no_object"))] Expr::Dot(..) => { - self.eval_dot_index_chain(global, caches, lib, scope, this_ptr, expr, &mut None) + self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, &mut None) } _ => unreachable!("expression cannot be evaluated: {:?}", expr), diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 94dcb837..703ae510 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -1,6 +1,6 @@ //! Global runtime state. -use crate::{Dynamic, Engine, ImmutableString}; +use crate::{Dynamic, Engine, ImmutableString, SharedModule, StaticVec}; use std::fmt; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -25,10 +25,13 @@ pub type GlobalConstants = pub struct GlobalRuntimeState { /// Names of imported [modules][crate::Module]. #[cfg(not(feature = "no_module"))] - imports: crate::StaticVec, + imports: StaticVec, /// Stack of imported [modules][crate::Module]. #[cfg(not(feature = "no_module"))] - modules: crate::StaticVec, + modules: StaticVec, + /// The current stack of loaded [modules][crate::Module] containing script-defined functions. + #[cfg(not(feature = "no_function"))] + pub lib: StaticVec, /// Source of the current context. /// /// No source if the string is empty. @@ -81,9 +84,11 @@ impl GlobalRuntimeState { pub fn new(engine: &Engine) -> Self { Self { #[cfg(not(feature = "no_module"))] - imports: crate::StaticVec::new_const(), + imports: StaticVec::new_const(), #[cfg(not(feature = "no_module"))] - modules: crate::StaticVec::new_const(), + modules: StaticVec::new_const(), + #[cfg(not(feature = "no_function"))] + lib: StaticVec::new_const(), source: None, num_operations: 0, #[cfg(not(feature = "no_module"))] @@ -130,7 +135,7 @@ impl GlobalRuntimeState { #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] - pub fn get_shared_import(&self, index: usize) -> Option { + pub fn get_shared_import(&self, index: usize) -> Option { self.modules.get(index).cloned() } /// Get a mutable reference to the globally-imported [module][crate::Module] at a @@ -141,10 +146,7 @@ impl GlobalRuntimeState { #[allow(dead_code)] #[inline(always)] #[must_use] - pub(crate) fn get_shared_import_mut( - &mut self, - index: usize, - ) -> Option<&mut crate::SharedModule> { + pub(crate) fn get_shared_import_mut(&mut self, index: usize) -> Option<&mut SharedModule> { self.modules.get_mut(index) } /// Get the index of a globally-imported [module][crate::Module] by name. @@ -168,7 +170,7 @@ impl GlobalRuntimeState { pub fn push_import( &mut self, name: impl Into, - module: impl Into, + module: impl Into, ) { self.imports.push(name.into()); self.modules.push(module.into()); @@ -201,7 +203,7 @@ impl GlobalRuntimeState { #[inline] pub(crate) fn iter_imports_raw( &self, - ) -> impl Iterator { + ) -> impl Iterator { self.imports.iter().zip(self.modules.iter()).rev() } /// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order. @@ -209,9 +211,7 @@ impl GlobalRuntimeState { /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] #[inline] - pub fn scan_imports_raw( - &self, - ) -> impl Iterator { + pub fn scan_imports_raw(&self) -> impl Iterator { self.imports.iter().zip(self.modules.iter()) } /// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of @@ -318,37 +318,7 @@ impl GlobalRuntimeState { } #[cfg(not(feature = "no_module"))] -impl IntoIterator for GlobalRuntimeState { - type Item = (ImmutableString, crate::SharedModule); - type IntoIter = std::iter::Rev< - std::iter::Zip< - smallvec::IntoIter<[ImmutableString; crate::STATIC_VEC_INLINE_SIZE]>, - smallvec::IntoIter<[crate::SharedModule; crate::STATIC_VEC_INLINE_SIZE]>, - >, - >; - - fn into_iter(self) -> Self::IntoIter { - self.imports.into_iter().zip(self.modules.into_iter()).rev() - } -} - -#[cfg(not(feature = "no_module"))] -impl<'a> IntoIterator for &'a GlobalRuntimeState { - type Item = (&'a ImmutableString, &'a crate::SharedModule); - type IntoIter = std::iter::Rev< - std::iter::Zip< - std::slice::Iter<'a, ImmutableString>, - std::slice::Iter<'a, crate::SharedModule>, - >, - >; - - fn into_iter(self) -> Self::IntoIter { - self.imports.iter().zip(self.modules.iter()).rev() - } -} - -#[cfg(not(feature = "no_module"))] -impl, M: Into> Extend<(K, M)> for GlobalRuntimeState { +impl, M: Into> Extend<(K, M)> for GlobalRuntimeState { #[inline] fn extend>(&mut self, iter: T) { for (k, m) in iter { @@ -365,22 +335,31 @@ impl fmt::Debug for GlobalRuntimeState { let mut f = f.debug_struct("GlobalRuntimeState"); #[cfg(not(feature = "no_module"))] - f.field("imports", &self.scan_imports_raw().collect::>()); + f.field("imports", &self.scan_imports_raw().collect::>()) + .field("num_modules_loaded", &self.num_modules_loaded) + .field("embedded_module_resolver", &self.embedded_module_resolver); + + #[cfg(not(feature = "no_function"))] + f.field("lib", &self.lib); f.field("source", &self.source) - .field("num_operations", &self.num_operations); + .field("num_operations", &self.num_operations) + .field("level", &self.level) + .field("scope_level", &self.scope_level) + .field("always_search_scope", &self.always_search_scope); #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] f.field("fn_hash_indexing", &self.fn_hash_indexing); - #[cfg(not(feature = "no_module"))] - f.field("num_modules_loaded", &self.num_modules_loaded) - .field("embedded_module_resolver", &self.embedded_module_resolver); - #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] f.field("constants", &self.constants); + f.field("tag", &self.tag); + + #[cfg(feature = "debugging")] + f.field("debugger", &self.debugger); + f.finish() } } diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 9a086ed9..ae5a7890 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -6,9 +6,11 @@ use crate::ast::{ ASTFlags, BinaryExpr, Expr, Ident, OpAssignment, Stmt, SwitchCasesCollection, TryCatchBlock, }; use crate::func::{get_builtin_op_assignment_fn, get_hasher}; -use crate::types::dynamic::{AccessMode, Union}; +use crate::types::dynamic::AccessMode; use crate::types::RestoreOnDrop; -use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR, INT}; +use crate::{ + Dynamic, Engine, ImmutableString, Position, RhaiResult, RhaiResultOf, Scope, ERR, INT, +}; use std::hash::{Hash, Hasher}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -26,7 +28,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, this_ptr: &mut Dynamic, statements: &[Stmt], @@ -73,15 +74,8 @@ impl Engine { #[cfg(not(feature = "no_module"))] let imports_len = global.num_imports(); - let result = self.eval_stmt( - global, - caches, - lib, - scope, - this_ptr, - stmt, - restore_orig_state, - )?; + let result = + self.eval_stmt(global, caches, scope, this_ptr, stmt, restore_orig_state)?; #[cfg(not(feature = "no_module"))] if matches!(stmt, Stmt::Import(..)) { @@ -117,7 +111,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], op_info: &OpAssignment, target: &mut Target, root: (&str, Position), @@ -148,10 +141,11 @@ impl Engine { // Built-in found let op = op_assign_token.literal_syntax(); + let orig_level = global.level; global.level += 1; - let global = &*RestoreOnDrop::lock(global, move |g| g.level -= 1); + let global = &*RestoreOnDrop::lock(global, move |g| g.level = orig_level); - let context = (self, op, None, global, lib, *op_pos).into(); + let context = (self, op, None, global, *op_pos).into(); return func(context, args).map(|_| ()); } } @@ -160,9 +154,9 @@ impl Engine { let op = op_token.literal_syntax(); let token = Some(op_assign_token); - match self.exec_native_fn_call( - global, caches, lib, op_assign, token, hash, args, true, *op_pos, - ) { + match self + .exec_native_fn_call(global, caches, op_assign, token, hash, args, true, *op_pos) + { Ok(_) => (), Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) => { @@ -171,11 +165,10 @@ impl Engine { *args[0] = self .exec_native_fn_call( - global, caches, lib, op, token, *hash_op, args, true, *op_pos, + global, caches, op, token, *hash_op, args, true, *op_pos, ) .map_err(|err| err.fill_position(op_info.pos))? - .0 - .flatten(); + .0; } Err(err) => return Err(err), } @@ -183,6 +176,13 @@ impl Engine { self.check_data_size(args[0], root.1)?; } else { // Normal assignment + + // If value is a string, intern it + if new_val.is_string() { + let value = new_val.into_immutable_string().expect("`ImmutableString`"); + new_val = self.get_interned_string(value).into(); + } + *target.write_lock::().unwrap() = new_val; } @@ -201,14 +201,13 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, this_ptr: &mut Dynamic, stmt: &Stmt, rewind_scope: bool, ) -> RhaiResult { #[cfg(feature = "debugging")] - let reset = self.run_debugger_with_reset(global, caches, lib, scope, this_ptr, stmt)?; + let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, stmt)?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset)); @@ -219,7 +218,7 @@ impl Engine { if let Stmt::FnCall(x, pos) = stmt { self.track_operation(global, stmt.position())?; - return self.eval_fn_call_expr(global, caches, lib, scope, this_ptr, x, *pos); + return self.eval_fn_call_expr(global, caches, scope, this_ptr, x, *pos); } // Then assignments. @@ -232,11 +231,11 @@ impl Engine { if let Expr::Variable(x, ..) = lhs { let rhs_val = self - .eval_expr(global, caches, lib, scope, this_ptr, rhs)? + .eval_expr(global, caches, scope, this_ptr, rhs)? .flatten(); let (mut lhs_ptr, pos) = - self.search_namespace(global, caches, lib, scope, this_ptr, lhs)?; + self.search_namespace(global, caches, scope, this_ptr, lhs)?; let var_name = x.3.as_str(); @@ -257,49 +256,45 @@ impl Engine { let root = (var_name, pos); let lhs_ptr = &mut lhs_ptr; - return self - .eval_op_assignment(global, caches, lib, op_info, lhs_ptr, root, rhs_val) - .map(|_| Dynamic::UNIT); + self.eval_op_assignment(global, caches, op_info, lhs_ptr, root, rhs_val)?; + + return Ok(Dynamic::UNIT); } #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] { - let rhs_val = self.eval_expr(global, caches, lib, scope, this_ptr, rhs)?; + let mut rhs_val = self + .eval_expr(global, caches, scope, this_ptr, rhs)? + .flatten(); - // Check if the result is a string. If so, intern it. - #[cfg(not(feature = "no_closure"))] - let is_string = !rhs_val.is_shared() && rhs_val.is::(); - #[cfg(feature = "no_closure")] - let is_string = rhs_val.is::(); - - let rhs_val = if is_string { - self.get_interned_string( - rhs_val.into_immutable_string().expect("`ImmutableString`"), - ) - .into() - } else { - rhs_val.flatten() - }; + // If value is a string, intern it + if rhs_val.is_string() { + let value = rhs_val.into_immutable_string().expect("`ImmutableString`"); + rhs_val = self.get_interned_string(value).into(); + } let _new_val = &mut Some((rhs_val, op_info)); // Must be either `var[index] op= val` or `var.prop op= val` - return match lhs { + match lhs { // name op= rhs (handled above) Expr::Variable(..) => { unreachable!("Expr::Variable case is already handled") } // idx_lhs[idx_expr] op= rhs #[cfg(not(feature = "no_index"))] - Expr::Index(..) => self - .eval_dot_index_chain(global, caches, lib, scope, this_ptr, lhs, _new_val), + Expr::Index(..) => { + self.eval_dot_index_chain(global, caches, scope, this_ptr, lhs, _new_val) + } // dot_lhs.dot_rhs op= rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(..) => self - .eval_dot_index_chain(global, caches, lib, scope, this_ptr, lhs, _new_val), + Expr::Dot(..) => { + self.eval_dot_index_chain(global, caches, scope, this_ptr, lhs, _new_val) + } _ => unreachable!("cannot assign to expression: {:?}", lhs), - } - .map(|_| Dynamic::UNIT); + }?; + + return Ok(Dynamic::UNIT); } } @@ -311,13 +306,13 @@ impl Engine { // Expression as statement Stmt::Expr(expr) => self - .eval_expr(global, caches, lib, scope, this_ptr, expr) + .eval_expr(global, caches, scope, this_ptr, expr) .map(Dynamic::flatten), // Block scope Stmt::Block(statements, ..) if statements.is_empty() => Ok(Dynamic::UNIT), Stmt::Block(statements, ..) => { - self.eval_stmt_block(global, caches, lib, scope, this_ptr, statements, true) + self.eval_stmt_block(global, caches, scope, this_ptr, statements, true) } // If statement @@ -325,14 +320,14 @@ impl Engine { let (expr, if_block, else_block) = &**x; let guard_val = self - .eval_expr(global, caches, lib, scope, this_ptr, expr)? + .eval_expr(global, caches, scope, this_ptr, expr)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; if guard_val && !if_block.is_empty() { - self.eval_stmt_block(global, caches, lib, scope, this_ptr, if_block, true) + self.eval_stmt_block(global, caches, scope, this_ptr, if_block, true) } else if !guard_val && !else_block.is_empty() { - self.eval_stmt_block(global, caches, lib, scope, this_ptr, else_block, true) + self.eval_stmt_block(global, caches, scope, this_ptr, else_block, true) } else { Ok(Dynamic::UNIT) } @@ -352,7 +347,7 @@ impl Engine { let mut result = None; - let value = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?; + let value = self.eval_expr(global, caches, scope, this_ptr, expr)?; if value.is_hashable() { let hasher = &mut get_hasher(); @@ -369,7 +364,7 @@ impl Engine { let cond_result = match block.condition { Expr::BoolConstant(b, ..) => b, ref c => self - .eval_expr(global, caches, lib, scope, this_ptr, c)? + .eval_expr(global, caches, scope, this_ptr, c)? .as_bool() .map_err(|typ| { self.make_type_mismatch_err::(typ, c.position()) @@ -381,7 +376,7 @@ impl Engine { break; } } - } else if value.is::() && !ranges.is_empty() { + } else if value.is_int() && !ranges.is_empty() { // Then check integer ranges let value = value.as_int().expect("`INT`"); @@ -391,7 +386,7 @@ impl Engine { let cond_result = match block.condition { Expr::BoolConstant(b, ..) => b, ref c => self - .eval_expr(global, caches, lib, scope, this_ptr, c)? + .eval_expr(global, caches, scope, this_ptr, c)? .as_bool() .map_err(|typ| { self.make_type_mismatch_err::(typ, c.position()) @@ -409,7 +404,7 @@ impl Engine { result .or_else(|| def_case.as_ref().map(|&index| &expressions[index].expr)) .map_or(Ok(Dynamic::UNIT), |expr| { - self.eval_expr(global, caches, lib, scope, this_ptr, expr) + self.eval_expr(global, caches, scope, this_ptr, expr) }) } @@ -425,7 +420,7 @@ impl Engine { loop { if let Err(err) = - self.eval_stmt_block(global, caches, lib, scope, this_ptr, body, true) + self.eval_stmt_block(global, caches, scope, this_ptr, body, true) { match *err { ERR::LoopBreak(false, ..) => (), @@ -442,7 +437,7 @@ impl Engine { loop { let condition = self - .eval_expr(global, caches, lib, scope, this_ptr, expr)? + .eval_expr(global, caches, scope, this_ptr, expr)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; @@ -455,7 +450,7 @@ impl Engine { } if let Err(err) = - self.eval_stmt_block(global, caches, lib, scope, this_ptr, body, true) + self.eval_stmt_block(global, caches, scope, this_ptr, body, true) { match *err { ERR::LoopBreak(false, ..) => (), @@ -474,7 +469,7 @@ impl Engine { loop { if !body.is_empty() { if let Err(err) = - self.eval_stmt_block(global, caches, lib, scope, this_ptr, body, true) + self.eval_stmt_block(global, caches, scope, this_ptr, body, true) { match *err { ERR::LoopBreak(false, ..) => continue, @@ -485,7 +480,7 @@ impl Engine { } let condition = self - .eval_expr(global, caches, lib, scope, this_ptr, expr)? + .eval_expr(global, caches, scope, this_ptr, expr)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; @@ -500,7 +495,7 @@ impl Engine { let (var_name, counter, expr, statements) = &**x; let iter_obj = self - .eval_expr(global, caches, lib, scope, this_ptr, expr)? + .eval_expr(global, caches, scope, this_ptr, expr)? .flatten(); let iter_type = iter_obj.type_id(); @@ -579,9 +574,7 @@ impl Engine { continue; } - match self - .eval_stmt_block(global, caches, lib, scope, this_ptr, statements, true) - { + match self.eval_stmt_block(global, caches, scope, this_ptr, statements, true) { Ok(_) => (), Err(err) => match *err { ERR::LoopBreak(false, ..) => (), @@ -602,7 +595,7 @@ impl Engine { let is_break = options.contains(ASTFlags::BREAK); let value = if let Some(ref expr) = expr { - self.eval_expr(global, caches, lib, scope, this_ptr, expr)? + self.eval_expr(global, caches, scope, this_ptr, expr)? } else { Dynamic::UNIT }; @@ -621,7 +614,7 @@ impl Engine { catch_block, } = &**x; - match self.eval_stmt_block(global, caches, lib, scope, this_ptr, try_block, true) { + match self.eval_stmt_block(global, caches, scope, this_ptr, try_block, true) { r @ Ok(_) => r, Err(err) if err.is_pseudo_error() => Err(err), Err(err) if !err.is_catchable() => Err(err), @@ -672,31 +665,23 @@ impl Engine { scope.push(catch_var.clone(), err_value); } - self.eval_stmt_block( - global, - caches, - lib, - scope, - this_ptr, - catch_block, - true, - ) - .map(|_| Dynamic::UNIT) - .map_err(|result_err| match *result_err { - // Re-throw exception - ERR::ErrorRuntime(Dynamic(Union::Unit(..)), pos) => { - err.set_position(pos); - err - } - _ => result_err, - }) + self.eval_stmt_block(global, caches, scope, this_ptr, catch_block, true) + .map(|_| Dynamic::UNIT) + .map_err(|result_err| match *result_err { + // Re-throw exception + ERR::ErrorRuntime(v, pos) if v.is_unit() => { + err.set_position(pos); + err + } + _ => result_err, + }) } } } // Throw value Stmt::Return(Some(expr), options, pos) if options.contains(ASTFlags::BREAK) => self - .eval_expr(global, caches, lib, scope, this_ptr, expr) + .eval_expr(global, caches, scope, this_ptr, expr) .and_then(|v| Err(ERR::ErrorRuntime(v.flatten(), *pos).into())), // Empty throw @@ -706,7 +691,7 @@ impl Engine { // Return value Stmt::Return(Some(expr), .., pos) => self - .eval_expr(global, caches, lib, scope, this_ptr, expr) + .eval_expr(global, caches, scope, this_ptr, expr) .and_then(|v| Err(ERR::Return(v.flatten(), *pos).into())), // Empty return @@ -737,7 +722,7 @@ impl Engine { nesting_level: global.scope_level, will_shadow, }; - let context = EvalContext::new(self, global, caches, lib, scope, this_ptr); + let context = EvalContext::new(self, global, caches, scope, this_ptr); if !filter(true, info, context)? { return Err(ERR::ErrorForbiddenVariable(var_name.to_string(), *pos).into()); @@ -746,7 +731,7 @@ impl Engine { // Evaluate initial value let mut value = self - .eval_expr(global, caches, lib, scope, this_ptr, expr)? + .eval_expr(global, caches, scope, this_ptr, expr)? .flatten(); let _alias = if !rewind_scope { @@ -755,7 +740,7 @@ impl Engine { #[cfg(not(feature = "no_module"))] if global.scope_level == 0 && access == AccessMode::ReadOnly - && lib.iter().any(|m| !m.is_empty()) + && global.lib.iter().any(|m| !m.is_empty()) { crate::func::locked_write(global.constants.get_or_insert_with(|| { crate::Shared::new( @@ -801,10 +786,10 @@ impl Engine { return Err(ERR::ErrorTooManyModules(*_pos).into()); } - let v = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?; + let v = self.eval_expr(global, caches, scope, this_ptr, expr)?; let typ = v.type_name(); - let path = v.try_cast::().ok_or_else(|| { - self.make_type_mismatch_err::(typ, expr.position()) + let path = v.try_cast::().ok_or_else(|| { + self.make_type_mismatch_err::(typ, expr.position()) })?; use crate::ModuleResolver; @@ -832,7 +817,7 @@ impl Engine { let (export, must_be_indexed) = if !export.is_empty() { (export.name.clone(), true) } else { - (self.get_interned_string(""), false) + (self.const_empty_string(), false) }; if !must_be_indexed || module.is_indexed() { @@ -897,13 +882,12 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, statements: &[Stmt], ) -> RhaiResult { let mut this = Dynamic::NULL; - self.eval_stmt_block(global, caches, lib, scope, &mut this, statements, false) + self.eval_stmt_block(global, caches, scope, &mut this, statements, false) .or_else(|err| match *err { ERR::Return(out, ..) => Ok(out), ERR::LoopBreak(..) => { diff --git a/src/eval/target.rs b/src/eval/target.rs index c48aa62a..64bfe3b3 100644 --- a/src/eval/target.rs +++ b/src/eval/target.rs @@ -9,11 +9,11 @@ use std::{ ops::{Deref, DerefMut}, }; -// Calculate an offset+len pair given an actual length of the underlying array. -// -// Negative starting positions count from the end. -// -// Values going over bounds are limited to the actual length. +/// Calculate an offset+len pair given an actual length of the underlying array. +/// +/// Negative starting positions count from the end. +/// +/// Values going over bounds are limited to the actual length. #[inline] #[allow(dead_code)] pub fn calc_offset_len(length: usize, start: crate::INT, len: crate::INT) -> (usize, usize) { @@ -41,11 +41,11 @@ pub fn calc_offset_len(length: usize, start: crate::INT, len: crate::INT) -> (us (start, len) } -// Calculate an offset+len pair given an actual length of the underlying array. -// -// Negative starting positions count from the end. -// -// Values going over bounds call the provided closure to return a default value or an error. +/// Calculate an offset+len pair given an actual length of the underlying array. +/// +/// Negative starting positions count from the end. +/// +/// Values going over bounds call the provided closure to return a default value or an error. #[inline] #[allow(dead_code)] pub fn calc_index( diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 8ff37e67..b6461191 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -690,7 +690,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt PlusAssign => Some(|_ctx, args| { let (first, second) = args.split_first_mut().expect(BUILTIN); let x = &mut *first.write_lock::().expect(BUILTIN); - let y = std::mem::take(second[0]).cast::(); + let y = &*second[0].read_lock::().expect(BUILTIN); #[cfg(not(feature = "unchecked"))] if !x.is_empty() && !y.is_empty() { @@ -704,7 +704,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt MinusAssign => Some(|_, args| { let (first, second) = args.split_first_mut().expect(BUILTIN); let x = &mut *first.write_lock::().expect(BUILTIN); - let y = std::mem::take(second[0]).cast::(); + let y = &*second[0].read_lock::().expect(BUILTIN); Ok((*x -= y).into()) }), _ => None, @@ -718,7 +718,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt return match op { PlusAssign => Some(|_ctx, args| { - let x = std::mem::take(args[1]).cast::(); + let x = std::mem::take(args[1]).into_array().expect(BUILTIN); if x.is_empty() { return Ok(Dynamic::UNIT); @@ -749,7 +749,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt return match op { PlusAssign => Some(|_ctx, args| { - let blob2 = std::mem::take(args[1]).cast::(); + let blob2 = std::mem::take(args[1]).into_blob().expect(BUILTIN); let blob1 = &mut *args[0].write_lock::().expect(BUILTIN); #[cfg(not(feature = "unchecked"))] @@ -951,14 +951,14 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt return match op { PlusAssign => Some(|_ctx, args| { - let s = std::mem::take(args[1]).cast::(); + let (first, second) = args.split_first_mut().expect(BUILTIN); + let blob = &mut *first.write_lock::().expect(BUILTIN); + let s = &*second[0].read_lock::().expect(BUILTIN); if s.is_empty() { return Ok(Dynamic::UNIT); } - let blob = &mut *args[0].write_lock::().expect(BUILTIN); - #[cfg(not(feature = "unchecked"))] _ctx.engine().raise_err_if_over_data_size_limit(( blob.len() + s.len(), @@ -966,7 +966,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt 0, ))?; - Ok(append_str(blob, &s).into()) + Ok(append_str(blob, s).into()) }), _ => None, }; diff --git a/src/func/call.rs b/src/func/call.rs index 38020404..4dcc7ced 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -12,7 +12,7 @@ use crate::tokenizer::{is_valid_function_name, Token}; use crate::types::RestoreOnDrop; use crate::{ calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, - OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR, + OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, ERR, }; #[cfg(feature = "no_std")] use hashbrown::hash_map::Entry; @@ -145,7 +145,7 @@ impl Engine { format!( "{fn_name} ({})", args.iter() - .map(|a| if a.is::() { + .map(|a| if a.is_string() { "&str | ImmutableString | String" } else { self.map_type_name(a.type_name()) @@ -166,10 +166,9 @@ impl Engine { #[must_use] fn resolve_fn<'s>( &self, - _global: &GlobalRuntimeState, + global: &GlobalRuntimeState, caches: &'s mut Caches, local_entry: &'s mut Option, - lib: &[SharedModule], op_token: Option<&Token>, hash_base: u64, args: Option<&mut FnCallArgs>, @@ -194,17 +193,28 @@ impl Engine { let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic` loop { - let func = lib + #[cfg(not(feature = "no_function"))] + let func = global + .lib .iter() + .rev() .chain(self.global_modules.iter()) .find_map(|m| m.get_fn(hash).map(|f| (f, m.id_raw()))); + #[cfg(feature = "no_function")] + let func = None; + + let func = func.or_else(|| { + self.global_modules + .iter() + .find_map(|m| m.get_fn(hash).map(|f| (f, m.id_raw()))) + }); #[cfg(not(feature = "no_module"))] let func = if args.is_none() { // Scripted functions are not exposed globally func } else { - func.or_else(|| _global.get_qualified_fn(hash)).or_else(|| { + func.or_else(|| global.get_qualified_fn(hash)).or_else(|| { self.global_sub_modules .values() .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))) @@ -229,15 +239,21 @@ impl Engine { // Check `Dynamic` parameters for functions with parameters if allow_dynamic && max_bitmask == 0 && num_args > 0 { - let is_dynamic = lib.iter().any(|m| m.may_contain_dynamic_fn(hash_base)) - || self - .global_modules + let is_dynamic = self + .global_modules + .iter() + .any(|m| m.may_contain_dynamic_fn(hash_base)); + + #[cfg(not(feature = "no_function"))] + let is_dynamic = is_dynamic + || global + .lib .iter() .any(|m| m.may_contain_dynamic_fn(hash_base)); #[cfg(not(feature = "no_module"))] let is_dynamic = is_dynamic - || _global.may_contain_dynamic_fn(hash_base) + || global.may_contain_dynamic_fn(hash_base) || self .global_sub_modules .values() @@ -325,7 +341,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], name: &str, op_token: Option<&Token>, hash: u64, @@ -342,7 +357,6 @@ impl Engine { global, caches, local_entry, - lib, op_token, hash, Some(args), @@ -384,7 +398,7 @@ impl Engine { // Run external function let src = source.as_ref().map(|s| s.as_str()); - let context = (self, name, src, &*global, lib, pos).into(); + let context = (self, name, src, &*global, pos).into(); let mut _result = if func.is_plugin_fn() { let f = func.get_plugin_fn().unwrap(); @@ -415,7 +429,7 @@ impl Engine { }; if let Err(err) = - self.run_debugger_raw(global, caches, lib, scope, &mut this, node, event) + self.run_debugger_raw(global, caches, scope, &mut this, node, event) { _result = Err(err); } @@ -537,12 +551,11 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], _scope: Option<&mut Scope>, fn_name: &str, op_token: Option<&Token>, hashes: FnCallHashes, - mut args: &mut FnCallArgs, + mut _args: &mut FnCallArgs, is_ref_mut: bool, _is_method_call: bool, pos: Position, @@ -557,27 +570,28 @@ impl Engine { // Check for data race. #[cfg(not(feature = "no_closure"))] - ensure_no_data_race(fn_name, args, is_ref_mut)?; + ensure_no_data_race(fn_name, _args, is_ref_mut)?; + let orig_level = global.level; global.level += 1; - let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level); // These may be redirected from method style calls. if hashes.is_native_only() { match fn_name { // Handle type_of() - KEYWORD_TYPE_OF if args.len() == 1 => { - let typ = self.map_type_name(args[0].type_name()).to_string().into(); + KEYWORD_TYPE_OF if _args.len() == 1 => { + let typ = self.map_type_name(_args[0].type_name()).to_string().into(); return Ok((typ, false)); } // Handle is_def_fn() #[cfg(not(feature = "no_function"))] crate::engine::KEYWORD_IS_DEF_FN - if args.len() == 2 && args[0].is::() && args[1].is::() => + if _args.len() == 2 && _args[0].is_fnptr() && _args[1].is_int() => { - let fn_name = args[0].read_lock::().expect("`FnPtr`"); - let num_params = args[1].as_int().expect("`INT`"); + let fn_name = _args[0].read_lock::().expect("`FnPtr`"); + let num_params = _args[1].as_int().expect("`INT`"); return Ok(( if num_params < 0 || num_params > crate::MAX_USIZE_INT { @@ -585,7 +599,7 @@ impl Engine { } else { let hash_script = calc_fn_hash(None, fn_name.as_str(), num_params as usize); - self.has_script_fn(global, caches, lib, hash_script) + self.has_script_fn(global, caches, hash_script) } .into(), false, @@ -594,15 +608,15 @@ impl Engine { // Handle is_shared() #[cfg(not(feature = "no_closure"))] - crate::engine::KEYWORD_IS_SHARED if args.len() == 1 => { + crate::engine::KEYWORD_IS_SHARED if _args.len() == 1 => { return no_method_err(fn_name, pos) } - KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR if args.len() == 1 => { + KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR if _args.len() == 1 => { return no_method_err(fn_name, pos) } - KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY if !args.is_empty() => { + KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY if !_args.is_empty() => { return no_method_err(fn_name, pos) } @@ -617,7 +631,7 @@ impl Engine { let local_entry = &mut None; if let Some(FnResolutionCacheEntry { func, ref source }) = self - .resolve_fn(global, caches, local_entry, lib, None, hash, None, false) + .resolve_fn(global, caches, local_entry, None, hash, None, false) .cloned() { // Script function call @@ -643,31 +657,29 @@ impl Engine { return if _is_method_call { // Method call of script function - map first argument to `this` - let (first_arg, rest_args) = args.split_first_mut().unwrap(); + let (first_arg, rest_args) = _args.split_first_mut().unwrap(); self.call_script_fn( - global, caches, lib, scope, first_arg, func, rest_args, true, pos, + global, caches, scope, first_arg, func, rest_args, true, pos, ) } else { // Normal call of script function let backup = &mut ArgBackup::new(); // The first argument is a reference? - let swap = is_ref_mut && !args.is_empty(); + let swap = is_ref_mut && !_args.is_empty(); if swap { - backup.change_first_arg_to_copy(args); + backup.change_first_arg_to_copy(_args); } - let args = &mut *RestoreOnDrop::lock_if(swap, &mut args, move |a| { + let args = &mut *RestoreOnDrop::lock_if(swap, &mut _args, move |a| { backup.restore_first_arg(a) }); let mut this = Dynamic::NULL; - self.call_script_fn( - global, caches, lib, scope, &mut this, func, args, true, pos, - ) + self.call_script_fn(global, caches, scope, &mut this, func, args, true, pos) } .map(|r| (r, false)); } @@ -677,7 +689,7 @@ impl Engine { let hash = hashes.native(); self.exec_native_fn_call( - global, caches, lib, fn_name, op_token, hash, args, is_ref_mut, pos, + global, caches, fn_name, op_token, hash, _args, is_ref_mut, pos, ) } @@ -687,7 +699,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, this_ptr: &mut Dynamic, arg_expr: &Expr, @@ -697,7 +708,7 @@ impl Engine { self.track_operation(global, arg_expr.start_position())?; #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, scope, this_ptr, arg_expr)?; + self.run_debugger(global, caches, scope, this_ptr, arg_expr)?; return Ok((value, arg_expr.start_position())); } @@ -710,7 +721,7 @@ impl Engine { #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset)); - self.eval_expr(global, caches, lib, scope, this_ptr, arg_expr) + self.eval_expr(global, caches, scope, this_ptr, arg_expr) .map(|r| (r, arg_expr.start_position())) } @@ -720,7 +731,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], fn_name: &str, mut hash: FnCallHashes, target: &mut crate::eval::Target, @@ -731,7 +741,7 @@ impl Engine { let is_ref_mut = target.is_ref(); let (result, updated) = match fn_name { - KEYWORD_FN_PTR_CALL if target.is::() => { + KEYWORD_FN_PTR_CALL if target.is_fnptr() => { // FnPtr call let fn_ptr = target.read_lock::().expect("`FnPtr`"); @@ -760,7 +770,6 @@ impl Engine { self.exec_fn_call( global, caches, - lib, None, fn_name, None, @@ -775,7 +784,7 @@ impl Engine { if call_args.is_empty() { let typ = self.map_type_name(target.type_name()); return Err(self.make_type_mismatch_err::(typ, fn_call_pos)); - } else if !call_args[0].is::() { + } else if !call_args[0].is_fnptr() { let typ = self.map_type_name(call_args[0].type_name()); return Err(self.make_type_mismatch_err::(typ, first_arg_pos)); } @@ -815,7 +824,6 @@ impl Engine { self.exec_fn_call( global, caches, - lib, None, &fn_name, None, @@ -827,7 +835,7 @@ impl Engine { ) } KEYWORD_FN_PTR_CURRY => { - if !target.is::() { + if !target.is_fnptr() { let typ = self.map_type_name(target.type_name()); return Err(self.make_type_mismatch_err::(typ, fn_call_pos)); } @@ -915,7 +923,6 @@ impl Engine { self.exec_fn_call( global, caches, - lib, None, fn_name, None, @@ -941,7 +948,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, this_ptr: &mut Dynamic, fn_name: &str, @@ -967,9 +973,9 @@ impl Engine { KEYWORD_FN_PTR_CALL if total_args >= 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, scope, this_ptr, arg)?; - if !arg_value.is::() { + if !arg_value.is_fnptr() { let typ = self.map_type_name(arg_value.type_name()); return Err(self.make_type_mismatch_err::(typ, arg_pos)); } @@ -1008,7 +1014,7 @@ impl Engine { KEYWORD_FN_PTR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, scope, this_ptr, arg)?; // Fn - only in function call style return arg_value @@ -1023,9 +1029,9 @@ impl Engine { KEYWORD_FN_PTR_CURRY if total_args > 1 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, scope, this_ptr, first)?; + self.get_arg_value(global, caches, scope, this_ptr, first)?; - if !arg_value.is::() { + if !arg_value.is_fnptr() { let typ = self.map_type_name(arg_value.type_name()); return Err(self.make_type_mismatch_err::(typ, arg_pos)); } @@ -1034,8 +1040,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(global, caches, lib, scope, this_ptr, expr)?; + let (value, ..) = self.get_arg_value(global, caches, scope, this_ptr, expr)?; curried.push(value); Ok::<_, RhaiError>(curried) })?; @@ -1047,8 +1052,7 @@ impl Engine { #[cfg(not(feature = "no_closure"))] crate::engine::KEYWORD_IS_SHARED if total_args == 1 => { let arg = first_arg.unwrap(); - let (arg_value, ..) = - self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; + let (arg_value, ..) = self.get_arg_value(global, caches, scope, this_ptr, arg)?; return Ok(arg_value.is_shared().into()); } @@ -1057,14 +1061,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(global, caches, lib, scope, this_ptr, first)?; + self.get_arg_value(global, caches, scope, this_ptr, first)?; 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(global, caches, lib, scope, this_ptr, &a_expr[0])?; + self.get_arg_value(global, caches, scope, this_ptr, &a_expr[0])?; let num_params = arg_value .as_int() @@ -1074,7 +1078,7 @@ impl Engine { false } else { let hash_script = calc_fn_hash(None, &fn_name, num_params as usize); - self.has_script_fn(global, caches, lib, hash_script) + self.has_script_fn(global, caches, hash_script) } .into()); } @@ -1083,7 +1087,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(global, caches, lib, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, scope, this_ptr, arg)?; let var_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; @@ -1097,16 +1101,15 @@ impl Engine { #[cfg(not(feature = "no_module"))] let orig_imports_len = global.num_imports(); let arg = first_arg.unwrap(); - let (arg_value, pos) = - self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; + let (arg_value, pos) = self.get_arg_value(global, caches, scope, this_ptr, arg)?; let s = &arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; + let orig_level = global.level; global.level += 1; - let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); - let result = self.eval_script_expr_in_place(global, caches, lib, scope, s, pos); + let result = self.eval_script_expr_in_place(global, caches, scope, s, pos); // IMPORTANT! If the eval defines new variables in the current scope, // all variable offsets from this point on will be mis-aligned. @@ -1118,6 +1121,7 @@ impl Engine { if scope_changed { global.always_search_scope = true; } + global.level = orig_level; return result.map_err(|err| { ERR::ErrorInFunctionCall( @@ -1148,7 +1152,7 @@ impl Engine { .copied() .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value(global, caches, lib, scope, this_ptr, expr) + self.get_arg_value(global, caches, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1159,8 +1163,8 @@ impl Engine { return self .exec_fn_call( - global, caches, lib, scope, name, op_token, hashes, &mut args, is_ref_mut, - false, pos, + global, caches, scope, name, op_token, hashes, &mut args, is_ref_mut, false, + pos, ) .map(|(v, ..)| v); } @@ -1176,16 +1180,16 @@ impl Engine { let first_expr = first_arg.unwrap(); #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, scope, this_ptr, first_expr)?; + self.run_debugger(global, caches, scope, this_ptr, first_expr)?; // func(x, ...) -> x.func(...) a_expr.iter().try_for_each(|expr| { - self.get_arg_value(global, caches, lib, scope, this_ptr, expr) + self.get_arg_value(global, caches, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; let (mut target, _pos) = - self.search_namespace(global, caches, lib, scope, this_ptr, first_expr)?; + self.search_namespace(global, caches, scope, this_ptr, first_expr)?; if target.is_read_only() { target = target.into_owned(); @@ -1212,7 +1216,7 @@ impl Engine { .into_iter() .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value(global, caches, lib, scope, this_ptr, expr) + self.get_arg_value(global, caches, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1222,7 +1226,7 @@ impl Engine { } self.exec_fn_call( - global, caches, lib, None, name, op_token, hashes, &mut args, is_ref_mut, false, pos, + global, caches, None, name, op_token, hashes, &mut args, is_ref_mut, false, pos, ) .map(|(v, ..)| v) } @@ -1233,7 +1237,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, this_ptr: &mut Dynamic, namespace: &crate::ast::Namespace, @@ -1254,20 +1257,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(global, caches, lib, scope, this_ptr, &args_expr[0])?; + self.run_debugger(global, caches, scope, this_ptr, &args_expr[0])?; // func(x, ...) -> x.func(...) arg_values.push(Dynamic::UNIT); args_expr.iter().skip(1).try_for_each(|expr| { - self.get_arg_value(global, caches, lib, scope, this_ptr, expr) + self.get_arg_value(global, caches, scope, this_ptr, expr) .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(global, caches, lib, scope, this_ptr, first_arg)?; + self.search_scope_only(global, caches, scope, this_ptr, first_arg)?; self.track_operation(global, _pos)?; @@ -1290,7 +1293,7 @@ impl Engine { } else { // func(..., ...) or func(mod::x, ...) args_expr.iter().try_for_each(|expr| { - self.get_arg_value(global, caches, lib, scope, this_ptr, expr) + self.get_arg_value(global, caches, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(arg_values.iter_mut()); @@ -1357,8 +1360,9 @@ impl Engine { } } + let orig_level = global.level; global.level += 1; - let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level); match func { #[cfg(not(feature = "no_function"))] @@ -1371,12 +1375,12 @@ impl Engine { let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source); self.call_script_fn( - global, caches, lib, new_scope, &mut this, fn_def, &mut args, true, pos, + global, caches, new_scope, &mut this, fn_def, &mut args, true, pos, ) } Some(f) if f.is_plugin_fn() => { - let context = (self, fn_name, module.id(), &*global, lib, pos).into(); + let context = (self, fn_name, module.id(), &*global, pos).into(); let f = f.get_plugin_fn().expect("plugin function"); let result = if !f.is_pure() && !args.is_empty() && args[0].is_read_only() { Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into()) @@ -1388,7 +1392,7 @@ impl Engine { Some(f) if f.is_native() => { let func = f.get_native_fn().expect("native function"); - let context = (self, fn_name, module.id(), &*global, lib, pos).into(); + let context = (self, fn_name, module.id(), &*global, pos).into(); let result = func(context, &mut args); self.check_return_value(result, pos) } @@ -1416,7 +1420,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, script: &str, _pos: Position, @@ -1442,7 +1445,7 @@ impl Engine { // If new functions are defined within the eval string, it is an error #[cfg(not(feature = "no_function"))] - if !ast.shared_lib().is_empty() { + if ast.has_functions() { return Err(crate::PERR::WrongFnDefinition.into()); } @@ -1452,7 +1455,7 @@ impl Engine { } // Evaluate the AST - self.eval_global_statements(global, caches, lib, scope, statements) + self.eval_global_statements(global, caches, scope, statements) } /// Evaluate a function call expression. @@ -1460,7 +1463,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, this_ptr: &mut Dynamic, expr: &FnCallExpr, @@ -1482,12 +1484,12 @@ impl Engine { // Short-circuit native binary operator call if under Fast Operators mode if op_token.is_some() && self.fast_operators() && args.len() == 2 { let mut lhs = self - .get_arg_value(global, caches, lib, scope, this_ptr, &args[0])? + .get_arg_value(global, caches, scope, this_ptr, &args[0])? .0 .flatten(); let mut rhs = self - .get_arg_value(global, caches, lib, scope, this_ptr, &args[1])? + .get_arg_value(global, caches, scope, this_ptr, &args[1])? .0 .flatten(); @@ -1497,16 +1499,17 @@ impl Engine { get_builtin_binary_op_fn(op_token.as_ref().unwrap(), operands[0], operands[1]) { // Built-in found + let orig_level = global.level; global.level += 1; - let global = &*RestoreOnDrop::lock(global, move |g| g.level -= 1); + let global = &*RestoreOnDrop::lock(global, move |g| g.level = orig_level); - let context = (self, name.as_str(), None, global, lib, pos).into(); + let context = (self, name.as_str(), None, global, pos).into(); return func(context, operands); } return self .exec_fn_call( - global, caches, lib, None, name, op_token, *hashes, operands, false, false, pos, + global, caches, None, name, op_token, *hashes, operands, false, false, pos, ) .map(|(v, ..)| v); } @@ -1517,7 +1520,7 @@ impl Engine { let hash = hashes.native(); return self.make_qualified_function_call( - global, caches, lib, scope, this_ptr, namespace, name, args, hash, pos, + global, caches, scope, this_ptr, namespace, name, args, hash, pos, ); } @@ -1528,8 +1531,8 @@ impl Engine { ); self.make_function_call( - global, caches, lib, scope, this_ptr, name, op_token, first_arg, args, *hashes, - *capture, pos, + global, caches, scope, this_ptr, name, op_token, first_arg, args, *hashes, *capture, + pos, ) } } diff --git a/src/func/hashing.rs b/src/func/hashing.rs index 83b2fed1..0f5af48f 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -78,7 +78,7 @@ impl BuildHasher for StraightHasherBuilder { #[must_use] pub fn get_hasher() -> ahash::AHasher { match config::hashing::get_ahash_seed() { - Some([seed1, seed2, seed3, seed4]) if seed1 | seed2 | seed3 | seed4 != 0 => { + Some([seed1, seed2, seed3, seed4]) if (seed1 | seed2 | seed3 | seed4) != 0 => { ahash::RandomState::with_seeds(*seed1, *seed2, *seed3, *seed4).build_hasher() } _ => ahash::AHasher::default(), diff --git a/src/func/mod.rs b/src/func/mod.rs index d5659854..0b31f72b 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -23,6 +23,7 @@ pub use callable_function::CallableFunction; pub use func::Func; pub use hashing::{calc_fn_hash, calc_fn_hash_full, calc_var_hash, get_hasher, StraightHashMap}; #[cfg(feature = "internals")] +#[allow(deprecated)] pub use native::NativeCallContextStore; pub use native::{ locked_read, locked_write, shared_get_mut, shared_make_mut, shared_take, shared_take_or_clone, diff --git a/src/func/native.rs b/src/func/native.rs index de74f6c6..fd5f1836 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -73,14 +73,17 @@ pub struct NativeCallContext<'a> { source: Option<&'a str>, /// The current [`GlobalRuntimeState`], if any. global: &'a GlobalRuntimeState, - /// The current stack of loaded [modules][Module]. - lib: &'a [SharedModule], /// [Position] of the function call. pos: Position, } /// _(internals)_ Context of a native Rust function call. /// Exported under the `internals` feature only. +/// +/// # WARNING - Volatile Type +/// +/// This type is volatile and may change in the future. +#[deprecated = "This type is NOT deprecated, but it is considered volatile and may change in the future."] #[cfg(feature = "internals")] #[derive(Debug, Clone)] pub struct NativeCallContextStore { @@ -90,15 +93,19 @@ pub struct NativeCallContextStore { pub source: Option, /// The current [`GlobalRuntimeState`], if any. pub global: GlobalRuntimeState, - /// The current stack of loaded [modules][Module]. - pub lib: StaticVec, /// [Position] of the function call. pub pos: Position, } #[cfg(feature = "internals")] +#[allow(deprecated)] impl NativeCallContextStore { /// Create a [`NativeCallContext`] from a [`NativeCallContextClone`]. + /// + /// # WARNING - Unstable API + /// + /// This API is volatile and may change in the future. + #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."] #[inline(always)] #[must_use] pub fn create_context<'a>(&'a self, engine: &'a Engine) -> NativeCallContext<'a> { @@ -112,7 +119,6 @@ impl<'a> &'a str, Option<&'a str>, &'a GlobalRuntimeState, - &'a [SharedModule], Position, )> for NativeCallContext<'a> { @@ -123,7 +129,6 @@ impl<'a> &'a str, Option<&'a str>, &'a GlobalRuntimeState, - &'a [SharedModule], Position, ), ) -> Self { @@ -132,8 +137,7 @@ impl<'a> fn_name: value.1, source: value.2, global: value.3, - lib: value.4, - pos: value.5, + pos: value.4, } } } @@ -152,7 +156,6 @@ impl<'a> NativeCallContext<'a> { fn_name: &'a str, source: Option<&'a str>, global: &'a GlobalRuntimeState, - lib: &'a [SharedModule], pos: Position, ) -> Self { Self { @@ -160,37 +163,46 @@ impl<'a> NativeCallContext<'a> { fn_name, source, global, - lib, pos, } } /// _(internals)_ Create a [`NativeCallContext`] from a [`NativeCallContextClone`]. /// Exported under the `internals` feature only. + /// + /// # WARNING - Unstable API + /// + /// This API is volatile and may change in the future. + #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."] #[cfg(feature = "internals")] #[inline] #[must_use] + #[allow(deprecated)] pub fn from_stored_data(engine: &'a Engine, context: &'a NativeCallContextStore) -> Self { Self { engine, fn_name: &context.fn_name, source: context.source.as_ref().map(String::as_str), global: &context.global, - lib: &context.lib, pos: context.pos, } } /// _(internals)_ Store this [`NativeCallContext`] into a [`NativeCallContextClone`]. /// Exported under the `internals` feature only. + /// + /// # WARNING - Unstable API + /// + /// This API is volatile and may change in the future. + #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."] #[cfg(feature = "internals")] #[inline] #[must_use] + #[allow(deprecated)] pub fn store_data(&self) -> NativeCallContextStore { NativeCallContextStore { fn_name: self.fn_name.to_string(), source: self.source.map(|s| s.to_string()), global: self.global.clone(), - lib: self.lib.iter().cloned().collect(), pos: self.pos, } } @@ -261,17 +273,23 @@ impl<'a> NativeCallContext<'a> { } /// Get an iterator over the namespaces containing definitions of all script-defined functions /// in reverse order (i.e. parent namespaces are iterated after child namespaces). + /// + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] #[inline] pub fn iter_namespaces(&self) -> impl Iterator { - self.lib.iter().map(|m| m.as_ref()) + self.global.lib.iter().map(|m| m.as_ref()) } /// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions. /// Exported under the `internals` feature only. + /// + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub const fn namespaces(&self) -> &[SharedModule] { - self.lib + pub fn namespaces(&self) -> &[SharedModule] { + &self.global.lib } /// Call a function inside the call context with the provided arguments. #[inline] @@ -408,7 +426,6 @@ impl<'a> NativeCallContext<'a> { .exec_native_fn_call( global, caches, - self.lib, fn_name, op_token, calc_fn_hash(None, fn_name, args_len), @@ -435,7 +452,6 @@ impl<'a> NativeCallContext<'a> { .exec_fn_call( global, caches, - self.lib, None, fn_name, op_token, @@ -517,14 +533,14 @@ pub fn locked_write(value: &Locked) -> LockGuardMut { return value.write().unwrap(); } -/// General function trail object. +/// General Rust function trail object. #[cfg(not(feature = "sync"))] pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult; -/// General function trail object. +/// General Rust function trail object. #[cfg(feature = "sync")] pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + Send + Sync; -/// Trail object for built-in functions. +/// Built-in function trait object. pub type FnBuiltin = fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult; /// Function that gets an iterator from a type. @@ -535,8 +551,10 @@ pub type IteratorFn = dyn Fn(Dynamic) -> Box Box>> + Send + Sync; +/// Plugin function trait object. #[cfg(not(feature = "sync"))] pub type FnPlugin = dyn PluginFunction; +/// Plugin function trait object. #[cfg(feature = "sync")] pub type FnPlugin = dyn PluginFunction + Send + Sync; diff --git a/src/func/script.rs b/src/func/script.rs index db3fa542..55ba1361 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -4,7 +4,7 @@ use super::call::FnCallArgs; use crate::ast::ScriptFnDef; use crate::eval::{Caches, GlobalRuntimeState}; -use crate::{Dynamic, Engine, Position, RhaiError, RhaiResult, Scope, SharedModule, ERR}; +use crate::{Dynamic, Engine, Position, RhaiError, RhaiResult, Scope, ERR}; use std::mem; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -26,7 +26,6 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], scope: &mut Scope, this_ptr: &mut Dynamic, fn_def: &ScriptFnDef, @@ -79,6 +78,7 @@ impl Engine { } let orig_scope_len = scope.len(); + let orig_lib_len = global.lib.len(); #[cfg(not(feature = "no_module"))] let orig_imports_len = global.num_imports(); @@ -106,53 +106,34 @@ impl Engine { 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); - - #[cfg(not(feature = "no_module"))] - let (lib, constants) = if let Some(ref environ) = fn_def.environ { + let orig_constants = if let Some(ref environ) = fn_def.environ { let crate::ast::EncapsulatedEnviron { - lib: fn_lib, - imports, - constants, - } = environ.as_ref(); + ref lib, + ref imports, + ref constants, + } = **environ; imports .iter() .cloned() .for_each(|(n, m)| global.push_import(n, m)); - ( - if fn_lib.is_empty() { - lib - } else { - caches.push_fn_resolution_cache(); - lib_merged.push(fn_lib.clone()); - lib_merged.extend(lib.iter().cloned()); - &lib_merged - }, - Some(mem::replace(&mut global.constants, constants.clone())), - ) + global.lib.push(lib.clone()); + + Some(mem::replace(&mut global.constants, constants.clone())) } else { - (lib, None) + None }; #[cfg(feature = "debugging")] { let node = crate::ast::Stmt::Noop(fn_def.body.position()); - self.run_debugger(global, caches, lib, scope, this_ptr, &node)?; + self.run_debugger(global, caches, scope, this_ptr, &node)?; } // Evaluate the function let mut _result = self - .eval_stmt_block( - global, - caches, - lib, - scope, - this_ptr, - &fn_def.body, - rewind_scope, - ) + .eval_stmt_block(global, caches, scope, this_ptr, &fn_def.body, rewind_scope) .or_else(|err| match *err { // Convert return statement to return value ERR::Return(x, ..) => Ok(x), @@ -188,7 +169,7 @@ impl Engine { Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r), Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), }; - match self.run_debugger_raw(global, caches, lib, scope, this_ptr, node, event) { + match self.run_debugger_raw(global, caches, scope, this_ptr, node, event) { Ok(_) => (), Err(err) => _result = Err(err), } @@ -205,12 +186,13 @@ impl Engine { // Remove arguments only, leaving new variables in the scope scope.remove_range(orig_scope_len, args.len()); } + global.lib.truncate(orig_lib_len); #[cfg(not(feature = "no_module"))] global.truncate_imports(orig_imports_len); // Restore constants #[cfg(not(feature = "no_module"))] - if let Some(constants) = constants { + if let Some(constants) = orig_constants { global.constants = constants; } @@ -224,9 +206,8 @@ impl Engine { #[must_use] pub(crate) fn has_script_fn( &self, - _global: &GlobalRuntimeState, + global: &GlobalRuntimeState, caches: &mut Caches, - lib: &[SharedModule], hash_script: u64, ) -> bool { let cache = caches.fn_resolution_cache_mut(); @@ -236,14 +217,14 @@ impl Engine { } // First check script-defined functions - let result = lib.iter().any(|m| m.contains_fn(hash_script)) + let result = global.lib.iter().any(|m| m.contains_fn(hash_script)) // Then check the global namespace and packages || self.global_modules.iter().any(|m| m.contains_fn(hash_script)); #[cfg(not(feature = "no_module"))] let result = result || // Then check imported modules - _global.contains_qualified_fn(hash_script) + global.contains_qualified_fn(hash_script) // Then check sub-modules || self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script)); diff --git a/src/lib.rs b/src/lib.rs index 4991287d..3a3ed821 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -303,17 +303,19 @@ pub type OptimizationLevel = (); pub use types::dynamic::{AccessMode, DynamicReadLock, DynamicWriteLock, Variant}; #[cfg(feature = "internals")] -pub use tokenizer::{get_next_token, parse_string_literal}; - -#[cfg(feature = "internals")] -pub use tokenizer::{ - is_valid_function_name, is_valid_identifier, InputStream, MultiInputsStream, Span, Token, - TokenIterator, TokenizeState, TokenizerControl, TokenizerControlBlock, -}; +#[cfg(not(feature = "no_float"))] +pub use types::FloatWrapper; #[cfg(feature = "internals")] pub use types::StringsInterner; +#[cfg(feature = "internals")] +pub use tokenizer::{ + get_next_token, is_valid_function_name, is_valid_identifier, parse_string_literal, InputStream, + MultiInputsStream, Span, Token, TokenIterator, TokenizeState, TokenizerControl, + TokenizerControlBlock, +}; + #[cfg(feature = "internals")] pub use parser::ParseState; @@ -337,11 +339,11 @@ pub use ast::Namespace; pub use ast::EncapsulatedEnviron; #[cfg(feature = "internals")] -#[cfg(not(feature = "no_float"))] -pub use ast::FloatWrapper; +pub use eval::{Caches, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState}; #[cfg(feature = "internals")] -pub use eval::{Caches, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState}; +#[allow(deprecated)] +pub use func::NativeCallContextStore; #[cfg(feature = "internals")] #[cfg(feature = "metadata")] diff --git a/src/module/mod.rs b/src/module/mod.rs index a8676655..91be1a53 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -543,14 +543,13 @@ impl Module { #[inline] #[must_use] pub fn is_empty(&self) -> bool { - self.indexed - && !self.contains_indexed_global_functions + !self.contains_indexed_global_functions && self.functions.is_empty() - && self.all_functions.as_ref().map_or(true, |m| m.is_empty()) && self.variables.as_ref().map_or(true, |m| m.is_empty()) - && self.all_variables.as_ref().map_or(true, |m| m.is_empty()) && self.modules.as_ref().map_or(true, |m| m.is_empty()) && self.type_iterators.as_ref().map_or(true, |t| t.is_empty()) + && self.all_functions.as_ref().map_or(true, |m| m.is_empty()) + && self.all_variables.as_ref().map_or(true, |m| m.is_empty()) && self .all_type_iterators .as_ref() @@ -1980,6 +1979,10 @@ impl Module { // Save global state let orig_imports_len = global.num_imports(); let orig_source = global.source.clone(); + + #[cfg(not(feature = "no_function"))] + let orig_lib_len = global.lib.len(); + #[cfg(not(feature = "no_function"))] let orig_constants = std::mem::take(&mut global.constants); @@ -2007,7 +2010,12 @@ impl Module { // Restore global state #[cfg(not(feature = "no_function"))] let constants = std::mem::replace(&mut global.constants, orig_constants); + global.truncate_imports(orig_imports_len); + + #[cfg(not(feature = "no_function"))] + global.lib.truncate(orig_lib_len); + global.source = orig_source; result?; diff --git a/src/optimizer.rs b/src/optimizer.rs index 9bf57005..d903c836 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -12,7 +12,7 @@ use crate::tokenizer::Token; use crate::types::dynamic::AccessMode; use crate::{ calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnPtr, Identifier, ImmutableString, Position, - Scope, StaticVec, AST, INT, + Scope, StaticVec, AST, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -59,9 +59,6 @@ struct OptimizerState<'a> { global: GlobalRuntimeState, /// Function resolution caches. caches: Caches, - /// [Module][crate::Module] containing script-defined functions. - #[cfg(not(feature = "no_function"))] - lib: &'a [crate::SharedModule], /// Optimization level. optimization_level: OptimizationLevel, } @@ -74,15 +71,20 @@ impl<'a> OptimizerState<'a> { #[cfg(not(feature = "no_function"))] lib: &'a [crate::SharedModule], optimization_level: OptimizationLevel, ) -> Self { + let mut _global = GlobalRuntimeState::new(engine); + + #[cfg(not(feature = "no_function"))] + { + _global.lib = lib.iter().cloned().collect(); + } + Self { changed: false, variables: StaticVec::new_const(), propagate_constants: true, engine, - global: GlobalRuntimeState::new(engine), + global: _global, caches: Caches::new(), - #[cfg(not(feature = "no_function"))] - lib, optimization_level, } } @@ -138,16 +140,10 @@ impl<'a> OptimizerState<'a> { op_token: Option<&Token>, arg_values: &mut [Dynamic], ) -> Dynamic { - #[cfg(not(feature = "no_function"))] - let lib = self.lib; - #[cfg(feature = "no_function")] - let lib = &[]; - self.engine .exec_native_fn_call( &mut self.global, &mut self.caches, - lib, fn_name, op_token, calc_fn_hash(None, fn_name, arg_values.len()), @@ -592,7 +588,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b } // Then check ranges - if value.is::() && !ranges.is_empty() { + if value.is_int() && !ranges.is_empty() { let value = value.as_int().unwrap(); // Only one range or all ranges without conditions @@ -1138,12 +1134,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { _ if x.args.len() == 2 && x.op_token.is_some() && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native(), &arg_types)) => { if let Some(result) = get_builtin_binary_op_fn(x.op_token.as_ref().unwrap(), &arg_values[0], &arg_values[1]) .and_then(|f| { - #[cfg(not(feature = "no_function"))] - let lib = state.lib; - #[cfg(feature = "no_function")] - let lib = &[][..]; - - let context = (state.engine, x.name.as_str(),None, &state.global, lib, *pos).into(); + let context = (state.engine, x.name.as_str(),None, &state.global, *pos).into(); let (first, second) = arg_values.split_first_mut().unwrap(); (f)(context, &mut [ first, &mut second[0] ]).ok() }) { @@ -1183,7 +1174,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { => { // First search for script-defined functions (can override built-in) #[cfg(not(feature = "no_function"))] - let has_script_fn = !x.hashes.is_native_only() && state.lib.iter().find_map(|m| m.get_script_fn(&x.name, x.args.len())).is_some(); + let has_script_fn = !x.hashes.is_native_only() && state.global.lib.iter().find_map(|m| m.get_script_fn(&x.name, x.args.len())).is_some(); #[cfg(feature = "no_function")] let has_script_fn = false; diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index a0763ada..63218d2b 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -39,7 +39,7 @@ pub fn print_with_func( value: &mut Dynamic, ) -> crate::ImmutableString { match ctx.call_native_fn_raw(fn_name, true, &mut [value]) { - Ok(result) if result.is::() => { + Ok(result) if result.is_string() => { result.into_immutable_string().expect("`ImmutableString`") } Ok(result) => ctx.engine().map_type_name(result.type_name()).into(), @@ -75,7 +75,7 @@ mod print_debug_functions { /// Return the empty string. #[rhai_fn(name = "print", name = "debug")] pub fn print_empty_string(ctx: NativeCallContext) -> ImmutableString { - ctx.engine().get_interned_string("") + ctx.engine().const_empty_string() } /// Return the `string`. @@ -121,7 +121,7 @@ mod print_debug_functions { #[rhai_fn(name = "print", name = "to_string")] pub fn print_unit(ctx: NativeCallContext, unit: ()) -> ImmutableString { let _ = unit; - ctx.engine().get_interned_string("") + ctx.engine().const_empty_string() } /// Convert the unit into a string in debug format. #[rhai_fn(name = "debug", name = "to_debug")] @@ -134,26 +134,26 @@ mod print_debug_functions { #[cfg(not(feature = "no_float"))] #[rhai_fn(name = "print", name = "to_string")] pub fn print_f64(number: f64) -> ImmutableString { - crate::ast::FloatWrapper::new(number).to_string().into() + crate::types::FloatWrapper::new(number).to_string().into() } /// Convert the value of `number` into a string. #[cfg(not(feature = "no_float"))] #[rhai_fn(name = "print", name = "to_string")] pub fn print_f32(number: f32) -> ImmutableString { - crate::ast::FloatWrapper::new(number).to_string().into() + crate::types::FloatWrapper::new(number).to_string().into() } /// Convert the value of `number` into a string. #[cfg(not(feature = "no_float"))] #[rhai_fn(name = "debug", name = "to_debug")] pub fn debug_f64(number: f64) -> ImmutableString { - let number = crate::ast::FloatWrapper::new(number); + let number = crate::types::FloatWrapper::new(number); format!("{number:?}").into() } /// Convert the value of `number` into a string. #[cfg(not(feature = "no_float"))] #[rhai_fn(name = "debug", name = "to_debug")] pub fn debug_f32(number: f32) -> ImmutableString { - let number = crate::ast::FloatWrapper::new(number); + let number = crate::types::FloatWrapper::new(number); format!("{number:?}").into() } diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 72c4a6ca..bc39f8cb 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -348,7 +348,7 @@ mod string_functions { len: INT, ) -> ImmutableString { if string.is_empty() || len <= 0 { - return ctx.engine().get_interned_string(""); + return ctx.engine().const_empty_string(); } let len = len.min(MAX_USIZE_INT) as usize; @@ -896,18 +896,18 @@ mod string_functions { len: INT, ) -> ImmutableString { if string.is_empty() { - return ctx.engine().get_interned_string(""); + return ctx.engine().const_empty_string(); } let mut chars = StaticVec::with_capacity(string.len()); let offset = if string.is_empty() || len <= 0 { - return ctx.engine().get_interned_string(""); + return ctx.engine().const_empty_string(); } else if start < 0 { let abs_start = start.unsigned_abs(); if abs_start as u64 > MAX_USIZE_INT as u64 { - return ctx.engine().get_interned_string(""); + return ctx.engine().const_empty_string(); } let abs_start = abs_start as usize; @@ -918,7 +918,7 @@ mod string_functions { chars.len() - abs_start } } else if start > MAX_USIZE_INT || start as usize >= string.chars().count() { - return ctx.engine().get_interned_string(""); + return ctx.engine().const_empty_string(); } else { start as usize }; @@ -964,7 +964,7 @@ mod string_functions { start: INT, ) -> ImmutableString { if string.is_empty() { - ctx.engine().get_interned_string("") + ctx.engine().const_empty_string() } else { let len = string.len() as INT; sub_string(ctx, string, start, len) @@ -1348,7 +1348,7 @@ mod string_functions { if abs_index as u64 > MAX_USIZE_INT as u64 { return vec![ - ctx.engine().get_interned_string("").into(), + ctx.engine().const_empty_string().into(), string.as_str().into(), ]; } @@ -1357,7 +1357,7 @@ mod string_functions { let num_chars = string.chars().count(); if abs_index > num_chars { vec![ - ctx.engine().get_interned_string("").into(), + ctx.engine().const_empty_string().into(), string.as_str().into(), ] } else { @@ -1368,7 +1368,7 @@ mod string_functions { } else if index > MAX_USIZE_INT { vec![ string.as_str().into(), - ctx.engine().get_interned_string("").into(), + ctx.engine().const_empty_string().into(), ] } else { let prefix: String = string.chars().take(index as usize).collect(); diff --git a/src/parser.rs b/src/parser.rs index fed55ac3..38f357bf 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -374,7 +374,7 @@ impl Expr { fn ensure_bool_expr(self) -> ParseResult { let type_name = match self { Expr::Unit(..) => "()", - Expr::DynamicConstant(ref v, ..) if !v.is::() => v.type_name(), + Expr::DynamicConstant(ref v, ..) if !v.is_bool() => v.type_name(), Expr::IntegerConstant(..) => "a number", #[cfg(not(feature = "no_float"))] Expr::FloatConstant(..) => "a floating-point number", @@ -1248,7 +1248,7 @@ impl Engine { continue; } - if value.is::() && !ranges.is_empty() { + if value.is_int() && !ranges.is_empty() { return Err(PERR::WrongSwitchIntegerCase.into_err(expr.start_position())); } @@ -2909,14 +2909,8 @@ impl Engine { let caches = &mut Caches::new(); let mut this = Dynamic::NULL; - let context = EvalContext::new( - self, - &mut state.global, - caches, - &[], - &mut state.stack, - &mut this, - ); + let context = + EvalContext::new(self, &mut state.global, caches, &mut state.stack, &mut this); match filter(false, info, context) { Ok(true) => (), diff --git a/src/serde/de.rs b/src/serde/de.rs index 42d4739f..1e3f3664 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -370,7 +370,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { } fn deserialize_option>(self, visitor: V) -> RhaiResultOf { - if self.0.is::<()>() { + if self.0.is_unit() { visitor.visit_none() } else { visitor.visit_some(self) diff --git a/src/tests.rs b/src/tests.rs index 6e36c977..01a0e8e7 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -45,9 +45,9 @@ fn check_struct_sizes() { assert_eq!( size_of::(), if cfg!(feature = "no_position") { - 64 + 48 } else { - 72 + 56 } ); } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 7dea89d9..17f9087e 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -382,7 +382,7 @@ pub enum Token { /// /// Reserved under the `no_float` feature. #[cfg(not(feature = "no_float"))] - FloatConstant(crate::ast::FloatWrapper), + FloatConstant(crate::types::FloatWrapper), /// A [`Decimal`][rust_decimal::Decimal] constant. /// /// Requires the `decimal` feature. @@ -1682,7 +1682,7 @@ fn get_next_token_inner( // If integer parsing is unnecessary, try float instead #[cfg(not(feature = "no_float"))] let num = num.or_else(|_| { - crate::ast::FloatWrapper::from_str(&result).map(Token::FloatConstant) + crate::types::FloatWrapper::from_str(&result).map(Token::FloatConstant) }); // Then try decimal diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index eeee966a..3b1a6cee 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -70,7 +70,7 @@ pub enum Union { Int(INT, Tag, AccessMode), /// A floating-point value. #[cfg(not(feature = "no_float"))] - Float(crate::ast::FloatWrapper, Tag, AccessMode), + Float(super::FloatWrapper, Tag, AccessMode), /// _(decimal)_ A fixed-precision decimal value. /// Exported under the `decimal` feature only. #[cfg(feature = "decimal")] @@ -260,10 +260,18 @@ impl Dynamic { } /// Is the value held by this [`Dynamic`] a particular type? /// - /// If the [`Dynamic`] is a shared variant checking is performed on top of its internal value. + /// # Panics or Deadlocks When Value is Shared + /// + /// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). + /// Otherwise, this call panics if the data is currently borrowed for write. #[inline] #[must_use] pub fn is(&self) -> bool { + #[cfg(not(feature = "no_closure"))] + if self.is_shared() { + return TypeId::of::() == self.type_id(); + } + if TypeId::of::() == TypeId::of::<()>() { return matches!(self.0, Union::Unit(..)); } @@ -867,7 +875,7 @@ impl Dynamic { #[must_use] pub const fn from_float(value: crate::FLOAT) -> Self { Self(Union::Float( - crate::ast::FloatWrapper::new(value), + super::FloatWrapper::new(value), DEFAULT_TAG_VALUE, ReadWrite, )) @@ -1626,14 +1634,205 @@ impl Dynamic { _ => None, } } + + /// Return `true` if the [`Dynamic`] holds a `()`. + #[inline] + #[must_use] + pub fn is_unit(&self) -> bool { + match self.0 { + Union::Unit(..) => true, + #[cfg(not(feature = "no_closure"))] + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Unit(..) => true, + _ => false, + }, + _ => false, + } + } + /// Return `true` if the [`Dynamic`] holds the system integer type [`INT`]. + #[inline] + #[must_use] + pub fn is_int(&self) -> bool { + match self.0 { + Union::Int(..) => true, + #[cfg(not(feature = "no_closure"))] + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Int(..) => true, + _ => false, + }, + _ => false, + } + } + /// Return `true` if the [`Dynamic`] holds the system floating-point type [`FLOAT`][crate::FLOAT]. + /// + /// Not available under `no_float`. + #[cfg(not(feature = "no_float"))] + #[inline] + #[must_use] + pub fn is_float(&self) -> bool { + match self.0 { + Union::Float(..) => true, + #[cfg(not(feature = "no_closure"))] + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Float(..) => true, + _ => false, + }, + _ => false, + } + } + /// _(decimal)_ Return `true` if the [`Dynamic`] holds a [`Decimal`][rust_decimal::Decimal]. + /// + /// Exported under the `decimal` feature only. + #[cfg(feature = "decimal")] + #[inline] + #[must_use] + pub fn is_decimal(&self) -> bool { + match self.0 { + Union::Decimal(..) => true, + #[cfg(not(feature = "no_closure"))] + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Decimal(..) => true, + _ => false, + }, + _ => false, + } + } + /// Return `true` if the [`Dynamic`] holds a [`bool`]. + #[inline] + #[must_use] + pub fn is_bool(&self) -> bool { + match self.0 { + Union::Bool(..) => true, + #[cfg(not(feature = "no_closure"))] + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Bool(..) => true, + _ => false, + }, + _ => false, + } + } + /// Return `true` if the [`Dynamic`] holds a [`char`]. + #[inline] + #[must_use] + pub fn is_char(&self) -> bool { + match self.0 { + Union::Char(..) => true, + #[cfg(not(feature = "no_closure"))] + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Char(..) => true, + _ => false, + }, + _ => false, + } + } + /// Return `true` if the [`Dynamic`] holds an [`ImmutableString`]. + #[inline] + #[must_use] + pub fn is_string(&self) -> bool { + match self.0 { + Union::Str(..) => true, + #[cfg(not(feature = "no_closure"))] + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Str(..) => true, + _ => false, + }, + _ => false, + } + } + /// Return `true` if the [`Dynamic`] holds an [`Array`][crate::Array]. + /// + /// Not available under `no_index`. + #[cfg(not(feature = "no_index"))] + #[inline] + #[must_use] + pub fn is_array(&self) -> bool { + match self.0 { + Union::Array(..) => true, + #[cfg(not(feature = "no_closure"))] + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Array(..) => true, + _ => false, + }, + _ => false, + } + } + /// Return `true` if the [`Dynamic`] holds a [`Blob`][crate::Blob]. + /// + /// Not available under `no_index`. + #[cfg(not(feature = "no_index"))] + #[inline] + #[must_use] + pub fn is_blob(&self) -> bool { + match self.0 { + Union::Blob(..) => true, + #[cfg(not(feature = "no_closure"))] + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Blob(..) => true, + _ => false, + }, + _ => false, + } + } + /// Return `true` if the [`Dynamic`] holds a [`Map`][crate::Map]. + /// + /// Not available under `no_object`. + #[cfg(not(feature = "no_object"))] + #[inline] + #[must_use] + pub fn is_map(&self) -> bool { + match self.0 { + Union::Map(..) => true, + #[cfg(not(feature = "no_closure"))] + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Map(..) => true, + _ => false, + }, + _ => false, + } + } + /// Return `true` if the [`Dynamic`] holds a [`FnPtr`]. + #[inline] + #[must_use] + pub(crate) fn is_fnptr(&self) -> bool { + match self.0 { + Union::FnPtr(..) => true, + #[cfg(not(feature = "no_closure"))] + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::FnPtr(..) => true, + _ => false, + }, + _ => false, + } + } + /// Return `true` if the [`Dynamic`] holds a [timestamp][Instant]. + /// + /// Not available under `no_time`. + #[cfg(not(feature = "no_time"))] + #[inline] + #[must_use] + pub fn is_timestamp(&self) -> bool { + match self.0 { + Union::TimeStamp(..) => true, + #[cfg(not(feature = "no_closure"))] + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::TimeStamp(..) => true, + _ => false, + }, + _ => false, + } + } + /// Cast the [`Dynamic`] as a unit `()`. /// Returns the name of the actual type if the cast fails. #[inline] pub fn as_unit(&self) -> Result<(), &'static str> { match self.0 { - Union::Unit(v, ..) => Ok(v), + Union::Unit(..) => Ok(()), #[cfg(not(feature = "no_closure"))] - Union::Shared(..) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()), + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Unit(..) => Ok(()), + _ => Err(cell.type_name()), + }, _ => Err(self.type_name()), } } @@ -1644,7 +1843,10 @@ impl Dynamic { match self.0 { Union::Int(n, ..) => Ok(n), #[cfg(not(feature = "no_closure"))] - Union::Shared(..) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()), + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Int(n, ..) => Ok(n), + _ => Err(cell.type_name()), + }, _ => Err(self.type_name()), } } @@ -1658,7 +1860,10 @@ impl Dynamic { match self.0 { Union::Float(n, ..) => Ok(*n), #[cfg(not(feature = "no_closure"))] - Union::Shared(..) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()), + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Float(n, ..) => Ok(*n), + _ => Err(cell.type_name()), + }, _ => Err(self.type_name()), } } @@ -1672,7 +1877,10 @@ impl Dynamic { match self.0 { Union::Decimal(ref n, ..) => Ok(**n), #[cfg(not(feature = "no_closure"))] - Union::Shared(..) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()), + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Decimal(ref n, ..) => Ok(**n), + _ => Err(cell.type_name()), + }, _ => Err(self.type_name()), } } @@ -1683,7 +1891,10 @@ impl Dynamic { match self.0 { Union::Bool(b, ..) => Ok(b), #[cfg(not(feature = "no_closure"))] - Union::Shared(..) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()), + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Bool(b, ..) => Ok(b), + _ => Err(cell.type_name()), + }, _ => Err(self.type_name()), } } @@ -1692,12 +1903,16 @@ impl Dynamic { #[inline] pub fn as_char(&self) -> Result { match self.0 { - Union::Char(n, ..) => Ok(n), + Union::Char(c, ..) => Ok(c), #[cfg(not(feature = "no_closure"))] - Union::Shared(..) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()), + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Char(c, ..) => Ok(c), + _ => Err(cell.type_name()), + }, _ => Err(self.type_name()), } } + /// Cast the [`Dynamic`] as a string slice. /// Returns the name of the actual type if the cast fails. /// @@ -1728,38 +1943,34 @@ impl Dynamic { match self.0 { Union::Str(s, ..) => Ok(s), #[cfg(not(feature = "no_closure"))] - Union::Shared(ref cell, ..) => { - let value = crate::func::locked_read(cell); - - match value.0 { - Union::Str(ref s, ..) => Ok(s.clone()), - _ => Err((*value).type_name()), - } - } + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Str(ref s, ..) => Ok(s.clone()), + _ => Err(cell.type_name()), + }, _ => Err(self.type_name()), } } /// Convert the [`Dynamic`] into an [`Array`][crate::Array]. /// Returns the name of the actual type if the cast fails. + /// + /// Not available under `no_index`. #[cfg(not(feature = "no_index"))] #[inline(always)] pub fn into_array(self) -> Result { match self.0 { Union::Array(a, ..) => Ok(*a), #[cfg(not(feature = "no_closure"))] - Union::Shared(ref cell, ..) => { - let value = crate::func::locked_read(cell); - - match value.0 { - Union::Array(ref a, ..) => Ok(a.as_ref().clone()), - _ => Err((*value).type_name()), - } - } + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Array(ref a, ..) => Ok(a.as_ref().clone()), + _ => Err(cell.type_name()), + }, _ => Err(self.type_name()), } } /// Convert the [`Dynamic`] into a [`Vec`]. /// Returns the name of the actual type if any cast fails. + /// + /// Not available under `no_index`. #[cfg(not(feature = "no_index"))] #[inline(always)] pub fn into_typed_array(self) -> Result, &'static str> { @@ -1780,12 +1991,12 @@ impl Dynamic { v.try_cast::().ok_or(typ) }) .collect(), - Union::Blob(..) if TypeId::of::() == TypeId::of::() => Ok(self.cast::>()), + Union::Blob(b, ..) if TypeId::of::() == TypeId::of::() => { + Ok(reify!(*b => Vec)) + } #[cfg(not(feature = "no_closure"))] Union::Shared(ref cell, ..) => { - let value = crate::func::locked_read(cell); - - match value.0 { + match crate::func::locked_read(cell).0 { Union::Array(ref a, ..) => { a.iter() .map(|v| { @@ -1803,10 +2014,10 @@ impl Dynamic { }) .collect() } - Union::Blob(..) if TypeId::of::() == TypeId::of::() => { - Ok((*value).clone().cast::>()) + Union::Blob(ref b, ..) if TypeId::of::() == TypeId::of::() => { + Ok(reify!(b.clone() => Vec)) } - _ => Err((*value).type_name()), + _ => Err(cell.type_name()), } } _ => Err(self.type_name()), @@ -1814,20 +2025,18 @@ impl Dynamic { } /// Convert the [`Dynamic`] into a [`Blob`][crate::Blob]. /// Returns the name of the actual type if the cast fails. + /// + /// Not available under `no_index`. #[cfg(not(feature = "no_index"))] #[inline(always)] pub fn into_blob(self) -> Result { match self.0 { - Union::Blob(a, ..) => Ok(*a), + Union::Blob(b, ..) => Ok(*b), #[cfg(not(feature = "no_closure"))] - Union::Shared(ref cell, ..) => { - let value = crate::func::locked_read(cell); - - match value.0 { - Union::Blob(ref a, ..) => Ok(a.as_ref().clone()), - _ => Err((*value).type_name()), - } - } + Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { + Union::Blob(ref b, ..) => Ok(b.as_ref().clone()), + _ => Err(cell.type_name()), + }, _ => Err(self.type_name()), } } @@ -1859,9 +2068,9 @@ impl From for Dynamic { } } #[cfg(not(feature = "no_float"))] -impl From> for Dynamic { +impl From> for Dynamic { #[inline(always)] - fn from(value: crate::ast::FloatWrapper) -> Self { + fn from(value: super::FloatWrapper) -> Self { Self(Union::Float(value, DEFAULT_TAG_VALUE, ReadWrite)) } } diff --git a/src/types/error.rs b/src/types/error.rs index 906f32c9..1a8035e5 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -174,7 +174,7 @@ impl fmt::Display for EvalAltResult { Self::ErrorStackOverflow(..) => f.write_str("Stack overflow")?, Self::ErrorTerminated(..) => f.write_str("Script terminated")?, - Self::ErrorRuntime(d, ..) if d.is::<()>() => f.write_str("Runtime error")?, + Self::ErrorRuntime(d, ..) if d.is_unit() => f.write_str("Runtime error")?, Self::ErrorRuntime(d, ..) if d.read_lock::() .map_or(false, |v| v.is_empty()) => diff --git a/src/types/float.rs b/src/types/float.rs new file mode 100644 index 00000000..a3b934a6 --- /dev/null +++ b/src/types/float.rs @@ -0,0 +1,115 @@ +#![cfg(not(feature = "no_float"))] + +#[cfg(feature = "no_std")] +use std::prelude::v1::*; +use std::{ + fmt, + hash::{Hash, Hasher}, + ops::{Deref, DerefMut}, + str::FromStr, +}; + +use num_traits::float::FloatCore as Float; + +/// A type that wraps a floating-point number and implements [`Hash`]. +/// +/// Not available under `no_float`. +#[derive(Clone, Copy, PartialEq, PartialOrd)] +pub struct FloatWrapper(F); + +impl Hash for FloatWrapper { + #[inline] + fn hash(&self, state: &mut H) { + self.0.to_ne_bytes().hash(state); + } +} + +impl AsRef for FloatWrapper { + #[inline(always)] + #[must_use] + fn as_ref(&self) -> &F { + &self.0 + } +} + +impl AsMut for FloatWrapper { + #[inline(always)] + #[must_use] + fn as_mut(&mut self) -> &mut F { + &mut self.0 + } +} + +impl Deref for FloatWrapper { + type Target = F; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for FloatWrapper { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl fmt::Debug for FloatWrapper { + #[cold] + #[inline(never)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + +impl> fmt::Display for FloatWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let abs = self.0.abs(); + if abs.is_zero() { + f.write_str("0.0") + } else if abs > Self::MAX_NATURAL_FLOAT_FOR_DISPLAY.into() + || abs < Self::MIN_NATURAL_FLOAT_FOR_DISPLAY.into() + { + write!(f, "{:e}", self.0) + } else { + fmt::Display::fmt(&self.0, f)?; + if abs.fract().is_zero() { + f.write_str(".0")?; + } + Ok(()) + } + } +} + +impl From for FloatWrapper { + #[inline(always)] + fn from(value: F) -> Self { + Self::new(value) + } +} + +impl FromStr for FloatWrapper { + type Err = ::Err; + + #[inline] + fn from_str(s: &str) -> Result { + F::from_str(s).map(Into::into) + } +} + +impl FloatWrapper { + /// Maximum floating-point number for natural display before switching to scientific notation. + pub const MAX_NATURAL_FLOAT_FOR_DISPLAY: f32 = 10_000_000_000_000.0; + + /// Minimum floating-point number for natural display before switching to scientific notation. + pub const MIN_NATURAL_FLOAT_FOR_DISPLAY: f32 = 0.000_000_000_000_1; + + /// Create a new [`FloatWrapper`]. + #[inline(always)] + #[must_use] + pub const fn new(value: F) -> Self { + Self(value) + } +} diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 5d087ef2..900b738b 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -5,7 +5,7 @@ use crate::tokenizer::is_valid_function_name; use crate::types::dynamic::Variant; use crate::{ Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError, RhaiResult, - RhaiResultOf, SharedModule, StaticVec, AST, ERR, + RhaiResultOf, StaticVec, AST, ERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -151,19 +151,12 @@ impl FnPtr { let mut arg_values = crate::StaticVec::new_const(); args.parse(&mut arg_values); - let lib: &[SharedModule] = &[ - #[cfg(not(feature = "no_function"))] - AsRef::::as_ref(ast).clone(), - ]; - let lib = if lib.first().map_or(true, |m| m.is_empty()) { - &[][..] - } else { - &lib - }; + let global = &mut GlobalRuntimeState::new(engine); - let global = &GlobalRuntimeState::new(engine); + #[cfg(not(feature = "no_function"))] + global.lib.push(_ast.shared_lib().clone()); - let ctx = (engine, self.fn_name(), None, global, lib, Position::NONE).into(); + let ctx = (engine, self.fn_name(), None, &*global, Position::NONE).into(); let result = self.call_raw(&ctx, None, arg_values)?; diff --git a/src/types/mod.rs b/src/types/mod.rs index 1fbf33d3..aad22a3c 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -4,6 +4,7 @@ pub mod bloom_filter; pub mod custom_types; pub mod dynamic; pub mod error; +pub mod float; pub mod fn_ptr; pub mod immutable_string; pub mod interner; @@ -18,6 +19,8 @@ pub use dynamic::Dynamic; #[cfg(not(feature = "no_time"))] pub use dynamic::Instant; pub use error::EvalAltResult; +#[cfg(not(feature = "no_float"))] +pub use float::FloatWrapper; pub use fn_ptr::FnPtr; pub use immutable_string::ImmutableString; pub use interner::StringsInterner; diff --git a/tests/serde.rs b/tests/serde.rs index 4a08bb81..28f3e1bb 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -19,11 +19,11 @@ use rust_decimal::Decimal; #[test] fn test_serde_ser_primary_types() -> Result<(), Box> { - assert!(to_dynamic(42_u64)?.is::()); + assert!(to_dynamic(42_u64)?.is_int()); assert!(to_dynamic(u64::MAX)?.is::()); - assert!(to_dynamic(42 as INT)?.is::()); - assert!(to_dynamic(true)?.is::()); - assert!(to_dynamic(())?.is::<()>()); + assert!(to_dynamic(42 as INT)?.is_int()); + assert!(to_dynamic(true)?.is_bool()); + assert!(to_dynamic(())?.is_unit()); #[cfg(not(feature = "no_float"))] { @@ -45,14 +45,14 @@ fn test_serde_ser_primary_types() -> Result<(), Box> { #[test] fn test_serde_ser_integer_types() -> Result<(), Box> { - assert!(to_dynamic(42_i8)?.is::()); - assert!(to_dynamic(42_i16)?.is::()); - assert!(to_dynamic(42_i32)?.is::()); - assert!(to_dynamic(42_i64)?.is::()); - assert!(to_dynamic(42_u8)?.is::()); - assert!(to_dynamic(42_u16)?.is::()); - assert!(to_dynamic(42_u32)?.is::()); - assert!(to_dynamic(42_u64)?.is::()); + assert!(to_dynamic(42_i8)?.is_int()); + assert!(to_dynamic(42_i16)?.is_int()); + assert!(to_dynamic(42_i32)?.is_int()); + assert!(to_dynamic(42_i64)?.is_int()); + assert!(to_dynamic(42_u8)?.is_int()); + assert!(to_dynamic(42_u16)?.is_int()); + assert!(to_dynamic(42_u32)?.is_int()); + assert!(to_dynamic(42_u64)?.is_int()); Ok(()) } @@ -63,7 +63,7 @@ fn test_serde_ser_array() -> Result<(), Box> { let arr: Vec = vec![123, 456, 42, 999]; let d = to_dynamic(arr)?; - assert!(d.is::()); + assert!(d.is_array()); assert_eq!(4, d.cast::().len()); Ok(()) @@ -94,7 +94,7 @@ fn test_serde_ser_struct() -> Result<(), Box> { let d = to_dynamic(x)?; - assert!(d.is::()); + assert!(d.is_map()); let mut map = d.cast::(); let obj = map.remove("obj").unwrap().cast::(); @@ -734,7 +734,7 @@ fn test_serde_json() -> serde_json::Result<()> { let d2: Dynamic = serde_json::from_str(&json)?; - assert!(d2.is::()); + assert!(d2.is_map()); let mut m = d2.cast::(); diff --git a/tests/throw.rs b/tests/throw.rs index caf2fed9..b83dbce2 100644 --- a/tests/throw.rs +++ b/tests/throw.rs @@ -11,7 +11,7 @@ fn test_throw() { assert!(matches!( *engine.run(r#"throw"#).expect_err("expects error"), - EvalAltResult::ErrorRuntime(s, ..) if s.is::<()>() + EvalAltResult::ErrorRuntime(s, ..) if s.is_unit() )); }