Refactor fast operators.
This commit is contained in:
parent
ce56c43bc0
commit
ece522ce2f
@ -3,17 +3,9 @@
|
|||||||
use super::{Caches, EvalContext, GlobalRuntimeState, Target};
|
use super::{Caches, EvalContext, GlobalRuntimeState, Target};
|
||||||
use crate::ast::{Expr, FnCallExpr, OpAssignment};
|
use crate::ast::{Expr, FnCallExpr, OpAssignment};
|
||||||
use crate::engine::{KEYWORD_THIS, OP_CONCAT};
|
use crate::engine::{KEYWORD_THIS, OP_CONCAT};
|
||||||
use crate::eval::FnResolutionCacheEntry;
|
use crate::func::get_builtin_binary_op_fn;
|
||||||
use crate::func::{
|
|
||||||
calc_fn_params_hash, combine_hashes, gen_fn_call_signature, get_builtin_binary_op_fn,
|
|
||||||
CallableFunction,
|
|
||||||
};
|
|
||||||
use crate::types::dynamic::AccessMode;
|
use crate::types::dynamic::AccessMode;
|
||||||
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR};
|
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;
|
use std::num::NonZeroUsize;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -231,83 +223,31 @@ impl Engine {
|
|||||||
} = expr;
|
} = expr;
|
||||||
|
|
||||||
// Short-circuit native binary operator call if under Fast Operators mode
|
// 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
|
let mut lhs = self
|
||||||
.get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)?
|
.get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)?
|
||||||
.0
|
.0
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
let mut rhs = if args.len() == 2 {
|
let mut rhs = self
|
||||||
self.get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)?
|
.get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)?
|
||||||
.0
|
.0
|
||||||
.flatten()
|
.flatten();
|
||||||
} else {
|
|
||||||
Dynamic::UNIT
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut operands = [&mut lhs, &mut rhs];
|
let operands = &mut [&mut lhs, &mut rhs];
|
||||||
let operands = if args.len() == 2 {
|
|
||||||
&mut operands[..]
|
|
||||||
} else {
|
|
||||||
&mut operands[0..1]
|
|
||||||
};
|
|
||||||
|
|
||||||
let hash = calc_fn_params_hash(operands.iter().map(|a| a.type_id()));
|
if let Some(func) = get_builtin_binary_op_fn(name, operands[0], operands[1]) {
|
||||||
let hash = combine_hashes(hashes.native, hash);
|
// 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();
|
return self
|
||||||
let local_entry: CallableFunction;
|
.exec_fn_call(
|
||||||
|
None, global, caches, lib, name, *hashes, operands, false, false, pos, level,
|
||||||
let func = match cache.map.entry(hash) {
|
)
|
||||||
Entry::Vacant(entry) => {
|
.map(|(v, ..)| v);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
@ -5,7 +5,7 @@ use crate::api::events::VarDefInfo;
|
|||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
ASTFlags, BinaryExpr, Expr, Ident, OpAssignment, Stmt, SwitchCasesCollection, TryCatchBlock,
|
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::types::dynamic::{AccessMode, Union};
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR, INT,
|
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 args = &mut [&mut *lock_guard, &mut new_val];
|
||||||
let level = level + 1;
|
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(
|
match self.call_native_fn(
|
||||||
global, caches, lib, op_assign, hash, args, true, true, op_pos, level,
|
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)) =>
|
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) =>
|
||||||
{
|
{
|
||||||
// Expand to `var = var op rhs`
|
// Expand to `var = var op rhs`
|
||||||
let (value, ..) = self
|
*args[0] = self
|
||||||
.call_native_fn(
|
.call_native_fn(
|
||||||
global, caches, lib, op, hash_op, args, true, false, op_pos, level,
|
global, caches, lib, op, hash_op, args, true, false, op_pos, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(op_info.pos))?;
|
.map_err(|err| err.fill_position(op_info.pos))?
|
||||||
|
.0
|
||||||
#[cfg(not(feature = "unchecked"))]
|
.flatten();
|
||||||
self.check_data_size(&value, root.1)?;
|
|
||||||
|
|
||||||
*args[0] = value.flatten();
|
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
}
|
}
|
||||||
|
@ -197,7 +197,7 @@ impl CallableFunction {
|
|||||||
Self::Script(..) => None,
|
Self::Script(..) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Create a new [`CallableFunction::Method`] from `FnBuiltin`.
|
/// Create a new [`CallableFunction::Method`] from a [built-in function][`FnBuiltin`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn from_fn_builtin(func: FnBuiltin) -> Self {
|
pub fn from_fn_builtin(func: FnBuiltin) -> Self {
|
||||||
|
@ -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)
|
// First search for script-defined functions (can override built-in)
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[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")]
|
#[cfg(feature = "no_function")]
|
||||||
let has_script_fn = false;
|
let has_script_fn = false;
|
||||||
|
|
||||||
if !has_script_fn {
|
if !has_script_fn {
|
||||||
let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::<StaticVec<_>>();
|
let arg_values = &mut x.args.iter().map(Expr::get_literal_value).collect::<Option<StaticVec<_>>>().unwrap();
|
||||||
|
|
||||||
let result = match x.name.as_str() {
|
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()),
|
KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()),
|
||||||
|
Loading…
Reference in New Issue
Block a user