Allow Rust functions in FnPtr::call_dynamic.

This commit is contained in:
Stephen Chung 2020-07-27 12:52:32 +08:00
parent 2dd4d9bcf9
commit a3a167424b
9 changed files with 248 additions and 135 deletions

View File

@ -125,8 +125,7 @@ engine.register_raw_fn(
let this_ptr = args.get_mut(0).unwrap(); // 1st argument - this pointer let this_ptr = args.get_mut(0).unwrap(); // 1st argument - this pointer
// Use 'FnPtr::call_dynamic' to call the function pointer. // Use 'FnPtr::call_dynamic' to call the function pointer.
// Beware, only script-defined functions are supported by 'FnPtr::call_dynamic'. // Beware, private script-defined functions will not be found.
// If it is a native Rust function, directly call it here in Rust instead!
fp.call_dynamic(engine, lib, Some(this_ptr), [value]) fp.call_dynamic(engine, lib, Some(this_ptr), [value])
}, },
); );

View File

@ -72,15 +72,17 @@ fn main() {
println!("=============="); println!("==============");
print_help(); print_help();
loop { 'main_loop: loop {
print!("rhai> "); print!("rhai> ");
stdout().flush().expect("couldn't flush stdout"); stdout().flush().expect("couldn't flush stdout");
input.clear(); input.clear();
loop { loop {
if let Err(err) = stdin().read_line(&mut input) { match stdin().read_line(&mut input) {
panic!("input error: {}", err); Ok(0) => break 'main_loop,
Ok(_) => (),
Err(err) => panic!("input error: {}", err),
} }
let line = input.as_str().trim_end(); let line = input.as_str().trim_end();

View File

@ -695,8 +695,8 @@ impl Engine {
let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val]; let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val];
self.exec_fn_call( self.exec_fn_call(
state, lib, FN_IDX_SET, true, 0, args, is_ref, true, None, state, lib, FN_IDX_SET, true, 0, args, is_ref, true, false,
level, None, level,
) )
.or_else(|err| match *err { .or_else(|err| match *err {
// If there is no index setter, no need to set it back because the indexer is read-only // If there is no index setter, no need to set it back because the indexer is read-only
@ -719,8 +719,8 @@ impl Engine {
let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val]; let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val];
self.exec_fn_call( self.exec_fn_call(
state, lib, FN_IDX_SET, true, 0, args, is_ref, true, None, state, lib, FN_IDX_SET, true, 0, args, is_ref, true, false,
level, None, level,
)?; )?;
} }
// Error // Error
@ -741,7 +741,12 @@ impl Engine {
match rhs { match rhs {
// xxx.fn_name(arg_expr_list) // xxx.fn_name(arg_expr_list)
Expr::FnCall(x) if x.1.is_none() => { Expr::FnCall(x) if x.1.is_none() => {
self.make_method_call(state, lib, target, rhs, idx_val, level) let ((name, native, pos), _, hash, _, def_val) = x.as_ref();
self.make_method_call(
state, lib, name, *hash, target, idx_val, *def_val, *native, false,
level,
)
.map_err(|err| err.new_position(*pos))
} }
// xxx.module::fn_name(...) - syntax error // xxx.module::fn_name(...) - syntax error
Expr::FnCall(_) => unreachable!(), Expr::FnCall(_) => unreachable!(),
@ -770,7 +775,8 @@ impl Engine {
let ((_, _, setter), pos) = x.as_ref(); let ((_, _, setter), pos) = x.as_ref();
let mut args = [target.as_mut(), _new_val.as_mut().unwrap()]; let mut args = [target.as_mut(), _new_val.as_mut().unwrap()];
self.exec_fn_call( self.exec_fn_call(
state, lib, setter, true, 0, &mut args, is_ref, true, None, level, state, lib, setter, true, 0, &mut args, is_ref, true, false, None,
level,
) )
.map(|(v, _)| (v, true)) .map(|(v, _)| (v, true))
.map_err(|err| err.new_position(*pos)) .map_err(|err| err.new_position(*pos))
@ -780,7 +786,8 @@ impl Engine {
let ((_, getter, _), pos) = x.as_ref(); let ((_, getter, _), pos) = x.as_ref();
let mut args = [target.as_mut()]; let mut args = [target.as_mut()];
self.exec_fn_call( self.exec_fn_call(
state, lib, getter, true, 0, &mut args, is_ref, true, None, level, state, lib, getter, true, 0, &mut args, is_ref, true, false, None,
level,
) )
.map(|(v, _)| (v, false)) .map(|(v, _)| (v, false))
.map_err(|err| err.new_position(*pos)) .map_err(|err| err.new_position(*pos))
@ -797,9 +804,13 @@ impl Engine {
} }
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
Expr::FnCall(x) if x.1.is_none() => { Expr::FnCall(x) if x.1.is_none() => {
let (val, _) = self.make_method_call( let ((name, native, pos), _, hash, _, def_val) = x.as_ref();
state, lib, target, sub_lhs, idx_val, level, let (val, _) = self
)?; .make_method_call(
state, lib, name, *hash, target, idx_val, *def_val,
*native, false, level,
)
.map_err(|err| err.new_position(*pos))?;
val.into() val.into()
} }
// {xxx:map}.module::fn_name(...) - syntax error // {xxx:map}.module::fn_name(...) - syntax error
@ -826,8 +837,8 @@ impl Engine {
let args = &mut arg_values[..1]; let args = &mut arg_values[..1];
let (mut val, updated) = self let (mut val, updated) = self
.exec_fn_call( .exec_fn_call(
state, lib, getter, true, 0, args, is_ref, true, None, state, lib, getter, true, 0, args, is_ref, true, false,
level, None, level,
) )
.map_err(|err| err.new_position(*pos))?; .map_err(|err| err.new_position(*pos))?;
@ -847,7 +858,7 @@ impl Engine {
arg_values[1] = val; arg_values[1] = val;
self.exec_fn_call( self.exec_fn_call(
state, lib, setter, true, 0, arg_values, is_ref, true, state, lib, setter, true, 0, arg_values, is_ref, true,
None, level, false, None, level,
) )
.or_else( .or_else(
|err| match *err { |err| match *err {
@ -864,9 +875,13 @@ impl Engine {
} }
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
Expr::FnCall(x) if x.1.is_none() => { Expr::FnCall(x) if x.1.is_none() => {
let (mut val, _) = self.make_method_call( let ((name, native, pos), _, hash, _, def_val) = x.as_ref();
state, lib, target, sub_lhs, idx_val, level, let (mut val, _) = self
)?; .make_method_call(
state, lib, name, *hash, target, idx_val, *def_val,
*native, false, level,
)
.map_err(|err| err.new_position(*pos))?;
let val = &mut val; let val = &mut val;
let target = &mut val.into(); let target = &mut val.into();
@ -1132,7 +1147,7 @@ impl Engine {
let type_name = val.type_name(); let type_name = val.type_name();
let args = &mut [val, &mut _idx]; let args = &mut [val, &mut _idx];
self.exec_fn_call( self.exec_fn_call(
state, _lib, FN_IDX_GET, true, 0, args, is_ref, true, None, _level, state, _lib, FN_IDX_GET, true, 0, args, is_ref, true, false, None, _level,
) )
.map(|(v, _)| v.into()) .map(|(v, _)| v.into())
.map_err(|err| match *err { .map_err(|err| match *err {
@ -1188,7 +1203,7 @@ impl Engine {
let (r, _) = self let (r, _) = self
.call_fn_raw( .call_fn_raw(
&mut scope, mods, state, lib, op, hashes, args, false, false, &mut scope, mods, state, lib, op, hashes, args, false, false, false,
def_value, level, def_value, level,
) )
.map_err(|err| err.new_position(rhs.position()))?; .map_err(|err| err.new_position(rhs.position()))?;
@ -1303,7 +1318,8 @@ impl Engine {
// Run function // Run function
let (value, _) = self let (value, _) = self
.exec_fn_call( .exec_fn_call(
state, lib, op, true, hash, args, false, false, None, level, state, lib, op, true, hash, args, false, false, false, None,
level,
) )
.map_err(|err| err.new_position(*op_pos))?; .map_err(|err| err.new_position(*op_pos))?;
// Set value to LHS // Set value to LHS
@ -1331,7 +1347,9 @@ impl Engine {
&mut self.eval_expr(scope, mods, state, lib, this_ptr, lhs_expr, level)?, &mut self.eval_expr(scope, mods, state, lib, this_ptr, lhs_expr, level)?,
&mut rhs_val, &mut rhs_val,
]; ];
self.exec_fn_call(state, lib, op, true, hash, args, false, false, None, level) self.exec_fn_call(
state, lib, op, true, hash, args, false, false, false, None, level,
)
.map(|(v, _)| v) .map(|(v, _)| v)
.map_err(|err| err.new_position(*op_pos))? .map_err(|err| err.new_position(*op_pos))?
}); });
@ -1403,7 +1421,7 @@ impl Engine {
let ((name, native, pos), _, hash, args_expr, def_val) = x.as_ref(); let ((name, native, pos), _, hash, args_expr, def_val) = x.as_ref();
self.make_function_call( self.make_function_call(
scope, mods, state, lib, this_ptr, name, args_expr, *def_val, *hash, *native, scope, mods, state, lib, this_ptr, name, args_expr, *def_val, *hash, *native,
level, false, level,
) )
.map_err(|err| err.new_position(*pos)) .map_err(|err| err.new_position(*pos))
} }
@ -1413,7 +1431,7 @@ impl Engine {
let ((name, _, pos), modules, hash, args_expr, def_val) = x.as_ref(); let ((name, _, pos), modules, hash, args_expr, def_val) = x.as_ref();
self.make_qualified_function_call( self.make_qualified_function_call(
scope, mods, state, lib, this_ptr, modules, name, args_expr, *def_val, *hash, scope, mods, state, lib, this_ptr, modules, name, args_expr, *def_val, *hash,
level, true, level,
) )
.map_err(|err| err.new_position(*pos)) .map_err(|err| err.new_position(*pos))
} }

View File

@ -8,10 +8,10 @@ use crate::engine::{
KEYWORD_TYPE_OF, KEYWORD_TYPE_OF,
}; };
use crate::error::ParseErrorType; use crate::error::ParseErrorType;
use crate::fn_native::{FnCallArgs, FnPtr}; use crate::fn_native::{CallableFunction, FnCallArgs, FnPtr};
use crate::module::{Module, ModuleRef}; use crate::module::{Module, ModuleRef};
use crate::optimize::OptimizationLevel; use crate::optimize::OptimizationLevel;
use crate::parser::{Expr, ImmutableString, AST, INT}; use crate::parser::{Expr, FnAccess, ImmutableString, AST, INT};
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::scope::Scope; use crate::scope::Scope;
use crate::token::Position; use crate::token::Position;
@ -105,6 +105,19 @@ fn restore_first_arg<'a>(old_this_ptr: Option<&'a mut Dynamic>, args: &mut FnCal
} }
} }
#[inline]
fn check_public_access(func: &CallableFunction) -> Option<&CallableFunction> {
if func.access() == FnAccess::Private {
None
} else {
Some(func)
}
}
#[inline(always)]
fn no_check_access(func: &CallableFunction) -> Option<&CallableFunction> {
Some(func)
}
impl Engine { impl Engine {
/// Universal method for calling functions either registered with the `Engine` or written in Rhai. /// Universal method for calling functions either registered with the `Engine` or written in Rhai.
/// Position in `EvalAltResult` is `None` and must be set afterwards. /// Position in `EvalAltResult` is `None` and must be set afterwards.
@ -125,6 +138,7 @@ impl Engine {
args: &mut FnCallArgs, args: &mut FnCallArgs,
is_ref: bool, is_ref: bool,
_is_method: bool, _is_method: bool,
pub_only: bool,
def_val: Option<bool>, def_val: Option<bool>,
_level: usize, _level: usize,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
@ -132,6 +146,12 @@ impl Engine {
let native_only = hash_script == 0; let native_only = hash_script == 0;
let check = if pub_only {
check_public_access
} else {
no_check_access
};
// Check for stack overflow // Check for stack overflow
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -150,14 +170,16 @@ impl Engine {
// Then search packages // Then search packages
// NOTE: We skip script functions for global_module and packages, and native functions for lib // NOTE: We skip script functions for global_module and packages, and native functions for lib
let func = if !native_only { let func = if !native_only {
lib.get_fn(hash_script) //.or_else(|| lib.get_fn(hash_fn)) lib.get_fn(hash_script).and_then(check) //.or_else(|| lib.get_fn(hash_fn)).and_then(check)
} else { } else {
None None
} }
//.or_else(|| self.global_module.get_fn(hash_script)) //.or_else(|| self.global_module.get_fn(hash_script)).and_then(check)
.or_else(|| self.global_module.get_fn(hash_fn)) .or_else(|| self.global_module.get_fn(hash_fn))
//.or_else(|| self.packages.get_fn(hash_script)) .and_then(check)
.or_else(|| self.packages.get_fn(hash_fn)); //.or_else(|| self.packages.get_fn(hash_script)).and_then(check)
.or_else(|| self.packages.get_fn(hash_fn))
.and_then(check);
if let Some(func) = func { if let Some(func) = func {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -378,18 +400,28 @@ impl Engine {
} }
// Has a system function an override? // Has a system function an override?
fn has_override(&self, lib: &Module, hash_fn: u64, hash_script: u64) -> bool { fn has_override(&self, lib: &Module, hash_fn: u64, hash_script: u64, pub_only: bool) -> bool {
let check = if pub_only {
check_public_access
} else {
no_check_access
};
// NOTE: We skip script functions for global_module and packages, and native functions for lib // NOTE: We skip script functions for global_module and packages, and native functions for lib
// First check script-defined functions // First check script-defined functions
lib.contains_fn(hash_script) lib.get_fn(hash_script)
//|| lib.contains_fn(hash_fn) .and_then(check)
//.or_else(|| lib.get_fn(hash_fn)).and_then(check)
// Then check registered functions // Then check registered functions
//|| self.global_module.contains_fn(hash_script) //.or_else(|| self.global_module.get_fn(hash_script)).and_then(check)
|| self.global_module.contains_fn(hash_fn) .or_else(|| self.global_module.get_fn(hash_fn))
.and_then(check)
// Then check packages // Then check packages
//|| self.packages.contains_fn(hash_script) //.or_else(|| self.packages.get_fn(hash_script)).and_then(check)
|| self.packages.contains_fn(hash_fn) .or_else(|| self.packages.get_fn(hash_fn))
.and_then(check)
.is_some()
} }
/// Perform an actual function call, taking care of special functions /// Perform an actual function call, taking care of special functions
@ -410,6 +442,7 @@ impl Engine {
args: &mut FnCallArgs, args: &mut FnCallArgs,
is_ref: bool, is_ref: bool,
is_method: bool, is_method: bool,
pub_only: bool,
def_val: Option<bool>, def_val: Option<bool>,
level: usize, level: usize,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
@ -420,7 +453,9 @@ impl Engine {
match fn_name { match fn_name {
// type_of // type_of
KEYWORD_TYPE_OF if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1) => { KEYWORD_TYPE_OF
if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1, pub_only) =>
{
Ok(( Ok((
self.map_type_name(args[0].type_name()).to_string().into(), self.map_type_name(args[0].type_name()).to_string().into(),
false, false,
@ -428,7 +463,9 @@ impl Engine {
} }
// Fn // Fn
KEYWORD_FN_PTR if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1) => { KEYWORD_FN_PTR
if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1, pub_only) =>
{
Err(Box::new(EvalAltResult::ErrorRuntime( Err(Box::new(EvalAltResult::ErrorRuntime(
"'Fn' should not be called in method style. Try Fn(...);".into(), "'Fn' should not be called in method style. Try Fn(...);".into(),
Position::none(), Position::none(),
@ -436,7 +473,9 @@ impl Engine {
} }
// eval - reaching this point it must be a method-style call // eval - reaching this point it must be a method-style call
KEYWORD_EVAL if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1) => { KEYWORD_EVAL
if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1, pub_only) =>
{
Err(Box::new(EvalAltResult::ErrorRuntime( Err(Box::new(EvalAltResult::ErrorRuntime(
"'eval' should not be called in method style. Try eval(...);".into(), "'eval' should not be called in method style. Try eval(...);".into(),
Position::none(), Position::none(),
@ -449,7 +488,7 @@ impl Engine {
let mut mods = Imports::new(); let mut mods = Imports::new();
self.call_fn_raw( self.call_fn_raw(
&mut scope, &mut mods, state, lib, fn_name, hashes, args, is_ref, is_method, &mut scope, &mut mods, state, lib, fn_name, hashes, args, is_ref, is_method,
def_val, level, pub_only, def_val, level,
) )
} }
} }
@ -499,28 +538,28 @@ impl Engine {
} }
/// Call a dot method. /// Call a dot method.
/// Position in `EvalAltResult` is `None` and must be set afterwards.
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
pub(crate) fn make_method_call( pub(crate) fn make_method_call(
&self, &self,
state: &mut State, state: &mut State,
lib: &Module, lib: &Module,
name: &str,
hash: u64,
target: &mut Target, target: &mut Target,
expr: &Expr,
idx_val: Dynamic, idx_val: Dynamic,
def_val: Option<bool>,
native: bool,
pub_only: bool,
level: usize, level: usize,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
let ((name, native, pos), _, hash, _, def_val) = match expr {
Expr::FnCall(x) => x.as_ref(),
_ => unreachable!(),
};
let is_ref = target.is_ref(); let is_ref = target.is_ref();
let is_value = target.is_value(); let is_value = target.is_value();
// Get a reference to the mutation target Dynamic // Get a reference to the mutation target Dynamic
let obj = target.as_mut(); let obj = target.as_mut();
let mut idx = idx_val.cast::<StaticVec<Dynamic>>(); let mut idx = idx_val.cast::<StaticVec<Dynamic>>();
let mut _fn_name = name.as_ref(); let mut _fn_name = name;
let (result, updated) = if _fn_name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() { let (result, updated) = if _fn_name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
// FnPtr call // FnPtr call
@ -539,7 +578,7 @@ impl Engine {
// Map it to name(args) in function-call style // Map it to name(args) in function-call style
self.exec_fn_call( self.exec_fn_call(
state, lib, fn_name, *native, hash, args, false, false, *def_val, level, state, lib, fn_name, native, hash, args, false, false, pub_only, def_val, level,
) )
} else if _fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::<FnPtr>() { } else if _fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::<FnPtr>() {
// FnPtr call on object // FnPtr call on object
@ -558,7 +597,7 @@ impl Engine {
// Map it to name(args) in function-call style // Map it to name(args) in function-call style
self.exec_fn_call( self.exec_fn_call(
state, lib, &fn_name, *native, hash, args, is_ref, true, *def_val, level, state, lib, &fn_name, native, hash, args, is_ref, true, pub_only, def_val, level,
) )
} else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() { } else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
// Curry call // Curry call
@ -579,7 +618,7 @@ impl Engine {
} else { } else {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
let redirected; let redirected;
let mut _hash = *hash; let mut _hash = hash;
// Check if it is a map method call in OOP style // Check if it is a map method call in OOP style
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -600,10 +639,9 @@ impl Engine {
let args = arg_values.as_mut(); let args = arg_values.as_mut();
self.exec_fn_call( self.exec_fn_call(
state, lib, _fn_name, *native, _hash, args, is_ref, true, *def_val, level, state, lib, _fn_name, native, _hash, args, is_ref, true, pub_only, def_val, level,
) )
} }?;
.map_err(|err| err.new_position(*pos))?;
// Feed the changed temp value back // Feed the changed temp value back
if updated && !is_ref && !is_value { if updated && !is_ref && !is_value {
@ -628,13 +666,14 @@ impl Engine {
def_val: Option<bool>, def_val: Option<bool>,
mut hash: u64, mut hash: u64,
native: bool, native: bool,
pub_only: bool,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
// Handle Fn() // Handle Fn()
if name == KEYWORD_FN_PTR && args_expr.len() == 1 { if name == KEYWORD_FN_PTR && args_expr.len() == 1 {
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>())); let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
if !self.has_override(lib, hash_fn, hash) { if !self.has_override(lib, hash_fn, hash, pub_only) {
// Fn - only in function call style // Fn - only in function call style
let expr = args_expr.get(0).unwrap(); let expr = args_expr.get(0).unwrap();
let arg_value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; let arg_value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
@ -685,7 +724,7 @@ impl Engine {
if name == KEYWORD_EVAL && args_expr.len() == 1 { if name == KEYWORD_EVAL && args_expr.len() == 1 {
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>())); let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
if !self.has_override(lib, hash_fn, hash) { if !self.has_override(lib, hash_fn, hash, pub_only) {
// eval - only in function call style // eval - only in function call style
let prev_len = scope.len(); let prev_len = scope.len();
let expr = args_expr.get(0).unwrap(); let expr = args_expr.get(0).unwrap();
@ -710,7 +749,10 @@ impl Engine {
let mut curry: StaticVec<_> = Default::default(); let mut curry: StaticVec<_> = Default::default();
let mut name = name; let mut name = name;
if name == KEYWORD_FN_PTR_CALL && args_expr.len() >= 1 && !self.has_override(lib, 0, hash) { if name == KEYWORD_FN_PTR_CALL
&& args_expr.len() >= 1
&& !self.has_override(lib, 0, hash, pub_only)
{
let expr = args_expr.get(0).unwrap(); let expr = args_expr.get(0).unwrap();
let fn_name = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; let fn_name = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
@ -779,7 +821,7 @@ impl Engine {
let args = args.as_mut(); let args = args.as_mut();
self.exec_fn_call( self.exec_fn_call(
state, lib, name, native, hash, args, is_ref, false, def_val, level, state, lib, name, native, hash, args, is_ref, false, pub_only, def_val, level,
) )
.map(|(v, _)| v) .map(|(v, _)| v)
} }
@ -798,6 +840,7 @@ impl Engine {
args_expr: &[Expr], args_expr: &[Expr],
def_val: Option<bool>, def_val: Option<bool>,
hash_script: u64, hash_script: u64,
pub_only: bool,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let modules = modules.as_ref().unwrap(); let modules = modules.as_ref().unwrap();
@ -844,9 +887,15 @@ impl Engine {
let module = search_imports(mods, state, modules)?; let module = search_imports(mods, state, modules)?;
// First search in script-defined functions (can override built-in) // First search in script-defined functions (can override built-in)
let func = match module.get_qualified_fn(hash_script) { let check = if pub_only {
Err(err) if matches!(*err, EvalAltResult::ErrorFunctionNotFound(_, _)) => { check_public_access
} else {
no_check_access
};
let func = match module.get_qualified_fn(hash_script).and_then(check) {
// Then search in Rust functions // Then search in Rust functions
None => {
self.inc_operations(state)?; self.inc_operations(state)?;
// Qualified Rust functions are indexed in two steps: // Qualified Rust functions are indexed in two steps:
@ -858,14 +907,14 @@ impl Engine {
// 3) The final hash is the XOR of the two hashes. // 3) The final hash is the XOR of the two hashes.
let hash_qualified_fn = hash_script ^ hash_fn_args; let hash_qualified_fn = hash_script ^ hash_fn_args;
module.get_qualified_fn(hash_qualified_fn) module.get_qualified_fn(hash_qualified_fn).and_then(check)
} }
r => r, r => r,
}; };
match func { match func {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Ok(f) if f.is_script() => { Some(f) if f.is_script() => {
let args = args.as_mut(); let args = args.as_mut();
let fn_def = f.get_fn_def(); let fn_def = f.get_fn_def();
let mut scope = Scope::new(); let mut scope = Scope::new();
@ -874,13 +923,9 @@ impl Engine {
&mut scope, &mut mods, state, lib, &mut None, name, fn_def, args, level, &mut scope, &mut mods, state, lib, &mut None, name, fn_def, args, level,
) )
} }
Ok(f) => f.get_native_fn()(self, lib, args.as_mut()), Some(f) => f.get_native_fn()(self, lib, args.as_mut()),
Err(err) => match *err { None if def_val.is_some() => Ok(def_val.unwrap().into()),
EvalAltResult::ErrorFunctionNotFound(_, _) if def_val.is_some() => { None => Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
Ok(def_val.unwrap().into())
}
EvalAltResult::ErrorFunctionNotFound(_, pos) => {
Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
format!( format!(
"{}{} ({})", "{}{} ({})",
modules, modules,
@ -894,11 +939,8 @@ impl Engine {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", ") .join(", ")
), ),
pos, Position::none(),
))) ))),
}
_ => Err(err),
},
} }
} }
} }

View File

@ -1,16 +1,18 @@
//! Module defining interfaces to native-Rust functions. //! Module defining interfaces to native-Rust functions.
use crate::any::Dynamic; use crate::any::Dynamic;
use crate::calc_fn_hash;
use crate::engine::Engine; use crate::engine::Engine;
use crate::module::Module; use crate::module::Module;
use crate::parser::FnAccess;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::token::{is_valid_identifier, Position}; use crate::token::{is_valid_identifier, Position};
use crate::utils::ImmutableString; use crate::utils::ImmutableString;
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
use crate::{module::FuncReturn, parser::ScriptFnDef, scope::Scope, utils::StaticVec}; use crate::{module::FuncReturn, parser::ScriptFnDef, utils::StaticVec};
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, string::String, vec::Vec}; use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, iter::empty, string::String, vec::Vec};
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
use crate::stdlib::mem; use crate::stdlib::mem;
@ -89,9 +91,7 @@ impl FnPtr {
/// Call the function pointer with curried arguments (if any). /// Call the function pointer with curried arguments (if any).
/// ///
/// The function must be a script-defined function. It cannot be a Rust function. /// If this function is a script-defined function, it must not be marked private.
///
/// To call a Rust function, just call it directly in Rust!
/// ///
/// ## WARNING /// ## WARNING
/// ///
@ -107,14 +107,39 @@ impl FnPtr {
this_ptr: Option<&mut Dynamic>, this_ptr: Option<&mut Dynamic>,
mut arg_values: impl AsMut<[Dynamic]>, mut arg_values: impl AsMut<[Dynamic]>,
) -> FuncReturn<Dynamic> { ) -> FuncReturn<Dynamic> {
let args = self let mut args_data = self
.1 .1
.iter() .iter()
.cloned() .cloned()
.chain(arg_values.as_mut().iter_mut().map(|v| mem::take(v))) .chain(arg_values.as_mut().iter_mut().map(|v| mem::take(v)))
.collect::<StaticVec<_>>(); .collect::<StaticVec<_>>();
engine.call_fn_dynamic(&mut Scope::new(), lib, self.0.as_str(), this_ptr, args) let has_this = this_ptr.is_some();
let args_len = args_data.len();
let mut args = args_data.iter_mut().collect::<StaticVec<_>>();
if let Some(obj) = this_ptr {
args.insert(0, obj);
}
let fn_name = self.0.as_str();
let hash_script = calc_fn_hash(empty(), fn_name, args_len, empty());
engine
.exec_fn_call(
&mut Default::default(),
lib.as_ref(),
fn_name,
false,
hash_script,
args.as_mut(),
has_this,
has_this,
true,
None,
0,
)
.map(|(v, _)| v)
} }
} }
@ -255,6 +280,15 @@ impl CallableFunction {
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false, Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false,
} }
} }
/// Get the access mode.
pub fn access(&self) -> FnAccess {
match self {
CallableFunction::Pure(_)
| CallableFunction::Method(_)
| CallableFunction::Iterator(_) => FnAccess::Public,
CallableFunction::Script(f) => f.access,
}
}
/// Get a reference to a native Rust function. /// Get a reference to a native Rust function.
/// ///
/// # Panics /// # Panics

View File

@ -912,16 +912,8 @@ impl Module {
/// ///
/// The `u64` hash is calculated by the function `crate::calc_fn_hash` and must match /// The `u64` hash is calculated by the function `crate::calc_fn_hash` and must match
/// the hash calculated by `index_all_sub_modules`. /// the hash calculated by `index_all_sub_modules`.
pub(crate) fn get_qualified_fn( pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&Func> {
&self, self.all_functions.get(&hash_qualified_fn)
hash_qualified_fn: u64,
) -> Result<&Func, Box<EvalAltResult>> {
self.all_functions.get(&hash_qualified_fn).ok_or_else(|| {
Box::new(EvalAltResult::ErrorFunctionNotFound(
String::new(),
Position::none(),
))
})
} }
/// Merge another module into this module. /// Merge another module into this module.

View File

@ -141,6 +141,7 @@ fn call_fn_with_constant_arguments(
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(), arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
false, false,
false, false,
true,
None, None,
0, 0,
) )

View File

@ -2674,10 +2674,9 @@ fn parse_block(
// Parse statements inside the block // Parse statements inside the block
settings.is_global = false; settings.is_global = false;
let stmt = if let Some(s) = parse_stmt(input, state, lib, settings.level_up())? { let stmt = match parse_stmt(input, state, lib, settings.level_up())? {
s Some(s) => s,
} else { None => continue,
continue;
}; };
// See if it needs a terminating semicolon // See if it needs a terminating semicolon
@ -3137,10 +3136,9 @@ impl Engine {
pos: Position::none(), pos: Position::none(),
}; };
let stmt = if let Some(s) = parse_stmt(input, &mut state, &mut functions, settings)? { let stmt = match parse_stmt(input, &mut state, &mut functions, settings)? {
s Some(s) => s,
} else { None => continue,
continue;
}; };
let need_semicolon = !stmt.is_self_terminated(); let need_semicolon = !stmt.is_self_terminated();

View File

@ -1,6 +1,7 @@
#![cfg(not(feature = "no_function"))] #![cfg(not(feature = "no_function"))]
use rhai::{ use rhai::{
Dynamic, Engine, EvalAltResult, FnPtr, Func, Module, ParseError, ParseErrorType, Scope, INT, Dynamic, Engine, EvalAltResult, FnPtr, Func, Module, ParseError, ParseErrorType, RegisterFn,
Scope, INT,
}; };
use std::any::TypeId; use std::any::TypeId;
@ -119,7 +120,9 @@ fn test_anonymous_fn() -> Result<(), Box<EvalAltResult>> {
fn test_fn_ptr_raw() -> Result<(), Box<EvalAltResult>> { fn test_fn_ptr_raw() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.register_raw_fn( engine
.register_fn("mul", |x: &mut INT, y: INT| *x *= y)
.register_raw_fn(
"bar", "bar",
&[ &[
TypeId::of::<INT>(), TypeId::of::<INT>(),
@ -148,6 +151,30 @@ fn test_fn_ptr_raw() -> Result<(), Box<EvalAltResult>> {
42 42
); );
assert!(matches!(
*engine.eval::<INT>(
r#"
private fn foo(x) { this += x; }
let x = 41;
x.bar(Fn("foo"), 1);
x
"#
).expect_err("should error"),
EvalAltResult::ErrorFunctionNotFound(x, _) if x.starts_with("foo (")
));
assert_eq!(
engine.eval::<INT>(
r#"
let x = 21;
x.bar(Fn("mul"), 2);
x
"#
)?,
42
);
Ok(()) Ok(())
} }