diff --git a/Cargo.toml b/Cargo.toml index ec00a10c..85b57974 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ no_index = [] # no arrays and indexing no_object = [] # no custom objects no_function = [] # no script-defined functions no_capture = [] # no automatic read/write binding of anonymous function's local variables to it's external context +no_shared = [] # no explicit shared variables in the script code no_module = [] # no modules internals = [] # expose internal data structures unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers. diff --git a/src/any.rs b/src/any.rs index 9d28c768..a7983030 100644 --- a/src/any.rs +++ b/src/any.rs @@ -1,6 +1,6 @@ //! Helper module which defines the `Any` trait to to allow dynamic value handling. -use crate::fn_native::{FnPtr, SendSync}; +use crate::fn_native::{FnPtr, SendSync, SharedMut}; use crate::parser::{ImmutableString, INT}; use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast}; @@ -18,8 +18,15 @@ use crate::stdlib::{ boxed::Box, fmt, string::String, + ops::{Deref, DerefMut} }; +#[cfg(not(feature = "sync"))] +use crate::stdlib::{rc::Rc, cell::{RefCell, Ref, RefMut}}; + +#[cfg(feature = "sync")] +use crate::stdlib::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; + #[cfg(not(feature = "no_object"))] use crate::stdlib::collections::HashMap; @@ -144,6 +151,88 @@ pub enum Union { Map(Box), FnPtr(Box), Variant(Box>), + Shared(Box), +} + +/// Internal Shared Dynamic representation. +/// +/// Created with `Dynamic::into_shared()`. +#[derive(Clone)] +pub struct SharedCell { + value_type_id: TypeId, + value_type_name: &'static str, + container: SharedMut +} + +/// Dynamic's underlying `Variant` read guard that supports `Deref` for Variant's +/// reference reading. +/// +/// This data structure provides transparent interoperability between normal +/// `Dynamic` types and Shared Dynamic references. +#[derive(Debug)] +pub struct DynamicReadLock<'d, T: Variant + Clone>(DynamicReadLockInner<'d, T>); + +#[derive(Debug)] +enum DynamicReadLockInner<'d, T: Variant + Clone> { + Reference(&'d T), + #[cfg(not(feature = "sync"))] + Guard(Ref<'d, Dynamic>), + #[cfg(feature = "sync")] + Guard(RwLockReadGuard<'d, Dynamic>), +} + +impl<'d, T: Variant + Clone> Deref for DynamicReadLock<'d, T> { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + match &self.0 { + DynamicReadLockInner::Reference(reference) => reference.deref(), + // unwrapping is safe because all checks already done in it's constructor + DynamicReadLockInner::Guard(guard) => guard.downcast_ref().unwrap(), + } + } +} + +/// Dynamic's underlying `Variant` write guard that supports `Deref` and `DerefMut` +/// for Variant's reference reading/writing. +/// +/// This data structure provides transparent interoperability between normal +/// `Dynamic` types and Shared Dynamic references. +#[derive(Debug)] +pub struct DynamicWriteLock<'d, T: Variant + Clone>(DynamicWriteLockInner<'d, T>); + +#[derive(Debug)] +enum DynamicWriteLockInner<'d, T: Variant + Clone> { + Reference(&'d mut T), + #[cfg(not(feature = "sync"))] + Guard(RefMut<'d, Dynamic>), + #[cfg(feature = "sync")] + Guard(RwLockWriteGuard<'d, Dynamic>), +} + +impl<'d, T: Variant + Clone> Deref for DynamicWriteLock<'d, T> { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + match &self.0 { + DynamicWriteLockInner::Reference(reference) => reference.deref(), + // unwrapping is safe because all checks already done in it's constructor + DynamicWriteLockInner::Guard(guard) => guard.downcast_ref().unwrap(), + } + } +} + +impl<'d, T: Variant + Clone> DerefMut for DynamicWriteLock<'d, T> { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + match &mut self.0 { + DynamicWriteLockInner::Reference(reference) => reference.deref_mut(), + // unwrapping is safe because all checks already done in it's constructor + DynamicWriteLockInner::Guard(guard) => guard.downcast_mut().unwrap(), + } + } } impl Dynamic { @@ -156,7 +245,19 @@ impl Dynamic { } } + /// Does this `Dynamic` hold a shared data type + /// instead of one of the support system primitive types? + pub fn is_shared(&self) -> bool { + match self.0 { + Union::Shared(_) => true, + _ => false, + } + } + /// Is the value held by this `Dynamic` a particular type? + /// + /// If the `Dynamic` is a Shared variant checking is performed on + /// top of it's internal value. pub fn is(&self) -> bool { self.type_id() == TypeId::of::() || match self.0 { @@ -181,6 +282,7 @@ impl Dynamic { Union::Map(_) => TypeId::of::(), Union::FnPtr(_) => TypeId::of::(), Union::Variant(value) => (***value).type_id(), + Union::Shared(cell) => (**cell).value_type_id, } } @@ -203,6 +305,7 @@ impl Dynamic { #[cfg(not(feature = "no_std"))] Union::Variant(value) if value.is::() => "timestamp", Union::Variant(value) => (***value).type_name(), + Union::Shared(cell) => (**cell).value_type_name, } } } @@ -258,6 +361,7 @@ impl fmt::Display for Dynamic { #[cfg(not(feature = "no_std"))] Union::Variant(value) if value.is::() => write!(f, ""), Union::Variant(value) => write!(f, "{}", (*value).type_name()), + Union::Shared(cell) => write!(f, "{}", (**cell).value_type_name), } } } @@ -284,6 +388,7 @@ impl fmt::Debug for Dynamic { #[cfg(not(feature = "no_std"))] Union::Variant(value) if value.is::() => write!(f, ""), Union::Variant(value) => write!(f, "{}", (*value).type_name()), + Union::Shared(cell) => write!(f, "{}", (**cell).value_type_name), } } } @@ -304,6 +409,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(), + Union::Shared(ref cell) => Self(Union::Shared(Box::new((**cell).clone()))) } } } @@ -415,10 +521,53 @@ impl Dynamic { Self(Union::Variant(Box::new(boxed))) } + /// Turns Dynamic into Shared Dynamic variant backed by runtime + /// mutable reference-counter container(`Arc>` or + /// `Rc` depending on `sync` feature). + /// + /// Instances of Shared Dynamic are relatively cheap to clone. All clones of + /// Shared Dynamic references the same chunk of memory. + /// + /// The Dynamic is capable to work transparently with the it's inner + /// data, seamlessly casting between ordinary instances of Dynamic and + /// Shared Dynamic instances. + /// + /// If original value already a Shared variant returns identity. + pub fn into_shared(self) -> Self { + match self.0 { + Union::Shared(..) => self, + _ => { + let cell = SharedCell { + value_type_id: self.type_id(), + value_type_name: self.type_name(), + #[cfg(not(feature = "sync"))] + container: Rc::new(RefCell::new(self)), + #[cfg(feature = "sync")] + container: Arc::new(RwLock::new(self)), + }; + + Self(Union::Shared(Box::new(cell))) + }, + } + } + /// Get a copy of the `Dynamic` value as a specific type. /// Casting to a `Dynamic` just returns as is. /// - /// Returns an error with the name of the value's actual type when the cast fails. + /// Returns None if types mismatched. + /// + /// # Shared Dynamic + /// + /// When accessing Shared Dynamic in sync mode(`sync` feature enabled) + /// can block current thread while the underlined data is being written. + /// + /// When accessing Shared Dynamic in NON-sync mode can **panic** if the data + /// is currently borrowed for write. + /// + /// ## Safety + /// + /// Both situations normally shouldn't happen since all operations in Rhai + /// use pass-by-value data and the script executed in a single thread. /// /// # Example /// @@ -433,6 +582,24 @@ impl Dynamic { pub fn try_cast(self) -> Option { let type_id = TypeId::of::(); + if type_id == TypeId::of::() { + return unsafe_cast_box::<_, T>(Box::new(self)).ok().map(|v| *v); + } + + #[cfg(not(feature = "sync"))] + if let Union::Shared(cell) = self.0 { + let reference = cell.container.borrow(); + + return (*reference).clone().try_cast() + } + + #[cfg(feature = "sync")] + if let Union::Shared(cell) = self.0 { + let read_lock = cell.container.read().unwrap(); + + return (*read_lock).clone().try_cast() + } + if type_id == TypeId::of::() { return match self.0 { Union::Int(value) => unsafe_try_cast(value), @@ -496,9 +663,6 @@ impl Dynamic { _ => None, }; } - if type_id == TypeId::of::() { - return unsafe_cast_box::<_, T>(Box::new(self)).ok().map(|v| *v); - } match self.0 { Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(), @@ -511,7 +675,14 @@ impl Dynamic { /// /// # Panics /// - /// Panics if the cast fails (e.g. the type of the actual value is not the same as the specified type). + /// Panics if the cast fails (e.g. the type of the actual value is not the + /// same as the specified type), or if the data held by Shared Dynamic is + /// currently borrowed(when the `sync` feature disabled). + /// + /// # Notes + /// + /// If the `sync` feature enabled Shared Dynamic can block current thread + /// while the data being written. /// /// # Example /// @@ -531,7 +702,104 @@ impl Dynamic { /// Casting to `Dynamic` just returns a reference to it. /// Returns `None` if the cast fails. #[inline(always)] - pub fn downcast_ref(&self) -> Option<&T> { + pub fn read_lock(&self) -> Option> { + match self.0 { + Union::Shared(ref cell) => { + let type_id = TypeId::of::(); + + if type_id != TypeId::of::() && cell.value_type_id != type_id { + return None + } + + #[cfg(not(feature = "sync"))] + return Some(DynamicReadLock(DynamicReadLockInner::Guard( + cell.container.borrow() + ))); + + #[cfg(feature = "sync")] + return Some(DynamicReadLock(DynamicReadLockInner::Guard( + cell.container.read().unwrap() + ))); + }, + _ => { + self.downcast_ref().map(|reference| { + DynamicReadLock(DynamicReadLockInner::Reference(reference)) + }) + } + } + } + + /// Get a copy of a specific type to the `Dynamic`. + /// Casting to `Dynamic` just returns a reference to it. + /// Returns `None` if the cast fails. + #[inline(always)] + pub fn read(&self) -> Option { + match self.0 { + Union::Shared(ref cell) => { + let type_id = TypeId::of::(); + + if type_id != TypeId::of::() && cell.value_type_id != type_id { + return None + } + + #[cfg(not(feature = "sync"))] + return Some(cell + .container + .borrow() + .deref() + .downcast_ref::() + .unwrap() + .clone()); + + #[cfg(feature = "sync")] + return Some(cell + .container + .read() + .unwrap() + .deref() + .downcast_ref::() + .unwrap() + .clone()); + }, + _ => { + self.downcast_ref().cloned() + } + } + } + + /// Get a mutable reference of a specific type to the `Dynamic`. + /// Casting to `Dynamic` just returns a mutable reference to it. + /// Returns `None` if the cast fails. + #[inline(always)] + pub fn write_lock(&mut self) -> Option> { + match self.0 { + Union::Shared(ref cell) => { + let type_id = TypeId::of::(); + + if type_id != TypeId::of::() && cell.value_type_id != type_id { + return None + } + + #[cfg(not(feature = "sync"))] + return Some(DynamicWriteLock(DynamicWriteLockInner::Guard( + cell.container.borrow_mut() + ))); + + #[cfg(feature = "sync")] + return Some(DynamicWriteLock(DynamicWriteLockInner::Guard( + cell.container.write().unwrap() + ))); + }, + _ => { + self.downcast_mut().map(|reference| { + DynamicWriteLock(DynamicWriteLockInner::Reference(reference)) + }) + } + } + } + + #[inline(always)] + fn downcast_ref(&self) -> Option<&T> { let type_id = TypeId::of::(); if type_id == TypeId::of::() { @@ -607,11 +875,8 @@ impl Dynamic { } } - /// Get a mutable reference of a specific type to the `Dynamic`. - /// Casting to `Dynamic` just returns a mutable reference to it. - /// Returns `None` if the cast fails. #[inline(always)] - pub fn downcast_mut(&mut self) -> Option<&mut T> { + fn downcast_mut(&mut self) -> Option<&mut T> { let type_id = TypeId::of::(); if type_id == TypeId::of::() { diff --git a/src/engine.rs b/src/engine.rs index 7806382f..06ff8d7f 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -91,6 +91,7 @@ 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_THIS: &str = "this"; pub const FN_TO_STRING: &str = "to_string"; #[cfg(not(feature = "no_object"))] @@ -1068,6 +1069,18 @@ impl Engine { let val = target.as_mut(); + // if val.is_shared() { + // return self.get_indexed_mut( + // state, + // lib, + // &mut Target::Value(val.read::().unwrap()), + // idx, + // idx_pos, + // create, + // level, + // ); + // } + match val { #[cfg(not(feature = "no_index"))] Dynamic(Union::Array(arr)) => { @@ -1102,7 +1115,7 @@ impl Engine { map.entry(index).or_insert(Default::default()).into() } else { let index = _idx - .downcast_ref::() + .read_lock::() .ok_or_else(|| EvalAltResult::ErrorStringIndexExpr(idx_pos))?; map.get_mut(index.as_str()) @@ -1280,7 +1293,11 @@ impl Engine { )), // Normal assignment ScopeEntryType::Normal if op.is_empty() => { - *lhs_ptr = rhs_val; + if lhs_ptr.is_shared() { + *lhs_ptr.write_lock::().unwrap() = rhs_val; + } else { + *lhs_ptr = rhs_val; + } Ok(Default::default()) } // Op-assignment - in order of precedence: @@ -1298,8 +1315,13 @@ impl Engine { .get_fn(hash_fn, false) .or_else(|| self.packages.get_fn(hash_fn, false)) { - // Overriding exact implementation - func(self, lib, &mut [lhs_ptr, &mut rhs_val])?; + if lhs_ptr.is_shared() { + // Overriding exact implementation + func(self, lib, &mut [&mut lhs_ptr.write_lock::().unwrap(), &mut rhs_val])?; + } else { + // Overriding exact implementation + func(self, lib, &mut [lhs_ptr, &mut rhs_val])?; + } } else if run_builtin_op_assignment(op, lhs_ptr, &rhs_val)?.is_none() { // Not built in, map to `var = var op rhs` let op = &op[..op.len() - 1]; // extract operator without = @@ -1313,8 +1335,13 @@ impl Engine { level, ) .map_err(|err| err.new_position(*op_pos))?; - // Set value to LHS - *lhs_ptr = value; + if lhs_ptr.is_shared() { + // Set value to LHS + *lhs_ptr.write_lock::().unwrap() = value; + } else { + // Set value to LHS + *lhs_ptr = value; + } } Ok(Default::default()) } diff --git a/src/fn_call.rs b/src/fn_call.rs index 162410f3..6ca46046 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -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_PRINT, - KEYWORD_TYPE_OF, + KEYWORD_TYPE_OF, KEYWORD_SHARED }; use crate::error::ParseErrorType; use crate::fn_native::{FnCallArgs, FnPtr}; @@ -16,6 +16,7 @@ use crate::result::EvalAltResult; use crate::scope::Scope; use crate::token::Position; use crate::utils::StaticVec; +use crate::stdlib::ops::Deref; #[cfg(not(feature = "no_function"))] use crate::{ @@ -540,7 +541,7 @@ impl Engine { let (result, updated) = if _fn_name == KEYWORD_FN_PTR_CALL && obj.is::() { // FnPtr call - let fn_ptr = obj.downcast_ref::().unwrap(); + let fn_ptr = obj.read_lock::().unwrap(); let mut curry = fn_ptr.curry().iter().cloned().collect::>(); // Redirect function name let fn_name = fn_ptr.fn_name(); @@ -578,7 +579,7 @@ impl Engine { ) } else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::() { // Curry call - let fn_ptr = obj.downcast_ref::().unwrap(); + let fn_ptr = obj.read_lock::().unwrap(); Ok(( FnPtr::new_unchecked( fn_ptr.get_fn_name().clone(), @@ -599,9 +600,9 @@ impl Engine { // Check if it is a map method call in OOP style #[cfg(not(feature = "no_object"))] - if let Some(map) = obj.downcast_ref::() { + if let Some(map) = obj.read_lock::() { if let Some(val) = map.get(_fn_name) { - if let Some(f) = val.downcast_ref::() { + if let Some(f) = val.read_lock::() { // Remap the function name redirected = f.get_fn_name().clone(); _fn_name = &redirected; @@ -697,6 +698,15 @@ impl Engine { .into()); } + // Handle shared() + #[cfg(not(feature = "no_shared"))] + if 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 eval() if name == KEYWORD_EVAL && args_expr.len() == 1 { let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::())); @@ -984,8 +994,8 @@ pub fn run_builtin_binary_op( _ => (), } } else if args_type == TypeId::of::() { - let x = x.downcast_ref::().unwrap(); - let y = y.downcast_ref::().unwrap(); + let x = &*x.read_lock::().unwrap(); + let y = &*y.read_lock::().unwrap(); match op { "+" => return Ok(Some((x + y).into())), @@ -1058,7 +1068,7 @@ pub fn run_builtin_op_assignment( } if args_type == TypeId::of::() { - let x = x.downcast_mut::().unwrap(); + let mut x = x.write_lock::().unwrap(); let y = y.clone().cast::(); #[cfg(not(feature = "unchecked"))] @@ -1094,7 +1104,7 @@ pub fn run_builtin_op_assignment( _ => (), } } else if args_type == TypeId::of::() { - let x = x.downcast_mut::().unwrap(); + let mut x = x.write_lock::().unwrap(); let y = y.clone().cast::(); match op { @@ -1103,18 +1113,18 @@ pub fn run_builtin_op_assignment( _ => (), } } else if args_type == TypeId::of::() { - let x = x.downcast_mut::().unwrap(); - let y = y.downcast_ref::().unwrap(); + let mut x = x.write_lock::().unwrap(); + let y = y.read_lock::().unwrap(); match op { - "+=" => return Ok(Some(*x += y)), + "+=" => return Ok(Some(*x += y.deref())), _ => (), } } #[cfg(not(feature = "no_float"))] if args_type == TypeId::of::() { - let x = x.downcast_mut::().unwrap(); + let mut x = x.write_lock::().unwrap(); let y = y.clone().cast::(); match op { diff --git a/src/fn_native.rs b/src/fn_native.rs index 5751351c..e51bf71b 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -1,6 +1,6 @@ //! Module defining interfaces to native-Rust functions. -use crate::any::Dynamic; +use crate::any::{Dynamic, Variant}; use crate::calc_fn_hash; use crate::engine::Engine; use crate::module::Module; @@ -18,9 +18,9 @@ use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, iter::empty, string::Stri use crate::stdlib::mem; #[cfg(not(feature = "sync"))] -use crate::stdlib::rc::Rc; +use crate::stdlib::{rc::Rc, cell::{RefCell, Ref, RefMut}}; #[cfg(feature = "sync")] -use crate::stdlib::sync::Arc; +use crate::stdlib::sync::{Arc, RwLock}; /// Trait that maps to `Send + Sync` only under the `sync` feature. #[cfg(feature = "sync")] @@ -34,11 +34,18 @@ pub trait SendSync {} #[cfg(not(feature = "sync"))] impl SendSync for T {} +/// Immutable reference counting container #[cfg(not(feature = "sync"))] pub type Shared = Rc; #[cfg(feature = "sync")] pub type Shared = Arc; +/// Mutable reference counting container(read-write lock) +#[cfg(not(feature = "sync"))] +pub type SharedMut = Rc>; +#[cfg(feature = "sync")] +pub type SharedMut = Arc>; + /// Consume a `Shared` resource and return a mutable reference to the wrapped value. /// If the resource is shared (i.e. has other outstanding references), a cloned copy is used. pub fn shared_make_mut(value: &mut Shared) -> &mut T { diff --git a/src/fn_register.rs b/src/fn_register.rs index c2eb5534..ddcddff3 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -2,7 +2,7 @@ #![allow(non_snake_case)] -use crate::any::{Dynamic, Variant}; +use crate::any::{Dynamic, Variant, DynamicWriteLock}; use crate::engine::Engine; use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync}; use crate::module::Module; @@ -97,11 +97,11 @@ pub trait RegisterResultFn { pub struct Mut(T); //pub struct Ref(T); -/// Dereference into &mut. +/// Dereference into DynamicWriteLock #[inline(always)] -pub fn by_ref(data: &mut Dynamic) -> &mut T { - // Directly cast the &mut Dynamic into &mut T to access the underlying data. - data.downcast_mut::().unwrap() +pub fn by_ref(data: &mut Dynamic) -> DynamicWriteLock { + // Directly cast the &mut Dynamic into DynamicWriteLock to access the underlying data. + data.write_lock::().unwrap() } /// Dereference into value. @@ -124,24 +124,23 @@ pub fn by_value(data: &mut Dynamic) -> T { /// This macro creates a closure wrapping a registered function. macro_rules! make_func { - ($fn:ident : $map:expr ; $($par:ident => $convert:expr),*) => { + ($fn:ident : $map:expr ; $($par:ident => $let:stmt => $convert:expr => $arg:expr),*) => { // ^ function pointer // ^ result mapping function // ^ function parameter generic type name (A, B, C etc.) -// ^ dereferencing function +// ^ argument let statement(e.g. let mut A ...) +// ^ dereferencing function +// ^ argument reference expression(like A, *B, &mut C etc) Box::new(move |_: &Engine, _: &Module, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! let mut _drain = args.iter_mut(); - $( - // Downcast every element, panic in case of a type mismatch (which shouldn't happen). - // Call the user-supplied function using ($convert) to access it either by value or by reference. - let $par = ($convert)(_drain.next().unwrap()); - )* + $($let)* + $($par = ($convert)(_drain.next().unwrap()); )* // Call the function with each parameter value - let r = $fn($($par),*); + let r = $fn($($arg),*); // Map the result $map(r) @@ -181,12 +180,13 @@ macro_rules! def_register { () => { def_register!(imp from_pure :); }; - (imp $abi:ident : $($par:ident => $mark:ty => $param:ty => $clone:expr),*) => { + (imp $abi:ident : $($par:ident => $arg:expr => $mark:ty => $param:ty => $let:stmt => $clone:expr),*) => { // ^ function ABI type // ^ function parameter generic type name (A, B, C etc.) - // ^ function parameter marker type (T, Ref or Mut) - // ^ function parameter actual type (T, &T or &mut T) - // ^ dereferencing function +// ^ call argument(like A, *B, &mut C etc) + // ^ function parameter marker type (T, Ref or Mut) + // ^ function parameter actual type (T, &T or &mut T) + // ^ argument let statement impl< $($par: Variant + Clone,)* FN: Fn($($param),*) -> RET + SendSync + 'static, @@ -196,7 +196,7 @@ macro_rules! def_register { fn register_fn(&mut self, name: &str, f: FN) -> &mut Self { self.global_module.set_fn(name, FnAccess::Public, &[$(map_type_id::<$par>()),*], - CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $clone),*)) + CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $let => $clone => $arg),*)) ); self } @@ -210,7 +210,7 @@ macro_rules! def_register { fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self { self.global_module.set_fn(name, FnAccess::Public, &[$(map_type_id::<$par>()),*], - CallableFunction::$abi(make_func!(f : map_result ; $($par => $clone),*)) + CallableFunction::$abi(make_func!(f : map_result ; $($par => $let => $clone => $arg),*)) ); self } @@ -219,11 +219,11 @@ macro_rules! def_register { //def_register!(imp_pop $($par => $mark => $param),*); }; ($p0:ident $(, $p:ident)*) => { - def_register!(imp from_pure : $p0 => $p0 => $p0 => by_value $(, $p => $p => $p => by_value)*); - def_register!(imp from_method : $p0 => Mut<$p0> => &mut $p0 => by_ref $(, $p => $p => $p => by_value)*); + def_register!(imp from_pure : $p0 => $p0 => $p0 => $p0 => let $p0 => by_value $(, $p => $p => $p => $p => let $p => by_value)*); + def_register!(imp from_method : $p0 => &mut $p0 => Mut<$p0> => &mut $p0 => let mut $p0 => by_ref $(, $p => $p => $p => $p => let $p => by_value)*); // ^ CallableFunction - // handle the first parameter ^ first parameter passed through - // ^ others passed by value (by_value) + // handle the first parameter ^ first parameter passed through + // ^ others passed by value (by_value) // Currently does not support first argument which is a reference, as there will be // conflicting implementations since &T: Any and T: Any cannot be distinguished diff --git a/src/module.rs b/src/module.rs index 8eacde7b..fa23abd6 100644 --- a/src/module.rs +++ b/src/module.rs @@ -442,7 +442,7 @@ impl Module { /// // Since it is a primary type, it can also be cheaply copied /// let double = args[1].clone().cast::(); /// // Get a mutable reference to the first argument. - /// let x = args[0].downcast_mut::().unwrap(); + /// let mut x = args[0].write_lock::().unwrap(); /// /// let orig = *x; /// @@ -534,7 +534,7 @@ impl Module { func: impl Fn(&mut A) -> FuncReturn + SendSync + 'static, ) -> u64 { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { - func(args[0].downcast_mut::().unwrap()).map(Dynamic::from) + func(&mut args[0].write_lock::().unwrap()).map(Dynamic::from) }; let arg_types = [TypeId::of::()]; self.set_fn(name, Public, &arg_types, Func::from_method(Box::new(f))) @@ -615,9 +615,9 @@ impl Module { ) -> u64 { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { let b = mem::take(args[1]).cast::(); - let a = args[0].downcast_mut::().unwrap(); + let mut a = args[0].write_lock::().unwrap(); - func(a, b).map(Dynamic::from) + func(&mut a, b).map(Dynamic::from) }; let arg_types = [TypeId::of::(), TypeId::of::()]; self.set_fn(name, Public, &arg_types, Func::from_method(Box::new(f))) @@ -739,9 +739,9 @@ impl Module { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { let b = mem::take(args[1]).cast::(); let c = mem::take(args[2]).cast::(); - let a = args[0].downcast_mut::().unwrap(); + let mut a = args[0].write_lock::().unwrap(); - func(a, b, c).map(Dynamic::from) + func(&mut a, b, c).map(Dynamic::from) }; let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; self.set_fn(name, Public, &arg_types, Func::from_method(Box::new(f))) @@ -773,9 +773,9 @@ impl Module { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { let b = mem::take(args[1]).cast::(); let c = mem::take(args[2]).cast::(); - let a = args[0].downcast_mut::().unwrap(); + let mut a = args[0].write_lock::().unwrap(); - func(a, b, c).map(Dynamic::from) + func(&mut a, b, c).map(Dynamic::from) }; let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; self.set_fn( @@ -896,9 +896,9 @@ impl Module { let b = mem::take(args[1]).cast::(); let c = mem::take(args[2]).cast::(); let d = mem::take(args[3]).cast::(); - let a = args[0].downcast_mut::().unwrap(); + let mut a = args[0].write_lock::().unwrap(); - func(a, b, c, d).map(Dynamic::from) + func(&mut a, b, c, d).map(Dynamic::from) }; let arg_types = [ TypeId::of::(), diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index c0bea7ee..5ca3b1c7 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -34,7 +34,7 @@ fn pad( _: &Module, args: &mut [&mut Dynamic], ) -> FuncReturn<()> { - let len = *args[1].downcast_ref::().unwrap(); + let len = *args[1].read_lock::().unwrap(); // Check if array will be over max size limit #[cfg(not(feature = "unchecked"))] @@ -52,7 +52,7 @@ fn pad( if len > 0 { let item = args[2].clone(); - let list = args[0].downcast_mut::().unwrap(); + let mut list = args[0].write_lock::().unwrap(); if len as usize > list.len() { list.resize(len as usize, item); diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 6ddc2b0c..b50d6915 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -228,7 +228,7 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str "pad", &[TypeId::of::(), TypeId::of::(), TypeId::of::()], |_engine: &Engine, _: &Module, args: &mut [&mut Dynamic]| { - let len = *args[1].downcast_ref::< INT>().unwrap(); + let len = *args[1].read_lock::< INT>().unwrap(); // Check if string will be over max size limit #[cfg(not(feature = "unchecked"))] @@ -243,7 +243,7 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str if len > 0 { let ch = mem::take(args[2]).cast::(); - let s = args[0].downcast_mut::().unwrap(); + let mut s = args[0].write_lock::().unwrap(); let orig_len = s.chars().count(); diff --git a/src/scope.rs b/src/scope.rs index 5693554c..b095f782 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -333,7 +333,7 @@ impl<'a> Scope<'a> { .iter() .rev() .find(|Entry { name: key, .. }| name == key) - .and_then(|Entry { value, .. }| value.downcast_ref::().cloned()) + .and_then(|Entry { value, .. }| value.read::()) } /// Update the value of the named entry. diff --git a/src/token.rs b/src/token.rs index 7d1a07ba..bd496afb 100644 --- a/src/token.rs +++ b/src/token.rs @@ -2,7 +2,7 @@ use crate::engine::{ Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, - KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF, + KEYWORD_SHARED, KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF, }; use crate::error::LexError; @@ -507,7 +507,7 @@ impl Token { | "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_THIS => Reserved(syntax.into()), + | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_SHARED | KEYWORD_THIS => Reserved(syntax.into()), _ => return None, })