Add short-circuits to op-assignments.

This commit is contained in:
Stephen Chung
2023-03-09 22:03:27 +08:00
parent bb404a415d
commit 906ab3a295
4 changed files with 193 additions and 111 deletions

View File

@@ -7,6 +7,7 @@ use crate::ast::{
SwitchCasesCollection,
};
use crate::func::{get_builtin_op_assignment_fn, get_hasher};
use crate::tokenizer::Token;
use crate::types::dynamic::{AccessMode, Union};
use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, ERR, INT};
use std::hash::{Hash, Hasher};
@@ -130,38 +131,117 @@ impl Engine {
if let Some((hash_x, hash, op_x, op_x_str, op, op_str)) = op_info.get_op_assignment_info() {
let mut lock_guard = target.write_lock::<Dynamic>().unwrap();
let args = &mut [&mut *lock_guard, &mut new_val];
let mut done = false;
// Short-circuit built-in op-assignments if under Fast Operators mode
if self.fast_operators() {
if let Some((func, need_context)) =
get_builtin_op_assignment_fn(op_x, args[0], args[1])
{
// Built-in found
auto_restore! { let orig_level = global.level; global.level += 1 }
#[allow(clippy::wildcard_imports)]
use Token::*;
let context = need_context
.then(|| (self, op_x_str, global.source(), &*global, pos).into());
return func(context, args).map(|_| ());
done = true;
// For extremely simple primary data operations, do it directly
// to avoid the overhead of calling a function.
match (&mut lock_guard.0, &mut new_val.0) {
(Union::Bool(b1, ..), Union::Bool(b2, ..)) => match op_x {
AndAssign => *b1 = *b1 && *b2,
OrAssign => *b1 = *b1 || *b2,
XOrAssign => *b1 = *b1 ^ *b2,
_ => done = false,
},
(Union::Int(n1, ..), Union::Int(n2, ..)) => {
#[cfg(not(feature = "unchecked"))]
#[allow(clippy::wildcard_imports)]
use crate::packages::arithmetic::arith_basic::INT::functions::*;
#[cfg(not(feature = "unchecked"))]
match op_x {
PlusAssign => {
*n1 = add(*n1, *n2).map_err(|err| err.fill_position(pos))?
}
MinusAssign => {
*n1 = subtract(*n1, *n2).map_err(|err| err.fill_position(pos))?
}
MultiplyAssign => {
*n1 = multiply(*n1, *n2).map_err(|err| err.fill_position(pos))?
}
DivideAssign => {
*n1 = divide(*n1, *n2).map_err(|err| err.fill_position(pos))?
}
ModuloAssign => {
*n1 = modulo(*n1, *n2).map_err(|err| err.fill_position(pos))?
}
_ => done = false,
}
#[cfg(feature = "unchecked")]
match op_x {
PlusAssign => *n1 += *n2,
MinusAssign => *n1 -= *n2,
MultiplyAssign => *n1 *= *n2,
DivideAssign => *n1 /= *n2,
ModuloAssign => *n1 %= *n2,
_ => done = false,
}
}
#[cfg(not(feature = "no_float"))]
(Union::Float(f1, ..), Union::Float(f2, ..)) => match op_x {
PlusAssign => **f1 += **f2,
MinusAssign => **f1 -= **f2,
MultiplyAssign => **f1 *= **f2,
DivideAssign => **f1 /= **f2,
ModuloAssign => **f1 %= **f2,
_ => done = false,
},
#[cfg(not(feature = "no_float"))]
(Union::Float(f1, ..), Union::Int(n2, ..)) => match op_x {
PlusAssign => **f1 += *n2 as crate::FLOAT,
MinusAssign => **f1 -= *n2 as crate::FLOAT,
MultiplyAssign => **f1 *= *n2 as crate::FLOAT,
DivideAssign => **f1 /= *n2 as crate::FLOAT,
ModuloAssign => **f1 %= *n2 as crate::FLOAT,
_ => done = false,
},
_ => done = false,
}
if !done {
if let Some((func, need_context)) =
get_builtin_op_assignment_fn(op_x, &*lock_guard, &new_val)
{
// We may not need to bump the level because built-in's do not need it.
//auto_restore! { let orig_level = global.level; global.level += 1 }
let args = &mut [&mut *lock_guard, &mut new_val];
let context = need_context
.then(|| (self, op_x_str, global.source(), &*global, pos).into());
let _ = func(context, args).map_err(|err| err.fill_position(pos))?;
done = true;
}
}
}
let opx = Some(op_x);
if !done {
let opx = Some(op_x);
let args = &mut [&mut *lock_guard, &mut new_val];
match self.exec_native_fn_call(global, caches, op_x_str, opx, hash_x, args, true, pos) {
Ok(_) => (),
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_x_str)) =>
match self
.exec_native_fn_call(global, caches, op_x_str, opx, hash_x, args, true, pos)
{
// Expand to `var = var op rhs`
let op = Some(op);
Ok(_) => (),
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_x_str)) =>
{
// Expand to `var = var op rhs`
let op = Some(op);
*args[0] = self
.exec_native_fn_call(global, caches, op_str, op, hash, args, true, pos)?
.0;
*args[0] = self
.exec_native_fn_call(global, caches, op_str, op, hash, args, true, pos)?
.0;
}
Err(err) => return Err(err),
}
Err(err) => return Err(err),
}
self.check_data_size(&*args[0], root.position())?;
self.check_data_size(&*args[0], root.position())?;
}
} else {
// Normal assignment
match target {