2020-11-20 09:52:28 +01:00
|
|
|
//! Implement function-calling mechanism for [`Engine`].
|
2017-12-20 22:16:53 +01:00
|
|
|
|
2020-10-29 04:37:51 +01:00
|
|
|
use crate::ast::{Expr, Stmt};
|
2020-07-23 12:40:42 +02:00
|
|
|
use crate::engine::{
|
2020-11-16 16:10:14 +01:00
|
|
|
search_imports, Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR,
|
2020-11-22 08:41:55 +01:00
|
|
|
KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
2020-07-23 12:40:42 +02:00
|
|
|
};
|
2020-11-16 16:10:14 +01:00
|
|
|
use crate::fn_native::FnCallArgs;
|
|
|
|
use crate::module::NamespaceRef;
|
2020-11-16 16:32:44 +01:00
|
|
|
use crate::optimize::OptimizationLevel;
|
2020-07-23 12:40:42 +02:00
|
|
|
use crate::stdlib::{
|
|
|
|
any::{type_name, TypeId},
|
|
|
|
boxed::Box,
|
|
|
|
convert::TryFrom,
|
|
|
|
format,
|
|
|
|
iter::{empty, once},
|
|
|
|
mem,
|
2020-12-24 09:32:43 +01:00
|
|
|
num::NonZeroU64,
|
2020-11-16 16:10:14 +01:00
|
|
|
ops::Deref,
|
2020-07-23 12:40:42 +02:00
|
|
|
string::ToString,
|
|
|
|
vec::Vec,
|
|
|
|
};
|
2020-12-24 11:43:04 +01:00
|
|
|
use crate::utils::combine_hashes;
|
2020-11-16 16:10:14 +01:00
|
|
|
use crate::{
|
|
|
|
calc_native_fn_hash, calc_script_fn_hash, Dynamic, Engine, EvalAltResult, FnPtr,
|
2020-11-20 09:52:28 +01:00
|
|
|
ImmutableString, Module, ParseErrorType, Position, Scope, StaticVec, INT,
|
2020-11-16 16:10:14 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
use crate::FLOAT;
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_object"))]
|
|
|
|
use crate::Map;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2021-02-19 04:47:26 +01:00
|
|
|
#[cfg(feature = "decimal")]
|
|
|
|
use rust_decimal::Decimal;
|
|
|
|
|
2020-08-23 12:28:55 +02:00
|
|
|
#[cfg(feature = "no_std")]
|
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
use num_traits::float::Float;
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
/// Extract the property name from a getter function name.
|
2020-11-16 14:14:32 +01:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-07-26 09:53:22 +02:00
|
|
|
#[inline(always)]
|
|
|
|
fn extract_prop_from_getter(_fn_name: &str) -> Option<&str> {
|
2020-11-16 09:28:04 +01:00
|
|
|
if _fn_name.starts_with(crate::engine::FN_GET) {
|
2020-11-16 14:14:32 +01:00
|
|
|
Some(&_fn_name[crate::engine::FN_GET.len()..])
|
|
|
|
} else {
|
|
|
|
None
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Extract the property name from a setter function name.
|
2020-11-16 14:14:32 +01:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-07-26 09:53:22 +02:00
|
|
|
#[inline(always)]
|
|
|
|
fn extract_prop_from_setter(_fn_name: &str) -> Option<&str> {
|
2020-11-16 09:28:04 +01:00
|
|
|
if _fn_name.starts_with(crate::engine::FN_SET) {
|
2020-11-16 14:14:32 +01:00
|
|
|
Some(&_fn_name[crate::engine::FN_SET.len()..])
|
|
|
|
} else {
|
|
|
|
None
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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, Default)]
|
|
|
|
struct ArgBackup<'a> {
|
|
|
|
orig_mut: Option<&'a mut Dynamic>,
|
|
|
|
value_copy: Dynamic,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> ArgBackup<'a> {
|
|
|
|
/// 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.
|
|
|
|
///
|
|
|
|
/// If `restore_first_arg` is called before the end of the scope, the shorter lifetime will not leak.
|
2020-12-29 03:41:20 +01:00
|
|
|
#[inline(always)]
|
2020-07-31 06:11:16 +02:00
|
|
|
fn change_first_arg_to_copy(&mut self, normalize: bool, args: &mut FnCallArgs<'a>) {
|
|
|
|
// Only do it for method calls with arguments.
|
|
|
|
if !normalize || args.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
self.orig_mut = Some(mem::replace(args.get_mut(0).unwrap(), unsafe {
|
|
|
|
mem::transmute(&mut self.value_copy)
|
|
|
|
}));
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
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)]
|
2020-07-31 06:11:16 +02:00
|
|
|
fn restore_first_arg(&mut self, args: &mut FnCallArgs<'a>) {
|
|
|
|
if let Some(this_pointer) = self.orig_mut.take() {
|
|
|
|
args[0] = this_pointer;
|
|
|
|
}
|
|
|
|
}
|
2017-12-20 22:16:53 +01:00
|
|
|
}
|
|
|
|
|
2020-07-31 06:11:16 +02:00
|
|
|
impl Drop for ArgBackup<'_> {
|
2020-12-29 03:41:20 +01:00
|
|
|
#[inline(always)]
|
2020-07-31 06:11:16 +02:00
|
|
|
fn drop(&mut self) {
|
|
|
|
// Panic if the shorter lifetime leaks.
|
|
|
|
assert!(
|
|
|
|
self.orig_mut.is_none(),
|
2020-12-28 07:21:13 +01:00
|
|
|
"ArgBackup::restore_first_arg has not been called prior to existing this scope"
|
2020-07-31 06:11:16 +02:00
|
|
|
);
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
2017-12-20 22:16:53 +01:00
|
|
|
}
|
|
|
|
|
2020-08-02 07:33:51 +02:00
|
|
|
#[inline(always)]
|
|
|
|
pub fn ensure_no_data_race(
|
|
|
|
fn_name: &str,
|
|
|
|
args: &FnCallArgs,
|
|
|
|
is_ref: bool,
|
|
|
|
) -> Result<(), Box<EvalAltResult>> {
|
2020-08-03 06:10:20 +02:00
|
|
|
if cfg!(not(feature = "no_closure")) {
|
2020-08-02 07:33:51 +02:00
|
|
|
let skip = if is_ref { 1 } else { 0 };
|
|
|
|
|
|
|
|
if let Some((n, _)) = args
|
|
|
|
.iter()
|
|
|
|
.skip(skip)
|
|
|
|
.enumerate()
|
|
|
|
.find(|(_, a)| a.is_locked())
|
|
|
|
{
|
2020-08-06 04:17:32 +02:00
|
|
|
return EvalAltResult::ErrorDataRace(
|
2020-08-02 07:33:51 +02:00
|
|
|
format!("argument #{} of function '{}'", n + 1 + skip, fn_name),
|
2020-11-20 09:52:28 +01:00
|
|
|
Position::NONE,
|
2020-08-06 04:17:32 +02:00
|
|
|
)
|
|
|
|
.into();
|
2020-08-02 07:33:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
impl Engine {
|
2020-11-20 09:52:28 +01:00
|
|
|
/// Call a native Rust function registered with the [`Engine`].
|
2020-07-23 12:40:42 +02:00
|
|
|
///
|
2021-01-02 16:30:10 +01:00
|
|
|
/// # WARNING
|
2020-07-23 12:40:42 +02:00
|
|
|
///
|
|
|
|
/// 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 `()`!
|
2020-07-31 06:11:16 +02:00
|
|
|
pub(crate) fn call_native_fn(
|
2020-07-23 12:40:42 +02:00
|
|
|
&self,
|
2020-11-16 07:07:48 +01:00
|
|
|
mods: &Imports,
|
2020-07-23 12:40:42 +02:00
|
|
|
state: &mut State,
|
2020-10-20 04:54:32 +02:00
|
|
|
lib: &[&Module],
|
2020-07-23 12:40:42 +02:00
|
|
|
fn_name: &str,
|
2020-12-24 09:32:43 +01:00
|
|
|
hash_fn: NonZeroU64,
|
2020-07-23 12:40:42 +02:00
|
|
|
args: &mut FnCallArgs,
|
|
|
|
is_ref: bool,
|
2020-07-27 06:52:32 +02:00
|
|
|
pub_only: bool,
|
2020-12-12 04:15:09 +01:00
|
|
|
pos: Position,
|
2020-11-20 09:52:28 +01:00
|
|
|
def_val: Option<&Dynamic>,
|
2020-07-23 12:40:42 +02:00
|
|
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
2020-12-20 16:25:11 +01:00
|
|
|
self.inc_operations(state, pos)?;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2021-02-07 10:56:29 +01:00
|
|
|
let source = state.source.clone();
|
2021-02-07 08:41:40 +01:00
|
|
|
|
2021-02-07 10:56:29 +01:00
|
|
|
// Check if function access already in the cache
|
2021-02-07 08:41:40 +01:00
|
|
|
let func = &*state
|
2021-02-07 10:56:29 +01:00
|
|
|
.fn_resolution_cache_mut()
|
2021-02-07 08:41:40 +01:00
|
|
|
.entry(hash_fn)
|
|
|
|
.or_insert_with(|| {
|
|
|
|
// Search for the native function
|
|
|
|
// First search registered functions (can override packages)
|
|
|
|
// Then search packages
|
|
|
|
// Finally search modules
|
|
|
|
|
|
|
|
//lib.get_fn(hash_fn, pub_only)
|
|
|
|
self.global_namespace
|
|
|
|
.get_fn(hash_fn, pub_only)
|
|
|
|
.cloned()
|
|
|
|
.map(|f| (f, None))
|
|
|
|
.or_else(|| {
|
|
|
|
self.global_modules.iter().find_map(|m| {
|
|
|
|
m.get_fn(hash_fn, false)
|
|
|
|
.map(|f| (f.clone(), m.id_raw().cloned()))
|
|
|
|
})
|
2020-12-30 14:12:51 +01:00
|
|
|
})
|
2021-02-07 08:41:40 +01:00
|
|
|
.or_else(|| {
|
|
|
|
mods.get_fn(hash_fn)
|
|
|
|
.map(|(f, source)| (f.clone(), source.cloned()))
|
|
|
|
})
|
|
|
|
});
|
2020-12-30 14:12:51 +01:00
|
|
|
|
2021-02-07 10:56:29 +01:00
|
|
|
if let Some((func, src)) = func {
|
2020-07-31 06:11:16 +02:00
|
|
|
assert!(func.is_native());
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
// Calling pure function but the first argument is a reference?
|
2020-07-31 06:11:16 +02:00
|
|
|
let mut backup: ArgBackup = Default::default();
|
|
|
|
backup.change_first_arg_to_copy(is_ref && func.is_pure(), args);
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
// Run external function
|
2021-02-07 10:56:29 +01:00
|
|
|
let source = src.as_ref().or_else(|| source.as_ref()).map(|s| s.as_str());
|
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-01-21 14:49:25 +01:00
|
|
|
.call((self, fn_name, source, mods, lib).into(), args)
|
2020-08-02 12:53:25 +02:00
|
|
|
} else {
|
2021-01-21 14:49:25 +01:00
|
|
|
func.get_native_fn()((self, fn_name, source, mods, lib).into(), args)
|
2020-08-02 12:53:25 +02:00
|
|
|
};
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
// Restore the original reference
|
2020-07-31 06:11:16 +02:00
|
|
|
backup.restore_first_arg(args);
|
|
|
|
|
|
|
|
let result = result?;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
// See if the function match print/debug (which requires special processing)
|
|
|
|
return Ok(match fn_name {
|
2020-12-22 08:27:27 +01:00
|
|
|
KEYWORD_PRINT => {
|
|
|
|
let text = result.as_str().map_err(|typ| {
|
2020-08-06 04:17:32 +02:00
|
|
|
EvalAltResult::ErrorMismatchOutputType(
|
2020-07-23 12:40:42 +02:00
|
|
|
self.map_type_name(type_name::<ImmutableString>()).into(),
|
|
|
|
typ.into(),
|
2020-12-12 04:15:09 +01:00
|
|
|
pos,
|
2020-08-06 04:17:32 +02:00
|
|
|
)
|
2020-12-22 08:27:27 +01:00
|
|
|
})?;
|
|
|
|
((self.print)(text).into(), false)
|
|
|
|
}
|
2020-12-21 15:04:46 +01:00
|
|
|
KEYWORD_DEBUG => {
|
|
|
|
let text = result.as_str().map_err(|typ| {
|
|
|
|
EvalAltResult::ErrorMismatchOutputType(
|
|
|
|
self.map_type_name(type_name::<ImmutableString>()).into(),
|
|
|
|
typ.into(),
|
|
|
|
pos,
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
let source = state.source.as_ref().map(|s| s.as_str());
|
|
|
|
((self.debug)(text, source, pos).into(), false)
|
|
|
|
}
|
2020-07-23 12:40:42 +02:00
|
|
|
_ => (result, func.is_method()),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if it is built in.
|
|
|
|
if args.len() == 2 {
|
|
|
|
match run_builtin_binary_op(fn_name, args[0], args[1])? {
|
|
|
|
Some(v) => return Ok((v, false)),
|
|
|
|
None => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return default value (if any)
|
|
|
|
if let Some(val) = def_val {
|
2020-11-20 09:52:28 +01:00
|
|
|
return Ok((val.clone(), false));
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Getter function not found?
|
2020-11-16 14:14:32 +01:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-07-23 12:40:42 +02:00
|
|
|
if let Some(prop) = extract_prop_from_getter(fn_name) {
|
2020-08-06 04:17:32 +02:00
|
|
|
return EvalAltResult::ErrorDotExpr(
|
2020-07-27 12:10:45 +02:00
|
|
|
format!(
|
2021-01-15 10:13:04 +01:00
|
|
|
"Unknown property '{}' - a getter is not registered for type '{}'",
|
2020-07-27 12:10:45 +02:00
|
|
|
prop,
|
|
|
|
self.map_type_name(args[0].type_name())
|
|
|
|
),
|
2020-12-12 04:15:09 +01:00
|
|
|
pos,
|
2020-08-06 04:17:32 +02:00
|
|
|
)
|
|
|
|
.into();
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Setter function not found?
|
2020-11-16 14:14:32 +01:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-07-23 12:40:42 +02:00
|
|
|
if let Some(prop) = extract_prop_from_setter(fn_name) {
|
2020-08-06 04:17:32 +02:00
|
|
|
return EvalAltResult::ErrorDotExpr(
|
2020-07-27 12:10:45 +02:00
|
|
|
format!(
|
2021-01-15 10:13:04 +01:00
|
|
|
"No writable property '{}' - a setter is not registered for type '{}' to handle '{}'",
|
2020-07-27 12:10:45 +02:00
|
|
|
prop,
|
2020-09-26 05:35:18 +02:00
|
|
|
self.map_type_name(args[0].type_name()),
|
|
|
|
self.map_type_name(args[1].type_name()),
|
2020-07-27 12:10:45 +02:00
|
|
|
),
|
2020-12-12 04:15:09 +01:00
|
|
|
pos,
|
2020-08-06 04:17:32 +02:00
|
|
|
)
|
|
|
|
.into();
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// index getter function not found?
|
2020-07-26 09:53:22 +02:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
2020-11-16 09:28:04 +01:00
|
|
|
if fn_name == crate::engine::FN_IDX_GET && args.len() == 2 {
|
2020-08-06 04:17:32 +02:00
|
|
|
return EvalAltResult::ErrorFunctionNotFound(
|
2020-07-23 12:40:42 +02:00
|
|
|
format!(
|
|
|
|
"{} [{}]",
|
|
|
|
self.map_type_name(args[0].type_name()),
|
|
|
|
self.map_type_name(args[1].type_name()),
|
|
|
|
),
|
2020-12-12 04:15:09 +01:00
|
|
|
pos,
|
2020-08-06 04:17:32 +02:00
|
|
|
)
|
|
|
|
.into();
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// index setter function not found?
|
2020-07-26 09:53:22 +02:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
2020-11-16 09:28:04 +01:00
|
|
|
if fn_name == crate::engine::FN_IDX_SET {
|
2020-08-06 04:17:32 +02:00
|
|
|
return EvalAltResult::ErrorFunctionNotFound(
|
2020-07-23 12:40:42 +02:00
|
|
|
format!(
|
|
|
|
"{} [{}]=",
|
|
|
|
self.map_type_name(args[0].type_name()),
|
|
|
|
self.map_type_name(args[1].type_name()),
|
|
|
|
),
|
2020-12-12 04:15:09 +01:00
|
|
|
pos,
|
2020-08-06 04:17:32 +02:00
|
|
|
)
|
|
|
|
.into();
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Raise error
|
2020-08-06 04:17:32 +02:00
|
|
|
EvalAltResult::ErrorFunctionNotFound(
|
2020-07-23 12:40:42 +02:00
|
|
|
format!(
|
|
|
|
"{} ({})",
|
|
|
|
fn_name,
|
|
|
|
args.iter()
|
|
|
|
.map(|name| if name.is::<ImmutableString>() {
|
|
|
|
"&str | ImmutableString | String"
|
|
|
|
} else {
|
|
|
|
self.map_type_name((*name).type_name())
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(", ")
|
|
|
|
),
|
2020-12-12 04:15:09 +01:00
|
|
|
pos,
|
2020-08-06 04:17:32 +02:00
|
|
|
)
|
|
|
|
.into()
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Call a script-defined function.
|
|
|
|
///
|
2021-01-02 16:30:10 +01:00
|
|
|
/// # WARNING
|
2020-07-23 12:40:42 +02:00
|
|
|
///
|
|
|
|
/// 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"))]
|
2020-07-23 12:40:42 +02:00
|
|
|
pub(crate) fn call_script_fn(
|
|
|
|
&self,
|
|
|
|
scope: &mut Scope,
|
|
|
|
mods: &mut Imports,
|
|
|
|
state: &mut State,
|
2020-10-20 04:54:32 +02:00
|
|
|
lib: &[&Module],
|
2020-07-23 12:40:42 +02:00
|
|
|
this_ptr: &mut Option<&mut Dynamic>,
|
2020-11-16 09:28:04 +01:00
|
|
|
fn_def: &crate::ast::ScriptFnDef,
|
2020-07-23 12:40:42 +02:00
|
|
|
args: &mut FnCallArgs,
|
2020-12-12 04:15:09 +01:00
|
|
|
pos: Position,
|
2020-07-23 12:40:42 +02:00
|
|
|
level: usize,
|
|
|
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
2021-02-20 07:32:37 +01:00
|
|
|
#[inline(always)]
|
|
|
|
fn make_error(
|
|
|
|
name: String,
|
|
|
|
fn_def: &crate::ast::ScriptFnDef,
|
|
|
|
state: &State,
|
|
|
|
err: Box<EvalAltResult>,
|
|
|
|
pos: Position,
|
|
|
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
|
|
Err(Box::new(EvalAltResult::ErrorInFunctionCall(
|
|
|
|
name,
|
|
|
|
fn_def
|
|
|
|
.lib
|
|
|
|
.as_ref()
|
|
|
|
.and_then(|m| m.id())
|
|
|
|
.unwrap_or_else(|| state.source.as_ref().map_or_else(|| "", |s| s.as_str()))
|
|
|
|
.to_string(),
|
|
|
|
err,
|
|
|
|
pos,
|
|
|
|
)))
|
|
|
|
}
|
|
|
|
|
2020-12-20 16:25:11 +01:00
|
|
|
self.inc_operations(state, pos)?;
|
2020-07-31 06:11:16 +02:00
|
|
|
|
|
|
|
// Check for stack overflow
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
2020-10-18 11:02:17 +02:00
|
|
|
if level > self.max_call_levels() {
|
2020-12-12 04:15:09 +01:00
|
|
|
return Err(Box::new(EvalAltResult::ErrorStackOverflow(pos)));
|
2020-07-31 06:11:16 +02:00
|
|
|
}
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
let orig_scope_level = state.scope_level;
|
|
|
|
state.scope_level += 1;
|
|
|
|
|
|
|
|
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)| {
|
2020-11-16 09:28:04 +01:00
|
|
|
let var_name: crate::stdlib::borrow::Cow<'_, str> =
|
|
|
|
crate::r#unsafe::unsafe_cast_var_name_to_lifetime(name).into();
|
2020-12-08 15:47:38 +01:00
|
|
|
(var_name, value)
|
2020-07-23 12:40:42 +02:00
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
2020-10-13 03:38:14 +02:00
|
|
|
// Merge in encapsulated environment, if any
|
2020-10-20 04:54:32 +02:00
|
|
|
let mut lib_merged: StaticVec<_>;
|
2021-02-07 08:41:40 +01:00
|
|
|
let mut unified = false;
|
2020-10-13 03:38:14 +02:00
|
|
|
|
|
|
|
let unified_lib = if let Some(ref env_lib) = fn_def.lib {
|
2021-02-07 08:41:40 +01:00
|
|
|
unified = true;
|
2021-02-07 10:56:29 +01:00
|
|
|
state.push_fn_resolution_cache();
|
2020-10-20 04:54:32 +02:00
|
|
|
lib_merged = Default::default();
|
|
|
|
lib_merged.push(env_lib.as_ref());
|
2020-10-27 04:30:38 +01:00
|
|
|
lib_merged.extend(lib.iter().cloned());
|
2020-10-20 04:54:32 +02:00
|
|
|
lib_merged.as_ref()
|
2020-10-13 03:38:14 +02:00
|
|
|
} else {
|
|
|
|
lib
|
|
|
|
};
|
|
|
|
|
2020-11-09 14:52:23 +01:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
if !fn_def.mods.is_empty() {
|
2020-12-21 15:04:46 +01:00
|
|
|
mods.extend(fn_def.mods.iter_raw().map(|(n, m)| (n.clone(), m.clone())));
|
2020-11-09 14:52:23 +01:00
|
|
|
}
|
|
|
|
|
2020-12-29 05:29:45 +01:00
|
|
|
// Evaluate the function
|
2020-10-13 03:38:14 +02:00
|
|
|
let stmt = &fn_def.body;
|
|
|
|
|
2020-11-20 09:52:28 +01:00
|
|
|
let result = self
|
2020-12-29 05:29:45 +01:00
|
|
|
.eval_stmt(scope, mods, state, unified_lib, this_ptr, stmt, 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),
|
2021-02-20 07:32:37 +01:00
|
|
|
// Error in sub function call
|
2020-12-30 14:12:51 +01:00
|
|
|
EvalAltResult::ErrorInFunctionCall(name, src, err, _) => {
|
2021-02-20 07:32:37 +01:00
|
|
|
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, state, err, pos)
|
2020-11-20 09:52:28 +01:00
|
|
|
}
|
|
|
|
// System errors are passed straight-through
|
2021-02-20 07:32:37 +01:00
|
|
|
mut err if err.is_system_exception() => Err(Box::new({
|
|
|
|
err.set_position(pos);
|
|
|
|
err
|
|
|
|
})),
|
2020-11-20 09:52:28 +01:00
|
|
|
// Other errors are wrapped in `ErrorInFunctionCall`
|
2021-02-20 07:32:37 +01:00
|
|
|
_ => make_error(fn_def.name.to_string(), fn_def, state, err, pos),
|
2020-11-20 09:52:28 +01:00
|
|
|
});
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
// Remove all local variables
|
|
|
|
scope.rewind(prev_scope_len);
|
|
|
|
mods.truncate(prev_mods_len);
|
|
|
|
state.scope_level = orig_scope_level;
|
|
|
|
|
2021-02-07 08:41:40 +01:00
|
|
|
if unified {
|
2021-02-07 10:56:29 +01:00
|
|
|
state.pop_fn_resolution_cache();
|
2021-02-06 15:16:05 +01:00
|
|
|
}
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
result
|
|
|
|
}
|
|
|
|
|
|
|
|
// Has a system function an override?
|
2020-12-29 03:41:20 +01:00
|
|
|
#[inline(always)]
|
2020-10-05 04:27:31 +02:00
|
|
|
pub(crate) fn has_override_by_name_and_arguments(
|
|
|
|
&self,
|
2020-11-22 08:41:55 +01:00
|
|
|
mods: Option<&Imports>,
|
2021-02-07 08:09:27 +01:00
|
|
|
state: Option<&mut State>,
|
2020-10-20 04:54:32 +02:00
|
|
|
lib: &[&Module],
|
2020-11-10 16:26:50 +01:00
|
|
|
fn_name: &str,
|
2020-10-19 13:11:55 +02:00
|
|
|
arg_types: impl AsRef<[TypeId]>,
|
2020-10-05 04:27:31 +02:00
|
|
|
pub_only: bool,
|
|
|
|
) -> bool {
|
2020-10-19 13:11:55 +02:00
|
|
|
let arg_types = arg_types.as_ref();
|
2020-11-10 16:26:50 +01:00
|
|
|
let hash_fn = calc_native_fn_hash(empty(), fn_name, arg_types.iter().cloned());
|
|
|
|
let hash_script = calc_script_fn_hash(empty(), fn_name, arg_types.len());
|
2020-10-05 04:27:31 +02:00
|
|
|
|
2021-02-07 08:09:27 +01:00
|
|
|
self.has_override(mods, state, lib, hash_fn, hash_script, pub_only)
|
2020-10-05 04:27:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Has a system function an override?
|
2020-10-08 16:25:50 +02:00
|
|
|
#[inline(always)]
|
2020-10-05 04:27:31 +02:00
|
|
|
pub(crate) fn has_override(
|
|
|
|
&self,
|
2020-11-22 08:41:55 +01:00
|
|
|
mods: Option<&Imports>,
|
2021-02-07 08:09:27 +01:00
|
|
|
mut state: Option<&mut State>,
|
2020-10-20 04:54:32 +02:00
|
|
|
lib: &[&Module],
|
2020-12-24 09:32:43 +01:00
|
|
|
hash_fn: Option<NonZeroU64>,
|
|
|
|
hash_script: Option<NonZeroU64>,
|
2020-10-05 04:27:31 +02:00
|
|
|
pub_only: bool,
|
|
|
|
) -> bool {
|
2021-02-07 08:09:27 +01:00
|
|
|
// Check if it is already in the cache
|
|
|
|
if let Some(state) = state.as_mut() {
|
|
|
|
if let Some(hash) = hash_script {
|
2021-02-07 10:56:29 +01:00
|
|
|
match state.fn_resolution_cache().map_or(None, |c| c.get(&hash)) {
|
2021-02-07 08:09:27 +01:00
|
|
|
Some(v) => return v.is_some(),
|
|
|
|
None => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(hash) = hash_fn {
|
2021-02-07 10:56:29 +01:00
|
|
|
match state.fn_resolution_cache().map_or(None, |c| c.get(&hash)) {
|
2021-02-07 08:09:27 +01:00
|
|
|
Some(v) => return v.is_some(),
|
|
|
|
None => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
// First check script-defined functions
|
2021-02-07 08:09:27 +01:00
|
|
|
let r = hash_script.map_or(false, |hash| lib.iter().any(|&m| m.contains_fn(hash, pub_only)))
|
|
|
|
//|| hash_fn.map_or(false, |hash| lib.iter().any(|&m| m.contains_fn(hash, pub_only)))
|
2020-07-27 06:52:32 +02:00
|
|
|
// Then check registered functions
|
2021-02-07 08:09:27 +01:00
|
|
|
//|| hash_script.map_or(false, |hash| self.global_namespace.contains_fn(hash, pub_only))
|
|
|
|
|| hash_fn.map_or(false, |hash| self.global_namespace.contains_fn(hash, false))
|
2020-07-27 06:52:32 +02:00
|
|
|
// Then check packages
|
2021-02-07 08:09:27 +01:00
|
|
|
|| hash_script.map_or(false, |hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false)))
|
|
|
|
|| hash_fn.map_or(false, |hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false)))
|
2020-11-16 07:07:48 +01:00
|
|
|
// Then check imported modules
|
2021-02-07 08:09:27 +01:00
|
|
|
|| hash_script.map_or(false, |hash| mods.map_or(false, |m| m.contains_fn(hash)))
|
|
|
|
|| hash_fn.map_or(false, |hash| mods.map_or(false, |m| m.contains_fn(hash)));
|
|
|
|
|
|
|
|
// If there is no override, put that information into the cache
|
|
|
|
if !r {
|
|
|
|
if let Some(state) = state.as_mut() {
|
|
|
|
if let Some(hash) = hash_script {
|
2021-02-07 10:56:29 +01:00
|
|
|
state.fn_resolution_cache_mut().insert(hash, None);
|
2021-02-07 08:09:27 +01:00
|
|
|
}
|
|
|
|
if let Some(hash) = hash_fn {
|
2021-02-07 10:56:29 +01:00
|
|
|
state.fn_resolution_cache_mut().insert(hash, None);
|
2021-02-07 08:09:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
2020-07-31 06:11:16 +02:00
|
|
|
/// Perform an actual function call, native Rust or scripted, taking care of special functions.
|
2020-07-23 12:40:42 +02:00
|
|
|
///
|
2021-01-02 16:30:10 +01:00
|
|
|
/// # WARNING
|
2020-07-23 12:40:42 +02:00
|
|
|
///
|
|
|
|
/// 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,
|
2020-11-06 12:17:07 +01:00
|
|
|
mods: &mut Imports,
|
2020-07-23 12:40:42 +02:00
|
|
|
state: &mut State,
|
2020-10-20 04:54:32 +02:00
|
|
|
lib: &[&Module],
|
2020-07-23 12:40:42 +02:00
|
|
|
fn_name: &str,
|
2020-12-24 09:32:43 +01:00
|
|
|
hash_script: Option<NonZeroU64>,
|
2020-07-23 12:40:42 +02:00
|
|
|
args: &mut FnCallArgs,
|
|
|
|
is_ref: bool,
|
2020-08-05 16:53:01 +02:00
|
|
|
_is_method: bool,
|
2020-07-27 06:52:32 +02:00
|
|
|
pub_only: bool,
|
2020-12-12 04:15:09 +01:00
|
|
|
pos: Position,
|
2020-10-22 06:26:44 +02:00
|
|
|
_capture_scope: Option<Scope>,
|
2020-11-20 09:52:28 +01:00
|
|
|
def_val: Option<&Dynamic>,
|
2020-08-05 16:53:01 +02:00
|
|
|
_level: usize,
|
2020-07-23 12:40:42 +02:00
|
|
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
2020-08-02 07:33:51 +02:00
|
|
|
// Check for data race.
|
2020-08-03 06:10:20 +02:00
|
|
|
if cfg!(not(feature = "no_closure")) {
|
2020-08-02 07:33:51 +02:00
|
|
|
ensure_no_data_race(fn_name, args, is_ref)?;
|
|
|
|
}
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
|
|
|
let arg_types = args.iter().map(|a| a.type_id());
|
2021-02-11 05:13:30 +01:00
|
|
|
let hash_fn = calc_native_fn_hash(empty(), fn_name, arg_types).unwrap();
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
match fn_name {
|
|
|
|
// type_of
|
2020-07-27 06:52:32 +02:00
|
|
|
KEYWORD_TYPE_OF
|
2020-11-16 07:07:48 +01:00
|
|
|
if args.len() == 1
|
2021-02-07 08:09:27 +01:00
|
|
|
&& !self.has_override(
|
|
|
|
Some(mods),
|
|
|
|
Some(state),
|
|
|
|
lib,
|
2021-02-11 05:13:30 +01:00
|
|
|
Some(hash_fn),
|
2021-02-07 08:09:27 +01:00
|
|
|
hash_script,
|
|
|
|
pub_only,
|
|
|
|
) =>
|
2020-07-27 06:52:32 +02:00
|
|
|
{
|
2020-07-23 12:40:42 +02:00
|
|
|
Ok((
|
|
|
|
self.map_type_name(args[0].type_name()).to_string().into(),
|
|
|
|
false,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2020-10-17 12:18:29 +02:00
|
|
|
// Fn/eval - reaching this point it must be a method-style call, mostly like redirected
|
|
|
|
// by a function pointer so it isn't caught at parse time.
|
2020-10-17 14:01:31 +02:00
|
|
|
KEYWORD_FN_PTR | KEYWORD_EVAL
|
2020-11-16 07:07:48 +01:00
|
|
|
if args.len() == 1
|
2021-02-07 08:09:27 +01:00
|
|
|
&& !self.has_override(
|
|
|
|
Some(mods),
|
|
|
|
Some(state),
|
|
|
|
lib,
|
2021-02-11 05:13:30 +01:00
|
|
|
Some(hash_fn),
|
2021-02-07 08:09:27 +01:00
|
|
|
hash_script,
|
|
|
|
pub_only,
|
|
|
|
) =>
|
2020-10-17 14:01:31 +02:00
|
|
|
{
|
|
|
|
EvalAltResult::ErrorRuntime(
|
|
|
|
format!(
|
|
|
|
"'{}' should not be called in method style. Try {}(...);",
|
|
|
|
fn_name, fn_name
|
2020-10-20 12:09:26 +02:00
|
|
|
)
|
|
|
|
.into(),
|
2020-12-12 04:15:09 +01:00
|
|
|
pos,
|
2020-10-17 14:01:31 +02:00
|
|
|
)
|
|
|
|
.into()
|
|
|
|
}
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2020-07-30 12:18:28 +02:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
2021-02-11 05:13:30 +01:00
|
|
|
_ if hash_script.is_some() => {
|
2020-12-24 09:32:43 +01:00
|
|
|
let hash_script = hash_script.unwrap();
|
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
// Check if script function access already in the cache
|
2021-02-07 08:09:27 +01:00
|
|
|
let (func, source) = state
|
2021-02-07 10:56:29 +01:00
|
|
|
.fn_resolution_cache_mut()
|
2021-02-07 08:09:27 +01:00
|
|
|
.entry(hash_script)
|
|
|
|
.or_insert_with(|| {
|
|
|
|
lib.iter()
|
|
|
|
.find_map(|&m| {
|
|
|
|
m.get_fn(hash_script, pub_only)
|
|
|
|
.map(|f| (f.clone(), m.id_raw().cloned()))
|
|
|
|
})
|
|
|
|
//.or_else(|| self.global_namespace.get_fn(hash_script, pub_only))
|
|
|
|
.or_else(|| {
|
|
|
|
self.global_modules.iter().find_map(|m| {
|
|
|
|
m.get_fn(hash_script, false)
|
|
|
|
.map(|f| (f.clone(), m.id_raw().cloned()))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
//.or_else(|| mods.iter().find_map(|(_, m)| m.get_qualified_fn(hash_script).map(|f| (f, m.id_raw().clone()))))
|
2020-12-22 15:36:30 +01:00
|
|
|
})
|
2021-02-07 08:09:27 +01:00
|
|
|
.as_ref()
|
2021-02-11 05:13:30 +01:00
|
|
|
.map(|(f, s)| (Some(f.clone()), s.clone()))
|
|
|
|
.unwrap_or((None, None));
|
|
|
|
|
|
|
|
if let Some(func) = func {
|
|
|
|
// Script function call
|
|
|
|
assert!(func.is_script());
|
|
|
|
|
|
|
|
let func = func.get_fn_def();
|
|
|
|
|
|
|
|
let scope: &mut Scope = &mut Default::default();
|
|
|
|
|
|
|
|
// Move captured variables into scope
|
|
|
|
#[cfg(not(feature = "no_closure"))]
|
|
|
|
if let Some(captured) = _capture_scope {
|
|
|
|
if !func.externals.is_empty() {
|
|
|
|
captured
|
|
|
|
.into_iter()
|
|
|
|
.filter(|(name, _, _)| func.externals.iter().any(|ex| ex == name))
|
|
|
|
.for_each(|(name, value, _)| {
|
|
|
|
// Consume the scope values.
|
|
|
|
scope.push_dynamic(name, value);
|
|
|
|
});
|
|
|
|
}
|
2020-09-25 13:07:24 +02:00
|
|
|
}
|
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
let result = if _is_method {
|
|
|
|
// Method call of script function - map first argument to `this`
|
|
|
|
let (first, rest) = args.split_first_mut().unwrap();
|
2020-12-21 15:04:46 +01:00
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
let orig_source = mem::take(&mut state.source);
|
|
|
|
state.source = source;
|
2020-12-21 15:04:46 +01:00
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
let level = _level + 1;
|
2020-12-29 05:29:45 +01:00
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
let result = self.call_script_fn(
|
|
|
|
scope,
|
|
|
|
mods,
|
|
|
|
state,
|
|
|
|
lib,
|
|
|
|
&mut Some(*first),
|
|
|
|
func,
|
|
|
|
rest,
|
|
|
|
pos,
|
|
|
|
level,
|
|
|
|
);
|
2020-12-21 15:04:46 +01:00
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
// Restore the original source
|
|
|
|
state.source = orig_source;
|
2020-12-21 15:04:46 +01:00
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
result?
|
|
|
|
} else {
|
|
|
|
// Normal call of script function
|
|
|
|
// The first argument is a reference?
|
|
|
|
let mut backup: ArgBackup = Default::default();
|
|
|
|
backup.change_first_arg_to_copy(is_ref, args);
|
2020-07-30 12:18:28 +02:00
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
let orig_source = mem::take(&mut state.source);
|
|
|
|
state.source = source;
|
2020-12-21 15:04:46 +01:00
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
let level = _level + 1;
|
2020-12-29 05:29:45 +01:00
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
let result = self.call_script_fn(
|
|
|
|
scope, mods, state, lib, &mut None, func, args, pos, level,
|
|
|
|
);
|
2020-07-30 12:18:28 +02:00
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
// Restore the original source
|
|
|
|
state.source = orig_source;
|
2020-12-21 15:04:46 +01:00
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
// Restore the original reference
|
|
|
|
backup.restore_first_arg(args);
|
2020-09-25 13:07:24 +02:00
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
result?
|
|
|
|
};
|
2020-07-30 12:18:28 +02:00
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
Ok((result, false))
|
|
|
|
} else {
|
|
|
|
// Native function call
|
|
|
|
self.call_native_fn(
|
|
|
|
mods, state, lib, fn_name, hash_fn, args, is_ref, pub_only, pos, def_val,
|
|
|
|
)
|
|
|
|
}
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
2020-09-11 16:32:59 +02:00
|
|
|
|
2021-02-11 05:13:30 +01:00
|
|
|
// Native function call
|
2020-07-31 06:11:16 +02:00
|
|
|
_ => self.call_native_fn(
|
2021-02-11 05:13:30 +01:00
|
|
|
mods, state, lib, fn_name, hash_fn, args, is_ref, pub_only, pos, def_val,
|
2020-07-31 06:11:16 +02:00
|
|
|
),
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-07 10:06:33 +01:00
|
|
|
/// 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.
|
2020-10-20 04:54:32 +02:00
|
|
|
#[inline]
|
2021-02-07 10:06:33 +01:00
|
|
|
pub(crate) fn eval_global_statements<'a>(
|
2020-10-20 04:54:32 +02:00
|
|
|
&self,
|
|
|
|
scope: &mut Scope,
|
|
|
|
mods: &mut Imports,
|
2020-12-20 16:25:11 +01:00
|
|
|
state: &mut State,
|
2020-10-20 04:54:32 +02:00
|
|
|
statements: impl IntoIterator<Item = &'a Stmt>,
|
|
|
|
lib: &[&Module],
|
2020-12-29 05:29:45 +01:00
|
|
|
level: usize,
|
2020-12-20 16:25:11 +01:00
|
|
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
2021-02-07 10:06:33 +01:00
|
|
|
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),
|
2020-12-28 07:21:13 +01:00
|
|
|
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(
|
2020-07-23 12:40:42 +02:00
|
|
|
&self,
|
|
|
|
scope: &mut Scope,
|
|
|
|
mods: &mut Imports,
|
|
|
|
state: &mut State,
|
2020-10-20 04:54:32 +02:00
|
|
|
lib: &[&Module],
|
2020-10-03 17:27:30 +02:00
|
|
|
script: &str,
|
2020-12-20 16:25:11 +01:00
|
|
|
pos: Position,
|
2020-12-29 05:29:45 +01:00
|
|
|
level: usize,
|
2020-07-23 12:40:42 +02:00
|
|
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
2020-12-20 16:25:11 +01:00
|
|
|
self.inc_operations(state, 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
|
|
|
}
|
|
|
|
|
2020-07-23 12:40:42 +02: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(
|
|
|
|
&Default::default(),
|
2020-07-23 12:40:42 +02:00
|
|
|
&[script],
|
|
|
|
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 {
|
2021-01-03 13:54:08 +01:00
|
|
|
return Err(ParseErrorType::FnWrongDefinition.into());
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Evaluate the AST
|
2021-02-07 10:56:29 +01:00
|
|
|
let mut new_state: State = Default::default();
|
|
|
|
new_state.source = state.source.clone();
|
|
|
|
new_state.operations = state.operations;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2020-12-29 05:29:45 +01:00
|
|
|
let result =
|
2021-02-07 10:56:29 +01:00
|
|
|
self.eval_global_statements(scope, mods, &mut new_state, ast.statements(), lib, level);
|
2020-12-20 16:46:46 +01:00
|
|
|
|
|
|
|
state.operations = new_state.operations;
|
2021-02-18 07:33:12 +01:00
|
|
|
|
2020-12-21 10:39:37 +01:00
|
|
|
result
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Call a dot method.
|
2020-07-26 09:53:22 +02:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-07-23 12:40:42 +02:00
|
|
|
pub(crate) fn make_method_call(
|
|
|
|
&self,
|
2020-11-06 12:17:07 +01:00
|
|
|
mods: &mut Imports,
|
2020-07-23 12:40:42 +02:00
|
|
|
state: &mut State,
|
2020-10-20 04:54:32 +02:00
|
|
|
lib: &[&Module],
|
2020-11-10 16:26:50 +01:00
|
|
|
fn_name: &str,
|
2020-12-24 09:32:43 +01:00
|
|
|
hash_script: Option<NonZeroU64>,
|
2020-11-16 09:28:04 +01:00
|
|
|
target: &mut crate::engine::Target,
|
2020-10-15 17:30:30 +02:00
|
|
|
mut call_args: StaticVec<Dynamic>,
|
2020-11-20 09:52:28 +01:00
|
|
|
def_val: Option<&Dynamic>,
|
2020-07-27 06:52:32 +02:00
|
|
|
pub_only: bool,
|
2020-12-12 04:15:09 +01:00
|
|
|
pos: Position,
|
2020-07-23 12:40:42 +02:00
|
|
|
level: usize,
|
|
|
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
|
|
|
let is_ref = target.is_ref();
|
|
|
|
|
|
|
|
// Get a reference to the mutation target Dynamic
|
|
|
|
let obj = target.as_mut();
|
2020-11-10 16:26:50 +01:00
|
|
|
let mut fn_name = fn_name;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2020-11-10 16:26:50 +01:00
|
|
|
let (result, updated) = if fn_name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
|
2020-07-23 12:40:42 +02:00
|
|
|
// FnPtr call
|
2020-07-27 06:30:09 +02:00
|
|
|
let fn_ptr = obj.read_lock::<FnPtr>().unwrap();
|
2020-07-23 12:40:42 +02:00
|
|
|
// Redirect function name
|
|
|
|
let fn_name = fn_ptr.fn_name();
|
2020-10-17 07:49:16 +02:00
|
|
|
let args_len = call_args.len() + fn_ptr.curry().len();
|
2020-07-23 12:40:42 +02:00
|
|
|
// Recalculate hash
|
2020-12-24 09:32:43 +01:00
|
|
|
let hash = hash_script.and_then(|_| calc_script_fn_hash(empty(), fn_name, args_len));
|
2020-07-23 12:40:42 +02:00
|
|
|
// Arguments are passed as-is, adding the curried arguments
|
2020-10-17 07:49:16 +02:00
|
|
|
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
2020-07-23 12:40:42 +02:00
|
|
|
let mut arg_values = curry
|
|
|
|
.iter_mut()
|
2020-10-15 17:30:30 +02:00
|
|
|
.chain(call_args.iter_mut())
|
2020-07-23 12:40:42 +02:00
|
|
|
.collect::<StaticVec<_>>();
|
|
|
|
let args = arg_values.as_mut();
|
|
|
|
|
|
|
|
// Map it to name(args) in function-call style
|
|
|
|
self.exec_fn_call(
|
2020-12-12 04:15:09 +01:00
|
|
|
mods, state, lib, fn_name, hash, args, false, false, pub_only, pos, None, def_val,
|
|
|
|
level,
|
2020-07-23 12:40:42 +02:00
|
|
|
)
|
2020-11-10 16:26:50 +01:00
|
|
|
} else if fn_name == KEYWORD_FN_PTR_CALL
|
2020-10-15 17:30:30 +02:00
|
|
|
&& call_args.len() > 0
|
|
|
|
&& call_args[0].is::<FnPtr>()
|
|
|
|
{
|
2020-07-23 12:40:42 +02:00
|
|
|
// FnPtr call on object
|
2020-10-15 17:30:30 +02:00
|
|
|
let fn_ptr = call_args.remove(0).cast::<FnPtr>();
|
2020-07-23 12:40:42 +02:00
|
|
|
// Redirect function name
|
2020-10-17 07:49:16 +02:00
|
|
|
let fn_name = fn_ptr.fn_name();
|
|
|
|
let args_len = call_args.len() + fn_ptr.curry().len();
|
2020-07-23 12:40:42 +02:00
|
|
|
// Recalculate hash
|
2020-12-24 09:32:43 +01:00
|
|
|
let hash = hash_script.and_then(|_| calc_script_fn_hash(empty(), fn_name, args_len));
|
2020-07-23 12:40:42 +02:00
|
|
|
// Replace the first argument with the object pointer, adding the curried arguments
|
2020-10-17 07:49:16 +02:00
|
|
|
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
2020-07-23 12:40:42 +02:00
|
|
|
let mut arg_values = once(obj)
|
|
|
|
.chain(curry.iter_mut())
|
2020-10-15 17:30:30 +02:00
|
|
|
.chain(call_args.iter_mut())
|
2020-07-23 12:40:42 +02:00
|
|
|
.collect::<StaticVec<_>>();
|
|
|
|
let args = arg_values.as_mut();
|
|
|
|
|
|
|
|
// Map it to name(args) in function-call style
|
|
|
|
self.exec_fn_call(
|
2020-12-12 04:15:09 +01:00
|
|
|
mods, state, lib, fn_name, hash, args, is_ref, true, pub_only, pos, None, def_val,
|
|
|
|
level,
|
2020-07-23 12:40:42 +02:00
|
|
|
)
|
2020-11-10 16:26:50 +01:00
|
|
|
} else if fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
|
2020-07-23 12:40:42 +02:00
|
|
|
// Curry call
|
2020-07-27 06:30:09 +02:00
|
|
|
let fn_ptr = obj.read_lock::<FnPtr>().unwrap();
|
2020-07-23 12:40:42 +02:00
|
|
|
Ok((
|
|
|
|
FnPtr::new_unchecked(
|
|
|
|
fn_ptr.get_fn_name().clone(),
|
|
|
|
fn_ptr
|
|
|
|
.curry()
|
|
|
|
.iter()
|
|
|
|
.cloned()
|
2020-10-15 17:30:30 +02:00
|
|
|
.chain(call_args.into_iter())
|
2020-07-23 12:40:42 +02:00
|
|
|
.collect(),
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
false,
|
|
|
|
))
|
2020-10-03 10:25:58 +02:00
|
|
|
} else if {
|
|
|
|
#[cfg(not(feature = "no_closure"))]
|
|
|
|
{
|
2020-11-16 09:28:04 +01:00
|
|
|
fn_name == crate::engine::KEYWORD_IS_SHARED && call_args.is_empty()
|
2020-10-03 10:25:58 +02:00
|
|
|
}
|
|
|
|
#[cfg(feature = "no_closure")]
|
|
|
|
false
|
|
|
|
} {
|
2020-08-05 11:55:36 +02:00
|
|
|
// is_shared call
|
2020-07-31 16:30:23 +02:00
|
|
|
Ok((target.is_shared().into(), false))
|
2020-07-23 12:40:42 +02:00
|
|
|
} else {
|
2020-10-03 10:25:58 +02:00
|
|
|
let _redirected;
|
2020-08-05 11:55:36 +02:00
|
|
|
let mut hash = hash_script;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
// Check if it is a map method call in OOP style
|
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-07-27 06:30:09 +02:00
|
|
|
if let Some(map) = obj.read_lock::<Map>() {
|
2020-11-10 16:26:50 +01:00
|
|
|
if let Some(val) = map.get(fn_name) {
|
2020-08-04 12:38:33 +02:00
|
|
|
if let Some(fn_ptr) = val.read_lock::<FnPtr>() {
|
2020-07-23 12:40:42 +02:00
|
|
|
// Remap the function name
|
2020-10-03 10:25:58 +02:00
|
|
|
_redirected = fn_ptr.get_fn_name().clone();
|
2020-11-10 16:26:50 +01:00
|
|
|
fn_name = &_redirected;
|
2020-08-04 12:38:33 +02:00
|
|
|
// Add curried arguments
|
2020-10-17 07:49:16 +02:00
|
|
|
fn_ptr
|
|
|
|
.curry()
|
|
|
|
.iter()
|
|
|
|
.cloned()
|
|
|
|
.enumerate()
|
|
|
|
.for_each(|(i, v)| call_args.insert(i, v));
|
2020-08-04 12:38:33 +02:00
|
|
|
// Recalculate the hash based on the new function name and new arguments
|
2020-12-24 09:32:43 +01:00
|
|
|
hash = hash_script
|
|
|
|
.and_then(|_| calc_script_fn_hash(empty(), fn_name, call_args.len()));
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-12-24 09:32:43 +01:00
|
|
|
if hash_script.is_none() {
|
|
|
|
hash = None;
|
2020-07-30 12:18:28 +02:00
|
|
|
}
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
// Attached object pointer in front of the arguments
|
2020-10-15 17:30:30 +02:00
|
|
|
let mut arg_values = once(obj)
|
|
|
|
.chain(call_args.iter_mut())
|
|
|
|
.collect::<StaticVec<_>>();
|
2020-07-23 12:40:42 +02:00
|
|
|
let args = arg_values.as_mut();
|
|
|
|
|
|
|
|
self.exec_fn_call(
|
2020-12-12 04:15:09 +01:00
|
|
|
mods, state, lib, fn_name, hash, args, is_ref, true, pub_only, pos, None, def_val,
|
|
|
|
level,
|
2020-07-23 12:40:42 +02:00
|
|
|
)
|
2020-07-27 06:52:32 +02:00
|
|
|
}?;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2020-10-04 04:40:44 +02:00
|
|
|
// Propagate the changed value back to the source if necessary
|
|
|
|
if updated {
|
|
|
|
target.propagate_changed_value();
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok((result, updated))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Call a function in normal function-call style.
|
|
|
|
pub(crate) fn make_function_call(
|
|
|
|
&self,
|
|
|
|
scope: &mut Scope,
|
|
|
|
mods: &mut Imports,
|
|
|
|
state: &mut State,
|
2020-10-20 04:54:32 +02:00
|
|
|
lib: &[&Module],
|
2020-07-23 12:40:42 +02:00
|
|
|
this_ptr: &mut Option<&mut Dynamic>,
|
2020-11-10 16:26:50 +01:00
|
|
|
fn_name: &str,
|
2020-10-19 13:11:55 +02:00
|
|
|
args_expr: impl AsRef<[Expr]>,
|
2020-11-20 09:52:28 +01:00
|
|
|
def_val: Option<&Dynamic>,
|
2020-12-24 09:32:43 +01:00
|
|
|
mut hash_script: Option<NonZeroU64>,
|
2020-07-27 06:52:32 +02:00
|
|
|
pub_only: bool,
|
2020-12-12 04:15:09 +01:00
|
|
|
pos: Position,
|
2020-10-22 06:26:44 +02:00
|
|
|
capture_scope: bool,
|
2020-07-23 12:40:42 +02:00
|
|
|
level: usize,
|
|
|
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
2020-10-19 13:11:55 +02:00
|
|
|
let args_expr = args_expr.as_ref();
|
|
|
|
|
2021-02-03 12:14:26 +01:00
|
|
|
// Handle call() - Redirect function call
|
|
|
|
let redirected;
|
|
|
|
let mut args_expr = args_expr.as_ref();
|
|
|
|
let mut curry = StaticVec::new();
|
|
|
|
let mut name = fn_name;
|
|
|
|
|
|
|
|
if name == KEYWORD_FN_PTR_CALL
|
|
|
|
&& args_expr.len() >= 1
|
2021-02-07 08:09:27 +01:00
|
|
|
&& !self.has_override(Some(mods), Some(state), lib, None, hash_script, pub_only)
|
2021-02-03 12:14:26 +01:00
|
|
|
{
|
|
|
|
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
|
|
|
|
|
|
|
if !fn_ptr.is::<FnPtr>() {
|
|
|
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
|
|
|
self.map_type_name(fn_ptr.type_name()),
|
|
|
|
args_expr[0].position(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
let fn_ptr = fn_ptr.cast::<FnPtr>();
|
|
|
|
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.as_ref()[1..];
|
|
|
|
|
|
|
|
// Recalculate hash
|
|
|
|
let args_len = args_expr.len() + curry.len();
|
|
|
|
hash_script = calc_script_fn_hash(empty(), name, args_len);
|
|
|
|
}
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
// Handle Fn()
|
2021-02-03 12:14:26 +01:00
|
|
|
if name == KEYWORD_FN_PTR && args_expr.len() == 1 {
|
|
|
|
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2021-02-07 08:09:27 +01:00
|
|
|
if !self.has_override(Some(mods), Some(state), lib, hash_fn, hash_script, pub_only) {
|
2020-07-23 12:40:42 +02:00
|
|
|
// Fn - only in function call style
|
2020-10-17 07:49:16 +02:00
|
|
|
return self
|
|
|
|
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?
|
2020-07-23 12:40:42 +02:00
|
|
|
.take_immutable_string()
|
2020-10-05 07:45:57 +02:00
|
|
|
.map_err(|typ| {
|
2020-10-17 07:49:16 +02:00
|
|
|
self.make_type_mismatch_err::<ImmutableString>(typ, args_expr[0].position())
|
2020-10-05 07:45:57 +02:00
|
|
|
})
|
2020-07-23 12:40:42 +02:00
|
|
|
.and_then(|s| FnPtr::try_from(s))
|
2020-07-30 07:28:25 +02:00
|
|
|
.map(Into::<Dynamic>::into)
|
2020-10-17 07:49:16 +02:00
|
|
|
.map_err(|err| err.fill_position(args_expr[0].position()));
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle curry()
|
2021-02-03 12:14:26 +01:00
|
|
|
if name == KEYWORD_FN_PTR_CURRY && args_expr.len() > 1 {
|
2020-10-17 07:49:16 +02:00
|
|
|
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2020-10-15 17:30:30 +02:00
|
|
|
if !fn_ptr.is::<FnPtr>() {
|
2020-10-05 07:45:57 +02:00
|
|
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
2020-10-15 17:30:30 +02:00
|
|
|
self.map_type_name(fn_ptr.type_name()),
|
2020-10-17 07:49:16 +02:00
|
|
|
args_expr[0].position(),
|
2020-10-03 17:27:30 +02:00
|
|
|
));
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
2021-02-03 12:14:26 +01:00
|
|
|
let (name, mut fn_curry) = fn_ptr.cast::<FnPtr>().take_data();
|
2020-10-15 17:30:30 +02:00
|
|
|
|
|
|
|
// Append the new curried arguments to the existing list.
|
2020-10-17 07:49:16 +02:00
|
|
|
|
2020-10-15 17:30:30 +02:00
|
|
|
args_expr
|
2020-07-23 12:40:42 +02:00
|
|
|
.iter()
|
|
|
|
.skip(1)
|
2020-10-15 17:30:30 +02:00
|
|
|
.try_for_each(|expr| -> Result<(), Box<EvalAltResult>> {
|
2020-10-15 17:44:05 +02:00
|
|
|
fn_curry.push(self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?);
|
2020-10-15 17:30:30 +02:00
|
|
|
Ok(())
|
|
|
|
})?;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2021-02-03 12:14:26 +01:00
|
|
|
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
2020-07-31 12:43:34 +02:00
|
|
|
// Handle is_shared()
|
2020-10-03 10:25:58 +02:00
|
|
|
#[cfg(not(feature = "no_closure"))]
|
2021-02-03 12:14:26 +01:00
|
|
|
if name == crate::engine::KEYWORD_IS_SHARED && args_expr.len() == 1 {
|
2020-10-17 07:49:16 +02:00
|
|
|
let value = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2020-07-31 12:43:34 +02:00
|
|
|
return Ok(value.is_shared().into());
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
2020-10-03 10:25:58 +02:00
|
|
|
// Handle is_def_var()
|
|
|
|
if name == KEYWORD_IS_DEF_VAR && args_expr.len() == 1 {
|
2020-10-28 15:18:44 +01:00
|
|
|
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
|
2020-10-03 10:25:58 +02:00
|
|
|
|
2021-02-07 08:09:27 +01:00
|
|
|
if !self.has_override(Some(mods), Some(state), lib, hash_fn, hash_script, pub_only) {
|
2020-10-17 07:49:16 +02:00
|
|
|
let var_name =
|
|
|
|
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
2020-10-15 17:30:30 +02:00
|
|
|
let var_name = var_name.as_str().map_err(|err| {
|
2020-10-17 07:49:16 +02:00
|
|
|
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
2020-10-05 07:45:57 +02:00
|
|
|
})?;
|
2020-10-27 04:30:38 +01:00
|
|
|
return Ok(scope.contains(var_name).into());
|
2020-10-03 10:25:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-30 12:18:28 +02:00
|
|
|
// Handle eval()
|
|
|
|
if name == KEYWORD_EVAL && args_expr.len() == 1 {
|
2020-10-28 15:18:44 +01:00
|
|
|
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
|
2020-07-30 12:18:28 +02:00
|
|
|
|
2021-02-18 07:33:12 +01:00
|
|
|
let script_expr = &args_expr[0];
|
|
|
|
|
2021-02-07 08:09:27 +01:00
|
|
|
if !self.has_override(Some(mods), Some(state), lib, hash_fn, hash_script, pub_only) {
|
2021-02-18 07:33:12 +01:00
|
|
|
let script_pos = script_expr.position();
|
|
|
|
|
2020-07-30 12:18:28 +02:00
|
|
|
// eval - only in function call style
|
|
|
|
let prev_len = scope.len();
|
2020-10-17 07:49:16 +02:00
|
|
|
let script =
|
2021-02-18 07:33:12 +01:00
|
|
|
self.eval_expr(scope, mods, state, lib, this_ptr, script_expr, level)?;
|
2020-10-15 17:30:30 +02:00
|
|
|
let script = script.as_str().map_err(|typ| {
|
2021-02-18 07:33:12 +01:00
|
|
|
self.make_type_mismatch_err::<ImmutableString>(typ, script_pos)
|
2020-10-05 07:45:57 +02:00
|
|
|
})?;
|
2021-02-18 07:33:12 +01:00
|
|
|
let result = self.eval_script_expr_in_place(
|
|
|
|
scope,
|
|
|
|
mods,
|
|
|
|
state,
|
|
|
|
lib,
|
|
|
|
script,
|
|
|
|
script_pos,
|
|
|
|
level + 1,
|
|
|
|
);
|
2020-07-30 12:18:28 +02:00
|
|
|
|
2020-07-31 06:11:16 +02:00
|
|
|
// IMPORTANT! If the eval defines new variables in the current scope,
|
|
|
|
// all variable offsets from this point on will be mis-aligned.
|
2020-07-30 12:18:28 +02:00
|
|
|
if scope.len() != prev_len {
|
|
|
|
state.always_search = true;
|
|
|
|
}
|
|
|
|
|
2021-02-18 07:33:12 +01:00
|
|
|
return result.map_err(|err| {
|
|
|
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
|
|
|
KEYWORD_EVAL.to_string(),
|
|
|
|
state
|
|
|
|
.source
|
|
|
|
.as_ref()
|
|
|
|
.map_or_else(|| "", |s| s.as_str())
|
|
|
|
.to_string(),
|
|
|
|
err,
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
});
|
2020-07-30 12:18:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Normal function call - except for Fn, curry, call and eval (handled above)
|
2020-07-23 12:40:42 +02:00
|
|
|
let mut arg_values: StaticVec<_>;
|
|
|
|
let mut args: StaticVec<_>;
|
|
|
|
let mut is_ref = 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
|
|
|
|
};
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
if args_expr.is_empty() && curry.is_empty() {
|
|
|
|
// No arguments
|
|
|
|
args = Default::default();
|
|
|
|
} 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
|
2020-10-25 15:08:02 +01:00
|
|
|
if curry.is_empty() && args_expr[0].get_variable_access(false).is_some() {
|
2020-07-23 12:40:42 +02:00
|
|
|
// func(x, ...) -> x.func(...)
|
2020-10-17 07:49:16 +02:00
|
|
|
arg_values = args_expr
|
|
|
|
.iter()
|
|
|
|
.skip(1)
|
|
|
|
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
|
|
|
.collect::<Result<_, _>>()?;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2020-12-26 06:05:57 +01:00
|
|
|
let (mut target, pos) =
|
2020-10-17 07:49:16 +02:00
|
|
|
self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
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();
|
|
|
|
}
|
2020-11-14 09:08:48 +01:00
|
|
|
|
2020-12-20 16:25:11 +01:00
|
|
|
self.inc_operations(state, pos)?;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2020-10-17 07:49:16 +02:00
|
|
|
args = if target.is_shared() || target.is_value() {
|
|
|
|
arg_values.insert(0, target.take_or_clone().flatten());
|
|
|
|
arg_values.iter_mut().collect()
|
|
|
|
} else {
|
|
|
|
// Turn it into a method call only if the object is not shared and not a simple value
|
|
|
|
is_ref = true;
|
|
|
|
once(target.take_ref().unwrap())
|
|
|
|
.chain(arg_values.iter_mut())
|
|
|
|
.collect()
|
|
|
|
};
|
|
|
|
} else {
|
2020-07-23 12:40:42 +02:00
|
|
|
// func(..., ...)
|
2020-10-17 07:49:16 +02:00
|
|
|
arg_values = args_expr
|
|
|
|
.iter()
|
|
|
|
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
|
|
|
.collect::<Result<_, _>>()?;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2020-10-17 07:49:16 +02:00
|
|
|
args = curry.iter_mut().chain(arg_values.iter_mut()).collect();
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let args = args.as_mut();
|
2020-07-30 12:18:28 +02:00
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
self.exec_fn_call(
|
2020-12-24 09:32:43 +01:00
|
|
|
mods,
|
|
|
|
state,
|
|
|
|
lib,
|
|
|
|
name,
|
|
|
|
hash_script,
|
|
|
|
args,
|
|
|
|
is_ref,
|
|
|
|
false,
|
|
|
|
pub_only,
|
|
|
|
pos,
|
|
|
|
capture,
|
|
|
|
def_val,
|
2020-12-12 04:15:09 +01:00
|
|
|
level,
|
2020-07-23 12:40:42 +02:00
|
|
|
)
|
|
|
|
.map(|(v, _)| v)
|
|
|
|
}
|
|
|
|
|
2020-11-10 16:26:50 +01:00
|
|
|
/// Call a namespace-qualified function in normal function-call style.
|
2020-07-23 12:40:42 +02:00
|
|
|
pub(crate) fn make_qualified_function_call(
|
|
|
|
&self,
|
|
|
|
scope: &mut Scope,
|
|
|
|
mods: &mut Imports,
|
|
|
|
state: &mut State,
|
2020-10-20 04:54:32 +02:00
|
|
|
lib: &[&Module],
|
2020-07-23 12:40:42 +02:00
|
|
|
this_ptr: &mut Option<&mut Dynamic>,
|
2020-11-10 16:26:50 +01:00
|
|
|
namespace: Option<&NamespaceRef>,
|
|
|
|
fn_name: &str,
|
2020-10-19 13:11:55 +02:00
|
|
|
args_expr: impl AsRef<[Expr]>,
|
2020-11-20 09:52:28 +01:00
|
|
|
def_val: Option<&Dynamic>,
|
2020-12-24 09:32:43 +01:00
|
|
|
hash_script: NonZeroU64,
|
2020-12-12 04:15:09 +01:00
|
|
|
pos: Position,
|
2020-07-23 12:40:42 +02:00
|
|
|
level: usize,
|
|
|
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
2020-10-19 13:11:55 +02:00
|
|
|
let args_expr = args_expr.as_ref();
|
|
|
|
|
2020-11-10 16:26:50 +01:00
|
|
|
let namespace = namespace.as_ref().unwrap();
|
2020-07-23 12:40:42 +02:00
|
|
|
let mut arg_values: StaticVec<_>;
|
2020-08-20 10:26:10 +02:00
|
|
|
let mut first_arg_value = None;
|
2020-07-23 12:40:42 +02:00
|
|
|
let mut args: StaticVec<_>;
|
|
|
|
|
|
|
|
if args_expr.is_empty() {
|
|
|
|
// No arguments
|
|
|
|
args = Default::default();
|
|
|
|
} else {
|
2020-11-10 16:26:50 +01:00
|
|
|
// See if the first argument is a variable (not namespace-qualified).
|
2020-07-23 12:40:42 +02:00
|
|
|
// If so, convert to method-call style in order to leverage potential
|
|
|
|
// &mut first argument and avoid cloning the value
|
2020-10-17 07:49:16 +02:00
|
|
|
if args_expr[0].get_variable_access(true).is_some() {
|
2020-07-23 12:40:42 +02:00
|
|
|
// func(x, ...) -> x.func(...)
|
2020-10-17 07:49:16 +02:00
|
|
|
arg_values = args_expr
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, expr)| {
|
|
|
|
// Skip the first argument
|
|
|
|
if i == 0 {
|
|
|
|
Ok(Default::default())
|
|
|
|
} else {
|
|
|
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Result<_, _>>()?;
|
|
|
|
|
|
|
|
// Get target reference to first argument
|
2020-12-26 06:05:57 +01:00
|
|
|
let (target, pos) =
|
2020-10-17 07:49:16 +02:00
|
|
|
self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2020-12-20 16:25:11 +01:00
|
|
|
self.inc_operations(state, pos)?;
|
2020-10-17 07:49:16 +02:00
|
|
|
|
|
|
|
if target.is_shared() || target.is_value() {
|
|
|
|
arg_values[0] = target.take_or_clone().flatten();
|
2020-07-23 12:40:42 +02:00
|
|
|
args = arg_values.iter_mut().collect();
|
2020-10-17 07:49:16 +02:00
|
|
|
} else {
|
|
|
|
let (first, rest) = arg_values.split_first_mut().unwrap();
|
|
|
|
first_arg_value = Some(first);
|
|
|
|
args = once(target.take_ref().unwrap())
|
|
|
|
.chain(rest.iter_mut())
|
|
|
|
.collect();
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
2020-10-17 07:49:16 +02:00
|
|
|
} else {
|
|
|
|
// func(..., ...) or func(mod::x, ...)
|
|
|
|
arg_values = args_expr
|
|
|
|
.iter()
|
|
|
|
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
|
|
|
.collect::<Result<_, _>>()?;
|
|
|
|
|
|
|
|
args = arg_values.iter_mut().collect();
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-10 16:26:50 +01:00
|
|
|
let module = search_imports(mods, state, namespace)?;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
// First search in script-defined functions (can override built-in)
|
|
|
|
let func = match module.get_qualified_fn(hash_script) {
|
2020-07-27 06:52:32 +02:00
|
|
|
// Then search in Rust functions
|
|
|
|
None => {
|
2020-12-20 16:25:11 +01:00
|
|
|
self.inc_operations(state, pos)?;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2020-11-10 16:26:50 +01:00
|
|
|
// Namespace-qualified Rust functions are indexed in two steps:
|
2020-07-23 12:40:42 +02:00
|
|
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
|
|
|
// i.e. qualifiers + function name + number of arguments.
|
|
|
|
// 2) Calculate a second hash with no qualifiers, empty function name,
|
2020-10-28 15:18:44 +01:00
|
|
|
// and the actual list of argument `TypeId`'.s
|
|
|
|
let hash_fn_args =
|
2020-12-24 09:32:43 +01:00
|
|
|
calc_native_fn_hash(empty(), "", args.iter().map(|a| a.type_id())).unwrap();
|
2020-12-24 11:43:04 +01:00
|
|
|
// 3) The two hashes are combined.
|
|
|
|
let hash_qualified_fn = combine_hashes(hash_script, hash_fn_args);
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
module.get_qualified_fn(hash_qualified_fn)
|
|
|
|
}
|
|
|
|
r => r,
|
|
|
|
};
|
|
|
|
|
2021-01-23 02:37:27 +01:00
|
|
|
// Clone first argument if the function is not a method after-all
|
|
|
|
if let Some(first) = first_arg_value {
|
|
|
|
if !func.map(|f| f.is_method()).unwrap_or(true) {
|
|
|
|
let first_val = args[0].clone();
|
|
|
|
args[0] = first;
|
|
|
|
*args[0] = first_val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
match func {
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
2020-07-27 06:52:32 +02:00
|
|
|
Some(f) if f.is_script() => {
|
2020-07-23 12:40:42 +02:00
|
|
|
let args = args.as_mut();
|
2020-10-20 04:54:32 +02:00
|
|
|
let new_scope = &mut Default::default();
|
2020-11-07 16:33:21 +01:00
|
|
|
let fn_def = f.get_fn_def().clone();
|
2020-12-21 15:04:46 +01:00
|
|
|
|
2021-01-03 04:25:25 +01:00
|
|
|
let mut source = module.id_raw().cloned();
|
2020-12-21 15:04:46 +01:00
|
|
|
mem::swap(&mut state.source, &mut source);
|
|
|
|
|
2020-12-29 05:29:45 +01:00
|
|
|
let level = level + 1;
|
|
|
|
|
2020-12-21 15:04:46 +01:00
|
|
|
let result = self.call_script_fn(
|
2020-12-12 04:15:09 +01:00
|
|
|
new_scope, mods, state, lib, &mut None, &fn_def, args, pos, level,
|
2020-12-21 15:04:46 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
state.source = source;
|
|
|
|
|
|
|
|
result
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
2021-01-21 14:49:25 +01:00
|
|
|
Some(f) if f.is_plugin_fn() => f.get_plugin_fn().clone().call(
|
|
|
|
(self, fn_name, module.id(), &*mods, lib).into(),
|
|
|
|
args.as_mut(),
|
|
|
|
),
|
2021-01-23 02:37:27 +01:00
|
|
|
Some(f) if f.is_native() => f.get_native_fn()(
|
|
|
|
(self, fn_name, module.id(), &*mods, lib).into(),
|
|
|
|
args.as_mut(),
|
|
|
|
),
|
2020-12-28 07:21:13 +01:00
|
|
|
Some(f) => unreachable!("unknown function type: {:?}", f),
|
2020-11-20 09:52:28 +01:00
|
|
|
None if def_val.is_some() => Ok(def_val.unwrap().clone()),
|
2020-08-06 04:17:32 +02:00
|
|
|
None => EvalAltResult::ErrorFunctionNotFound(
|
2020-07-27 06:52:32 +02:00
|
|
|
format!(
|
|
|
|
"{}{} ({})",
|
2020-11-10 16:26:50 +01:00
|
|
|
namespace,
|
|
|
|
fn_name,
|
2020-07-27 06:52:32 +02:00
|
|
|
args.iter()
|
|
|
|
.map(|a| if a.is::<ImmutableString>() {
|
|
|
|
"&str | ImmutableString | String"
|
|
|
|
} else {
|
|
|
|
self.map_type_name((*a).type_name())
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(", ")
|
|
|
|
),
|
2020-12-12 04:15:09 +01:00
|
|
|
pos,
|
2020-08-06 04:17:32 +02:00
|
|
|
)
|
|
|
|
.into(),
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Build in common binary operator implementations to avoid the cost of calling a registered function.
|
|
|
|
pub fn run_builtin_binary_op(
|
|
|
|
op: &str,
|
|
|
|
x: &Dynamic,
|
|
|
|
y: &Dynamic,
|
|
|
|
) -> Result<Option<Dynamic>, Box<EvalAltResult>> {
|
2021-02-19 04:47:26 +01:00
|
|
|
let first_type = x.type_id();
|
2020-10-10 16:13:55 +02:00
|
|
|
let second_type = y.type_id();
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2021-02-19 04:47:26 +01:00
|
|
|
let type_id = (first_type, second_type);
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
if let Some((x, y)) = if type_id == (TypeId::of::<FLOAT>(), TypeId::of::<FLOAT>()) {
|
|
|
|
Some((x.clone().cast::<FLOAT>(), y.clone().cast::<FLOAT>()))
|
|
|
|
} else if type_id == (TypeId::of::<FLOAT>(), TypeId::of::<INT>()) {
|
|
|
|
Some((x.clone().cast::<FLOAT>(), y.clone().cast::<INT>() as FLOAT))
|
|
|
|
} else if type_id == (TypeId::of::<INT>(), TypeId::of::<FLOAT>()) {
|
|
|
|
Some((x.clone().cast::<INT>() as FLOAT, y.clone().cast::<FLOAT>()))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
} {
|
|
|
|
match op {
|
|
|
|
"+" => return Ok(Some((x + y).into())),
|
|
|
|
"-" => return Ok(Some((x - y).into())),
|
|
|
|
"*" => return Ok(Some((x * y).into())),
|
|
|
|
"/" => return Ok(Some((x / y).into())),
|
|
|
|
"%" => return Ok(Some((x % y).into())),
|
|
|
|
"**" => return Ok(Some(x.powf(y).into())),
|
|
|
|
"==" => return Ok(Some((x == y).into())),
|
|
|
|
"!=" => return Ok(Some((x != y).into())),
|
|
|
|
">" => return Ok(Some((x > y).into())),
|
|
|
|
">=" => return Ok(Some((x >= y).into())),
|
|
|
|
"<" => return Ok(Some((x < y).into())),
|
|
|
|
"<=" => return Ok(Some((x <= y).into())),
|
|
|
|
_ => return Ok(None),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "decimal")]
|
|
|
|
if let Some((x, y)) = if type_id == (TypeId::of::<Decimal>(), TypeId::of::<Decimal>()) {
|
|
|
|
Some((
|
|
|
|
*x.read_lock::<Decimal>().unwrap(),
|
|
|
|
*y.read_lock::<Decimal>().unwrap(),
|
|
|
|
))
|
|
|
|
} else if type_id == (TypeId::of::<Decimal>(), TypeId::of::<INT>()) {
|
|
|
|
Some((
|
|
|
|
*x.read_lock::<Decimal>().unwrap(),
|
|
|
|
y.clone().cast::<INT>().into(),
|
|
|
|
))
|
|
|
|
} else if type_id == (TypeId::of::<INT>(), TypeId::of::<Decimal>()) {
|
|
|
|
Some((
|
|
|
|
x.clone().cast::<INT>().into(),
|
|
|
|
*y.read_lock::<Decimal>().unwrap(),
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
} {
|
|
|
|
if cfg!(not(feature = "unchecked")) {
|
|
|
|
use crate::packages::arithmetic::decimal_functions::*;
|
|
|
|
|
|
|
|
match op {
|
|
|
|
"+" => return add(x, y).map(Some),
|
|
|
|
"-" => return subtract(x, y).map(Some),
|
|
|
|
"*" => return multiply(x, y).map(Some),
|
|
|
|
"/" => return divide(x, y).map(Some),
|
|
|
|
"%" => return modulo(x, y).map(Some),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
match op {
|
|
|
|
"+" => return Ok(Some((x + y).into())),
|
|
|
|
"-" => return Ok(Some((x - y).into())),
|
|
|
|
"*" => return Ok(Some((x * y).into())),
|
|
|
|
"/" => return Ok(Some((x / y).into())),
|
|
|
|
"%" => return Ok(Some((x % y).into())),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
match op {
|
|
|
|
"==" => return Ok(Some((x == y).into())),
|
|
|
|
"!=" => return Ok(Some((x != y).into())),
|
|
|
|
">" => return Ok(Some((x > y).into())),
|
|
|
|
">=" => return Ok(Some((x >= y).into())),
|
|
|
|
"<" => return Ok(Some((x < y).into())),
|
|
|
|
"<=" => return Ok(Some((x <= y).into())),
|
|
|
|
_ => return Ok(None),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if second_type != first_type {
|
|
|
|
if type_id == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
2020-10-10 16:13:55 +02:00
|
|
|
let x = x.clone().cast::<char>();
|
|
|
|
let y = &*y.read_lock::<ImmutableString>().unwrap();
|
|
|
|
|
|
|
|
match op {
|
|
|
|
"+" => return Ok(Some(format!("{}{}", x, y).into())),
|
2021-02-19 04:47:26 +01:00
|
|
|
_ => return Ok(None),
|
2020-10-10 16:13:55 +02:00
|
|
|
}
|
2021-02-19 04:47:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if type_id == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
2020-10-10 16:13:55 +02:00
|
|
|
let x = &*x.read_lock::<ImmutableString>().unwrap();
|
|
|
|
let y = y.clone().cast::<char>();
|
|
|
|
|
|
|
|
match op {
|
|
|
|
"+" => return Ok(Some((x + y).into())),
|
2021-02-19 04:47:26 +01:00
|
|
|
_ => return Ok(None),
|
2020-10-10 16:13:55 +02:00
|
|
|
}
|
|
|
|
}
|
2021-02-19 04:47:26 +01:00
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
|
2021-02-19 04:47:26 +01:00
|
|
|
if first_type == TypeId::of::<INT>() {
|
2020-07-28 13:11:46 +02:00
|
|
|
let x = x.clone().cast::<INT>();
|
|
|
|
let y = y.clone().cast::<INT>();
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2020-07-31 16:30:23 +02:00
|
|
|
if cfg!(not(feature = "unchecked")) {
|
2021-02-15 10:42:33 +01:00
|
|
|
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
|
|
|
|
2020-07-31 16:30:23 +02:00
|
|
|
match op {
|
2020-08-22 16:26:49 +02:00
|
|
|
"+" => return add(x, y).map(Some),
|
|
|
|
"-" => return subtract(x, y).map(Some),
|
|
|
|
"*" => return multiply(x, y).map(Some),
|
|
|
|
"/" => return divide(x, y).map(Some),
|
|
|
|
"%" => return modulo(x, y).map(Some),
|
2021-02-10 05:41:27 +01:00
|
|
|
"**" => return power(x, y).map(Some),
|
2020-08-22 16:26:49 +02:00
|
|
|
">>" => return shift_right(x, y).map(Some),
|
|
|
|
"<<" => return shift_left(x, y).map(Some),
|
2020-07-31 16:30:23 +02:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
match op {
|
|
|
|
"+" => return Ok(Some((x + y).into())),
|
|
|
|
"-" => return Ok(Some((x - y).into())),
|
|
|
|
"*" => return Ok(Some((x * y).into())),
|
|
|
|
"/" => return Ok(Some((x / y).into())),
|
|
|
|
"%" => return Ok(Some((x % y).into())),
|
2021-02-10 05:41:27 +01:00
|
|
|
"**" => return Ok(Some(x.pow(y as u32).into())),
|
2020-08-22 16:26:49 +02:00
|
|
|
">>" => return Ok(Some((x >> y).into())),
|
|
|
|
"<<" => return Ok(Some((x << y).into())),
|
2020-07-31 16:30:23 +02:00
|
|
|
_ => (),
|
|
|
|
}
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
match op {
|
|
|
|
"==" => return Ok(Some((x == y).into())),
|
|
|
|
"!=" => return Ok(Some((x != y).into())),
|
|
|
|
">" => return Ok(Some((x > y).into())),
|
|
|
|
">=" => return Ok(Some((x >= y).into())),
|
|
|
|
"<" => return Ok(Some((x < y).into())),
|
|
|
|
"<=" => return Ok(Some((x <= y).into())),
|
|
|
|
"&" => return Ok(Some((x & y).into())),
|
|
|
|
"|" => return Ok(Some((x | y).into())),
|
|
|
|
"^" => return Ok(Some((x ^ y).into())),
|
2021-02-19 04:47:26 +01:00
|
|
|
_ => return Ok(None),
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
2021-02-19 04:47:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if first_type == TypeId::of::<bool>() {
|
2020-07-28 13:11:46 +02:00
|
|
|
let x = x.clone().cast::<bool>();
|
|
|
|
let y = y.clone().cast::<bool>();
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
match op {
|
|
|
|
"&" => return Ok(Some((x && y).into())),
|
|
|
|
"|" => return Ok(Some((x || y).into())),
|
|
|
|
"^" => return Ok(Some((x ^ y).into())),
|
|
|
|
"==" => return Ok(Some((x == y).into())),
|
|
|
|
"!=" => return Ok(Some((x != y).into())),
|
2021-02-19 04:47:26 +01:00
|
|
|
_ => return Ok(None),
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
2021-02-19 04:47:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if first_type == TypeId::of::<ImmutableString>() {
|
2020-07-27 06:30:09 +02:00
|
|
|
let x = &*x.read_lock::<ImmutableString>().unwrap();
|
|
|
|
let y = &*y.read_lock::<ImmutableString>().unwrap();
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
match op {
|
|
|
|
"+" => return Ok(Some((x + y).into())),
|
|
|
|
"==" => return Ok(Some((x == y).into())),
|
|
|
|
"!=" => return Ok(Some((x != y).into())),
|
|
|
|
">" => return Ok(Some((x > y).into())),
|
|
|
|
">=" => return Ok(Some((x >= y).into())),
|
|
|
|
"<" => return Ok(Some((x < y).into())),
|
|
|
|
"<=" => return Ok(Some((x <= y).into())),
|
2021-02-19 04:47:26 +01:00
|
|
|
_ => return Ok(None),
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
2021-02-19 04:47:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if first_type == TypeId::of::<char>() {
|
2020-07-28 13:11:46 +02:00
|
|
|
let x = x.clone().cast::<char>();
|
|
|
|
let y = y.clone().cast::<char>();
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
match op {
|
2020-10-10 16:13:55 +02:00
|
|
|
"+" => return Ok(Some(format!("{}{}", x, y).into())),
|
2020-07-23 12:40:42 +02:00
|
|
|
"==" => return Ok(Some((x == y).into())),
|
|
|
|
"!=" => return Ok(Some((x != y).into())),
|
|
|
|
">" => return Ok(Some((x > y).into())),
|
|
|
|
">=" => return Ok(Some((x >= y).into())),
|
|
|
|
"<" => return Ok(Some((x < y).into())),
|
|
|
|
"<=" => return Ok(Some((x <= y).into())),
|
2021-02-19 04:47:26 +01:00
|
|
|
_ => return Ok(None),
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
2021-02-19 04:47:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if first_type == TypeId::of::<()>() {
|
2020-07-23 12:40:42 +02:00
|
|
|
match op {
|
|
|
|
"==" => return Ok(Some(true.into())),
|
|
|
|
"!=" | ">" | ">=" | "<" | "<=" => return Ok(Some(false.into())),
|
2021-02-19 04:47:26 +01:00
|
|
|
_ => return Ok(None),
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-19 04:47:26 +01:00
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Build in common operator assignment implementations to avoid the cost of calling a registered function.
|
|
|
|
pub fn run_builtin_op_assignment(
|
|
|
|
op: &str,
|
|
|
|
x: &mut Dynamic,
|
|
|
|
y: &Dynamic,
|
|
|
|
) -> Result<Option<()>, Box<EvalAltResult>> {
|
|
|
|
let first_type = x.type_id();
|
|
|
|
let second_type = y.type_id();
|
|
|
|
|
|
|
|
let type_id = (first_type, second_type);
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2021-02-19 04:47:26 +01:00
|
|
|
if let Some((mut x, y)) = if type_id == (TypeId::of::<FLOAT>(), TypeId::of::<FLOAT>()) {
|
2020-07-28 13:11:46 +02:00
|
|
|
let y = y.clone().cast::<FLOAT>();
|
2021-02-19 04:47:26 +01:00
|
|
|
Some((x.write_lock::<FLOAT>().unwrap(), y))
|
|
|
|
} else if type_id == (TypeId::of::<FLOAT>(), TypeId::of::<INT>()) {
|
|
|
|
let y = y.clone().cast::<INT>() as FLOAT;
|
|
|
|
Some((x.write_lock::<FLOAT>().unwrap(), y))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
} {
|
2020-07-23 12:40:42 +02:00
|
|
|
match op {
|
2021-02-19 04:47:26 +01:00
|
|
|
"+=" => return Ok(Some(*x += y)),
|
|
|
|
"-=" => return Ok(Some(*x -= y)),
|
|
|
|
"*=" => return Ok(Some(*x *= y)),
|
|
|
|
"/=" => return Ok(Some(*x /= y)),
|
|
|
|
"%=" => return Ok(Some(*x %= y)),
|
|
|
|
"**=" => return Ok(Some(*x = x.powf(y))),
|
|
|
|
_ => return Ok(None),
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-15 10:42:33 +01:00
|
|
|
#[cfg(feature = "decimal")]
|
2021-02-19 04:47:26 +01:00
|
|
|
if let Some((mut x, y)) = if type_id == (TypeId::of::<Decimal>(), TypeId::of::<Decimal>()) {
|
|
|
|
let y = *y.read_lock::<Decimal>().unwrap();
|
|
|
|
Some((x.write_lock::<Decimal>().unwrap(), y))
|
|
|
|
} else if type_id == (TypeId::of::<Decimal>(), TypeId::of::<INT>()) {
|
|
|
|
let y = y.clone().cast::<INT>().into();
|
|
|
|
Some((x.write_lock::<Decimal>().unwrap(), y))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
} {
|
2021-02-15 10:42:33 +01:00
|
|
|
if cfg!(not(feature = "unchecked")) {
|
|
|
|
use crate::packages::arithmetic::decimal_functions::*;
|
|
|
|
|
|
|
|
match op {
|
2021-02-19 04:47:26 +01:00
|
|
|
"+=" => return Ok(Some(*x = add(*x, y)?.as_decimal().unwrap())),
|
|
|
|
"-=" => return Ok(Some(*x = subtract(*x, y)?.as_decimal().unwrap())),
|
|
|
|
"*=" => return Ok(Some(*x = multiply(*x, y)?.as_decimal().unwrap())),
|
|
|
|
"/=" => return Ok(Some(*x = divide(*x, y)?.as_decimal().unwrap())),
|
|
|
|
"%=" => return Ok(Some(*x = modulo(*x, y)?.as_decimal().unwrap())),
|
2021-02-15 10:42:33 +01:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
match op {
|
2021-02-19 04:47:26 +01:00
|
|
|
"+=" => return Ok(Some(*x += y)),
|
|
|
|
"-=" => return Ok(Some(*x -= y)),
|
|
|
|
"*=" => return Ok(Some(*x *= y)),
|
|
|
|
"/=" => return Ok(Some(*x /= y)),
|
|
|
|
"%=" => return Ok(Some(*x %= y)),
|
2021-02-15 10:42:33 +01:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-19 04:47:26 +01:00
|
|
|
if second_type != first_type {
|
|
|
|
if type_id == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
2020-10-10 16:13:55 +02:00
|
|
|
let y = y.read_lock::<char>().unwrap().deref().clone();
|
|
|
|
let mut x = x.write_lock::<ImmutableString>().unwrap();
|
|
|
|
|
|
|
|
match op {
|
|
|
|
"+=" => return Ok(Some(*x += y)),
|
2021-02-19 04:47:26 +01:00
|
|
|
_ => return Ok(None),
|
2020-10-10 16:13:55 +02:00
|
|
|
}
|
|
|
|
}
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
|
2021-02-19 04:47:26 +01:00
|
|
|
if first_type == TypeId::of::<INT>() {
|
2020-07-28 13:11:46 +02:00
|
|
|
let y = y.clone().cast::<INT>();
|
2020-08-01 16:28:13 +02:00
|
|
|
let mut x = x.write_lock::<INT>().unwrap();
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2020-08-01 06:21:15 +02:00
|
|
|
if cfg!(not(feature = "unchecked")) {
|
2021-02-15 10:42:33 +01:00
|
|
|
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
|
|
|
|
2020-08-01 06:21:15 +02:00
|
|
|
match op {
|
2020-08-22 16:26:49 +02:00
|
|
|
"+=" => return Ok(Some(*x = add(*x, y)?.as_int().unwrap())),
|
|
|
|
"-=" => return Ok(Some(*x = subtract(*x, y)?.as_int().unwrap())),
|
|
|
|
"*=" => return Ok(Some(*x = multiply(*x, y)?.as_int().unwrap())),
|
|
|
|
"/=" => return Ok(Some(*x = divide(*x, y)?.as_int().unwrap())),
|
|
|
|
"%=" => return Ok(Some(*x = modulo(*x, y)?.as_int().unwrap())),
|
2021-02-15 10:42:33 +01:00
|
|
|
"**=" => return Ok(Some(*x = power(*x, y)?.as_int().unwrap())),
|
2020-08-22 16:26:49 +02:00
|
|
|
">>=" => return Ok(Some(*x = shift_right(*x, y)?.as_int().unwrap())),
|
|
|
|
"<<=" => return Ok(Some(*x = shift_left(*x, y)?.as_int().unwrap())),
|
2020-08-01 06:21:15 +02:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
match op {
|
|
|
|
"+=" => return Ok(Some(*x += y)),
|
|
|
|
"-=" => return Ok(Some(*x -= y)),
|
|
|
|
"*=" => return Ok(Some(*x *= y)),
|
|
|
|
"/=" => return Ok(Some(*x /= y)),
|
|
|
|
"%=" => return Ok(Some(*x %= y)),
|
2021-02-15 10:42:33 +01:00
|
|
|
"**=" => return Ok(Some(*x = x.pow(y as u32))),
|
2020-08-23 12:04:19 +02:00
|
|
|
">>=" => return Ok(Some(*x = *x >> y)),
|
2020-08-22 16:26:49 +02:00
|
|
|
"<<=" => return Ok(Some(*x = *x << y)),
|
2020-08-01 06:21:15 +02:00
|
|
|
_ => (),
|
|
|
|
}
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
match op {
|
|
|
|
"&=" => return Ok(Some(*x &= y)),
|
|
|
|
"|=" => return Ok(Some(*x |= y)),
|
|
|
|
"^=" => return Ok(Some(*x ^= y)),
|
2021-02-19 04:47:26 +01:00
|
|
|
_ => return Ok(None),
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
2021-02-19 04:47:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if first_type == TypeId::of::<bool>() {
|
2020-07-28 13:11:46 +02:00
|
|
|
let y = y.clone().cast::<bool>();
|
2020-08-01 16:28:13 +02:00
|
|
|
let mut x = x.write_lock::<bool>().unwrap();
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
match op {
|
|
|
|
"&=" => return Ok(Some(*x = *x && y)),
|
|
|
|
"|=" => return Ok(Some(*x = *x || y)),
|
2021-02-19 04:47:26 +01:00
|
|
|
_ => return Ok(None),
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
2021-02-19 04:47:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if first_type == TypeId::of::<char>() {
|
2020-10-10 16:13:55 +02:00
|
|
|
let y = y.read_lock::<char>().unwrap().deref().clone();
|
|
|
|
let mut x = x.write_lock::<Dynamic>().unwrap();
|
|
|
|
|
|
|
|
match op {
|
|
|
|
"+=" => return Ok(Some(*x = format!("{}{}", *x, y).into())),
|
2021-02-19 04:47:26 +01:00
|
|
|
_ => return Ok(None),
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-19 04:47:26 +01:00
|
|
|
if first_type == TypeId::of::<ImmutableString>() {
|
|
|
|
let y = y.read_lock::<ImmutableString>().unwrap().deref().clone();
|
|
|
|
let mut x = x.write_lock::<ImmutableString>().unwrap();
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
match op {
|
|
|
|
"+=" => return Ok(Some(*x += y)),
|
2021-02-19 04:47:26 +01:00
|
|
|
_ => return Ok(None),
|
2021-02-15 10:42:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
Ok(None)
|
|
|
|
}
|