Merge pull request #404 from schungx/master

Fix bugs.
This commit is contained in:
Stephen Chung 2021-04-19 23:05:58 +08:00 committed by GitHub
commit b73d7f21a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 162 additions and 113 deletions

View File

@ -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
----------------

View File

@ -88,7 +88,7 @@ default_features = false
optional = true
[dependencies.rust_decimal]
version = "1.10"
version = "1.11"
default_features = false
optional = true

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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<T: Variant + Clone>(&self) -> bool {
pub fn is<T: Any + Clone>(&self) -> bool {
let mut target_type_id = TypeId::of::<T>();
if target_type_id == TypeId::of::<String>() {
@ -983,7 +983,7 @@ impl Dynamic {
/// assert_eq!(x.try_cast::<u32>().unwrap(), 42);
/// ```
#[inline(always)]
pub fn try_cast<T: Variant>(self) -> Option<T> {
pub fn try_cast<T: Any>(self) -> Option<T> {
// 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::<u32>(), 42);
/// ```
#[inline(always)]
pub fn cast<T: Variant + Clone>(self) -> T {
pub fn cast<T: Any + Clone>(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::<u32>(), 42);
/// ```
#[inline(always)]
pub fn clone_cast<T: Variant + Clone>(&self) -> T {
pub fn clone_cast<T: Any + Clone>(&self) -> T {
self.read_lock::<T>().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<T: Variant + Clone>(&self) -> Option<DynamicReadLock<T>> {
pub fn read_lock<T: Any + Clone>(&self) -> Option<DynamicReadLock<T>> {
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<T: Variant + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
pub fn write_lock<T: Any + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
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<T: Variant + Clone>(&self) -> Option<&T> {
pub(crate) fn downcast_ref<T: Any + Clone>(&self) -> Option<&T> {
// Coded this way in order to maximally leverage potentials for dead-code removal.
if TypeId::of::<T>() == TypeId::of::<INT>() {
@ -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<T: Variant + Clone>(&mut self) -> Option<&mut T> {
pub(crate) fn downcast_mut<T: Any + Clone>(&mut self) -> Option<&mut T> {
// Coded this way in order to maximally leverage potentials for dead-code removal.
if TypeId::of::<T>() == TypeId::of::<INT>() {

View File

@ -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();
}
}

View File

@ -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,

View File

@ -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;

View File

@ -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<Identifier>,
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
}

View File

@ -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)

View File

@ -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<Decimal, Box<EvalAltResult>> {
@ -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<Decimal, Box<EvalAltResult>> {
@ -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<Decimal, Box<EvalAltResult>> {
@ -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 {

View File

@ -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: Any, B: Any>(a: A) -> Result<B, A> {
/// Cast a Boxed type into another type.
#[inline(always)]
pub fn unsafe_cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, Box<X>> {
pub fn unsafe_cast_box<X: Any, T: Any>(item: Box<X>) -> Result<Box<T>, Box<X>> {
// Only allow casting to the exact same type
if TypeId::of::<X>() == TypeId::of::<T>() {
// SAFETY: just checked whether we are pointing to the correct type

View File

@ -1,8 +1,17 @@
#![cfg(not(feature = "no_index"))]
use rhai::{Array, Engine, EvalAltResult, INT};
fn convert_to_vec<T: Clone + 'static>(array: Array) -> Vec<T> {
array.into_iter().map(|v| v.clone_cast::<T>()).collect()
}
#[test]
fn test_arrays() -> Result<(), Box<EvalAltResult>> {
let mut a = Array::new();
a.push((42 as INT).into());
assert_eq!(a[0].clone_cast::<INT>(), 42);
let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = [1, 2, 3]; x[1]")?, 2);
@ -14,58 +23,78 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
);
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y[0]")?, 1);
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y[-1]")?, 3);
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y[-3]")?, 1);
assert!(engine.eval::<bool>("let y = [1, 2, 3]; 2 in y")?);
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y += 4; y[3]")?, 4);
#[cfg(not(feature = "no_object"))]
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y.push(4); y[3]")?, 4);
#[cfg(not(feature = "no_object"))]
assert_eq!(
engine.eval::<INT>(
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::<INT>(engine.eval("let y = [1, 2, 3]; y[1] += 4; y")?),
[1, 6, 3]
);
#[cfg(not(feature = "no_object"))]
{
assert_eq!(
convert_to_vec::<INT>(engine.eval("let y = [1, 2, 3]; y.push(4); y")?),
[1, 2, 3, 4]
);
assert_eq!(
convert_to_vec::<INT>(engine.eval("let y = [1, 2, 3]; y.insert(0, 4); y")?),
[4, 1, 2, 3]
);
assert_eq!(
convert_to_vec::<INT>(engine.eval("let y = [1, 2, 3]; y.insert(999, 4); y")?),
[1, 2, 3, 4]
);
assert_eq!(
convert_to_vec::<INT>(engine.eval("let y = [1, 2, 3]; y.insert(-2, 4); y")?),
[1, 4, 2, 3]
);
assert_eq!(
convert_to_vec::<INT>(engine.eval("let y = [1, 2, 3]; y.insert(-999, 4); y")?),
[4, 1, 2, 3]
);
assert_eq!(
convert_to_vec::<INT>(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::<INT>(
convert_to_vec::<INT>(engine.eval(
r"
let x = [1, 2, 3];
x += [4, 5];
len(x)
x
"
)?,
5
)?),
[1, 2, 3, 4, 5]
);
assert_eq!(
engine
.eval::<Array>(
r"
let x = [1, 2, 3];
let y = [4, 5];
x + y
"
)?
.len(),
5
convert_to_vec::<INT>(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::<INT>(), 42);
Ok(())
}
@ -128,47 +157,43 @@ fn test_arrays_map_reduce() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new();
assert_eq!(
engine.eval::<INT>(
convert_to_vec::<INT>(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::<INT>(
convert_to_vec::<INT>(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::<INT>(
convert_to_vec::<INT>(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::<INT>(
convert_to_vec::<INT>(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!(