diff --git a/src/eval/expr.rs b/src/eval/expr.rs index af762e20..ff3abaff 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -3,17 +3,9 @@ use super::{Caches, EvalContext, GlobalRuntimeState, Target}; use crate::ast::{Expr, FnCallExpr, OpAssignment}; use crate::engine::{KEYWORD_THIS, OP_CONCAT}; -use crate::eval::FnResolutionCacheEntry; -use crate::func::{ - calc_fn_params_hash, combine_hashes, gen_fn_call_signature, get_builtin_binary_op_fn, - CallableFunction, -}; +use crate::func::get_builtin_binary_op_fn; use crate::types::dynamic::AccessMode; use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR}; -#[cfg(feature = "no_std")] -use hashbrown::hash_map::Entry; -#[cfg(not(feature = "no_std"))] -use std::collections::hash_map::Entry; use std::num::NonZeroUsize; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -231,83 +223,31 @@ impl Engine { } = expr; // Short-circuit native binary operator call if under Fast Operators mode - if expr.is_native_operator && self.fast_operators() && (args.len() == 1 || args.len() == 2) - { + if expr.is_native_operator && self.fast_operators() && args.len() == 2 { let mut lhs = self .get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)? .0 .flatten(); - let mut rhs = if args.len() == 2 { - self.get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)? - .0 - .flatten() - } else { - Dynamic::UNIT - }; + let mut rhs = self + .get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)? + .0 + .flatten(); - let mut operands = [&mut lhs, &mut rhs]; - let operands = if args.len() == 2 { - &mut operands[..] - } else { - &mut operands[0..1] - }; + let operands = &mut [&mut lhs, &mut rhs]; - let hash = calc_fn_params_hash(operands.iter().map(|a| a.type_id())); - let hash = combine_hashes(hashes.native, hash); + if let Some(func) = get_builtin_binary_op_fn(name, operands[0], operands[1]) { + // Built-in found + let context = (self, name, None, &*global, lib, pos, level + 1).into(); + let result = func(context, operands); + return self.check_return_value(result, pos); + } - let cache = caches.fn_resolution_cache_mut(); - let local_entry: CallableFunction; - - let func = match cache.map.entry(hash) { - Entry::Vacant(entry) => { - let func = if args.len() == 2 { - get_builtin_binary_op_fn(name, operands[0], operands[1]) - } else { - None - }; - - if let Some(f) = func { - if cache.filter.is_absent_and_set(hash) { - // Do not cache "one-hit wonders" - local_entry = CallableFunction::from_fn_builtin(f); - &local_entry - } else { - // Cache repeated calls - &entry - .insert(Some(FnResolutionCacheEntry { - func: CallableFunction::from_fn_builtin(f), - source: None, - })) - .as_ref() - .unwrap() - .func - } - } else { - let result = self.exec_fn_call( - None, global, caches, lib, name, *hashes, operands, false, false, pos, - level, - ); - return result.map(|(v, ..)| v); - } - } - Entry::Occupied(entry) => { - if let Some(entry) = entry.into_mut() { - &entry.func - } else { - let sig = gen_fn_call_signature(self, name, operands); - return Err(ERR::ErrorFunctionNotFound(sig, pos).into()); - } - } - }; - - let context = (self, name, None, &*global, lib, pos, level).into(); - let result = if func.is_plugin_fn() { - func.get_plugin_fn().unwrap().call(context, operands) - } else { - func.get_native_fn().unwrap()(context, operands) - }; - return self.check_return_value(result, pos); + return self + .exec_fn_call( + None, global, caches, lib, name, *hashes, operands, false, false, pos, level, + ) + .map(|(v, ..)| v); } #[cfg(not(feature = "no_module"))] diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 60587b4f..bfb79908 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -5,7 +5,7 @@ use crate::api::events::VarDefInfo; use crate::ast::{ ASTFlags, BinaryExpr, Expr, Ident, OpAssignment, Stmt, SwitchCasesCollection, TryCatchBlock, }; -use crate::func::get_hasher; +use crate::func::{get_builtin_op_assignment_fn, get_hasher}; use crate::types::dynamic::{AccessMode, Union}; use crate::{ Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR, INT, @@ -145,6 +145,19 @@ impl Engine { let args = &mut [&mut *lock_guard, &mut new_val]; let level = level + 1; + if self.fast_operators() { + if let Some(func) = get_builtin_op_assignment_fn(op_assign, args[0], args[1]) { + // Built-in found + let context = (self, op_assign, None, &*global, lib, op_pos, level).into(); + let result = func(context, args).map(|_| ()); + + #[cfg(not(feature = "unchecked"))] + self.check_data_size(args[0], root.1)?; + + return result; + } + } + match self.call_native_fn( global, caches, lib, op_assign, hash, args, true, true, op_pos, level, ) { @@ -155,16 +168,13 @@ impl Engine { Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) => { // Expand to `var = var op rhs` - let (value, ..) = self + *args[0] = self .call_native_fn( global, caches, lib, op, hash_op, args, true, false, op_pos, level, ) - .map_err(|err| err.fill_position(op_info.pos))?; - - #[cfg(not(feature = "unchecked"))] - self.check_data_size(&value, root.1)?; - - *args[0] = value.flatten(); + .map_err(|err| err.fill_position(op_info.pos))? + .0 + .flatten(); } Err(err) => return Err(err), } diff --git a/src/func/callable_function.rs b/src/func/callable_function.rs index c08eb845..babf1ebf 100644 --- a/src/func/callable_function.rs +++ b/src/func/callable_function.rs @@ -197,7 +197,7 @@ impl CallableFunction { Self::Script(..) => None, } } - /// Create a new [`CallableFunction::Method`] from `FnBuiltin`. + /// Create a new [`CallableFunction::Method`] from a [built-in function][`FnBuiltin`]. #[inline(always)] #[must_use] pub fn from_fn_builtin(func: FnBuiltin) -> Self { diff --git a/src/optimizer.rs b/src/optimizer.rs index b9a4dd97..216e4f3c 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1229,12 +1229,12 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { => { // First search for script-defined functions (can override built-in) #[cfg(not(feature = "no_function"))] - let has_script_fn = state.lib.iter().copied().any(|m| m.get_script_fn(&x.name, x.args.len()).is_some()); + let has_script_fn = state.lib.iter().find_map(|&m| m.get_script_fn(&x.name, x.args.len())).is_some(); #[cfg(feature = "no_function")] let has_script_fn = false; if !has_script_fn { - let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::>(); + let arg_values = &mut x.args.iter().map(Expr::get_literal_value).collect::>>().unwrap(); let result = match x.name.as_str() { KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()),