diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b093301..d16a72aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,13 +16,15 @@ Breaking changes * Zero step in the `range` function now raises an error instead of creating an infinite stream. * Error variable captured by `catch` is now an _object map_ containing error fields. * `EvalAltResult::clear_position` is renamed `EvalAltResult::take_position` and returns the position taken. +* `private` functions in an `AST` can now be called with `call_fn` etc. +* `NativeCallContext::call_fn_dynamic_raw` no longer has the `pub_only` parameter. Enhancements ------------ * `range` function now supports negative step and decreasing streams (i.e. to < from). * More information is provided to the error variable captured by the `catch` statement in an _object map_. - +* Previously, `private` functions in an `AST` cannot be called with `call_fn` etc. This is inconvenient when trying to call a function inside a script which also serves as a loadable module exporting part (but not all) of the functions. Now, all functions (`private` or not) can be called in an `AST`. The `private` keyword is relegated to preventing a function from being exported. Version 0.19.13 =============== diff --git a/src/engine.rs b/src/engine.rs index febc1441..3dc81871 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1148,8 +1148,8 @@ impl Engine { let args = &mut [target_val, &mut idx_val2, &mut (new_val.0).0]; self.exec_fn_call( - mods, state, lib, FN_IDX_SET, None, args, is_ref, true, false, - val_pos, None, level, + mods, state, lib, FN_IDX_SET, None, args, is_ref, true, val_pos, + None, level, ) .map_err(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) @@ -1189,7 +1189,7 @@ impl Engine { } = x.as_ref(); let args = idx_val.as_fn_call_args(); self.make_method_call( - mods, state, lib, name, *hash, target, args, false, *pos, level, + mods, state, lib, name, *hash, target, args, *pos, level, ) } // xxx.fn_name(...) = ??? @@ -1229,8 +1229,8 @@ impl Engine { let mut new_val = new_val; let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0]; self.exec_fn_call( - mods, state, lib, setter, None, &mut args, is_ref, true, false, *pos, - None, level, + mods, state, lib, setter, None, &mut args, is_ref, true, *pos, None, + level, ) .map(|(v, _)| (v, true)) } @@ -1239,8 +1239,8 @@ impl Engine { let (getter, _, Ident { pos, .. }) = x.as_ref(); let mut args = [target_val]; self.exec_fn_call( - mods, state, lib, getter, None, &mut args, is_ref, true, false, *pos, - None, level, + mods, state, lib, getter, None, &mut args, is_ref, true, *pos, None, + level, ) .map(|(v, _)| (v, false)) } @@ -1264,7 +1264,7 @@ impl Engine { } = x.as_ref(); let args = idx_val.as_fn_call_args(); let (val, _) = self.make_method_call( - mods, state, lib, name, *hash, target, args, false, *pos, level, + mods, state, lib, name, *hash, target, args, *pos, level, )?; val.into() } @@ -1291,8 +1291,8 @@ impl Engine { let arg_values = &mut [target_val, &mut Default::default()]; let args = &mut arg_values[..1]; let (mut val, updated) = self.exec_fn_call( - mods, state, lib, getter, None, args, is_ref, true, false, - *pos, None, level, + mods, state, lib, getter, None, args, is_ref, true, *pos, None, + level, )?; let val = &mut val; @@ -1318,7 +1318,7 @@ impl Engine { arg_values[1] = val; self.exec_fn_call( mods, state, lib, setter, None, arg_values, is_ref, true, - false, *pos, None, level, + *pos, None, level, ) .or_else( |err| match *err { @@ -1343,7 +1343,7 @@ impl Engine { } = f.as_ref(); let args = idx_val.as_fn_call_args(); let (mut val, _) = self.make_method_call( - mods, state, lib, name, *hash, target, args, false, *pos, level, + mods, state, lib, name, *hash, target, args, *pos, level, )?; let val = &mut val; let target = &mut val.into(); @@ -1613,8 +1613,8 @@ impl Engine { let mut idx = idx; let args = &mut [target, &mut idx]; self.exec_fn_call( - _mods, state, _lib, FN_IDX_GET, None, args, _is_ref, true, false, idx_pos, - None, _level, + _mods, state, _lib, FN_IDX_GET, None, args, _is_ref, true, idx_pos, None, + _level, ) .map(|(v, _)| v.into()) .map_err(|err| match *err { @@ -1775,8 +1775,7 @@ impl Engine { .. } = x.as_ref(); self.make_function_call( - scope, mods, state, lib, this_ptr, name, args, *hash, false, *pos, *cap_scope, - level, + scope, mods, state, lib, this_ptr, name, args, *hash, *pos, *cap_scope, level, ) } diff --git a/src/engine_api.rs b/src/engine_api.rs index f40b8bb3..9fcd266d 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1775,7 +1775,7 @@ impl Engine { let fn_def = ast .lib() - .get_script_fn(name, args.len(), true) + .get_script_fn(name, args.len(), false) .ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?; // Check for data race. diff --git a/src/fn_call.rs b/src/fn_call.rs index 1adc1db8..9bae00a6 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -546,13 +546,12 @@ impl Engine { lib: &[&Module], fn_name: &str, arg_types: impl AsRef<[TypeId]>, - pub_only: bool, ) -> bool { let arg_types = arg_types.as_ref(); let hash_fn = calc_native_fn_hash(empty(), fn_name, arg_types.iter().cloned()); let hash_script = calc_script_fn_hash(empty(), fn_name, arg_types.len()); - self.has_override(mods, state, lib, hash_fn, hash_script, pub_only) + self.has_override(mods, state, lib, hash_fn, hash_script) } // Has a system function an override? @@ -564,7 +563,6 @@ impl Engine { lib: &[&Module], hash_fn: Option, hash_script: Option, - pub_only: bool, ) -> bool { // Check if it is already in the cache if let Some(state) = state.as_mut() { @@ -583,17 +581,20 @@ impl Engine { } // First check script-defined functions - let r = hash_script.map_or(false, |hash| lib.iter().any(|&m| m.contains_fn(hash, pub_only))) - //|| hash_fn.map_or(false, |hash| lib.iter().any(|&m| m.contains_fn(hash, pub_only))) + let r = hash_script.map_or(false, |hash| lib.iter().any(|&m| m.contains_fn(hash, false))) + //|| hash_fn.map_or(false, |hash| lib.iter().any(|&m| m.contains_fn(hash, false))) // Then check registered functions - //|| hash_script.map_or(false, |hash| self.global_namespace.contains_fn(hash, pub_only)) + || hash_script.map_or(false, |hash| self.global_namespace.contains_fn(hash, false)) || hash_fn.map_or(false, |hash| self.global_namespace.contains_fn(hash, false)) // Then check packages || hash_script.map_or(false, |hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false))) || hash_fn.map_or(false, |hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false))) // Then check imported modules || hash_script.map_or(false, |hash| mods.map_or(false, |m| m.contains_fn(hash))) - || hash_fn.map_or(false, |hash| mods.map_or(false, |m| m.contains_fn(hash))); + || hash_fn.map_or(false, |hash| mods.map_or(false, |m| m.contains_fn(hash))) + // Then check sub-modules + || hash_script.map_or(false, |hash| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash))) + || hash_fn.map_or(false, |hash| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash))); // If there is no override, put that information into the cache if !r { @@ -627,7 +628,6 @@ impl Engine { args: &mut FnCallArgs, is_ref: bool, _is_method: bool, - pub_only: bool, pos: Position, _capture_scope: Option, _level: usize, @@ -650,7 +650,6 @@ impl Engine { lib, Some(hash_fn), hash_script, - pub_only, ) => { Ok(( @@ -669,7 +668,6 @@ impl Engine { lib, Some(hash_fn), hash_script, - pub_only, ) => { EvalAltResult::ErrorRuntime( @@ -692,19 +690,35 @@ impl Engine { .fn_resolution_cache_mut() .entry(hash_script) .or_insert_with(|| { + // Search order: + // 1) AST - script functions in the AST + // 2) Global modules - packages + // 3) Imported modules - functions marked with global namespace + // 4) Global sub-modules - functions marked with global namespace lib.iter() .find_map(|&m| { - m.get_fn(hash_script, pub_only) + m.get_fn(hash_script, false) .map(|f| (f.clone(), m.id_raw().cloned())) }) - //.or_else(|| self.global_namespace.get_fn(hash_script, pub_only)) + //.or_else(|| self.global_namespace.get_fn(hash_script, false).cloned().map(|f| (f, None))) .or_else(|| { self.global_modules.iter().find_map(|m| { m.get_fn(hash_script, false) .map(|f| (f.clone(), m.id_raw().cloned())) }) }) - // .or_else(|| mods.iter().find_map(|(_, m)| m.get_qualified_fn(hash_script).map(|f| (f.clone(), m.id_raw().cloned())))) + .or_else(|| { + mods.iter().find_map(|(_, m)| { + m.get_qualified_fn(hash_script) + .map(|f| (f.clone(), m.id_raw().cloned())) + }) + }) + .or_else(|| { + self.global_sub_modules.values().find_map(|m| { + m.get_qualified_fn(hash_script) + .map(|f| (f.clone(), m.id_raw().cloned())) + }) + }) }) .as_ref() .map(|(f, s)| (Some(f.clone()), s.clone())) @@ -872,7 +886,6 @@ impl Engine { hash_script: Option, target: &mut crate::engine::Target, mut call_args: StaticVec, - pub_only: bool, pos: Position, level: usize, ) -> Result<(Dynamic, bool), Box> { @@ -900,7 +913,7 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - mods, state, lib, fn_name, hash, args, false, false, pub_only, pos, None, level, + mods, state, lib, fn_name, hash, args, false, false, pos, None, level, ) } else if fn_name == KEYWORD_FN_PTR_CALL && call_args.len() > 0 @@ -923,7 +936,7 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - mods, state, lib, fn_name, hash, args, is_ref, true, pub_only, pos, None, level, + mods, state, lib, fn_name, hash, args, is_ref, true, pos, None, level, ) } else if fn_name == KEYWORD_FN_PTR_CURRY && obj.is::() { // Curry call @@ -988,7 +1001,7 @@ impl Engine { let args = arg_values.as_mut(); self.exec_fn_call( - mods, state, lib, fn_name, hash, args, is_ref, true, pub_only, pos, None, level, + mods, state, lib, fn_name, hash, args, is_ref, true, pos, None, level, ) }?; @@ -1011,7 +1024,6 @@ impl Engine { fn_name: &str, args_expr: impl AsRef<[Expr]>, mut hash_script: Option, - pub_only: bool, pos: Position, capture_scope: bool, level: usize, @@ -1026,7 +1038,7 @@ impl Engine { if name == KEYWORD_FN_PTR_CALL && args_expr.len() >= 1 - && !self.has_override(Some(mods), Some(state), lib, None, hash_script, pub_only) + && !self.has_override(Some(mods), Some(state), lib, None, hash_script) { let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; @@ -1056,7 +1068,7 @@ impl Engine { if name == KEYWORD_FN_PTR && args_expr.len() == 1 { let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); - if !self.has_override(Some(mods), Some(state), lib, hash_fn, hash_script, pub_only) { + if !self.has_override(Some(mods), Some(state), lib, hash_fn, hash_script) { // Fn - only in function call style return self .eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)? @@ -1108,7 +1120,7 @@ impl Engine { if name == KEYWORD_IS_DEF_VAR && args_expr.len() == 1 { let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); - if !self.has_override(Some(mods), Some(state), lib, hash_fn, hash_script, pub_only) { + if !self.has_override(Some(mods), Some(state), lib, hash_fn, hash_script) { let var_name = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; let var_name = var_name.as_str().map_err(|err| { @@ -1124,7 +1136,7 @@ impl Engine { let script_expr = &args_expr[0]; - if !self.has_override(Some(mods), Some(state), lib, hash_fn, hash_script, pub_only) { + if !self.has_override(Some(mods), Some(state), lib, hash_fn, hash_script) { let script_pos = script_expr.position(); // eval - only in function call style @@ -1231,7 +1243,6 @@ impl Engine { args, is_ref, false, - pub_only, pos, capture, level, diff --git a/src/fn_native.rs b/src/fn_native.rs index 7f458ad2..23ac2b50 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -184,7 +184,6 @@ impl<'e, 'n, 's, 'a, 'm> NativeCallContext<'e, 'n, 's, 'a, 'm> { &self, fn_name: &str, is_method: bool, - public_only: bool, args: &mut [&mut Dynamic], ) -> Result> { self.engine() @@ -197,7 +196,6 @@ impl<'e, 'n, 's, 'a, 'm> NativeCallContext<'e, 'n, 's, 'a, 'm> { args, is_method, is_method, - public_only, Position::NONE, None, 0, @@ -337,7 +335,7 @@ impl FnPtr { args.insert(0, obj); } - ctx.call_fn_dynamic_raw(self.fn_name(), is_method, true, args.as_mut()) + ctx.call_fn_dynamic_raw(self.fn_name(), is_method, args.as_mut()) } } diff --git a/src/optimize.rs b/src/optimize.rs index dda80da1..4106f3cc 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -673,7 +673,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); // Search for overloaded operators (can override built-in). - if !state.engine.has_override_by_name_and_arguments(Some(&state.mods), None, state.lib, x.name.as_ref(), arg_types.as_ref(), false) { + if !state.engine.has_override_by_name_and_arguments(Some(&state.mods), None, state.lib, x.name.as_ref(), arg_types.as_ref()) { if let Some(result) = run_builtin_binary_op(x.name.as_ref(), &arg_values[0], &arg_values[1]) .ok().flatten() .and_then(|result| map_dynamic_to_expr(result, *pos)) diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 0eca9695..dc66d207 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -649,7 +649,7 @@ mod array_functions { for (a1, a2) in array.iter_mut().zip(array2.iter_mut()) { let equals = ctx - .call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [a1, a2]) + .call_fn_dynamic_raw(OP_EQUALS, true, &mut [a1, a2]) .map(|v| v.as_bool().unwrap_or(false))?; if !equals { diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index 86185215..91baee2a 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -33,7 +33,7 @@ mod fn_ptr_functions { let hash_script = calc_script_fn_hash(empty(), fn_name, num_params as usize); ctx.engine() - .has_override(ctx.mods, None, ctx.lib, None, hash_script, true) + .has_override(ctx.mods, None, ctx.lib, None, hash_script) } } } diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index a9fbb79a..e1c0558b 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -61,7 +61,7 @@ mod map_functions { for (m1, v1) in map.iter_mut() { if let Some(v2) = map2.get_mut(m1) { let equals = ctx - .call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [v1, v2]) + .call_fn_dynamic_raw(OP_EQUALS, true, &mut [v1, v2]) .map(|v| v.as_bool().unwrap_or(false))?; if !equals { diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index f02e2464..3861a0a2 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -21,7 +21,7 @@ def_package!(crate:BasicStringPackage:"Basic string utilities, including printin #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[inline(always)] fn print_with_func(fn_name: &str, ctx: &NativeCallContext, value: &mut Dynamic) -> ImmutableString { - match ctx.call_fn_dynamic_raw(fn_name, true, false, &mut [value]) { + match ctx.call_fn_dynamic_raw(fn_name, true, &mut [value]) { Ok(result) if result.is::() => result.take_immutable_string().unwrap(), Ok(result) => ctx.engine().map_type_name(result.type_name()).into(), Err(_) => ctx.engine().map_type_name(value.type_name()).into(), diff --git a/tests/call_fn.rs b/tests/call_fn.rs index 552ae48f..f7e4fa67 100644 --- a/tests/call_fn.rs +++ b/tests/call_fn.rs @@ -105,11 +105,8 @@ fn test_call_fn_private() -> Result<(), Box> { let ast = engine.compile("private fn add(x, n, ) { x + n }")?; - assert!(matches!( - *(engine.call_fn(&mut scope, &ast, "add", (40 as INT, 2 as INT)) as Result>) - .expect_err("should error"), - EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "add" - )); + let r: INT = engine.call_fn(&mut scope, &ast, "add", (40 as INT, 2 as INT))?; + assert_eq!(r, 42); Ok(()) } @@ -165,8 +162,8 @@ fn test_fn_ptr_raw() -> Result<(), Box> { 42 ); - assert!(matches!( - *engine.eval::( + assert_eq!( + engine.eval::( r#" private fn foo(x) { this += x; } @@ -174,9 +171,9 @@ fn test_fn_ptr_raw() -> Result<(), Box> { x.bar(Fn("foo"), 1); x "# - ).expect_err("should error"), - EvalAltResult::ErrorFunctionNotFound(x, _) if x.starts_with("foo (") - )); + )?, + 42 + ); assert_eq!( engine.eval::( @@ -210,16 +207,13 @@ fn test_anonymous_fn() -> Result<(), Box> { assert_eq!(calc_func(42, "hello".to_string(), 9)?, 423); - let calc_func = Func::<(INT, INT, INT), INT>::create_from_script( + let calc_func = Func::<(INT, String, INT), INT>::create_from_script( Engine::new(), - "private fn calc(x, y, z) { (x + y) * z }", + "private fn calc(x, y, z) { (x + len(y)) * z }", "calc", )?; - assert!(matches!( - *calc_func(42, 123, 9).expect_err("should error"), - EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "calc" - )); + assert_eq!(calc_func(42, "hello".to_string(), 9)?, 423); Ok(()) } diff --git a/tests/string.rs b/tests/string.rs index a4f80c07..a5328fad 100644 --- a/tests/string.rs +++ b/tests/string.rs @@ -1,4 +1,4 @@ -use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString, RegisterFn, Scope, INT}; +use rhai::{Engine, EvalAltResult, ImmutableString, RegisterFn, Scope, INT}; #[test] fn test_string() -> Result<(), Box> {