Evaluate function call args more efficiently.
This commit is contained in:
parent
afb651d0aa
commit
f399e8a905
@ -229,9 +229,14 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// Normal function call
|
// Normal function call
|
||||||
|
let (first_arg, args) = args.split_first().map_or_else(
|
||||||
|
|| (None, args.as_ref()),
|
||||||
|
|(first, rest)| (Some(first), rest),
|
||||||
|
);
|
||||||
|
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
scope, global, state, lib, this_ptr, name, args, constants, *hashes, pos, *capture,
|
scope, global, state, lib, this_ptr, name, first_arg, args, constants, *hashes,
|
||||||
level,
|
pos, *capture, level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
132
src/func/call.rs
132
src/func/call.rs
@ -887,12 +887,16 @@ impl Engine {
|
|||||||
arg_expr: &Expr,
|
arg_expr: &Expr,
|
||||||
constants: &[Dynamic],
|
constants: &[Dynamic],
|
||||||
) -> RhaiResultOf<(Dynamic, Position)> {
|
) -> RhaiResultOf<(Dynamic, Position)> {
|
||||||
match arg_expr {
|
Ok((
|
||||||
Expr::Stack(slot, pos) => Ok((constants[*slot].clone(), *pos)),
|
if let Expr::Stack(slot, _) = arg_expr {
|
||||||
ref arg => self
|
constants[*slot].clone()
|
||||||
.eval_expr(scope, global, state, lib, this_ptr, arg, level)
|
} else if let Some(value) = arg_expr.get_literal_value() {
|
||||||
.map(|v| (v, arg.position())),
|
value
|
||||||
}
|
} else {
|
||||||
|
self.eval_expr(scope, global, state, lib, this_ptr, arg_expr, level)?
|
||||||
|
},
|
||||||
|
arg_expr.position(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a function in normal function-call style.
|
/// Call a function in normal function-call style.
|
||||||
@ -904,6 +908,7 @@ impl Engine {
|
|||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
|
first_arg: Option<&Expr>,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
constants: &[Dynamic],
|
constants: &[Dynamic],
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
@ -911,8 +916,9 @@ impl Engine {
|
|||||||
capture_scope: bool,
|
capture_scope: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
|
let mut first_arg = first_arg;
|
||||||
let mut a_expr = args_expr;
|
let mut a_expr = args_expr;
|
||||||
let mut total_args = a_expr.len();
|
let mut total_args = if first_arg.is_some() { 1 } else { 0 } + a_expr.len();
|
||||||
let mut curry = FnArgsVec::new_const();
|
let mut curry = FnArgsVec::new_const();
|
||||||
let mut name = fn_name;
|
let mut name = fn_name;
|
||||||
let mut hashes = hashes;
|
let mut hashes = hashes;
|
||||||
@ -921,26 +927,29 @@ impl Engine {
|
|||||||
match name {
|
match name {
|
||||||
// Handle call()
|
// Handle call()
|
||||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||||
let (arg, arg_pos) = self.get_arg_value(
|
let arg = first_arg.unwrap();
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
let (arg_value, arg_pos) =
|
||||||
)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
|
||||||
|
|
||||||
if !arg.is::<FnPtr>() {
|
if !arg_value.is::<FnPtr>() {
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
self.map_type_name(arg.type_name()),
|
self.map_type_name(arg_value.type_name()),
|
||||||
arg_pos,
|
arg_pos,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_ptr = arg.cast::<FnPtr>();
|
let fn_ptr = arg_value.cast::<FnPtr>();
|
||||||
curry.extend(fn_ptr.curry().iter().cloned());
|
curry.extend(fn_ptr.curry().iter().cloned());
|
||||||
|
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
redirected = fn_ptr.take_data().0;
|
redirected = fn_ptr.take_data().0;
|
||||||
name = &redirected;
|
name = &redirected;
|
||||||
|
|
||||||
// Skip the first argument
|
// Shift the arguments
|
||||||
|
first_arg = a_expr.get(0);
|
||||||
|
if !a_expr.is_empty() {
|
||||||
a_expr = &a_expr[1..];
|
a_expr = &a_expr[1..];
|
||||||
|
}
|
||||||
total_args -= 1;
|
total_args -= 1;
|
||||||
|
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
@ -953,12 +962,12 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// Handle Fn()
|
// Handle Fn()
|
||||||
KEYWORD_FN_PTR if total_args == 1 => {
|
KEYWORD_FN_PTR if total_args == 1 => {
|
||||||
let (arg, arg_pos) = self.get_arg_value(
|
let arg = first_arg.unwrap();
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
let (arg_value, arg_pos) =
|
||||||
)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
|
||||||
|
|
||||||
// Fn - only in function call style
|
// Fn - only in function call style
|
||||||
return arg
|
return 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))
|
||||||
.and_then(FnPtr::try_from)
|
.and_then(FnPtr::try_from)
|
||||||
@ -968,30 +977,30 @@ impl Engine {
|
|||||||
|
|
||||||
// Handle curry()
|
// Handle curry()
|
||||||
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
||||||
let (arg, arg_pos) = self.get_arg_value(
|
let first = first_arg.unwrap();
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
let (arg_value, arg_pos) = self
|
||||||
)?;
|
.get_arg_value(scope, global, state, lib, this_ptr, level, first, constants)?;
|
||||||
|
|
||||||
if !arg.is::<FnPtr>() {
|
if !arg_value.is::<FnPtr>() {
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
self.map_type_name(arg.type_name()),
|
self.map_type_name(arg_value.type_name()),
|
||||||
arg_pos,
|
arg_pos,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (name, fn_curry) = arg.cast::<FnPtr>().take_data();
|
let (name, fn_curry) = arg_value.cast::<FnPtr>().take_data();
|
||||||
|
|
||||||
// Append the new curried arguments to the existing list.
|
// Append the new curried arguments to the existing list.
|
||||||
let fn_curry = a_expr.iter().skip(1).try_fold(
|
let fn_curry =
|
||||||
fn_curry,
|
a_expr
|
||||||
|mut curried, expr| -> RhaiResultOf<_> {
|
.iter()
|
||||||
|
.try_fold(fn_curry, |mut curried, expr| -> RhaiResultOf<_> {
|
||||||
let (value, _) = self.get_arg_value(
|
let (value, _) = self.get_arg_value(
|
||||||
scope, global, state, lib, this_ptr, level, expr, constants,
|
scope, global, state, lib, this_ptr, level, expr, constants,
|
||||||
)?;
|
)?;
|
||||||
curried.push(value);
|
curried.push(value);
|
||||||
Ok(curried)
|
Ok(curried)
|
||||||
},
|
})?;
|
||||||
)?;
|
|
||||||
|
|
||||||
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
|
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
|
||||||
}
|
}
|
||||||
@ -999,28 +1008,28 @@ impl Engine {
|
|||||||
// Handle is_shared()
|
// Handle is_shared()
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
||||||
let (arg, _) = self.get_arg_value(
|
let arg = first_arg.unwrap();
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
let (arg_value, _) =
|
||||||
)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
|
||||||
return Ok(arg.is_shared().into());
|
return Ok(arg_value.is_shared().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_def_fn()
|
// Handle is_def_fn()
|
||||||
#[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 (arg, arg_pos) = self.get_arg_value(
|
let first = first_arg.unwrap();
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
let (arg_value, arg_pos) = self
|
||||||
)?;
|
.get_arg_value(scope, global, state, lib, this_ptr, level, first, constants)?;
|
||||||
|
|
||||||
let fn_name = arg
|
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, arg_pos) = self.get_arg_value(
|
let (arg_value, arg_pos) = self.get_arg_value(
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[1], constants,
|
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let num_params = arg
|
let num_params = arg_value
|
||||||
.as_int()
|
.as_int()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, arg_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, arg_pos))?;
|
||||||
|
|
||||||
@ -1035,10 +1044,10 @@ impl Engine {
|
|||||||
|
|
||||||
// Handle is_def_var()
|
// Handle is_def_var()
|
||||||
KEYWORD_IS_DEF_VAR if total_args == 1 => {
|
KEYWORD_IS_DEF_VAR if total_args == 1 => {
|
||||||
let (arg, arg_pos) = self.get_arg_value(
|
let arg = first_arg.unwrap();
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
let (arg_value, arg_pos) =
|
||||||
)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
|
||||||
let var_name = arg
|
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))?;
|
||||||
return Ok(scope.contains(&var_name).into());
|
return Ok(scope.contains(&var_name).into());
|
||||||
@ -1048,10 +1057,10 @@ impl Engine {
|
|||||||
KEYWORD_EVAL if total_args == 1 => {
|
KEYWORD_EVAL if total_args == 1 => {
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
let (value, pos) = self.get_arg_value(
|
let arg = first_arg.unwrap();
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
let (arg_value, pos) =
|
||||||
)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
|
||||||
let script = &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))?;
|
||||||
let result = self.eval_script_expr_in_place(
|
let result = self.eval_script_expr_in_place(
|
||||||
@ -1085,8 +1094,8 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Normal function call - except for Fn, curry, call and eval (handled above)
|
// Normal function call - except for Fn, curry, call and eval (handled above)
|
||||||
let mut arg_values = FnArgsVec::with_capacity(a_expr.len());
|
let mut arg_values = FnArgsVec::with_capacity(total_args);
|
||||||
let mut args = FnArgsVec::with_capacity(a_expr.len() + curry.len());
|
let mut args = FnArgsVec::with_capacity(total_args + curry.len());
|
||||||
let mut is_ref_mut = false;
|
let mut is_ref_mut = false;
|
||||||
|
|
||||||
// Capture parent scope?
|
// Capture parent scope?
|
||||||
@ -1094,7 +1103,11 @@ impl Engine {
|
|||||||
// If so, do it separately because we cannot convert the first argument (if it is a simple
|
// If so, do it separately because we cannot convert the first argument (if it is a simple
|
||||||
// variable access) to &mut because `scope` is needed.
|
// variable access) to &mut because `scope` is needed.
|
||||||
if capture_scope && !scope.is_empty() {
|
if capture_scope && !scope.is_empty() {
|
||||||
a_expr.iter().try_for_each(|expr| {
|
first_arg
|
||||||
|
.iter()
|
||||||
|
.map(|&v| v)
|
||||||
|
.chain(a_expr.iter())
|
||||||
|
.try_for_each(|expr| {
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants)
|
self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants)
|
||||||
.map(|(value, _)| arg_values.push(value.flatten()))
|
.map(|(value, _)| arg_values.push(value.flatten()))
|
||||||
})?;
|
})?;
|
||||||
@ -1113,17 +1126,17 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call with blank scope
|
// Call with blank scope
|
||||||
if a_expr.is_empty() && curry.is_empty() {
|
if total_args == 0 && curry.is_empty() {
|
||||||
// No arguments
|
// No arguments
|
||||||
} else {
|
} else {
|
||||||
// If the first argument is a variable, and there is no curried arguments,
|
// If the first argument is a variable, and there is no curried arguments,
|
||||||
// convert to method-call style in order to leverage potential &mut first argument and
|
// convert to method-call style in order to leverage potential &mut first argument and
|
||||||
// avoid cloning the value
|
// avoid cloning the value
|
||||||
if curry.is_empty() && !a_expr.is_empty() && a_expr[0].is_variable_access(false) {
|
if curry.is_empty() && first_arg.map_or(false, |expr| expr.is_variable_access(false)) {
|
||||||
// func(x, ...) -> x.func(...)
|
// func(x, ...) -> x.func(...)
|
||||||
let (first_expr, rest_expr) = a_expr.split_first().unwrap();
|
let first_expr = first_arg.unwrap();
|
||||||
|
|
||||||
rest_expr.iter().try_for_each(|expr| {
|
a_expr.iter().try_for_each(|expr| {
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants)
|
self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants)
|
||||||
.map(|(value, _)| arg_values.push(value.flatten()))
|
.map(|(value, _)| arg_values.push(value.flatten()))
|
||||||
})?;
|
})?;
|
||||||
@ -1155,8 +1168,13 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// func(..., ...)
|
// func(..., ...)
|
||||||
a_expr.iter().try_for_each(|expr| {
|
first_arg
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants)
|
.into_iter()
|
||||||
|
.chain(a_expr.iter())
|
||||||
|
.try_for_each(|expr| {
|
||||||
|
self.get_arg_value(
|
||||||
|
scope, global, state, lib, this_ptr, level, expr, constants,
|
||||||
|
)
|
||||||
.map(|(value, _)| arg_values.push(value.flatten()))
|
.map(|(value, _)| arg_values.push(value.flatten()))
|
||||||
})?;
|
})?;
|
||||||
args.extend(curry.iter_mut());
|
args.extend(curry.iter_mut());
|
||||||
|
Loading…
Reference in New Issue
Block a user