Remove constants in function call expressions.

This commit is contained in:
Stephen Chung 2022-03-05 12:06:47 +08:00
parent 0335035b0f
commit e06c2b2abb
7 changed files with 77 additions and 124 deletions

View File

@ -28,6 +28,7 @@ Enhancements
* Variable definitions are optimized so that shadowed variables are reused as much as possible to reduce memory consumption. * Variable definitions are optimized so that shadowed variables are reused as much as possible to reduce memory consumption.
* `FnAccess` and `FnNamespace` now implement `Ord` and `PartialOrd`. * `FnAccess` and `FnNamespace` now implement `Ord` and `PartialOrd`.
* The `event_handler_map` example is enhanced to prevent shadowing of the state object map. * The `event_handler_map` example is enhanced to prevent shadowing of the state object map.
* Separation of constants in function calls is removed as its performance benefit is dubious.
Version 1.5.0 Version 1.5.0

View File

@ -176,17 +176,6 @@ pub struct FnCallExpr {
pub hashes: FnCallHashes, pub hashes: FnCallHashes,
/// List of function call argument expressions. /// List of function call argument expressions.
pub args: StaticVec<Expr>, pub args: StaticVec<Expr>,
/// List of function call arguments that are constants.
///
/// Any arguments in `args` that is [`Expr::Stack`] indexes into this
/// array to find the constant for use as its argument value.
///
/// # Notes
///
/// Constant arguments are very common in function calls, and keeping each constant in
/// an [`Expr::DynamicConstant`] involves an additional allocation. Keeping the constant
/// values in an inlined array avoids these extra allocations.
pub constants: StaticVec<Dynamic>,
/// Does this function call capture the parent scope? /// Does this function call capture the parent scope?
pub capture_parent_scope: bool, pub capture_parent_scope: bool,
/// [Position] of the function name. /// [Position] of the function name.
@ -206,9 +195,6 @@ impl fmt::Debug for FnCallExpr {
ff.field("hash", &self.hashes) ff.field("hash", &self.hashes)
.field("name", &self.name) .field("name", &self.name)
.field("args", &self.args); .field("args", &self.args);
if !self.constants.is_empty() {
ff.field("constants", &self.constants);
}
ff.field("pos", &self.pos); ff.field("pos", &self.pos);
ff.finish() ff.finish()
} }
@ -409,12 +395,6 @@ pub enum Expr {
), ),
/// xxx `.` method `(` expr `,` ... `)` /// xxx `.` method `(` expr `,` ... `)`
MethodCall(Box<FnCallExpr>, Position), MethodCall(Box<FnCallExpr>, Position),
/// Stack slot for function calls. See [`FnCallExpr`] for more details.
///
/// This variant does not map to any language structure. It is used in function calls with
/// constant arguments where the `usize` number indexes into an array containing a list of
/// constant arguments for the function call.
Stack(usize, Position),
/// { [statement][Stmt] ... } /// { [statement][Stmt] ... }
Stmt(Box<StmtBlock>), Stmt(Box<StmtBlock>),
/// func `(` expr `,` ... `)` /// func `(` expr `,` ... `)`
@ -490,7 +470,6 @@ impl fmt::Debug for Expr {
} }
Self::Property(x, ..) => write!(f, "Property({})", x.2), Self::Property(x, ..) => write!(f, "Property({})", x.2),
Self::MethodCall(x, ..) => f.debug_tuple("MethodCall").field(x).finish(), Self::MethodCall(x, ..) => f.debug_tuple("MethodCall").field(x).finish(),
Self::Stack(x, ..) => write!(f, "ConstantArg[{}]", x),
Self::Stmt(x) => { Self::Stmt(x) => {
let pos = x.span(); let pos = x.span();
if !pos.is_none() { if !pos.is_none() {
@ -645,8 +624,7 @@ impl Expr {
namespace: None, namespace: None,
name: KEYWORD_FN_PTR.into(), name: KEYWORD_FN_PTR.into(),
hashes: calc_fn_hash(f.fn_name(), 1).into(), hashes: calc_fn_hash(f.fn_name(), 1).into(),
args: once(Self::Stack(0, pos)).collect(), args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
constants: once(f.fn_name().into()).collect(),
capture_parent_scope: false, capture_parent_scope: false,
pos, pos,
} }
@ -704,7 +682,6 @@ impl Expr {
| Self::Array(.., pos) | Self::Array(.., pos)
| Self::Map(.., pos) | Self::Map(.., pos)
| Self::Variable(.., pos, _) | Self::Variable(.., pos, _)
| Self::Stack(.., pos)
| Self::And(.., pos) | Self::And(.., pos)
| Self::Or(.., pos) | Self::Or(.., pos)
| Self::Index(.., pos) | Self::Index(.., pos)
@ -759,7 +736,6 @@ impl Expr {
| Self::Dot(.., pos) | Self::Dot(.., pos)
| Self::Index(.., pos) | Self::Index(.., pos)
| Self::Variable(.., pos, _) | Self::Variable(.., pos, _)
| Self::Stack(.., pos)
| Self::FnCall(.., pos) | Self::FnCall(.., pos)
| Self::MethodCall(.., pos) | Self::MethodCall(.., pos)
| Self::Custom(.., pos) | Self::Custom(.., pos)
@ -786,7 +762,7 @@ impl Expr {
Self::Stmt(x) => x.iter().all(Stmt::is_pure), Self::Stmt(x) => x.iter().all(Stmt::is_pure),
Self::Variable(..) | Self::Stack(..) => true, Self::Variable(..) => true,
_ => self.is_constant(), _ => self.is_constant(),
} }
@ -810,8 +786,7 @@ impl Expr {
| Self::IntegerConstant(..) | Self::IntegerConstant(..)
| Self::CharConstant(..) | Self::CharConstant(..)
| Self::StringConstant(..) | Self::StringConstant(..)
| Self::Unit(..) | Self::Unit(..) => true,
| Self::Stack(..) => true,
Self::InterpolatedString(x, ..) | Self::Array(x, ..) => x.iter().all(Self::is_constant), Self::InterpolatedString(x, ..) | Self::Array(x, ..) => x.iter().all(Self::is_constant),
@ -872,8 +847,6 @@ impl Expr {
Token::LeftParen => true, Token::LeftParen => true,
_ => false, _ => false,
}, },
Self::Stack(..) => false,
} }
} }
/// Recursively walk this expression. /// Recursively walk this expression.

