diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 9b1bc2bf..1e1e8de5 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -197,15 +197,17 @@ impl Engine { name, hashes, args, .. } = x.as_ref(); - let call_args = &mut ( - idx_values.drain(idx_values.len() - args.len()..).collect(), - args.get(0).map_or(Position::NONE, Expr::position), - ); + let offset = idx_values.len() - args.len(); + let call_args = &mut idx_values[offset..]; + let pos1 = args.get(0).map_or(Position::NONE, Expr::position); let result = self.make_method_call( - global, caches, lib, name, *hashes, target, call_args, *pos, level, + global, caches, lib, name, *hashes, target, call_args, pos1, *pos, + level, ); + idx_values.truncate(offset); + #[cfg(feature = "debugging")] global.debugger.reset_status(reset_debugger); @@ -372,16 +374,17 @@ impl Engine { name, hashes, args, .. } = x.as_ref(); - let call_args = &mut ( - idx_values.drain(idx_values.len() - args.len()..).collect(), - args.get(0).map_or(Position::NONE, Expr::position), - ); + let offset = idx_values.len() - args.len(); + let call_args = &mut idx_values[offset..]; + let pos1 = args.get(0).map_or(Position::NONE, Expr::position); let result = self.make_method_call( - global, caches, lib, name, *hashes, target, call_args, pos, - level, + global, caches, lib, name, *hashes, target, call_args, pos1, + pos, level, ); + idx_values.truncate(offset); + #[cfg(feature = "debugging")] global.debugger.reset_status(reset_debugger); @@ -497,16 +500,17 @@ impl Engine { } = f.as_ref(); let rhs_chain = rhs.into(); - let call_args = &mut ( - idx_values.drain(idx_values.len() - args.len()..).collect(), - args.get(0).map_or(Position::NONE, Expr::position), - ); + let offset = idx_values.len() - args.len(); + let call_args = &mut idx_values[offset..]; + let pos1 = args.get(0).map_or(Position::NONE, Expr::position); let result = self.make_method_call( - global, caches, lib, name, *hashes, target, call_args, pos, - level, + global, caches, lib, name, *hashes, target, call_args, pos1, + pos, level, ); + idx_values.truncate(offset); + #[cfg(feature = "debugging")] global.debugger.reset_status(reset_debugger); @@ -570,11 +574,14 @@ impl Engine { Expr::FnCall(x, ..) if chain_type == ChainType::Dotting && x.args.iter().all(Expr::is_constant) => { - x.args - .iter() - .map(|expr| expr.get_literal_value().unwrap()) - .for_each(|v| idx_values.push(v)) + idx_values.extend(x.args.iter().map(|expr| expr.get_literal_value().unwrap())) } + // Short-circuit for indexing with literal: {expr}[1] + #[cfg(not(feature = "no_index"))] + _ if chain_type == ChainType::Indexing && rhs.is_constant() => { + idx_values.push(rhs.get_literal_value().unwrap()) + } + // All other patterns - evaluate the arguments chain _ => { self.eval_dot_index_chain_arguments( scope, global, caches, lib, this_ptr, rhs, options, chain_type, idx_values, 0, @@ -649,9 +656,11 @@ impl Engine { if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => { for arg_expr in x.args.as_ref() { - let (value, _) = - self.get_arg_value(scope, global, caches, lib, this_ptr, arg_expr, level)?; - idx_values.push(value.flatten()); + idx_values.push( + self.get_arg_value(scope, global, caches, lib, this_ptr, arg_expr, level)? + .0 + .flatten(), + ); } } #[cfg(not(feature = "no_object"))] @@ -681,10 +690,13 @@ impl Engine { if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => { for arg_expr in x.args.as_ref() { - let (value, _) = self.get_arg_value( - scope, global, caches, lib, this_ptr, arg_expr, level, - )?; - arg_values.push(value.flatten()); + arg_values.push( + self.get_arg_value( + scope, global, caches, lib, this_ptr, arg_expr, level, + )? + .0 + .flatten(), + ); } } #[cfg(not(feature = "no_object"))] diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 617b1db4..21f9e739 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -315,7 +315,7 @@ impl Debugger { /// Change the current status to [`CONTINUE`][DebuggerStatus::CONTINUE] and return the previous status. pub(crate) fn clear_status_if( &mut self, - filter: impl Fn(&DebuggerStatus) -> bool, + filter: impl FnOnce(&DebuggerStatus) -> bool, ) -> Option { if filter(&self.status) { Some(mem::replace(&mut self.status, DebuggerStatus::CONTINUE)) diff --git a/src/eval/target.rs b/src/eval/target.rs index 2e462677..a2c4d34c 100644 --- a/src/eval/target.rs +++ b/src/eval/target.rs @@ -46,7 +46,7 @@ pub fn calc_index( length: usize, start: crate::INT, negative_count_from_end: bool, - err: impl Fn() -> Result, + err: impl FnOnce() -> Result, ) -> Result { if start < 0 { if negative_count_from_end { diff --git a/src/func/call.rs b/src/func/call.rs index 9852d18b..6cee575e 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -782,8 +782,9 @@ impl Engine { fn_name: &str, mut hash: FnCallHashes, target: &mut crate::eval::Target, - (call_args, call_arg_pos): &mut (FnArgsVec, Position), - pos: Position, + mut call_args: &mut [Dynamic], + first_arg_pos: Position, + fn_call_pos: Position, level: usize, ) -> RhaiResultOf<(Dynamic, bool)> { let is_ref_mut = target.is_ref(); @@ -806,7 +807,16 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - None, global, caches, lib, fn_name, new_hash, &mut args, false, false, pos, + None, + global, + caches, + lib, + fn_name, + new_hash, + &mut args, + false, + false, + fn_call_pos, level, ) } @@ -814,15 +824,17 @@ impl Engine { if !call_args.is_empty() { if !call_args[0].is::() { let typ = self.map_type_name(call_args[0].type_name()); - return Err(self.make_type_mismatch_err::(typ, *call_arg_pos)); + return Err(self.make_type_mismatch_err::(typ, first_arg_pos)); } } else { let typ = self.map_type_name(target.type_name()); - return Err(self.make_type_mismatch_err::(typ, pos)); + return Err(self.make_type_mismatch_err::(typ, fn_call_pos)); } // FnPtr call on object - let fn_ptr = call_args.remove(0).cast::(); + let fn_ptr = mem::take(&mut call_args[0]).cast::(); + call_args = &mut call_args[1..]; + // Redirect function name let fn_name = fn_ptr.fn_name(); let args_len = call_args.len() + fn_ptr.curry().len(); @@ -842,14 +854,23 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - None, global, caches, lib, fn_name, new_hash, &mut args, is_ref_mut, true, pos, + None, + global, + caches, + lib, + fn_name, + new_hash, + &mut args, + is_ref_mut, + true, + fn_call_pos, level, ) } KEYWORD_FN_PTR_CURRY => { if !target.is::() { let typ = self.map_type_name(target.type_name()); - return Err(self.make_type_mismatch_err::(typ, pos)); + return Err(self.make_type_mismatch_err::(typ, fn_call_pos)); } let fn_ptr = target.read_lock::().expect("`FnPtr`"); @@ -883,6 +904,8 @@ impl Engine { _ => { let mut fn_name = fn_name; let _redirected; + let mut call_args = call_args; + let mut arg_values: FnArgsVec<_>; // Check if it is a map method call in OOP style #[cfg(not(feature = "no_object"))] @@ -894,7 +917,13 @@ impl Engine { fn_name = &_redirected; // Add curried arguments if fn_ptr.is_curried() { - call_args.insert_many(0, fn_ptr.curry().iter().cloned()); + arg_values = fn_ptr + .curry() + .iter() + .cloned() + .chain(call_args.iter_mut().map(mem::take)) + .collect(); + call_args = &mut arg_values; } // Recalculate the hash based on the new function name and new arguments hash = FnCallHashes::from_all( @@ -912,7 +941,16 @@ impl Engine { args.extend(call_args.iter_mut()); self.exec_fn_call( - None, global, caches, lib, fn_name, hash, &mut args, is_ref_mut, true, pos, + None, + global, + caches, + lib, + fn_name, + hash, + &mut args, + is_ref_mut, + true, + fn_call_pos, level, ) } @@ -920,7 +958,7 @@ impl Engine { // Propagate the changed value back to the source if necessary if updated { - target.propagate_changed_value(pos)?; + target.propagate_changed_value(fn_call_pos)?; } Ok((result, updated))