Allow Rust functions in FnPtr::call_dynamic.
This commit is contained in:
parent
2dd4d9bcf9
commit
a3a167424b
@ -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])
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -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();
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
142
src/fn_call.rs
142
src/fn_call.rs
@ -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),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
|
@ -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();
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user