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.
|
* `StaticVec` is changed to keep three items inline instead of four.
|
||||||
|
|
||||||
|
|
||||||
|
Version 1.0.7
|
||||||
|
=============
|
||||||
|
|
||||||
|
|
||||||
Version 1.0.6
|
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_.
|
* 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
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
@ -336,6 +336,20 @@ impl ChainArgument {
|
|||||||
_ => None,
|
_ => 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"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -1279,6 +1293,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
ChainType::Indexing => {
|
ChainType::Indexing => {
|
||||||
let pos = rhs.position();
|
let pos = rhs.position();
|
||||||
|
let root_pos = idx_val.position();
|
||||||
let idx_val = idx_val
|
let idx_val = idx_val
|
||||||
.into_index_value()
|
.into_index_value()
|
||||||
.expect("`chain_type` is `ChainType::Index`");
|
.expect("`chain_type` is `ChainType::Index`");
|
||||||
@ -1288,17 +1303,50 @@ impl Engine {
|
|||||||
Expr::Dot(x, term, x_pos) | Expr::Index(x, term, x_pos)
|
Expr::Dot(x, term, x_pos) | Expr::Index(x, term, x_pos)
|
||||||
if !_terminate_chaining =>
|
if !_terminate_chaining =>
|
||||||
{
|
{
|
||||||
|
let mut idx_val_for_setter = idx_val.clone();
|
||||||
let idx_pos = x.lhs.position();
|
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,
|
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);
|
match self.eval_dot_index_chain_helper(
|
||||||
self.eval_dot_index_chain_helper(
|
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, *term,
|
||||||
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, *term, idx_values,
|
idx_values, rhs_chain, level, new_val,
|
||||||
rhs_chain, level, new_val,
|
) {
|
||||||
)
|
Ok((result, true)) if is_obj_temp_val => {
|
||||||
.map_err(|err| err.fill_position(*x_pos))
|
(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
|
// xxx[rhs] op= new_val
|
||||||
_ if new_val.is_some() => {
|
_ if new_val.is_some() => {
|
||||||
@ -1331,11 +1379,10 @@ impl Engine {
|
|||||||
let hash_set =
|
let hash_set =
|
||||||
FnCallHashes::from_native(self.constants.fn_hash_idx_set);
|
FnCallHashes::from_native(self.constants.fn_hash_idx_set);
|
||||||
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
||||||
let pos = Position::NONE;
|
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, true,
|
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];
|
let args = &mut [lhs_ptr_inner, &mut new_val];
|
||||||
|
|
||||||
match self.call_native_fn(mods, state, lib, op, hash, args, true, true, op_pos) {
|
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`
|
// Expand to `var = var op rhs`
|
||||||
let op = &op[..op.len() - 1]; // extract operator without =
|
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("x", TestChild::get_x, TestChild::set_x);
|
||||||
engine.register_get_set("child", TestParent::get_child, TestParent::set_child);
|
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_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!(engine.eval::<INT>("let a = new_tp(); a.child.x")?, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -132,6 +142,18 @@ fn test_get_set_chain_with_write_back() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"TestParent"
|
"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(())
|
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),
|
|_: &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!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<INT>(&mut scope, "outer.inner.value")?,
|
engine.eval_with_scope::<INT>(&mut scope, "outer.inner.value")?,
|
||||||
42
|
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)")?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user