From 45f0fdcbe07057d74d4ed9dd3f62e0592e17e7ce Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 15 Oct 2022 15:11:51 +0800 Subject: [PATCH 01/22] no_std = no_time. --- .github/workflows/build.yml | 5 +++-- Cargo.toml | 4 ++-- src/api/definitions/mod.rs | 1 - src/api/type_names.rs | 1 - src/lib.rs | 1 - src/packages/mod.rs | 1 - src/packages/pkg_std.rs | 2 +- src/packages/time_basic.rs | 1 - src/serde/de.rs | 1 - src/serde/serialize.rs | 2 -- src/types/dynamic.rs | 20 -------------------- src/types/mod.rs | 1 - 12 files changed, 6 insertions(+), 34 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a72d1d4..6823d676 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,10 +60,11 @@ jobs: - "--features no_object,serde,metadata,internals,debugging" - "--features no_function,serde,metadata,internals,debugging" - "--features no_module,serde,metadata,internals,debugging" + - "--features no_time,serde,metadata,internals,debugging" - "--features no_closure,serde,metadata,internals,debugging" - "--features unicode-xid-ident,serde,metadata,internals,debugging" - - "--features sync,no_function,no_float,no_position,no_optimize,no_module,no_closure,no_custom_syntax,metadata,serde,unchecked,debugging" - - "--features no_function,no_float,no_position,no_index,no_object,no_optimize,no_module,no_closure,no_custom_syntax,unchecked" + - "--features sync,no_time,no_function,no_float,no_position,no_optimize,no_module,no_closure,no_custom_syntax,metadata,serde,unchecked,debugging" + - "--features no_time,no_function,no_float,no_position,no_index,no_object,no_optimize,no_module,no_closure,no_custom_syntax,unchecked" toolchain: [stable] experimental: [false] include: diff --git a/Cargo.toml b/Cargo.toml index ff8a8b69..e1f3bd4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,11 +53,11 @@ only_i64 = [] # set INT=i64 (default) and disable support for decimal = ["rust_decimal"] # add the Decimal number type no_index = [] # no arrays and indexing no_object = [] # no custom objects +no_time = [] # no timestamps no_function = ["no_closure"] # no script-defined functions (meaning no closures) no_closure = [] # no automatic sharing and capture of anonymous functions to external variables no_module = [] # no modules no_custom_syntax = [] # no custom syntax or custom operators -no_time = [] # no timestamps unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers. metadata = ["serde", "serde_json", "rhai_codegen/metadata", "smartstring/serde"] # enable exporting functions metadata internals = [] # expose internal data structures @@ -65,7 +65,7 @@ debugging = ["internals"] # enable debugging serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types # compiling for no-std -no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "hashbrown"] +no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "hashbrown", "no_time"] # compiling for WASM wasm-bindgen = ["instant/wasm-bindgen"] diff --git a/src/api/definitions/mod.rs b/src/api/definitions/mod.rs index 4ac1280d..7be44611 100644 --- a/src/api/definitions/mod.rs +++ b/src/api/definitions/mod.rs @@ -555,7 +555,6 @@ fn def_type_name<'a>(ty: &'a str, engine: &'a Engine) -> Cow<'a, str> { #[cfg(not(feature = "no_object"))] let ty = ty.replace(type_name::(), "Map"); - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] let ty = ty.replace(type_name::(), "Instant"); diff --git a/src/api/type_names.rs b/src/api/type_names.rs index eb0f9857..f7897bf0 100644 --- a/src/api/type_names.rs +++ b/src/api/type_names.rs @@ -44,7 +44,6 @@ fn map_std_type_name(name: &str, shorthands: bool) -> &str { if name == type_name::() || name == "Map" { return if shorthands { "map" } else { "Map" }; } - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] if name == type_name::() || name == "Instant" { return if shorthands { "timestamp" } else { "Instant" }; diff --git a/src/lib.rs b/src/lib.rs index 9a540e3a..199edb75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,7 +204,6 @@ pub use eval::EvalContext; pub use func::{NativeCallContext, RegisterNativeFunction}; pub use module::{FnNamespace, Module}; pub use tokenizer::Position; -#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] pub use types::Instant; pub use types::{ diff --git a/src/packages/mod.rs b/src/packages/mod.rs index c9f31bcf..499a1762 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -38,7 +38,6 @@ pub use pkg_core::CorePackage; pub use pkg_std::StandardPackage; pub use string_basic::BasicStringPackage; pub use string_more::MoreStringPackage; -#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] pub use time_basic::BasicTimePackage; diff --git a/src/packages/pkg_std.rs b/src/packages/pkg_std.rs index cdc5fbbf..b054b3ae 100644 --- a/src/packages/pkg_std.rs +++ b/src/packages/pkg_std.rs @@ -26,7 +26,7 @@ def_package! { #[cfg(not(feature = "no_index"))] BasicArrayPackage, #[cfg(not(feature = "no_index"))] BasicBlobPackage, #[cfg(not(feature = "no_object"))] BasicMapPackage, - #[cfg(all(not(feature = "no_std"), not(feature = "no_time")))] BasicTimePackage, + #[cfg(not(feature = "no_time"))] BasicTimePackage, MoreStringPackage { lib.standard = true; diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index e76cc06e..cf7f3dc4 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -1,4 +1,3 @@ -#![cfg(not(feature = "no_std"))] #![cfg(not(feature = "no_time"))] use super::arithmetic::make_err as make_arithmetic_err; diff --git a/src/serde/de.rs b/src/serde/de.rs index 4dd0b133..fcb26a9d 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -156,7 +156,6 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { #[cfg(not(feature = "no_object"))] Union::Map(..) => self.deserialize_map(visitor), Union::FnPtr(..) => self.type_error(), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => self.type_error(), diff --git a/src/serde/serialize.rs b/src/serde/serialize.rs index 509dad56..424de0d5 100644 --- a/src/serde/serialize.rs +++ b/src/serde/serialize.rs @@ -9,7 +9,6 @@ use std::prelude::v1::*; #[cfg(not(feature = "no_object"))] use serde::ser::SerializeMap; -#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] use crate::types::dynamic::Variant; @@ -66,7 +65,6 @@ impl Serialize for Dynamic { map.end() } Union::FnPtr(ref f, ..) => ser.serialize_str(f.fn_name()), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(ref x, ..) => ser.serialize_str(x.as_ref().type_name()), diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 5643901a..0c9b7661 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -14,12 +14,10 @@ use std::{ pub use super::Variant; -#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] #[cfg(not(target_family = "wasm"))] pub use std::time::Instant; -#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] #[cfg(target_family = "wasm")] pub use instant::Instant; @@ -86,7 +84,6 @@ pub enum Union { /// A function pointer. FnPtr(Box, Tag, AccessMode), /// A timestamp value. - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] TimeStamp(Box, Tag, AccessMode), @@ -197,7 +194,6 @@ impl Dynamic { Union::Array(_, tag, _) | Union::Blob(_, tag, _) => tag, #[cfg(not(feature = "no_object"))] Union::Map(_, tag, _) => tag, - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(_, tag, _) => tag, #[cfg(not(feature = "no_closure"))] @@ -223,7 +219,6 @@ impl Dynamic { Union::Array(_, ref mut tag, _) | Union::Blob(_, ref mut tag, _) => *tag = value, #[cfg(not(feature = "no_object"))] Union::Map(_, ref mut tag, _) => *tag = value, - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(_, ref mut tag, _) => *tag = value, #[cfg(not(feature = "no_closure"))] @@ -296,7 +291,6 @@ impl Dynamic { if TypeId::of::() == TypeId::of::() { return matches!(self.0, Union::FnPtr(..)); } - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] if TypeId::of::() == TypeId::of::() { return matches!(self.0, Union::TimeStamp(..)); @@ -329,7 +323,6 @@ impl Dynamic { #[cfg(not(feature = "no_object"))] Union::Map(..) => TypeId::of::(), Union::FnPtr(..) => TypeId::of::(), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => TypeId::of::(), @@ -364,7 +357,6 @@ impl Dynamic { #[cfg(not(feature = "no_object"))] Union::Map(..) => "map", Union::FnPtr(..) => "Fn", - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => "timestamp", @@ -415,7 +407,6 @@ impl Hash for Dynamic { Union::Variant(..) => unimplemented!("{} cannot be hashed", self.type_name()), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => unimplemented!("{} cannot be hashed", self.type_name()), } @@ -441,7 +432,6 @@ impl fmt::Display for Dynamic { #[cfg(not(feature = "no_object"))] Union::Map(..) => fmt::Debug::fmt(self, f), Union::FnPtr(ref v, ..) => fmt::Display::fmt(v, f), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => f.write_str(""), @@ -547,7 +537,6 @@ impl fmt::Debug for Dynamic { fmt::Debug::fmt(v, f) } Union::FnPtr(ref v, ..) => fmt::Debug::fmt(v, f), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => write!(f, ""), @@ -646,7 +635,6 @@ impl Clone for Dynamic { #[cfg(not(feature = "no_object"))] Union::Map(ref v, tag, ..) => Self(Union::Map(v.clone(), tag, ReadWrite)), Union::FnPtr(ref v, tag, ..) => Self(Union::FnPtr(v.clone(), tag, ReadWrite)), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(ref v, tag, ..) => Self(Union::TimeStamp(v.clone(), tag, ReadWrite)), @@ -889,7 +877,6 @@ impl Dynamic { /// Create a new [`Dynamic`] from an [`Instant`]. /// /// Not available under `no-std` or `no_time`. - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] #[inline(always)] #[must_use] @@ -917,7 +904,6 @@ impl Dynamic { Union::Array(.., access) | Union::Blob(.., access) => access, #[cfg(not(feature = "no_object"))] Union::Map(.., access) => access, - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(.., access) => access, #[cfg(not(feature = "no_closure"))] @@ -955,7 +941,6 @@ impl Dynamic { v.set_access_mode(typ); } } - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(.., ref mut access) => *access = typ, #[cfg(not(feature = "no_closure"))] @@ -1090,7 +1075,6 @@ impl Dynamic { reify!(value, |v: crate::Map| return v.into()); reify!(value, |v: FnPtr| return v.into()); - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] reify!(value, |v: Instant| return v.into()); #[cfg(not(feature = "no_closure"))] @@ -1184,7 +1168,6 @@ impl Dynamic { #[cfg(not(feature = "no_object"))] Union::Map(v, ..) => reify!(*v => Option), Union::FnPtr(v, ..) => reify!(*v => Option), - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] Union::TimeStamp(v, ..) => reify!(*v => Option), Union::Unit(v, ..) => reify!(v => Option), @@ -1484,7 +1467,6 @@ impl Dynamic { _ => None, }; } - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] if TypeId::of::() == TypeId::of::() { return match self.0 { @@ -1583,7 +1565,6 @@ impl Dynamic { _ => None, }; } - #[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] if TypeId::of::() == TypeId::of::() { return match self.0 { @@ -1978,7 +1959,6 @@ impl From for Dynamic { Self(Union::FnPtr(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } -#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] impl From for Dynamic { #[inline(always)] diff --git a/src/types/mod.rs b/src/types/mod.rs index 8de89ca8..b6ca3802 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -14,7 +14,6 @@ pub mod variant; pub use bloom_filter::BloomFilterU64; pub use custom_types::{CustomTypeInfo, CustomTypesCollection}; pub use dynamic::Dynamic; -#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_time"))] pub use dynamic::Instant; pub use error::EvalAltResult; From 530109275ff9b544ff88934580e3c1cfa03e63b7 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 16 Oct 2022 11:35:21 +0800 Subject: [PATCH 02/22] Minor code restructure. --- src/api/mod.rs | 77 ++++++++++++++++++++++++++++++++--- src/ast/expr.rs | 12 +++--- src/engine.rs | 92 +----------------------------------------- src/eval/data_check.rs | 12 +++++- src/eval/mod.rs | 33 +++++++++++++++ src/optimizer.rs | 23 ++++++----- 6 files changed, 137 insertions(+), 112 deletions(-) diff --git a/src/api/mod.rs b/src/api/mod.rs index c296c81e..5f85f140 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -35,9 +35,6 @@ pub mod definitions; use crate::{Dynamic, Engine, Identifier}; -#[cfg(not(feature = "no_custom_syntax"))] -use crate::{engine::Precedence, tokenizer::Token}; - #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -150,8 +147,10 @@ impl Engine { keyword: impl AsRef, precedence: u8, ) -> Result<&mut Self, String> { - let precedence = - Precedence::new(precedence).ok_or_else(|| "precedence cannot be zero".to_string())?; + use crate::tokenizer::Token; + + let precedence = crate::engine::Precedence::new(precedence) + .ok_or_else(|| "precedence cannot be zero".to_string())?; let keyword = keyword.as_ref(); @@ -215,3 +214,71 @@ impl Engine { self } } + +#[cfg(feature = "unchecked")] +impl Engine { + /// The maximum levels of function calls allowed for a script. + /// + /// Always returns [`usize::MAX`] under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_call_levels(&self) -> usize { + usize::MAX + } + /// The maximum number of operations allowed for a script to run (0 for unlimited). + /// + /// Always returns zero under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_operations(&self) -> u64 { + 0 + } + /// The maximum number of imported [modules][crate::Module] allowed for a script. + /// + /// Always returns [`usize::MAX`] under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_modules(&self) -> usize { + usize::MAX + } + /// The depth limit for expressions (0 for unlimited). + /// + /// Always returns zero under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_expr_depth(&self) -> usize { + 0 + } + /// The depth limit for expressions in functions (0 for unlimited). + /// + /// Always returns zero under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_function_expr_depth(&self) -> usize { + 0 + } + /// The maximum length of [strings][crate::ImmutableString] (0 for unlimited). + /// + /// Always returns zero under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_string_size(&self) -> usize { + 0 + } + /// The maximum length of [arrays][crate::Array] (0 for unlimited). + /// + /// Always returns zero under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_array_size(&self) -> usize { + 0 + } + /// The maximum size of [object maps][crate::Map] (0 for unlimited). + /// + /// Always returns zero under `unchecked`. + #[inline(always)] + #[must_use] + pub const fn max_map_size(&self) -> usize { + 0 + } +} diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 4adaaecf..72d866fc 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -212,19 +212,19 @@ impl fmt::Debug for FnCallExpr { if !self.namespace.is_empty() { ff.field("namespace", &self.namespace); } - if self.capture_parent_scope { - ff.field("capture_parent_scope", &self.capture_parent_scope); - } + ff.field("hash", &self.hashes) + .field("name", &self.name) + .field("args", &self.args); if let Some(ref token) = self.operator_token { ff.field("operator_token", token); } + if self.capture_parent_scope { + ff.field("capture_parent_scope", &self.capture_parent_scope); + } #[cfg(not(feature = "no_function"))] if self.can_be_script { ff.field("can_be_script", &self.can_be_script); } - ff.field("hash", &self.hashes) - .field("name", &self.name) - .field("args", &self.args); ff.field("pos", &self.pos); ff.finish() } diff --git a/src/engine.rs b/src/engine.rs index 323f05b5..8edc62ff 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -9,8 +9,7 @@ use crate::packages::{Package, StandardPackage}; use crate::tokenizer::Token; use crate::types::StringsInterner; use crate::{ - Dynamic, Identifier, ImmutableString, Locked, Module, OptimizationLevel, Position, RhaiResult, - Shared, StaticVec, + Dynamic, Identifier, ImmutableString, Locked, Module, OptimizationLevel, Shared, StaticVec, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -345,93 +344,4 @@ impl Engine { pub fn const_empty_string(&self) -> ImmutableString { self.get_interned_string("") } - - /// Check a result to ensure that it is valid. - #[cfg(not(feature = "unchecked"))] - #[inline] - pub(crate) fn check_return_value(&self, result: RhaiResult, _pos: Position) -> RhaiResult { - if let Ok(ref r) = result { - self.check_data_size(r, _pos)?; - } - - result - } -} - -#[cfg(feature = "unchecked")] -impl Engine { - /// The maximum levels of function calls allowed for a script. - #[inline(always)] - #[must_use] - pub const fn max_call_levels(&self) -> usize { - usize::MAX - } - /// The maximum number of operations allowed for a script to run (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_operations(&self) -> u64 { - 0 - } - /// The maximum number of imported [modules][crate::Module] allowed for a script. - #[inline(always)] - #[must_use] - pub const fn max_modules(&self) -> usize { - usize::MAX - } - /// The depth limit for expressions (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_expr_depth(&self) -> usize { - 0 - } - /// The depth limit for expressions in functions (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_function_expr_depth(&self) -> usize { - 0 - } - /// The maximum length of [strings][crate::ImmutableString] (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_string_size(&self) -> usize { - 0 - } - /// The maximum length of [arrays][crate::Array] (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_array_size(&self) -> usize { - 0 - } - /// The maximum size of [object maps][crate::Map] (0 for unlimited). - #[inline(always)] - #[must_use] - pub const fn max_map_size(&self) -> usize { - 0 - } - - /// Check if the number of operations stay within limit. - #[inline(always)] - pub(crate) const fn track_operation( - &self, - _: &crate::eval::GlobalRuntimeState, - _: Position, - ) -> crate::RhaiResultOf<()> { - Ok(()) - } - - /// Check whether the size of a [`Dynamic`] is within limits. - #[inline(always)] - pub(crate) const fn check_data_size( - &self, - _: &Dynamic, - _: Position, - ) -> crate::RhaiResultOf<()> { - Ok(()) - } - - /// Check a result to ensure that it is valid. - #[inline(always)] - pub(crate) const fn check_return_value(&self, result: RhaiResult, _: Position) -> RhaiResult { - result - } } diff --git a/src/eval/data_check.rs b/src/eval/data_check.rs index 76cf7a5c..bc8953ca 100644 --- a/src/eval/data_check.rs +++ b/src/eval/data_check.rs @@ -3,7 +3,7 @@ use super::GlobalRuntimeState; use crate::types::dynamic::Union; -use crate::{Dynamic, Engine, Position, RhaiResultOf, ERR}; +use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, ERR}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -150,4 +150,14 @@ impl Engine { Ok(()) } + + /// Check a result to ensure that it is valid. + #[inline] + pub(crate) fn check_return_value(&self, result: RhaiResult, pos: Position) -> RhaiResult { + if let Ok(ref r) = result { + self.check_data_size(r, pos)?; + } + + result + } } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 2daf2432..1daf85ae 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -22,3 +22,36 @@ pub use eval_context::EvalContext; pub use global_state::GlobalConstants; pub use global_state::GlobalRuntimeState; pub use target::{calc_index, calc_offset_len, Target}; + +#[cfg(feature = "unchecked")] +mod unchecked { + use crate::{eval::GlobalRuntimeState, Dynamic, Engine, Position, RhaiResult, RhaiResultOf}; + + impl Engine { + /// Check if the number of operations stay within limit. + #[inline(always)] + pub(crate) const fn track_operation( + &self, + _: &GlobalRuntimeState, + _: Position, + ) -> RhaiResultOf<()> { + Ok(()) + } + + /// Check whether the size of a [`Dynamic`] is within limits. + #[inline(always)] + pub(crate) const fn check_data_size(&self, _: &Dynamic, _: Position) -> RhaiResultOf<()> { + Ok(()) + } + + /// Check a result to ensure that it is valid. + #[inline(always)] + pub(crate) const fn check_return_value( + &self, + result: RhaiResult, + _: Position, + ) -> RhaiResult { + result + } + } +} diff --git a/src/optimizer.rs b/src/optimizer.rs index dea68ea3..20145659 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -175,21 +175,26 @@ fn has_native_fn_override( // First check the global namespace and packages, but skip modules that are standard because // they should never conflict with system functions. - let result = engine + if engine .global_modules .iter() .filter(|m| !m.standard) - .any(|m| m.contains_fn(hash)); + .any(|m| m.contains_fn(hash)) + { + return true; + } - #[cfg(not(feature = "no_module"))] // Then check sub-modules - let result = result - || engine - .global_sub_modules - .values() - .any(|m| m.contains_qualified_fn(hash)); + #[cfg(not(feature = "no_module"))] + if engine + .global_sub_modules + .values() + .any(|m| m.contains_qualified_fn(hash)) + { + return true; + } - result + false } /// Optimize a block of [statements][Stmt]. From 3e46263347ba18732eabad04c9fbe521bacca46c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 16 Oct 2022 12:07:47 +0800 Subject: [PATCH 03/22] Fix tests. --- tests/switch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/switch.rs b/tests/switch.rs index 8008ccf2..2a4aed98 100644 --- a/tests/switch.rs +++ b/tests/switch.rs @@ -52,7 +52,7 @@ fn test_switch() -> Result<(), Box> { 123 ); assert_eq!( - engine.eval::("let x = timestamp(); switch x { 1 => 123, _ => 42 }")?, + engine.eval_with_scope::(&mut scope, "switch x { 424242 => 123, _ => 42 }")?, 42 ); assert_eq!( From c5a111ddb2498f4552e625a0bc833eaf0a887004 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 17 Oct 2022 11:54:25 +0800 Subject: [PATCH 04/22] Set getrandom/js with wasm-bindgen or stdweb --- Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e1f3bd4e..d52f8b30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ serde = { version = "1.0", default-features = false, features = ["derive", "allo serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true } unicode-xid = { version = "0.2", default-features = false, optional = true } rust_decimal = { version = "1.16", default-features = false, features = ["maths"], optional = true } +getrandom = { version = "0.2", optional = true } rustyline = { version = "10", optional = true } [dev-dependencies] @@ -68,8 +69,8 @@ serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde f no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "hashbrown", "no_time"] # compiling for WASM -wasm-bindgen = ["instant/wasm-bindgen"] -stdweb = ["instant/stdweb"] +wasm-bindgen = ["getrandom/js", "instant/wasm-bindgen"] +stdweb = ["getrandom/js", "instant/stdweb"] # compiling bin tools bin-features = ["decimal", "metadata", "serde", "debugging", "rustyline"] From 4642895de8b44e81e8ae085af6e399fb3b6bae19 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 18 Oct 2022 15:44:24 +0800 Subject: [PATCH 05/22] Change loops to iterators. --- src/api/definitions/mod.rs | 20 +++-- src/api/json.rs | 5 +- src/eval/expr.rs | 161 ++++++++++++++++--------------------- src/eval/stmt.rs | 134 +++++++++++++----------------- 4 files changed, 137 insertions(+), 183 deletions(-) diff --git a/src/api/definitions/mod.rs b/src/api/definitions/mod.rs index 7be44611..18d9c992 100644 --- a/src/api/definitions/mod.rs +++ b/src/api/definitions/mod.rs @@ -440,18 +440,16 @@ impl Module { } first = false; - if f.access == FnAccess::Private { - continue; + if f.access != FnAccess::Private { + #[cfg(not(feature = "no_custom_syntax"))] + let operator = def.engine.custom_keywords.contains_key(&f.name) + || (!f.name.contains('$') && !is_valid_function_name(&f.name)); + + #[cfg(feature = "no_custom_syntax")] + let operator = !f.name.contains('$') && !is_valid_function_name(&f.name); + + f.write_definition(writer, def, operator)?; } - - #[cfg(not(feature = "no_custom_syntax"))] - let operator = def.engine.custom_keywords.contains_key(&f.name) - || (!f.name.contains('$') && !is_valid_function_name(&f.name)); - - #[cfg(feature = "no_custom_syntax")] - let operator = !f.name.contains('$') && !is_valid_function_name(&f.name); - - f.write_definition(writer, def, operator)?; } Ok(()) diff --git a/src/api/json.rs b/src/api/json.rs index 24069fdd..c22d682c 100644 --- a/src/api/json.rs +++ b/src/api/json.rs @@ -166,10 +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)); - continue; - } - - if value.is::<()>() { + } else if value.is::<()>() { result.push_str("null"); } else { write!(result, "{:?}", value).unwrap(); diff --git a/src/eval/expr.rs b/src/eval/expr.rs index c09b48a3..d86c3da6 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -379,114 +379,93 @@ impl Engine { Expr::InterpolatedString(x, _) => { let mut concat = self.get_interned_string("").into(); let target = &mut concat; - let mut result = Ok(Dynamic::UNIT); let mut op_info = OpAssignment::new_op_assignment(OP_CONCAT, Position::NONE); let root = ("", Position::NONE); - for expr in &**x { - let item = - match self.eval_expr(scope, global, caches, lib, this_ptr, expr, level) { - Ok(r) => r, - err => { - result = err; - break; - } - }; + let result = x + .iter() + .try_for_each(|expr| { + let item = + self.eval_expr(scope, global, caches, lib, this_ptr, expr, level)?; - op_info.pos = expr.start_position(); + op_info.pos = expr.start_position(); - if let Err(err) = self.eval_op_assignment( - global, caches, lib, &op_info, target, root, item, level, - ) { - result = Err(err); - break; - } - } + self.eval_op_assignment( + global, caches, lib, &op_info, target, root, item, level, + ) + }) + .map(|_| concat.take_or_clone()); - self.check_return_value( - result.map(|_| concat.take_or_clone()), - expr.start_position(), - ) + self.check_return_value(result, expr.start_position()) } #[cfg(not(feature = "no_index"))] Expr::Array(x, ..) => { - let mut array = crate::Array::with_capacity(x.len()); - let mut result = Ok(Dynamic::UNIT); - #[cfg(not(feature = "unchecked"))] let mut total_data_sizes = (0, 0, 0); - for item_expr in &**x { - let value = match self - .eval_expr(scope, global, caches, lib, this_ptr, item_expr, level) - { - Ok(r) => r.flatten(), - err => { - result = err; - break; - } - }; + x.iter() + .try_fold( + crate::Array::with_capacity(x.len()), + |mut array, item_expr| { + let value = self + .eval_expr(scope, global, caches, lib, this_ptr, item_expr, level)? + .flatten(); - #[cfg(not(feature = "unchecked"))] - if self.has_data_size_limit() { - let val_sizes = Self::calc_data_sizes(&value, true); + #[cfg(not(feature = "unchecked"))] + if self.has_data_size_limit() { + let val_sizes = Self::calc_data_sizes(&value, true); - total_data_sizes = ( - total_data_sizes.0 + val_sizes.0, - total_data_sizes.1 + val_sizes.1, - total_data_sizes.2 + val_sizes.2, - ); - self.raise_err_if_over_data_size_limit( - total_data_sizes, - item_expr.position(), - )?; - } + total_data_sizes = ( + total_data_sizes.0 + val_sizes.0, + total_data_sizes.1 + val_sizes.1, + total_data_sizes.2 + val_sizes.2, + ); + self.raise_err_if_over_data_size_limit( + total_data_sizes, + item_expr.position(), + )?; + } - array.push(value); - } + array.push(value); - result.map(|_| array.into()) + Ok(array) + }, + ) + .map(Into::into) } #[cfg(not(feature = "no_object"))] Expr::Map(x, ..) => { - let mut map = x.1.clone(); - let mut result = Ok(Dynamic::UNIT); - #[cfg(not(feature = "unchecked"))] let mut total_data_sizes = (0, 0, 0); - for (key, value_expr) in &x.0 { - let value = match self - .eval_expr(scope, global, caches, lib, this_ptr, value_expr, level) - { - Ok(r) => r.flatten(), - err => { - result = err; - break; + x.0.iter() + .try_fold(x.1.clone(), |mut map, (key, value_expr)| { + let value = self + .eval_expr(scope, global, caches, lib, this_ptr, value_expr, level)? + .flatten(); + + #[cfg(not(feature = "unchecked"))] + if self.has_data_size_limit() { + let delta = Self::calc_data_sizes(&value, true); + total_data_sizes = ( + total_data_sizes.0 + delta.0, + total_data_sizes.1 + delta.1, + total_data_sizes.2 + delta.2, + ); + self.raise_err_if_over_data_size_limit( + total_data_sizes, + value_expr.position(), + )?; } - }; - #[cfg(not(feature = "unchecked"))] - if self.has_data_size_limit() { - let delta = Self::calc_data_sizes(&value, true); - total_data_sizes = ( - total_data_sizes.0 + delta.0, - total_data_sizes.1 + delta.1, - total_data_sizes.2 + delta.2, - ); - self.raise_err_if_over_data_size_limit( - total_data_sizes, - value_expr.position(), - )?; - } + *map.get_mut(key.as_str()).unwrap() = value; - *map.get_mut(key.as_str()).unwrap() = value; - } - - result.map(|_| map.into()) + Ok(map) + }) + .map(Into::into) } Expr::And(x, ..) => { @@ -498,17 +477,17 @@ impl Engine { }) }); - if let Ok(true) = lhs { - self.eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) + match lhs { + Ok(true) => self + .eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) .and_then(|v| { v.as_bool() .map_err(|typ| { self.make_type_mismatch_err::(typ, x.rhs.position()) }) .map(Into::into) - }) - } else { - lhs.map(Into::into) + }), + _ => lhs.map(Into::into), } } @@ -521,17 +500,17 @@ impl Engine { }) }); - if let Ok(false) = lhs { - self.eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) + match lhs { + Ok(false) => self + .eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) .and_then(|v| { v.as_bool() .map_err(|typ| { self.make_type_mismatch_err::(typ, x.rhs.position()) }) .map(Into::into) - }) - } else { - lhs.map(Into::into) + }), + _ => lhs.map(Into::into), } } @@ -542,7 +521,7 @@ impl Engine { Ok(value) if value.is::<()>() => { self.eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level) } - Ok(_) | Err(_) => lhs, + _ => lhs, } } diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 77e39dea..387b434e 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -48,13 +48,11 @@ impl Engine { global.scope_level += 1; } - let mut result = Ok(Dynamic::UNIT); - - for stmt in statements { + let result = statements.iter().try_fold(Dynamic::UNIT, |_, stmt| { #[cfg(not(feature = "no_module"))] let imports_len = global.num_imports(); - result = self.eval_stmt( + let result = self.eval_stmt( scope, global, caches, @@ -63,11 +61,7 @@ impl Engine { stmt, restore_orig_state, level, - ); - - if result.is_err() { - break; - } + )?; #[cfg(not(feature = "no_module"))] if matches!(stmt, Stmt::Import(..)) { @@ -92,7 +86,9 @@ impl Engine { } } } - } + + Ok(result) + }); // If imports list is modified, pop the functions lookup cache caches.rewind_fn_resolution_caches(orig_fn_resolution_caches_len); @@ -429,16 +425,11 @@ impl Engine { }; match cond_result { - Ok(true) => { - result = Ok(Some(&block.expr)); - break; - } - Ok(false) => (), - _ => { - result = cond_result.map(|_| None); - break; - } + Ok(true) => result = Ok(Some(&block.expr)), + Ok(false) => continue, + _ => result = cond_result.map(|_| None), } + break; } result @@ -469,7 +460,6 @@ impl Engine { Ok(false) => continue, _ => result = cond_result.map(|_| None), } - break; } @@ -626,68 +616,58 @@ impl Engine { scope.push(var_name.name.clone(), ()); let index = scope.len() - 1; - let mut loop_result = Ok(Dynamic::UNIT); + let loop_result = func(iter_obj) + .enumerate() + .try_for_each(|(x, iter_value)| { + // Increment counter + if counter_index < usize::MAX { + // As the variable increments from 0, this should always work + // since any overflow will first be caught below. + let index_value = x as INT; - for (x, iter_value) in func(iter_obj).enumerate() { - // Increment counter - if counter_index < usize::MAX { - // As the variable increments from 0, this should always work - // since any overflow will first be caught below. - let index_value = x as INT; - - #[cfg(not(feature = "unchecked"))] - if index_value > crate::MAX_USIZE_INT { - loop_result = Err(ERR::ErrorArithmetic( - format!("for-loop counter overflow: {x}"), - counter.pos, - ) - .into()); - break; - } - - *scope.get_mut_by_index(counter_index).write_lock().unwrap() = - Dynamic::from_int(index_value); - } - - let value = match iter_value { - Ok(v) => v.flatten(), - Err(err) => { - loop_result = Err(err.fill_position(expr.position())); - break; - } - }; - - *scope.get_mut_by_index(index).write_lock().unwrap() = value; - - if let Err(err) = self.track_operation(global, statements.position()) { - loop_result = Err(err); - break; - } - - if statements.is_empty() { - continue; - } - - let result = self.eval_stmt_block( - scope, global, caches, lib, this_ptr, statements, true, level, - ); - - match result { - Ok(_) => (), - Err(err) => match *err { - ERR::LoopBreak(false, ..) => (), - ERR::LoopBreak(true, ..) => break, - _ => { - loop_result = Err(err); - break; + #[cfg(not(feature = "unchecked"))] + if index_value > crate::MAX_USIZE_INT { + return Err(ERR::ErrorArithmetic( + format!("for-loop counter overflow: {x}"), + counter.pos, + ) + .into()); } - }, - } - } + + *scope.get_mut_by_index(counter_index).write_lock().unwrap() = + Dynamic::from_int(index_value); + } + + let value = match iter_value { + Ok(v) => v.flatten(), + Err(err) => return Err(err.fill_position(expr.position())), + }; + + *scope.get_mut_by_index(index).write_lock().unwrap() = value; + + self.track_operation(global, statements.position())?; + + if statements.is_empty() { + return Ok(()); + } + + self.eval_stmt_block( + scope, global, caches, lib, this_ptr, statements, true, level, + ) + .map(|_| ()) + .or_else(|err| match *err { + ERR::LoopBreak(false, ..) => Ok(()), + _ => Err(err), + }) + }) + .or_else(|err| match *err { + ERR::LoopBreak(true, ..) => Ok(()), + _ => Err(err), + }); scope.rewind(orig_scope_len); - loop_result + loop_result.map(|_| Dynamic::UNIT) } else { Err(ERR::ErrorFor(expr.start_position()).into()) } From d49dca8a297b723d3355cecf4e70e0452057d569 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 18 Oct 2022 16:54:26 +0800 Subject: [PATCH 06/22] Fix test. --- src/eval/stmt.rs | 150 +++++++++++++++++++++++------------------------ tests/time.rs | 3 +- 2 files changed, 75 insertions(+), 78 deletions(-) diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 387b434e..c3588754 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -358,24 +358,14 @@ impl Engine { }); match guard_val { - Ok(true) => { - if if_block.is_empty() { - Ok(Dynamic::UNIT) - } else { - self.eval_stmt_block( - scope, global, caches, lib, this_ptr, if_block, true, level, - ) - } - } - Ok(false) => { - if else_block.is_empty() { - Ok(Dynamic::UNIT) - } else { - self.eval_stmt_block( - scope, global, caches, lib, this_ptr, else_block, true, level, - ) - } - } + Ok(true) if if_block.is_empty() => Ok(Dynamic::UNIT), + Ok(true) => self.eval_stmt_block( + scope, global, caches, lib, this_ptr, if_block, true, level, + ), + Ok(false) if else_block.is_empty() => Ok(Dynamic::UNIT), + Ok(false) => self.eval_stmt_block( + scope, global, caches, lib, this_ptr, else_block, true, level, + ), err => err.map(Into::into), } } @@ -490,41 +480,15 @@ impl Engine { } // Loop - Stmt::While(x, ..) if matches!(x.0, Expr::Unit(..)) => loop { + Stmt::While(x, ..) if matches!(x.0, Expr::Unit(..) | Expr::BoolConstant(true, ..)) => { let (.., body) = &**x; if body.is_empty() { - self.track_operation(global, body.position())?; - } else { - match self - .eval_stmt_block(scope, global, caches, lib, this_ptr, body, true, level) - { - Ok(_) => (), - Err(err) => match *err { - ERR::LoopBreak(false, ..) => (), - ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT), - _ => break Err(err), - }, + loop { + self.track_operation(global, body.position())?; } - } - }, - - // While loop - Stmt::While(x, ..) => loop { - let (expr, body) = &**x; - - let condition = self - .eval_expr(scope, global, caches, lib, this_ptr, expr, level) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::(typ, expr.position()) - }) - }); - - match condition { - Ok(false) => break Ok(Dynamic::UNIT), - Ok(true) if body.is_empty() => (), - Ok(true) => { + } else { + loop { match self.eval_stmt_block( scope, global, caches, lib, this_ptr, body, true, level, ) { @@ -536,42 +500,76 @@ impl Engine { }, } } - err => break err.map(|_| Dynamic::UNIT), } - }, + } + + // While loop + Stmt::While(x, ..) => { + let (expr, body) = &**x; + + loop { + let condition = self + .eval_expr(scope, global, caches, lib, this_ptr, expr, level) + .and_then(|v| { + v.as_bool().map_err(|typ| { + self.make_type_mismatch_err::(typ, expr.position()) + }) + }); + + match condition { + Ok(false) => break Ok(Dynamic::UNIT), + Ok(true) if body.is_empty() => (), + Ok(true) => { + match self.eval_stmt_block( + scope, global, caches, lib, this_ptr, body, true, level, + ) { + Ok(_) => (), + Err(err) => match *err { + ERR::LoopBreak(false, ..) => (), + ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT), + _ => break Err(err), + }, + } + } + err => break err.map(|_| Dynamic::UNIT), + } + } + } // Do loop - Stmt::Do(x, options, ..) => loop { + Stmt::Do(x, options, ..) => { let (expr, body) = &**x; let is_while = !options.contains(ASTFlags::NEGATED); - if !body.is_empty() { - match self - .eval_stmt_block(scope, global, caches, lib, this_ptr, body, true, level) - { + loop { + if !body.is_empty() { + match self.eval_stmt_block( + scope, global, caches, lib, this_ptr, body, true, level, + ) { + Ok(_) => (), + Err(err) => match *err { + ERR::LoopBreak(false, ..) => continue, + ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT), + _ => break Err(err), + }, + } + } + + let condition = self + .eval_expr(scope, global, caches, lib, this_ptr, expr, level) + .and_then(|v| { + v.as_bool().map_err(|typ| { + self.make_type_mismatch_err::(typ, expr.position()) + }) + }); + + match condition { + Ok(condition) if condition ^ is_while => break Ok(Dynamic::UNIT), Ok(_) => (), - Err(err) => match *err { - ERR::LoopBreak(false, ..) => continue, - ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT), - _ => break Err(err), - }, + err => break err.map(|_| Dynamic::UNIT), } } - - let condition = self - .eval_expr(scope, global, caches, lib, this_ptr, expr, level) - .and_then(|v| { - v.as_bool().map_err(|typ| { - self.make_type_mismatch_err::(typ, expr.position()) - }) - }); - - match condition { - Ok(condition) if condition ^ is_while => break Ok(Dynamic::UNIT), - Ok(_) => (), - err => break err.map(|_| Dynamic::UNIT), - } - }, + } // For loop Stmt::For(x, ..) => { diff --git a/tests/time.rs b/tests/time.rs index 7a35da15..5434faa5 100644 --- a/tests/time.rs +++ b/tests/time.rs @@ -1,5 +1,4 @@ -#![cfg(not(feature = "no_std"))] -#![cfg(not(target_family = "wasm"))] +#![cfg(not(feature = "no_time"))] use rhai::{Engine, EvalAltResult}; From 3073f815ec0749e9417078f549680b52e9320264 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 19 Oct 2022 12:51:52 +0800 Subject: [PATCH 07/22] Add unsafe handle test. --- tests/plugins.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/tests/plugins.rs b/tests/plugins.rs index 5fe58fdc..dfb0c9c5 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -2,10 +2,10 @@ #![cfg(not(feature = "no_module"))] use rhai::plugin::*; -use rhai::{Engine, EvalAltResult, INT}; +use rhai::{Engine, EvalAltResult, Scope, INT}; mod test { - use rhai::plugin::*; + use super::*; #[export_module] pub mod special_array_package { @@ -170,3 +170,56 @@ fn test_plugins_parameters() -> Result<(), Box> { Ok(()) } + +#[cfg(target_pointer_width = "64")] +mod handle { + use super::*; + + #[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] + pub struct WorldHandle(usize); + pub type World = Vec; + + impl From<&mut World> for WorldHandle { + fn from(world: &mut World) -> Self { + Self::new(world) + } + } + + impl AsMut for WorldHandle { + fn as_mut(&mut self) -> &mut World { + unsafe { std::mem::transmute(self.0) } + } + } + + impl WorldHandle { + pub fn new(world: &mut World) -> Self { + Self(unsafe { std::mem::transmute(world) }) + } + } + + #[export_module] + pub mod handle_module { + pub type Handle = WorldHandle; + + #[rhai_fn(get = "len")] + pub fn len(world: &mut Handle) -> INT { + world.as_mut().len() as INT + } + } + + #[test] + fn test_module_handle() -> Result<(), Box> { + let mut engine = Engine::new(); + + engine.register_global_module(exported_module!(handle_module).into()); + + let mut scope = Scope::new(); + + let world: &mut World = &mut vec![42]; + scope.push("world", WorldHandle::from(world)); + + assert_eq!(engine.eval_with_scope::(&mut scope, "world.len")?, 1); + + Ok(()) + } +} From 9f36afc84cdb9a711247a9877a0081393b9269a6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 20 Oct 2022 09:53:11 +0800 Subject: [PATCH 08/22] Check result size in op assignment. --- src/eval/stmt.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index c3588754..fac3c749 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -160,7 +160,7 @@ impl Engine { match self.call_native_fn( global, caches, lib, op_assign, hash, args, true, true, *op_pos, level, ) { - Ok(_) => self.check_data_size(args[0], root.1)?, + Ok(_) => (), Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) => { // Expand to `var = var op rhs` @@ -174,6 +174,8 @@ impl Engine { } Err(err) => return Err(err), } + + self.check_data_size(args[0], root.1)?; } else { // Normal assignment *target.write_lock::().unwrap() = new_val; From 988222418eff98f29645307d67dbd409bfd9376f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 20 Oct 2022 11:38:54 +0800 Subject: [PATCH 09/22] Check data size at built-in. --- src/eval/data_check.rs | 16 ++- src/eval/expr.rs | 15 +-- src/eval/stmt.rs | 6 +- src/func/builtin.rs | 223 +++++++++++++++++++++++++++++------- src/packages/array_basic.rs | 2 +- src/packages/blob_basic.rs | 8 +- 6 files changed, 202 insertions(+), 68 deletions(-) diff --git a/src/eval/data_check.rs b/src/eval/data_check.rs index bc8953ca..9195f632 100644 --- a/src/eval/data_check.rs +++ b/src/eval/data_check.rs @@ -73,14 +73,15 @@ impl Engine { pub(crate) fn raise_err_if_over_data_size_limit( &self, (_arr, _map, s): (usize, usize, usize), - pos: Position, ) -> RhaiResultOf<()> { if self .limits .max_string_size .map_or(false, |max| s > max.get()) { - return Err(ERR::ErrorDataTooLarge("Length of string".to_string(), pos).into()); + return Err( + ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(), + ); } #[cfg(not(feature = "no_index"))] @@ -89,7 +90,9 @@ impl Engine { .max_array_size .map_or(false, |max| _arr > max.get()) { - return Err(ERR::ErrorDataTooLarge("Size of array".to_string(), pos).into()); + return Err( + ERR::ErrorDataTooLarge("Size of array/BLOB".to_string(), Position::NONE).into(), + ); } #[cfg(not(feature = "no_object"))] @@ -98,7 +101,9 @@ impl Engine { .max_map_size .map_or(false, |max| _map > max.get()) { - return Err(ERR::ErrorDataTooLarge("Size of object map".to_string(), pos).into()); + return Err( + ERR::ErrorDataTooLarge("Size of object map".to_string(), Position::NONE).into(), + ); } Ok(()) @@ -113,7 +118,8 @@ impl Engine { let sizes = Self::calc_data_sizes(value, true); - self.raise_err_if_over_data_size_limit(sizes, pos) + self.raise_err_if_over_data_size_limit(sizes) + .map_err(|err| err.fill_position(pos)) } /// Raise an error if the size of a [`Dynamic`] is out of limits (if any). diff --git a/src/eval/expr.rs b/src/eval/expr.rs index d86c3da6..285f5e23 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -252,8 +252,7 @@ impl Engine { { // Built-in found let context = (self, name, None, &*global, lib, pos, level + 1).into(); - let result = func(context, operands); - return self.check_return_value(result, pos); + return func(context, operands); } return self @@ -422,10 +421,8 @@ impl Engine { total_data_sizes.1 + val_sizes.1, total_data_sizes.2 + val_sizes.2, ); - self.raise_err_if_over_data_size_limit( - total_data_sizes, - item_expr.position(), - )?; + self.raise_err_if_over_data_size_limit(total_data_sizes) + .map_err(|err| err.fill_position(item_expr.position()))?; } array.push(value); @@ -455,10 +452,8 @@ impl Engine { total_data_sizes.1 + delta.1, total_data_sizes.2 + delta.2, ); - self.raise_err_if_over_data_size_limit( - total_data_sizes, - value_expr.position(), - )?; + self.raise_err_if_over_data_size_limit(total_data_sizes) + .map_err(|err| err.fill_position(value_expr.position()))?; } *map.get_mut(key.as_str()).unwrap() = value; diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index fac3c749..4e26d7d3 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -146,11 +146,7 @@ impl Engine { // Built-in found let op = op_assign.literal_syntax(); let context = (self, op, None, &*global, lib, *op_pos, level).into(); - let result = func(context, args).map(|_| ()); - - self.check_data_size(args[0], root.1)?; - - return result; + return func(context, args).map(|_| ()); } } diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 6b16366f..0803d35e 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -196,7 +196,19 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type1 == TypeId::of::() { return match op { - Token::Plus => Some(impl_op!(ImmutableString + ImmutableString)), + Token::Plus => Some(|_ctx, args| { + let s1 = &*args[0].read_lock::().expect(BUILTIN); + let s2 = &*args[1].read_lock::().expect(BUILTIN); + + #[cfg(not(feature = "unchecked"))] + if !s1.is_empty() && !s2.is_empty() { + let total_len = s1.len() + s2.len(); + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, total_len))?; + } + + Ok((s1 + s2).into()) + }), Token::Minus => Some(impl_op!(ImmutableString - ImmutableString)), Token::EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)), Token::NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)), @@ -210,10 +222,17 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type1 == TypeId::of::() { return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let x = args[0].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); - Ok(format!("{x}{y}").into()) + + let result = format!("{x}{y}"); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, result.len()))?; + + Ok(result.into()) }), Token::EqualsTo => Some(impl_op!(char => as_char == as_char)), Token::NotEqualsTo => Some(impl_op!(char => as_char != as_char)), @@ -230,7 +249,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< use crate::Blob; return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let blob1 = &*args[0].read_lock::().expect(BUILTIN); let blob2 = &*args[1].read_lock::().expect(BUILTIN); @@ -239,6 +258,13 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } else if blob1.is_empty() { blob2.clone() } else { + #[cfg(not(feature = "unchecked"))] + _ctx.engine().raise_err_if_over_data_size_limit(( + blob1.len() + blob2.len(), + 0, + 0, + ))?; + let mut blob = blob1.clone(); blob.extend(blob2); blob @@ -357,10 +383,16 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let x = args[0].as_char().expect(BUILTIN); let y = &*args[1].read_lock::().expect(BUILTIN); - Ok(format!("{x}{y}").into()) + let result = format!("{x}{y}"); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, result.len()))?; + + Ok(result.into()) }), Token::EqualsTo => Some(impl_op!(get_s1s2(==))), Token::NotEqualsTo => Some(impl_op!(get_s1s2(!=))), @@ -383,10 +415,16 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let x = &*args[0].read_lock::().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); - Ok((x + y).into()) + let result = x + y; + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, result.len()))?; + + Ok(result.into()) }), Token::Minus => Some(|_, args| { let x = &*args[0].read_lock::().expect(BUILTIN); @@ -436,10 +474,18 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type2 == TypeId::of::() { return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let mut buf = [0_u8; 4]; let mut blob = args[0].read_lock::().expect(BUILTIN).clone(); - let x = args[1].as_char().expect("`char`").encode_utf8(&mut buf); + let x = args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine().raise_err_if_over_data_size_limit(( + blob.len() + x.len(), + 0, + 0, + ))?; + blob.extend(x.as_bytes()); Ok(Dynamic::from_blob(blob)) }), @@ -638,10 +684,18 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt if type1 == TypeId::of::() { return match op { - Token::PlusAssign => Some(|_, args| { + Token::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::(); + + #[cfg(not(feature = "unchecked"))] + if !x.is_empty() && !y.is_empty() { + let total_len = x.len() + y.len(); + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, total_len))?; + } + Ok((*x += y).into()) }), Token::MinusAssign => Some(|_, args| { @@ -654,14 +708,56 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt }; } + #[cfg(not(feature = "no_index"))] + if type1 == TypeId::of::() { + use crate::packages::array_basic::array_functions::*; + use crate::Array; + + return match op { + Token::PlusAssign => Some(|_ctx, args| { + let x = std::mem::take(args[1]).cast::(); + + if x.is_empty() { + return Ok(Dynamic::UNIT); + } + + let _array_was_empty = { + let array = &mut &mut *args[0].write_lock::().expect(BUILTIN); + let array_is_empty = array.is_empty(); + append(array, x); + array_is_empty + }; + + #[cfg(not(feature = "unchecked"))] + if !_array_was_empty { + _ctx.engine().check_data_size( + &*args[0].read_lock().expect(BUILTIN), + crate::Position::NONE, + )?; + } + + Ok(Dynamic::UNIT) + }), + _ => None, + }; + } + #[cfg(not(feature = "no_index"))] if type1 == TypeId::of::() { use crate::Blob; return match op { - Token::PlusAssign => Some(|_, args| { + Token::PlusAssign => Some(|_ctx, args| { let blob2 = std::mem::take(args[1]).cast::(); let blob1 = &mut *args[0].write_lock::().expect(BUILTIN); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine().raise_err_if_over_data_size_limit(( + blob1.len() + blob2.len(), + 0, + 0, + ))?; + Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into()) }), _ => None, @@ -736,7 +832,17 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // string op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(impl_op!(ImmutableString += as_char as char)), + Token::PlusAssign => Some(|_ctx, args| { + let mut buf = [0_u8; 4]; + let ch = &*args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf); + let mut x = args[0].write_lock::().expect(BUILTIN); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, x.len() + ch.len()))?; + + Ok((*x += ch).into()) + }), Token::MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)), _ => None, }; @@ -744,17 +850,27 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // char op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_, args| { - let mut ch = args[0].as_char().expect(BUILTIN).to_string(); - ch.push_str( - args[1] - .read_lock::() - .expect(BUILTIN) - .as_str(), - ); + Token::PlusAssign => Some(|_ctx, args| { + let ch = { + let s = &*args[1].read_lock::().expect(BUILTIN); - let mut x = args[0].write_lock::().expect(BUILTIN); - Ok((*x = ch.into()).into()) + if s.is_empty() { + return Ok(Dynamic::UNIT); + } + + let mut ch = args[0].as_char().expect(BUILTIN).to_string(); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, ch.len() + s.len()))?; + + ch.push_str(s); + ch + }; + + *args[0].write_lock::().expect(BUILTIN) = ch.into(); + + Ok(Dynamic::UNIT) }), _ => None, }; @@ -766,21 +882,21 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt use crate::packages::array_basic::array_functions::*; use crate::Array; - if type2 == TypeId::of::() { - return match op { - Token::PlusAssign => Some(|_, args| { - let array2 = std::mem::take(args[1]).cast::(); - let array1 = &mut *args[0].write_lock::().expect(BUILTIN); - Ok(append(array1, array2).into()) - }), - _ => None, - }; - } return match op { - Token::PlusAssign => Some(|_, args| { - let x = std::mem::take(args[1]); - let array = &mut *args[0].write_lock::().expect(BUILTIN); - Ok(push(array, x).into()) + Token::PlusAssign => Some(|_ctx, args| { + { + let x = std::mem::take(args[1]); + let array = &mut *args[0].write_lock::().expect(BUILTIN); + push(array, x); + } + + #[cfg(not(feature = "unchecked"))] + _ctx.engine().check_data_size( + &*args[0].read_lock().expect(BUILTIN), + crate::Position::NONE, + )?; + + Ok(Dynamic::UNIT) }), _ => None, }; @@ -793,9 +909,14 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= int if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_, args| { - let x = args[1].as_int().expect("`INT`"); + Token::PlusAssign => Some(|_ctx, args| { + let x = args[1].as_int().expect(BUILTIN); let blob = &mut *args[0].write_lock::().expect(BUILTIN); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?; + Ok(crate::packages::blob_basic::blob_functions::push(blob, x).into()) }), _ => None, @@ -805,9 +926,14 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_, args| { - let x = args[1].as_char().expect("`char`"); + Token::PlusAssign => Some(|_ctx, args| { + let x = args[1].as_char().expect(BUILTIN); let blob = &mut *args[0].write_lock::().expect(BUILTIN); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?; + Ok(crate::packages::blob_basic::blob_functions::append_char(blob, x).into()) }), _ => None, @@ -817,9 +943,22 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_, args| { + Token::PlusAssign => Some(|_ctx, args| { let s = std::mem::take(args[1]).cast::(); + + 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(), + 0, + 0, + ))?; + Ok(crate::packages::blob_basic::blob_functions::append_str(blob, &s).into()) }), _ => None, diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 2c5bd716..098b7f7d 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -259,7 +259,7 @@ pub mod array_functions { s1 += s2; _ctx.engine() - .raise_err_if_over_data_size_limit((a1, m1, s1), Position::NONE)?; + .raise_err_if_over_data_size_limit((a1, m1, s1))?; guard.push(item.clone()); arr_len += 1; diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index 4ddd68b6..2e51b108 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -79,11 +79,9 @@ pub mod blob_functions { let _ctx = ctx; // Check if blob will be over max size limit - if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() { - return Err( - crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(), - ); - } + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((len, 0, 0))?; let mut blob = Blob::new(); blob.resize(len, (value & 0x0000_00ff) as u8); From f8888c83e7c0d320e46c7eef4a9f0d8abca8cc31 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 20 Oct 2022 13:01:36 +0800 Subject: [PATCH 10/22] Fix test. --- tests/plugins.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/plugins.rs b/tests/plugins.rs index dfb0c9c5..f5c8d966 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -218,6 +218,7 @@ mod handle { let world: &mut World = &mut vec![42]; scope.push("world", WorldHandle::from(world)); + #[cfg(not(feature = "no_object"))] assert_eq!(engine.eval_with_scope::(&mut scope, "world.len")?, 1); Ok(()) From c24794187f745cd0ee2c086bc8d36ceaa4819845 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 20 Oct 2022 15:31:57 +0800 Subject: [PATCH 11/22] Reduce unnecessary generics. --- src/api/custom_syntax.rs | 10 ++++++++- src/ast/ast.rs | 27 +++++++++++++++++++++++ src/ast/ident.rs | 9 ++++++++ src/ast/stmt.rs | 9 ++++++++ src/eval/expr.rs | 2 +- src/eval/target.rs | 13 +++++++++++- src/func/native.rs | 46 +++++++++++++++++----------------------- src/optimizer.rs | 2 +- 8 files changed, 88 insertions(+), 30 deletions(-) diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index 7ee568c0..868b9931 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -10,9 +10,9 @@ use crate::{ reify, Dynamic, Engine, EvalContext, Identifier, ImmutableString, LexError, Position, RhaiResult, StaticVec, }; -use std::ops::Deref; #[cfg(feature = "no_std")] use std::prelude::v1::*; +use std::{borrow::Borrow, ops::Deref}; /// Collection of special markers for custom syntax definition. pub mod markers { @@ -145,6 +145,14 @@ impl Expression<'_> { } } +impl Borrow for Expression<'_> { + #[inline(always)] + #[must_use] + fn borrow(&self) -> &Expr { + self.0 + } +} + impl AsRef for Expression<'_> { #[inline(always)] #[must_use] diff --git a/src/ast/ast.rs b/src/ast/ast.rs index d0b88e26..98ed86b3 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -5,6 +5,7 @@ use crate::{Dynamic, FnNamespace, Identifier, Position}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ + borrow::Borrow, fmt, hash::Hash, ops::{Add, AddAssign}, @@ -919,6 +920,14 @@ impl> AddAssign for AST { } } +impl Borrow<[Stmt]> for AST { + #[inline(always)] + #[must_use] + fn borrow(&self) -> &[Stmt] { + self.statements() + } +} + impl AsRef<[Stmt]> for AST { #[inline(always)] #[must_use] @@ -927,6 +936,15 @@ impl AsRef<[Stmt]> for AST { } } +#[cfg(not(feature = "no_function"))] +impl Borrow for AST { + #[inline(always)] + #[must_use] + fn borrow(&self) -> &crate::Module { + &self.shared_lib() + } +} + #[cfg(not(feature = "no_function"))] impl AsRef for AST { #[inline(always)] @@ -936,6 +954,15 @@ impl AsRef for AST { } } +#[cfg(not(feature = "no_function"))] +impl Borrow> for AST { + #[inline(always)] + #[must_use] + fn borrow(&self) -> &crate::Shared { + self.shared_lib() + } +} + #[cfg(not(feature = "no_function"))] impl AsRef> for AST { #[inline(always)] diff --git a/src/ast/ident.rs b/src/ast/ident.rs index e8eb6626..ffc5f968 100644 --- a/src/ast/ident.rs +++ b/src/ast/ident.rs @@ -4,6 +4,7 @@ use crate::{ImmutableString, Position}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ + borrow::Borrow, fmt, hash::Hash, ops::{Deref, DerefMut}, @@ -28,6 +29,14 @@ impl fmt::Debug for Ident { } } +impl Borrow for Ident { + #[inline(always)] + #[must_use] + fn borrow(&self) -> &str { + self.name.as_ref() + } +} + impl AsRef for Ident { #[inline(always)] #[must_use] diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index a72e2f01..6b15061c 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -7,6 +7,7 @@ use crate::{calc_fn_hash, Position, StaticVec, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ + borrow::Borrow, collections::BTreeMap, fmt, hash::Hash, @@ -443,6 +444,14 @@ impl DerefMut for StmtBlock { } } +impl Borrow<[Stmt]> for StmtBlock { + #[inline(always)] + #[must_use] + fn borrow(&self) -> &[Stmt] { + &self.block + } +} + impl AsRef<[Stmt]> for StmtBlock { #[inline(always)] #[must_use] diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 285f5e23..c8d17896 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -251,7 +251,7 @@ impl Engine { get_builtin_binary_op_fn(operator_token.as_ref().unwrap(), operands[0], operands[1]) { // Built-in found - let context = (self, name, None, &*global, lib, pos, level + 1).into(); + let context = (self, name.as_str(), None, &*global, lib, pos, level + 1).into(); return func(context, operands); } diff --git a/src/eval/target.rs b/src/eval/target.rs index 5a441520..c48aa62a 100644 --- a/src/eval/target.rs +++ b/src/eval/target.rs @@ -2,9 +2,12 @@ use crate::types::dynamic::Variant; use crate::{Dynamic, Position, RhaiResultOf}; -use std::ops::{Deref, DerefMut}; #[cfg(feature = "no_std")] use std::prelude::v1::*; +use std::{ + borrow::Borrow, + ops::{Deref, DerefMut}, +}; // Calculate an offset+len pair given an actual length of the underlying array. // @@ -422,6 +425,14 @@ impl AsRef for Target<'_> { } } +impl Borrow for Target<'_> { + #[inline(always)] + #[must_use] + fn borrow(&self) -> &Dynamic { + self + } +} + impl DerefMut for Target<'_> { #[inline] fn deref_mut(&mut self) -> &mut Dynamic { diff --git a/src/func/native.rs b/src/func/native.rs index b3681e74..707f8f4c 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -81,13 +81,13 @@ pub struct NativeCallContext<'a> { level: usize, } -impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef + 'a + ?Sized> +impl<'a> From<( &'a Engine, - &'a S, - Option<&'a S>, + &'a str, + Option<&'a str>, &'a GlobalRuntimeState<'a>, - &'a M, + &'a [&Module], Position, usize, )> for NativeCallContext<'a> @@ -96,37 +96,35 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef + 'a + ?Sized> fn from( value: ( &'a Engine, - &'a S, - Option<&'a S>, + &'a str, + Option<&'a str>, &'a GlobalRuntimeState, - &'a M, + &'a [&Module], Position, usize, ), ) -> Self { Self { engine: value.0, - fn_name: value.1.as_ref(), - source: value.2.map(<_>::as_ref), + fn_name: value.1, + source: value.2, global: Some(value.3), - lib: value.4.as_ref(), + lib: value.4, pos: value.5, level: value.6, } } } -impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef + 'a + ?Sized> - From<(&'a Engine, &'a S, &'a M)> for NativeCallContext<'a> -{ +impl<'a> From<(&'a Engine, &'a str, &'a [&'a Module])> for NativeCallContext<'a> { #[inline(always)] - fn from(value: (&'a Engine, &'a S, &'a M)) -> Self { + fn from(value: (&'a Engine, &'a str, &'a [&Module])) -> Self { Self { engine: value.0, - fn_name: value.1.as_ref(), + fn_name: value.1, source: None, global: None, - lib: value.2.as_ref(), + lib: value.2, pos: Position::NONE, level: 0, } @@ -142,14 +140,10 @@ impl<'a> NativeCallContext<'a> { )] #[inline(always)] #[must_use] - pub fn new( - engine: &'a Engine, - fn_name: &'a (impl AsRef + 'a + ?Sized), - lib: &'a [&Module], - ) -> Self { + pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self { Self { engine, - fn_name: fn_name.as_ref(), + fn_name, source: None, global: None, lib, @@ -167,8 +161,8 @@ impl<'a> NativeCallContext<'a> { #[must_use] pub fn new_with_all_fields( engine: &'a Engine, - fn_name: &'a (impl AsRef + 'a + ?Sized), - source: Option<&'a (impl AsRef + 'a + ?Sized)>, + fn_name: &'a str, + source: Option<&'a str>, global: &'a GlobalRuntimeState, lib: &'a [&Module], pos: Position, @@ -176,8 +170,8 @@ impl<'a> NativeCallContext<'a> { ) -> Self { Self { engine, - fn_name: fn_name.as_ref(), - source: source.map(<_>::as_ref), + fn_name, + source, global: Some(global), lib, pos, diff --git a/src/optimizer.rs b/src/optimizer.rs index 20145659..42e9dd90 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1191,7 +1191,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { #[cfg(feature = "no_function")] let lib = &[]; - let context = (state.engine, &x.name, lib).into(); + let context = (state.engine, x.name.as_str(), lib).into(); let (first, second) = arg_values.split_first_mut().unwrap(); (f)(context, &mut [ first, &mut second[0] ]).ok() }) { From 2f88757efebf52cb1c4f3bea76e00e8cfcf1ef4b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 21 Oct 2022 19:02:40 +0800 Subject: [PATCH 12/22] Fix builds. --- src/optimizer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/optimizer.rs b/src/optimizer.rs index 42e9dd90..bd51a7c4 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1189,7 +1189,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { #[cfg(not(feature = "no_function"))] let lib = state.lib; #[cfg(feature = "no_function")] - let lib = &[]; + let lib = &[][..]; let context = (state.engine, x.name.as_str(), lib).into(); let (first, second) = arg_values.split_first_mut().unwrap(); From d55b5a91f7b0595324440dc47a61d3f8153ac9d1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 24 Oct 2022 23:33:24 +0800 Subject: [PATCH 13/22] Fill variable index on captured variables in closures. --- src/parser.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 6c100008..cb002908 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1441,7 +1441,7 @@ impl Engine { ..settings }; - let result = self.parse_anon_fn(input, &mut new_state, lib, new_settings); + let result = self.parse_anon_fn(input, &mut new_state, state, lib, new_settings); // Restore parse state state.interned_strings = new_state.interned_strings; @@ -1464,7 +1464,7 @@ impl Engine { // Under Strict Variables mode, this is not allowed. Err(PERR::VariableUndefined(name.to_string()).into_err(*pos)) } else { - Ok::<_, ParseError>(()) + Ok(()) } }, )?; @@ -3655,6 +3655,8 @@ impl Engine { #[cfg(not(feature = "no_closure"))] fn make_curry_from_externals( state: &mut ParseState, + parent: &mut ParseState, + lib: &FnLib, fn_expr: Expr, externals: StaticVec, pos: Position, @@ -3674,12 +3676,14 @@ impl Engine { .iter() .cloned() .map(|crate::ast::Ident { name, pos }| { - #[cfg(not(feature = "no_module"))] - let ns = crate::ast::Namespace::NONE; - #[cfg(feature = "no_module")] - let ns = (); - - Expr::Variable((None, ns, 0, name).into(), None, pos) + let (index, is_func) = parent.access_var(&name, lib, pos); + let idx = match index { + Some(n) if !is_func && n.get() <= u8::MAX as usize => { + NonZeroU8::new(n.get() as u8) + } + _ => None, + }; + Expr::Variable((index, Default::default(), 0, name).into(), idx, pos) }), ); @@ -3719,6 +3723,7 @@ impl Engine { &self, input: &mut TokenStream, state: &mut ParseState, + parent: &mut ParseState, lib: &mut FnLib, settings: ParseSettings, ) -> ParseResult<(Expr, ScriptFnDef)> { @@ -3815,7 +3820,8 @@ impl Engine { let expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), settings.pos); #[cfg(not(feature = "no_closure"))] - let expr = Self::make_curry_from_externals(state, expr, externals, settings.pos); + let expr = + Self::make_curry_from_externals(state, parent, lib, expr, externals, settings.pos); Ok((expr, script)) } From 05b9c83bb889e789e20db0424aedc4d4036443af Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 24 Oct 2022 23:33:33 +0800 Subject: [PATCH 14/22] Reformat. --- src/func/builtin.rs | 433 ++++++++++++++++++++++---------------------- 1 file changed, 220 insertions(+), 213 deletions(-) diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 0803d35e..8ff37e67 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -2,8 +2,10 @@ use super::call::FnCallArgs; use super::native::FnBuiltin; -use crate::tokenizer::Token; -use crate::{Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, INT}; +use crate::tokenizer::{Token, Token::*}; +use crate::{ + Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult, INT, +}; use std::any::TypeId; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -63,6 +65,19 @@ fn is_numeric(type_id: TypeId) -> bool { false } +/// A function that returns `true`. +#[inline(always)] +#[must_use] +fn const_true_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult { + Ok(Dynamic::TRUE) +} +/// A function that returns `false`. +#[inline(always)] +#[must_use] +fn const_false_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult { + Ok(Dynamic::FALSE) +} + /// Build in common binary operator implementations to avoid the cost of calling a registered function. /// /// The return function will be registered as a _method_, so the first parameter cannot be consumed. @@ -131,46 +146,46 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< #[cfg(not(feature = "unchecked"))] match op { - Token::Plus => return Some(impl_op!(INT => add(as_int, as_int))), - Token::Minus => return Some(impl_op!(INT => subtract(as_int, as_int))), - Token::Multiply => return Some(impl_op!(INT => multiply(as_int, as_int))), - Token::Divide => return Some(impl_op!(INT => divide(as_int, as_int))), - Token::Modulo => return Some(impl_op!(INT => modulo(as_int, as_int))), - Token::PowerOf => return Some(impl_op!(INT => power(as_int, as_int))), - Token::RightShift => return Some(impl_op!(INT => shift_right(as_int, as_int))), - Token::LeftShift => return Some(impl_op!(INT => shift_left(as_int, as_int))), + Plus => return Some(impl_op!(INT => add(as_int, as_int))), + Minus => return Some(impl_op!(INT => subtract(as_int, as_int))), + Multiply => return Some(impl_op!(INT => multiply(as_int, as_int))), + Divide => return Some(impl_op!(INT => divide(as_int, as_int))), + Modulo => return Some(impl_op!(INT => modulo(as_int, as_int))), + PowerOf => return Some(impl_op!(INT => power(as_int, as_int))), + RightShift => return Some(impl_op!(INT => shift_right(as_int, as_int))), + LeftShift => return Some(impl_op!(INT => shift_left(as_int, as_int))), _ => (), } #[cfg(feature = "unchecked")] match op { - Token::Plus => return Some(impl_op!(INT => as_int + as_int)), - Token::Minus => return Some(impl_op!(INT => as_int - as_int)), - Token::Multiply => return Some(impl_op!(INT => as_int * as_int)), - Token::Divide => return Some(impl_op!(INT => as_int / as_int)), - Token::Modulo => return Some(impl_op!(INT => as_int % as_int)), - Token::PowerOf => return Some(impl_op!(INT => as_int.pow(as_int as u32))), - Token::RightShift => return Some(impl_op!(INT => as_int >> as_int)), - Token::LeftShift => return Some(impl_op!(INT => as_int << as_int)), + Plus => return Some(impl_op!(INT => as_int + as_int)), + Minus => return Some(impl_op!(INT => as_int - as_int)), + Multiply => return Some(impl_op!(INT => as_int * as_int)), + Divide => return Some(impl_op!(INT => as_int / as_int)), + Modulo => return Some(impl_op!(INT => as_int % as_int)), + PowerOf => return Some(impl_op!(INT => as_int.pow(as_int as u32))), + RightShift => return Some(impl_op!(INT => as_int >> as_int)), + LeftShift => return Some(impl_op!(INT => as_int << as_int)), _ => (), } return match op { - Token::EqualsTo => Some(impl_op!(INT => as_int == as_int)), - Token::NotEqualsTo => Some(impl_op!(INT => as_int != as_int)), - Token::GreaterThan => Some(impl_op!(INT => as_int > as_int)), - Token::GreaterThanEqualsTo => Some(impl_op!(INT => as_int >= as_int)), - Token::LessThan => Some(impl_op!(INT => as_int < as_int)), - Token::LessThanEqualsTo => Some(impl_op!(INT => as_int <= as_int)), - Token::Ampersand => Some(impl_op!(INT => as_int & as_int)), - Token::Pipe => Some(impl_op!(INT => as_int | as_int)), - Token::XOr => Some(impl_op!(INT => as_int ^ as_int)), - Token::ExclusiveRange => Some(|_, args| { + EqualsTo => Some(impl_op!(INT => as_int == as_int)), + NotEqualsTo => Some(impl_op!(INT => as_int != as_int)), + GreaterThan => Some(impl_op!(INT => as_int > as_int)), + GreaterThanEqualsTo => Some(impl_op!(INT => as_int >= as_int)), + LessThan => Some(impl_op!(INT => as_int < as_int)), + LessThanEqualsTo => Some(impl_op!(INT => as_int <= as_int)), + Ampersand => Some(impl_op!(INT => as_int & as_int)), + Pipe => Some(impl_op!(INT => as_int | as_int)), + XOr => Some(impl_op!(INT => as_int ^ as_int)), + ExclusiveRange => Some(|_, args| { let x = args[0].as_int().expect(BUILTIN); let y = args[1].as_int().expect(BUILTIN); Ok((x..y).into()) }), - Token::InclusiveRange => Some(|_, args| { + InclusiveRange => Some(|_, args| { let x = args[0].as_int().expect(BUILTIN); let y = args[1].as_int().expect(BUILTIN); Ok((x..=y).into()) @@ -181,22 +196,22 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type1 == TypeId::of::() { return match op { - Token::EqualsTo => Some(impl_op!(bool => as_bool == as_bool)), - Token::NotEqualsTo => Some(impl_op!(bool => as_bool != as_bool)), - Token::GreaterThan => Some(impl_op!(bool => as_bool > as_bool)), - Token::GreaterThanEqualsTo => Some(impl_op!(bool => as_bool >= as_bool)), - Token::LessThan => Some(impl_op!(bool => as_bool < as_bool)), - Token::LessThanEqualsTo => Some(impl_op!(bool => as_bool <= as_bool)), - Token::Ampersand => Some(impl_op!(bool => as_bool & as_bool)), - Token::Pipe => Some(impl_op!(bool => as_bool | as_bool)), - Token::XOr => Some(impl_op!(bool => as_bool ^ as_bool)), + EqualsTo => Some(impl_op!(bool => as_bool == as_bool)), + NotEqualsTo => Some(impl_op!(bool => as_bool != as_bool)), + GreaterThan => Some(impl_op!(bool => as_bool > as_bool)), + GreaterThanEqualsTo => Some(impl_op!(bool => as_bool >= as_bool)), + LessThan => Some(impl_op!(bool => as_bool < as_bool)), + LessThanEqualsTo => Some(impl_op!(bool => as_bool <= as_bool)), + Ampersand => Some(impl_op!(bool => as_bool & as_bool)), + Pipe => Some(impl_op!(bool => as_bool | as_bool)), + XOr => Some(impl_op!(bool => as_bool ^ as_bool)), _ => None, }; } if type1 == TypeId::of::() { return match op { - Token::Plus => Some(|_ctx, args| { + Plus => Some(|_ctx, args| { let s1 = &*args[0].read_lock::().expect(BUILTIN); let s2 = &*args[1].read_lock::().expect(BUILTIN); @@ -209,20 +224,20 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< Ok((s1 + s2).into()) }), - Token::Minus => Some(impl_op!(ImmutableString - ImmutableString)), - Token::EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)), - Token::NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)), - Token::GreaterThan => Some(impl_op!(ImmutableString > ImmutableString)), - Token::GreaterThanEqualsTo => Some(impl_op!(ImmutableString >= ImmutableString)), - Token::LessThan => Some(impl_op!(ImmutableString < ImmutableString)), - Token::LessThanEqualsTo => Some(impl_op!(ImmutableString <= ImmutableString)), + Minus => Some(impl_op!(ImmutableString - ImmutableString)), + EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)), + NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)), + GreaterThan => Some(impl_op!(ImmutableString > ImmutableString)), + GreaterThanEqualsTo => Some(impl_op!(ImmutableString >= ImmutableString)), + LessThan => Some(impl_op!(ImmutableString < ImmutableString)), + LessThanEqualsTo => Some(impl_op!(ImmutableString <= ImmutableString)), _ => None, }; } if type1 == TypeId::of::() { return match op { - Token::Plus => Some(|_ctx, args| { + Plus => Some(|_ctx, args| { let x = args[0].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); @@ -234,12 +249,12 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< Ok(result.into()) }), - Token::EqualsTo => Some(impl_op!(char => as_char == as_char)), - Token::NotEqualsTo => Some(impl_op!(char => as_char != as_char)), - Token::GreaterThan => Some(impl_op!(char => as_char > as_char)), - Token::GreaterThanEqualsTo => Some(impl_op!(char => as_char >= as_char)), - Token::LessThan => Some(impl_op!(char => as_char < as_char)), - Token::LessThanEqualsTo => Some(impl_op!(char => as_char <= as_char)), + EqualsTo => Some(impl_op!(char => as_char == as_char)), + NotEqualsTo => Some(impl_op!(char => as_char != as_char)), + GreaterThan => Some(impl_op!(char => as_char > as_char)), + GreaterThanEqualsTo => Some(impl_op!(char => as_char >= as_char)), + LessThan => Some(impl_op!(char => as_char < as_char)), + LessThanEqualsTo => Some(impl_op!(char => as_char <= as_char)), _ => None, }; } @@ -249,7 +264,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< use crate::Blob; return match op { - Token::Plus => Some(|_ctx, args| { + Plus => Some(|_ctx, args| { let blob1 = &*args[0].read_lock::().expect(BUILTIN); let blob2 = &*args[1].read_lock::().expect(BUILTIN); @@ -270,20 +285,18 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< blob })) }), - Token::EqualsTo => Some(impl_op!(Blob == Blob)), - Token::NotEqualsTo => Some(impl_op!(Blob != Blob)), + EqualsTo => Some(impl_op!(Blob == Blob)), + NotEqualsTo => Some(impl_op!(Blob != Blob)), _ => None, }; } if type1 == TypeId::of::<()>() { return match op { - Token::EqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), - Token::NotEqualsTo - | Token::GreaterThan - | Token::GreaterThanEqualsTo - | Token::LessThan - | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), + EqualsTo => Some(const_true_fn), + NotEqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { + Some(const_false_fn) + } _ => None, }; } @@ -294,19 +307,19 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< ($x:ty, $xx:ident, $y:ty, $yy:ident) => { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { return match op { - Token::Plus => Some(impl_op!(FLOAT => $xx + $yy)), - Token::Minus => Some(impl_op!(FLOAT => $xx - $yy)), - Token::Multiply => Some(impl_op!(FLOAT => $xx * $yy)), - Token::Divide => Some(impl_op!(FLOAT => $xx / $yy)), - Token::Modulo => Some(impl_op!(FLOAT => $xx % $yy)), - Token::PowerOf => Some(impl_op!(FLOAT => $xx.powf($yy as FLOAT))), - Token::EqualsTo => Some(impl_op!(FLOAT => $xx == $yy)), - Token::NotEqualsTo => Some(impl_op!(FLOAT => $xx != $yy)), - Token::GreaterThan => Some(impl_op!(FLOAT => $xx > $yy)), - Token::GreaterThanEqualsTo => Some(impl_op!(FLOAT => $xx >= $yy)), - Token::LessThan => Some(impl_op!(FLOAT => $xx < $yy)), - Token::LessThanEqualsTo => Some(impl_op!(FLOAT => $xx <= $yy)), - _ => None, + Plus => Some(impl_op!(FLOAT => $xx + $yy)), + Minus => Some(impl_op!(FLOAT => $xx - $yy)), + Multiply => Some(impl_op!(FLOAT => $xx * $yy)), + Divide => Some(impl_op!(FLOAT => $xx / $yy)), + Modulo => Some(impl_op!(FLOAT => $xx % $yy)), + PowerOf => Some(impl_op!(FLOAT => $xx.powf($yy as FLOAT))), + EqualsTo => Some(impl_op!(FLOAT => $xx == $yy)), + NotEqualsTo => Some(impl_op!(FLOAT => $xx != $yy)), + GreaterThan => Some(impl_op!(FLOAT => $xx > $yy)), + GreaterThanEqualsTo => Some(impl_op!(FLOAT => $xx >= $yy)), + LessThan => Some(impl_op!(FLOAT => $xx < $yy)), + LessThanEqualsTo => Some(impl_op!(FLOAT => $xx <= $yy)), + _ => None, }; } }; @@ -328,13 +341,13 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< #[cfg(not(feature = "unchecked"))] match op { - Token::Plus => return Some(impl_op!(from Decimal => add($xx, $yy))), - Token::Minus => return Some(impl_op!(from Decimal => subtract($xx, $yy))), - Token::Multiply => return Some(impl_op!(from Decimal => multiply($xx, $yy))), - Token::Divide => return Some(impl_op!(from Decimal => divide($xx, $yy))), - Token::Modulo => return Some(impl_op!(from Decimal => modulo($xx, $yy))), - Token::PowerOf => return Some(impl_op!(from Decimal => power($xx, $yy))), - _ => () + Plus => return Some(impl_op!(from Decimal => add($xx, $yy))), + Minus => return Some(impl_op!(from Decimal => subtract($xx, $yy))), + Multiply => return Some(impl_op!(from Decimal => multiply($xx, $yy))), + Divide => return Some(impl_op!(from Decimal => divide($xx, $yy))), + Modulo => return Some(impl_op!(from Decimal => modulo($xx, $yy))), + PowerOf => return Some(impl_op!(from Decimal => power($xx, $yy))), + _ => () } #[cfg(feature = "unchecked")] @@ -342,23 +355,23 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< #[cfg(feature = "unchecked")] match op { - Token::Plus => return Some(impl_op!(from Decimal => $xx + $yy)), - Token::Minus => return Some(impl_op!(from Decimal => $xx - $yy)), - Token::Multiply => return Some(impl_op!(from Decimal => $xx * $yy)), - Token::Divide => return Some(impl_op!(from Decimal => $xx / $yy)), - Token::Modulo => return Some(impl_op!(from Decimal => $xx % $yy)), - Token::PowerOf => return Some(impl_op!(from Decimal => $xx.powd($yy))), - _ => () + Plus => return Some(impl_op!(from Decimal => $xx + $yy)), + Minus => return Some(impl_op!(from Decimal => $xx - $yy)), + Multiply => return Some(impl_op!(from Decimal => $xx * $yy)), + Divide => return Some(impl_op!(from Decimal => $xx / $yy)), + Modulo => return Some(impl_op!(from Decimal => $xx % $yy)), + PowerOf => return Some(impl_op!(from Decimal => $xx.powd($yy))), + _ => () } return match op { - Token::EqualsTo => Some(impl_op!(from Decimal => $xx == $yy)), - Token::NotEqualsTo => Some(impl_op!(from Decimal => $xx != $yy)), - Token::GreaterThan => Some(impl_op!(from Decimal => $xx > $yy)), - Token::GreaterThanEqualsTo => Some(impl_op!(from Decimal => $xx >= $yy)), - Token::LessThan => Some(impl_op!(from Decimal => $xx < $yy)), - Token::LessThanEqualsTo => Some(impl_op!(from Decimal => $xx <= $yy)), - _ => None + EqualsTo => Some(impl_op!(from Decimal => $xx == $yy)), + NotEqualsTo => Some(impl_op!(from Decimal => $xx != $yy)), + GreaterThan => Some(impl_op!(from Decimal => $xx > $yy)), + GreaterThanEqualsTo => Some(impl_op!(from Decimal => $xx >= $yy)), + LessThan => Some(impl_op!(from Decimal => $xx < $yy)), + LessThanEqualsTo => Some(impl_op!(from Decimal => $xx <= $yy)), + _ => None }; } }; @@ -383,7 +396,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } return match op { - Token::Plus => Some(|_ctx, args| { + Plus => Some(|_ctx, args| { let x = args[0].as_char().expect(BUILTIN); let y = &*args[1].read_lock::().expect(BUILTIN); let result = format!("{x}{y}"); @@ -394,12 +407,12 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< Ok(result.into()) }), - Token::EqualsTo => Some(impl_op!(get_s1s2(==))), - Token::NotEqualsTo => Some(impl_op!(get_s1s2(!=))), - Token::GreaterThan => Some(impl_op!(get_s1s2(>))), - Token::GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))), - Token::LessThan => Some(impl_op!(get_s1s2(<))), - Token::LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))), + EqualsTo => Some(impl_op!(get_s1s2(==))), + NotEqualsTo => Some(impl_op!(get_s1s2(!=))), + GreaterThan => Some(impl_op!(get_s1s2(>))), + GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))), + LessThan => Some(impl_op!(get_s1s2(<))), + LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))), _ => None, }; } @@ -415,7 +428,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } return match op { - Token::Plus => Some(|_ctx, args| { + Plus => Some(|_ctx, args| { let x = &*args[0].read_lock::().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); let result = x + y; @@ -426,43 +439,39 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< Ok(result.into()) }), - Token::Minus => Some(|_, args| { + Minus => Some(|_, args| { let x = &*args[0].read_lock::().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); Ok((x - y).into()) }), - Token::EqualsTo => Some(impl_op!(get_s1s2(==))), - Token::NotEqualsTo => Some(impl_op!(get_s1s2(!=))), - Token::GreaterThan => Some(impl_op!(get_s1s2(>))), - Token::GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))), - Token::LessThan => Some(impl_op!(get_s1s2(<))), - Token::LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))), + EqualsTo => Some(impl_op!(get_s1s2(==))), + NotEqualsTo => Some(impl_op!(get_s1s2(!=))), + GreaterThan => Some(impl_op!(get_s1s2(>))), + GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))), + LessThan => Some(impl_op!(get_s1s2(<))), + LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))), _ => None, }; } // () op string if (type1, type2) == (TypeId::of::<()>(), TypeId::of::()) { return match op { - Token::Plus => Some(|_, args| Ok(args[1].clone())), - Token::EqualsTo - | Token::GreaterThan - | Token::GreaterThanEqualsTo - | Token::LessThan - | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), - Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), + Plus => Some(|_, args| Ok(args[1].clone())), + EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { + Some(const_false_fn) + } + NotEqualsTo => Some(const_true_fn), _ => None, }; } // string op () if (type1, type2) == (TypeId::of::(), TypeId::of::<()>()) { return match op { - Token::Plus => Some(|_, args| Ok(args[0].clone())), - Token::EqualsTo - | Token::GreaterThan - | Token::GreaterThanEqualsTo - | Token::LessThan - | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), - Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), + Plus => Some(|_, args| Ok(args[0].clone())), + EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { + Some(const_false_fn) + } + NotEqualsTo => Some(const_true_fn), _ => None, }; } @@ -474,9 +483,9 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type2 == TypeId::of::() { return match op { - Token::Plus => Some(|_ctx, args| { - let mut buf = [0_u8; 4]; + Plus => Some(|_ctx, args| { let mut blob = args[0].read_lock::().expect(BUILTIN).clone(); + let mut buf = [0_u8; 4]; let x = args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf); #[cfg(not(feature = "unchecked"))] @@ -507,8 +516,8 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< ) { return match op { - Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), - Token::Equals => Some(|_, _| Ok(Dynamic::FALSE)), + NotEqualsTo => Some(const_true_fn), + Equals => Some(const_false_fn), _ => None, }; } @@ -517,8 +526,8 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type1 == TypeId::of::() { if type1 == type2 { return match op { - Token::EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)), - Token::NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)), + EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)), + NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)), _ => None, }; } @@ -527,8 +536,8 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type1 == TypeId::of::() { if type1 == type2 { return match op { - Token::EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)), - Token::NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)), + EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)), + NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)), _ => None, }; } @@ -542,12 +551,10 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } else if type1 != type2 { // If the types are not the same, default to not compare match op { - Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), - Token::EqualsTo - | Token::GreaterThan - | Token::GreaterThanEqualsTo - | Token::LessThan - | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), + NotEqualsTo => Some(const_true_fn), + EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { + Some(const_false_fn) + } _ => None, } } else { @@ -559,12 +566,10 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< // Default comparison operators for different types if type2 != type1 { return match op { - Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), - Token::EqualsTo - | Token::GreaterThan - | Token::GreaterThanEqualsTo - | Token::LessThan - | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), + NotEqualsTo => Some(const_true_fn), + EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { + Some(const_false_fn) + } _ => None, }; } @@ -629,51 +634,49 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt #[cfg(not(feature = "unchecked"))] match op { - Token::PlusAssign => return Some(impl_op!(INT => add(as_int, as_int))), - Token::MinusAssign => return Some(impl_op!(INT => subtract(as_int, as_int))), - Token::MultiplyAssign => return Some(impl_op!(INT => multiply(as_int, as_int))), - Token::DivideAssign => return Some(impl_op!(INT => divide(as_int, as_int))), - Token::ModuloAssign => return Some(impl_op!(INT => modulo(as_int, as_int))), - Token::PowerOfAssign => return Some(impl_op!(INT => power(as_int, as_int))), - Token::RightShiftAssign => { - return Some(impl_op!(INT => shift_right(as_int, as_int))) - } - Token::LeftShiftAssign => return Some(impl_op!(INT => shift_left(as_int, as_int))), + PlusAssign => return Some(impl_op!(INT => add(as_int, as_int))), + MinusAssign => return Some(impl_op!(INT => subtract(as_int, as_int))), + MultiplyAssign => return Some(impl_op!(INT => multiply(as_int, as_int))), + DivideAssign => return Some(impl_op!(INT => divide(as_int, as_int))), + ModuloAssign => return Some(impl_op!(INT => modulo(as_int, as_int))), + PowerOfAssign => return Some(impl_op!(INT => power(as_int, as_int))), + RightShiftAssign => return Some(impl_op!(INT => shift_right(as_int, as_int))), + LeftShiftAssign => return Some(impl_op!(INT => shift_left(as_int, as_int))), _ => (), } #[cfg(feature = "unchecked")] match op { - Token::PlusAssign => return Some(impl_op!(INT += as_int)), - Token::MinusAssign => return Some(impl_op!(INT -= as_int)), - Token::MultiplyAssign => return Some(impl_op!(INT *= as_int)), - Token::DivideAssign => return Some(impl_op!(INT /= as_int)), - Token::ModuloAssign => return Some(impl_op!(INT %= as_int)), - Token::PowerOfAssign => return Some(impl_op!(INT => as_int.pow(as_int as u32))), - Token::RightShiftAssign => return Some(impl_op!(INT >>= as_int)), - Token::LeftShiftAssign => return Some(impl_op!(INT <<= as_int)), + PlusAssign => return Some(impl_op!(INT += as_int)), + MinusAssign => return Some(impl_op!(INT -= as_int)), + MultiplyAssign => return Some(impl_op!(INT *= as_int)), + DivideAssign => return Some(impl_op!(INT /= as_int)), + ModuloAssign => return Some(impl_op!(INT %= as_int)), + PowerOfAssign => return Some(impl_op!(INT => as_int.pow(as_int as u32))), + RightShiftAssign => return Some(impl_op!(INT >>= as_int)), + LeftShiftAssign => return Some(impl_op!(INT <<= as_int)), _ => (), } return match op { - Token::AndAssign => Some(impl_op!(INT &= as_int)), - Token::OrAssign => Some(impl_op!(INT |= as_int)), - Token::XOrAssign => Some(impl_op!(INT ^= as_int)), + AndAssign => Some(impl_op!(INT &= as_int)), + OrAssign => Some(impl_op!(INT |= as_int)), + XOrAssign => Some(impl_op!(INT ^= as_int)), _ => None, }; } if type1 == TypeId::of::() { return match op { - Token::AndAssign => Some(impl_op!(bool = x && as_bool)), - Token::OrAssign => Some(impl_op!(bool = x || as_bool)), + AndAssign => Some(impl_op!(bool = x && as_bool)), + OrAssign => Some(impl_op!(bool = x || as_bool)), _ => None, }; } if type1 == TypeId::of::() { return match op { - Token::PlusAssign => Some(|_, args| { + PlusAssign => Some(|_, args| { let y = args[1].as_char().expect(BUILTIN); let x = &mut *args[0].write_lock::().expect(BUILTIN); Ok((*x = format!("{x}{y}").into()).into()) @@ -684,7 +687,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt if type1 == TypeId::of::() { return match op { - Token::PlusAssign => Some(|_ctx, args| { + 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::(); @@ -698,7 +701,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt Ok((*x += y).into()) }), - Token::MinusAssign => Some(|_, args| { + 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::(); @@ -714,29 +717,26 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt use crate::Array; return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let x = std::mem::take(args[1]).cast::(); if x.is_empty() { return Ok(Dynamic::UNIT); } - let _array_was_empty = { - let array = &mut &mut *args[0].write_lock::().expect(BUILTIN); - let array_is_empty = array.is_empty(); - append(array, x); - array_is_empty - }; + let _array_is_empty = args[0].read_lock::().expect(BUILTIN).is_empty(); #[cfg(not(feature = "unchecked"))] - if !_array_was_empty { + if !_array_is_empty { _ctx.engine().check_data_size( &*args[0].read_lock().expect(BUILTIN), crate::Position::NONE, )?; } - Ok(Dynamic::UNIT) + let array = &mut *args[0].write_lock::().expect(BUILTIN); + + Ok(append(array, x).into()) }), _ => None, }; @@ -744,10 +744,11 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt #[cfg(not(feature = "no_index"))] if type1 == TypeId::of::() { + use crate::packages::blob_basic::blob_functions::*; use crate::Blob; return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let blob2 = std::mem::take(args[1]).cast::(); let blob1 = &mut *args[0].write_lock::().expect(BUILTIN); @@ -758,7 +759,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt 0, ))?; - Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into()) + Ok(append(blob1, blob2).into()) }), _ => None, }; @@ -770,13 +771,13 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt ($x:ident, $xx:ident, $y:ty, $yy:ident) => { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { return match op { - Token::PlusAssign => Some(impl_op!($x += $yy)), - Token::MinusAssign => Some(impl_op!($x -= $yy)), - Token::MultiplyAssign => Some(impl_op!($x *= $yy)), - Token::DivideAssign => Some(impl_op!($x /= $yy)), - Token::ModuloAssign => Some(impl_op!($x %= $yy)), - Token::PowerOfAssign => Some(impl_op!($x => $xx.powf($yy as $x))), - _ => None, + PlusAssign => Some(impl_op!($x += $yy)), + MinusAssign => Some(impl_op!($x -= $yy)), + MultiplyAssign => Some(impl_op!($x *= $yy)), + DivideAssign => Some(impl_op!($x /= $yy)), + ModuloAssign => Some(impl_op!($x %= $yy)), + PowerOfAssign => Some(impl_op!($x => $xx.powf($yy as $x))), + _ => None, }; } } @@ -797,13 +798,13 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt #[cfg(not(feature = "unchecked"))] return match op { - Token::PlusAssign => Some(impl_op!(from $x => add($xx, $yy))), - Token::MinusAssign => Some(impl_op!(from $x => subtract($xx, $yy))), - Token::MultiplyAssign => Some(impl_op!(from $x => multiply($xx, $yy))), - Token::DivideAssign => Some(impl_op!(from $x => divide($xx, $yy))), - Token::ModuloAssign => Some(impl_op!(from $x => modulo($xx, $yy))), - Token::PowerOfAssign => Some(impl_op!(from $x => power($xx, $yy))), - _ => None, + PlusAssign => Some(impl_op!(from $x => add($xx, $yy))), + MinusAssign => Some(impl_op!(from $x => subtract($xx, $yy))), + MultiplyAssign => Some(impl_op!(from $x => multiply($xx, $yy))), + DivideAssign => Some(impl_op!(from $x => divide($xx, $yy))), + ModuloAssign => Some(impl_op!(from $x => modulo($xx, $yy))), + PowerOfAssign => Some(impl_op!(from $x => power($xx, $yy))), + _ => None, }; #[cfg(feature = "unchecked")] @@ -811,13 +812,13 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt #[cfg(feature = "unchecked")] return match op { - Token::PlusAssign => Some(impl_op!(from $x += $yy)), - Token::MinusAssign => Some(impl_op!(from $x -= $yy)), - Token::MultiplyAssign => Some(impl_op!(from $x *= $yy)), - Token::DivideAssign => Some(impl_op!(from $x /= $yy)), - Token::ModuloAssign => Some(impl_op!(from $x %= $yy)), - Token::PowerOfAssign => Some(impl_op!(from $x => $xx.powd($yy))), - _ => None, + PlusAssign => Some(impl_op!(from $x += $yy)), + MinusAssign => Some(impl_op!(from $x -= $yy)), + MultiplyAssign => Some(impl_op!(from $x *= $yy)), + DivideAssign => Some(impl_op!(from $x /= $yy)), + ModuloAssign => Some(impl_op!(from $x %= $yy)), + PowerOfAssign => Some(impl_op!(from $x => $xx.powd($yy))), + _ => None, }; } }; @@ -832,7 +833,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // string op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let mut buf = [0_u8; 4]; let ch = &*args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf); let mut x = args[0].write_lock::().expect(BUILTIN); @@ -843,14 +844,14 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt Ok((*x += ch).into()) }), - Token::MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)), + MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)), _ => None, }; } // char op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let ch = { let s = &*args[1].read_lock::().expect(BUILTIN); @@ -883,7 +884,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt use crate::Array; return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { { let x = std::mem::take(args[1]); let array = &mut *args[0].write_lock::().expect(BUILTIN); @@ -908,8 +909,10 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= int if (type1, type2) == (TypeId::of::(), TypeId::of::()) { + use crate::packages::blob_basic::blob_functions::*; + return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let x = args[1].as_int().expect(BUILTIN); let blob = &mut *args[0].write_lock::().expect(BUILTIN); @@ -917,7 +920,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt _ctx.engine() .raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?; - Ok(crate::packages::blob_basic::blob_functions::push(blob, x).into()) + Ok(push(blob, x).into()) }), _ => None, }; @@ -925,8 +928,10 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { + use crate::packages::blob_basic::blob_functions::*; + return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let x = args[1].as_char().expect(BUILTIN); let blob = &mut *args[0].write_lock::().expect(BUILTIN); @@ -934,7 +939,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt _ctx.engine() .raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?; - Ok(crate::packages::blob_basic::blob_functions::append_char(blob, x).into()) + Ok(append_char(blob, x).into()) }), _ => None, }; @@ -942,8 +947,10 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { + use crate::packages::blob_basic::blob_functions::*; + return match op { - Token::PlusAssign => Some(|_ctx, args| { + PlusAssign => Some(|_ctx, args| { let s = std::mem::take(args[1]).cast::(); if s.is_empty() { @@ -959,7 +966,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt 0, ))?; - Ok(crate::packages::blob_basic::blob_functions::append_str(blob, &s).into()) + Ok(append_str(blob, &s).into()) }), _ => None, }; From 6880d449009b6e2bdee10619457907e976a7c4a5 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 25 Oct 2022 08:31:13 +0800 Subject: [PATCH 15/22] Remove unnecessary data. --- src/eval/expr.rs | 2 +- src/eval/stmt.rs | 4 ++-- src/types/scope.rs | 33 ++++++++++++++++++++------------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/eval/expr.rs b/src/eval/expr.rs index c8d17896..6480da35 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -189,7 +189,7 @@ impl Engine { let var_name = expr.get_variable_name(true).expect("`Expr::Variable`"); match scope.get_index(var_name) { - Some((index, _)) => index, + Some(index) => index, None => { return match self.global_modules.iter().find_map(|m| m.get_var(var_name)) { Some(val) => Ok((val.into(), var_pos)), diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 4e26d7d3..18f5465f 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -959,7 +959,7 @@ impl Engine { Stmt::Export(x, ..) => { let (Ident { name, pos, .. }, Ident { name: alias, .. }) = &**x; // Mark scope variables as public - if let Some((index, ..)) = scope.get_index(name) { + if let Some(index) = scope.get_index(name) { let alias = if alias.is_empty() { name } else { alias }.clone(); scope.add_alias_by_index(index, alias.into()); Ok(Dynamic::UNIT) @@ -971,7 +971,7 @@ impl Engine { // Share statement #[cfg(not(feature = "no_closure"))] Stmt::Share(name, pos) => { - if let Some((index, ..)) = scope.get_index(name) { + if let Some(index) = scope.get_index(name) { let val = scope.get_mut_by_index(index); if !val.is_shared() { diff --git a/src/types/scope.rs b/src/types/scope.rs index a3787421..05d566a8 100644 --- a/src/types/scope.rs +++ b/src/types/scope.rs @@ -407,7 +407,7 @@ impl Scope<'_> { /// Find an entry in the [`Scope`], starting from the last. #[inline] #[must_use] - pub(crate) fn get_index(&self, name: &str) -> Option<(usize, AccessMode)> { + pub(crate) fn get_index(&self, name: &str) -> Option { let len = self.len(); self.names @@ -417,7 +417,7 @@ impl Scope<'_> { .find_map(|(i, key)| { if name == key { let index = len - 1 - i; - Some((index, self.values[index].access_mode())) + Some(index) } else { None } @@ -467,10 +467,11 @@ impl Scope<'_> { #[inline] #[must_use] pub fn is_constant(&self, name: &str) -> Option { - self.get_index(name).map(|(.., access)| match access { - AccessMode::ReadWrite => false, - AccessMode::ReadOnly => true, - }) + self.get_index(name) + .map(|n| match self.values[n].access_mode() { + AccessMode::ReadWrite => false, + AccessMode::ReadOnly => true, + }) } /// Update the value of the named entry in the [`Scope`] if it already exists and is not constant. /// Push a new entry with the value into the [`Scope`] if the name doesn't exist or if the @@ -503,7 +504,10 @@ impl Scope<'_> { name: impl AsRef + Into, value: impl Variant + Clone, ) -> &mut Self { - match self.get_index(name.as_ref()) { + match self + .get_index(name.as_ref()) + .map(|n| (n, self.values[n].access_mode())) + { None | Some((.., AccessMode::ReadOnly)) => { self.push(name, value); } @@ -542,7 +546,10 @@ impl Scope<'_> { name: impl AsRef + Into, value: impl Variant + Clone, ) -> &mut Self { - match self.get_index(name.as_ref()) { + match self + .get_index(name.as_ref()) + .map(|n| (n, self.values[n].access_mode())) + { None => { self.push(name, value); } @@ -576,7 +583,7 @@ impl Scope<'_> { #[inline(always)] #[must_use] pub fn get(&self, name: &str) -> Option<&Dynamic> { - self.get_index(name).map(|(index, _)| &self.values[index]) + self.get_index(name).map(|index| &self.values[index]) } /// Remove the last entry in the [`Scope`] by the specified name and return its value. /// @@ -607,7 +614,7 @@ impl Scope<'_> { #[inline(always)] #[must_use] pub fn remove(&mut self, name: &str) -> Option { - self.get_index(name).and_then(|(index, _)| { + self.get_index(name).and_then(|index| { self.names.remove(index); self.aliases.remove(index); self.values.remove(index).try_cast() @@ -640,8 +647,8 @@ impl Scope<'_> { #[must_use] pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> { self.get_index(name) - .and_then(move |(index, access)| match access { - AccessMode::ReadWrite => Some(self.get_mut_by_index(index)), + .and_then(move |n| match self.values[n].access_mode() { + AccessMode::ReadWrite => Some(self.get_mut_by_index(n)), AccessMode::ReadOnly => None, }) } @@ -685,7 +692,7 @@ impl Scope<'_> { name: impl AsRef + Into, alias: impl Into, ) { - if let Some((index, ..)) = self.get_index(name.as_ref()) { + if let Some(index) = self.get_index(name.as_ref()) { let alias = match alias.into() { x if x.is_empty() => name.into(), x => x, From 6702fe349c117ffe2fa197f32265a1dbe9f3c0f8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 25 Oct 2022 10:05:31 +0800 Subject: [PATCH 16/22] Add index to Share statement. --- src/ast/stmt.rs | 5 ++++- src/eval/expr.rs | 2 +- src/eval/stmt.rs | 11 ++++++++--- src/parser.rs | 5 ++++- src/types/scope.rs | 16 ++++++++-------- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 6b15061c..80ed1b36 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -614,7 +614,10 @@ pub enum Stmt { /// This variant does not map to any language structure. It is currently only used only to /// convert a normal variable into a shared variable when the variable is _captured_ by a closure. #[cfg(not(feature = "no_closure"))] - Share(crate::ImmutableString, Position), + Share( + Box<(crate::ImmutableString, Option)>, + Position, + ), } impl Default for Stmt { diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 6480da35..5afd07d9 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -188,7 +188,7 @@ impl Engine { // Find the variable in the scope let var_name = expr.get_variable_name(true).expect("`Expr::Variable`"); - match scope.get_index(var_name) { + match scope.search(var_name) { Some(index) => index, None => { return match self.global_modules.iter().find_map(|m| m.get_var(var_name)) { diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 18f5465f..770d4174 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -959,7 +959,7 @@ impl Engine { Stmt::Export(x, ..) => { let (Ident { name, pos, .. }, Ident { name: alias, .. }) = &**x; // Mark scope variables as public - if let Some(index) = scope.get_index(name) { + if let Some(index) = scope.search(name) { let alias = if alias.is_empty() { name } else { alias }.clone(); scope.add_alias_by_index(index, alias.into()); Ok(Dynamic::UNIT) @@ -970,8 +970,13 @@ impl Engine { // Share statement #[cfg(not(feature = "no_closure"))] - Stmt::Share(name, pos) => { - if let Some(index) = scope.get_index(name) { + Stmt::Share(x, pos) => { + let (name, index) = &**x; + + if let Some(index) = index + .map(|n| scope.len() - n.get()) + .or_else(|| scope.search(name)) + { let val = scope.get_mut_by_index(index); if !val.is_shared() { diff --git a/src/parser.rs b/src/parser.rs index cb002908..e2bcb181 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3711,7 +3711,10 @@ impl Engine { statements.extend( externals .into_iter() - .map(|crate::ast::Ident { name, pos }| Stmt::Share(name, pos)), + .map(|crate::ast::Ident { name, pos }| { + let (index, _) = parent.access_var(&name, lib, pos); + Stmt::Share((name, index).into(), pos) + }), ); statements.push(Stmt::Expr(expr.into())); Expr::Stmt(crate::ast::StmtBlock::new(statements, pos, Position::NONE).into()) diff --git a/src/types/scope.rs b/src/types/scope.rs index 05d566a8..435042e8 100644 --- a/src/types/scope.rs +++ b/src/types/scope.rs @@ -407,7 +407,7 @@ impl Scope<'_> { /// Find an entry in the [`Scope`], starting from the last. #[inline] #[must_use] - pub(crate) fn get_index(&self, name: &str) -> Option { + pub(crate) fn search(&self, name: &str) -> Option { let len = self.len(); self.names @@ -467,7 +467,7 @@ impl Scope<'_> { #[inline] #[must_use] pub fn is_constant(&self, name: &str) -> Option { - self.get_index(name) + self.search(name) .map(|n| match self.values[n].access_mode() { AccessMode::ReadWrite => false, AccessMode::ReadOnly => true, @@ -505,7 +505,7 @@ impl Scope<'_> { value: impl Variant + Clone, ) -> &mut Self { match self - .get_index(name.as_ref()) + .search(name.as_ref()) .map(|n| (n, self.values[n].access_mode())) { None | Some((.., AccessMode::ReadOnly)) => { @@ -547,7 +547,7 @@ impl Scope<'_> { value: impl Variant + Clone, ) -> &mut Self { match self - .get_index(name.as_ref()) + .search(name.as_ref()) .map(|n| (n, self.values[n].access_mode())) { None => { @@ -583,7 +583,7 @@ impl Scope<'_> { #[inline(always)] #[must_use] pub fn get(&self, name: &str) -> Option<&Dynamic> { - self.get_index(name).map(|index| &self.values[index]) + self.search(name).map(|index| &self.values[index]) } /// Remove the last entry in the [`Scope`] by the specified name and return its value. /// @@ -614,7 +614,7 @@ impl Scope<'_> { #[inline(always)] #[must_use] pub fn remove(&mut self, name: &str) -> Option { - self.get_index(name).and_then(|index| { + self.search(name).and_then(|index| { self.names.remove(index); self.aliases.remove(index); self.values.remove(index).try_cast() @@ -646,7 +646,7 @@ impl Scope<'_> { #[inline] #[must_use] pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> { - self.get_index(name) + self.search(name) .and_then(move |n| match self.values[n].access_mode() { AccessMode::ReadWrite => Some(self.get_mut_by_index(n)), AccessMode::ReadOnly => None, @@ -692,7 +692,7 @@ impl Scope<'_> { name: impl AsRef + Into, alias: impl Into, ) { - if let Some(index) = self.get_index(name.as_ref()) { + if let Some(index) = self.search(name.as_ref()) { let alias = match alias.into() { x if x.is_empty() => name.into(), x => x, From 6b24cc151e455a89754feca155cc3b22a9fb17a8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 25 Oct 2022 20:53:27 +0800 Subject: [PATCH 17/22] Add auto-gen notice to build template. --- build.rs | 1 + build.template | 2 ++ src/config.rs | 2 ++ 3 files changed, 5 insertions(+) diff --git a/build.rs b/build.rs index fa7570c5..66903154 100644 --- a/build.rs +++ b/build.rs @@ -6,6 +6,7 @@ use std::{ fn main() { // Tell Cargo that if the given environment variable changes, to rerun this build script. + println!("cargo:rerun-if-changed=build.template"); println!("cargo:rerun-if-env-changed=RHAI_AHASH_SEED"); let mut contents = String::new(); diff --git a/build.template b/build.template index b55d13e8..6f09909f 100644 --- a/build.template +++ b/build.template @@ -1,4 +1,6 @@ //! Configuration settings for this Rhai build +//! +//! This file is auto-generated from `build.template` /// Fixed hashing seeds for stable hashing. /// Set to [`None`] to disable stable hashing. diff --git a/src/config.rs b/src/config.rs index 9eb456fd..93db79d5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,6 @@ //! Configuration settings for this Rhai build +//! +//! This file is auto-generated from `build.template` /// Fixed hashing seeds for stable hashing. /// Set to [`None`] to disable stable hashing. From 3c2e0318836fe44b9faeb1a386bb6a2794dcc39d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 27 Oct 2022 13:38:21 +0800 Subject: [PATCH 18/22] Use variable interpolation for println!. --- examples/arrays_and_structs.rs | 6 +-- examples/callback.rs | 2 +- examples/custom_types.rs | 4 +- examples/custom_types_and_methods.rs | 4 +- examples/event_handler_js/main.rs | 8 ++-- examples/event_handler_main/main.rs | 6 +-- examples/event_handler_map/main.rs | 8 ++-- examples/hello.rs | 2 +- examples/reuse_scope.rs | 2 +- examples/serde.rs | 8 ++-- examples/simple_fn.rs | 2 +- examples/strings.rs | 4 +- examples/threading.rs | 2 +- src/ast/expr.rs | 16 +++---- src/ast/namespace.rs | 2 +- src/ast/stmt.rs | 4 +- src/bin/rhai-dbg.rs | 65 ++++++++++++++-------------- src/bin/rhai-repl.rs | 41 +++++++++--------- src/bin/rhai-run.rs | 11 +++-- src/engine.rs | 20 +++------ src/eval/debugger.rs | 33 +++++--------- src/func/callable_function.rs | 16 +++---- src/types/dynamic.rs | 4 +- src/types/parse_error.rs | 12 ++--- 24 files changed, 132 insertions(+), 150 deletions(-) diff --git a/examples/arrays_and_structs.rs b/examples/arrays_and_structs.rs index fc937bcd..b5a7889a 100644 --- a/examples/arrays_and_structs.rs +++ b/examples/arrays_and_structs.rs @@ -38,7 +38,7 @@ fn main() -> Result<(), Box> { engine .gen_fn_signatures(false) .into_iter() - .for_each(|func| println!("{}", func)); + .for_each(|func| println!("{func}")); println!(); } @@ -51,7 +51,7 @@ fn main() -> Result<(), Box> { ", )?; - println!("{:?}", result); + println!("{result:?}"); let result = engine.eval::( " @@ -61,7 +61,7 @@ fn main() -> Result<(), Box> { ", )?; - println!("{:?}", result); + println!("{result:?}"); Ok(()) } diff --git a/examples/callback.rs b/examples/callback.rs index ce27f77c..99984691 100644 --- a/examples/callback.rs +++ b/examples/callback.rs @@ -36,7 +36,7 @@ fn main() -> Result<(), Box> { let r2 = func(1, 2); let r3 = func(1, 2); - println!("The Answers: {}, {}, {}", r1, r2, r3); // prints 40, 42, 44 + println!("The Answers: {r1}, {r2}, {r3}"); // prints 40, 42, 44 Ok(()) } diff --git a/examples/custom_types.rs b/examples/custom_types.rs index ba57de58..c4d7804f 100644 --- a/examples/custom_types.rs +++ b/examples/custom_types.rs @@ -67,7 +67,7 @@ fn main() -> Result<(), Box> { engine .gen_fn_signatures(false) .into_iter() - .for_each(|func| println!("{}", func)); + .for_each(|func| println!("{func}")); println!(); } @@ -89,7 +89,7 @@ fn main() -> Result<(), Box> { ", )?; - println!("result: {}", result); // prints 1085764 + println!("result: {result}"); // prints 1085764 Ok(()) } diff --git a/examples/custom_types_and_methods.rs b/examples/custom_types_and_methods.rs index a70265ff..fecbccfe 100644 --- a/examples/custom_types_and_methods.rs +++ b/examples/custom_types_and_methods.rs @@ -48,7 +48,7 @@ fn main() -> Result<(), Box> { engine .gen_fn_signatures(false) .into_iter() - .for_each(|func| println!("{}", func)); + .for_each(|func| println!("{func}")); println!(); } @@ -62,7 +62,7 @@ fn main() -> Result<(), Box> { ", )?; - println!("result: {}", result); // prints 1085764 + println!("result: {result}"); // prints 1085764 Ok(()) } diff --git a/examples/event_handler_js/main.rs b/examples/event_handler_js/main.rs index db5740f3..22c128c3 100644 --- a/examples/event_handler_js/main.rs +++ b/examples/event_handler_js/main.rs @@ -78,12 +78,12 @@ pub fn main() { scope.push_constant("MY_CONSTANT", 42_i64); // Compile the handler script. - println!("> Loading script file: {}", path); + println!("> Loading script file: {path}"); let ast = match engine.compile_file_with_scope(&mut scope, path.into()) { Ok(ast) => ast, Err(err) => { - eprintln!("! Error: {}", err); + eprintln!("! Error: {err}"); println!("Cannot continue. Bye!"); return; } @@ -101,7 +101,7 @@ pub fn main() { let result = engine.call_fn_raw(&mut scope, &ast, false, true, "init", Some(&mut states), []); if let Err(err) = result { - eprintln!("! {}", err) + eprintln!("! {err}") } // Create handler instance @@ -152,7 +152,7 @@ pub fn main() { engine.call_fn_raw(scope, ast, false, true, event, this_ptr, [arg.into()]); if let Err(err) = result { - eprintln!("! {}", err) + eprintln!("! {err}") } } } diff --git a/examples/event_handler_main/main.rs b/examples/event_handler_main/main.rs index c70c8740..b5b46577 100644 --- a/examples/event_handler_main/main.rs +++ b/examples/event_handler_main/main.rs @@ -67,7 +67,7 @@ pub fn main() { scope.push_constant("MY_CONSTANT", 42_i64); // Compile the handler script. - println!("> Loading script file: {}", path); + println!("> Loading script file: {path}"); let ast = match engine.compile_file_with_scope(&mut scope, path.into()) { Ok(ast) => ast, @@ -89,7 +89,7 @@ pub fn main() { let result = engine.call_fn_raw(&mut scope, &ast, false, false, "init", None, []); if let Err(err) = result { - eprintln!("! {}", err) + eprintln!("! {err}") } // Create handler instance @@ -127,7 +127,7 @@ pub fn main() { let result = engine.call_fn::<()>(scope, ast, event, (arg.to_string(),)); if let Err(err) = result { - eprintln!("! {}", err) + eprintln!("! {err}") } } } diff --git a/examples/event_handler_map/main.rs b/examples/event_handler_map/main.rs index e45688db..b41795c4 100644 --- a/examples/event_handler_map/main.rs +++ b/examples/event_handler_map/main.rs @@ -81,12 +81,12 @@ pub fn main() { scope.push("state", states); // Compile the handler script. - println!("> Loading script file: {}", path); + println!("> Loading script file: {path}"); let ast = match engine.compile_file_with_scope(&mut scope, path.into()) { Ok(ast) => ast, Err(err) => { - eprintln!("! Error: {}", err); + eprintln!("! Error: {err}"); println!("Cannot continue. Bye!"); return; } @@ -103,7 +103,7 @@ pub fn main() { let result = engine.call_fn::<()>(&mut scope, &ast, "init", ()); if let Err(err) = result { - eprintln!("! {}", err) + eprintln!("! {err}") } // Create handler instance @@ -141,7 +141,7 @@ pub fn main() { let result = engine.call_fn::<()>(scope, ast, event, (arg.to_string(),)); if let Err(err) = result { - eprintln!("! {}", err) + eprintln!("! {err}") } } } diff --git a/examples/hello.rs b/examples/hello.rs index b85b06da..dba66e5b 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -9,7 +9,7 @@ fn main() -> Result<(), Box> { let result = engine.eval::("40 + 2")?; - println!("The Answer: {}", result); // prints 42 + println!("The Answer: {result}"); // prints 42 Ok(()) } diff --git a/examples/reuse_scope.rs b/examples/reuse_scope.rs index 3bfa6bf3..486382a2 100644 --- a/examples/reuse_scope.rs +++ b/examples/reuse_scope.rs @@ -13,7 +13,7 @@ fn main() -> Result<(), Box> { for _ in 0..10 { let result = engine.eval_with_scope::(&mut scope, "x += 1; x")?; - println!("result: {}", result); + println!("result: {result}"); } println!("x = {}", scope.get_value::("x").unwrap()); diff --git a/examples/serde.rs b/examples/serde.rs index b145e247..cbdab2b2 100644 --- a/examples/serde.rs +++ b/examples/serde.rs @@ -36,13 +36,13 @@ fn main() { }, }; - println!("Source struct: {:#?}", x); + println!("Source struct: {x:#?}"); // Convert the 'MyStruct' into a 'Dynamic' let map: Dynamic = to_dynamic(x).unwrap(); assert!(map.is::()); - println!("Serialized to Dynamic: {:#?}", map); + println!("Serialized to Dynamic: {map:#?}"); } pub fn de() { @@ -60,7 +60,7 @@ fn main() { ) .unwrap(); - println!("Source Dynamic: {:#?}", result); + println!("Source Dynamic: {result:#?}"); // Convert the 'Dynamic' object map into 'MyStruct' let x: MyStruct = from_dynamic(&result).unwrap(); @@ -77,7 +77,7 @@ fn main() { }, } ); - println!("Deserialized to struct: {:#?}", x); + println!("Deserialized to struct: {x:#?}"); } ser(); diff --git a/examples/simple_fn.rs b/examples/simple_fn.rs index 06b28c53..887cad30 100644 --- a/examples/simple_fn.rs +++ b/examples/simple_fn.rs @@ -13,7 +13,7 @@ fn main() -> Result<(), Box> { let result = engine.eval::("add(40, 2)")?; - println!("Answer: {}", result); // prints 42 + println!("Answer: {result}"); // prints 42 Ok(()) } diff --git a/examples/strings.rs b/examples/strings.rs index e3fac382..b9c7285a 100644 --- a/examples/strings.rs +++ b/examples/strings.rs @@ -37,10 +37,10 @@ fn main() -> Result<(), Box> { .register_fn("index_of", find_substring) // Register string functions using closures .register_fn("display", |label: &str, value: i64| { - println!("{}: {}", label, value) + println!("{label}: {value}") }) .register_fn("display", |label: ImmutableString, value: &str| { - println!(r#"{}: "{}""#, label, value) // Quote the input string + println!(r#"{label}: "{value}""#) // Quote the input string }); let mut scope = Scope::new(); diff --git a/examples/threading.rs b/examples/threading.rs index 64cd04dc..adcf9bea 100644 --- a/examples/threading.rs +++ b/examples/threading.rs @@ -60,7 +60,7 @@ fn main() { let mut value: i64 = 0; while value < 10 { - println!("Value: {}", value); + println!("Value: {value}"); // Send value to script tx_master.send(value).unwrap(); // Receive value from script diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 72d866fc..7a9e1cae 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -465,13 +465,13 @@ impl fmt::Debug for Expr { let mut display_pos = format!(" @ {:?}", self.start_position()); match self { - Self::DynamicConstant(value, ..) => write!(f, "{:?}", value), - Self::BoolConstant(value, ..) => write!(f, "{:?}", value), - Self::IntegerConstant(value, ..) => write!(f, "{:?}", value), + Self::DynamicConstant(value, ..) => write!(f, "{value:?}"), + Self::BoolConstant(value, ..) => write!(f, "{value:?}"), + Self::IntegerConstant(value, ..) => write!(f, "{value:?}"), #[cfg(not(feature = "no_float"))] - Self::FloatConstant(value, ..) => write!(f, "{:?}", value), - Self::CharConstant(value, ..) => write!(f, "{:?}", value), - Self::StringConstant(value, ..) => write!(f, "{:?}", value), + Self::FloatConstant(value, ..) => write!(f, "{value:?}"), + Self::CharConstant(value, ..) => write!(f, "{value:?}"), + Self::StringConstant(value, ..) => write!(f, "{value:?}"), Self::Unit(..) => f.write_str("()"), Self::InterpolatedString(x, ..) => { @@ -502,10 +502,10 @@ impl fmt::Debug for Expr { f.write_str(&x.3)?; #[cfg(not(feature = "no_module"))] if let Some(n) = x.1.index() { - write!(f, " #{}", n)?; + write!(f, " #{n}")?; } if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) { - write!(f, " #{}", n)?; + write!(f, " #{n}")?; } f.write_str(")") } diff --git a/src/ast/namespace.rs b/src/ast/namespace.rs index e0939384..2888b25e 100644 --- a/src/ast/namespace.rs +++ b/src/ast/namespace.rs @@ -37,7 +37,7 @@ impl fmt::Debug for Namespace { } if let Some(index) = self.index { - write!(f, "{} -> ", index)?; + write!(f, "{index} -> ")?; } f.write_str( diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 80ed1b36..284d0133 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -185,8 +185,8 @@ impl fmt::Debug for RangeCase { #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::ExclusiveInt(r, n) => write!(f, "{}..{} => {}", r.start, r.end, n), - Self::InclusiveInt(r, n) => write!(f, "{}..={} => {}", *r.start(), *r.end(), n), + Self::ExclusiveInt(r, n) => write!(f, "{}..{} => {n}", r.start, r.end), + Self::InclusiveInt(r, n) => write!(f, "{}..={} => {n}", *r.start(), *r.end()), } } } diff --git a/src/bin/rhai-dbg.rs b/src/bin/rhai-dbg.rs index 71d5152a..5649c0c1 100644 --- a/src/bin/rhai-dbg.rs +++ b/src/bin/rhai-dbg.rs @@ -75,7 +75,7 @@ fn print_current_source( } if !src.is_empty() { // Print just a line number for imported modules - println!("{} @ {:?}", src, pos); + println!("{src} @ {pos:?}"); } else { // Print the current source line print_source(lines, pos, 0, window); @@ -100,17 +100,16 @@ fn print_error(input: &str, mut err: EvalAltResult) { // Print error position if pos.is_none() { // No position - println!("{}", err); + println!("{err}"); } else { // Specific position - print line text - println!("{}{}", line_no, lines[pos.line().unwrap() - 1]); + println!("{line_no}{}", lines[pos.line().unwrap() - 1]); // Display position marker println!( - "{0:>1$} {2}", + "{0:>1$} {err}", "^", line_no.len() + pos.position().unwrap(), - err ); } } @@ -247,11 +246,11 @@ fn debug_callback( BreakPoint::AtPosition { .. } => (), BreakPoint::AtFunctionName { ref name, .. } | BreakPoint::AtFunctionCall { ref name, .. } => { - println!("! Call to function {}.", name) + println!("! Call to function {name}.") } #[cfg(not(feature = "no_object"))] BreakPoint::AtProperty { ref name, .. } => { - println!("! Property {} accessed.", name) + println!("! Property {name} accessed.") } _ => unreachable!(), } @@ -311,8 +310,8 @@ fn debug_callback( println!("{:?}", node); } else { match source { - Some(source) => println!("{:?} {} @ {:?}", node, source, pos), - None => println!("{:?} @ {:?}", node, pos), + Some(source) => println!("{node:?} {source} @ {pos:?}"), + None => println!("{node:?} @ {pos:?}"), } } println!(); @@ -327,7 +326,7 @@ fn debug_callback( ["list" | "l", n] if n.parse::().is_ok() => { let num = n.parse::().unwrap(); if num == 0 || num > lines.len() { - eprintln!("\x1b[31mInvalid line: {}\x1b[39m", num); + eprintln!("\x1b[31mInvalid line: {num}\x1b[39m"); } else { let pos = Position::new(num as u16, 0); print_current_source(&mut context, source, pos, lines, (3, 6)); @@ -340,17 +339,17 @@ fn debug_callback( ["next" | "n"] => break Ok(DebuggerCommand::Next), ["scope"] => println!("{}", context.scope()), ["print" | "p", "this"] => match context.this_ptr() { - Some(value) => println!("=> {:?}", value), + Some(value) => println!("=> {value:?}"), None => println!("`this` pointer is unbound."), }, ["print" | "p", var_name] => match context.scope().get_value::(var_name) { - Some(value) => println!("=> {:?}", value), - None => eprintln!("Variable not found: {}", var_name), + Some(value) => println!("=> {value:?}"), + None => eprintln!("Variable not found: {var_name}"), }, ["print" | "p"] => { println!("{}", context.scope().clone_visible()); if let Some(value) = context.this_ptr() { - println!("this = {:?}", value); + println!("this = {value:?}"); } } #[cfg(not(feature = "no_module"))] @@ -379,7 +378,7 @@ fn debug_callback( .iter() .rev() { - println!("{}", frame) + println!("{frame}") } } ["info" | "i", "break" | "b"] => Iterator::for_each( @@ -396,7 +395,7 @@ fn debug_callback( print!("{}", line_num); print_source(lines, *pos, line_num.len(), (0, 0)); } - _ => println!("[{}] {}", i + 1, bp), + _ => println!("[{}] {bp}", i + 1), }, ), ["enable" | "en", n] => { @@ -414,12 +413,12 @@ fn debug_callback( .get_mut(n - 1) .unwrap() .enable(true); - println!("Break-point #{} enabled.", n) + println!("Break-point #{n} enabled.") } else { - eprintln!("\x1b[31mInvalid break-point: {}\x1b[39m", n); + eprintln!("\x1b[31mInvalid break-point: {n}\x1b[39m"); } } else { - eprintln!("\x1b[31mInvalid break-point: '{}'\x1b[39m", n); + eprintln!("\x1b[31mInvalid break-point: '{n}'\x1b[39m"); } } ["disable" | "dis", n] => { @@ -437,12 +436,12 @@ fn debug_callback( .get_mut(n - 1) .unwrap() .enable(false); - println!("Break-point #{} disabled.", n) + println!("Break-point #{n} disabled.") } else { - eprintln!("\x1b[31mInvalid break-point: {}\x1b[39m", n); + eprintln!("\x1b[31mInvalid break-point: {n}\x1b[39m"); } } else { - eprintln!("\x1b[31mInvalid break-point: '{}'\x1b[39m", n); + eprintln!("\x1b[31mInvalid break-point: '{n}'\x1b[39m"); } } ["delete" | "d", n] => { @@ -458,12 +457,12 @@ fn debug_callback( .debugger .break_points_mut() .remove(n - 1); - println!("Break-point #{} deleted.", n) + println!("Break-point #{n} deleted.") } else { - eprintln!("\x1b[31mInvalid break-point: {}\x1b[39m", n); + eprintln!("\x1b[31mInvalid break-point: {n}\x1b[39m"); } } else { - eprintln!("\x1b[31mInvalid break-point: '{}'\x1b[39m", n); + eprintln!("\x1b[31mInvalid break-point: '{n}'\x1b[39m"); } } ["delete" | "d"] => { @@ -481,14 +480,14 @@ fn debug_callback( args, enabled: true, }; - println!("Break-point added for {}", bp); + println!("Break-point added for {bp}"); context .global_runtime_state_mut() .debugger .break_points_mut() .push(bp); } else { - eprintln!("\x1b[31mInvalid number of arguments: '{}'\x1b[39m", args); + eprintln!("\x1b[31mInvalid number of arguments: '{args}'\x1b[39m"); } } // Property name @@ -498,7 +497,7 @@ fn debug_callback( name: param[1..].into(), enabled: true, }; - println!("Break-point added for {}", bp); + println!("Break-point added for {bp}"); context .global_runtime_state_mut() .debugger @@ -521,14 +520,14 @@ fn debug_callback( pos: Position::new(n as u16, 0), enabled: true, }; - println!("Break-point added {}", bp); + println!("Break-point added {bp}"); context .global_runtime_state_mut() .debugger .break_points_mut() .push(bp); } else { - eprintln!("\x1b[31mInvalid line number: '{}'\x1b[39m", n); + eprintln!("\x1b[31mInvalid line number: '{n}'\x1b[39m"); } } // Function name parameter @@ -537,7 +536,7 @@ fn debug_callback( name: param.trim().into(), enabled: true, }; - println!("Break-point added for {}", bp); + println!("Break-point added for {bp}"); context .global_runtime_state_mut() .debugger @@ -551,7 +550,7 @@ fn debug_callback( pos, enabled: true, }; - println!("Break-point added {}", bp); + println!("Break-point added {bp}"); context .global_runtime_state_mut() .debugger @@ -588,7 +587,7 @@ fn debug_callback( fn main() { let title = format!("Rhai Debugger (version {})", env!("CARGO_PKG_VERSION")); - println!("{}", title); + println!("{title}"); println!("{0:=<1$}", "", title.len()); // Initialize scripting engine diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index dcab9974..71801d82 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -26,17 +26,16 @@ fn print_error(input: &str, mut err: EvalAltResult) { // Print error position if pos.is_none() { // No position - println!("{}", err); + println!("{err}"); } else { // Specific position - print line text - println!("{}{}", line_no, lines[pos.line().unwrap() - 1]); + println!("{line_no}{}", lines[pos.line().unwrap() - 1]); // Display position marker println!( - "{0:>1$} {2}", + "{0:>1$} {err}", "^", line_no.len() + pos.position().unwrap(), - err ); } } @@ -119,7 +118,7 @@ fn load_script_files(engine: &mut Engine) { for filename in env::args().skip(1) { let filename = match Path::new(&filename).canonicalize() { Err(err) => { - eprintln!("Error script file path: {}\n{}", filename, err); + eprintln!("Error script file path: {filename}\n{err}"); exit(1); } Ok(f) => { @@ -164,7 +163,7 @@ fn load_script_files(engine: &mut Engine) { let filename = filename.to_string_lossy(); eprintln!("{:=<1$}", "", filename.len()); - eprintln!("{}", filename); + eprintln!("{filename}"); eprintln!("{:=<1$}", "", filename.len()); eprintln!(); @@ -277,13 +276,13 @@ mod sample_functions { #[rhai_fn(name = "test")] pub fn test2(x: &mut INT, y: INT, z: &str) { *x += y + (z.len() as INT); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); } } fn main() { let title = format!("Rhai REPL tool (version {})", env!("CARGO_PKG_VERSION")); - println!("{}", title); + println!("{title}"); println!("{0:=<1$}", "", title.len()); #[cfg(not(feature = "no_optimize"))] @@ -338,11 +337,11 @@ fn main() { history_offset += 1; } if input.contains('\n') { - println!("[{}] ~~~~", replacement_index); - println!("{}", input); + println!("[{replacement_index}] ~~~~"); + println!("{input}"); println!("~~~~"); } else { - println!("[{}] {}", replacement_index, input); + println!("[{replacement_index}] {input}"); } replacement_index = 0; } else { @@ -374,7 +373,7 @@ fn main() { Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break 'main_loop, Err(err) => { - eprintln!("Error: {:?}", err); + eprintln!("Error: {err:?}"); break 'main_loop; } } @@ -401,12 +400,12 @@ fn main() { "history" => { for (i, h) in rl.history().iter().enumerate() { match &h.split('\n').collect::>()[..] { - [line] => println!("[{}] {}", history_offset + i, line), + [line] => println!("[{}] {line}", history_offset + i), lines => { for (x, line) in lines.iter().enumerate() { let number = format!("[{}]", history_offset + i); if x == 0 { - println!("{} {}", number, line.trim_end()); + println!("{number} {}", line.trim_end()); } else { println!("{0:>1$} {2}", "", number.len(), line.trim_end()); } @@ -439,30 +438,30 @@ fn main() { continue; } "scope" => { - println!("{}", scope); + println!("{scope}"); continue; } #[cfg(not(feature = "no_optimize"))] "astu" => { // print the last un-optimized AST - println!("{:#?}\n", ast_u); + println!("{ast_u:#?}\n"); continue; } "ast" => { // print the last AST - println!("{:#?}\n", ast); + println!("{ast:#?}\n"); continue; } #[cfg(feature = "metadata")] "functions" => { // print a list of all registered functions for f in engine.gen_fn_signatures(false) { - println!("{}", f) + println!("{f}") } #[cfg(not(feature = "no_function"))] for f in main_ast.iter_functions() { - println!("{}", f) + println!("{f}") } println!(); @@ -505,7 +504,7 @@ fn main() { replacement = Some(line.clone()); replacement_index = history_offset + (rl.history().len() - 1 - n); } - None => eprintln!("History line not found: {}", text), + None => eprintln!("History line not found: {text}"), } continue; } @@ -561,7 +560,7 @@ fn main() { engine.eval_ast_with_scope::(&mut scope, &main_ast) }) { Ok(result) if !result.is::<()>() => { - println!("=> {:?}", result); + println!("=> {result:?}"); println!(); } Ok(_) => (), diff --git a/src/bin/rhai-run.rs b/src/bin/rhai-run.rs index e87e99b4..5bd464a3 100644 --- a/src/bin/rhai-run.rs +++ b/src/bin/rhai-run.rs @@ -7,12 +7,11 @@ fn eprint_error(input: &str, mut err: EvalAltResult) { let line = pos.line().unwrap(); let line_no = format!("{line}: "); - eprintln!("{}{}", line_no, lines[line - 1]); + eprintln!("{line_no}{}", lines[line - 1]); eprintln!( - "{:>1$} {2}", + "{:>1$} {err_msg}", "^", line_no.len() + pos.position().unwrap(), - err_msg ); eprintln!(); } @@ -24,7 +23,7 @@ fn eprint_error(input: &str, mut err: EvalAltResult) { if pos.is_none() { // No position - eprintln!("{}", err); + eprintln!("{err}"); } else { // Specific position eprint_line(&lines, pos, &err.to_string()) @@ -37,7 +36,7 @@ fn main() { for filename in env::args().skip(1) { let filename = match Path::new(&filename).canonicalize() { Err(err) => { - eprintln!("Error script file path: {}\n{}", filename, err); + eprintln!("Error script file path: {filename}\n{err}"); exit(1); } Ok(f) => match f.strip_prefix(std::env::current_dir().unwrap().canonicalize().unwrap()) @@ -94,7 +93,7 @@ fn main() { let filename = filename.to_string_lossy(); eprintln!("{:=<1$}", "", filename.len()); - eprintln!("{}", filename); + eprintln!("{filename}"); eprintln!("{:=<1$}", "", filename.len()); eprintln!(); diff --git a/src/engine.rs b/src/engine.rs index 8edc62ff..8071b9de 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -85,7 +85,7 @@ pub const OP_INCLUSIVE_RANGE: &str = Token::InclusiveRange.literal_syntax(); /// /// let result = engine.eval::("40 + 2")?; /// -/// println!("Answer: {}", result); // prints 42 +/// println!("Answer: {result}"); // prints 42 /// # Ok(()) /// # } /// ``` @@ -236,18 +236,12 @@ impl Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(target_family = "wasm"))] { - engine.print = Box::new(|s| println!("{}", s)); - engine.debug = Box::new(|s, source, pos| { - source.map_or_else( - || { - if pos.is_none() { - println!("{s}"); - } else { - println!("{pos:?} | {s}"); - } - }, - |source| println!("{source} @ {pos:?} | {s}"), - ) + engine.print = Box::new(|s| println!("{s}")); + engine.debug = Box::new(|s, source, pos| match (source, pos) { + (Some(source), crate::Position::NONE) => println!("{source} | {s}"), + (Some(source), pos) => println!("{source} @ {pos:?} | {s}"), + (None, crate::Position::NONE) => println!("{s}"), + (None, pos) => println!("{pos:?} | {s}"), }); } diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 249b0319..63bb3d22 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -148,35 +148,30 @@ impl fmt::Display for BreakPoint { pos, enabled, } => { - if source.is_empty() { - write!(f, "@ {:?}", pos)?; - } else { - write!(f, "{} @ {:?}", source, pos)?; + if !source.is_empty() { + write!(f, "{source} ")?; } + write!(f, "@ {pos:?}")?; if !*enabled { f.write_str(" (disabled)")?; } Ok(()) } - Self::AtFunctionName { - name: fn_name, - enabled, - } => { - write!(f, "{} (...)", fn_name)?; + Self::AtFunctionName { name, enabled } => { + write!(f, "{name} (...)")?; if !*enabled { f.write_str(" (disabled)")?; } Ok(()) } Self::AtFunctionCall { - name: fn_name, + name, args, enabled, } => { write!( f, - "{} ({})", - fn_name, + "{name} ({})", repeat("_").take(*args).collect::>().join(", ") )?; if !*enabled { @@ -185,11 +180,8 @@ impl fmt::Display for BreakPoint { Ok(()) } #[cfg(not(feature = "no_object"))] - Self::AtProperty { - name: prop, - enabled, - } => { - write!(f, ".{}", prop)?; + Self::AtProperty { name, enabled } => { + write!(f, ".{name}")?; if !*enabled { f.write_str(" (disabled)")?; } @@ -251,11 +243,10 @@ impl fmt::Display for CallStackFrame { fp.finish()?; if !self.pos.is_none() { - if self.source.is_empty() { - write!(f, " @ {:?}", self.pos)?; - } else { - write!(f, ": {} @ {:?}", self.source, self.pos)?; + if !self.source.is_empty() { + write!(f, ": {}", self.source)?; } + write!(f, " @ {:?}", self.pos)?; } Ok(()) diff --git a/src/func/callable_function.rs b/src/func/callable_function.rs index e6b85a29..f11d41a7 100644 --- a/src/func/callable_function.rs +++ b/src/func/callable_function.rs @@ -31,10 +31,10 @@ impl fmt::Debug for CallableFunction { #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Pure(..) => write!(f, "NativePureFunction"), - Self::Method(..) => write!(f, "NativeMethod"), - Self::Iterator(..) => write!(f, "NativeIterator"), - Self::Plugin(..) => write!(f, "PluginFunction"), + Self::Pure(..) => f.write_str("NativePureFunction"), + Self::Method(..) => f.write_str("NativeMethod"), + Self::Iterator(..) => f.write_str("NativeIterator"), + Self::Plugin(..) => f.write_str("PluginFunction"), #[cfg(not(feature = "no_function"))] Self::Script(fn_def) => fmt::Debug::fmt(fn_def, f), @@ -45,10 +45,10 @@ impl fmt::Debug for CallableFunction { impl fmt::Display for CallableFunction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Pure(..) => write!(f, "NativePureFunction"), - Self::Method(..) => write!(f, "NativeMethod"), - Self::Iterator(..) => write!(f, "NativeIterator"), - Self::Plugin(..) => write!(f, "PluginFunction"), + Self::Pure(..) => f.write_str("NativePureFunction"), + Self::Method(..) => f.write_str("NativeMethod"), + Self::Iterator(..) => f.write_str("NativeIterator"), + Self::Plugin(..) => f.write_str("PluginFunction"), #[cfg(not(feature = "no_function"))] Self::Script(s) => fmt::Display::fmt(s, f), diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 0c9b7661..c53b8ea3 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -416,7 +416,7 @@ impl Hash for Dynamic { impl fmt::Display for Dynamic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { - Union::Unit(..) => write!(f, ""), + Union::Unit(..) => Ok(()), Union::Bool(ref v, ..) => fmt::Display::fmt(v, f), Union::Str(ref v, ..) => fmt::Display::fmt(v, f), Union::Char(ref v, ..) => fmt::Display::fmt(v, f), @@ -527,7 +527,7 @@ impl fmt::Debug for Dynamic { if i > 0 && i % 8 == 0 { f.write_str(" ")?; } - write!(f, "{:02x}", v) + write!(f, "{v:02x}") })?; f.write_str("]") } diff --git a/src/types/parse_error.rs b/src/types/parse_error.rs index 7ac362af..d11a49c6 100644 --- a/src/types/parse_error.rs +++ b/src/types/parse_error.rs @@ -38,11 +38,11 @@ impl Error for LexError {} impl fmt::Display for LexError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s), - Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{}'", s), - Self::MalformedNumber(s) => write!(f, "Invalid number: '{}'", s), - Self::MalformedChar(s) => write!(f, "Invalid character: '{}'", s), - Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{}'", s), + Self::UnexpectedInput(s) => write!(f, "Unexpected '{s}'"), + Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{s}'"), + Self::MalformedNumber(s) => write!(f, "Invalid number: '{s}'"), + Self::MalformedChar(s) => write!(f, "Invalid character: '{s}'"), + Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{s}'"), Self::UnterminatedString => f.write_str("Open string is not terminated"), Self::StringTooLong(max) => write!( f, @@ -50,7 +50,7 @@ impl fmt::Display for LexError { max ), Self::ImproperSymbol(s, d) if d.is_empty() => { - write!(f, "Invalid symbol encountered: '{}'", s) + write!(f, "Invalid symbol encountered: '{s}'") } Self::ImproperSymbol(.., d) => f.write_str(d), } From 4708fde8bd6fd739dfa2a440db6cfd6793946bc0 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 27 Oct 2022 15:52:24 +0800 Subject: [PATCH 19/22] Compact data structures. --- src/eval/global_state.rs | 81 ++++++++++++++++++++++++---------------- src/func/call.rs | 4 +- src/func/native.rs | 2 +- 3 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 98e39870..a22a45a2 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -23,9 +23,12 @@ pub type GlobalConstants = // corresponds to that key. #[derive(Clone)] pub struct GlobalRuntimeState<'a> { + /// Names of imported [modules][crate::Module]. + #[cfg(not(feature = "no_module"))] + imports: crate::StaticVec, /// Stack of imported [modules][crate::Module]. #[cfg(not(feature = "no_module"))] - modules: crate::StaticVec<(crate::ImmutableString, crate::Shared)>, + modules: crate::StaticVec>, /// Source of the current context. /// /// No source if the string is empty. @@ -77,6 +80,8 @@ impl GlobalRuntimeState<'_> { #[must_use] pub fn new(engine: &Engine) -> Self { Self { + #[cfg(not(feature = "no_module"))] + imports: crate::StaticVec::new_const(), #[cfg(not(feature = "no_module"))] modules: crate::StaticVec::new_const(), source: Identifier::new_const(), @@ -127,7 +132,7 @@ impl GlobalRuntimeState<'_> { #[inline(always)] #[must_use] pub fn get_shared_import(&self, index: usize) -> Option> { - self.modules.get(index).map(|(_, m)| m).cloned() + self.modules.get(index).cloned() } /// Get a mutable reference to the globally-imported [module][crate::Module] at a /// particular index. @@ -141,7 +146,7 @@ impl GlobalRuntimeState<'_> { &mut self, index: usize, ) -> Option<&mut crate::Shared> { - self.modules.get_mut(index).map(|(_, m)| m) + self.modules.get_mut(index) } /// Get the index of a globally-imported [module][crate::Module] by name. /// @@ -150,13 +155,11 @@ impl GlobalRuntimeState<'_> { #[inline] #[must_use] pub fn find_import(&self, name: &str) -> Option { - let len = self.modules.len(); - - self.modules + self.imports .iter() .rev() - .position(|(key, _)| key.as_str() == name) - .map(|i| len - 1 - i) + .position(|key| key.as_str() == name) + .map(|i| self.imports.len() - 1 - i) } /// Push an imported [module][crate::Module] onto the stack. /// @@ -168,7 +171,8 @@ impl GlobalRuntimeState<'_> { name: impl Into, module: impl Into>, ) { - self.modules.push((name.into(), module.into())); + self.imports.push(name.into()); + self.modules.push(module.into()); } /// Truncate the stack of globally-imported [modules][crate::Module] to a particular length. /// @@ -176,17 +180,18 @@ impl GlobalRuntimeState<'_> { #[cfg(not(feature = "no_module"))] #[inline(always)] pub fn truncate_imports(&mut self, size: usize) { + self.imports.truncate(size); self.modules.truncate(size); } /// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order. /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] - #[allow(dead_code)] #[inline] pub fn iter_imports(&self) -> impl Iterator { - self.modules + self.imports .iter() + .zip(self.modules.iter()) .rev() .map(|(name, module)| (name.as_str(), &**module)) } @@ -194,23 +199,32 @@ impl GlobalRuntimeState<'_> { /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] - #[allow(dead_code)] #[inline] pub(crate) fn iter_imports_raw( &self, - ) -> impl Iterator)> { - self.modules.iter().rev() + ) -> 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. /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] - #[allow(dead_code)] #[inline] pub fn scan_imports_raw( &self, - ) -> impl Iterator)> { - self.modules.iter() + ) -> impl Iterator)> { + self.imports.iter().zip(self.modules.iter()) + } + /// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of + /// globally-imported [modules][crate::Module]? + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] + #[inline(always)] + pub(crate) fn may_contain_dynamic_fn(&self, hash_script: u64) -> bool { + self.modules + .iter() + .any(|m| m.may_contain_dynamic_fn(hash_script)) } /// Does the specified function hash key exist in the stack of globally-imported /// [modules][crate::Module]? @@ -221,9 +235,7 @@ impl GlobalRuntimeState<'_> { #[inline] #[must_use] pub fn contains_qualified_fn(&self, hash: u64) -> bool { - self.modules - .iter() - .any(|(_, m)| m.contains_qualified_fn(hash)) + self.modules.iter().any(|m| m.contains_qualified_fn(hash)) } /// Get the specified function via its hash key from the stack of globally-imported /// [modules][crate::Module]. @@ -239,7 +251,7 @@ impl GlobalRuntimeState<'_> { self.modules .iter() .rev() - .find_map(|(_, m)| m.get_qualified_fn(hash).map(|f| (f, m.id()))) + .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id()))) } /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of /// globally-imported [modules][crate::Module]? @@ -250,9 +262,7 @@ impl GlobalRuntimeState<'_> { #[inline] #[must_use] pub fn contains_iter(&self, id: std::any::TypeId) -> bool { - self.modules - .iter() - .any(|(_, m)| m.contains_qualified_iter(id)) + self.modules.iter().any(|m| m.contains_qualified_iter(id)) } /// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported /// [modules][crate::Module]. @@ -265,7 +275,7 @@ impl GlobalRuntimeState<'_> { self.modules .iter() .rev() - .find_map(|(_, m)| m.get_qualified_iter(id)) + .find_map(|m| m.get_qualified_iter(id)) } /// Get the current source. #[inline] @@ -309,25 +319,29 @@ impl GlobalRuntimeState<'_> { impl IntoIterator for GlobalRuntimeState<'_> { type Item = (crate::ImmutableString, crate::Shared); type IntoIter = std::iter::Rev< - smallvec::IntoIter< - [(crate::ImmutableString, crate::Shared); crate::STATIC_VEC_INLINE_SIZE], + std::iter::Zip< + smallvec::IntoIter<[crate::ImmutableString; crate::STATIC_VEC_INLINE_SIZE]>, + smallvec::IntoIter<[crate::Shared; crate::STATIC_VEC_INLINE_SIZE]>, >, >; fn into_iter(self) -> Self::IntoIter { - self.modules.into_iter().rev() + self.imports.into_iter().zip(self.modules.into_iter()).rev() } } #[cfg(not(feature = "no_module"))] impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> { - type Item = &'a (crate::ImmutableString, crate::Shared); + type Item = (&'a crate::ImmutableString, &'a crate::Shared); type IntoIter = std::iter::Rev< - std::slice::Iter<'a, (crate::ImmutableString, crate::Shared)>, + std::iter::Zip< + std::slice::Iter<'a, crate::ImmutableString>, + std::slice::Iter<'a, crate::Shared>, + >, >; fn into_iter(self) -> Self::IntoIter { - self.modules.iter().rev() + self.imports.iter().zip(self.modules.iter()).rev() } } @@ -338,7 +352,8 @@ impl, M: Into>> Ext #[inline] fn extend>(&mut self, iter: T) { for (k, m) in iter { - self.modules.push((k.into(), m.into())); + self.imports.push(k.into()); + self.modules.push(m.into()); } } } @@ -350,7 +365,7 @@ impl fmt::Debug for GlobalRuntimeState<'_> { let mut f = f.debug_struct("GlobalRuntimeState"); #[cfg(not(feature = "no_module"))] - f.field("imports", &self.modules); + f.field("imports", &self.scan_imports_raw().collect::>()); f.field("source", &self.source) .field("num_operations", &self.num_operations); diff --git a/src/func/call.rs b/src/func/call.rs index c3ca7511..9e0d6c31 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -258,9 +258,7 @@ impl Engine { #[cfg(not(feature = "no_module"))] let is_dynamic = is_dynamic - || _global - .iter_imports_raw() - .any(|(_, m)| m.may_contain_dynamic_fn(hash_base)) + || _global.may_contain_dynamic_fn(hash_base) || self .global_sub_modules .values() diff --git a/src/func/native.rs b/src/func/native.rs index 707f8f4c..6afda4da 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -229,7 +229,7 @@ impl<'a> NativeCallContext<'a> { #[inline] pub(crate) fn iter_imports_raw( &self, - ) -> impl Iterator)> { + ) -> impl Iterator)> { self.global.iter().flat_map(|&g| g.iter_imports_raw()) } /// _(internals)_ The current [`GlobalRuntimeState`], if any. From 41006455124eeece0e28c03676c34d4a6baa127d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 27 Oct 2022 20:42:10 +0800 Subject: [PATCH 20/22] Move pure checking out of functions. --- CHANGELOG.md | 1 + Cargo.toml | 2 +- codegen/src/function.rs | 14 ++------ codegen/src/test/function.rs | 11 +++++-- codegen/src/test/module.rs | 64 ++++++++++++++---------------------- src/bin/rhai-repl.rs | 8 ++++- src/eval/target.rs | 11 ------- src/func/call.rs | 24 +++++++------- src/func/plugin.rs | 10 ++++++ src/func/register.rs | 45 ++++++++++++------------- src/types/error.rs | 32 ++++++++++++++++-- 11 files changed, 120 insertions(+), 102 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61d09c7d..49aa5966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ Enhancements ------------ * `parse_json` function is added to parse a JSON string into an object map. +* `Error::ErrorNonPureMethodCallOnConstant` is added which is raised when a non-pure method is called on a constant value. Version 1.10.1 diff --git a/Cargo.toml b/Cargo.toml index d52f8b30..3999b7de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "codegen"] [package] name = "rhai" -version = "1.10.1" +version = "1.11.0" rust-version = "1.61.0" edition = "2018" resolver = "2" diff --git a/codegen/src/function.rs b/codegen/src/function.rs index d356645e..833e3d98 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -673,6 +673,7 @@ impl ExportedFn { let sig_name = self.name().clone(); let arg_count = self.arg_count(); let is_method_call = self.mutable_receiver(); + let is_pure = !self.mutable_receiver() || self.params().pure.is_some(); let mut unpack_statements = Vec::new(); let mut unpack_exprs = Vec::new(); @@ -713,18 +714,6 @@ impl ExportedFn { }) .unwrap(), ); - if self.params().pure.is_none() { - let arg_lit_str = - syn::LitStr::new(&pat.to_token_stream().to_string(), pat.span()); - unpack_statements.push( - syn::parse2::(quote! { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant(#arg_lit_str.to_string(), Position::NONE).into()); - } - }) - .unwrap(), - ); - } #[cfg(feature = "metadata")] input_type_names.push(arg_name); input_type_exprs.push( @@ -877,6 +866,7 @@ impl ExportedFn { } #[inline(always)] fn is_method_call(&self) -> bool { #is_method_call } + #[inline(always)] fn is_pure(&self) -> bool { #is_pure } } } } diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index 8465cd61..05d99130 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -285,6 +285,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } #[allow(unused)] #[doc(hidden)] @@ -323,6 +324,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } #[allow(unused)] #[doc(hidden)] @@ -361,6 +363,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } #[allow(unused)] #[doc(hidden)] @@ -401,6 +404,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } #[allow(unused)] #[doc(hidden)] @@ -434,6 +438,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } }; @@ -467,6 +472,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } #[allow(unused)] #[doc(hidden)] @@ -500,15 +506,13 @@ mod generate_tests { impl PluginFunction for Token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(increment(arg0, arg1))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } #[allow(unused)] #[doc(hidden)] @@ -548,6 +552,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } #[allow(unused)] #[doc(hidden)] diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 881746b9..0a279452 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -390,6 +390,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -467,6 +468,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -525,6 +527,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -582,6 +585,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -653,6 +657,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } #[allow(non_camel_case_types)] @@ -672,6 +677,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -730,6 +736,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -795,6 +802,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -864,14 +872,12 @@ mod generate_tests { impl PluginFunction for get_mystic_number_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(get_mystic_number(arg0))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1080,6 +1086,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -1170,6 +1177,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -1227,6 +1235,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { false } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -1286,6 +1295,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { true } } } }; @@ -1338,14 +1348,12 @@ mod generate_tests { impl PluginFunction for increment_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(increment(arg0))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1401,14 +1409,12 @@ mod generate_tests { impl PluginFunction for increment_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(increment(arg0))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } #[allow(unused_imports)] @@ -1487,14 +1493,12 @@ mod generate_tests { impl PluginFunction for increment_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(increment(arg0))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } #[allow(unused_imports)] @@ -1574,14 +1578,12 @@ mod generate_tests { impl PluginFunction for int_foo_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(int_foo(arg0))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1638,14 +1640,12 @@ mod generate_tests { impl PluginFunction for int_foo_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(int_foo(arg0))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1699,15 +1699,13 @@ mod generate_tests { impl PluginFunction for int_foo_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(int_foo(arg0, arg1))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1764,15 +1762,13 @@ mod generate_tests { impl PluginFunction for int_foo_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(int_foo(arg0, arg1))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1826,15 +1822,13 @@ mod generate_tests { impl PluginFunction for get_by_index_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(get_by_index(arg0, arg1))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1896,15 +1890,13 @@ mod generate_tests { impl PluginFunction for get_by_index_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(get_by_index(arg0, arg1))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -1961,15 +1953,13 @@ mod generate_tests { impl PluginFunction for get_by_index_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(get_by_index(arg0, arg1))) } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -2023,9 +2013,6 @@ mod generate_tests { impl PluginFunction for set_by_index_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg2 = mem::take(args[2usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); @@ -2033,6 +2020,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; @@ -2089,9 +2077,6 @@ mod generate_tests { impl PluginFunction for set_by_index_token { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { - if args[0usize].is_read_only() { - return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into()); - } let arg1 = mem::take(args[1usize]).cast::(); let arg2 = mem::take(args[2usize]).cast::(); let arg0 = &mut args[0usize].write_lock::().unwrap(); @@ -2099,6 +2084,7 @@ mod generate_tests { } #[inline(always)] fn is_method_call(&self) -> bool { true } + #[inline(always)] fn is_pure(&self) -> bool { false } } } }; diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index 71801d82..ea452269 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -309,7 +309,13 @@ fn main() { } // Register sample functions - engine.register_global_module(exported_module!(sample_functions).into()); + engine + .register_global_module(exported_module!(sample_functions).into()) + .register_get_set( + "test", + |x: &mut INT| *x % 2 == 0, + |x: &mut INT, y: bool| if y { *x *= 2 } else { *x /= 2 }, + ); // Create scope let mut scope = Scope::new(); diff --git a/src/eval/target.rs b/src/eval/target.rs index c48aa62a..a12aa96c 100644 --- a/src/eval/target.rs +++ b/src/eval/target.rs @@ -239,17 +239,6 @@ impl<'a> Target<'a> { _ => None, } } - /// Convert a shared or reference [`Target`] into a target with an owned value. - #[inline(always)] - #[must_use] - pub fn into_owned(self) -> Self { - match self { - Self::RefMut(r) => Self::TempValue(r.clone()), - #[cfg(not(feature = "no_closure"))] - Self::SharedValue { value, .. } => Self::TempValue(value), - _ => self, - } - } /// Get the source [`Dynamic`] of the [`Target`]. #[allow(dead_code)] #[inline] diff --git a/src/func/call.rs b/src/func/call.rs index 9e0d6c31..758767fb 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -418,7 +418,12 @@ impl Engine { let context = (self, name, source, &*global, lib, pos, level).into(); let result = if func.is_plugin_fn() { - func.get_plugin_fn().unwrap().call(context, args) + let f = func.get_plugin_fn().unwrap(); + if !f.is_pure() && !args.is_empty() && args[0].is_read_only() { + Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into()) + } else { + f.call(context, args) + } } else { func.get_native_fn().unwrap()(context, args) }; @@ -1229,13 +1234,9 @@ impl Engine { .map(|(value, ..)| arg_values.push(value.flatten())) })?; - let (mut target, _pos) = + let (target, _pos) = self.search_namespace(scope, global, lib, this_ptr, first_expr, level)?; - if target.is_read_only() { - target = target.into_owned(); - } - self.track_operation(global, _pos)?; #[cfg(not(feature = "no_closure"))] @@ -1425,11 +1426,12 @@ impl Engine { Some(f) if f.is_plugin_fn() => { let context = (self, fn_name, module.id(), &*global, lib, pos, level).into(); - let result = f - .get_plugin_fn() - .expect("plugin function") - .clone() - .call(context, &mut args); + 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()) + } else { + f.call(context, &mut args) + }; self.check_return_value(result, pos) } diff --git a/src/func/plugin.rs b/src/func/plugin.rs index 7c4f84fe..b4a9b9d0 100644 --- a/src/func/plugin.rs +++ b/src/func/plugin.rs @@ -29,4 +29,14 @@ pub trait PluginFunction { /// Is this plugin function a method? #[must_use] fn is_method_call(&self) -> bool; + + /// Is this plugin function pure? + /// + /// This defaults to `true` such that any old implementation that has constant-checking code + /// inside the function itself will continue to work. + #[inline(always)] + #[must_use] + fn is_pure(&self) -> bool { + true + } } diff --git a/src/func/register.rs b/src/func/register.rs index 928b08b0..8899548b 100644 --- a/src/func/register.rs +++ b/src/func/register.rs @@ -96,26 +96,27 @@ pub trait RegisterNativeFunction { const EXPECT_ARGS: &str = "arguments"; macro_rules! check_constant { - ($ctx:ident, $args:ident) => { + ($abi:ident, $ctx:ident, $args:ident) => { #[cfg(any(not(feature = "no_object"), not(feature = "no_index")))] - { - let args_len = $args.len(); - - if args_len > 0 && $args[0].is_read_only() { - let deny = match $ctx.fn_name() { - #[cfg(not(feature = "no_object"))] - f if args_len == 2 && f.starts_with(crate::engine::FN_SET) => true, - #[cfg(not(feature = "no_index"))] - crate::engine::FN_IDX_SET if args_len == 3 => true, - _ => false, - }; - if deny { - return Err(crate::ERR::ErrorAssignmentToConstant( - String::new(), - crate::Position::NONE, - ) - .into()); + if stringify!($abi) == "Method" && !$args.is_empty() { + let deny = match $args.len() { + #[cfg(not(feature = "no_index"))] + 3 if $ctx.fn_name() == crate::engine::FN_IDX_SET && $args[0].is_read_only() => true, + #[cfg(not(feature = "no_object"))] + 2 if $ctx.fn_name().starts_with(crate::engine::FN_SET) + && $args[0].is_read_only() => + { + true } + _ => false, + }; + + if deny { + return Err(crate::ERR::ErrorNonPureMethodCallOnConstant( + $ctx.fn_name().to_string(), + crate::Position::NONE, + ) + .into()); } } }; @@ -144,7 +145,7 @@ macro_rules! def_register { #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! - check_constant!(_ctx, args); + check_constant!($abi, _ctx, args); let mut _drain = args.iter_mut(); $($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )* @@ -169,7 +170,7 @@ macro_rules! def_register { #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! - check_constant!(ctx, args); + check_constant!($abi, ctx, args); let mut _drain = args.iter_mut(); $($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )* @@ -195,7 +196,7 @@ macro_rules! def_register { #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! - check_constant!(_ctx, args); + check_constant!($abi, _ctx, args); let mut _drain = args.iter_mut(); $($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )* @@ -218,7 +219,7 @@ macro_rules! def_register { #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! - check_constant!(ctx, args); + check_constant!($abi, ctx, args); let mut _drain = args.iter_mut(); $($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )* diff --git a/src/types/error.rs b/src/types/error.rs index 89bbedd8..e18414de 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -1,5 +1,6 @@ //! Module containing error definitions for the evaluation process. +use crate::engine::{FN_GET, FN_IDX_GET, FN_IDX_SET, FN_SET}; use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT}; #[cfg(feature = "no_std")] use core_error::Error; @@ -84,6 +85,8 @@ pub enum EvalAltResult { /// Data race detected when accessing a variable. Wrapped value is the variable name. ErrorDataRace(String, Position), + /// Calling a non-pure method on a constant. Wrapped value is the function name. + ErrorNonPureMethodCallOnConstant(String, Position), /// Assignment to a constant variable. Wrapped value is the variable name. ErrorAssignmentToConstant(String, Position), /// Inappropriate property access. Wrapped value is the property name. @@ -181,7 +184,29 @@ impl fmt::Display for EvalAltResult { } Self::ErrorRuntime(d, ..) => write!(f, "Runtime error: {d}")?, - Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant: {s}")?, + Self::ErrorNonPureMethodCallOnConstant(s, ..) if s.starts_with(FN_GET) => { + let prop = &s[FN_GET.len()..]; + write!( + f, + "Property {prop} is not pure and cannot be accessed on a constant" + )? + } + Self::ErrorNonPureMethodCallOnConstant(s, ..) if s.starts_with(FN_SET) => { + let prop = &s[FN_SET.len()..]; + write!(f, "Cannot modify property {prop} of constant")? + } + Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == FN_IDX_GET => write!( + f, + "Indexer is not pure and cannot be accessed on a constant" + )?, + Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == FN_IDX_SET => { + write!(f, "Cannot assign to indexer of constant")? + } + Self::ErrorNonPureMethodCallOnConstant(s, ..) => { + write!(f, "Non-pure method {s} cannot be called on constant")? + } + + Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant {s}")?, Self::ErrorMismatchOutputType(e, a, ..) => match (a.as_str(), e.as_str()) { ("", e) => write!(f, "Output type incorrect, expecting {e}"), (a, "") => write!(f, "Output type incorrect: {a}"), @@ -296,6 +321,7 @@ impl EvalAltResult { | Self::ErrorIndexNotFound(..) | Self::ErrorModuleNotFound(..) | Self::ErrorDataRace(..) + | Self::ErrorNonPureMethodCallOnConstant(..) | Self::ErrorAssignmentToConstant(..) | Self::ErrorMismatchOutputType(..) | Self::ErrorDotExpr(..) @@ -364,7 +390,7 @@ impl EvalAltResult { | Self::ErrorStackOverflow(..) | Self::ErrorRuntime(..) => (), - Self::ErrorFunctionNotFound(f, ..) => { + Self::ErrorFunctionNotFound(f, ..) | Self::ErrorNonPureMethodCallOnConstant(f, ..) => { map.insert("function".into(), f.into()); } Self::ErrorInFunctionCall(f, s, ..) => { @@ -459,6 +485,7 @@ impl EvalAltResult { | Self::ErrorIndexNotFound(.., pos) | Self::ErrorModuleNotFound(.., pos) | Self::ErrorDataRace(.., pos) + | Self::ErrorNonPureMethodCallOnConstant(.., pos) | Self::ErrorAssignmentToConstant(.., pos) | Self::ErrorMismatchOutputType(.., pos) | Self::ErrorDotExpr(.., pos) @@ -518,6 +545,7 @@ impl EvalAltResult { | Self::ErrorIndexNotFound(.., pos) | Self::ErrorModuleNotFound(.., pos) | Self::ErrorDataRace(.., pos) + | Self::ErrorNonPureMethodCallOnConstant(.., pos) | Self::ErrorAssignmentToConstant(.., pos) | Self::ErrorMismatchOutputType(.., pos) | Self::ErrorDotExpr(.., pos) From 25fde7dbed08ad3174c3d77aa017b799dad967d8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 27 Oct 2022 22:08:47 +0800 Subject: [PATCH 21/22] Fix tests. --- codegen/Cargo.toml | 4 ++-- src/eval/target.rs | 11 +++++++++++ src/func/call.rs | 6 +++++- tests/constants.rs | 4 ++-- tests/plugins.rs | 2 +- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index d384c210..c1efde59 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rhai_codegen" -version = "1.4.2" +version = "1.4.3" edition = "2018" resolver = "2" authors = ["jhwgh1968", "Stephen Chung"] @@ -22,5 +22,5 @@ syn = { version = "1.0", features = ["full", "parsing", "printing", "proc-macro" quote = "1" [dev-dependencies] -rhai = { path = "..", version = "1.6", features = ["metadata"] } +rhai = { path = "..", version = "1.11", features = ["metadata"] } trybuild = "1" diff --git a/src/eval/target.rs b/src/eval/target.rs index a12aa96c..c48aa62a 100644 --- a/src/eval/target.rs +++ b/src/eval/target.rs @@ -239,6 +239,17 @@ impl<'a> Target<'a> { _ => None, } } + /// Convert a shared or reference [`Target`] into a target with an owned value. + #[inline(always)] + #[must_use] + pub fn into_owned(self) -> Self { + match self { + Self::RefMut(r) => Self::TempValue(r.clone()), + #[cfg(not(feature = "no_closure"))] + Self::SharedValue { value, .. } => Self::TempValue(value), + _ => self, + } + } /// Get the source [`Dynamic`] of the [`Target`]. #[allow(dead_code)] #[inline] diff --git a/src/func/call.rs b/src/func/call.rs index 758767fb..ae4298fe 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1234,9 +1234,13 @@ impl Engine { .map(|(value, ..)| arg_values.push(value.flatten())) })?; - let (target, _pos) = + let (mut target, _pos) = self.search_namespace(scope, global, lib, this_ptr, first_expr, level)?; + if target.is_read_only() { + target = target.into_owned(); + } + self.track_operation(global, _pos)?; #[cfg(not(feature = "no_closure"))] diff --git a/tests/constants.rs b/tests/constants.rs index b7df1b3a..bc49e8ee 100644 --- a/tests/constants.rs +++ b/tests/constants.rs @@ -87,7 +87,7 @@ fn test_constant_mut() -> Result<(), Box> { " ) .expect_err("should error"), - EvalAltResult::ErrorAssignmentToConstant(..) + EvalAltResult::ErrorNonPureMethodCallOnConstant(..) )); let mut scope = Scope::new(); @@ -120,7 +120,7 @@ fn test_constant_mut() -> Result<(), Box> { *engine .run_with_scope(&mut scope, "MY_NUMBER.value = 42;") .expect_err("should error"), - EvalAltResult::ErrorAssignmentToConstant(..) + EvalAltResult::ErrorNonPureMethodCallOnConstant(..) )); Ok(()) diff --git a/tests/plugins.rs b/tests/plugins.rs index f5c8d966..ab752bc3 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -119,7 +119,7 @@ fn test_plugins_package() -> Result<(), Box> { assert!( matches!(*engine.run("const A = [1, 2, 3]; A.test(42);").expect_err("should error"), - EvalAltResult::ErrorAssignmentToConstant(x, ..) if x == "array") + EvalAltResult::ErrorNonPureMethodCallOnConstant(x, ..) if x == "test") ) } From 6de493c8c2167edcd1d08747c9f79a025a022df3 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 27 Oct 2022 22:25:18 +0800 Subject: [PATCH 22/22] Fix builds. --- src/types/error.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/types/error.rs b/src/types/error.rs index e18414de..7991c44f 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -1,6 +1,5 @@ //! Module containing error definitions for the evaluation process. -use crate::engine::{FN_GET, FN_IDX_GET, FN_IDX_SET, FN_SET}; use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT}; #[cfg(feature = "no_std")] use core_error::Error; @@ -184,22 +183,32 @@ impl fmt::Display for EvalAltResult { } Self::ErrorRuntime(d, ..) => write!(f, "Runtime error: {d}")?, - Self::ErrorNonPureMethodCallOnConstant(s, ..) if s.starts_with(FN_GET) => { - let prop = &s[FN_GET.len()..]; + #[cfg(not(feature = "no_object"))] + Self::ErrorNonPureMethodCallOnConstant(s, ..) + if s.starts_with(crate::engine::FN_GET) => + { + let prop = &s[crate::engine::FN_GET.len()..]; write!( f, "Property {prop} is not pure and cannot be accessed on a constant" )? } - Self::ErrorNonPureMethodCallOnConstant(s, ..) if s.starts_with(FN_SET) => { - let prop = &s[FN_SET.len()..]; + #[cfg(not(feature = "no_object"))] + Self::ErrorNonPureMethodCallOnConstant(s, ..) + if s.starts_with(crate::engine::FN_SET) => + { + let prop = &s[crate::engine::FN_SET.len()..]; write!(f, "Cannot modify property {prop} of constant")? } - Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == FN_IDX_GET => write!( - f, - "Indexer is not pure and cannot be accessed on a constant" - )?, - Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == FN_IDX_SET => { + #[cfg(not(feature = "no_index"))] + Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_GET => { + write!( + f, + "Indexer is not pure and cannot be accessed on a constant" + )? + } + #[cfg(not(feature = "no_index"))] + Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_SET => { write!(f, "Cannot assign to indexer of constant")? } Self::ErrorNonPureMethodCallOnConstant(s, ..) => {