Skip to content

Commit

Permalink
Merge pull request #863 from schungx/master
Browse files Browse the repository at this point in the history
Invoke variable resolver for captured variables in closures
  • Loading branch information
schungx committed Apr 16, 2024
2 parents 5f2f243 + 5d331a9 commit 0fdf842
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 1 deletion.
9 changes: 9 additions & 0 deletions CHANGELOG.md
@@ -1,6 +1,15 @@
Rhai Release Notes
==================

Version 1.18.1
==============

Bug fixes
---------

* Variable resolver now correctly resolves variables that are captured in a closure.


Version 1.18.0
==============

Expand Down
24 changes: 24 additions & 0 deletions src/eval/stmt.rs
Expand Up @@ -8,6 +8,7 @@ use crate::func::{get_builtin_op_assignment_fn, get_hasher};
use crate::tokenizer::Token;
use crate::types::dynamic::{AccessMode, Union};
use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, VarDefInfo, ERR, INT};
use core::num::NonZeroUsize;

Check warning on line 11 in src/eval/stmt.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_function,serde,metadata,internals,debugging, ...

unused import: `core::num::NonZeroUsize`

Check warning on line 11 in src/eval/stmt.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_closure,serde,metadata,internals,debugging, s...

unused import: `core::num::NonZeroUsize`

Check warning on line 11 in src/eval/stmt.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,sync,no_time,no_function,no_float,no_position,no...

unused import: `core::num::NonZeroUsize`

Check warning on line 11 in src/eval/stmt.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_time,no_function,no_float,no_position,no_inde...

unused import: `core::num::NonZeroUsize`
use std::hash::{Hash, Hasher};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
Expand Down Expand Up @@ -971,6 +972,29 @@ impl Engine {
#[cfg(not(feature = "no_closure"))]
Stmt::Share(x) => {
for (var, index) in &**x {
// Check the variable resolver, if any
if let Some(ref resolve_var) = self.resolve_var {
let orig_scope_len = scope.len();

let context =
EvalContext::new(self, global, caches, scope, this_ptr.as_deref_mut());
let resolved_var =
resolve_var(&var.name, index.map_or(0, NonZeroUsize::get), context);

if orig_scope_len != scope.len() {
// The scope is changed, always search from now on
global.always_search_scope = true;
}

match resolved_var {
// If resolved to a variable, skip it (because we cannot make it shared)
Ok(Some(_)) => continue,
Ok(None) => (),
Err(err) => return Err(err.fill_position(var.pos)),
}
}

// Search the scope
let index = index
.map(|n| scope.len() - n.get())
.or_else(|| scope.search(&var.name))
Expand Down
18 changes: 17 additions & 1 deletion tests/var_scope.rs
Expand Up @@ -324,7 +324,7 @@ fn test_scope_eval() {
}

#[test]
fn test_var_resolver() {
fn test_var_resolver1() {
let mut engine = Engine::new();

let mut scope = Scope::new();
Expand Down Expand Up @@ -392,6 +392,22 @@ fn test_var_resolver() {
EvalAltResult::ErrorVariableNotFound(n, ..) if n == "DO_NOT_USE"));
}

#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_object"))]
#[test]
fn test_var_resolver2() {
let mut engine = Engine::new();
let shared_state: INT = 42;

#[allow(deprecated)]
engine.on_var(move |name, _, _| if name == "state" { Ok(Some(Dynamic::from(shared_state))) } else { Ok(None) });

assert_eq!(engine.eval::<INT>("state").unwrap(), 42);
assert_eq!(engine.eval::<INT>("fn f() { state }; f()").unwrap(), 42);
assert_eq!(engine.eval::<INT>("let f = || state; f.call()").unwrap(), 42);
}

#[test]
fn test_var_def_filter() {
let mut engine = Engine::new();
Expand Down

0 comments on commit 0fdf842

Please sign in to comment.