Implement closures.

This commit is contained in:
Stephen Chung
2020-08-03 12:10:20 +08:00
parent 747c0345f2
commit 4079164bfd
24 changed files with 340 additions and 588 deletions

View File

@@ -4,7 +4,7 @@ use crate::fn_native::{FnPtr, SendSync};
use crate::parser::{ImmutableString, INT};
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
use crate::fn_native::SharedMut;
#[cfg(not(feature = "no_float"))]
@@ -24,14 +24,14 @@ use crate::stdlib::{
string::String,
};
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "sync"))]
use crate::stdlib::{
cell::{Ref, RefCell, RefMut},
rc::Rc,
};
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(feature = "sync")]
use crate::stdlib::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
@@ -159,7 +159,7 @@ pub enum Union {
Map(Box<Map>),
FnPtr(Box<FnPtr>),
Variant(Box<Box<dyn Variant>>),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
Shared(SharedMut<Dynamic>),
}
@@ -176,11 +176,11 @@ enum DynamicReadLockInner<'d, T: Variant + Clone> {
/// A simple reference to a non-shared value.
Reference(&'d T),
/// A read guard to a shared `RefCell`.
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "sync"))]
Guard(Ref<'d, Dynamic>),
/// A read guard to a shared `RwLock`.
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(feature = "sync")]
Guard(RwLockReadGuard<'d, Dynamic>),
}
@@ -193,7 +193,7 @@ impl<'d, T: Variant + Clone> Deref for DynamicReadLock<'d, T> {
match &self.0 {
DynamicReadLockInner::Reference(reference) => *reference,
// Unwrapping is safe because all checking is already done in its constructor
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
DynamicReadLockInner::Guard(guard) => guard.downcast_ref().unwrap(),
}
}
@@ -212,11 +212,11 @@ enum DynamicWriteLockInner<'d, T: Variant + Clone> {
/// A simple mutable reference to a non-shared value.
Reference(&'d mut T),
/// A write guard to a shared `RefCell`.
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "sync"))]
Guard(RefMut<'d, Dynamic>),
/// A write guard to a shared `RwLock`.
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(feature = "sync")]
Guard(RwLockWriteGuard<'d, Dynamic>),
}
@@ -229,7 +229,7 @@ impl<'d, T: Variant + Clone> Deref for DynamicWriteLock<'d, T> {
match &self.0 {
DynamicWriteLockInner::Reference(reference) => *reference,
// Unwrapping is safe because all checking is already done in its constructor
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
DynamicWriteLockInner::Guard(guard) => guard.downcast_ref().unwrap(),
}
}
@@ -241,7 +241,7 @@ impl<'d, T: Variant + Clone> DerefMut for DynamicWriteLock<'d, T> {
match &mut self.0 {
DynamicWriteLockInner::Reference(reference) => *reference,
// Unwrapping is safe because all checking is already done in its constructor
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
DynamicWriteLockInner::Guard(guard) => guard.downcast_mut().unwrap(),
}
}
@@ -261,7 +261,7 @@ impl Dynamic {
/// instead of one of the supported system primitive types?
pub fn is_shared(&self) -> bool {
match self.0 {
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
Union::Shared(_) => true,
_ => false,
}
@@ -302,10 +302,10 @@ impl Dynamic {
Union::Map(_) => TypeId::of::<Map>(),
Union::FnPtr(_) => TypeId::of::<FnPtr>(),
Union::Variant(value) => (***value).type_id(),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "sync"))]
Union::Shared(cell) => (*cell.borrow()).type_id(),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(feature = "sync")]
Union::Shared(cell) => (*cell.read().unwrap()).type_id(),
}
@@ -335,10 +335,10 @@ impl Dynamic {
#[cfg(not(feature = "no_std"))]
Union::Variant(value) if value.is::<Instant>() => "timestamp",
Union::Variant(value) => (***value).type_name(),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "sync"))]
Union::Shared(cell) => (*cell.borrow()).type_name(),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(feature = "sync")]
Union::Shared(cell) => (*cell.read().unwrap()).type_name(),
}
@@ -396,7 +396,7 @@ impl fmt::Display for Dynamic {
#[cfg(not(feature = "no_std"))]
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
Union::Variant(value) => write!(f, "{}", (*value).type_name()),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
Union::Shared(_) => f.write_str("<shared>"),
}
}
@@ -424,7 +424,7 @@ impl fmt::Debug for Dynamic {
#[cfg(not(feature = "no_std"))]
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
Union::Variant(value) => write!(f, "{}", (*value).type_name()),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
Union::Shared(_) => f.write_str("<shared>"),
}
}
@@ -446,7 +446,7 @@ impl Clone for Dynamic {
Union::Map(ref value) => Self(Union::Map(value.clone())),
Union::FnPtr(ref value) => Self(Union::FnPtr(value.clone())),
Union::Variant(ref value) => (***value).clone_into_dynamic(),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell) => Self(Union::Shared(cell.clone())),
}
}
@@ -571,9 +571,9 @@ impl Dynamic {
///
/// # Panics
///
/// Panics under the `no_shared` feature.
/// Panics under the `no_closure` feature.
pub fn into_shared(self) -> Self {
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
return match self.0 {
Union::Shared(..) => self,
#[cfg(not(feature = "sync"))]
@@ -582,7 +582,7 @@ impl Dynamic {
_ => Self(Union::Shared(Arc::new(RwLock::new(self)))),
};
#[cfg(feature = "no_shared")]
#[cfg(feature = "no_closure")]
unimplemented!()
}
@@ -614,11 +614,11 @@ impl Dynamic {
let type_id = TypeId::of::<T>();
match self.0 {
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "sync"))]
Union::Shared(cell) => return cell.borrow().clone().try_cast(),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(feature = "sync")]
Union::Shared(cell) => return cell.read().unwrap().clone().try_cast(),
_ => (),
@@ -703,7 +703,7 @@ impl Dynamic {
match self.0 {
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
Union::Shared(_) => unreachable!(),
_ => None,
}
@@ -748,17 +748,17 @@ impl Dynamic {
///
/// Returns `None` if the cast fails.
#[inline(always)]
pub fn clone_inner_data<T: Variant + Clone>(&self) -> Option<T> {
pub fn clone_inner_data<T: Variant + Clone>(self) -> Option<T> {
match self.0 {
#[cfg(not(feature = "no_shared"))]
Union::Shared(ref cell) => {
#[cfg(not(feature = "no_closure"))]
Union::Shared(cell) => {
#[cfg(not(feature = "sync"))]
return Some(cell.borrow().downcast_ref::<T>().unwrap().clone());
#[cfg(feature = "sync")]
return Some(cell.read().unwrap().downcast_ref::<T>().unwrap().clone());
}
_ => self.downcast_ref().cloned(),
_ => self.try_cast(),
}
}
@@ -772,7 +772,7 @@ impl Dynamic {
#[inline(always)]
pub fn is_locked(&self) -> bool {
match self.0 {
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref _cell) => {
#[cfg(not(feature = "sync"))]
return _cell.try_borrow().is_err();
@@ -788,22 +788,33 @@ impl Dynamic {
/// Casting to `Dynamic` just returns a reference to it.
///
/// Returns `None` if the cast fails.
///
/// # Panics and Deadlocks When Value is Shared
///
/// Under the `sync` feature, this call may deadlock.
/// 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>> {
match self.0 {
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell) => {
#[cfg(not(feature = "sync"))]
return Some(DynamicReadLock(DynamicReadLockInner::Guard(cell.borrow())));
let data = cell.borrow();
#[cfg(feature = "sync")]
return Some(DynamicReadLock(DynamicReadLockInner::Guard(
cell.read().unwrap(),
)));
let data = cell.read().unwrap();
let type_id = (*data).type_id();
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
None
} else {
Some(DynamicReadLock(DynamicReadLockInner::Guard(data)))
}
}
_ => self
.downcast_ref()
.map(|reference| DynamicReadLock(DynamicReadLockInner::Reference(reference))),
.map(|r| DynamicReadLock(DynamicReadLockInner::Reference(r))),
}
}
@@ -811,24 +822,33 @@ impl Dynamic {
/// Casting to `Dynamic` just returns a mutable reference to it.
///
/// Returns `None` if the cast fails.
///
/// # Panics and Deadlocks When Value is Shared
///
/// Under the `sync` feature, this call may deadlock.
/// 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>> {
match self.0 {
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell) => {
#[cfg(not(feature = "sync"))]
return Some(DynamicWriteLock(DynamicWriteLockInner::Guard(
cell.borrow_mut(),
)));
let data = cell.borrow_mut();
#[cfg(feature = "sync")]
return Some(DynamicWriteLock(DynamicWriteLockInner::Guard(
cell.write().unwrap(),
)));
let data = cell.write().unwrap();
let type_id = (*data).type_id();
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
None
} else {
Some(DynamicWriteLock(DynamicWriteLockInner::Guard(data)))
}
}
_ => self
.downcast_mut()
.map(|reference| DynamicWriteLock(DynamicWriteLockInner::Reference(reference))),
.map(|r| DynamicWriteLock(DynamicWriteLockInner::Reference(r))),
}
}
@@ -836,6 +856,10 @@ impl Dynamic {
/// Casting to `Dynamic` just returns a reference to it.
///
/// Returns `None` if the cast fails.
///
/// # Panics
///
/// Panics if the value is shared.
#[inline(always)]
fn downcast_ref<T: Variant + Clone>(&self) -> Option<&T> {
let type_id = TypeId::of::<T>();
@@ -909,7 +933,7 @@ impl Dynamic {
match &self.0 {
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
Union::Shared(_) => unreachable!(),
_ => None,
}
@@ -919,6 +943,10 @@ impl Dynamic {
/// Casting to `Dynamic` just returns a mutable reference to it.
///
/// Returns `None` if the cast fails.
///
/// # Panics
///
/// Panics if the value is shared.
#[inline(always)]
fn downcast_mut<T: Variant + Clone>(&mut self) -> Option<&mut T> {
let type_id = TypeId::of::<T>();
@@ -986,7 +1014,7 @@ impl Dynamic {
match &mut self.0 {
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
Union::Shared(_) => unreachable!(),
_ => None,
}
@@ -997,10 +1025,8 @@ impl Dynamic {
pub fn as_int(&self) -> Result<INT, &'static str> {
match self.0 {
Union::Int(n) => Ok(n),
#[cfg(not(feature = "no_shared"))]
Union::Shared(_) => self
.clone_inner_data::<INT>()
.ok_or_else(|| self.type_name()),
#[cfg(not(feature = "no_closure"))]
Union::Shared(_) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
_ => Err(self.type_name()),
}
}
@@ -1011,10 +1037,8 @@ impl Dynamic {
pub fn as_float(&self) -> Result<FLOAT, &'static str> {
match self.0 {
Union::Float(n) => Ok(n),
#[cfg(not(feature = "no_shared"))]
Union::Shared(_) => self
.clone_inner_data::<FLOAT>()
.ok_or_else(|| self.type_name()),
#[cfg(not(feature = "no_closure"))]
Union::Shared(_) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
_ => Err(self.type_name()),
}
}
@@ -1024,10 +1048,8 @@ impl Dynamic {
pub fn as_bool(&self) -> Result<bool, &'static str> {
match self.0 {
Union::Bool(b) => Ok(b),
#[cfg(not(feature = "no_shared"))]
Union::Shared(_) => self
.clone_inner_data::<bool>()
.ok_or_else(|| self.type_name()),
#[cfg(not(feature = "no_closure"))]
Union::Shared(_) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
_ => Err(self.type_name()),
}
}
@@ -1037,10 +1059,8 @@ impl Dynamic {
pub fn as_char(&self) -> Result<char, &'static str> {
match self.0 {
Union::Char(n) => Ok(n),
#[cfg(not(feature = "no_shared"))]
Union::Shared(_) => self
.clone_inner_data::<char>()
.ok_or_else(|| self.type_name()),
#[cfg(not(feature = "no_closure"))]
Union::Shared(_) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
_ => Err(self.type_name()),
}
}
@@ -1070,7 +1090,7 @@ impl Dynamic {
match self.0 {
Union::Str(s) => Ok(s),
Union::FnPtr(f) => Ok(f.take_data().0),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
Union::Shared(cell) => {
#[cfg(not(feature = "sync"))]
{

View File

@@ -1284,7 +1284,7 @@ impl Engine {
let args = args.as_mut();
// Check for data race.
if cfg!(not(feature = "no_shared")) {
if cfg!(not(feature = "no_closure")) {
ensure_no_data_race(name, args, false)?;
}

View File

@@ -31,7 +31,7 @@ use crate::module::resolvers;
#[cfg(any(not(feature = "no_object"), not(feature = "no_module")))]
use crate::utils::ImmutableString;
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_object"))]
use crate::any::DynamicWriteLock;
@@ -49,6 +49,9 @@ use crate::stdlib::{
#[cfg(not(feature = "no_index"))]
use crate::stdlib::any::TypeId;
#[cfg(not(feature = "no_closure"))]
use crate::stdlib::mem;
/// Variable-sized array of `Dynamic` values.
///
/// Not available under the `no_index` feature.
@@ -96,8 +99,6 @@ pub const KEYWORD_EVAL: &str = "eval";
pub const KEYWORD_FN_PTR: &str = "Fn";
pub const KEYWORD_FN_PTR_CALL: &str = "call";
pub const KEYWORD_FN_PTR_CURRY: &str = "curry";
pub const KEYWORD_SHARED: &str = "shared";
pub const KEYWORD_TAKE: &str = "take";
pub const KEYWORD_IS_SHARED: &str = "is_shared";
pub const KEYWORD_THIS: &str = "this";
pub const FN_TO_STRING: &str = "to_string";
@@ -132,7 +133,7 @@ pub enum Target<'a> {
Ref(&'a mut Dynamic),
/// The target is a mutable reference to a Shared `Dynamic` value.
/// It holds both the access guard and the original shared value.
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_object"))]
LockGuard((DynamicWriteLock<'a, Dynamic>, Dynamic)),
/// The target is a temporary `Dynamic` value (i.e. the mutation can cause no side effects).
@@ -149,7 +150,7 @@ impl Target<'_> {
pub fn is_ref(&self) -> bool {
match self {
Self::Ref(_) => true,
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_object"))]
Self::LockGuard(_) => true,
Self::Value(_) => false,
@@ -161,7 +162,7 @@ impl Target<'_> {
pub fn is_value(&self) -> bool {
match self {
Self::Ref(_) => false,
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_object"))]
Self::LockGuard(_) => false,
Self::Value(_) => true,
@@ -173,7 +174,7 @@ impl Target<'_> {
pub fn is_shared(&self) -> bool {
match self {
Self::Ref(r) => r.is_shared(),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_object"))]
Self::LockGuard(_) => true,
Self::Value(r) => r.is_shared(),
@@ -186,7 +187,7 @@ impl Target<'_> {
pub fn is<T: Variant + Clone>(&self) -> bool {
match self {
Target::Ref(r) => r.is::<T>(),
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_object"))]
Target::LockGuard((r, _)) => r.is::<T>(),
Target::Value(r) => r.is::<T>(),
@@ -198,7 +199,7 @@ impl Target<'_> {
pub fn clone_into_dynamic(self) -> Dynamic {
match self {
Self::Ref(r) => r.clone(), // Referenced value is cloned
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_object"))]
Self::LockGuard((_, orig)) => orig, // Original value is simply taken
Self::Value(v) => v, // Owned value is simply taken
@@ -210,7 +211,7 @@ impl Target<'_> {
pub fn as_mut(&mut self) -> &mut Dynamic {
match self {
Self::Ref(r) => *r,
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_object"))]
Self::LockGuard((r, _)) => r.deref_mut(),
Self::Value(ref mut r) => r,
@@ -223,7 +224,7 @@ impl Target<'_> {
pub fn set_value(&mut self, new_val: Dynamic) -> Result<(), Box<EvalAltResult>> {
match self {
Self::Ref(r) => **r = new_val,
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_object"))]
Self::LockGuard((r, _)) => **r = new_val,
Self::Value(_) => {
@@ -260,7 +261,7 @@ impl Target<'_> {
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
impl<'a> From<&'a mut Dynamic> for Target<'a> {
fn from(value: &'a mut Dynamic) -> Self {
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_object"))]
if value.is_shared() {
// Cloning is cheap for a shared value
@@ -632,7 +633,7 @@ pub fn search_scope_only<'s, 'a>(
// Check for data race - probably not necessary because the only place it should conflict is in a method call
// when the object variable is also used as a parameter.
// if cfg!(not(feature = "no_shared")) && val.is_locked() {
// if cfg!(not(feature = "no_closure")) && val.is_locked() {
// return Err(Box::new(EvalAltResult::ErrorDataRace(name.into(), *pos)));
// }
@@ -1351,12 +1352,9 @@ impl Engine {
)),
// Normal assignment
ScopeEntryType::Normal if op.is_empty() => {
if cfg!(not(feature = "no_shared")) && lhs_ptr.is_shared() {
*lhs_ptr.write_lock::<Dynamic>().unwrap() = if rhs_val.is_shared() {
rhs_val.clone_inner_data().unwrap()
} else {
rhs_val
};
let rhs_val = rhs_val.clone_inner_data().unwrap();
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
*lhs_ptr.write_lock::<Dynamic>().unwrap() = rhs_val;
} else {
*lhs_ptr = rhs_val;
}
@@ -1377,7 +1375,7 @@ impl Engine {
.get_fn(hash_fn, false)
.or_else(|| self.packages.get_fn(hash_fn, false))
{
if cfg!(not(feature = "no_shared")) && lhs_ptr.is_shared() {
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
let mut lock_guard = lhs_ptr.write_lock::<Dynamic>().unwrap();
let lhs_ptr_inner = lock_guard.deref_mut();
@@ -1401,12 +1399,9 @@ impl Engine {
)
.map_err(|err| err.new_position(*op_pos))?;
if cfg!(not(feature = "no_shared")) && lhs_ptr.is_shared() {
*lhs_ptr.write_lock::<Dynamic>().unwrap() = if value.is_shared() {
value.clone_inner_data().unwrap()
} else {
value
};
let value = value.clone_inner_data().unwrap();
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
*lhs_ptr.write_lock::<Dynamic>().unwrap() = value;
} else {
*lhs_ptr = value;
}
@@ -1845,6 +1840,25 @@ impl Engine {
}
Ok(Default::default())
}
// Share statement
#[cfg(not(feature = "no_closure"))]
Stmt::Share(x) => {
let (var_name, _) = x.as_ref();
match scope.get_index(var_name) {
Some((index, ScopeEntryType::Normal)) => {
let (val, _) = scope.get_mut(index);
if !val.is_shared() {
// Replace the variable with a shared value.
*val = mem::take(val).into_shared();
}
}
_ => (),
}
Ok(Default::default())
}
};
self.check_data_size(result)

View File

@@ -93,7 +93,7 @@ pub enum ParseErrorType {
MalformedInExpr(String),
/// A capturing has syntax error. Wrapped value is the error description (if any).
///
/// Never appears under the `no_capture` feature.
/// Never appears under the `no_closure` feature.
MalformedCapture(String),
/// A map definition has duplicated property names. Wrapped value is the property name.
///

View File

@@ -5,7 +5,7 @@ use crate::calc_fn_hash;
use crate::engine::{
search_imports, search_namespace, search_scope_only, Engine, Imports, State, KEYWORD_DEBUG,
KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_SHARED,
KEYWORD_PRINT, KEYWORD_SHARED, KEYWORD_TAKE, KEYWORD_TYPE_OF,
KEYWORD_PRINT, KEYWORD_TYPE_OF,
};
use crate::error::ParseErrorType;
use crate::fn_native::{FnCallArgs, FnPtr};
@@ -33,7 +33,7 @@ use crate::engine::{FN_IDX_GET, FN_IDX_SET};
#[cfg(not(feature = "no_object"))]
use crate::engine::{Map, Target, FN_GET, FN_SET};
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
use crate::scope::Entry as ScopeEntry;
use crate::stdlib::{
@@ -47,7 +47,7 @@ use crate::stdlib::{
vec::Vec,
};
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
use crate::stdlib::{collections::HashSet, string::String};
/// Extract the property name from a getter function name.
@@ -139,7 +139,7 @@ impl Drop for ArgBackup<'_> {
}
// Add captured variables into scope
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
fn add_captured_variables_into_scope<'s>(
externals: &HashSet<String>,
captured: Scope<'s>,
@@ -166,7 +166,7 @@ pub fn ensure_no_data_race(
args: &FnCallArgs,
is_ref: bool,
) -> Result<(), Box<EvalAltResult>> {
if cfg!(not(feature = "no_shared")) {
if cfg!(not(feature = "no_closure")) {
let skip = if is_ref { 1 } else { 0 };
if let Some((n, _)) = args
@@ -456,7 +456,7 @@ impl Engine {
level: usize,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
// Check for data race.
if cfg!(not(feature = "no_shared")) {
if cfg!(not(feature = "no_closure")) {
ensure_no_data_race(fn_name, args, is_ref)?;
}
@@ -505,7 +505,7 @@ impl Engine {
let mods = &mut Imports::new();
// Add captured variables into scope
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
if let Some(captured) = _capture {
add_captured_variables_into_scope(&func.externals, captured, scope);
}
@@ -690,18 +690,12 @@ impl Engine {
.into(),
false,
))
} else if cfg!(not(feature = "no_shared"))
} else if cfg!(not(feature = "no_closure"))
&& _fn_name == KEYWORD_IS_SHARED
&& idx.is_empty()
{
// take call
Ok((target.is_shared().into(), false))
} else if cfg!(not(feature = "no_shared")) && _fn_name == KEYWORD_SHARED && idx.is_empty() {
// take call
Ok((obj.clone().into_shared(), false))
} else if cfg!(not(feature = "no_shared")) && _fn_name == KEYWORD_TAKE && idx.is_empty() {
// take call
Ok((obj.clone_inner_data::<Dynamic>().unwrap(), false))
} else {
#[cfg(not(feature = "no_object"))]
let redirected;
@@ -814,29 +808,13 @@ impl Engine {
}
// Handle is_shared()
if cfg!(not(feature = "no_shared")) && name == KEYWORD_IS_SHARED && args_expr.len() == 1 {
if cfg!(not(feature = "no_closure")) && name == KEYWORD_IS_SHARED && args_expr.len() == 1 {
let expr = args_expr.get(0).unwrap();
let value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
return Ok(value.is_shared().into());
}
// Handle shared()
if cfg!(not(feature = "no_shared")) && name == KEYWORD_SHARED && args_expr.len() == 1 {
let expr = args_expr.get(0).unwrap();
let value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
return Ok(value.into_shared());
}
// Handle take()
if cfg!(not(feature = "no_shared")) && name == KEYWORD_TAKE && args_expr.len() == 1 {
let expr = args_expr.get(0).unwrap();
let value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
return Ok(value.clone_inner_data::<Dynamic>().unwrap());
}
// Handle call() - Redirect function call
let redirected;
let mut args_expr = args_expr.as_ref();
@@ -896,7 +874,7 @@ impl Engine {
let mut arg_values: StaticVec<_>;
let mut args: StaticVec<_>;
let mut is_ref = false;
let capture = if cfg!(not(feature = "no_capture")) && capture && !scope.is_empty() {
let capture = if cfg!(not(feature = "no_closure")) && capture && !scope.is_empty() {
Some(scope.flatten_clone())
} else {
None
@@ -924,7 +902,7 @@ impl Engine {
// Turn it into a method call only if the object is not shared
args = if target.is_shared() {
arg_values.insert(0, target.clone_inner_data().unwrap());
arg_values.insert(0, target.clone().clone_inner_data().unwrap());
arg_values.iter_mut().collect()
} else {
is_ref = true;
@@ -1041,7 +1019,7 @@ impl Engine {
let mods = &mut Imports::new();
// Add captured variables into scope
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
if _capture && !scope.is_empty() {
add_captured_variables_into_scope(
&func.externals,

View File

@@ -22,10 +22,10 @@ use crate::stdlib::rc::Rc;
#[cfg(feature = "sync")]
use crate::stdlib::sync::Arc;
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "sync"))]
use crate::stdlib::cell::RefCell;
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(feature = "sync")]
use crate::stdlib::sync::RwLock;
@@ -51,11 +51,11 @@ pub type Shared<T> = Rc<T>;
pub type Shared<T> = Arc<T>;
/// Mutable reference-counted container (read-write lock)
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "sync"))]
pub type SharedMut<T> = Shared<RefCell<T>>;
/// Mutable reference-counted container (read-write lock)
#[cfg(not(feature = "no_shared"))]
#[cfg(not(feature = "no_closure"))]
#[cfg(feature = "sync")]
pub type SharedMut<T> = Shared<RwLock<T>>;

View File

@@ -765,7 +765,7 @@ pub fn optimize_into_ast(
access: fn_def.access,
body: Default::default(),
params: fn_def.params.clone(),
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
externals: fn_def.externals.clone(),
pos: fn_def.pos,
}

View File

@@ -39,7 +39,7 @@ use crate::stdlib::{
#[cfg(not(feature = "no_function"))]
use crate::stdlib::collections::hash_map::DefaultHasher;
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
use crate::stdlib::collections::HashSet;
#[cfg(feature = "no_std")]
@@ -366,7 +366,7 @@ pub struct ScriptFnDef {
/// Names of function parameters.
pub params: StaticVec<String>,
/// Access to external variables.
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
pub externals: HashSet<String>,
/// Function body.
pub body: Stmt,
@@ -414,13 +414,13 @@ struct ParseState<'e> {
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
stack: Vec<(String, ScopeEntryType)>,
/// Tracks a list of external variables (variables that are not explicitly declared in the scope).
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
externals: HashMap<String, Position>,
/// An indicator that prevents variables capturing into externals one time.
/// If set to true the next call of `access_var` will not capture the variable.
/// An indicator that disables variable capturing into externals one single time.
/// If set to false the next call to `access_var` will not capture the variable.
/// All consequent calls to `access_var` will not be affected
#[cfg(not(feature = "no_capture"))]
capture: bool,
#[cfg(not(feature = "no_closure"))]
allow_capture: bool,
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
modules: Vec<String>,
/// Maximum levels of expression nesting.
@@ -444,10 +444,10 @@ impl<'e> ParseState<'e> {
max_expr_depth,
#[cfg(not(feature = "unchecked"))]
max_function_expr_depth,
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
externals: Default::default(),
#[cfg(not(feature = "no_capture"))]
capture: true,
#[cfg(not(feature = "no_closure"))]
allow_capture: true,
stack: Default::default(),
modules: Default::default(),
}
@@ -469,13 +469,13 @@ impl<'e> ParseState<'e> {
.find(|(_, (n, _))| *n == name)
.and_then(|(i, _)| NonZeroUsize::new(i + 1));
#[cfg(not(feature = "no_capture"))]
if self.capture {
#[cfg(not(feature = "no_closure"))]
if self.allow_capture {
if index.is_none() && !self.externals.contains_key(name) {
self.externals.insert(name.to_string(), _pos);
}
} else {
self.capture = true
self.allow_capture = true
}
index
@@ -579,6 +579,9 @@ pub enum Stmt {
Position,
)>,
),
/// Convert a variable to shared.
#[cfg(not(feature = "no_closure"))]
Share(Box<(String, Position)>),
}
impl Default for Stmt {
@@ -606,6 +609,9 @@ impl Stmt {
Stmt::Import(x) => x.2,
#[cfg(not(feature = "no_module"))]
Stmt::Export(x) => x.1,
#[cfg(not(feature = "no_closure"))]
Stmt::Share(x) => x.1,
}
}
@@ -629,6 +635,9 @@ impl Stmt {
Stmt::Import(x) => x.2 = new_pos,
#[cfg(not(feature = "no_module"))]
Stmt::Export(x) => x.1 = new_pos,
#[cfg(not(feature = "no_closure"))]
Stmt::Share(x) => x.1 = new_pos,
}
self
@@ -655,6 +664,9 @@ impl Stmt {
#[cfg(not(feature = "no_module"))]
Stmt::Import(_) | Stmt::Export(_) => false,
#[cfg(not(feature = "no_closure"))]
Stmt::Share(_) => false,
}
}
@@ -678,6 +690,9 @@ impl Stmt {
Stmt::Import(_) => false,
#[cfg(not(feature = "no_module"))]
Stmt::Export(_) => false,
#[cfg(not(feature = "no_closure"))]
Stmt::Share(_) => false,
}
}
}
@@ -1671,7 +1686,7 @@ fn parse_primary(
root_expr = match (root_expr, token) {
// Function call
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
(Expr::Variable(x), Token::Bang) => {
if !match_token(input, Token::LeftParen)? {
return Err(PERR::MissingToken(
@@ -1855,7 +1870,7 @@ fn parse_unary(
let (expr, func) = parse_anon_fn(input, &mut new_state, lib, settings)?;
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
new_state.externals.iter().for_each(|(closure, pos)| {
state.access_var(closure, *pos);
});
@@ -2216,9 +2231,9 @@ fn parse_binary_op(
if cfg!(not(feature = "no_object")) && op_token == Token::Period {
if let (Token::Identifier(_), _) = input.peek().unwrap() {
// prevents capturing of the object properties as vars: xxx.<var>
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
{
state.capture = false;
state.allow_capture = false;
}
}
}
@@ -3095,7 +3110,7 @@ fn parse_fn(
let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect();
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
let externals = state
.externals
.iter()
@@ -3108,7 +3123,7 @@ fn parse_fn(
name: name.into(),
access,
params,
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
externals,
body,
pos: settings.pos,
@@ -3128,6 +3143,17 @@ fn make_curry_from_externals(
let num_externals = externals.len();
let mut args: StaticVec<_> = Default::default();
#[cfg(not(feature = "no_closure"))]
externals.iter().for_each(|(var_name, pos)| {
args.push(Expr::Variable(Box::new((
(var_name.into(), *pos),
None,
0,
None,
))));
});
#[cfg(feature = "no_closure")]
externals.into_iter().for_each(|(var_name, pos)| {
args.push(Expr::Variable(Box::new(((var_name, pos), None, 0, None))));
});
@@ -3142,7 +3168,23 @@ fn make_curry_from_externals(
None,
)));
Expr::Dot(Box::new((fn_expr, fn_call, pos)))
let expr = Expr::Dot(Box::new((fn_expr, fn_call, pos)));
// If there are captured variables, convert the entire expression into a statement block,
// then insert the relevant `Share` statements.
#[cfg(not(feature = "no_closure"))]
{
// Statement block
let mut statements: StaticVec<_> = Default::default();
// Insert `Share` statements
statements.extend(externals.into_iter().map(|x| Stmt::Share(Box::new(x))));
// Final expression
statements.push(Stmt::Expr(Box::new(expr)));
Expr::Stmt(Box::new((Stmt::Block(Box::new((statements, pos))), pos)))
}
#[cfg(feature = "no_closure")]
return expr;
}
/// Parse an anonymous function definition.
@@ -3215,7 +3257,7 @@ fn parse_anon_fn(
// External variables may need to be processed in a consistent order,
// so extract them into a list.
let externals: StaticVec<_> = {
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
{
state
.externals
@@ -3223,11 +3265,11 @@ fn parse_anon_fn(
.map(|(k, &v)| (k.clone(), v))
.collect()
}
#[cfg(feature = "no_capture")]
#[cfg(feature = "no_closure")]
Default::default()
};
let params: StaticVec<_> = if cfg!(not(feature = "no_capture")) {
let params: StaticVec<_> = if cfg!(not(feature = "no_closure")) {
externals
.iter()
.map(|(k, _)| k)
@@ -3257,7 +3299,7 @@ fn parse_anon_fn(
name: fn_name.clone(),
access: FnAccess::Public,
params,
#[cfg(not(feature = "no_capture"))]
#[cfg(not(feature = "no_closure"))]
externals: Default::default(),
body,
pos: settings.pos,
@@ -3265,7 +3307,7 @@ fn parse_anon_fn(
let expr = Expr::FnPointer(Box::new((fn_name, settings.pos)));
let expr = if cfg!(not(feature = "no_capture")) {
let expr = if cfg!(not(feature = "no_closure")) {
make_curry_from_externals(expr, externals, settings.pos)
} else {
expr

View File

@@ -158,32 +158,6 @@ impl<'a> Scope<'a> {
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value), false)
}
/// Add (push) a new shared entry to the Scope.
///
/// # Examples
///
/// ```
/// use rhai::Scope;
///
/// let mut my_scope = Scope::new();
///
/// my_scope.push_shared("x", 42_i64);
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
/// ```
#[cfg(not(feature = "no_shared"))]
pub fn push_shared<K: Into<Cow<'a, str>>, T: Variant + Clone>(
&mut self,
name: K,
value: T,
) -> &mut Self {
self.push_dynamic_value(
name,
EntryType::Normal,
Dynamic::from(value).into_shared(),
false,
)
}
/// Add (push) a new `Dynamic` entry to the Scope.
///
/// # Examples
@@ -226,34 +200,6 @@ impl<'a> Scope<'a> {
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value), true)
}
/// Add (push) a new shared constant to the Scope.
///
/// Shared constants are immutable and cannot be assigned to, but their shared values can change.
///
/// # Examples
///
/// ```
/// use rhai::Scope;
///
/// let mut my_scope = Scope::new();
///
/// my_scope.push_constant_shared("x", 42_i64);
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
/// ```
#[cfg(not(feature = "no_shared"))]
pub fn push_constant_shared<K: Into<Cow<'a, str>>, T: Variant + Clone>(
&mut self,
name: K,
value: T,
) -> &mut Self {
self.push_dynamic_value(
name,
EntryType::Constant,
Dynamic::from(value).into_shared(),
true,
)
}
/// Add (push) a new constant with a `Dynamic` value to the Scope.
///
/// Constants are immutable and cannot be assigned to. Their values never change.
@@ -394,7 +340,7 @@ impl<'a> Scope<'a> {
/// ```
pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
self.get_entry(name)
.and_then(|Entry { value, .. }| value.clone_inner_data::<T>())
.and_then(|Entry { value, .. }| value.clone().clone_inner_data::<T>())
}
/// Update the value of the named entry.
@@ -485,13 +431,20 @@ impl<'a> Scope<'a> {
///
/// let (name, value) = iter.next().unwrap();
/// assert_eq!(name, "x");
/// assert_eq!(value.clone().cast::<i64>(), 42);
/// assert_eq!(value.cast::<i64>(), 42);
///
/// let (name, value) = iter.next().unwrap();
/// assert_eq!(name, "foo");
/// assert_eq!(value.clone().cast::<String>(), "hello");
/// assert_eq!(value.cast::<String>(), "hello");
/// ```
pub fn iter(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
pub fn iter(&self) -> impl Iterator<Item = (&str, Dynamic)> {
self.iter_raw()
.map(|(name, value)| (name, value.clone().clone_inner_data().unwrap()))
}
/// Get an iterator to entries in the Scope.
/// Shared values are not expanded.
pub fn iter_raw(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
self.0
.iter()
.map(|Entry { name, value, .. }| (name.as_ref(), value))

View File

@@ -2,7 +2,7 @@
use crate::engine::{
Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY,
KEYWORD_IS_SHARED, KEYWORD_PRINT, KEYWORD_SHARED, KEYWORD_TAKE, KEYWORD_THIS, KEYWORD_TYPE_OF,
KEYWORD_IS_SHARED, KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF,
};
use crate::error::LexError;
@@ -501,14 +501,15 @@ impl Token {
"import" | "export" | "as" => Reserved(syntax.into()),
"===" | "!==" | "->" | "<-" | "=>" | ":=" | "::<" | "(*" | "*)" | "#" | "public"
| "new" | "use" | "module" | "package" | "var" | "static" | "with" | "do" | "each"
| "then" | "goto" | "exit" | "switch" | "match" | "case" | "try" | "catch"
| "default" | "void" | "null" | "nil" | "spawn" | "go" | "sync" | "async" | "await"
| "yield" => Reserved(syntax.into()),
| "new" | "use" | "module" | "package" | "var" | "static" | "shared" | "with"
| "do" | "each" | "then" | "goto" | "exit" | "switch" | "match" | "case" | "try"
| "catch" | "default" | "void" | "null" | "nil" | "spawn" | "go" | "sync" | "async"
| "await" | "yield" => Reserved(syntax.into()),
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_SHARED | KEYWORD_SHARED
| KEYWORD_TAKE | KEYWORD_THIS => Reserved(syntax.into()),
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_SHARED | KEYWORD_THIS => {
Reserved(syntax.into())
}
_ => return None,
})
@@ -1432,8 +1433,8 @@ fn get_identifier(
#[inline(always)]
pub fn is_keyword_function(name: &str) -> bool {
match name {
#[cfg(not(feature = "no_shared"))]
KEYWORD_SHARED | KEYWORD_TAKE | KEYWORD_IS_SHARED => true,
#[cfg(not(feature = "no_closure"))]
KEYWORD_IS_SHARED => true,
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY => true,
_ => false,