From ae9f4b5b71796d9ac5cb06832c0dd9dcbbb19cfd Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 8 Jun 2021 15:48:55 +0800 Subject: [PATCH] Remove collect() with exact sizes. --- src/engine.rs | 54 ++++++++--------- src/fn_call.rs | 147 ++++++++++++++++++++++------------------------- src/fn_native.rs | 29 ++++------ 3 files changed, 104 insertions(+), 126 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index dc4323a5..9097c7df 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1667,22 +1667,19 @@ impl Engine { match expr { #[cfg(not(feature = "no_object"))] Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dot && !x.is_qualified() => { - let arg_values = x - .args - .iter() - .map(|arg_expr| match arg_expr { - Expr::Stack(slot, _) => Ok(x.constants[*slot].clone()), - _ => self - .eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level) - .map(Dynamic::flatten), - }) - .collect::, _>>()?; + let crate::ast::FnCallExpr { + args, constants, .. + } = x.as_ref(); + let mut arg_values = StaticVec::with_capacity(args.len()); - let pos = x - .args - .get(0) - .map(|arg_expr| arg_expr.position()) - .unwrap_or_default(); + for index in 0..args.len() { + let (value, _) = self.get_arg_value( + scope, mods, state, lib, this_ptr, level, args, constants, index, + )?; + arg_values.push(value.flatten()); + } + + let pos = x.args.get(0).map(Expr::position).unwrap_or_default(); idx_values.push((arg_values, pos).into()); } @@ -1712,22 +1709,19 @@ impl Engine { Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dot && !x.is_qualified() => { - let arg_values = x - .args - .iter() - .map(|arg_expr| match arg_expr { - Expr::Stack(slot, _) => Ok(x.constants[*slot].clone()), - _ => self - .eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level) - .map(Dynamic::flatten), - }) - .collect::, _>>()?; + let crate::ast::FnCallExpr { + args, constants, .. + } = x.as_ref(); + let mut arg_values = StaticVec::with_capacity(args.len()); - let pos = x - .args - .get(0) - .map(|arg_expr| arg_expr.position()) - .unwrap_or_default(); + for index in 0..args.len() { + let (value, _) = self.get_arg_value( + scope, mods, state, lib, this_ptr, level, args, constants, index, + )?; + arg_values.push(value.flatten()); + } + + let pos = x.args.get(0).map(Expr::position).unwrap_or_default(); (arg_values, pos).into() } diff --git a/src/fn_call.rs b/src/fn_call.rs index 201e31b1..e3899089 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -24,7 +24,6 @@ use std::prelude::v1::*; use std::{ any::{type_name, TypeId}, convert::TryFrom, - iter::once, mem, }; @@ -515,11 +514,12 @@ impl Engine { ); // Merge in encapsulated environment, if any - let lib_merged: StaticVec<_>; + let mut lib_merged = StaticVec::with_capacity(lib.len() + 1); let (unified_lib, unified) = if let Some(ref env_lib) = fn_def.lib { state.push_fn_resolution_cache(); - lib_merged = once(env_lib.as_ref()).chain(lib.iter().cloned()).collect(); + lib_merged.push(env_lib.as_ref()); + lib_merged.extend(lib.iter().cloned()); (lib_merged.as_ref(), true) } else { (lib, false) @@ -911,8 +911,11 @@ impl Engine { // Recalculate hashes let new_hash = FnCallHashes::from_script(calc_fn_hash(fn_name, args_len)); // Arguments are passed as-is, adding the curried arguments - let mut curry: StaticVec<_> = fn_ptr.curry().iter().cloned().collect(); - let mut args: StaticVec<_> = curry.iter_mut().chain(call_args.iter_mut()).collect(); + let mut curry = StaticVec::with_capacity(fn_ptr.num_curried()); + curry.extend(fn_ptr.curry().iter().cloned()); + let mut args = StaticVec::with_capacity(curry.len() + call_args.len()); + args.extend(curry.iter_mut()); + args.extend(call_args.iter_mut()); // Map it to name(args) in function-call style self.exec_fn_call( @@ -945,11 +948,12 @@ impl Engine { calc_fn_hash(fn_name, args_len + 1), ); // Replace the first argument with the object pointer, adding the curried arguments - let mut curry: StaticVec<_> = fn_ptr.curry().iter().cloned().collect(); - let mut args: StaticVec<_> = once(target.as_mut()) - .chain(curry.iter_mut()) - .chain(call_args.iter_mut()) - .collect(); + let mut curry = StaticVec::with_capacity(fn_ptr.num_curried()); + curry.extend(fn_ptr.curry().iter().cloned()); + let mut args = StaticVec::with_capacity(curry.len() + call_args.len() + 1); + args.push(target.as_mut()); + args.extend(curry.iter_mut()); + args.extend(call_args.iter_mut()); // Map it to name(args) in function-call style self.exec_fn_call( @@ -1020,8 +1024,9 @@ impl Engine { }; // Attached object pointer in front of the arguments - let mut args: StaticVec<_> = - once(target.as_mut()).chain(call_args.iter_mut()).collect(); + let mut args = StaticVec::with_capacity(call_args.len() + 1); + args.push(target.as_mut()); + args.extend(call_args.iter_mut()); self.exec_fn_call( mods, state, lib, fn_name, hash, &mut args, is_ref, true, pos, None, level, @@ -1039,8 +1044,9 @@ impl Engine { Ok((result, updated)) } + /// Evaluate an argument. #[inline(always)] - fn get_arg_value( + pub(crate) fn get_arg_value( &self, scope: &mut Scope, mods: &mut Imports, @@ -1147,12 +1153,12 @@ impl Engine { let (name, mut fn_curry) = arg.cast::().take_data(); // Append the new curried arguments to the existing list. - args_expr.iter().skip(1).try_for_each(|expr| match expr { - Expr::Stack(slot, _) => Ok(fn_curry.push(constants[*slot].clone())), - _ => self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level) - .map(|value| fn_curry.push(value)), - })?; + for index in 1..args_expr.len() { + let (value, _) = self.get_arg_value( + scope, mods, state, lib, this_ptr, level, args_expr, constants, index, + )?; + fn_curry.push(value); + } return Ok(FnPtr::new_unchecked(name, fn_curry).into()); } @@ -1249,8 +1255,8 @@ impl Engine { } // Normal function call - except for Fn, curry, call and eval (handled above) - let mut arg_values: StaticVec<_>; - let mut args: StaticVec<_>; + let mut arg_values = StaticVec::with_capacity(args_expr.len()); + let mut args = StaticVec::with_capacity(args_expr.len() + curry.len()); let mut is_ref = false; let capture = if capture_scope && !scope.is_empty() { Some(scope.clone_visible()) @@ -1260,23 +1266,18 @@ impl Engine { if args_expr.is_empty() && curry.is_empty() { // No arguments - args = Default::default(); } else { // 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 // avoid cloning the value if curry.is_empty() && !args_expr.is_empty() && args_expr[0].is_variable_access(false) { // func(x, ...) -> x.func(...) - arg_values = args_expr - .iter() - .skip(1) - .map(|expr| match expr { - Expr::Stack(slot, _) => Ok(constants[*slot].clone()), - _ => self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level) - .map(Dynamic::flatten), - }) - .collect::>()?; + for index in 1..args_expr.len() { + let (value, _) = self.get_arg_value( + scope, mods, state, lib, this_ptr, level, args_expr, constants, index, + )?; + arg_values.push(value.flatten()); + } let (mut target, _pos) = self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?; @@ -1293,28 +1294,26 @@ impl Engine { #[cfg(feature = "no_closure")] let target_is_shared = false; - args = if target_is_shared || target.is_value() { + if target_is_shared || target.is_value() { arg_values.insert(0, target.take_or_clone().flatten()); - arg_values.iter_mut().collect() + args.extend(arg_values.iter_mut()) } else { // Turn it into a method call only if the object is not shared and not a simple value is_ref = true; let obj_ref = target.take_ref().expect("never fails because `target` is a reference if it is not a value and not shared"); - once(obj_ref).chain(arg_values.iter_mut()).collect() - }; + args.push(obj_ref); + args.extend(arg_values.iter_mut()); + } } else { // func(..., ...) - arg_values = args_expr - .iter() - .map(|expr| match expr { - Expr::Stack(slot, _) => Ok(constants[*slot].clone()), - _ => self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level) - .map(Dynamic::flatten), - }) - .collect::>()?; - - args = curry.iter_mut().chain(arg_values.iter_mut()).collect(); + for index in 0..args_expr.len() { + let (value, _) = self.get_arg_value( + scope, mods, state, lib, this_ptr, level, args_expr, constants, index, + )?; + arg_values.push(value.flatten()); + } + args.extend(curry.iter_mut()); + args.extend(arg_values.iter_mut()); } } @@ -1340,35 +1339,28 @@ impl Engine { pos: Position, level: usize, ) -> RhaiResult { - let mut arg_values: StaticVec<_>; + let mut arg_values = StaticVec::with_capacity(args_expr.len()); + let mut args = StaticVec::with_capacity(args_expr.len()); let mut first_arg_value = None; - let mut args: StaticVec<_>; if args_expr.is_empty() { // No arguments - args = Default::default(); } else { // See if the first argument is a variable (not namespace-qualified). // If so, convert to method-call style in order to leverage potential // &mut first argument and avoid cloning the value if !args_expr.is_empty() && args_expr[0].is_variable_access(true) { // func(x, ...) -> x.func(...) - arg_values = args_expr - .iter() - .enumerate() - .map(|(i, expr)| { - // Skip the first argument - if i == 0 { - return Ok(Default::default()); - } - match expr { - Expr::Stack(slot, _) => Ok(constants[*slot].clone()), - _ => self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level) - .map(Dynamic::flatten), - } - }) - .collect::>()?; + for index in 0..args_expr.len() { + if index == 0 { + arg_values.push(Default::default()); + } else { + let (value, _) = self.get_arg_value( + scope, mods, state, lib, this_ptr, level, args_expr, constants, index, + )?; + arg_values.push(value.flatten()); + } + } // Get target reference to first argument let (target, _pos) = @@ -1384,7 +1376,7 @@ impl Engine { if target_is_shared || target.is_value() { arg_values[0] = target.take_or_clone().flatten(); - args = arg_values.iter_mut().collect(); + args.extend(arg_values.iter_mut()); } else { // Turn it into a method call only if the object is not shared and not a simple value let (first, rest) = arg_values @@ -1392,21 +1384,18 @@ impl Engine { .expect("never fails because the arguments list is not empty"); first_arg_value = Some(first); let obj_ref = target.take_ref().expect("never fails because `target` is a reference if it is not a value and not shared"); - args = once(obj_ref).chain(rest.iter_mut()).collect(); + args.push(obj_ref); + args.extend(rest.iter_mut()); } } else { // func(..., ...) or func(mod::x, ...) - arg_values = args_expr - .iter() - .map(|expr| match expr { - Expr::Stack(slot, _) => Ok(constants[*slot].clone()), - _ => self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level) - .map(Dynamic::flatten), - }) - .collect::>()?; - - args = arg_values.iter_mut().collect(); + for index in 0..args_expr.len() { + let (value, _) = self.get_arg_value( + scope, mods, state, lib, this_ptr, level, args_expr, constants, index, + )?; + arg_values.push(value.flatten()); + } + args.extend(arg_values.iter_mut()); } } diff --git a/src/fn_native.rs b/src/fn_native.rs index 06dccf38..362874d8 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -344,28 +344,23 @@ impl FnPtr { this_ptr: Option<&mut Dynamic>, mut arg_values: impl AsMut<[Dynamic]>, ) -> RhaiResult { - let mut args_data: StaticVec<_>; + let mut arg_values = arg_values.as_mut(); + let mut args_data; - let arg_values = if self.curry().is_empty() { - arg_values.as_mut() - } else { - args_data = self - .curry() - .iter() - .cloned() - .chain(arg_values.as_mut().iter_mut().map(mem::take)) - .collect(); - - args_data.as_mut() + if self.num_curried() > 0 { + args_data = StaticVec::with_capacity(self.num_curried() + arg_values.len()); + args_data.extend(self.curry().iter().cloned()); + args_data.extend(arg_values.iter_mut().map(mem::take)); + arg_values = args_data.as_mut(); }; let is_method = this_ptr.is_some(); - let mut args: StaticVec<_> = if let Some(obj) = this_ptr { - once(obj).chain(arg_values.iter_mut()).collect() - } else { - arg_values.iter_mut().collect() - }; + let mut args = StaticVec::with_capacity(arg_values.len() + 1); + if let Some(obj) = this_ptr { + args.push(obj); + } + args.extend(arg_values.iter_mut()); ctx.call_fn_dynamic_raw(self.fn_name(), is_method, &mut args) }