diff --git a/RELEASES.md b/RELEASES.md index d9cd5309..fcd99454 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -7,6 +7,11 @@ Version 0.15.1 This is a minor release which enables updating indexers (via registered indexer setters) and supports functions with `&str` parameters (maps transparently to `ImmutableString`). +Buf fix +------- + +* `let s="abc"; s[1].change_to('X');` now correctly sets the character '`X`' into '`s`' yielding `"aXc"`. + Breaking changes ---------------- diff --git a/src/engine.rs b/src/engine.rs index c3a7be8e..66f04f3b 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,6 +1,6 @@ //! Main module defining the script evaluation `Engine`. -use crate::any::{Dynamic, Union}; +use crate::any::{Dynamic, Union, Variant}; use crate::calc_fn_hash; use crate::error::ParseErrorType; use crate::fn_native::{CallableFunction, Callback, FnCallArgs}; @@ -105,6 +105,14 @@ impl Target<'_> { Self::StringChar(_, _, _) => false, } } + /// Is the `Target` a specific type? + pub fn is(&self) -> bool { + match self { + Target::Ref(r) => r.is::(), + Target::Value(r) => r.is::(), + Target::StringChar(_, _, _) => TypeId::of::() == TypeId::of::(), + } + } /// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary. pub fn clone_into_dynamic(self) -> Dynamic { match self { @@ -817,7 +825,7 @@ impl Engine { .map(|name| if name.is::() { "&str | ImmutableString" } else { - self.map_type_name(name.type_name()) + self.map_type_name((*name).type_name()) }) .collect::>() .join(", ") @@ -1006,9 +1014,7 @@ impl Engine { mut new_val: Option, ) -> Result<(Dynamic, bool), Box> { let is_ref = target.is_ref(); - - // Get a reference to the mutation target Dynamic - let obj = target.as_mut(); + let is_value = target.is_value(); // Pop the last index value let mut idx_val = idx_values.pop(); @@ -1090,24 +1096,38 @@ impl Engine { let ((name, native, pos), _, hash, _, def_val) = x.as_ref(); let def_val = def_val.as_ref(); - let mut arg_values: StaticVec<_> = once(obj) - .chain( - idx_val - .downcast_mut::>() - .unwrap() - .iter_mut(), - ) - .collect(); - let args = arg_values.as_mut(); + // Get a reference to the mutation target Dynamic + let (result, updated) = { + let obj = target.as_mut(); + let mut arg_values: StaticVec<_> = once(obj) + .chain( + idx_val + .downcast_mut::>() + .unwrap() + .iter_mut(), + ) + .collect(); + let args = arg_values.as_mut(); - self.exec_fn_call(state, lib, name, *native, *hash, args, is_ref, def_val, 0) - .map_err(|err| EvalAltResult::new_position(err, *pos)) + self.exec_fn_call( + state, lib, name, *native, *hash, args, is_ref, def_val, 0, + ) + .map_err(|err| EvalAltResult::new_position(err, *pos))? + }; + + // Feed the changed temp value back + if updated && !is_ref && !is_value { + let new_val = target.as_mut().clone(); + target.set_value(new_val)?; + } + + Ok((result, updated)) } // xxx.module::fn_name(...) - syntax error Expr::FnCall(_) => unreachable!(), // {xxx:map}.id = ??? #[cfg(not(feature = "no_object"))] - Expr::Property(x) if obj.is::() && new_val.is_some() => { + Expr::Property(x) if target.is::() && new_val.is_some() => { let ((prop, _, _), pos) = x.as_ref(); let index = prop.clone().into(); let mut val = self.get_indexed_mut(state, lib, target, index, *pos, true)?; @@ -1118,7 +1138,7 @@ impl Engine { } // {xxx:map}.id #[cfg(not(feature = "no_object"))] - Expr::Property(x) if obj.is::() => { + Expr::Property(x) if target.is::() => { let ((prop, _, _), pos) = x.as_ref(); let index = prop.clone().into(); let val = self.get_indexed_mut(state, lib, target, index, *pos, false)?; @@ -1128,7 +1148,7 @@ impl Engine { // xxx.id = ??? Expr::Property(x) if new_val.is_some() => { let ((_, _, setter), pos) = x.as_ref(); - let mut args = [obj, new_val.as_mut().unwrap()]; + let mut args = [target.as_mut(), new_val.as_mut().unwrap()]; self.exec_fn_call(state, lib, setter, true, 0, &mut args, is_ref, None, 0) .map(|(v, _)| (v, true)) .map_err(|err| EvalAltResult::new_position(err, *pos)) @@ -1136,14 +1156,14 @@ impl Engine { // xxx.id Expr::Property(x) => { let ((_, getter, _), pos) = x.as_ref(); - let mut args = [obj]; + let mut args = [target.as_mut()]; self.exec_fn_call(state, lib, getter, true, 0, &mut args, is_ref, None, 0) .map(|(v, _)| (v, false)) .map_err(|err| EvalAltResult::new_position(err, *pos)) } #[cfg(not(feature = "no_object"))] // {xxx:map}.prop[expr] | {xxx:map}.prop.expr - Expr::Index(x) | Expr::Dot(x) if obj.is::() => { + Expr::Index(x) | Expr::Dot(x) if target.is::() => { let (prop, expr, pos) = x.as_ref(); let is_idx = matches!(rhs, Expr::Index(_)); @@ -1164,7 +1184,7 @@ impl Engine { Expr::Index(x) | Expr::Dot(x) => { let (prop, expr, pos) = x.as_ref(); let is_idx = matches!(rhs, Expr::Index(_)); - let args = &mut [obj, &mut Default::default()]; + let args = &mut [target.as_mut(), &mut Default::default()]; let (mut val, updated) = if let Expr::Property(p) = prop { let ((_, getter, _), _) = p.as_ref();