Improve speed on common dot/index expressions.
This commit is contained in:
parent
84e3296559
commit
8501d9d33f
@ -401,6 +401,10 @@ pub enum Expr {
|
||||
/// func `(` expr `,` ... `)`
|
||||
FnCall(Box<FnCallExpr>, Position),
|
||||
/// lhs `.` rhs
|
||||
///
|
||||
/// ### Flags
|
||||
///
|
||||
/// No flags are defined at this time. Use [`NONE`][ASTFlags::NONE].
|
||||
Dot(Box<BinaryExpr>, ASTFlags, Position),
|
||||
/// lhs `[` rhs `]`
|
||||
///
|
||||
|
@ -118,7 +118,7 @@ impl ChainArgument {
|
||||
|
||||
impl Engine {
|
||||
/// Chain-evaluate a dot/index chain.
|
||||
/// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and must be set afterwards.
|
||||
/// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards.
|
||||
fn eval_dot_index_chain_helper(
|
||||
&self,
|
||||
global: &mut GlobalRuntimeState,
|
||||
@ -620,11 +620,41 @@ impl Engine {
|
||||
|
||||
let idx_values = &mut StaticVec::new_const();
|
||||
|
||||
self.eval_dot_index_chain_arguments(
|
||||
scope, global, caches, lib, this_ptr, rhs, options, chain_type, idx_values, 0, level,
|
||||
)?;
|
||||
match rhs {
|
||||
// Short-circuit for simple property access: {expr}.prop
|
||||
Expr::Property(..) if chain_type == ChainType::Dotting => {
|
||||
idx_values.push(ChainArgument::Property(rhs.position()))
|
||||
}
|
||||
Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"),
|
||||
// Short-circuit for simple method call: {expr}.func()
|
||||
Expr::FnCall(x, ..) if chain_type == ChainType::Dotting && x.args.is_empty() => {
|
||||
idx_values.push(ChainArgument::MethodCallArgs(
|
||||
Default::default(),
|
||||
Position::NONE,
|
||||
))
|
||||
}
|
||||
// Short-circuit for method call with all literal arguments: {expr}.func(1, 2, 3)
|
||||
Expr::FnCall(x, ..)
|
||||
if chain_type == ChainType::Dotting && x.args.iter().all(Expr::is_constant) =>
|
||||
{
|
||||
let args: Vec<_> = x
|
||||
.args
|
||||
.iter()
|
||||
.map(|expr| expr.get_literal_value().unwrap())
|
||||
.collect();
|
||||
|
||||
let is_assignment = new_val.is_some();
|
||||
idx_values.push(ChainArgument::MethodCallArgs(
|
||||
args.into_boxed_slice(),
|
||||
x.args[0].position(),
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
self.eval_dot_index_chain_arguments(
|
||||
scope, global, caches, lib, this_ptr, rhs, options, chain_type, idx_values, 0,
|
||||
level,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
match lhs {
|
||||
// id.??? or id[???]
|
||||
@ -645,11 +675,9 @@ impl Engine {
|
||||
global, caches, lib, &mut None, obj_ptr, root, expr, rhs, options, idx_values,
|
||||
chain_type, level, new_val,
|
||||
)
|
||||
.map(|(v, ..)| v)
|
||||
.map_err(|err| err.fill_position(op_pos))
|
||||
}
|
||||
// {expr}.??? = ??? or {expr}[???] = ???
|
||||
_ if is_assignment => unreachable!("cannot assign to an expression"),
|
||||
_ if new_val.is_some() => unreachable!("cannot assign to an expression"),
|
||||
// {expr}.??? or {expr}[???]
|
||||
expr => {
|
||||
let value = self
|
||||
@ -657,15 +685,16 @@ impl Engine {
|
||||
.flatten();
|
||||
let obj_ptr = &mut value.into();
|
||||
let root = ("", expr.start_position());
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
global, caches, lib, this_ptr, obj_ptr, root, expr, rhs, options, idx_values,
|
||||
chain_type, level, new_val,
|
||||
)
|
||||
.map(|(v, ..)| if is_assignment { Dynamic::UNIT } else { v })
|
||||
}
|
||||
}
|
||||
.map(|(v, ..)| v)
|
||||
.map_err(|err| err.fill_position(op_pos))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate a chain of indexes and store the results in a [`StaticVec`].
|
||||
/// [`StaticVec`] is used to avoid an allocation in the overwhelming cases of
|
||||
@ -798,7 +827,6 @@ impl Engine {
|
||||
}
|
||||
|
||||
/// Call a get indexer.
|
||||
/// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards.
|
||||
#[inline(always)]
|
||||
fn call_indexer_get(
|
||||
&self,
|
||||
@ -822,7 +850,6 @@ impl Engine {
|
||||
}
|
||||
|
||||
/// Call a set indexer.
|
||||
/// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards.
|
||||
#[inline(always)]
|
||||
fn call_indexer_set(
|
||||
&self,
|
||||
|
@ -184,9 +184,7 @@ impl Engine {
|
||||
*target.as_mut() = new_val;
|
||||
}
|
||||
|
||||
target
|
||||
.propagate_changed_value()
|
||||
.map_err(|err| err.fill_position(op_info.pos))
|
||||
target.propagate_changed_value(op_info.pos)
|
||||
}
|
||||
|
||||
/// Evaluate a statement.
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Type to hold a mutable reference to the target of an evaluation.
|
||||
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{Dynamic, RhaiResultOf};
|
||||
use crate::{Dynamic, Position, RhaiResultOf};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -272,10 +272,8 @@ impl<'a> Target<'a> {
|
||||
}
|
||||
/// Propagate a changed value back to the original source.
|
||||
/// This has no effect for direct references.
|
||||
///
|
||||
/// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and should be set afterwards.
|
||||
#[inline]
|
||||
pub fn propagate_changed_value(&mut self) -> RhaiResultOf<()> {
|
||||
pub fn propagate_changed_value(&mut self, pos: Position) -> RhaiResultOf<()> {
|
||||
match self {
|
||||
Self::RefMut(..) | Self::TempValue(..) => (),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
@ -287,7 +285,7 @@ impl<'a> Target<'a> {
|
||||
Box::new(crate::ERR::ErrorMismatchDataType(
|
||||
"bool".to_string(),
|
||||
err.to_string(),
|
||||
crate::Position::NONE,
|
||||
pos,
|
||||
))
|
||||
})?;
|
||||
|
||||
@ -317,7 +315,7 @@ impl<'a> Target<'a> {
|
||||
Box::new(crate::ERR::ErrorMismatchDataType(
|
||||
"integer".to_string(),
|
||||
err.to_string(),
|
||||
crate::Position::NONE,
|
||||
pos,
|
||||
))
|
||||
})?;
|
||||
|
||||
@ -338,7 +336,7 @@ impl<'a> Target<'a> {
|
||||
Box::new(crate::ERR::ErrorMismatchDataType(
|
||||
"INT".to_string(),
|
||||
err.to_string(),
|
||||
crate::Position::NONE,
|
||||
pos,
|
||||
))
|
||||
})?;
|
||||
|
||||
@ -363,7 +361,7 @@ impl<'a> Target<'a> {
|
||||
Box::new(crate::ERR::ErrorMismatchDataType(
|
||||
"char".to_string(),
|
||||
err.to_string(),
|
||||
crate::Position::NONE,
|
||||
pos,
|
||||
))
|
||||
})?;
|
||||
|
||||
|
@ -920,9 +920,7 @@ impl Engine {
|
||||
|
||||
// Propagate the changed value back to the source if necessary
|
||||
if updated {
|
||||
target
|
||||
.propagate_changed_value()
|
||||
.map_err(|err| err.fill_position(pos))?;
|
||||
target.propagate_changed_value(pos)?;
|
||||
}
|
||||
|
||||
Ok((result, updated))
|
||||
|
Loading…
Reference in New Issue
Block a user