Merge branch 'bug-fixes'
This commit is contained in:
commit
87d48948f9
@ -59,6 +59,10 @@ Enhancements
|
||||
* `StaticVec` is changed to keep three items inline instead of four.
|
||||
|
||||
|
||||
Version 1.0.7
|
||||
=============
|
||||
|
||||
|
||||
Version 1.0.6
|
||||
=============
|
||||
|
||||
@ -66,6 +70,7 @@ Bug fixes
|
||||
---------
|
||||
|
||||
* Eliminate unnecessary property write-back when accessed via a getter since property getters are assumed to be _pure_.
|
||||
* Writing to a property of an indexed valued obtained via an indexer now works properly by writing back the changed value via an index setter.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
@ -336,6 +336,20 @@ impl ChainArgument {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// Return the [position][Position].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub const fn position(&self) -> Position {
|
||||
match self {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
ChainArgument::Property(pos) => *pos,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
ChainArgument::MethodCallArgs(_, pos) => *pos,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
ChainArgument::IndexValue(_, pos) => *pos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -1279,6 +1293,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
ChainType::Indexing => {
|
||||
let pos = rhs.position();
|
||||
let root_pos = idx_val.position();
|
||||
let idx_val = idx_val
|
||||
.into_index_value()
|
||||
.expect("`chain_type` is `ChainType::Index`");
|
||||
@ -1288,17 +1303,50 @@ impl Engine {
|
||||
Expr::Dot(x, term, x_pos) | Expr::Index(x, term, x_pos)
|
||||
if !_terminate_chaining =>
|
||||
{
|
||||
let mut idx_val_for_setter = idx_val.clone();
|
||||
let idx_pos = x.lhs.position();
|
||||
let obj_ptr = &mut self.get_indexed_mut(
|
||||
let rhs_chain = match_chaining_type(rhs);
|
||||
|
||||
let (try_setter, result) = {
|
||||
let mut obj = self.get_indexed_mut(
|
||||
mods, state, lib, target, idx_val, idx_pos, false, true, level,
|
||||
)?;
|
||||
let is_obj_temp_val = obj.is_temp_value();
|
||||
let obj_ptr = &mut obj;
|
||||
|
||||
let rhs_chain = match_chaining_type(rhs);
|
||||
self.eval_dot_index_chain_helper(
|
||||
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, *term, idx_values,
|
||||
rhs_chain, level, new_val,
|
||||
)
|
||||
.map_err(|err| err.fill_position(*x_pos))
|
||||
match self.eval_dot_index_chain_helper(
|
||||
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, *term,
|
||||
idx_values, rhs_chain, level, new_val,
|
||||
) {
|
||||
Ok((result, true)) if is_obj_temp_val => {
|
||||
(Some(obj.take_or_clone()), (result, true))
|
||||
}
|
||||
Ok(result) => (None, result),
|
||||
Err(err) => return Err(err.fill_position(*x_pos)),
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(mut new_val) = try_setter {
|
||||
// Try to call index setter if value is changed
|
||||
let hash_set =
|
||||
FnCallHashes::from_native(self.constants.fn_hash_idx_set);
|
||||
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
||||
|
||||
if let Err(err) = self.exec_fn_call(
|
||||
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, true,
|
||||
root_pos, None, level,
|
||||
) {
|
||||
// Just ignore if there is no index setter
|
||||
if !matches!(*err, EvalAltResult::ErrorFunctionNotFound(_, _)) {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.check_data_size(target.as_ref())
|
||||
.map_err(|err| err.fill_position(root.1))?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
// xxx[rhs] op= new_val
|
||||
_ if new_val.is_some() => {
|
||||
@ -1331,11 +1379,10 @@ impl Engine {
|
||||
let hash_set =
|
||||
FnCallHashes::from_native(self.constants.fn_hash_idx_set);
|
||||
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
||||
let pos = Position::NONE;
|
||||
|
||||
self.exec_fn_call(
|
||||
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, true,
|
||||
pos, None, level,
|
||||
root_pos, None, level,
|
||||
)?;
|
||||
}
|
||||
|
||||
@ -2368,7 +2415,7 @@ impl Engine {
|
||||
let args = &mut [lhs_ptr_inner, &mut new_val];
|
||||
|
||||
match self.call_native_fn(mods, state, lib, op, hash, args, true, true, op_pos) {
|
||||
Err(err) if matches!(err.as_ref(), EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with(op)) =>
|
||||
Err(err) if matches!(*err, EvalAltResult::ErrorFunctionNotFound(ref f, _) if f.starts_with(op)) =>
|
||||
{
|
||||
// Expand to `var = var op rhs`
|
||||
let op = &op[..op.len() - 1]; // extract operator without =
|
||||
|
@ -119,7 +119,17 @@ fn test_get_set_chain_with_write_back() -> Result<(), Box<EvalAltResult>> {
|
||||
engine.register_get_set("x", TestChild::get_x, TestChild::set_x);
|
||||
engine.register_get_set("child", TestParent::get_child, TestParent::set_child);
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
engine.register_indexer_get_set(
|
||||
|parent: &mut TestParent, _: INT| parent.child.clone(),
|
||||
|parent: &mut TestParent, n: INT, mut new_child: TestChild| {
|
||||
new_child.x *= n;
|
||||
parent.child = new_child;
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn("new_tp", TestParent::new);
|
||||
engine.register_fn("new_tc", TestChild::new);
|
||||
|
||||
assert_eq!(engine.eval::<INT>("let a = new_tp(); a.child.x")?, 1);
|
||||
assert_eq!(
|
||||
@ -132,6 +142,18 @@ fn test_get_set_chain_with_write_back() -> Result<(), Box<EvalAltResult>> {
|
||||
"TestParent"
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
assert_eq!(
|
||||
engine.eval::<INT>("let a = new_tp(); let c = new_tc(); c.x = 123; a[2] = c; a.child.x")?,
|
||||
246
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
assert_eq!(
|
||||
engine.eval::<INT>("let a = new_tp(); a[2].x = 42; a.child.x")?,
|
||||
84
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -204,11 +226,31 @@ fn test_get_set_chain_without_write_back() -> Result<(), Box<EvalAltResult>> {
|
||||
|_: &mut Outer, new: Inner| panic!("Outer::inner setter called with {:?}", new),
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
engine.register_indexer_get_set(
|
||||
|t: &mut Outer, n: INT| Inner {
|
||||
value: t.inner.value * n,
|
||||
},
|
||||
|_: &mut Outer, n: INT, new: Inner| {
|
||||
panic!("Outer::inner index setter called with {} and {:?}", n, new)
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval_with_scope::<INT>(&mut scope, "outer.inner.value")?,
|
||||
42
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
assert_eq!(
|
||||
engine.eval_with_scope::<INT>(&mut scope, "outer[2].value")?,
|
||||
84
|
||||
);
|
||||
|
||||
engine.run_with_scope(&mut scope, "print(outer.inner.value)")?;
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
engine.run_with_scope(&mut scope, "print(outer[0].value)")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user