Deprecate call_fn_dynamic into call_fn_raw.

This commit is contained in:
Stephen Chung 2021-11-15 11:13:00 +08:00
parent 615c3acad6
commit de906053ed
7 changed files with 218 additions and 92 deletions

View File

@ -19,8 +19,8 @@ New features
* `#[cfg(...)]` attributes can now be put directly on plugin functions or function defined in a plugin module. * `#[cfg(...)]` attributes can now be put directly on plugin functions or function defined in a plugin module.
* A custom syntax parser can now return a symbol starting with `$$` to inform the implementation function which syntax variant was actually parsed. * A custom syntax parser can now return a symbol starting with `$$` to inform the implementation function which syntax variant was actually parsed.
* `AST::iter_literal_variables` extracts all top-level literal constant/variable definitions from a script without running it. * `AST::iter_literal_variables` is added to extract all top-level literal constant/variable definitions from a script without running it.
* `Scope::clone_visible` is added that copies only the last instance of each variable, omitting all shadowed variables. * `Engine::call_fn_dynamic` is deprecated and `Engine::call_fn_raw` is added which allows keeping new variables in the custom scope.
Enhancements Enhancements
------------ ------------
@ -31,7 +31,8 @@ Enhancements
* Array adds a `sort` method with no parameters which sorts homogeneous arrays of built-in comparable types (e.g. `INT`). * Array adds a `sort` method with no parameters which sorts homogeneous arrays of built-in comparable types (e.g. `INT`).
* Inlining is disabled for error-path functions because errors are exceptional and scripts usually fail completely when an error is encountered. * Inlining is disabled for error-path functions because errors are exceptional and scripts usually fail completely when an error is encountered.
* The `optimize` module is completely eliminated under `no_optimize`, which should yield smaller code size. * The `optimize` module is completely eliminated under `no_optimize`, which should yield smaller code size.
* Add `NativeCallContext::position` to return the position of the function call. * `NativeCallContext::position` is added to return the position of the function call.
* `Scope::clone_visible` is added that copies only the last instance of each variable, omitting all shadowed variables.
Deprecated API's Deprecated API's
---------------- ----------------

View File

