rhai/src/func/call.rs

1446 lines
54 KiB
Rust
Raw Normal View History

2020-11-20 09:52:28 +01:00
//! Implement function-calling mechanism for [`Engine`].
2017-12-20 22:16:53 +01:00
2021-11-13 15:36:23 +01:00
use super::builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn};
use super::native::{CallableFunction, FnAny};
2021-04-20 16:26:08 +02:00
use crate::ast::FnCallHashes;
use crate::engine::{
2021-07-04 10:40:15 +02:00
EvalState, FnResolutionCacheEntry, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR,
2021-03-13 11:46:08 +01:00
KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
2021-02-24 15:40:18 +01:00
MAX_DYNAMIC_PARAMETERS,
};
2020-11-16 16:10:14 +01:00
use crate::module::NamespaceRef;
2021-11-13 15:36:23 +01:00
use crate::tokenizer::Token;
2021-03-01 09:53:03 +01:00
use crate::{
ast::{Expr, Stmt},
2021-11-13 15:36:23 +01:00
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, EvalAltResult, FnPtr,
Identifier, ImmutableString, Module, ParseErrorType, Position, RhaiResult, Scope, StaticVec,
2020-11-16 16:10:14 +01:00
};
2021-04-17 09:15:54 +02:00
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
any::{type_name, TypeId},
convert::TryFrom,
mem,
};
2020-11-16 16:10:14 +01:00
#[cfg(not(feature = "no_object"))]
use crate::Map;
2021-06-17 03:50:32 +02:00
/// Arguments to a function call, which is a list of [`&mut Dynamic`][Dynamic].
pub type FnCallArgs<'a> = [&'a mut Dynamic];
2020-07-31 06:11:16 +02:00
/// A type that temporarily stores a mutable reference to a `Dynamic`,
/// replacing it with a cloned copy.
#[derive(Debug)]
2020-07-31 06:11:16 +02:00
struct ArgBackup<'a> {
orig_mut: Option<&'a mut Dynamic>,
value_copy: Dynamic,
}
impl<'a> ArgBackup<'a> {
/// Create a new `ArgBackup`.
pub fn new() -> Self {
Self {
orig_mut: None,
value_copy: Dynamic::UNIT,
}
}
2020-07-31 06:11:16 +02:00
/// This function replaces the first argument of a method call with a clone copy.
/// This is to prevent a pure function unintentionally consuming the first argument.
///
/// `restore_first_arg` must be called before the end of the scope to prevent the shorter lifetime from leaking.
///
/// # Safety
///
/// This method blindly casts a reference to another lifetime, which saves allocation and string cloning.
///
2021-05-22 13:14:24 +02:00
/// As long as `restore_first_arg` is called before the end of the scope, the shorter lifetime
/// will not leak.
///
/// # Panics
///
/// Panics when `args` is empty.
#[inline]
2021-03-02 07:44:21 +01:00
fn change_first_arg_to_copy(&mut self, args: &mut FnCallArgs<'a>) {
2020-07-31 06:11:16 +02:00
// Clone the original value.
self.value_copy = args[0].clone();
// Replace the first reference with a reference to the clone, force-casting the lifetime.
// Must remember to restore it later with `restore_first_arg`.
//
// # Safety
//
// Blindly casting a reference to another lifetime saves allocation and string cloning,
// but must be used with the utmost care.
//
// We can do this here because, before the end of this scope, we'd restore the original reference
// via `restore_first_arg`. Therefore this shorter lifetime does not leak.
2021-05-25 04:54:48 +02:00
self.orig_mut = Some(mem::replace(&mut args[0], unsafe {
2020-07-31 06:11:16 +02:00
mem::transmute(&mut self.value_copy)
}));
}
2020-07-31 06:11:16 +02:00
/// This function restores the first argument that was replaced by `change_first_arg_to_copy`.
///
/// # Safety
///
/// If `change_first_arg_to_copy` has been called, this function **MUST** be called _BEFORE_ exiting
/// the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak.
2020-12-29 03:41:20 +01:00
#[inline(always)]
2021-03-02 07:44:21 +01:00
fn restore_first_arg(mut self, args: &mut FnCallArgs<'a>) {
2021-07-24 08:11:16 +02:00
if let Some(p) = self.orig_mut.take() {
args[0] = p;
}
2020-07-31 06:11:16 +02:00
}
2017-12-20 22:16:53 +01:00
}
2020-07-31 06:11:16 +02:00
impl Drop for ArgBackup<'_> {
#[inline]
2020-07-31 06:11:16 +02:00
fn drop(&mut self) {
// Panic if the shorter lifetime leaks.
assert!(
self.orig_mut.is_none(),
"ArgBackup::restore_first_arg has not been called prior to existing this scope"
2020-07-31 06:11:16 +02:00
);
}
2017-12-20 22:16:53 +01:00
}
2021-03-29 07:07:10 +02:00
#[cfg(not(feature = "no_closure"))]
#[inline]
2020-08-02 07:33:51 +02:00
pub fn ensure_no_data_race(
fn_name: &str,
args: &FnCallArgs,
2021-06-17 03:50:32 +02:00
is_method_call: bool,
2020-08-02 07:33:51 +02:00
) -> Result<(), Box<EvalAltResult>> {
2021-03-20 16:57:43 +01:00
if let Some((n, _)) = args
.iter()
.enumerate()
2021-06-17 03:50:32 +02:00
.skip(if is_method_call { 1 } else { 0 })
2021-03-20 16:57:43 +01:00
.find(|(_, a)| a.is_locked())
{
return Err(EvalAltResult::ErrorDataRace(
2021-03-20 16:57:43 +01:00
format!("argument #{} of function '{}'", n + 1, fn_name),
Position::NONE,
)
.into());
2020-08-02 07:33:51 +02:00
}
Ok(())
}
impl Engine {
2021-02-27 08:07:16 +01:00
/// Generate the signature for a function call.
2021-03-17 02:58:08 +01:00
#[inline]
2021-06-12 16:47:43 +02:00
#[must_use]
2021-02-27 08:07:16 +01:00
fn gen_call_signature(
&self,
namespace: Option<&NamespaceRef>,
fn_name: &str,
args: &[&mut Dynamic],
) -> String {
format!(
2021-10-27 17:30:25 +02:00
"{}{}{} ({})",
2021-02-27 08:07:16 +01:00
namespace.map_or(String::new(), |ns| ns.to_string()),
2021-10-27 17:30:25 +02:00
if namespace.is_some() {
Token::DoubleColon.literal_syntax()
} else {
""
},
2021-02-27 08:07:16 +01:00
fn_name,
args.iter()
.map(|a| if a.is::<ImmutableString>() {
"&str | ImmutableString | String"
} else {
2021-06-21 13:12:28 +02:00
self.map_type_name(a.type_name())
2021-02-27 08:07:16 +01:00
})
2021-06-06 06:17:04 +02:00
.collect::<StaticVec<_>>()
2021-02-27 08:07:16 +01:00
.join(", ")
)
}
2021-03-01 09:53:03 +01:00
/// Resolve a function call.
///
2021-03-01 09:53:03 +01:00
/// Search order:
/// 1) AST - script functions in the AST
/// 2) Global namespace - functions registered via Engine::register_XXX
/// 3) Global modules - packages
/// 4) Imported modules - functions marked with global namespace
/// 5) Global sub-modules - functions marked with global namespace
2021-06-12 16:47:43 +02:00
#[must_use]
fn resolve_fn<'s>(
&self,
2021-03-01 10:17:13 +01:00
mods: &Imports,
2021-07-04 10:40:15 +02:00
state: &'s mut EvalState,
2020-10-20 04:54:32 +02:00
lib: &[&Module],
fn_name: &str,
2021-03-08 08:30:32 +01:00
hash_script: u64,
args: Option<&mut FnCallArgs>,
2021-03-01 09:53:03 +01:00
allow_dynamic: bool,
is_op_assignment: bool,
) -> Option<&'s FnResolutionCacheEntry> {
2021-05-25 04:54:48 +02:00
let mut hash = args.as_ref().map_or(hash_script, |args| {
2021-06-21 13:12:28 +02:00
combine_hashes(
hash_script,
calc_fn_params_hash(args.iter().map(|a| a.type_id())),
)
2021-05-25 04:54:48 +02:00
});
2021-03-08 08:30:32 +01:00
let result = state
.fn_resolution_cache_mut()
2021-03-01 09:53:03 +01:00
.entry(hash)
.or_insert_with(|| {
2021-03-17 02:58:08 +01:00
let num_args = args.as_ref().map_or(0, |a| a.len());
2021-03-01 09:53:03 +01:00
let max_bitmask = if !allow_dynamic {
0
} else {
2021-03-08 08:30:32 +01:00
1usize << num_args.min(MAX_DYNAMIC_PARAMETERS)
2021-03-01 09:53:03 +01:00
};
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
loop {
2021-08-26 17:58:41 +02:00
let func = lib
.iter()
.find_map(|m| {
m.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry {
func,
source: m.id_raw().cloned(),
})
})
.or_else(|| {
self.global_modules.iter().find_map(|m| {
m.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry {
func,
source: m.id_raw().cloned(),
})
})
})
.or_else(|| {
mods.get_fn(hash)
.map(|(func, source)| FnResolutionCacheEntry {
func: func.clone(),
source: source.cloned(),
})
})
.or_else(|| {
self.global_sub_modules.values().find_map(|m| {
m.get_qualified_fn(hash).cloned().map(|func| {
FnResolutionCacheEntry {
func,
source: m.id_raw().cloned(),
}
})
})
});
match func {
// Specific version found
2021-05-03 07:45:41 +02:00
Some(f) => return Some(Box::new(f)),
2021-02-24 15:40:18 +01:00
// Stop when all permutations are exhausted
None if bitmask >= max_bitmask => {
2021-03-17 02:58:08 +01:00
if num_args != 2 {
return None;
}
return args.and_then(|args| {
2021-03-08 08:30:32 +01:00
if !is_op_assignment {
2021-03-17 02:58:08 +01:00
get_builtin_binary_op_fn(fn_name, &args[0], &args[1]).map(|f| {
2021-08-26 17:58:41 +02:00
FnResolutionCacheEntry {
func: CallableFunction::from_method(
Box::new(f) as Box<FnAny>
),
source: None,
}
2021-03-17 02:58:08 +01:00
})
} else {
let (first_arg, rest_args) =
2021-11-13 05:23:35 +01:00
args.split_first().expect("two arguments");
2021-08-26 17:58:41 +02:00
get_builtin_op_assignment_fn(fn_name, *first_arg, rest_args[0])
.map(|f| FnResolutionCacheEntry {
2021-08-26 17:58:41 +02:00
func: CallableFunction::from_method(
Box::new(f) as Box<FnAny>
),
source: None,
})
}
2021-05-03 07:45:41 +02:00
.map(Box::new)
2021-03-17 02:58:08 +01:00
});
}
// Try all permutations with `Dynamic` wildcards
2021-03-01 09:53:03 +01:00
None => {
2021-03-08 08:30:32 +01:00
let hash_params = calc_fn_params_hash(
2021-08-26 17:58:41 +02:00
args.as_ref()
2021-11-13 05:23:35 +01:00
.expect("no permutations")
2021-08-26 17:58:41 +02:00
.iter()
.enumerate()
.map(|(i, a)| {
let mask = 1usize << (num_args - i - 1);
if bitmask & mask != 0 {
// Replace with `Dynamic`
TypeId::of::<Dynamic>()
} else {
a.type_id()
}
}),
2021-03-08 08:30:32 +01:00
);
hash = combine_hashes(hash_script, hash_params);
bitmask += 1;
}
}
}
});
result.as_ref().map(Box::as_ref)
2021-03-01 09:53:03 +01:00
}
/// Call a native Rust function registered with the [`Engine`].
///
/// # WARNING
///
/// Function call arguments be _consumed_ when the function requires them to be passed by value.
/// All function arguments not in the first position are always passed by value and thus consumed.
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
pub(crate) fn call_native_fn(
&self,
mods: &mut Imports,
2021-07-04 10:40:15 +02:00
state: &mut EvalState,
2021-03-01 09:53:03 +01:00
lib: &[&Module],
2021-06-12 16:47:43 +02:00
name: &str,
hash: u64,
2021-03-01 09:53:03 +01:00
args: &mut FnCallArgs,
2021-06-17 03:50:32 +02:00
is_method_call: bool,
2021-06-12 16:47:43 +02:00
is_op_assign: bool,
2021-03-01 09:53:03 +01:00
pos: Position,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
2021-04-25 09:27:58 +02:00
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut mods.num_operations, pos)?;
2021-03-01 09:53:03 +01:00
let parent_source = mods.source.clone();
2021-03-01 09:53:03 +01:00
// Check if function access already in the cache
2021-06-12 16:47:43 +02:00
let func = self.resolve_fn(mods, state, lib, name, hash, Some(args), true, is_op_assign);
2020-12-30 14:12:51 +01:00
if let Some(FnResolutionCacheEntry { func, source }) = func {
2020-07-31 06:11:16 +02:00
assert!(func.is_native());
// Calling pure function but the first argument is a reference?
2021-03-02 07:44:21 +01:00
let mut backup: Option<ArgBackup> = None;
2021-06-17 03:50:32 +02:00
if is_method_call && func.is_pure() && !args.is_empty() {
backup = Some(ArgBackup::new());
2021-08-26 17:58:41 +02:00
backup
.as_mut()
2021-11-13 05:23:35 +01:00
.expect("`Some`")
2021-08-26 17:58:41 +02:00
.change_first_arg_to_copy(args);
2021-03-02 07:44:21 +01:00
}
// Run external function
2021-03-13 11:46:08 +01:00
let source = source
.as_ref()
.or_else(|| parent_source.as_ref())
2021-03-13 11:46:08 +01:00
.map(|s| s.as_str());
2021-06-11 13:59:50 +02:00
let context = (self, name, source, &*mods, lib, pos).into();
2020-08-02 12:53:25 +02:00
let result = if func.is_plugin_fn() {
2020-12-21 16:12:45 +01:00
func.get_plugin_fn()
2021-08-26 17:58:41 +02:00
.expect("plugin function")
2021-11-05 12:35:33 +01:00
.call(context, args)
2020-08-02 12:53:25 +02:00
} else {
func.get_native_fn().expect("native function")(context, args)
2020-08-02 12:53:25 +02:00
};
// Restore the original reference
2021-07-24 08:11:16 +02:00
if let Some(bk) = backup {
bk.restore_first_arg(args)
}
2020-07-31 06:11:16 +02:00
let result = result.map_err(|err| err.fill_position(pos))?;
// See if the function match print/debug (which requires special processing)
2021-06-12 16:47:43 +02:00
return Ok(match name {
2020-12-22 08:27:27 +01:00
KEYWORD_PRINT => {
2021-06-29 11:42:03 +02:00
if let Some(ref print) = self.print {
let text = result.into_immutable_string().map_err(|typ| {
2021-06-29 11:42:03 +02:00
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<ImmutableString>()).into(),
typ.into(),
pos,
)
})?;
(print(&text).into(), false)
} else {
(Dynamic::UNIT, false)
}
2020-12-22 08:27:27 +01:00
}
2020-12-21 15:04:46 +01:00
KEYWORD_DEBUG => {
2021-06-29 11:42:03 +02:00
if let Some(ref debug) = self.debug {
let text = result.into_immutable_string().map_err(|typ| {
2021-06-29 11:42:03 +02:00
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<ImmutableString>()).into(),
typ.into(),
pos,
)
})?;
let source = mods.source.as_ref().map(|s| s.as_str());
2021-06-29 11:42:03 +02:00
(debug(&text, source, pos).into(), false)
} else {
(Dynamic::UNIT, false)
}
2020-12-21 15:04:46 +01:00
}
_ => (result, func.is_method()),
});
}
2021-06-12 16:47:43 +02:00
match name {
2021-03-16 11:16:40 +01:00
// index getter function not found?
2021-05-18 15:38:09 +02:00
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
2021-03-16 11:16:40 +01:00
crate::engine::FN_IDX_GET => {
assert!(args.len() == 2);
Err(EvalAltResult::ErrorIndexingType(
format!(
"{} [{}]",
self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name())
),
2021-03-16 11:16:40 +01:00
pos,
)
.into())
2021-03-16 11:16:40 +01:00
}
2021-03-16 11:16:40 +01:00
// index setter function not found?
2021-05-18 15:38:09 +02:00
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
2021-03-16 11:16:40 +01:00
crate::engine::FN_IDX_SET => {
assert!(args.len() == 3);
Err(EvalAltResult::ErrorIndexingType(
format!(
"{} [{}] = {}",
self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name()),
self.map_type_name(args[2].type_name())
),
2021-03-16 11:16:40 +01:00
pos,
)
.into())
2021-03-16 11:16:40 +01:00
}
// Getter function not found?
#[cfg(not(feature = "no_object"))]
2021-06-12 16:47:43 +02:00
_ if name.starts_with(crate::engine::FN_GET) => {
2021-03-16 11:16:40 +01:00
assert!(args.len() == 1);
Err(EvalAltResult::ErrorDotExpr(
2021-03-16 11:16:40 +01:00
format!(
"Unknown property '{}' - a getter is not registered for type '{}'",
2021-06-12 16:47:43 +02:00
&name[crate::engine::FN_GET.len()..],
2021-03-16 11:16:40 +01:00
self.map_type_name(args[0].type_name())
),
pos,
)
.into())
2021-03-16 11:16:40 +01:00
}
2021-03-16 11:16:40 +01:00
// Setter function not found?
#[cfg(not(feature = "no_object"))]
2021-06-12 16:47:43 +02:00
_ if name.starts_with(crate::engine::FN_SET) => {
2021-03-16 11:16:40 +01:00
assert!(args.len() == 2);
Err(EvalAltResult::ErrorDotExpr(
2021-03-16 11:16:40 +01:00
format!(
"No writable property '{}' - a setter is not registered for type '{}' to handle '{}'",
2021-06-12 16:47:43 +02:00
&name[crate::engine::FN_SET.len()..],
2021-03-16 11:16:40 +01:00
self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name()),
),
pos,
)
.into())
2021-03-16 11:16:40 +01:00
}
// Raise error
_ => Err(EvalAltResult::ErrorFunctionNotFound(
self.gen_call_signature(None, name, args),
pos,
)
.into()),
}
}
/// Call a script-defined function.
///
2021-01-02 16:30:10 +01:00
/// # WARNING
///
/// Function call arguments may be _consumed_ when the function requires them to be passed by value.
/// All function arguments not in the first position are always passed by value and thus consumed.
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
2020-07-26 07:51:09 +02:00
#[cfg(not(feature = "no_function"))]
pub(crate) fn call_script_fn(
&self,
scope: &mut Scope,
mods: &mut Imports,
2021-07-04 10:40:15 +02:00
state: &mut EvalState,
2020-10-20 04:54:32 +02:00
lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>,
2020-11-16 09:28:04 +01:00
fn_def: &crate::ast::ScriptFnDef,
args: &mut FnCallArgs,
2020-12-12 04:15:09 +01:00
pos: Position,
level: usize,
2021-03-02 08:02:28 +01:00
) -> RhaiResult {
2021-10-19 14:16:36 +02:00
#[inline(never)]
fn make_error(
2021-04-20 13:19:35 +02:00
name: String,
fn_def: &crate::ast::ScriptFnDef,
mods: &Imports,
err: Box<EvalAltResult>,
pos: Position,
2021-03-02 08:02:28 +01:00
) -> RhaiResult {
Err(EvalAltResult::ErrorInFunctionCall(
name,
fn_def
.lib
.as_ref()
2021-03-24 06:17:52 +01:00
.and_then(|m| m.id().map(|id| id.to_string()))
.or_else(|| mods.source.as_ref().map(|s| s.to_string()))
2021-03-24 06:17:52 +01:00
.unwrap_or_default(),
err,
pos,
2021-03-16 11:16:40 +01:00
)
.into())
}
2021-04-25 09:27:58 +02:00
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut mods.num_operations, pos)?;
2020-07-31 06:11:16 +02:00
2021-03-10 15:12:48 +01:00
if fn_def.body.is_empty() {
return Ok(Dynamic::UNIT);
}
2020-07-31 06:11:16 +02:00
// Check for stack overflow
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "unchecked"))]
if level > self.max_call_levels() {
return Err(EvalAltResult::ErrorStackOverflow(pos).into());
2020-07-31 06:11:16 +02:00
}
let prev_scope_len = scope.len();
let prev_mods_len = mods.len();
// Put arguments into scope as variables
// Actually consume the arguments instead of cloning them
scope.extend(
fn_def
.params
.iter()
.zip(args.iter_mut().map(|v| mem::take(*v)))
.map(|(name, value)| {
2021-04-17 09:15:54 +02:00
let var_name: std::borrow::Cow<'_, str> =
2020-11-16 09:28:04 +01:00
crate::r#unsafe::unsafe_cast_var_name_to_lifetime(name).into();
2020-12-08 15:47:38 +01:00
(var_name, value)
}),
);
// Merge in encapsulated environment, if any
2021-06-08 09:48:55 +02:00
let mut lib_merged = StaticVec::with_capacity(lib.len() + 1);
let (unified_lib, unified) = if let Some(ref env_lib) = fn_def.lib {
state.push_fn_resolution_cache();
2021-06-08 09:48:55 +02:00
lib_merged.push(env_lib.as_ref());
lib_merged.extend(lib.iter().cloned());
(lib_merged.as_ref(), true)
} else {
(lib, false)
};
2020-11-09 14:52:23 +01:00
#[cfg(not(feature = "no_module"))]
if !fn_def.mods.is_empty() {
2021-03-12 15:30:08 +01:00
fn_def
.mods
.iter_raw()
.for_each(|(n, m)| mods.push(n.clone(), m.clone()));
2020-11-09 14:52:23 +01:00
}
2020-12-29 05:29:45 +01:00
// Evaluate the function
2021-04-16 07:15:11 +02:00
let body = &fn_def.body;
2020-11-20 09:52:28 +01:00
let result = self
2021-03-10 15:12:48 +01:00
.eval_stmt_block(scope, mods, state, unified_lib, this_ptr, body, true, level)
2020-11-20 09:52:28 +01:00
.or_else(|err| match *err {
// Convert return statement to return value
EvalAltResult::Return(x, _) => Ok(x),
// Error in sub function call
2020-12-30 14:12:51 +01:00
EvalAltResult::ErrorInFunctionCall(name, src, err, _) => {
let fn_name = if src.is_empty() {
format!("{} < {}", name, fn_def.name)
} else {
format!("{} @ '{}' < {}", name, src, fn_def.name)
};
make_error(fn_name, fn_def, mods, err, pos)
2020-11-20 09:52:28 +01:00
}
// System errors are passed straight-through
2021-03-16 11:16:40 +01:00
mut err if err.is_system_exception() => {
err.set_position(pos);
Err(err.into())
2021-03-16 11:16:40 +01:00
}
2020-11-20 09:52:28 +01:00
// Other errors are wrapped in `ErrorInFunctionCall`
_ => make_error(fn_def.name.to_string(), fn_def, mods, err, pos),
2020-11-20 09:52:28 +01:00
});
// Remove all local variables
scope.rewind(prev_scope_len);
mods.truncate(prev_mods_len);
if unified {
state.pop_fn_resolution_cache();
}
result
}
2021-03-08 11:40:23 +01:00
// Does a scripted function exist?
2021-03-14 03:47:21 +01:00
#[cfg(not(feature = "no_function"))]
2021-06-12 16:47:43 +02:00
#[must_use]
2021-03-08 11:40:23 +01:00
pub(crate) fn has_script_fn(
&self,
2020-11-22 08:41:55 +01:00
mods: Option<&Imports>,
2021-07-04 10:40:15 +02:00
state: &mut EvalState,
2020-10-20 04:54:32 +02:00
lib: &[&Module],
2021-03-08 11:40:23 +01:00
hash_script: u64,
) -> bool {
let cache = state.fn_resolution_cache_mut();
2021-03-08 11:40:23 +01:00
if let Some(result) = cache.get(&hash_script).map(|v| v.is_some()) {
return result;
}
// First check script-defined functions
2021-03-17 02:58:08 +01:00
let result = lib.iter().any(|&m| m.contains_fn(hash_script))
// Then check the global namespace and packages
2021-03-17 02:58:08 +01:00
|| self.global_modules.iter().any(|m| m.contains_fn(hash_script))
// Then check imported modules
2021-03-08 11:40:23 +01:00
|| mods.map_or(false, |m| m.contains_fn(hash_script))
2021-03-01 08:39:49 +01:00
// Then check sub-modules
2021-03-08 11:40:23 +01:00
|| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script));
if !result {
cache.insert(hash_script, None);
}
2021-03-08 11:40:23 +01:00
result
}
2020-07-31 06:11:16 +02:00
/// Perform an actual function call, native Rust or scripted, taking care of special functions.
///
2021-01-02 16:30:10 +01:00
/// # WARNING
///
/// Function call arguments may be _consumed_ when the function requires them to be passed by value.
/// All function arguments not in the first position are always passed by value and thus consumed.
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
pub(crate) fn exec_fn_call(
&self,
mods: &mut Imports,
2021-07-04 10:40:15 +02:00
state: &mut EvalState,
2020-10-20 04:54:32 +02:00
lib: &[&Module],
fn_name: &str,
2021-06-13 11:41:34 +02:00
hashes: FnCallHashes,
args: &mut FnCallArgs,
2021-06-17 03:50:32 +02:00
is_ref_mut: bool,
2021-08-13 07:42:39 +02:00
is_method_call: bool,
2020-12-12 04:15:09 +01:00
pos: Position,
captured_scope: Option<Scope>,
2020-08-05 16:53:01 +02:00
_level: usize,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
2021-06-08 17:40:10 +02:00
fn no_method_err(name: &str, pos: Position) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
let msg = format!("'{0}' should not be called this way. Try {0}(...);", name);
Err(EvalAltResult::ErrorRuntime(msg.into(), pos).into())
2021-06-08 17:40:10 +02:00
}
2020-08-02 07:33:51 +02:00
// Check for data race.
2021-03-20 16:57:43 +01:00
#[cfg(not(feature = "no_closure"))]
2021-06-17 03:50:32 +02:00
ensure_no_data_race(fn_name, args, is_ref_mut)?;
2020-08-02 07:33:51 +02:00
2021-08-13 07:42:39 +02:00
let _is_method_call = is_method_call;
2021-03-01 15:44:56 +01:00
// These may be redirected from method style calls.
match fn_name {
2021-03-01 15:44:56 +01:00
// Handle type_of()
KEYWORD_TYPE_OF if args.len() == 1 => {
return Ok((
self.map_type_name(args[0].type_name()).to_string().into(),
false,
2021-06-08 17:40:10 +02:00
))
2021-03-01 15:44:56 +01:00
}
2021-03-01 15:44:56 +01:00
// Handle is_def_fn()
2020-07-30 12:18:28 +02:00
#[cfg(not(feature = "no_function"))]
2021-03-01 15:44:56 +01:00
crate::engine::KEYWORD_IS_DEF_FN
2021-03-07 15:10:54 +01:00
if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<crate::INT>() =>
2021-03-01 15:44:56 +01:00
{
2021-11-13 05:23:35 +01:00
let fn_name = args[0].read_lock::<ImmutableString>().expect("`FnPtr`");
let num_params = args[1].as_int().expect("`INT`");
2020-09-25 13:07:24 +02:00
2021-03-01 15:44:56 +01:00
return Ok((
if num_params < 0 {
2021-10-11 09:49:51 +02:00
false
2021-03-01 15:44:56 +01:00
} else {
2021-07-04 10:40:15 +02:00
let hash_script = calc_fn_hash(fn_name.as_str(), num_params as usize);
2021-03-08 11:40:23 +01:00
self.has_script_fn(Some(mods), state, lib, hash_script)
2021-10-11 09:49:51 +02:00
}
.into(),
2021-03-01 15:44:56 +01:00
false,
));
}
2020-12-21 15:04:46 +01:00
2021-03-01 15:44:56 +01:00
// Handle is_shared()
#[cfg(not(feature = "no_closure"))]
crate::engine::KEYWORD_IS_SHARED if args.len() == 1 => {
2021-06-08 17:40:10 +02:00
return no_method_err(fn_name, pos)
2021-03-01 15:44:56 +01:00
}
2020-12-21 15:04:46 +01:00
2021-03-01 15:44:56 +01:00
KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR if args.len() == 1 => {
2021-06-08 17:40:10 +02:00
return no_method_err(fn_name, pos)
2021-03-01 15:44:56 +01:00
}
2020-12-29 05:29:45 +01:00
2021-03-01 15:44:56 +01:00
KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY if !args.is_empty() => {
2021-06-08 17:40:10 +02:00
return no_method_err(fn_name, pos)
2021-03-01 15:44:56 +01:00
}
2020-12-21 15:04:46 +01:00
2021-03-01 15:44:56 +01:00
_ => (),
}
2020-12-21 15:04:46 +01:00
2021-03-08 08:30:32 +01:00
// Scripted function call?
2021-03-14 03:47:21 +01:00
#[cfg(not(feature = "no_function"))]
2021-06-13 11:41:34 +02:00
let hash_script = hashes.script;
2021-03-08 08:30:32 +01:00
2021-03-01 15:44:56 +01:00
#[cfg(not(feature = "no_function"))]
if let Some(FnResolutionCacheEntry { func, source }) = hash_script.and_then(|hash| {
2021-06-12 16:47:43 +02:00
self.resolve_fn(mods, state, lib, fn_name, hash, None, false, false)
.cloned()
2021-03-01 15:44:56 +01:00
}) {
// Script function call
assert!(func.is_script());
2020-07-30 12:18:28 +02:00
2021-08-26 17:58:41 +02:00
let func = func.get_script_fn_def().expect("scripted function");
2020-12-21 15:04:46 +01:00
2021-03-10 15:12:48 +01:00
if func.body.is_empty() {
return Ok((Dynamic::UNIT, false));
}
let mut scope = captured_scope.unwrap_or_else(|| Scope::new());
2020-07-30 12:18:28 +02:00
2021-06-17 03:50:32 +02:00
let result = if _is_method_call {
2021-03-01 15:44:56 +01:00
// Method call of script function - map first argument to `this`
2021-11-13 05:23:35 +01:00
let (first_arg, rest_args) = args.split_first_mut().expect("not empty");
2020-12-21 15:04:46 +01:00
let orig_source = mods.source.take();
mods.source = source;
2020-09-25 13:07:24 +02:00
2021-03-01 15:44:56 +01:00
let level = _level + 1;
2020-07-30 12:18:28 +02:00
2021-03-01 15:44:56 +01:00
let result = self.call_script_fn(
&mut scope,
2021-03-01 15:44:56 +01:00
mods,
state,
lib,
&mut Some(*first_arg),
2021-03-01 15:44:56 +01:00
func,
rest_args,
2021-03-01 15:44:56 +01:00
pos,
level,
);
// Restore the original source
mods.source = orig_source;
2021-03-01 15:44:56 +01:00
result?
} else {
// Normal call of script function
// The first argument is a reference?
2021-03-02 07:44:21 +01:00
let mut backup: Option<ArgBackup> = None;
2021-06-17 03:50:32 +02:00
if is_ref_mut && !args.is_empty() {
backup = Some(ArgBackup::new());
2021-08-26 17:58:41 +02:00
backup
.as_mut()
2021-11-13 05:23:35 +01:00
.expect("`Some`")
2021-08-26 17:58:41 +02:00
.change_first_arg_to_copy(args);
2021-03-02 07:44:21 +01:00
}
2020-09-11 16:32:59 +02:00
let orig_source = mods.source.take();
mods.source = source;
2021-03-01 15:44:56 +01:00
let level = _level + 1;
let result = self.call_script_fn(
&mut scope, mods, state, lib, &mut None, func, args, pos, level,
);
2021-03-01 15:44:56 +01:00
// Restore the original source
mods.source = orig_source;
2021-03-01 15:44:56 +01:00
// Restore the original reference
2021-07-24 08:11:16 +02:00
if let Some(bk) = backup {
bk.restore_first_arg(args)
}
2021-03-01 15:44:56 +01:00
result?
};
return Ok((result, false));
}
2021-03-01 15:44:56 +01:00
// Native function call
2021-06-13 11:41:34 +02:00
let hash = hashes.native;
2021-06-17 03:50:32 +02:00
self.call_native_fn(
mods, state, lib, fn_name, hash, args, is_ref_mut, false, pos,
)
}
/// Evaluate a list of statements with no `this` pointer.
2020-11-20 09:52:28 +01:00
/// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body.
#[inline]
pub(crate) fn eval_global_statements(
2020-10-20 04:54:32 +02:00
&self,
scope: &mut Scope,
mods: &mut Imports,
2021-07-04 10:40:15 +02:00
state: &mut EvalState,
statements: &[Stmt],
2020-10-20 04:54:32 +02:00
lib: &[&Module],
2020-12-29 05:29:45 +01:00
level: usize,
2021-03-02 08:02:28 +01:00
) -> RhaiResult {
self.eval_stmt_block(scope, mods, state, lib, &mut None, statements, false, level)
2020-10-20 04:54:32 +02:00
.or_else(|err| match *err {
EvalAltResult::Return(out, _) => Ok(out),
EvalAltResult::LoopBreak(_, _) => {
unreachable!("no outer loop scope to break out of")
}
2020-10-20 04:54:32 +02:00
_ => Err(err),
})
}
2020-12-20 16:25:11 +01:00
/// Evaluate a text script in place - used primarily for 'eval'.
fn eval_script_expr_in_place(
&self,
scope: &mut Scope,
mods: &mut Imports,
2020-10-20 04:54:32 +02:00
lib: &[&Module],
2020-10-03 17:27:30 +02:00
script: &str,
2021-04-25 09:27:58 +02:00
_pos: Position,
2020-12-29 05:29:45 +01:00
level: usize,
2021-03-02 08:02:28 +01:00
) -> RhaiResult {
2021-04-25 09:27:58 +02:00
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut mods.num_operations, _pos)?;
2020-07-31 06:11:16 +02:00
2020-10-27 04:30:38 +01:00
let script = script.trim();
if script.is_empty() {
2020-11-15 16:14:29 +01:00
return Ok(Dynamic::UNIT);
2020-10-27 04:30:38 +01:00
}
// Compile the script text
// No optimizations because we only run it once
2020-10-20 04:54:32 +02:00
let ast = self.compile_with_scope_and_optimization_level(
&Scope::new(),
&[script],
#[cfg(not(feature = "no_optimize"))]
crate::OptimizationLevel::None,
)?;
// If new functions are defined within the eval string, it is an error
2020-10-05 15:52:39 +02:00
if ast.lib().count().0 != 0 {
return Err(ParseErrorType::WrongFnDefinition.into());
}
2021-04-17 16:19:34 +02:00
let statements = ast.statements();
if statements.is_empty() {
return Ok(Dynamic::UNIT);
}
// Evaluate the AST
self.eval_global_statements(scope, mods, &mut EvalState::new(), statements, lib, level)
}
/// Call a dot method.
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_object"))]
pub(crate) fn make_method_call(
&self,
mods: &mut Imports,
2021-07-04 10:40:15 +02:00
state: &mut EvalState,
2020-10-20 04:54:32 +02:00
lib: &[&Module],
2020-11-10 16:26:50 +01:00
fn_name: &str,
2021-04-20 16:26:08 +02:00
mut hash: FnCallHashes,
2020-11-16 09:28:04 +01:00
target: &mut crate::engine::Target,
2021-05-29 12:33:29 +02:00
(call_args, call_arg_pos): &mut (StaticVec<Dynamic>, Position),
2020-12-12 04:15:09 +01:00
pos: Position,
level: usize,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
2021-06-17 03:50:32 +02:00
let is_ref_mut = target.is_ref();
2021-03-01 15:44:56 +01:00
let (result, updated) = match fn_name {
2021-05-29 12:33:29 +02:00
KEYWORD_FN_PTR_CALL if target.is::<FnPtr>() => {
2021-03-01 15:44:56 +01:00
// FnPtr call
2021-11-13 05:23:35 +01:00
let fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`");
2021-03-01 15:44:56 +01:00
// Redirect function name
let fn_name = fn_ptr.fn_name();
let args_len = call_args.len() + fn_ptr.curry().len();
2021-03-08 08:30:32 +01:00
// Recalculate hashes
2021-05-19 14:26:11 +02:00
let new_hash = FnCallHashes::from_script(calc_fn_hash(fn_name, args_len));
2021-03-01 15:44:56 +01:00
// Arguments are passed as-is, adding the curried arguments
2021-06-08 09:48:55 +02:00
let mut curry = StaticVec::with_capacity(fn_ptr.num_curried());
curry.extend(fn_ptr.curry().iter().cloned());
let mut args = StaticVec::with_capacity(curry.len() + call_args.len());
args.extend(curry.iter_mut());
args.extend(call_args.iter_mut());
2021-03-01 15:44:56 +01:00
// Map it to name(args) in function-call style
self.exec_fn_call(
2021-04-17 07:54:24 +02:00
mods, state, lib, fn_name, new_hash, &mut args, false, false, pos, None, level,
)
2020-10-03 10:25:58 +02:00
}
KEYWORD_FN_PTR_CALL => {
2021-07-24 08:11:16 +02:00
if !call_args.is_empty() {
2021-03-09 11:11:43 +01:00
if !call_args[0].is::<FnPtr>() {
return Err(self.make_type_mismatch_err::<FnPtr>(
2021-06-12 16:47:43 +02:00
self.map_type_name(call_args[0].type_name()),
2021-05-29 12:33:29 +02:00
*call_arg_pos,
));
}
} else {
return Err(self.make_type_mismatch_err::<FnPtr>(
2021-05-29 12:33:29 +02:00
self.map_type_name(target.type_name()),
pos,
));
}
2021-03-01 15:44:56 +01:00
// FnPtr call on object
2021-03-09 11:11:43 +01:00
let fn_ptr = call_args.remove(0).cast::<FnPtr>();
2021-03-01 15:44:56 +01:00
// Redirect function name
let fn_name = fn_ptr.fn_name();
let args_len = call_args.len() + fn_ptr.curry().len();
// Recalculate hash
2021-04-20 16:26:08 +02:00
let new_hash = FnCallHashes::from_script_and_native(
2021-05-19 14:26:11 +02:00
calc_fn_hash(fn_name, args_len),
calc_fn_hash(fn_name, args_len + 1),
2021-03-08 08:30:32 +01:00
);
2021-03-01 15:44:56 +01:00
// Replace the first argument with the object pointer, adding the curried arguments
2021-06-08 09:48:55 +02:00
let mut curry = StaticVec::with_capacity(fn_ptr.num_curried());
curry.extend(fn_ptr.curry().iter().cloned());
let mut args = StaticVec::with_capacity(curry.len() + call_args.len() + 1);
args.push(target.as_mut());
args.extend(curry.iter_mut());
args.extend(call_args.iter_mut());
2021-03-01 15:44:56 +01:00
// Map it to name(args) in function-call style
self.exec_fn_call(
2021-06-17 03:50:32 +02:00
mods, state, lib, fn_name, new_hash, &mut args, is_ref_mut, true, pos, None,
level,
2021-03-01 15:44:56 +01:00
)
}
KEYWORD_FN_PTR_CURRY => {
2021-05-29 12:33:29 +02:00
if !target.is::<FnPtr>() {
return Err(self.make_type_mismatch_err::<FnPtr>(
2021-05-29 12:33:29 +02:00
self.map_type_name(target.type_name()),
pos,
));
}
2021-11-13 05:23:35 +01:00
let fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`");
// Curry call
2021-03-01 15:44:56 +01:00
Ok((
if call_args.is_empty() {
fn_ptr.clone()
} else {
FnPtr::new_unchecked(
2021-06-29 15:47:27 +02:00
fn_ptr.fn_name_raw().clone(),
fn_ptr
.curry()
.iter()
.cloned()
2021-06-08 17:40:10 +02:00
.chain(call_args.iter_mut().map(mem::take))
.collect(),
)
}
2021-03-01 15:44:56 +01:00
.into(),
false,
))
}
2021-03-01 15:44:56 +01:00
// Handle is_shared()
#[cfg(not(feature = "no_closure"))]
crate::engine::KEYWORD_IS_SHARED if call_args.is_empty() => {
return Ok((target.is_shared().into(), false));
2020-07-30 12:18:28 +02:00
}
2021-03-01 15:44:56 +01:00
_ => {
2021-05-29 12:33:29 +02:00
let mut fn_name = fn_name;
2021-03-01 15:44:56 +01:00
let _redirected;
// Check if it is a map method call in OOP style
#[cfg(not(feature = "no_object"))]
2021-05-29 12:33:29 +02:00
if let Some(map) = target.read_lock::<Map>() {
2021-03-01 15:44:56 +01:00
if let Some(val) = map.get(fn_name) {
if let Some(fn_ptr) = val.read_lock::<FnPtr>() {
// Remap the function name
2021-06-29 15:47:27 +02:00
_redirected = fn_ptr.fn_name_raw().clone();
2021-03-01 15:44:56 +01:00
fn_name = &_redirected;
// Add curried arguments
2021-05-29 12:33:29 +02:00
if fn_ptr.is_curried() {
call_args.insert_many(0, fn_ptr.curry().iter().cloned());
}
2021-03-01 15:44:56 +01:00
// Recalculate the hash based on the new function name and new arguments
2021-04-20 16:26:08 +02:00
hash = FnCallHashes::from_script_and_native(
2021-05-19 14:26:11 +02:00
calc_fn_hash(fn_name, call_args.len()),
calc_fn_hash(fn_name, call_args.len() + 1),
2021-03-08 08:30:32 +01:00
);
2021-03-01 15:44:56 +01:00
}
}
};
2021-03-01 15:44:56 +01:00
// Attached object pointer in front of the arguments
2021-06-08 09:48:55 +02:00
let mut args = StaticVec::with_capacity(call_args.len() + 1);
args.push(target.as_mut());
args.extend(call_args.iter_mut());
2021-03-01 15:44:56 +01:00
self.exec_fn_call(
2021-06-17 03:50:32 +02:00
mods, state, lib, fn_name, hash, &mut args, is_ref_mut, true, pos, None, level,
2021-03-01 15:44:56 +01:00
)
}
}?;
2020-10-04 04:40:44 +02:00
// Propagate the changed value back to the source if necessary
if updated {
2021-05-25 04:54:48 +02:00
target
.propagate_changed_value()
.map_err(|err| err.fill_position(pos))?;
}
Ok((result, updated))
}
2021-06-08 09:48:55 +02:00
/// Evaluate an argument.
#[inline]
2021-06-08 09:48:55 +02:00
pub(crate) fn get_arg_value(
&self,
scope: &mut Scope,
mods: &mut Imports,
2021-07-04 10:40:15 +02:00
state: &mut EvalState,
lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>,
level: usize,
args_expr: &[Expr],
constants: &[Dynamic],
index: usize,
) -> Result<(Dynamic, Position), Box<EvalAltResult>> {
match args_expr[index] {
Expr::Stack(slot, pos) => Ok((constants[slot].clone(), pos)),
ref arg => self
.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
.map(|v| (v, arg.position())),
}
}
/// Call a function in normal function-call style.
pub(crate) fn make_function_call(
&self,
scope: &mut Scope,
mods: &mut Imports,
2021-07-04 10:40:15 +02:00
state: &mut EvalState,
2020-10-20 04:54:32 +02:00
lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>,
2020-11-10 16:26:50 +01:00
fn_name: &str,
args_expr: &[Expr],
constants: &[Dynamic],
2021-04-20 16:26:08 +02:00
mut hashes: FnCallHashes,
2020-12-12 04:15:09 +01:00
pos: Position,
2020-10-22 06:26:44 +02:00
capture_scope: bool,
level: usize,
2021-03-02 08:02:28 +01:00
) -> RhaiResult {
2021-02-03 12:14:26 +01:00
// Handle call() - Redirect function call
let redirected;
let mut args_expr = args_expr;
let mut total_args = args_expr.len();
2021-02-03 12:14:26 +01:00
let mut curry = StaticVec::new();
let mut name = fn_name;
2021-03-01 15:44:56 +01:00
match name {
// Handle call()
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
let (arg, arg_pos) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
)?;
2021-03-01 15:44:56 +01:00
if !arg.is::<FnPtr>() {
2021-03-01 15:44:56 +01:00
return Err(self.make_type_mismatch_err::<FnPtr>(
self.map_type_name(arg.type_name()),
arg_pos,
2021-03-01 15:44:56 +01:00
));
}
2021-02-03 12:14:26 +01:00
let fn_ptr = arg.cast::<FnPtr>();
2021-03-01 15:44:56 +01:00
curry.extend(fn_ptr.curry().iter().cloned());
// Redirect function name
redirected = fn_ptr.take_data().0;
name = &redirected;
// Skip the first argument
args_expr = &args_expr[1..];
total_args -= 1;
2021-03-01 15:44:56 +01:00
// Recalculate hash
let args_len = total_args + curry.len();
2021-04-20 16:26:08 +02:00
hashes = if !hashes.is_native_only() {
2021-05-19 14:26:11 +02:00
FnCallHashes::from_script(calc_fn_hash(name, args_len))
2021-03-08 08:30:32 +01:00
} else {
2021-05-19 14:26:11 +02:00
FnCallHashes::from_native(calc_fn_hash(name, args_len))
2021-03-08 08:30:32 +01:00
};
2021-02-03 12:14:26 +01:00
}
2021-03-01 15:44:56 +01:00
// Handle Fn()
KEYWORD_FN_PTR if total_args == 1 => {
let (arg, arg_pos) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
)?;
2021-03-01 15:44:56 +01:00
// Fn - only in function call style
return arg
.into_immutable_string()
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))
2021-07-24 08:11:16 +02:00
.and_then(FnPtr::try_from)
2021-03-01 15:44:56 +01:00
.map(Into::<Dynamic>::into)
.map_err(|err| err.fill_position(arg_pos));
2021-03-01 15:44:56 +01:00
}
2021-02-03 12:14:26 +01:00
2021-03-01 15:44:56 +01:00
// Handle curry()
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
let (arg, arg_pos) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
)?;
2021-02-03 12:14:26 +01:00
if !arg.is::<FnPtr>() {
2021-03-01 15:44:56 +01:00
return Err(self.make_type_mismatch_err::<FnPtr>(
self.map_type_name(arg.type_name()),
arg_pos,
2021-03-01 15:44:56 +01:00
));
}
2021-02-03 12:14:26 +01:00
let (name, mut fn_curry) = arg.cast::<FnPtr>().take_data();
2021-02-03 12:14:26 +01:00
2021-03-01 15:44:56 +01:00
// Append the new curried arguments to the existing list.
2021-06-08 09:48:55 +02:00
for index in 1..args_expr.len() {
let (value, _) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, args_expr, constants, index,
)?;
fn_curry.push(value);
}
2021-03-01 15:44:56 +01:00
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
}
2021-03-01 15:44:56 +01:00
// Handle is_shared()
#[cfg(not(feature = "no_closure"))]
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
let (arg, _) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
)?;
return Ok(arg.is_shared().into());
2021-03-01 15:44:56 +01:00
}
2021-03-01 15:44:56 +01:00
// Handle is_def_fn()
#[cfg(not(feature = "no_function"))]
crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => {
let (arg, arg_pos) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
)?;
let fn_name = arg
.into_immutable_string()
2021-06-12 16:47:43 +02:00
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
let (arg, arg_pos) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, args_expr, constants, 1,
)?;
let num_params = arg
.as_int()
2021-06-12 16:47:43 +02:00
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, arg_pos))?;
return Ok(if num_params < 0 {
2021-10-11 09:49:51 +02:00
false
2021-03-01 15:44:56 +01:00
} else {
2021-05-19 14:26:11 +02:00
let hash_script = calc_fn_hash(&fn_name, num_params as usize);
2021-03-08 11:40:23 +01:00
self.has_script_fn(Some(mods), state, lib, hash_script)
2021-10-11 09:49:51 +02:00
}
.into());
2021-03-01 15:44:56 +01:00
}
2021-03-01 15:44:56 +01:00
// Handle is_def_var()
KEYWORD_IS_DEF_VAR if total_args == 1 => {
let (arg, arg_pos) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
)?;
let var_name = arg
.into_immutable_string()
2021-06-12 16:47:43 +02:00
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
2021-03-05 16:00:27 +01:00
return Ok(scope.contains(&var_name).into());
2021-03-01 15:44:56 +01:00
}
2021-03-01 15:44:56 +01:00
// Handle eval()
KEYWORD_EVAL if total_args == 1 => {
2021-03-01 15:44:56 +01:00
// eval - only in function call style
let prev_len = scope.len();
2021-06-08 17:40:10 +02:00
let (value, pos) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
)?;
2021-06-08 17:40:10 +02:00
let script = &value
.into_immutable_string()
2021-06-08 17:40:10 +02:00
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
let result =
self.eval_script_expr_in_place(scope, mods, lib, script, pos, level + 1);
2021-03-01 15:44:56 +01:00
// IMPORTANT! If the eval defines new variables in the current scope,
// all variable offsets from this point on will be mis-aligned.
if scope.len() != prev_len {
2021-07-04 10:40:15 +02:00
state.always_search_scope = true;
2021-03-01 15:44:56 +01:00
}
2020-10-03 10:25:58 +02:00
2021-03-01 15:44:56 +01:00
return result.map_err(|err| {
2021-06-29 12:25:20 +02:00
EvalAltResult::ErrorInFunctionCall(
2021-03-01 15:44:56 +01:00
KEYWORD_EVAL.to_string(),
mods.source
2021-03-01 15:44:56 +01:00
.as_ref()
2021-06-08 17:40:10 +02:00
.map(Identifier::to_string)
2021-03-24 06:17:52 +01:00
.unwrap_or_default(),
2021-03-01 15:44:56 +01:00
err,
pos,
2021-06-29 12:25:20 +02:00
)
.into()
2021-03-01 15:44:56 +01:00
});
2020-07-30 12:18:28 +02:00
}
2021-03-01 10:17:13 +01:00
2021-03-01 15:44:56 +01:00
_ => (),
2020-07-30 12:18:28 +02:00
}
// Normal function call - except for Fn, curry, call and eval (handled above)
2021-06-08 09:48:55 +02:00
let mut arg_values = StaticVec::with_capacity(args_expr.len());
let mut args = StaticVec::with_capacity(args_expr.len() + curry.len());
2021-06-17 03:50:32 +02:00
let mut is_ref_mut = false;
2020-10-22 06:26:44 +02:00
let capture = if capture_scope && !scope.is_empty() {
2020-10-12 11:00:58 +02:00
Some(scope.clone_visible())
2020-07-30 12:18:28 +02:00
} else {
None
};
if args_expr.is_empty() && curry.is_empty() {
// No arguments
} else {
2021-02-03 12:14:26 +01:00
// If the first argument is a variable, and there is no curried arguments,
// convert to method-call style in order to leverage potential &mut first argument and
// avoid cloning the value
2021-04-05 17:59:15 +02:00
if curry.is_empty() && !args_expr.is_empty() && args_expr[0].is_variable_access(false) {
// func(x, ...) -> x.func(...)
2021-11-13 05:23:35 +01:00
let (first_expr, rest_expr) = args_expr.split_first().expect("not empty");
for index in 0..rest_expr.len() {
2021-06-08 09:48:55 +02:00
let (value, _) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, rest_expr, constants, index,
2021-06-08 09:48:55 +02:00
)?;
arg_values.push(value.flatten());
}
2021-04-25 09:27:58 +02:00
let (mut target, _pos) =
self.search_namespace(scope, mods, state, lib, this_ptr, first_expr)?;
2020-12-08 16:09:12 +01:00
if target.as_ref().is_read_only() {
2020-12-08 15:47:38 +01:00
target = target.into_owned();
}
2021-04-25 09:27:58 +02:00
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut mods.num_operations, _pos)?;
#[cfg(not(feature = "no_closure"))]
let target_is_shared = target.is_shared();
#[cfg(feature = "no_closure")]
let target_is_shared = false;
2021-06-08 17:40:10 +02:00
if target_is_shared || target.is_temp_value() {
arg_values.insert(0, target.take_or_clone().flatten());
2021-06-08 09:48:55 +02:00
args.extend(arg_values.iter_mut())
} else {
// Turn it into a method call only if the object is not shared and not a simple value
2021-06-17 03:50:32 +02:00
is_ref_mut = true;
2021-11-13 05:23:35 +01:00
let obj_ref = target.take_ref().expect("reference");
2021-06-08 09:48:55 +02:00
args.push(obj_ref);
args.extend(arg_values.iter_mut());
}
} else {
// func(..., ...)
2021-06-08 09:48:55 +02:00
for index in 0..args_expr.len() {
let (value, _) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, args_expr, constants, index,
)?;
arg_values.push(value.flatten());
}
args.extend(curry.iter_mut());
args.extend(arg_values.iter_mut());
}
}
self.exec_fn_call(
2021-06-17 03:50:32 +02:00
mods, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, capture, level,
)
.map(|(v, _)| v)
}
2020-11-10 16:26:50 +01:00
/// Call a namespace-qualified function in normal function-call style.
pub(crate) fn make_qualified_function_call(
&self,
scope: &mut Scope,
mods: &mut Imports,
2021-07-04 10:40:15 +02:00
state: &mut EvalState,
2020-10-20 04:54:32 +02:00
lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>,
2021-05-22 13:14:24 +02:00
namespace: &NamespaceRef,
2020-11-10 16:26:50 +01:00
fn_name: &str,
args_expr: &[Expr],
constants: &[Dynamic],
2021-03-08 08:30:32 +01:00
hash: u64,
2020-12-12 04:15:09 +01:00
pos: Position,
level: usize,
2021-03-02 08:02:28 +01:00
) -> RhaiResult {
2021-06-08 09:48:55 +02:00
let mut arg_values = StaticVec::with_capacity(args_expr.len());
let mut args = StaticVec::with_capacity(args_expr.len());
let mut first_arg_value = None;
if args_expr.is_empty() {
// No arguments
} else {
2020-11-10 16:26:50 +01:00
// See if the first argument is a variable (not namespace-qualified).
// If so, convert to method-call style in order to leverage potential
// &mut first argument and avoid cloning the value
2021-04-05 17:59:15 +02:00
if !args_expr.is_empty() && args_expr[0].is_variable_access(true) {
// func(x, ...) -> x.func(...)
2021-06-08 09:48:55 +02:00
for index in 0..args_expr.len() {
if index == 0 {
arg_values.push(Dynamic::UNIT);
2021-06-08 09:48:55 +02:00
} else {
let (value, _) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, args_expr, constants, index,
)?;
arg_values.push(value.flatten());
}
}
// Get target reference to first argument
2021-04-25 09:27:58 +02:00
let (target, _pos) =
self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
2021-04-25 09:27:58 +02:00
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut mods.num_operations, _pos)?;
#[cfg(not(feature = "no_closure"))]
let target_is_shared = target.is_shared();
#[cfg(feature = "no_closure")]
let target_is_shared = false;
2021-06-08 17:40:10 +02:00
if target_is_shared || target.is_temp_value() {
arg_values[0] = target.take_or_clone().flatten();
2021-06-08 09:48:55 +02:00
args.extend(arg_values.iter_mut());
} else {
2021-06-02 08:29:18 +02:00
// Turn it into a method call only if the object is not shared and not a simple value
2021-11-13 05:23:35 +01:00
let (first, rest) = arg_values.split_first_mut().expect("not empty");
first_arg_value = Some(first);
2021-11-13 05:23:35 +01:00
let obj_ref = target.take_ref().expect("reference");
2021-06-08 09:48:55 +02:00
args.push(obj_ref);
args.extend(rest.iter_mut());
}
} else {
// func(..., ...) or func(mod::x, ...)
2021-06-08 09:48:55 +02:00
for index in 0..args_expr.len() {
let (value, _) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, args_expr, constants, index,
)?;
arg_values.push(value.flatten());
}
args.extend(arg_values.iter_mut());
}
}
2021-03-03 15:49:57 +01:00
let module = self.search_imports(mods, state, namespace).ok_or_else(|| {
2021-10-27 17:30:25 +02:00
EvalAltResult::ErrorModuleNotFound(namespace.to_string(), namespace[0].pos)
2021-03-03 15:49:57 +01:00
})?;
// First search in script-defined functions (can override built-in)
2021-03-08 08:30:32 +01:00
let func = match module.get_qualified_fn(hash) {
// Then search in Rust functions
None => {
2021-04-25 09:27:58 +02:00
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut mods.num_operations, pos)?;
2021-03-08 08:30:32 +01:00
let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id()));
let hash_qualified_fn = combine_hashes(hash, hash_params);
module.get_qualified_fn(hash_qualified_fn)
}
r => r,
};
// Clone first argument if the function is not a method after-all
2021-05-25 04:54:48 +02:00
if !func.map(|f| f.is_method()).unwrap_or(true) {
2021-07-24 08:11:16 +02:00
if let Some(first) = first_arg_value {
2021-05-25 04:54:48 +02:00
*first = args[0].clone();
args[0] = first;
2021-07-24 08:11:16 +02:00
}
}
match func {
#[cfg(not(feature = "no_function"))]
Some(f) if f.is_script() => {
2021-08-26 17:58:41 +02:00
let fn_def = f.get_script_fn_def().expect("scripted function");
2020-12-21 15:04:46 +01:00
2021-03-10 15:12:48 +01:00
if fn_def.body.is_empty() {
Ok(Dynamic::UNIT)
} else {
let new_scope = &mut Scope::new();
2020-12-21 15:04:46 +01:00
2021-03-10 15:12:48 +01:00
let mut source = module.id_raw().cloned();
mem::swap(&mut mods.source, &mut source);
2020-12-29 05:29:45 +01:00
2021-03-10 15:12:48 +01:00
let level = level + 1;
2020-12-21 15:04:46 +01:00
2021-03-10 15:12:48 +01:00
let result = self.call_script_fn(
2021-04-17 07:54:24 +02:00
new_scope, mods, state, lib, &mut None, fn_def, &mut args, pos, level,
2021-03-10 15:12:48 +01:00
);
2020-12-21 15:04:46 +01:00
mods.source = source;
2021-03-10 15:12:48 +01:00
result
}
}
2021-11-05 12:35:33 +01:00
Some(f) if f.is_plugin_fn() => {
let context = (self, fn_name, module.id(), &*mods, lib, pos).into();
f.get_plugin_fn()
.expect("plugin function")
.clone()
.call(context, &mut args)
.map_err(|err| err.fill_position(pos))
}
2021-04-17 07:54:24 +02:00
Some(f) if f.is_native() => {
2021-08-26 17:58:41 +02:00
let func = f.get_native_fn().expect("native function");
2021-11-05 12:35:33 +01:00
let context = (self, fn_name, module.id(), &*mods, lib, pos).into();
func(context, &mut args).map_err(|err| err.fill_position(pos))
2021-04-17 07:54:24 +02:00
}
Some(f) => unreachable!("unknown function type: {:?}", f),
None => Err(EvalAltResult::ErrorFunctionNotFound(
2021-06-21 13:12:28 +02:00
self.gen_call_signature(Some(namespace), fn_name, &args),
2020-12-12 04:15:09 +01:00
pos,
2020-08-06 04:17:32 +02:00
)
.into()),
}
}
}