rhai/src/fn_call.rs

1487 lines
54 KiB
Rust
Raw Normal View History

//! Implement function-calling mechanism for `Engine`.
2017-12-20 22:16:53 +01:00
use crate::any::Dynamic;
use crate::engine::{
2020-10-11 15:58:11 +02:00
search_imports, Engine, Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR,
KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR,
KEYWORD_PRINT, KEYWORD_TYPE_OF,
};
use crate::error::ParseErrorType;
use crate::fn_native::{FnCallArgs, FnPtr};
use crate::module::{Module, ModuleRef};
use crate::optimize::OptimizationLevel;
2020-07-26 07:51:09 +02:00
use crate::parser::{Expr, ImmutableString, AST, INT};
use crate::result::EvalAltResult;
2020-07-26 07:51:09 +02:00
use crate::scope::Scope;
use crate::stdlib::ops::Deref;
use crate::token::Position;
2020-10-10 07:41:55 +02:00
use crate::{calc_fn_hash, StaticVec};
2020-03-18 15:03:50 +01:00
2020-07-26 07:51:09 +02:00
#[cfg(not(feature = "no_function"))]
use crate::{
parser::ScriptFnDef, r#unsafe::unsafe_cast_var_name_to_lifetime,
scope::EntryType as ScopeEntryType,
};
#[cfg(not(feature = "no_float"))]
use crate::parser::FLOAT;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_index"))]
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
#[cfg(not(feature = "no_object"))]
2020-07-26 09:53:22 +02:00
use crate::engine::{Map, Target, FN_GET, FN_SET};
2020-10-03 10:25:58 +02:00
#[cfg(not(feature = "no_closure"))]
use crate::engine::KEYWORD_IS_SHARED;
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
2020-08-05 16:53:01 +02:00
#[cfg(not(feature = "no_function"))]
2020-07-31 16:30:23 +02:00
use crate::scope::Entry as ScopeEntry;
use crate::stdlib::{
any::{type_name, TypeId},
boxed::Box,
convert::TryFrom,
format,
iter::{empty, once},
mem,
string::ToString,
vec::Vec,
};
#[cfg(feature = "no_std")]
#[cfg(not(feature = "no_float"))]
use num_traits::float::Float;
/// Extract the property name from a getter function name.
2020-07-26 09:53:22 +02:00
#[inline(always)]
fn extract_prop_from_getter(_fn_name: &str) -> Option<&str> {
#[cfg(not(feature = "no_object"))]
2020-07-26 09:53:22 +02:00
if _fn_name.starts_with(FN_GET) {
return Some(&_fn_name[FN_GET.len()..]);
}
None
}
/// Extract the property name from a setter function name.
2020-07-26 09:53:22 +02:00
#[inline(always)]
fn extract_prop_from_setter(_fn_name: &str) -> Option<&str> {
#[cfg(not(feature = "no_object"))]
2020-07-26 09:53:22 +02:00
if _fn_name.starts_with(FN_SET) {
return Some(&_fn_name[FN_SET.len()..]);
}
None
}
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.
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-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.
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<'_> {
fn drop(&mut self) {
// Panic if the shorter lifetime leaks.
assert!(
self.orig_mut.is_none(),
"MutBackup::restore has not been called prior to existing this scope"
);
}
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),
Position::none(),
2020-08-06 04:17:32 +02:00
)
.into();
2020-08-02 07:33:51 +02:00
}
}
Ok(())
}
impl Engine {
2020-07-30 12:18:28 +02:00
/// Call a native Rust function registered with the `Engine`.
/// Position in `EvalAltResult` is `None` and must be set afterwards.
///
/// ## 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 `()`!
2020-07-31 06:11:16 +02:00
pub(crate) fn call_native_fn(
&self,
state: &mut State,
lib: &Module,
fn_name: &str,
2020-07-30 12:18:28 +02:00
hash_fn: u64,
args: &mut FnCallArgs,
is_ref: bool,
pub_only: bool,
def_val: &Option<Dynamic>,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
self.inc_operations(state)?;
2020-07-31 06:11:16 +02:00
// Search for the native function
// First search registered functions (can override packages)
// Then search packages
let func = //lib.get_fn(hash_fn, pub_only)
self.global_module.get_fn(hash_fn, pub_only)
.or_else(|| self.packages.get_fn(hash_fn, pub_only));
if let Some(func) = func {
2020-07-31 06:11:16 +02:00
assert!(func.is_native());
// 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);
// Run external function
2020-08-02 12:53:25 +02:00
let result = if func.is_plugin_fn() {
func.get_plugin_fn().call((self, lib).into(), args)
2020-08-02 12:53:25 +02:00
} else {
func.get_native_fn()((self, lib).into(), args)
2020-08-02 12:53:25 +02:00
};
// Restore the original reference
2020-07-31 06:11:16 +02:00
backup.restore_first_arg(args);
let result = result?;
// See if the function match print/debug (which requires special processing)
return Ok(match fn_name {
KEYWORD_PRINT => (
(self.print)(result.as_str().map_err(|typ| {
2020-08-06 04:17:32 +02:00
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<ImmutableString>()).into(),
typ.into(),
Position::none(),
2020-08-06 04:17:32 +02:00
)
})?)
.into(),
false,
),
KEYWORD_DEBUG => (
(self.debug)(result.as_str().map_err(|typ| {
2020-08-06 04:17:32 +02:00
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<ImmutableString>()).into(),
typ.into(),
Position::none(),
2020-08-06 04:17:32 +02:00
)
})?)
.into(),
false,
),
_ => (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 {
return Ok((val.clone(), false));
}
// Getter function not found?
if let Some(prop) = extract_prop_from_getter(fn_name) {
2020-08-06 04:17:32 +02:00
return EvalAltResult::ErrorDotExpr(
format!(
2020-09-26 05:35:18 +02:00
"Failed to get property '{}' of '{}' - the property may not exist, or it may be write-only",
prop,
self.map_type_name(args[0].type_name())
),
Position::none(),
2020-08-06 04:17:32 +02:00
)
.into();
}
// Setter function not found?
if let Some(prop) = extract_prop_from_setter(fn_name) {
2020-08-06 04:17:32 +02:00
return EvalAltResult::ErrorDotExpr(
format!(
2020-09-26 05:35:18 +02:00
"Failed to set property '{}' of '{}' - the property may not exist, may be read-only, or '{}' is the wrong type",
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()),
),
Position::none(),
2020-08-06 04:17:32 +02:00
)
.into();
}
// index getter function not found?
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_index"))]
if fn_name == FN_IDX_GET && args.len() == 2 {
2020-08-06 04:17:32 +02:00
return EvalAltResult::ErrorFunctionNotFound(
format!(
"{} [{}]",
self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name()),
),
Position::none(),
2020-08-06 04:17:32 +02:00
)
.into();
}
// index setter function not found?
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_index"))]
if fn_name == FN_IDX_SET {
2020-08-06 04:17:32 +02:00
return EvalAltResult::ErrorFunctionNotFound(
format!(
"{} [{}]=",
self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name()),
),
Position::none(),
2020-08-06 04:17:32 +02:00
)
.into();
}
// Raise error
2020-08-06 04:17:32 +02:00
EvalAltResult::ErrorFunctionNotFound(
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(", ")
),
Position::none(),
2020-08-06 04:17:32 +02:00
)
.into()
}
/// Call a script-defined function.
/// Position in `EvalAltResult` is `None` and must be set afterwards.
///
/// ## 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,
state: &mut State,
lib: &Module,
this_ptr: &mut Option<&mut Dynamic>,
fn_def: &ScriptFnDef,
args: &mut FnCallArgs,
level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> {
2020-07-31 06:11:16 +02:00
self.inc_operations(state)?;
// Check for stack overflow
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "unchecked"))]
if level > self.max_call_levels() {
2020-07-31 06:11:16 +02:00
return Err(Box::new(
EvalAltResult::ErrorStackOverflow(Position::none()),
));
}
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)| {
let var_name = unsafe_cast_var_name_to_lifetime(name.as_str(), state);
(var_name, ScopeEntryType::Normal, value)
}),
);
// Merge in encapsulated environment, if any
let mut lib_merged;
let unified_lib = if let Some(ref env_lib) = fn_def.lib {
if lib.is_empty() {
// In the special case of the main script not defining any function
env_lib
} else {
lib_merged = lib.clone();
lib_merged.merge(env_lib);
&lib_merged
}
} else {
lib
};
// Evaluate the function at one higher level of call depth
let stmt = &fn_def.body;
let result = self
.eval_stmt(scope, mods, state, unified_lib, this_ptr, stmt, level + 1)
.or_else(|err| match *err {
// Convert return statement to return value
EvalAltResult::Return(x, _) => Ok(x),
EvalAltResult::ErrorInFunctionCall(name, err, _) => {
2020-08-06 04:17:32 +02:00
EvalAltResult::ErrorInFunctionCall(
format!("{} > {}", fn_def.name, name),
err,
Position::none(),
2020-08-06 04:17:32 +02:00
)
.into()
}
_ => EvalAltResult::ErrorInFunctionCall(
fn_def.name.to_string(),
err,
Position::none(),
)
.into(),
});
// Remove all local variables
scope.rewind(prev_scope_len);
mods.truncate(prev_mods_len);
state.scope_level = orig_scope_level;
result
}
// Has a system function an override?
2020-10-08 16:25:50 +02:00
#[inline]
pub(crate) fn has_override_by_name_and_arguments(
&self,
lib: &Module,
name: &str,
arg_types: impl AsRef<[TypeId]>,
pub_only: bool,
) -> bool {
let arg_types = arg_types.as_ref();
let arg_len = if arg_types.is_empty() {
usize::MAX
} else {
arg_types.len()
};
let hash_fn = calc_fn_hash(empty(), name, arg_len, arg_types.iter().cloned());
let hash_script = calc_fn_hash(empty(), name, arg_types.len(), empty());
self.has_override(lib, hash_fn, hash_script, pub_only)
}
// Has a system function an override?
2020-10-08 16:25:50 +02:00
#[inline(always)]
pub(crate) fn has_override(
&self,
lib: &Module,
hash_fn: u64,
hash_script: u64,
pub_only: bool,
) -> bool {
// NOTE: We skip script functions for global_module and packages, and native functions for lib
// First check script-defined functions
lib.contains_fn(hash_script, pub_only)
//|| lib.contains_fn(hash_fn, pub_only)
// Then check registered functions
//|| self.global_module.contains_fn(hash_script, pub_only)
|| self.global_module.contains_fn(hash_fn, pub_only)
// Then check packages
2020-09-11 16:32:59 +02:00
|| self.packages.contains_fn(hash_script, pub_only)
|| self.packages.contains_fn(hash_fn, pub_only)
}
2020-07-31 06:11:16 +02:00
/// Perform an actual function call, native Rust or scripted, taking care of special functions.
/// Position in `EvalAltResult` is `None` and must be set afterwards.
///
/// ## 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,
state: &mut State,
lib: &Module,
fn_name: &str,
hash_script: u64,
args: &mut FnCallArgs,
is_ref: bool,
2020-08-05 16:53:01 +02:00
_is_method: bool,
pub_only: bool,
_capture: Option<Scope>,
def_val: &Option<Dynamic>,
2020-08-05 16:53:01 +02:00
_level: usize,
) -> 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)?;
}
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
let arg_types = args.iter().map(|a| a.type_id());
2020-09-11 16:32:59 +02:00
let hash_fn = calc_fn_hash(
empty(),
fn_name,
if args.is_empty() {
// Distinguish between a script function and a native function with no parameters
usize::MAX
} else {
args.len()
},
arg_types,
);
match fn_name {
// type_of
KEYWORD_TYPE_OF
2020-07-30 12:18:28 +02:00
if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) =>
{
Ok((
self.map_type_name(args[0].type_name()).to_string().into(),
false,
))
}
// 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
if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) =>
{
EvalAltResult::ErrorRuntime(
format!(
"'{}' should not be called in method style. Try {}(...);",
fn_name, fn_name
),
Position::none(),
)
.into()
}
2020-09-25 13:07:24 +02:00
// Script-like function found
2020-07-30 12:18:28 +02:00
#[cfg(not(feature = "no_function"))]
_ if lib.contains_fn(hash_script, pub_only)
//|| self.global_module.contains_fn(hash_script, pub_only)
2020-09-11 16:32:59 +02:00
|| self.packages.contains_fn(hash_script, pub_only) =>
{
2020-09-25 13:07:24 +02:00
// Get function
let func = lib
2020-09-11 16:32:59 +02:00
.get_fn(hash_script, pub_only)
//.or_else(|| self.global_module.get_fn(hash_script, pub_only))
2020-09-11 16:32:59 +02:00
.or_else(|| self.packages.get_fn(hash_script, pub_only))
2020-09-25 13:07:24 +02:00
.unwrap();
if func.is_script() {
let func = func.get_fn_def();
let scope = &mut Scope::new();
let mods = &mut Imports::new();
2020-10-12 11:00:58 +02:00
// Move captured variables into scope
2020-09-25 13:07:24 +02:00
#[cfg(not(feature = "no_closure"))]
if let Some(captured) = _capture {
2020-10-12 11:00:58 +02:00
captured
.into_iter()
2020-10-12 11:00:58 +02:00
.filter(|ScopeEntry { name, .. }| {
func.externals.contains(name.as_ref())
})
.for_each(
|ScopeEntry {
name, typ, value, ..
}| {
// Consume the scope values.
match typ {
ScopeEntryType::Normal => scope.push(name, value),
2020-10-12 11:00:58 +02:00
ScopeEntryType::Constant => {
scope.push_constant(name, value)
2020-10-12 11:00:58 +02:00
}
};
},
);
2020-09-25 13:07:24 +02:00
}
let result = if _is_method {
// Method call of script function - map first argument to `this`
let (first, rest) = args.split_first_mut().unwrap();
self.call_script_fn(
scope,
mods,
state,
lib,
&mut Some(*first),
func,
rest,
_level,
)?
} else {
// Normal call of script function - map first argument to `this`
// 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
let result = self
.call_script_fn(scope, mods, state, lib, &mut None, func, args, _level);
2020-07-30 12:18:28 +02:00
2020-09-25 13:07:24 +02:00
// Restore the original reference
backup.restore_first_arg(args);
result?
};
2020-07-30 12:18:28 +02:00
2020-09-25 13:07:24 +02:00
Ok((result, false))
} else {
// If it is a native function, redirect it
self.call_native_fn(
2020-07-30 12:18:28 +02:00
state,
lib,
fn_name,
2020-09-25 13:07:24 +02:00
hash_script,
args,
is_ref,
pub_only,
def_val,
)
}
}
2020-09-11 16:32:59 +02:00
2020-07-30 12:18:28 +02:00
// Normal native function call
2020-07-31 06:11:16 +02:00
_ => self.call_native_fn(
state, lib, fn_name, hash_fn, args, is_ref, pub_only, def_val,
),
}
}
/// Evaluate a text string as a script - used primarily for 'eval'.
/// Position in `EvalAltResult` is `None` and must be set afterwards.
fn eval_script_expr(
&self,
scope: &mut Scope,
mods: &mut Imports,
state: &mut State,
lib: &Module,
2020-10-03 17:27:30 +02:00
script: &str,
2020-07-31 06:40:16 +02:00
_level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> {
2020-07-31 06:11:16 +02:00
self.inc_operations(state)?;
// Check for stack overflow
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "unchecked"))]
if _level > self.max_call_levels() {
2020-07-31 06:11:16 +02:00
return Err(Box::new(
EvalAltResult::ErrorStackOverflow(Position::none()),
));
}
// Compile the script text
// No optimizations because we only run it once
let mut ast = self.compile_with_scope_and_optimization_level(
&Scope::new(),
&[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 {
return Err(ParseErrorType::WrongFnDefinition.into());
}
let statements = mem::take(ast.statements_mut());
let ast = AST::new(statements, lib.clone());
// Evaluate the AST
let (result, operations) = self.eval_ast_with_scope_raw(scope, mods, &ast)?;
state.operations += operations;
self.inc_operations(state)?;
return Ok(result);
}
/// Call a dot method.
/// Position in `EvalAltResult` is `None` and must be set afterwards.
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_object"))]
pub(crate) fn make_method_call(
&self,
state: &mut State,
lib: &Module,
name: &str,
2020-07-30 12:18:28 +02:00
hash_script: u64,
target: &mut Target,
2020-10-15 17:30:30 +02:00
mut call_args: StaticVec<Dynamic>,
def_val: &Option<Dynamic>,
native: bool,
pub_only: bool,
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();
let mut _fn_name = name;
2020-07-26 09:53:22 +02:00
let (result, updated) = if _fn_name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
// FnPtr call
let fn_ptr = obj.read_lock::<FnPtr>().unwrap();
// Redirect function name
let fn_name = fn_ptr.fn_name();
let args_len = call_args.len() + fn_ptr.curry().len();
// Recalculate hash
2020-07-30 12:18:28 +02:00
let hash = if native {
0
} else {
calc_fn_hash(empty(), fn_name, args_len, empty())
2020-07-30 12:18:28 +02:00
};
// Arguments are passed as-is, adding the curried arguments
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
let mut arg_values = curry
.iter_mut()
2020-10-15 17:30:30 +02:00
.chain(call_args.iter_mut())
.collect::<StaticVec<_>>();
let args = arg_values.as_mut();
// Map it to name(args) in function-call style
self.exec_fn_call(
2020-07-30 12:18:28 +02:00
state, lib, fn_name, hash, args, false, false, pub_only, None, def_val, level,
)
2020-10-15 17:30:30 +02:00
} else if _fn_name == KEYWORD_FN_PTR_CALL
&& call_args.len() > 0
&& call_args[0].is::<FnPtr>()
{
// FnPtr call on object
2020-10-15 17:30:30 +02:00
let fn_ptr = call_args.remove(0).cast::<FnPtr>();
// Redirect function name
let fn_name = fn_ptr.fn_name();
let args_len = call_args.len() + fn_ptr.curry().len();
// Recalculate hash
2020-07-30 12:18:28 +02:00
let hash = if native {
0
} else {
calc_fn_hash(empty(), fn_name, args_len, empty())
2020-07-30 12:18:28 +02:00
};
// Replace the first argument with the object pointer, adding the curried arguments
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
let mut arg_values = once(obj)
.chain(curry.iter_mut())
2020-10-15 17:30:30 +02:00
.chain(call_args.iter_mut())
.collect::<StaticVec<_>>();
let args = arg_values.as_mut();
// Map it to name(args) in function-call style
self.exec_fn_call(
state, lib, fn_name, hash, args, is_ref, true, pub_only, None, def_val, level,
)
2020-07-26 09:53:22 +02:00
} else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
// Curry call
let fn_ptr = obj.read_lock::<FnPtr>().unwrap();
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())
.collect(),
)
.into(),
false,
))
2020-10-03 10:25:58 +02:00
} else if {
#[cfg(not(feature = "no_closure"))]
{
2020-10-15 17:30:30 +02:00
_fn_name == 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))
} else {
2020-10-03 10:25:58 +02:00
let _redirected;
2020-08-05 11:55:36 +02:00
let mut hash = hash_script;
// Check if it is a map method call in OOP style
#[cfg(not(feature = "no_object"))]
if let Some(map) = obj.read_lock::<Map>() {
2020-07-26 09:53:22 +02: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>() {
// Remap the function name
2020-10-03 10:25:58 +02:00
_redirected = fn_ptr.get_fn_name().clone();
_fn_name = &_redirected;
2020-08-04 12:38:33 +02:00
// Add curried arguments
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-08-05 11:55:36 +02:00
hash = if native {
2020-08-04 12:38:33 +02:00
0
} else {
2020-10-15 17:30:30 +02:00
calc_fn_hash(empty(), _fn_name, call_args.len(), empty())
2020-08-04 12:38:33 +02:00
};
}
}
};
2020-07-30 12:18:28 +02:00
if native {
2020-08-05 11:55:36 +02:00
hash = 0;
2020-07-30 12:18:28 +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<_>>();
let args = arg_values.as_mut();
self.exec_fn_call(
2020-08-05 11:55:36 +02:00
state, lib, _fn_name, hash, args, is_ref, true, pub_only, None, def_val, level,
)
}?;
2020-10-04 04:40:44 +02:00
// Propagate the changed value back to the source if necessary
if updated {
target.propagate_changed_value();
}
Ok((result, updated))
}
/// Call a function in normal function-call style.
/// Position in `EvalAltResult` is `None` and must be set afterwards.
pub(crate) fn make_function_call(
&self,
scope: &mut Scope,
mods: &mut Imports,
state: &mut State,
lib: &Module,
this_ptr: &mut Option<&mut Dynamic>,
name: &str,
args_expr: impl AsRef<[Expr]>,
def_val: &Option<Dynamic>,
2020-07-30 12:18:28 +02:00
mut hash_script: u64,
native: bool,
pub_only: bool,
2020-07-30 12:18:28 +02:00
capture: bool,
level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> {
let args_expr = args_expr.as_ref();
// Handle Fn()
if name == KEYWORD_FN_PTR && args_expr.len() == 1 {
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
2020-07-30 12:18:28 +02:00
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
// Fn - only in function call style
return self
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?
.take_immutable_string()
2020-10-05 07:45:57 +02:00
.map_err(|typ| {
self.make_type_mismatch_err::<ImmutableString>(typ, args_expr[0].position())
2020-10-05 07:45:57 +02:00
})
.and_then(|s| FnPtr::try_from(s))
.map(Into::<Dynamic>::into)
.map_err(|err| err.fill_position(args_expr[0].position()));
}
}
// Handle curry()
if name == KEYWORD_FN_PTR_CURRY && args_expr.len() > 1 {
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
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()),
args_expr[0].position(),
2020-10-03 17:27:30 +02:00
));
}
2020-10-15 17:44:05 +02:00
let (fn_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-15 17:30:30 +02:00
args_expr
.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-10-15 17:44:05 +02:00
return Ok(FnPtr::new_unchecked(fn_name, fn_curry).into());
}
2020-07-31 12:43:34 +02:00
// Handle is_shared()
2020-10-03 10:25:58 +02:00
#[cfg(not(feature = "no_closure"))]
if name == KEYWORD_IS_SHARED && args_expr.len() == 1 {
let value = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
2020-07-31 12:43:34 +02:00
return Ok(value.is_shared().into());
}
// Handle call() - Redirect function call
let redirected;
let mut args_expr = args_expr.as_ref();
let mut curry = StaticVec::new();
let mut name = name;
if name == KEYWORD_FN_PTR_CALL
&& args_expr.len() >= 1
2020-07-30 12:18:28 +02:00
&& !self.has_override(lib, 0, hash_script, pub_only)
{
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
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()),
args_expr[0].position(),
2020-10-03 17:27:30 +02:00
));
}
2020-10-15 17:30:30 +02:00
let fn_ptr = fn_ptr.cast::<FnPtr>();
curry.extend(fn_ptr.curry().iter().cloned());
2020-10-15 17:30:30 +02:00
// 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_fn_hash(empty(), name, args_len, empty());
}
2020-10-03 10:25:58 +02:00
// Handle is_def_var()
if name == KEYWORD_IS_DEF_VAR && args_expr.len() == 1 {
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
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| {
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
2020-10-05 07:45:57 +02:00
})?;
2020-10-03 17:27:30 +02:00
if var_name.is_empty() {
return Ok(false.into());
} else {
2020-10-03 10:25:58 +02:00
return Ok(scope.contains(var_name).into());
}
}
}
// Handle is_def_fn()
if name == KEYWORD_IS_DEF_FN && args_expr.len() == 2 {
let hash_fn = calc_fn_hash(
empty(),
name,
2,
[TypeId::of::<ImmutableString>(), TypeId::of::<INT>()]
.iter()
.cloned(),
);
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
let fn_name =
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
let num_params =
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[1], level)?;
2020-10-03 17:27:30 +02:00
2020-10-15 17:30:30 +02:00
let fn_name = fn_name.as_str().map_err(|err| {
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
})?;
let num_params = num_params.as_int().map_err(|err| {
self.make_type_mismatch_err::<INT>(err, args_expr[1].position())
2020-10-03 17:27:30 +02:00
})?;
if fn_name.is_empty() || num_params < 0 {
return Ok(false.into());
} else {
2020-10-03 10:25:58 +02:00
let hash = calc_fn_hash(empty(), fn_name, num_params as usize, empty());
return Ok(lib.contains_fn(hash, false).into());
}
}
}
2020-07-30 12:18:28 +02:00
// Handle eval()
if name == KEYWORD_EVAL && args_expr.len() == 1 {
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
// eval - only in function call style
let prev_len = scope.len();
let script =
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
2020-10-15 17:30:30 +02:00
let script = script.as_str().map_err(|typ| {
self.make_type_mismatch_err::<ImmutableString>(typ, args_expr[0].position())
2020-10-05 07:45:57 +02:00
})?;
2020-10-03 17:27:30 +02:00
let result = if !script.is_empty() {
self.eval_script_expr(scope, mods, state, lib, script, level + 1)
.map_err(|err| err.fill_position(args_expr[0].position()))
2020-10-03 17:27:30 +02:00
} else {
Ok(().into())
};
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;
}
return result;
}
}
// Normal function call - except for Fn, curry, call and eval (handled above)
let mut arg_values: StaticVec<_>;
let mut args: StaticVec<_>;
let mut is_ref = false;
let capture = if cfg!(not(feature = "no_closure")) && capture && !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
args = Default::default();
} else {
// 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
if args_expr[0].get_variable_access(false).is_some() && curry.is_empty() {
// func(x, ...) -> x.func(...)
arg_values = args_expr
.iter()
.skip(1)
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
.collect::<Result<_, _>>()?;
let (target, _, _, pos) =
self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?;
self.inc_operations(state)
.map_err(|err| err.fill_position(pos))?;
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 {
// func(..., ...)
arg_values = args_expr
.iter()
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
.collect::<Result<_, _>>()?;
args = curry.iter_mut().chain(arg_values.iter_mut()).collect();
}
}
2020-07-30 12:18:28 +02:00
let hash = if native { 0 } else { hash_script };
let args = args.as_mut();
2020-07-30 12:18:28 +02:00
self.exec_fn_call(
2020-07-30 12:18:28 +02:00
state, lib, name, hash, args, is_ref, false, pub_only, capture, def_val, level,
)
.map(|(v, _)| v)
}
/// Call a module-qualified function in normal function-call style.
/// Position in `EvalAltResult` is `None` and must be set afterwards.
pub(crate) fn make_qualified_function_call(
&self,
scope: &mut Scope,
mods: &mut Imports,
state: &mut State,
lib: &Module,
this_ptr: &mut Option<&mut Dynamic>,
modules: &Option<Box<ModuleRef>>,
name: &str,
args_expr: impl AsRef<[Expr]>,
def_val: Option<bool>,
hash_script: u64,
level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> {
let args_expr = args_expr.as_ref();
let modules = modules.as_ref().unwrap();
let mut arg_values: StaticVec<_>;
let mut first_arg_value = None;
let mut args: StaticVec<_>;
if args_expr.is_empty() {
// No arguments
args = Default::default();
} else {
// See if the first argument is a variable (not module-qualified).
// If so, convert to method-call style in order to leverage potential
// &mut first argument and avoid cloning the value
if args_expr[0].get_variable_access(true).is_some() {
// func(x, ...) -> x.func(...)
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
let (target, _, _, pos) =
self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
self.inc_operations(state)
.map_err(|err| err.fill_position(pos))?;
if target.is_shared() || target.is_value() {
arg_values[0] = target.take_or_clone().flatten();
args = arg_values.iter_mut().collect();
} 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();
}
} 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();
}
}
let module = search_imports(mods, state, modules)?;
// First search in script-defined functions (can override built-in)
let func = match module.get_qualified_fn(hash_script) {
// Then search in Rust functions
None => {
self.inc_operations(state)?;
// Qualified Rust functions are indexed in two steps:
// 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,
// zero number of arguments, and the actual list of argument `TypeId`'.s
let hash_fn_args = calc_fn_hash(empty(), "", 0, args.iter().map(|a| a.type_id()));
// 3) The final hash is the XOR of the two hashes.
let hash_qualified_fn = hash_script ^ hash_fn_args;
module.get_qualified_fn(hash_qualified_fn)
}
r => r,
};
match func {
#[cfg(not(feature = "no_function"))]
Some(f) if f.is_script() => {
// Clone first argument
if let Some(first) = first_arg_value {
let first_val = args[0].clone();
args[0] = first;
*args[0] = first_val;
}
let args = args.as_mut();
let fn_def = f.get_fn_def();
2020-07-30 12:18:28 +02:00
2020-10-12 11:00:58 +02:00
let new_scope = &mut Scope::new();
2020-07-30 12:18:28 +02:00
let mods = &mut Imports::new();
self.call_script_fn(new_scope, mods, state, lib, &mut None, fn_def, args, level)
}
Some(f) if f.is_plugin_fn() => {
f.get_plugin_fn().call((self, lib).into(), args.as_mut())
}
Some(f) if f.is_native() => {
if !f.is_method() {
// Clone first argument
if let Some(first) = first_arg_value {
let first_val = args[0].clone();
args[0] = first;
*args[0] = first_val;
}
}
f.get_native_fn()((self, lib).into(), args.as_mut())
}
Some(_) => unreachable!(),
None if def_val.is_some() => Ok(def_val.unwrap().into()),
2020-08-06 04:17:32 +02:00
None => EvalAltResult::ErrorFunctionNotFound(
format!(
"{}{} ({})",
modules,
name,
args.iter()
.map(|a| if a.is::<ImmutableString>() {
"&str | ImmutableString | String"
} else {
self.map_type_name((*a).type_name())
})
.collect::<Vec<_>>()
.join(", ")
),
Position::none(),
2020-08-06 04:17:32 +02:00
)
.into(),
}
}
}
/// 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>> {
2020-08-22 16:26:49 +02:00
use crate::packages::arithmetic::arith_basic::INT::functions::*;
let args_type = x.type_id();
2020-10-10 16:13:55 +02:00
let second_type = y.type_id();
2020-10-10 16:13:55 +02:00
if second_type != args_type {
if args_type == TypeId::of::<char>() && second_type == TypeId::of::<ImmutableString>() {
let x = x.clone().cast::<char>();
let y = &*y.read_lock::<ImmutableString>().unwrap();
match op {
"+" => return Ok(Some(format!("{}{}", x, y).into())),
_ => (),
}
} else if args_type == TypeId::of::<ImmutableString>()
&& second_type == TypeId::of::<char>()
{
let x = &*x.read_lock::<ImmutableString>().unwrap();
let y = y.clone().cast::<char>();
match op {
"+" => return Ok(Some((x + y).into())),
_ => (),
}
}
return Ok(None);
}
if args_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-31 16:30:23 +02:00
if cfg!(not(feature = "unchecked")) {
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),
"~" => return power(x, y).map(Some),
">>" => 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())),
2020-08-22 16:26:49 +02:00
"~" => return Ok(Some(x.pow(y as u32).into())),
">>" => return Ok(Some((x >> y).into())),
"<<" => return Ok(Some((x << y).into())),
2020-07-31 16:30:23 +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())),
_ => (),
}
} else if args_type == TypeId::of::<bool>() {
2020-07-28 13:11:46 +02:00
let x = x.clone().cast::<bool>();
let y = y.clone().cast::<bool>();
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())),
_ => (),
}
} else if args_type == TypeId::of::<ImmutableString>() {
let x = &*x.read_lock::<ImmutableString>().unwrap();
let y = &*y.read_lock::<ImmutableString>().unwrap();
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())),
_ => (),
}
} else if args_type == TypeId::of::<char>() {
2020-07-28 13:11:46 +02:00
let x = x.clone().cast::<char>();
let y = y.clone().cast::<char>();
match op {
2020-10-10 16:13:55 +02:00
"+" => return Ok(Some(format!("{}{}", 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())),
_ => (),
}
} else if args_type == TypeId::of::<()>() {
match op {
"==" => return Ok(Some(true.into())),
"!=" | ">" | ">=" | "<" | "<=" => return Ok(Some(false.into())),
_ => (),
}
}
#[cfg(not(feature = "no_float"))]
if args_type == TypeId::of::<FLOAT>() {
2020-07-28 13:11:46 +02:00
let x = x.clone().cast::<FLOAT>();
let y = y.clone().cast::<FLOAT>();
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())),
2020-08-22 16:26:49 +02:00
"~" => 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())),
_ => (),
}
}
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>> {
2020-08-22 16:26:49 +02:00
use crate::packages::arithmetic::arith_basic::INT::functions::*;
let args_type = x.type_id();
2020-10-10 16:13:55 +02:00
let second_type = y.type_id();
if second_type != args_type {
if args_type == TypeId::of::<ImmutableString>() && second_type == TypeId::of::<char>() {
let y = y.read_lock::<char>().unwrap().deref().clone();
let mut x = x.write_lock::<ImmutableString>().unwrap();
match op {
"+=" => return Ok(Some(*x += y)),
_ => (),
}
}
return Ok(None);
}
if args_type == TypeId::of::<INT>() {
2020-07-28 13:11:46 +02:00
let y = y.clone().cast::<INT>();
let mut x = x.write_lock::<INT>().unwrap();
2020-08-01 06:21:15 +02:00
if cfg!(not(feature = "unchecked")) {
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())),
"~=" => return Ok(Some(*x = power(*x, y)?.as_int().unwrap())),
">>=" => 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)),
2020-08-22 16:26:49 +02: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
_ => (),
}
}
match op {
"&=" => return Ok(Some(*x &= y)),
"|=" => return Ok(Some(*x |= y)),
"^=" => return Ok(Some(*x ^= y)),
_ => (),
}
} else if args_type == TypeId::of::<bool>() {
2020-07-28 13:11:46 +02:00
let y = y.clone().cast::<bool>();
let mut x = x.write_lock::<bool>().unwrap();
match op {
"&=" => return Ok(Some(*x = *x && y)),
"|=" => return Ok(Some(*x = *x || y)),
_ => (),
}
2020-10-10 16:13:55 +02:00
} else if args_type == TypeId::of::<char>() {
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())),
_ => (),
}
} else if args_type == TypeId::of::<ImmutableString>() {
let y = y.read_lock::<ImmutableString>().unwrap().deref().clone();
let mut x = x.write_lock::<ImmutableString>().unwrap();
match op {
"+=" => return Ok(Some(*x += y)),
_ => (),
}
}
#[cfg(not(feature = "no_float"))]
if args_type == TypeId::of::<FLOAT>() {
2020-07-28 13:11:46 +02:00
let y = y.clone().cast::<FLOAT>();
let mut x = x.write_lock::<FLOAT>().unwrap();
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)),
2020-08-22 16:26:49 +02:00
"~=" => return Ok(Some(*x = x.powf(y))),
_ => (),
}
}
Ok(None)
}