2020-07-23 12:40:42 +02:00
|
|
|
//! Implement function-calling mechanism for `Engine`.
|
2017-12-20 22:16:53 +01:00
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
use crate::any::Dynamic;
|
|
|
|
use crate::calc_fn_hash;
|
|
|
|
use crate::engine::{
|
2020-07-26 09:53:22 +02:00
|
|
|
search_imports, search_namespace, search_scope_only, Engine, Imports, State, KEYWORD_DEBUG,
|
|
|
|
KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_PRINT,
|
|
|
|
KEYWORD_TYPE_OF,
|
2020-07-23 12:40:42 +02:00
|
|
|
};
|
|
|
|
use crate::error::ParseErrorType;
|
2020-07-27 12:10:45 +02:00
|
|
|
use crate::fn_native::{FnCallArgs, FnPtr};
|
2020-07-23 12:40:42 +02:00
|
|
|
use crate::module::{Module, ModuleRef};
|
|
|
|
use crate::optimize::OptimizationLevel;
|
2020-07-27 12:10:45 +02:00
|
|
|
use crate::parser::{Expr, ImmutableString, AST, INT};
|
2020-07-23 12:40:42 +02:00
|
|
|
use crate::result::EvalAltResult;
|
2020-07-26 09:53:22 +02:00
|
|
|
use crate::scope::Scope;
|
2020-07-23 12:40:42 +02:00
|
|
|
use crate::token::Position;
|
2020-05-10 10:56:17 +02:00
|
|
|
use crate::utils::StaticVec;
|
2020-03-18 15:03:50 +01:00
|
|
|
|
2020-07-26 09:53:22 +02:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
use crate::{
|
|
|
|
parser::ScriptFnDef, r#unsafe::unsafe_cast_var_name_to_lifetime,
|
|
|
|
scope::EntryType as ScopeEntryType,
|
|
|
|
};
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
#[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};
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-07-26 09:53:22 +02:00
|
|
|
use crate::engine::{Map, Target, FN_GET, FN_SET};
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
use crate::stdlib::{
|
|
|
|
any::{type_name, TypeId},
|
|
|
|
boxed::Box,
|
|
|
|
convert::TryFrom,
|
|
|
|
format,
|
|
|
|
iter::{empty, once},
|
|
|
|
mem,
|
|
|
|
string::ToString,
|
|
|
|
vec::Vec,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// 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> {
|
2020-07-23 12:40:42 +02:00
|
|
|
#[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()..]);
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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> {
|
2020-07-23 12:40:42 +02:00
|
|
|
#[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()..]);
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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.
|
|
|
|
fn normalize_first_arg<'a>(
|
|
|
|
normalize: bool,
|
|
|
|
this_copy: &mut Dynamic,
|
|
|
|
old_this_ptr: &mut Option<&'a mut Dynamic>,
|
|
|
|
args: &mut FnCallArgs<'a>,
|
|
|
|
) {
|
|
|
|
// Only do it for method calls with arguments.
|
|
|
|
if !normalize || args.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clone the original value.
|
|
|
|
*this_copy = args[0].clone();
|
|
|
|
|
|
|
|
// Replace the first reference with a reference to the clone, force-casting the lifetime.
|
|
|
|
// Keep the original reference. Must remember to restore it later with `restore_first_arg_of_method_call`.
|
|
|
|
//
|
|
|
|
// # Safety
|
|
|
|
//
|
|
|
|
// Blindly casting a a reference to another lifetime saves on allocations and string cloning,
|
|
|
|
// but must be used with the utmost care.
|
|
|
|
//
|
|
|
|
// We can do this here because, at the end of this scope, we'd restore the original reference
|
|
|
|
// with `restore_first_arg_of_method_call`. Therefore this shorter lifetime does not get "out".
|
|
|
|
let this_pointer = mem::replace(args.get_mut(0).unwrap(), unsafe {
|
|
|
|
mem::transmute(this_copy)
|
|
|
|
});
|
|
|
|
|
|
|
|
*old_this_ptr = Some(this_pointer);
|
2017-12-20 22:16:53 +01:00
|
|
|
}
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
/// This function restores the first argument that was replaced by `normalize_first_arg_of_method_call`.
|
|
|
|
fn restore_first_arg<'a>(old_this_ptr: Option<&'a mut Dynamic>, args: &mut FnCallArgs<'a>) {
|
|
|
|
if let Some(this_pointer) = old_this_ptr {
|
|
|
|
args[0] = this_pointer;
|
|
|
|
}
|
2017-12-20 22:16:53 +01:00
|
|
|
}
|
|
|
|
|
2020-07-23 12:40:42 +02:00
|
|
|
impl Engine {
|
|
|
|
/// Universal method for calling functions either registered with the `Engine` or written in Rhai.
|
|
|
|
/// 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 `()`!
|
|
|
|
pub(crate) fn call_fn_raw(
|
|
|
|
&self,
|
2020-07-26 09:53:22 +02:00
|
|
|
_scope: &mut Scope,
|
|
|
|
_mods: &mut Imports,
|
2020-07-23 12:40:42 +02:00
|
|
|
state: &mut State,
|
|
|
|
lib: &Module,
|
|
|
|
fn_name: &str,
|
|
|
|
(hash_fn, hash_script): (u64, u64),
|
|
|
|
args: &mut FnCallArgs,
|
|
|
|
is_ref: bool,
|
2020-07-26 09:53:22 +02:00
|
|
|
_is_method: bool,
|
2020-07-27 06:52:32 +02:00
|
|
|
pub_only: bool,
|
2020-07-23 12:40:42 +02:00
|
|
|
def_val: Option<bool>,
|
2020-07-26 09:53:22 +02:00
|
|
|
_level: usize,
|
2020-07-23 12:40:42 +02:00
|
|
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
|
|
|
self.inc_operations(state)?;
|
|
|
|
|
|
|
|
let native_only = hash_script == 0;
|
|
|
|
|
|
|
|
// Check for stack overflow
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
2020-07-26 09:53:22 +02:00
|
|
|
if _level > self.limits.max_call_stack_depth {
|
2020-07-23 12:40:42 +02:00
|
|
|
return Err(Box::new(
|
|
|
|
EvalAltResult::ErrorStackOverflow(Position::none()),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut this_copy: Dynamic = Default::default();
|
|
|
|
let mut old_this_ptr: Option<&mut Dynamic> = None;
|
|
|
|
|
|
|
|
// Search for the function
|
|
|
|
// First search in script-defined functions (can override built-in)
|
|
|
|
// Then search registered native functions (can override packages)
|
|
|
|
// Then search packages
|
|
|
|
// NOTE: We skip script functions for global_module and packages, and native functions for lib
|
|
|
|
let func = if !native_only {
|
2020-07-27 12:10:45 +02:00
|
|
|
lib.get_fn(hash_script, pub_only) //.or_else(|| lib.get_fn(hash_fn, pub_only))
|
2020-07-23 12:40:42 +02:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2020-07-27 12:10:45 +02:00
|
|
|
//.or_else(|| self.global_module.get_fn(hash_script, pub_only))
|
|
|
|
.or_else(|| self.global_module.get_fn(hash_fn, pub_only))
|
|
|
|
//.or_else(|| self.packages.get_fn(hash_script, pub_only))
|
|
|
|
.or_else(|| self.packages.get_fn(hash_fn, pub_only));
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
if let Some(func) = func {
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
2020-07-26 09:53:22 +02:00
|
|
|
let need_normalize = is_ref && (func.is_pure() || (func.is_script() && !_is_method));
|
2020-07-23 12:40:42 +02:00
|
|
|
#[cfg(feature = "no_function")]
|
|
|
|
let need_normalize = is_ref && func.is_pure();
|
|
|
|
|
|
|
|
// Calling pure function but the first argument is a reference?
|
|
|
|
normalize_first_arg(need_normalize, &mut this_copy, &mut old_this_ptr, args);
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
if func.is_script() {
|
|
|
|
// Run scripted function
|
|
|
|
let fn_def = func.get_fn_def();
|
|
|
|
|
|
|
|
// Method call of script function - map first argument to `this`
|
2020-07-26 09:53:22 +02:00
|
|
|
return if _is_method {
|
2020-07-23 12:40:42 +02:00
|
|
|
let (first, rest) = args.split_at_mut(1);
|
|
|
|
Ok((
|
|
|
|
self.call_script_fn(
|
2020-07-26 09:53:22 +02:00
|
|
|
_scope,
|
|
|
|
_mods,
|
2020-07-23 12:40:42 +02:00
|
|
|
state,
|
|
|
|
lib,
|
|
|
|
&mut Some(first[0]),
|
|
|
|
fn_name,
|
|
|
|
fn_def,
|
|
|
|
rest,
|
2020-07-26 09:53:22 +02:00
|
|
|
_level,
|
2020-07-23 12:40:42 +02:00
|
|
|
)?,
|
|
|
|
false,
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
let result = self.call_script_fn(
|
2020-07-26 09:53:22 +02:00
|
|
|
_scope, _mods, state, lib, &mut None, fn_name, fn_def, args, _level,
|
2020-07-23 12:40:42 +02:00
|
|
|
)?;
|
|
|
|
|
|
|
|
// Restore the original reference
|
|
|
|
restore_first_arg(old_this_ptr, args);
|
|
|
|
|
|
|
|
Ok((result, false))
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run external function
|
|
|
|
let result = func.get_native_fn()(self, lib, args)?;
|
|
|
|
|
|
|
|
// Restore the original reference
|
|
|
|
restore_first_arg(old_this_ptr, args);
|
|
|
|
|
|
|
|
// 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| {
|
|
|
|
Box::new(EvalAltResult::ErrorMismatchOutputType(
|
|
|
|
self.map_type_name(type_name::<ImmutableString>()).into(),
|
|
|
|
typ.into(),
|
|
|
|
Position::none(),
|
|
|
|
))
|
|
|
|
})?)
|
|
|
|
.into(),
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
KEYWORD_DEBUG => (
|
|
|
|
(self.debug)(result.as_str().map_err(|typ| {
|
|
|
|
Box::new(EvalAltResult::ErrorMismatchOutputType(
|
|
|
|
self.map_type_name(type_name::<ImmutableString>()).into(),
|
|
|
|
typ.into(),
|
|
|
|
Position::none(),
|
|
|
|
))
|
|
|
|
})?)
|
|
|
|
.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.into(), false));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Getter function not found?
|
|
|
|
if let Some(prop) = extract_prop_from_getter(fn_name) {
|
|
|
|
return Err(Box::new(EvalAltResult::ErrorDotExpr(
|
2020-07-27 12:10:45 +02:00
|
|
|
format!(
|
|
|
|
"Unknown property '{}' for {}, or it is write-only",
|
|
|
|
prop,
|
|
|
|
self.map_type_name(args[0].type_name())
|
|
|
|
),
|
2020-07-23 12:40:42 +02:00
|
|
|
Position::none(),
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setter function not found?
|
|
|
|
if let Some(prop) = extract_prop_from_setter(fn_name) {
|
|
|
|
return Err(Box::new(EvalAltResult::ErrorDotExpr(
|
2020-07-27 12:10:45 +02:00
|
|
|
format!(
|
|
|
|
"Unknown property '{}' for {}, or it is read-only",
|
|
|
|
prop,
|
|
|
|
self.map_type_name(args[0].type_name())
|
|
|
|
),
|
2020-07-23 12:40:42 +02:00
|
|
|
Position::none(),
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// index getter function not found?
|
2020-07-26 09:53:22 +02:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
2020-07-23 12:40:42 +02:00
|
|
|
if fn_name == FN_IDX_GET && args.len() == 2 {
|
|
|
|
return Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
|
|
|
|
format!(
|
|
|
|
"{} [{}]",
|
|
|
|
self.map_type_name(args[0].type_name()),
|
|
|
|
self.map_type_name(args[1].type_name()),
|
|
|
|
),
|
|
|
|
Position::none(),
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// index setter function not found?
|
2020-07-26 09:53:22 +02:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
2020-07-23 12:40:42 +02:00
|
|
|
if fn_name == FN_IDX_SET {
|
|
|
|
return Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
|
|
|
|
format!(
|
|
|
|
"{} [{}]=",
|
|
|
|
self.map_type_name(args[0].type_name()),
|
|
|
|
self.map_type_name(args[1].type_name()),
|
|
|
|
),
|
|
|
|
Position::none(),
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Raise error
|
|
|
|
Err(Box::new(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(),
|
|
|
|
)))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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 09:53:22 +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,
|
|
|
|
lib: &Module,
|
|
|
|
this_ptr: &mut Option<&mut Dynamic>,
|
|
|
|
fn_name: &str,
|
|
|
|
fn_def: &ScriptFnDef,
|
|
|
|
args: &mut FnCallArgs,
|
|
|
|
level: usize,
|
|
|
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
|
|
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)
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Evaluate the function at one higher level of call depth
|
|
|
|
let result = self
|
|
|
|
.eval_stmt(scope, mods, state, lib, this_ptr, &fn_def.body, level + 1)
|
|
|
|
.or_else(|err| match *err {
|
|
|
|
// Convert return statement to return value
|
|
|
|
EvalAltResult::Return(x, _) => Ok(x),
|
|
|
|
EvalAltResult::ErrorInFunctionCall(name, err, _) => {
|
|
|
|
Err(Box::new(EvalAltResult::ErrorInFunctionCall(
|
|
|
|
format!("{} > {}", fn_name, name),
|
|
|
|
err,
|
|
|
|
Position::none(),
|
|
|
|
)))
|
|
|
|
}
|
|
|
|
_ => Err(Box::new(EvalAltResult::ErrorInFunctionCall(
|
|
|
|
fn_name.to_string(),
|
|
|
|
err,
|
|
|
|
Position::none(),
|
|
|
|
))),
|
|
|
|
});
|
|
|
|
|
|
|
|
// 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-07-27 06:52:32 +02:00
|
|
|
fn has_override(&self, lib: &Module, hash_fn: u64, hash_script: u64, pub_only: bool) -> bool {
|
2020-07-23 12:40:42 +02:00
|
|
|
// NOTE: We skip script functions for global_module and packages, and native functions for lib
|
|
|
|
|
|
|
|
// First check script-defined functions
|
2020-07-27 12:10:45 +02:00
|
|
|
lib.contains_fn(hash_script, pub_only)
|
|
|
|
//|| lib.contains_fn(hash_fn, pub_only)
|
2020-07-27 06:52:32 +02:00
|
|
|
// Then check registered functions
|
2020-07-27 12:10:45 +02:00
|
|
|
//|| self.global_module.contains_fn(hash_script, pub_only)
|
|
|
|
|| self.global_module.contains_fn(hash_fn, pub_only)
|
2020-07-27 06:52:32 +02:00
|
|
|
// Then check packages
|
2020-07-27 12:10:45 +02:00
|
|
|
//|| self.packages.contains_fn(hash_script, pub_only)
|
|
|
|
|| self.packages.contains_fn(hash_fn, pub_only)
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Perform an actual function call, 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,
|
|
|
|
native_only: bool,
|
|
|
|
hash_script: u64,
|
|
|
|
args: &mut FnCallArgs,
|
|
|
|
is_ref: bool,
|
|
|
|
is_method: bool,
|
2020-07-27 06:52:32 +02:00
|
|
|
pub_only: bool,
|
2020-07-23 12:40:42 +02:00
|
|
|
def_val: Option<bool>,
|
|
|
|
level: usize,
|
|
|
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
|
|
|
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
|
|
|
let arg_types = args.iter().map(|a| a.type_id());
|
|
|
|
let hash_fn = calc_fn_hash(empty(), fn_name, args.len(), arg_types);
|
|
|
|
let hashes = (hash_fn, if native_only { 0 } else { hash_script });
|
|
|
|
|
|
|
|
match fn_name {
|
|
|
|
// type_of
|
2020-07-27 06:52:32 +02:00
|
|
|
KEYWORD_TYPE_OF
|
|
|
|
if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1, pub_only) =>
|
|
|
|
{
|
2020-07-23 12:40:42 +02:00
|
|
|
Ok((
|
|
|
|
self.map_type_name(args[0].type_name()).to_string().into(),
|
|
|
|
false,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fn
|
2020-07-27 06:52:32 +02:00
|
|
|
KEYWORD_FN_PTR
|
|
|
|
if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1, pub_only) =>
|
|
|
|
{
|
2020-07-23 12:40:42 +02:00
|
|
|
Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
|
|
"'Fn' should not be called in method style. Try Fn(...);".into(),
|
|
|
|
Position::none(),
|
|
|
|
)))
|
|
|
|
}
|
|
|
|
|
|
|
|
// eval - reaching this point it must be a method-style call
|
2020-07-27 06:52:32 +02:00
|
|
|
KEYWORD_EVAL
|
|
|
|
if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1, pub_only) =>
|
|
|
|
{
|
2020-07-23 12:40:42 +02:00
|
|
|
Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
|
|
"'eval' should not be called in method style. Try eval(...);".into(),
|
|
|
|
Position::none(),
|
|
|
|
)))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Normal function call
|
|
|
|
_ => {
|
|
|
|
let mut scope = Scope::new();
|
|
|
|
let mut mods = Imports::new();
|
|
|
|
self.call_fn_raw(
|
|
|
|
&mut scope, &mut mods, state, lib, fn_name, hashes, args, is_ref, is_method,
|
2020-07-27 06:52:32 +02:00
|
|
|
pub_only, def_val, level,
|
2020-07-23 12:40:42 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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,
|
|
|
|
script: &Dynamic,
|
|
|
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
|
|
let script = script.as_str().map_err(|typ| {
|
|
|
|
EvalAltResult::ErrorMismatchOutputType(
|
|
|
|
self.map_type_name(type_name::<ImmutableString>()).into(),
|
|
|
|
typ.into(),
|
|
|
|
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
|
|
|
|
if ast.lib().num_fn() != 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.
|
2020-07-27 06:52:32 +02:00
|
|
|
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
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,
|
|
|
|
state: &mut State,
|
|
|
|
lib: &Module,
|
2020-07-27 06:52:32 +02:00
|
|
|
name: &str,
|
|
|
|
hash: u64,
|
2020-07-23 12:40:42 +02:00
|
|
|
target: &mut Target,
|
|
|
|
idx_val: Dynamic,
|
2020-07-27 06:52:32 +02:00
|
|
|
def_val: Option<bool>,
|
|
|
|
native: bool,
|
|
|
|
pub_only: bool,
|
2020-07-23 12:40:42 +02:00
|
|
|
level: usize,
|
|
|
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
|
|
|
let is_ref = target.is_ref();
|
|
|
|
let is_value = target.is_value();
|
|
|
|
|
|
|
|
// Get a reference to the mutation target Dynamic
|
|
|
|
let obj = target.as_mut();
|
|
|
|
let mut idx = idx_val.cast::<StaticVec<Dynamic>>();
|
2020-07-27 06:52:32 +02:00
|
|
|
let mut _fn_name = name;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
2020-07-26 09:53:22 +02:00
|
|
|
let (result, updated) = if _fn_name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
|
2020-07-23 12:40:42 +02:00
|
|
|
// FnPtr call
|
|
|
|
let fn_ptr = obj.downcast_ref::<FnPtr>().unwrap();
|
|
|
|
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
|
|
|
// Redirect function name
|
|
|
|
let fn_name = fn_ptr.fn_name();
|
|
|
|
// Recalculate hash
|
|
|
|
let hash = calc_fn_hash(empty(), fn_name, curry.len() + idx.len(), empty());
|
|
|
|
// Arguments are passed as-is, adding the curried arguments
|
|
|
|
let mut arg_values = curry
|
|
|
|
.iter_mut()
|
|
|
|
.chain(idx.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-27 06:52:32 +02:00
|
|
|
state, lib, fn_name, native, hash, args, false, false, pub_only, def_val, level,
|
2020-07-23 12:40:42 +02:00
|
|
|
)
|
2020-07-26 09:53:22 +02:00
|
|
|
} else if _fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::<FnPtr>() {
|
2020-07-23 12:40:42 +02:00
|
|
|
// FnPtr call on object
|
|
|
|
let fn_ptr = idx.remove(0).cast::<FnPtr>();
|
|
|
|
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
|
|
|
// Redirect function name
|
|
|
|
let fn_name = fn_ptr.get_fn_name().clone();
|
|
|
|
// Recalculate hash
|
|
|
|
let hash = calc_fn_hash(empty(), &fn_name, curry.len() + idx.len(), empty());
|
|
|
|
// Replace the first argument with the object pointer, adding the curried arguments
|
|
|
|
let mut arg_values = once(obj)
|
|
|
|
.chain(curry.iter_mut())
|
|
|
|
.chain(idx.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-27 06:52:32 +02:00
|
|
|
state, lib, &fn_name, native, hash, args, is_ref, true, pub_only, def_val, level,
|
2020-07-23 12:40:42 +02:00
|
|
|
)
|
2020-07-26 09:53:22 +02:00
|
|
|
} else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
|
2020-07-23 12:40:42 +02:00
|
|
|
// Curry call
|
|
|
|
let fn_ptr = obj.downcast_ref::<FnPtr>().unwrap();
|
|
|
|
Ok((
|
|
|
|
FnPtr::new_unchecked(
|
|
|
|
fn_ptr.get_fn_name().clone(),
|
|
|
|
fn_ptr
|
|
|
|
.curry()
|
|
|
|
.iter()
|
|
|
|
.cloned()
|
|
|
|
.chain(idx.into_iter())
|
|
|
|
.collect(),
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
false,
|
|
|
|
))
|
|
|
|
} else {
|
2020-07-26 09:53:22 +02:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-07-23 12:40:42 +02:00
|
|
|
let redirected;
|
2020-07-27 06:52:32 +02:00
|
|
|
let mut _hash = hash;
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
// Check if it is a map method call in OOP style
|
|
|
|
#[cfg(not(feature = "no_object"))]
|
|
|
|
if let Some(map) = obj.downcast_ref::<Map>() {
|
2020-07-26 09:53:22 +02:00
|
|
|
if let Some(val) = map.get(_fn_name) {
|
2020-07-23 12:40:42 +02:00
|
|
|
if let Some(f) = val.downcast_ref::<FnPtr>() {
|
|
|
|
// Remap the function name
|
|
|
|
redirected = f.get_fn_name().clone();
|
2020-07-26 09:53:22 +02:00
|
|
|
_fn_name = &redirected;
|
2020-07-23 12:40:42 +02:00
|
|
|
// Recalculate the hash based on the new function name
|
2020-07-26 09:53:22 +02:00
|
|
|
_hash = calc_fn_hash(empty(), _fn_name, idx.len(), empty());
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Attached object pointer in front of the arguments
|
|
|
|
let mut arg_values = once(obj).chain(idx.iter_mut()).collect::<StaticVec<_>>();
|
|
|
|
let args = arg_values.as_mut();
|
|
|
|
|
|
|
|
self.exec_fn_call(
|
2020-07-27 06:52:32 +02:00
|
|
|
state, lib, _fn_name, native, _hash, args, is_ref, true, pub_only, 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
|
|
|
|
|
|
|
// Feed the changed temp value back
|
|
|
|
if updated && !is_ref && !is_value {
|
|
|
|
let new_val = target.as_mut().clone();
|
|
|
|
target.set_value(new_val)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
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: &[Expr],
|
|
|
|
def_val: Option<bool>,
|
|
|
|
mut hash: u64,
|
|
|
|
native: bool,
|
2020-07-27 06:52:32 +02:00
|
|
|
pub_only: bool,
|
2020-07-23 12:40:42 +02:00
|
|
|
level: usize,
|
|
|
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
|
|
// 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-27 06:52:32 +02:00
|
|
|
if !self.has_override(lib, hash_fn, hash, pub_only) {
|
2020-07-23 12:40:42 +02:00
|
|
|
// Fn - only in function call style
|
|
|
|
let expr = args_expr.get(0).unwrap();
|
|
|
|
let arg_value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
|
|
|
|
|
|
|
return arg_value
|
|
|
|
.take_immutable_string()
|
|
|
|
.map_err(|typ| {
|
|
|
|
Box::new(EvalAltResult::ErrorMismatchOutputType(
|
|
|
|
self.map_type_name(type_name::<ImmutableString>()).into(),
|
|
|
|
typ.into(),
|
|
|
|
expr.position(),
|
|
|
|
))
|
|
|
|
})
|
|
|
|
.and_then(|s| FnPtr::try_from(s))
|
|
|
|
.map(Into::<Dynamic>::into);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle curry()
|
|
|
|
if name == KEYWORD_FN_PTR_CURRY && args_expr.len() > 1 {
|
|
|
|
let expr = args_expr.get(0).unwrap();
|
|
|
|
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
|
|
|
|
|
|
|
if !fn_ptr.is::<FnPtr>() {
|
|
|
|
return Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
|
|
|
|
self.map_type_name(type_name::<FnPtr>()).into(),
|
|
|
|
self.map_type_name(fn_ptr.type_name()).into(),
|
|
|
|
expr.position(),
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
|
|
|
|
let (fn_name, fn_curry) = fn_ptr.cast::<FnPtr>().take_data();
|
|
|
|
|
|
|
|
let curry: StaticVec<_> = args_expr
|
|
|
|
.iter()
|
|
|
|
.skip(1)
|
|
|
|
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
|
|
|
.collect::<Result<_, _>>()?;
|
|
|
|
|
|
|
|
return Ok(FnPtr::new_unchecked(
|
|
|
|
fn_name,
|
|
|
|
fn_curry.into_iter().chain(curry.into_iter()).collect(),
|
|
|
|
)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle eval()
|
|
|
|
if name == KEYWORD_EVAL && args_expr.len() == 1 {
|
|
|
|
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
|
|
|
|
|
2020-07-27 06:52:32 +02:00
|
|
|
if !self.has_override(lib, hash_fn, hash, pub_only) {
|
2020-07-23 12:40:42 +02:00
|
|
|
// eval - only in function call style
|
|
|
|
let prev_len = scope.len();
|
|
|
|
let expr = args_expr.get(0).unwrap();
|
|
|
|
let script = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
|
|
|
let result = self
|
|
|
|
.eval_script_expr(scope, mods, state, lib, &script)
|
|
|
|
.map_err(|err| err.new_position(expr.position()));
|
|
|
|
|
|
|
|
if scope.len() != prev_len {
|
|
|
|
// IMPORTANT! If the eval defines new variables in the current scope,
|
|
|
|
// all variable offsets from this point on will be mis-aligned.
|
|
|
|
state.always_search = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle call() - Redirect function call
|
|
|
|
let redirected;
|
|
|
|
let mut args_expr = args_expr.as_ref();
|
|
|
|
let mut curry: StaticVec<_> = Default::default();
|
|
|
|
let mut name = name;
|
|
|
|
|
2020-07-27 06:52:32 +02:00
|
|
|
if name == KEYWORD_FN_PTR_CALL
|
|
|
|
&& args_expr.len() >= 1
|
|
|
|
&& !self.has_override(lib, 0, hash, pub_only)
|
|
|
|
{
|
2020-07-23 12:40:42 +02:00
|
|
|
let expr = args_expr.get(0).unwrap();
|
|
|
|
let fn_name = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
|
|
|
|
|
|
|
if fn_name.is::<FnPtr>() {
|
|
|
|
let fn_ptr = fn_name.cast::<FnPtr>();
|
|
|
|
curry = fn_ptr.curry().iter().cloned().collect();
|
|
|
|
// Redirect function name
|
|
|
|
redirected = fn_ptr.take_data().0;
|
|
|
|
name = &redirected;
|
|
|
|
// Skip the first argument
|
|
|
|
args_expr = &args_expr.as_ref()[1..];
|
|
|
|
// Recalculate hash
|
|
|
|
hash = calc_fn_hash(empty(), name, curry.len() + args_expr.len(), empty());
|
|
|
|
} else {
|
|
|
|
return Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
|
|
|
|
self.map_type_name(type_name::<FnPtr>()).into(),
|
|
|
|
fn_name.type_name().into(),
|
|
|
|
expr.position(),
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Normal function call - except for Fn and eval (handled above)
|
|
|
|
let mut arg_values: StaticVec<_>;
|
|
|
|
let mut args: StaticVec<_>;
|
|
|
|
let mut is_ref = false;
|
|
|
|
|
|
|
|
if args_expr.is_empty() && curry.is_empty() {
|
|
|
|
// No arguments
|
|
|
|
args = Default::default();
|
|
|
|
} else {
|
|
|
|
// See if the first argument is a variable, if so, convert to method-call style
|
|
|
|
// in order to leverage potential &mut first argument and avoid cloning the value
|
|
|
|
match args_expr.get(0).unwrap() {
|
|
|
|
// func(x, ...) -> x.func(...)
|
|
|
|
lhs @ Expr::Variable(_) => {
|
|
|
|
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) = search_namespace(scope, mods, state, this_ptr, lhs)?;
|
|
|
|
|
|
|
|
self.inc_operations(state)
|
|
|
|
.map_err(|err| err.new_position(pos))?;
|
|
|
|
|
|
|
|
args = once(target)
|
|
|
|
.chain(curry.iter_mut())
|
|
|
|
.chain(arg_values.iter_mut())
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
is_ref = true;
|
|
|
|
}
|
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let args = args.as_mut();
|
|
|
|
self.exec_fn_call(
|
2020-07-27 06:52:32 +02:00
|
|
|
state, lib, name, native, hash, args, is_ref, false, pub_only, def_val, level,
|
2020-07-23 12:40:42 +02:00
|
|
|
)
|
|
|
|
.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: &[Expr],
|
|
|
|
def_val: Option<bool>,
|
|
|
|
hash_script: u64,
|
|
|
|
level: usize,
|
|
|
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
|
|
let modules = modules.as_ref().unwrap();
|
|
|
|
|
|
|
|
let mut arg_values: StaticVec<_>;
|
|
|
|
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
|
|
|
|
match args_expr.get(0).unwrap() {
|
|
|
|
// func(x, ...) -> x.func(...)
|
|
|
|
Expr::Variable(x) if x.1.is_none() => {
|
|
|
|
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) =
|
|
|
|
search_scope_only(scope, state, this_ptr, args_expr.get(0).unwrap())?;
|
|
|
|
|
|
|
|
self.inc_operations(state)
|
|
|
|
.map_err(|err| err.new_position(pos))?;
|
|
|
|
|
|
|
|
args = once(target).chain(arg_values.iter_mut()).collect();
|
|
|
|
}
|
|
|
|
// 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)
|
2020-07-27 12:10:45 +02:00
|
|
|
let func = match module.get_qualified_fn(hash_script) {
|
2020-07-27 06:52:32 +02:00
|
|
|
// Then search in Rust functions
|
|
|
|
None => {
|
2020-07-23 12:40:42 +02:00
|
|
|
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;
|
|
|
|
|
2020-07-27 12:10:45 +02:00
|
|
|
module.get_qualified_fn(hash_qualified_fn)
|
2020-07-23 12:40:42 +02:00
|
|
|
}
|
|
|
|
r => r,
|
|
|
|
};
|
|
|
|
|
|
|
|
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();
|
|
|
|
let fn_def = f.get_fn_def();
|
|
|
|
let mut scope = Scope::new();
|
|
|
|
let mut mods = Imports::new();
|
|
|
|
self.call_script_fn(
|
|
|
|
&mut scope, &mut mods, state, lib, &mut None, name, fn_def, args, level,
|
|
|
|
)
|
|
|
|
}
|
2020-07-27 06:52:32 +02:00
|
|
|
Some(f) => f.get_native_fn()(self, lib, args.as_mut()),
|
|
|
|
None if def_val.is_some() => Ok(def_val.unwrap().into()),
|
|
|
|
None => Err(Box::new(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-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>> {
|
|
|
|
use crate::packages::arithmetic::*;
|
|
|
|
|
|
|
|
let args_type = x.type_id();
|
|
|
|
|
|
|
|
if y.type_id() != args_type {
|
|
|
|
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-23 12:40:42 +02:00
|
|
|
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
match op {
|
|
|
|
"+" => return add(x, y).map(Into::into).map(Some),
|
|
|
|
"-" => return sub(x, y).map(Into::into).map(Some),
|
|
|
|
"*" => return mul(x, y).map(Into::into).map(Some),
|
|
|
|
"/" => return div(x, y).map(Into::into).map(Some),
|
|
|
|
"%" => return modulo(x, y).map(Into::into).map(Some),
|
|
|
|
"~" => return pow_i_i(x, y).map(Into::into).map(Some),
|
|
|
|
">>" => return shr(x, y).map(Into::into).map(Some),
|
|
|
|
"<<" => return shl(x, y).map(Into::into).map(Some),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "unchecked")]
|
|
|
|
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 pow_i_i_u(x, y).map(Into::into).map(Some),
|
|
|
|
">>" => return shr_u(x, y).map(Into::into).map(Some),
|
|
|
|
"<<" => return shl_u(x, y).map(Into::into).map(Some),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
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>();
|
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())),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
} else if args_type == TypeId::of::<ImmutableString>() {
|
|
|
|
let x = x.downcast_ref::<ImmutableString>().unwrap();
|
|
|
|
let y = y.downcast_ref::<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>();
|
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())),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
} 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>();
|
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 pow_f_f(x, y).map(Into::into).map(Some),
|
|
|
|
"==" => 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>> {
|
|
|
|
use crate::packages::arithmetic::*;
|
|
|
|
|
|
|
|
let args_type = x.type_id();
|
|
|
|
|
|
|
|
if y.type_id() != args_type {
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
|
|
|
|
if args_type == TypeId::of::<INT>() {
|
|
|
|
let x = x.downcast_mut::<INT>().unwrap();
|
2020-07-28 13:11:46 +02:00
|
|
|
let y = y.clone().cast::<INT>();
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
match op {
|
|
|
|
"+=" => return Ok(Some(*x = add(*x, y)?)),
|
|
|
|
"-=" => return Ok(Some(*x = sub(*x, y)?)),
|
|
|
|
"*=" => return Ok(Some(*x = mul(*x, y)?)),
|
|
|
|
"/=" => return Ok(Some(*x = div(*x, y)?)),
|
|
|
|
"%=" => return Ok(Some(*x = modulo(*x, y)?)),
|
|
|
|
"~=" => return Ok(Some(*x = pow_i_i(*x, y)?)),
|
|
|
|
">>=" => return Ok(Some(*x = shr(*x, y)?)),
|
|
|
|
"<<=" => return Ok(Some(*x = shl(*x, y)?)),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "unchecked")]
|
|
|
|
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)),
|
|
|
|
"~=" => return Ok(Some(*x = pow_i_i_u(*x, y)?)),
|
|
|
|
">>=" => return Ok(Some(*x = shr_u(*x, y)?)),
|
|
|
|
"<<=" => return Ok(Some(*x = shl_u(*x, y)?)),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
match op {
|
|
|
|
"&=" => return Ok(Some(*x &= y)),
|
|
|
|
"|=" => return Ok(Some(*x |= y)),
|
|
|
|
"^=" => return Ok(Some(*x ^= y)),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
} else if args_type == TypeId::of::<bool>() {
|
|
|
|
let x = x.downcast_mut::<bool>().unwrap();
|
2020-07-28 13:11:46 +02:00
|
|
|
let y = y.clone().cast::<bool>();
|
2020-07-23 12:40:42 +02:00
|
|
|
|
|
|
|
match op {
|
|
|
|
"&=" => return Ok(Some(*x = *x && y)),
|
|
|
|
"|=" => return Ok(Some(*x = *x || y)),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
} else if args_type == TypeId::of::<ImmutableString>() {
|
|
|
|
let x = x.downcast_mut::<ImmutableString>().unwrap();
|
|
|
|
let y = y.downcast_ref::<ImmutableString>().unwrap();
|
|
|
|
|
|
|
|
match op {
|
|
|
|
"+=" => return Ok(Some(*x += y)),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
if args_type == TypeId::of::<FLOAT>() {
|
|
|
|
let x = x.downcast_mut::<FLOAT>().unwrap();
|
2020-07-28 13:11:46 +02:00
|
|
|
let y = y.clone().cast::<FLOAT>();
|
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)),
|
|
|
|
"/=" => return Ok(Some(*x /= y)),
|
|
|
|
"%=" => return Ok(Some(*x %= y)),
|
|
|
|
"~=" => return Ok(Some(*x = pow_f_f(*x, y)?)),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(None)
|
|
|
|
}
|