Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Bug fixes
* Fixed bug in using bare function name as a variable (identified by Grain VM).
* Support bare function names as variables in Rhai Grain ([`#1158`](https://github.com/rhaiscript/rhai/pull/1158)).
* (Fuzzing) Fixed missing data-race condition in native function callbacks ([`#1161`](https://github.com/rhaiscript/rhai/pull/1161)).
* The `Engine::on_map_missing_property` callback now works properly with Rhai Grain ([`#1164`](https://github.com/rhaiscript/rhai/pull/1164)).

New features
------------
Expand Down
2 changes: 0 additions & 2 deletions src/eval/indexing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ impl Engine {
Err(err) => {
#[cfg(not(feature = "no_index"))]
#[cfg(feature = "internals")]
#[cfg(not(feature = "no_ast"))]
if let Some(ref cb) = self.invalid_array_index {
let context =
super::EvalContext::new(self, global, caches, _scope, _this_ptr);
Expand Down Expand Up @@ -107,7 +106,6 @@ impl Engine {

#[cfg(not(feature = "no_object"))]
#[cfg(feature = "internals")]
#[cfg(not(feature = "no_ast"))]
if let Some(ref cb) = self.missing_map_property {
if !map.contains_key(index.as_str()) {
let context =
Expand Down
2 changes: 2 additions & 0 deletions src/grain/bytecode/op.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::types::Token;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;

/// What `x op= y` needs to reproduce Rhai's resolution order.
///
Expand Down
75 changes: 30 additions & 45 deletions src/grain/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1785,41 +1785,43 @@ impl<'e> Vm<'e> {
.name(name)
.ok_or_else(|| malformed(format!("no name {name}")))?;

// A map is the one property holder that is not a host type, and
// `no_object` removes both it and the syntax that would reach one.
// Properties on maps and indexed values share the same reference path.
// Besides avoiding a copy for nested walks, this applies map callbacks
// and strict missing-property handling consistently with `[index]`.
#[cfg(not(feature = "no_object"))]
if target.is_map() {
let mut map = target
.write_lock::<Map>()
.ok_or_else(|| malformed("a map that is not a map".to_string()))?;

// Only a write creates a key. Rhai passes `add_if_not_found` for
// an assignment (`eval/chaining.rs:930`) and withholds it for a
// read (`:959`) and for a step on the way through (`:1086`), so
// reading `m.absent` must leave `m` alone — otherwise a closure
// holding the map sees a key nobody wrote.
let mut detached = Scope::new();
let mut index = self.strings_interner.get(key).into();
let assigning = last && value.is_some();

let mut item = self.engine.get_indexed_mut(
&mut self.global,
&mut self.caches,
&mut detached,
None,
target,
&mut index,
step_pos,
step_pos,
assigning,
false,
)?;

let target = item.as_mut();

if last {
if let Some(value) = value {
let entry = map.entry(key.into()).or_insert(Dynamic::UNIT);
self.store(program, chain_op(program, chain)?, entry, value, pos)?;
self.store(program, chain_op(program, chain)?, target, value, pos)?;
item.propagate_changed_value(pos)?;
return Ok((Dynamic::UNIT, true));
}
return match map.get(key) {
Some(entry) => Ok((entry.clone(), false)),
None => self.absent_key(key, step_pos).map(|unit| (unit, false)),
};
return Ok((item.take_or_clone(), false));
} else {
let (out, changed) =
self.walk_chain(program, chain, rest, target, operands, value, pos)?;
item.propagate_changed_value(pos)?;
return Ok((out, changed));
}

return match map.get_mut(key) {
Some(entry) => self.walk_chain(program, chain, rest, entry, operands, value, pos),
// Rhai walks on into a detached unit, so whatever the rest of
// the chain does to it is discarded (`eval/chaining.rs:211`).
None => {
let mut absent = self.absent_key(key, step_pos)?;
drop(map);
self.walk_chain(program, chain, rest, &mut absent, operands, value, pos)
}
};
}

// A host type: getter in, setter out.
Expand Down Expand Up @@ -3249,23 +3251,6 @@ impl<'e> Vm<'e> {
))
}

/// What reading a key a map does not have produces.
///
/// Unit, unless the host asked for the strict reading — which is a whole
/// engine option (`fail_on_invalid_map_property`) rather than anything the
/// script says, so it has to be consulted rather than assumed.
#[cfg(not(feature = "no_object"))]
fn absent_key(&self, key: &str, pos: Position) -> VmResult {
if self.engine.fail_on_invalid_map_property() {
Err(Box::new(EvalAltResult::ErrorPropertyNotFound(
key.to_string(),
pos,
)))
} else {
Ok(Dynamic::UNIT)
}
}

/// Fold the element on top of the stack into the literal's running total,
/// and refuse it if that puts the literal over a configured limit.
///
Expand Down
Loading