@ -111,6 +111,79 @@ impl Engine {
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
self.run_ast_with_scope(scope, ast) self.run_ast_with_scope(scope, ast)
} }
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments
/// and optionally a value for binding to the `this` pointer.
///
/// Not available under `no_function`.
///
/// There is an option to evaluate the [`AST`] to load necessary modules before calling the function.
///
/// # Deprecated
///
/// This method is deprecated. Use [`run_ast_with_scope`][Engine::run_ast_with_scope] instead.
///
/// This method will be removed in the next major version.
///
/// # WARNING
///
/// All the arguments are _consumed_, meaning that they're replaced by `()`.
/// This is to avoid unnecessarily cloning the arguments.
/// Do not use the arguments after this call. If they are needed afterwards,
/// clone them _before_ calling this function.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// # #[cfg(not(feature = "no_function"))]
/// # {
/// use rhai::{Engine, Scope, Dynamic};
///
/// let engine = Engine::new();
///
/// let ast = engine.compile("
/// fn add(x, y) { len(x) + y + foo }
/// fn add1(x) { len(x) + 1 + foo }
/// fn bar() { foo/2 }
/// fn action(x) { this += x; } // function using 'this' pointer
/// ")?;
///
/// let mut scope = Scope::new();
/// scope.push("foo", 42_i64);
///
/// // Call the script-defined function
/// let result = engine.call_fn_dynamic(&mut scope, &ast, true, "add", None, [ "abc".into(), 123_i64.into() ])?;
/// // ^^^^ no 'this' pointer
/// assert_eq!(result.cast::<i64>(), 168);
///
/// let result = engine.call_fn_dynamic(&mut scope, &ast, true, "add1", None, [ "abc".into() ])?;
/// assert_eq!(result.cast::<i64>(), 46);
///
/// let result = engine.call_fn_dynamic(&mut scope, &ast, true, "bar", None, [])?;
/// assert_eq!(result.cast::<i64>(), 21);
///
/// let mut value: Dynamic = 1_i64.into();
/// let result = engine.call_fn_dynamic(&mut scope, &ast, true, "action", Some(&mut value), [ 41_i64.into() ])?;
/// // ^^^^^^^^^^^^^^^^ binding the 'this' pointer
/// assert_eq!(value.as_int().expect("value should be INT"), 42);
/// # }
/// # Ok(())
/// # }
/// ```
#[deprecated(since = "1.1.0", note = "use `call_fn_raw` instead")]
#[cfg(not(feature = "no_function"))]
#[inline(always)]
pub fn call_fn_dynamic(
&self,
scope: &mut Scope,
ast: &AST,
eval_ast: bool,
name: impl AsRef<str>,
this_ptr: Option<&mut Dynamic>,
arg_values: impl AsMut<[Dynamic]>,
) -> RhaiResult {
self.call_fn_raw(scope, ast, eval_ast, true, name, this_ptr, arg_values)
}
} }
impl Dynamic { impl Dynamic {

View File

@ -1901,10 +1901,8 @@ impl Engine {
) -> Result<T, Box<EvalAltResult>> { ) -> Result<T, Box<EvalAltResult>> {
let mut arg_values = crate::StaticVec::new(); let mut arg_values = crate::StaticVec::new();
args.parse(&mut arg_values); args.parse(&mut arg_values);
let mut args: crate::StaticVec<_> = arg_values.iter_mut().collect();
let name = name.as_ref();
let result = self.call_fn_dynamic_raw(scope, ast, true, name, &mut None, &mut args)?; let result = self.call_fn_raw(scope, ast, true, true, name, None, arg_values)?;
let typ = self.map_type_name(result.type_name()); let typ = self.map_type_name(result.type_name());
@ -1918,12 +1916,14 @@ impl Engine {
}) })
} }
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments /// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments
/// and optionally a value for binding to the `this` pointer. /// and the following options:
///
/// * whether to evaluate the [`AST`] to load necessary modules before calling the function
/// * whether to rewind the [`Scope`] after the function call
/// * a value for binding to the `this` pointer (if any)
/// ///
/// Not available under `no_function`. /// Not available under `no_function`.
/// ///
/// There is an option to evaluate the [`AST`] to load necessary modules before calling the function.
///
/// # WARNING /// # WARNING
/// ///
/// All the arguments are _consumed_, meaning that they're replaced by `()`. /// All the arguments are _consumed_, meaning that they're replaced by `()`.
@ -1946,76 +1946,66 @@ impl Engine {
/// fn add1(x) { len(x) + 1 + foo } /// fn add1(x) { len(x) + 1 + foo }
/// fn bar() { foo/2 } /// fn bar() { foo/2 }
/// fn action(x) { this += x; } // function using 'this' pointer /// fn action(x) { this += x; } // function using 'this' pointer
/// fn decl(x) { let hello = x; } // declaring variables
/// ")?; /// ")?;
/// ///
/// let mut scope = Scope::new(); /// let mut scope = Scope::new();
/// scope.push("foo", 42_i64); /// scope.push("foo", 42_i64);
/// ///
/// // Call the script-defined function /// // Call the script-defined function
/// let result = engine.call_fn_dynamic(&mut scope, &ast, true, "add", None, [ "abc".into(), 123_i64.into() ])?; /// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "add", None, [ "abc".into(), 123_i64.into() ])?;
/// // ^^^^ no 'this' pointer /// // ^^^^ no 'this' pointer
/// assert_eq!(result.cast::<i64>(), 168); /// assert_eq!(result.cast::<i64>(), 168);
/// ///
/// let result = engine.call_fn_dynamic(&mut scope, &ast, true, "add1", None, [ "abc".into() ])?; /// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "add1", None, [ "abc".into() ])?;
/// assert_eq!(result.cast::<i64>(), 46); /// assert_eq!(result.cast::<i64>(), 46);
/// ///
/// let result = engine.call_fn_dynamic(&mut scope, &ast, true, "bar", None, [])?; /// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "bar", None, [])?;
/// assert_eq!(result.cast::<i64>(), 21); /// assert_eq!(result.cast::<i64>(), 21);
/// ///
/// let mut value: Dynamic = 1_i64.into(); /// let mut value: Dynamic = 1_i64.into();
/// let result = engine.call_fn_dynamic(&mut scope, &ast, true, "action", Some(&mut value), [ 41_i64.into() ])?; /// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "action", Some(&mut value), [ 41_i64.into() ])?;
/// // ^^^^^^^^^^^^^^^^ binding the 'this' pointer /// // ^^^^^^^^^^^^^^^^ binding the 'this' pointer
/// assert_eq!(value.as_int().expect("value should be INT"), 42); /// assert_eq!(value.as_int().expect("value should be INT"), 42);
///
/// engine.call_fn_raw(&mut scope, &ast, true, false, "decl", None, [ 42_i64.into() ])?;
/// // ^^^^^ do not rewind scope
/// assert_eq!(scope.get_value::<i64>("hello").unwrap(), 42);
/// # } /// # }
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline] #[inline]
pub fn call_fn_dynamic( pub fn call_fn_raw(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
eval_ast: bool, eval_ast: bool,
rewind_scope: bool,
name: impl AsRef<str>, name: impl AsRef<str>,
mut this_ptr: Option<&mut Dynamic>, this_ptr: Option<&mut Dynamic>,
mut arg_values: impl AsMut<[Dynamic]>, arg_values: impl AsMut<[Dynamic]>,
) -> RhaiResult {
let name = name.as_ref();
let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect();
self.call_fn_dynamic_raw(scope, ast, eval_ast, name, &mut this_ptr, &mut args)
}
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
///
/// # WARNING
///
/// All the arguments are _consumed_, meaning that they're replaced by `()`.
/// This is to avoid unnecessarily cloning the arguments.
/// Do not use the arguments after this call. If they are needed afterwards,
/// clone them _before_ calling this function.
#[cfg(not(feature = "no_function"))]
#[inline]
pub(crate) fn call_fn_dynamic_raw(
&self,
scope: &mut Scope,
ast: &AST,
eval_ast: bool,
name: &str,
this_ptr: &mut Option<&mut Dynamic>,
args: &mut FnCallArgs,
) -> RhaiResult { ) -> RhaiResult {
let state = &mut EvalState::new(); let state = &mut EvalState::new();
let mods = &mut Imports::new(); let mods = &mut Imports::new();
let lib = &[ast.lib()];
let statements = ast.statements(); let statements = ast.statements();
let orig_scope_len = scope.len();
if eval_ast && !statements.is_empty() { if eval_ast && !statements.is_empty() {
// Make sure new variables introduced at global level do not _spill_ into the function call // Make sure new variables introduced at global level do not _spill_ into the function call
let orig_scope_len = scope.len(); self.eval_global_statements(scope, mods, state, statements, &[ast.lib()], 0)?;
self.eval_global_statements(scope, mods, state, statements, lib, 0)?;
if rewind_scope {
scope.rewind(orig_scope_len); scope.rewind(orig_scope_len);
} }
}
let name = name.as_ref();
let mut this_ptr = this_ptr;
let mut arg_values = arg_values;
let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect();
let fn_def = ast let fn_def = ast
.lib() .lib()
@ -2024,19 +2014,27 @@ impl Engine {
// Check for data race. // Check for data race.
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
crate::func::call::ensure_no_data_race(name, args, false)?; crate::func::call::ensure_no_data_race(name, &mut args, false)?;
self.call_script_fn( let result = self.call_script_fn(
scope, scope,
mods, mods,
state, state,
lib, &[ast.lib()],
this_ptr, &mut this_ptr,
fn_def, fn_def,
args, &mut args,
Position::NONE, Position::NONE,
rewind_scope,
0, 0,
) );
// Remove arguments
if !rewind_scope && !args.is_empty() {
scope.remove_range(orig_scope_len, args.len())
}
result
} }
/// Optimize the [`AST`] with constants defined in an external Scope. /// Optimize the [`AST`] with constants defined in an external Scope.
/// An optimized copy of the [`AST`] is returned while the original [`AST`] is consumed. /// An optimized copy of the [`AST`] is returned while the original [`AST`] is consumed.

View File

@ -2205,7 +2205,7 @@ impl Engine {
// Statement block // Statement block
Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT), Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT),
Expr::Stmt(x) => { Expr::Stmt(x) => {
self.eval_stmt_block(scope, mods, state, lib, this_ptr, x, true, level) self.eval_stmt_block(scope, mods, state, lib, this_ptr, x, true, true, level)
} }
// lhs[idx_expr] // lhs[idx_expr]
@ -2374,6 +2374,7 @@ impl Engine {
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
statements: &[Stmt], statements: &[Stmt],
restore_prev_state: bool, restore_prev_state: bool,
rewind_scope: bool,
level: usize, level: usize,
) -> RhaiResult { ) -> RhaiResult {
if statements.is_empty() { if statements.is_empty() {
@ -2385,7 +2386,7 @@ impl Engine {
let prev_scope_len = scope.len(); let prev_scope_len = scope.len();
let prev_mods_len = mods.len(); let prev_mods_len = mods.len();
if restore_prev_state { if rewind_scope {
state.scope_level += 1; state.scope_level += 1;
} }
@ -2427,10 +2428,12 @@ impl Engine {
state.pop_fn_resolution_cache(); state.pop_fn_resolution_cache();
} }
if restore_prev_state { if rewind_scope {
scope.rewind(prev_scope_len); scope.rewind(prev_scope_len);
mods.truncate(prev_mods_len);
state.scope_level -= 1; state.scope_level -= 1;
}
if restore_prev_state {
mods.truncate(prev_mods_len);
// The impact of new local variables goes away at the end of a block // The impact of new local variables goes away at the end of a block
// because any new variables introduced will go out of scope // because any new variables introduced will go out of scope
@ -2617,9 +2620,9 @@ impl Engine {
// Block scope // Block scope
Stmt::Block(statements, _) if statements.is_empty() => Ok(Dynamic::UNIT), Stmt::Block(statements, _) if statements.is_empty() => Ok(Dynamic::UNIT),
Stmt::Block(statements, _) => { Stmt::Block(statements, _) => self.eval_stmt_block(
self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, true, level) scope, mods, state, lib, this_ptr, statements, true, true, level,
} ),
// If statement // If statement
Stmt::If(expr, x, _) => { Stmt::If(expr, x, _) => {
@ -2630,13 +2633,17 @@ impl Engine {
if guard_val { if guard_val {
if !x.0.is_empty() { if !x.0.is_empty() {
self.eval_stmt_block(scope, mods, state, lib, this_ptr, &x.0, true, level) self.eval_stmt_block(
scope, mods, state, lib, this_ptr, &x.0, true, true, level,
)
} else { } else {
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
} else { } else {
if !x.1.is_empty() { if !x.1.is_empty() {
self.eval_stmt_block(scope, mods, state, lib, this_ptr, &x.1, true, level) self.eval_stmt_block(
scope, mods, state, lib, this_ptr, &x.1, true, true, level,
)
} else { } else {
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
@ -2676,7 +2683,7 @@ impl Engine {
Some(if !statements.is_empty() { Some(if !statements.is_empty() {
self.eval_stmt_block( self.eval_stmt_block(
scope, mods, state, lib, this_ptr, statements, true, level, scope, mods, state, lib, this_ptr, statements, true, true, level,
) )
} else { } else {
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
@ -2690,7 +2697,7 @@ impl Engine {
// Default match clause // Default match clause
if !def_stmt.is_empty() { if !def_stmt.is_empty() {
self.eval_stmt_block( self.eval_stmt_block(
scope, mods, state, lib, this_ptr, def_stmt, true, level, scope, mods, state, lib, this_ptr, def_stmt, true, true, level,
) )
} else { } else {
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
@ -2701,7 +2708,8 @@ impl Engine {
// Loop // Loop
Stmt::While(Expr::Unit(_), body, _) => loop { Stmt::While(Expr::Unit(_), body, _) => loop {
if !body.is_empty() { if !body.is_empty() {
match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level) match self
.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, true, level)
{ {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
@ -2727,7 +2735,8 @@ impl Engine {
return Ok(Dynamic::UNIT); return Ok(Dynamic::UNIT);
} }
if !body.is_empty() { if !body.is_empty() {
match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level) match self
.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, true, level)
{ {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
@ -2744,7 +2753,8 @@ impl Engine {
let is_while = !options.contains(AST_OPTION_NEGATED); let is_while = !options.contains(AST_OPTION_NEGATED);
if !body.is_empty() { if !body.is_empty() {
match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level) match self
.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, true, level)
{ {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
@ -2843,7 +2853,7 @@ impl Engine {
} }
let result = self.eval_stmt_block( let result = self.eval_stmt_block(
scope, mods, state, lib, this_ptr, statements, true, level, scope, mods, state, lib, this_ptr, statements, true, true, level,
); );
match result { match result {
@ -2907,7 +2917,9 @@ impl Engine {
let (try_stmt, err_var, catch_stmt) = x.as_ref(); let (try_stmt, err_var, catch_stmt) = x.as_ref();
let result = self let result = self
.eval_stmt_block(scope, mods, state, lib, this_ptr, try_stmt, true, level) .eval_stmt_block(
scope, mods, state, lib, this_ptr, try_stmt, true, true, level,
)
.map(|_| Dynamic::UNIT); .map(|_| Dynamic::UNIT);
match result { match result {
@ -2959,7 +2971,7 @@ impl Engine {
}); });
let result = self.eval_stmt_block( let result = self.eval_stmt_block(
scope, mods, state, lib, this_ptr, catch_stmt, true, level, scope, mods, state, lib, this_ptr, catch_stmt, true, true, level,
); );
scope.rewind(orig_scope_len); scope.rewind(orig_scope_len);

View File

@ -487,6 +487,7 @@ impl Engine {
fn_def: &crate::ast::ScriptFnDef, fn_def: &crate::ast::ScriptFnDef,
args: &mut FnCallArgs, args: &mut FnCallArgs,
pos: Position, pos: Position,
rewind_scope: bool,
level: usize, level: usize,
) -> RhaiResult { ) -> RhaiResult {
#[inline(never)] #[inline(never)]
@ -511,6 +512,8 @@ impl Engine {
.into()) .into())
} }
assert!(fn_def.params.len() == args.len());
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut mods.num_operations, pos)?; self.inc_operations(&mut mods.num_operations, pos)?;
@ -565,7 +568,17 @@ impl Engine {
// Evaluate the function // Evaluate the function
let body = &fn_def.body; let body = &fn_def.body;
let result = self let result = self
.eval_stmt_block(scope, mods, state, unified_lib, this_ptr, body, true, level) .eval_stmt_block(
scope,
mods,
state,
unified_lib,
this_ptr,
body,
true,
rewind_scope,
level,
)
.or_else(|err| match *err { .or_else(|err| match *err {
// Convert return statement to return value // Convert return statement to return value
EvalAltResult::Return(x, _) => Ok(x), EvalAltResult::Return(x, _) => Ok(x),
@ -589,7 +602,9 @@ impl Engine {
}); });
// Remove all local variables // Remove all local variables
if rewind_scope {
scope.rewind(prev_scope_len); scope.rewind(prev_scope_len);
}
mods.truncate(prev_mods_len); mods.truncate(prev_mods_len);
if unified { if unified {
@ -735,7 +750,6 @@ impl Engine {
empty_scope = Scope::new(); empty_scope = Scope::new();
&mut empty_scope &mut empty_scope
}; };
let orig_scope_len = scope.len();
let result = if _is_method_call { let result = if _is_method_call {
// Method call of script function - map first argument to `this` // Method call of script function - map first argument to `this`
@ -755,6 +769,7 @@ impl Engine {
func, func,
rest_args, rest_args,
pos, pos,
true,
level, level,
); );
@ -779,8 +794,9 @@ impl Engine {
let level = _level + 1; let level = _level + 1;
let result = let result = self.call_script_fn(
self.call_script_fn(scope, mods, state, lib, &mut None, func, args, pos, level); scope, mods, state, lib, &mut None, func, args, pos, true, level,
);
// Restore the original source // Restore the original source
mods.source = orig_source; mods.source = orig_source;
@ -793,8 +809,6 @@ impl Engine {
result? result?
}; };
scope.rewind(orig_scope_len);
return Ok((result, false)); return Ok((result, false));
} }
@ -817,7 +831,9 @@ impl Engine {
lib: &[&Module], lib: &[&Module],
level: usize, level: usize,
) -> RhaiResult { ) -> RhaiResult {
self.eval_stmt_block(scope, mods, state, lib, &mut None, statements, false, level) self.eval_stmt_block(
scope, mods, state, lib, &mut None, statements, false, false, level,
)
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::Return(out, _) => Ok(out), EvalAltResult::Return(out, _) => Ok(out),
EvalAltResult::LoopBreak(_, _) => { EvalAltResult::LoopBreak(_, _) => {
@ -1435,7 +1451,7 @@ impl Engine {
let level = level + 1; let level = level + 1;
let result = self.call_script_fn( let result = self.call_script_fn(
new_scope, mods, state, lib, &mut None, fn_def, &mut args, pos, level, new_scope, mods, state, lib, &mut None, fn_def, &mut args, pos, true, level,
); );
mods.source = source; mods.source = source;

View File

@ -587,6 +587,16 @@ impl<'a> Scope<'a> {
.zip(self.values.iter()) .zip(self.values.iter())
.map(|((name, _), value)| (name.as_ref(), value.is_read_only(), value)) .map(|((name, _), value)| (name.as_ref(), value.is_read_only(), value))
} }
/// Remove a range of entries within the [`Scope`].
///
/// # Panics
///
/// Panics if the range is out of bounds.
#[inline]
pub(crate) fn remove_range(&mut self, start: usize, len: usize) {
self.values.drain(start..start + len).for_each(|_| {});
self.names.drain(start..start + len).for_each(|_| {});
}
} }
impl<'a, K: Into<Cow<'a, str>>> Extend<(K, Dynamic)> for Scope<'a> { impl<'a, K: Into<Cow<'a, str>>> Extend<(K, Dynamic)> for Scope<'a> {

View File

@ -22,9 +22,9 @@ fn test_call_fn() -> Result<(), Box<EvalAltResult>> {
fn hello() { fn hello() {
41 + foo 41 + foo
} }
fn define_var() { fn define_var(scale) {
let bar = 21; let bar = 21;
bar * 2 bar * scale
} }
", ",
)?; )?;
@ -38,11 +38,6 @@ fn test_call_fn() -> Result<(), Box<EvalAltResult>> {
let r: INT = engine.call_fn(&mut scope, &ast, "hello", ())?; let r: INT = engine.call_fn(&mut scope, &ast, "hello", ())?;
assert_eq!(r, 42); assert_eq!(r, 42);
let r: INT = engine.call_fn(&mut scope, &ast, "define_var", ())?;
assert_eq!(r, 42);
assert!(!scope.contains("bar"));
assert_eq!( assert_eq!(
scope scope
.get_value::<INT>("foo") .get_value::<INT>("foo")
@ -50,6 +45,27 @@ fn test_call_fn() -> Result<(), Box<EvalAltResult>> {
1 1
); );
let r: INT = engine.call_fn(&mut scope, &ast, "define_var", (2 as INT,))?;
assert_eq!(r, 42);
assert!(!scope.contains("bar"));
let args = [(2 as INT).into()];
let r = engine
.call_fn_raw(&mut scope, &ast, false, false, "define_var", None, args)?
.as_int()
.unwrap();
assert_eq!(r, 42);
assert_eq!(
scope
.get_value::<INT>("bar")
.expect("variable bar should exist"),
21
);
assert!(!scope.contains("scale"));
Ok(()) Ok(())
} }