diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ede0882..142f71ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ Version 0.20.1 This version enables functions to access constants declared at global level via the special `global` module. +Bug fixes +--------- + +* Fixed bug when position is zero in `insert` and `split_at` methods for arrays. + Breaking changes ---------------- diff --git a/Cargo.toml b/Cargo.toml index d6e30562..e97fc7f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,7 +88,7 @@ default_features = false optional = true [dependencies.rust_decimal] -version = "1.10" +version = "1.11" default_features = false optional = true diff --git a/codegen/ui_tests/rhai_mod_unknown_type.stderr b/codegen/ui_tests/rhai_mod_unknown_type.stderr index 5f61d9d3..6997a79d 100644 --- a/codegen/ui_tests/rhai_mod_unknown_type.stderr +++ b/codegen/ui_tests/rhai_mod_unknown_type.stderr @@ -15,8 +15,6 @@ help: consider importing one of these items | 11 | use core::fmt::Pointer; | -11 | use crate::mem::fmt::Pointer; - | 11 | use std::fmt::Pointer; | 11 | use syn::__private::fmt::Pointer; diff --git a/scripts/primes.rhai b/scripts/primes.rhai index 380750d7..085228ee 100644 --- a/scripts/primes.rhai +++ b/scripts/primes.rhai @@ -21,7 +21,6 @@ for p in range(2, MAX_NUMBER_TO_CHECK) { for i in range(2 * p, MAX_NUMBER_TO_CHECK, p) { prime_mask[i] = false; - i += p; } } diff --git a/src/dynamic.rs b/src/dynamic.rs index 4ae4d80f..cc0ca5c7 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -188,11 +188,11 @@ pub enum Union { /// This data structure provides transparent interoperability between /// normal [`Dynamic`] and shared [`Dynamic`] values. #[derive(Debug)] -pub struct DynamicReadLock<'d, T: Variant + Clone>(DynamicReadLockInner<'d, T>); +pub struct DynamicReadLock<'d, T: Clone>(DynamicReadLockInner<'d, T>); /// Different types of read guards for [`DynamicReadLock`]. #[derive(Debug)] -enum DynamicReadLockInner<'d, T: Variant + Clone> { +enum DynamicReadLockInner<'d, T: Clone> { /// A simple reference to a non-shared value. Reference(&'d T), @@ -206,7 +206,7 @@ enum DynamicReadLockInner<'d, T: Variant + Clone> { Guard(std::sync::RwLockReadGuard<'d, Dynamic>), } -impl<'d, T: Variant + Clone> Deref for DynamicReadLock<'d, T> { +impl<'d, T: Any + Clone> Deref for DynamicReadLock<'d, T> { type Target = T; #[inline(always)] @@ -225,11 +225,11 @@ impl<'d, T: Variant + Clone> Deref for DynamicReadLock<'d, T> { /// This data structure provides transparent interoperability between /// normal [`Dynamic`] and shared [`Dynamic`] values. #[derive(Debug)] -pub struct DynamicWriteLock<'d, T: Variant + Clone>(DynamicWriteLockInner<'d, T>); +pub struct DynamicWriteLock<'d, T: Clone>(DynamicWriteLockInner<'d, T>); /// Different types of write guards for [`DynamicReadLock`]. #[derive(Debug)] -enum DynamicWriteLockInner<'d, T: Variant + Clone> { +enum DynamicWriteLockInner<'d, T: Clone> { /// A simple mutable reference to a non-shared value. Reference(&'d mut T), @@ -243,7 +243,7 @@ enum DynamicWriteLockInner<'d, T: Variant + Clone> { Guard(std::sync::RwLockWriteGuard<'d, Dynamic>), } -impl<'d, T: Variant + Clone> Deref for DynamicWriteLock<'d, T> { +impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> { type Target = T; #[inline(always)] @@ -257,7 +257,7 @@ impl<'d, T: Variant + Clone> Deref for DynamicWriteLock<'d, T> { } } -impl<'d, T: Variant + Clone> DerefMut for DynamicWriteLock<'d, T> { +impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { match &mut self.0 { @@ -298,7 +298,7 @@ impl Dynamic { /// If the [`Dynamic`] is a shared variant checking is performed on /// top of its internal value. #[inline(always)] - pub fn is(&self) -> bool { + pub fn is(&self) -> bool { let mut target_type_id = TypeId::of::(); if target_type_id == TypeId::of::() { @@ -983,7 +983,7 @@ impl Dynamic { /// assert_eq!(x.try_cast::().unwrap(), 42); /// ``` #[inline(always)] - pub fn try_cast(self) -> Option { + pub fn try_cast(self) -> Option { // Coded this way in order to maximally leverage potentials for dead-code removal. #[cfg(not(feature = "no_closure"))] @@ -1118,7 +1118,7 @@ impl Dynamic { /// assert_eq!(x.cast::(), 42); /// ``` #[inline(always)] - pub fn cast(self) -> T { + pub fn cast(self) -> T { #[cfg(not(feature = "no_closure"))] let self_type_name = if self.is_shared() { // Avoid panics/deadlocks with shared values @@ -1165,7 +1165,7 @@ impl Dynamic { /// assert_eq!(y.clone_cast::(), 42); /// ``` #[inline(always)] - pub fn clone_cast(&self) -> T { + pub fn clone_cast(&self) -> T { self.read_lock::().unwrap().clone() } /// Flatten the [`Dynamic`] and clone it. @@ -1293,7 +1293,7 @@ impl Dynamic { /// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). /// Otherwise, this call panics if the data is currently borrowed for write. #[inline(always)] - pub fn read_lock(&self) -> Option> { + pub fn read_lock(&self) -> Option> { match self.0 { #[cfg(not(feature = "no_closure"))] Union::Shared(ref cell, _) => { @@ -1326,7 +1326,7 @@ impl Dynamic { /// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). /// Otherwise, this call panics if the data is currently borrowed for write. #[inline(always)] - pub fn write_lock(&mut self) -> Option> { + pub fn write_lock(&mut self) -> Option> { match self.0 { #[cfg(not(feature = "no_closure"))] Union::Shared(ref cell, _) => { @@ -1354,7 +1354,7 @@ impl Dynamic { /// /// Returns [`None`] if the cast fails, or if the value is shared. #[inline(always)] - pub(crate) fn downcast_ref(&self) -> Option<&T> { + pub(crate) fn downcast_ref(&self) -> Option<&T> { // Coded this way in order to maximally leverage potentials for dead-code removal. if TypeId::of::() == TypeId::of::() { @@ -1450,7 +1450,7 @@ impl Dynamic { /// /// Returns [`None`] if the cast fails, or if the value is shared. #[inline(always)] - pub(crate) fn downcast_mut(&mut self) -> Option<&mut T> { + pub(crate) fn downcast_mut(&mut self) -> Option<&mut T> { // Coded this way in order to maximally leverage potentials for dead-code removal. if TypeId::of::() == TypeId::of::() { diff --git a/src/engine.rs b/src/engine.rs index c5a24897..f38cf536 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2538,7 +2538,6 @@ impl Engine { if let Some(global) = global { let global = Shared::get_mut(global).unwrap(); global.set_var(name.clone(), value.clone()); - global.build_index(); } } diff --git a/src/engine_api.rs b/src/engine_api.rs index b0aab3e1..918263d3 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1696,6 +1696,11 @@ impl Engine { } let statements = ast.statements(); + + if statements.is_empty() { + return Ok(Dynamic::UNIT); + } + let lib = &[ast.lib()]; self.eval_global_statements(scope, mods, &mut state, statements, lib, level) } @@ -1771,9 +1776,12 @@ impl Engine { { state.resolver = ast.resolver(); } + let statements = ast.statements(); - let lib = &[ast.lib()]; - self.eval_global_statements(scope, mods, &mut state, statements, lib, 0)?; + if !statements.is_empty() { + let lib = &[ast.lib()]; + self.eval_global_statements(scope, mods, &mut state, statements, lib, 0)?; + } Ok(()) } /// Call a script function defined in an [`AST`] with multiple arguments. @@ -1931,21 +1939,21 @@ impl Engine { let state = &mut Default::default(); let mods = &mut Default::default(); let lib = &[ast.lib()]; + let statements = ast.statements(); + let name = name.as_ref(); - if eval_ast { - self.eval_global_statements(scope, mods, state, ast.statements(), lib, 0)?; + if eval_ast && !statements.is_empty() { + self.eval_global_statements(scope, mods, state, statements, lib, 0)?; } let fn_def = ast .lib() - .get_script_fn(name.as_ref(), args.len()) - .ok_or_else(|| { - EvalAltResult::ErrorFunctionNotFound(name.as_ref().into(), Position::NONE) - })?; + .get_script_fn(name, args.len()) + .ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?; // Check for data race. #[cfg(not(feature = "no_closure"))] - crate::fn_call::ensure_no_data_race(name.as_ref(), args, false)?; + crate::fn_call::ensure_no_data_race(name, args, false)?; self.call_script_fn( scope, diff --git a/src/fn_call.rs b/src/fn_call.rs index 2a5c904c..2443fef1 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -867,13 +867,18 @@ impl Engine { return Err(ParseErrorType::WrongFnDefinition.into()); } + let statements = ast.statements(); + if statements.is_empty() { + return Ok(Dynamic::UNIT); + } + // Evaluate the AST let mut new_state: State = Default::default(); new_state.source = state.source.clone(); new_state.operations = state.operations; let result = - self.eval_global_statements(scope, mods, &mut new_state, ast.statements(), lib, level); + self.eval_global_statements(scope, mods, &mut new_state, statements, lib, level); state.operations = new_state.operations; diff --git a/src/module/mod.rs b/src/module/mod.rs index 7a8881c6..50088597 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -16,7 +16,7 @@ use std::{ any::TypeId, collections::{BTreeMap, BTreeSet}, fmt, - iter::empty, + iter::{empty, once}, num::NonZeroUsize, ops::{Add, AddAssign, Deref, DerefMut}, }; @@ -166,7 +166,7 @@ impl Default for Module { all_functions: Default::default(), type_iterators: Default::default(), all_type_iterators: Default::default(), - indexed: false, + indexed: true, contains_indexed_global_functions: false, identifiers: Default::default(), } @@ -316,7 +316,7 @@ impl Module { self.internal } - /// Set the interal status of the [`Module`]. + /// Set the internal status of the [`Module`]. #[inline(always)] pub(crate) fn set_internal(&mut self, value: bool) -> &mut Self { self.internal = value; @@ -346,12 +346,17 @@ impl Module { /// Is the [`Module`] indexed? /// + /// A module must be indexed before it can be used in an `import` statement. + /// /// # Example /// /// ``` /// use rhai::Module; /// /// let mut module = Module::new(); + /// assert!(module.is_indexed()); + /// + /// module.set_native_fn("foo", |x: &mut i64, y: i64| { *x = y; Ok(()) }); /// assert!(!module.is_indexed()); /// /// # #[cfg(not(feature = "no_module"))] @@ -446,8 +451,14 @@ impl Module { name: impl Into, value: impl Variant + Clone, ) -> &mut Self { - self.variables.insert(name.into(), Dynamic::from(value)); - self.indexed = false; + let ident = name.into(); + let value = Dynamic::from(value); + + if self.indexed { + let hash_var = crate::calc_fn_hash(once(""), &ident, 0); + self.all_variables.insert(hash_var, value.clone()); + } + self.variables.insert(ident, value); self } @@ -1015,8 +1026,7 @@ impl Module { /// /// let mut module = Module::new(); /// let hash = module.set_indexer_set_fn(|x: &mut i64, y: ImmutableString, value: i64| { - /// *x = y.len() as i64 + value; - /// Ok(()) + /// *x = y.len() as i64 + value; Ok(()) /// }); /// assert!(module.contains_fn(hash)); /// ``` @@ -1080,8 +1090,7 @@ impl Module { /// Ok(*x + y.len() as i64) /// }, /// |x: &mut i64, y: ImmutableString, value: i64| { - /// *x = y.len() as i64 + value; - /// Ok(()) + /// *x = y.len() as i64 + value; Ok(()) /// } /// ); /// assert!(module.contains_fn(hash_get)); @@ -1417,10 +1426,10 @@ impl Module { match aliases.len() { 0 => (), 1 => { - module.variables.insert(aliases.pop().unwrap(), value); + module.set_var(aliases.pop().unwrap(), value); } _ => aliases.into_iter().for_each(|alias| { - module.variables.insert(alias, value.clone()); + module.set_var(alias, value.clone()); }), } }); @@ -1538,7 +1547,7 @@ impl Module { let mut functions = Default::default(); let mut type_iterators = Default::default(); - path.push("root"); + path.push(""); self.contains_indexed_global_functions = index_module( self, @@ -1571,10 +1580,12 @@ impl Module { /// Set a type iterator into the [`Module`]. #[inline(always)] - pub fn set_iter(&mut self, typ: TypeId, func: IteratorFn) -> &mut Self { - self.type_iterators.insert(typ, func); - self.indexed = false; - self.contains_indexed_global_functions = false; + pub fn set_iter(&mut self, type_id: TypeId, func: IteratorFn) -> &mut Self { + if self.indexed { + self.all_type_iterators.insert(type_id, func.clone()); + self.contains_indexed_global_functions = true; + } + self.type_iterators.insert(type_id, func); self } diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 0751428c..f17a62aa 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -35,7 +35,7 @@ mod array_functions { array } pub fn insert(array: &mut Array, position: INT, item: Dynamic) { - if position <= 0 { + if position < 0 { if let Some(n) = position.checked_abs() { if n as usize > array.len() { array.insert(0, item); @@ -174,7 +174,7 @@ mod array_functions { } #[rhai_fn(name = "split")] pub fn split_at(array: &mut Array, start: INT) -> Array { - if start <= 0 { + if start < 0 { if let Some(n) = start.checked_abs() { if n as usize > array.len() { mem::take(array) diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 1012b6d2..c3e992e0 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -351,7 +351,7 @@ mod decimal_functions { } } - Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::RoundUp)) + Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::AwayFromZero)) } #[rhai_fn(return_raw)] pub fn round_down(x: Decimal, dp: INT) -> Result> { @@ -367,7 +367,7 @@ mod decimal_functions { } } - Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::RoundDown)) + Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::ToZero)) } #[rhai_fn(return_raw)] pub fn round_half_up(x: Decimal, dp: INT) -> Result> { @@ -383,7 +383,7 @@ mod decimal_functions { } } - Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::RoundHalfUp)) + Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointAwayFromZero)) } #[rhai_fn(return_raw)] pub fn round_half_down(x: Decimal, dp: INT) -> Result> { @@ -399,7 +399,7 @@ mod decimal_functions { } } - Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::RoundHalfDown)) + Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointTowardZero)) } #[rhai_fn(name = "int", get = "int")] pub fn int(x: Decimal) -> Decimal { diff --git a/src/unsafe.rs b/src/unsafe.rs index 54cbab67..01e663aa 100644 --- a/src/unsafe.rs +++ b/src/unsafe.rs @@ -1,6 +1,5 @@ //! A helper module containing unsafe utility functions. -use crate::dynamic::Variant; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ @@ -27,7 +26,7 @@ pub fn unsafe_try_cast(a: A) -> Result { /// Cast a Boxed type into another type. #[inline(always)] -pub fn unsafe_cast_box(item: Box) -> Result, Box> { +pub fn unsafe_cast_box(item: Box) -> Result, Box> { // Only allow casting to the exact same type if TypeId::of::() == TypeId::of::() { // SAFETY: just checked whether we are pointing to the correct type diff --git a/tests/arrays.rs b/tests/arrays.rs index 0d080557..3760305d 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -1,8 +1,17 @@ #![cfg(not(feature = "no_index"))] use rhai::{Array, Engine, EvalAltResult, INT}; +fn convert_to_vec(array: Array) -> Vec { + array.into_iter().map(|v| v.clone_cast::()).collect() +} + #[test] fn test_arrays() -> Result<(), Box> { + let mut a = Array::new(); + a.push((42 as INT).into()); + + assert_eq!(a[0].clone_cast::(), 42); + let engine = Engine::new(); assert_eq!(engine.eval::("let x = [1, 2, 3]; x[1]")?, 2); @@ -14,58 +23,78 @@ fn test_arrays() -> Result<(), Box> { ); assert_eq!(engine.eval::("let y = [1, 2, 3]; y[0]")?, 1); assert_eq!(engine.eval::("let y = [1, 2, 3]; y[-1]")?, 3); + assert_eq!(engine.eval::("let y = [1, 2, 3]; y[-3]")?, 1); assert!(engine.eval::("let y = [1, 2, 3]; 2 in y")?); assert_eq!(engine.eval::("let y = [1, 2, 3]; y += 4; y[3]")?, 4); - - #[cfg(not(feature = "no_object"))] - assert_eq!(engine.eval::("let y = [1, 2, 3]; y.push(4); y[3]")?, 4); - - #[cfg(not(feature = "no_object"))] assert_eq!( - engine.eval::( - r" - let x = [2, 9]; - x.insert(-1, 1); - x.insert(999, 3); - - let r = x.remove(2); - - let y = [4, 5]; - x.append(y); - - x.len + r - " - )?, - 14 + convert_to_vec::(engine.eval("let y = [1, 2, 3]; y[1] += 4; y")?), + [1, 6, 3] ); + + #[cfg(not(feature = "no_object"))] + { + assert_eq!( + convert_to_vec::(engine.eval("let y = [1, 2, 3]; y.push(4); y")?), + [1, 2, 3, 4] + ); + assert_eq!( + convert_to_vec::(engine.eval("let y = [1, 2, 3]; y.insert(0, 4); y")?), + [4, 1, 2, 3] + ); + assert_eq!( + convert_to_vec::(engine.eval("let y = [1, 2, 3]; y.insert(999, 4); y")?), + [1, 2, 3, 4] + ); + assert_eq!( + convert_to_vec::(engine.eval("let y = [1, 2, 3]; y.insert(-2, 4); y")?), + [1, 4, 2, 3] + ); + assert_eq!( + convert_to_vec::(engine.eval("let y = [1, 2, 3]; y.insert(-999, 4); y")?), + [4, 1, 2, 3] + ); + + assert_eq!( + convert_to_vec::(engine.eval( + r" + let x = [2, 9]; + x.insert(-1, 1); + x.insert(999, 3); + x.insert(-9, 99); + + let r = x.remove(2); + + let y = [4, 5]; + x.append(y); + + x + " + )?), + [99, 2, 9, 3, 4, 5] + ); + } + assert_eq!( - engine.eval::( + convert_to_vec::(engine.eval( r" let x = [1, 2, 3]; x += [4, 5]; - len(x) + x " - )?, - 5 + )?), + [1, 2, 3, 4, 5] ); assert_eq!( - engine - .eval::( - r" - let x = [1, 2, 3]; - let y = [4, 5]; - x + y - " - )? - .len(), - 5 + convert_to_vec::(engine.eval( + r" + let x = [1, 2, 3]; + let y = [4, 5]; + x + y + " + )?), + [1, 2, 3, 4, 5] ); - let mut a = Array::new(); - a.push((42 as INT).into()); - - assert_eq!(a[0].clone_cast::(), 42); - Ok(()) } @@ -128,47 +157,43 @@ fn test_arrays_map_reduce() -> Result<(), Box> { let engine = Engine::new(); assert_eq!( - engine.eval::( + convert_to_vec::(engine.eval( r" let x = [1, 2, 3]; - let y = x.filter(|v| v > 2); - y[0] + x.filter(|v| v > 2) " - )?, - 3 + )?), + [3] ); assert_eq!( - engine.eval::( + convert_to_vec::(engine.eval( r" let x = [1, 2, 3]; - let y = x.filter(|v, i| v > i); - y.len() + x.filter(|v, i| v > i) " - )?, - 3 + )?), + [1, 2, 3] ); assert_eq!( - engine.eval::( + convert_to_vec::(engine.eval( r" let x = [1, 2, 3]; - let y = x.map(|v| v * 2); - y[2] + x.map(|v| v * 2) " - )?, - 6 + )?), + [2, 4, 6] ); assert_eq!( - engine.eval::( + convert_to_vec::(engine.eval( r" let x = [1, 2, 3]; - let y = x.map(|v, i| v * i); - y[2] + x.map(|v, i| v * i) " - )?, - 6 + )?), + [0, 2, 6] ); assert_eq!(