View File

@ -684,16 +684,13 @@ impl Engine {
Expr::MethodCall(x, ..) Expr::MethodCall(x, ..)
if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => if _parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
{ {
let crate::ast::FnCallExpr { let crate::ast::FnCallExpr { args, .. } = x.as_ref();
args, constants, ..
} = x.as_ref();
let (values, pos) = args.iter().try_fold( let (values, pos) = args.iter().try_fold(
(crate::FnArgsVec::with_capacity(args.len()), Position::NONE), (crate::FnArgsVec::with_capacity(args.len()), Position::NONE),
|(mut values, mut pos), expr| { |(mut values, mut pos), expr| {
let (value, arg_pos) = self.get_arg_value( let (value, arg_pos) =
scope, global, state, lib, this_ptr, expr, constants, level, self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)?;
)?;
if values.is_empty() { if values.is_empty() {
pos = arg_pos; pos = arg_pos;
} }
@ -732,15 +729,13 @@ impl Engine {
Expr::MethodCall(x, ..) Expr::MethodCall(x, ..)
if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => if _parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
{ {
let crate::ast::FnCallExpr { let crate::ast::FnCallExpr { args, .. } = x.as_ref();
args, constants, ..
} = x.as_ref();
let (values, pos) = args.iter().try_fold( let (values, pos) = args.iter().try_fold(
(crate::FnArgsVec::with_capacity(args.len()), Position::NONE), (crate::FnArgsVec::with_capacity(args.len()), Position::NONE),
|(mut values, mut pos), expr| { |(mut values, mut pos), expr| {
let (value, arg_pos) = self.get_arg_value( let (value, arg_pos) = self.get_arg_value(
scope, global, state, lib, this_ptr, expr, constants, level, scope, global, state, lib, this_ptr, expr, level,
)?; )?;
if values.is_empty() { if values.is_empty() {
pos = arg_pos pos = arg_pos

View File

@ -212,7 +212,6 @@ impl Engine {
capture_parent_scope: capture, capture_parent_scope: capture,
hashes, hashes,
args, args,
constants,
.. ..
} = expr; } = expr;
@ -222,8 +221,7 @@ impl Engine {
let hash = hashes.native; let hash = hashes.native;
return self.make_qualified_function_call( return self.make_qualified_function_call(
scope, global, state, lib, this_ptr, namespace, name, args, constants, hash, pos, scope, global, state, lib, this_ptr, namespace, name, args, hash, pos, level,
level,
); );
} }
@ -234,8 +232,8 @@ impl Engine {
); );
self.make_function_call( self.make_function_call(
scope, global, state, lib, this_ptr, name, first_arg, args, constants, *hashes, scope, global, state, lib, this_ptr, name, first_arg, args, *hashes, *capture, pos,
*capture, pos, level, level,
) )
} }

View File

@ -958,19 +958,17 @@ impl Engine {
lib: &[&Module], lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
arg_expr: &Expr, arg_expr: &Expr,
constants: &[Dynamic],
level: usize, level: usize,
) -> RhaiResultOf<(Dynamic, Position)> { ) -> RhaiResultOf<(Dynamic, Position)> {
Ok(( #[cfg(feature = "debugging")]
if let Expr::Stack(slot, ..) = arg_expr { if self.debugger.is_some() {
if let Some(value) = arg_expr.get_literal_value() {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?; self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?;
constants[*slot].clone() return Ok((value, arg_expr.start_position()));
} else if let Some(value) = arg_expr.get_literal_value() { }
#[cfg(feature = "debugging")] }
self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?;
value
} else {
// Do not match function exit for arguments // Do not match function exit for arguments
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let reset_debugger = global.debugger.clear_status_if(|status| { let reset_debugger = global.debugger.clear_status_if(|status| {
@ -983,10 +981,7 @@ impl Engine {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
global.debugger.reset_status(reset_debugger); global.debugger.reset_status(reset_debugger);
result? Ok((result?, arg_expr.start_position()))
},
arg_expr.start_position(),
))
} }
/// Call a function in normal function-call style. /// Call a function in normal function-call style.
@ -1000,7 +995,6 @@ impl Engine {
fn_name: &str, fn_name: &str,
first_arg: Option<&Expr>, first_arg: Option<&Expr>,
args_expr: &[Expr], args_expr: &[Expr],
constants: &[Dynamic],
hashes: FnCallHashes, hashes: FnCallHashes,
capture_scope: bool, capture_scope: bool,
pos: Position, pos: Position,
@ -1019,7 +1013,7 @@ impl Engine {
KEYWORD_FN_PTR_CALL if total_args >= 1 => { KEYWORD_FN_PTR_CALL if total_args >= 1 => {
let arg = first_arg.unwrap(); let arg = first_arg.unwrap();
let (arg_value, arg_pos) = let (arg_value, arg_pos) =
self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?;
if !arg_value.is::<FnPtr>() { if !arg_value.is::<FnPtr>() {
return Err(self.make_type_mismatch_err::<FnPtr>( return Err(self.make_type_mismatch_err::<FnPtr>(
@ -1054,7 +1048,7 @@ impl Engine {
KEYWORD_FN_PTR if total_args == 1 => { KEYWORD_FN_PTR if total_args == 1 => {
let arg = first_arg.unwrap(); let arg = first_arg.unwrap();
let (arg_value, arg_pos) = let (arg_value, arg_pos) =
self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?;
// Fn - only in function call style // Fn - only in function call style
return arg_value return arg_value
@ -1068,8 +1062,8 @@ impl Engine {
// Handle curry() // Handle curry()
KEYWORD_FN_PTR_CURRY if total_args > 1 => { KEYWORD_FN_PTR_CURRY if total_args > 1 => {
let first = first_arg.unwrap(); let first = first_arg.unwrap();
let (arg_value, arg_pos) = self let (arg_value, arg_pos) =
.get_arg_value(scope, global, state, lib, this_ptr, first, constants, level)?; self.get_arg_value(scope, global, state, lib, this_ptr, first, level)?;
if !arg_value.is::<FnPtr>() { if !arg_value.is::<FnPtr>() {
return Err(self.make_type_mismatch_err::<FnPtr>( return Err(self.make_type_mismatch_err::<FnPtr>(
@ -1082,9 +1076,8 @@ impl Engine {
// Append the new curried arguments to the existing list. // Append the new curried arguments to the existing list.
let fn_curry = a_expr.iter().try_fold(fn_curry, |mut curried, expr| { let fn_curry = a_expr.iter().try_fold(fn_curry, |mut curried, expr| {
let (value, ..) = self.get_arg_value( let (value, ..) =
scope, global, state, lib, this_ptr, expr, constants, level, self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)?;
)?;
curried.push(value); curried.push(value);
Ok::<_, RhaiError>(curried) Ok::<_, RhaiError>(curried)
})?; })?;
@ -1097,7 +1090,7 @@ impl Engine {
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => { crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
let arg = first_arg.unwrap(); let arg = first_arg.unwrap();
let (arg_value, ..) = let (arg_value, ..) =
self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?;
return Ok(arg_value.is_shared().into()); return Ok(arg_value.is_shared().into());
} }
@ -1105,16 +1098,15 @@ impl Engine {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => { crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => {
let first = first_arg.unwrap(); let first = first_arg.unwrap();
let (arg_value, arg_pos) = self let (arg_value, arg_pos) =
.get_arg_value(scope, global, state, lib, this_ptr, first, constants, level)?; self.get_arg_value(scope, global, state, lib, this_ptr, first, level)?;
let fn_name = arg_value let fn_name = arg_value
.into_immutable_string() .into_immutable_string()
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?; .map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
let (arg_value, arg_pos) = self.get_arg_value( let (arg_value, arg_pos) =
scope, global, state, lib, this_ptr, &a_expr[0], constants, level, self.get_arg_value(scope, global, state, lib, this_ptr, &a_expr[0], level)?;
)?;
let num_params = arg_value let num_params = arg_value
.as_int() .as_int()
@ -1133,7 +1125,7 @@ impl Engine {
KEYWORD_IS_DEF_VAR if total_args == 1 => { KEYWORD_IS_DEF_VAR if total_args == 1 => {
let arg = first_arg.unwrap(); let arg = first_arg.unwrap();
let (arg_value, arg_pos) = let (arg_value, arg_pos) =
self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?;
let var_name = arg_value let var_name = arg_value
.into_immutable_string() .into_immutable_string()
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?; .map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
@ -1146,7 +1138,7 @@ impl Engine {
let orig_scope_len = scope.len(); let orig_scope_len = scope.len();
let arg = first_arg.unwrap(); let arg = first_arg.unwrap();
let (arg_value, pos) = let (arg_value, pos) =
self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?;
let script = &arg_value let script = &arg_value
.into_immutable_string() .into_immutable_string()
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?; .map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
@ -1195,7 +1187,7 @@ impl Engine {
.map(|&v| v) .map(|&v| v)
.chain(a_expr.iter()) .chain(a_expr.iter())
.try_for_each(|expr| { .try_for_each(|expr| {
self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)
.map(|(value, ..)| arg_values.push(value.flatten())) .map(|(value, ..)| arg_values.push(value.flatten()))
})?; })?;
args.extend(curry.iter_mut()); args.extend(curry.iter_mut());
@ -1227,7 +1219,7 @@ impl Engine {
// func(x, ...) -> x.func(...) // func(x, ...) -> x.func(...)
a_expr.iter().try_for_each(|expr| { a_expr.iter().try_for_each(|expr| {
self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)
.map(|(value, ..)| arg_values.push(value.flatten())) .map(|(value, ..)| arg_values.push(value.flatten()))
})?; })?;
@ -1262,9 +1254,7 @@ impl Engine {
.into_iter() .into_iter()
.chain(a_expr.iter()) .chain(a_expr.iter())
.try_for_each(|expr| { .try_for_each(|expr| {
self.get_arg_value( self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)
scope, global, state, lib, this_ptr, expr, constants, level,
)
.map(|(value, ..)| arg_values.push(value.flatten())) .map(|(value, ..)| arg_values.push(value.flatten()))
})?; })?;
args.extend(curry.iter_mut()); args.extend(curry.iter_mut());
@ -1290,7 +1280,6 @@ impl Engine {
namespace: &crate::module::Namespace, namespace: &crate::module::Namespace,
fn_name: &str, fn_name: &str,
args_expr: &[Expr], args_expr: &[Expr],
constants: &[Dynamic],
hash: u64, hash: u64,
pos: Position, pos: Position,
level: usize, level: usize,
@ -1313,7 +1302,7 @@ impl Engine {
arg_values.push(Dynamic::UNIT); arg_values.push(Dynamic::UNIT);
args_expr.iter().skip(1).try_for_each(|expr| { args_expr.iter().skip(1).try_for_each(|expr| {
self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)
.map(|(value, ..)| arg_values.push(value.flatten())) .map(|(value, ..)| arg_values.push(value.flatten()))
})?; })?;
@ -1344,7 +1333,7 @@ impl Engine {
} else { } else {
// func(..., ...) or func(mod::x, ...) // func(..., ...) or func(mod::x, ...)
args_expr.iter().try_for_each(|expr| { args_expr.iter().try_for_each(|expr| {
self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)
.map(|(value, ..)| arg_values.push(value.flatten())) .map(|(value, ..)| arg_values.push(value.flatten()))
})?; })?;
args.extend(arg_values.iter_mut()); args.extend(arg_values.iter_mut());

View File

@ -438,15 +438,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
Expr::FnCall(ref mut x2, ..) => { Expr::FnCall(ref mut x2, ..) => {
state.set_dirty(); state.set_dirty();
x.0 = Some(OpAssignment::new_from_base(&x2.name)); x.0 = Some(OpAssignment::new_from_base(&x2.name));
x.1.rhs = mem::take(&mut x2.args[1]);
let value = mem::take(&mut x2.args[1]);
if let Expr::Stack(slot, pos) = value {
x.1.rhs =
Expr::from_dynamic(mem::take(x2.constants.get_mut(slot).unwrap()), pos);
} else {
x.1.rhs = value;
}
} }
ref expr => unreachable!("Expr::FnCall expected but gets {:?}", expr), ref expr => unreachable!("Expr::FnCall expected but gets {:?}", expr),
} }
@ -1063,7 +1055,6 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
&& x.args[0].is_constant() && x.args[0].is_constant()
=> { => {
let fn_name = match x.args[0] { let fn_name = match x.args[0] {
Expr::Stack(slot, ..) => x.constants[slot].clone(),
Expr::StringConstant(ref s, ..) => s.clone().into(), Expr::StringConstant(ref s, ..) => s.clone().into(),
_ => Dynamic::UNIT _ => Dynamic::UNIT
}; };
@ -1088,11 +1079,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
&& x.args.iter().all(Expr::is_constant) // all arguments are constants && x.args.iter().all(Expr::is_constant) // all arguments are constants
//&& !is_valid_identifier(x.name.chars()) // cannot be scripted //&& !is_valid_identifier(x.name.chars()) // cannot be scripted
=> { => {
let arg_values = &mut x.args.iter().map(|e| match e { let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::<StaticVec<_>>();
Expr::Stack(slot, ..) => x.constants[*slot].clone(),
_ => e.get_literal_value().unwrap()
}).collect::<StaticVec<_>>();
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
match x.name.as_str() { match x.name.as_str() {
@ -1131,14 +1118,21 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false)); x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
// Move constant arguments // Move constant arguments
let constants = &mut x.constants; for arg in x.args.iter_mut() {
x.args.iter_mut().for_each(|arg| { match arg {
if let Some(value) = arg.get_literal_value() { Expr::DynamicConstant(..) | Expr::Unit(..)
| Expr::StringConstant(..) | Expr::CharConstant(..)
| Expr::BoolConstant(..) | Expr::IntegerConstant(..) => (),
#[cfg(not(feature = "no_float"))]
Expr:: FloatConstant(..) => (),
_ => if let Some(value) = arg.get_literal_value() {
state.set_dirty(); state.set_dirty();
constants.push(value); *arg = Expr::DynamicConstant(value.into(), arg.start_position());
*arg = Expr::Stack(constants.len()-1, arg.start_position()); },
}
} }
});
} }
// Eagerly call functions // Eagerly call functions
@ -1154,10 +1148,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
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| match e { let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::<StaticVec<_>>();
Expr::Stack(slot, ..) => x.constants[*slot].clone(),
_ => e.get_literal_value().unwrap()
}).collect::<StaticVec<_>>();
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()),
@ -1181,10 +1172,18 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
optimize_expr(arg, state, false); optimize_expr(arg, state, false);
// Move constant arguments // Move constant arguments
if let Some(value) = arg.get_literal_value() { match arg {
Expr::DynamicConstant(..) | Expr::Unit(..)
| Expr::StringConstant(..) | Expr::CharConstant(..)
| Expr::BoolConstant(..) | Expr::IntegerConstant(..) => (),
#[cfg(not(feature = "no_float"))]
Expr:: FloatConstant(..) => (),
_ => if let Some(value) = arg.get_literal_value() {
state.set_dirty(); state.set_dirty();
x.constants.push(value); *arg = Expr::DynamicConstant(value.into(), arg.start_position());
*arg = Expr::Stack(x.constants.len()-1, arg.start_position()); },
} }
}, },

View File

@ -521,7 +521,6 @@ impl Engine {
namespace, namespace,
hashes, hashes,
args, args,
constants: StaticVec::new_const(),
pos: settings.pos, pos: settings.pos,
} }
.into_fn_call_expr(settings.pos)); .into_fn_call_expr(settings.pos));
@ -586,7 +585,6 @@ impl Engine {
namespace, namespace,
hashes, hashes,
args, args,
constants: StaticVec::new_const(),
pos: settings.pos, pos: settings.pos,
} }
.into_fn_call_expr(settings.pos)); .into_fn_call_expr(settings.pos));