From 72cfd42ab78ca8b9021d0eba86e08c77b5ae18e2 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 28 Apr 2023 15:05:31 +0800 Subject: [PATCH] Code refactor. --- src/func/call.rs | 314 ++++++++++++++++-------------------- src/func/native.rs | 12 +- src/module/mod.rs | 14 +- src/optimizer.rs | 7 +- src/packages/string_more.rs | 12 +- src/serde/metadata.rs | 2 +- 6 files changed, 163 insertions(+), 198 deletions(-) diff --git a/src/func/call.rs b/src/func/call.rs index d6edfb55..f52fb915 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -576,39 +576,32 @@ impl Engine { ) -> RhaiResultOf<(Dynamic, bool)> { // These may be redirected from method style calls. if hashes.is_native_only() { - let error = match fn_name { - // Handle type_of() - KEYWORD_TYPE_OF => { - if args.len() == 1 { + loop { + match fn_name { + // Handle type_of() + KEYWORD_TYPE_OF if args.len() == 1 => { let typ = self.get_interned_string(self.map_type_name(args[0].type_name())); return Ok((typ.into(), false)); } - true - } - #[cfg(not(feature = "no_closure"))] - crate::engine::KEYWORD_IS_SHARED => { - if args.len() == 1 { - return Ok((args[0].is_shared().into(), false)); + #[cfg(not(feature = "no_closure"))] + crate::engine::KEYWORD_IS_SHARED if args.len() == 1 => { + return Ok((args[0].is_shared().into(), false)) } - true + #[cfg(not(feature = "no_closure"))] + crate::engine::KEYWORD_IS_SHARED => (), + + #[cfg(not(feature = "no_function"))] + crate::engine::KEYWORD_IS_DEF_FN => (), + + KEYWORD_TYPE_OF | KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR + | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY => (), + + _ => break, } - #[cfg(not(feature = "no_function"))] - crate::engine::KEYWORD_IS_DEF_FN => true, - - KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR | KEYWORD_FN_PTR_CALL - | KEYWORD_FN_PTR_CURRY => true, - - _ => false, - }; - - if error { - return Err(ERR::ErrorFunctionNotFound( - self.gen_fn_call_signature(fn_name, args), - pos, - ) - .into()); + let sig = self.gen_fn_call_signature(fn_name, args); + return Err(ERR::ErrorFunctionNotFound(sig, pos).into()); } } @@ -624,31 +617,27 @@ impl Engine { let hash = hashes.script(); let local_entry = &mut None; + let mut resolved = None; #[cfg(not(feature = "no_object"))] - let resolved = if _is_method_call && !args.is_empty() { + if _is_method_call && !args.is_empty() { let typed_hash = crate::calc_typed_method_hash(hash, self.map_type_name(args[0].type_name())); - self.resolve_fn(global, caches, local_entry, None, typed_hash, None, false) - } else { - None - }; - #[cfg(feature = "no_object")] - let resolved = None; + resolved = + self.resolve_fn(global, caches, local_entry, None, typed_hash, None, false); + } - let resolved = if resolved.is_none() { - self.resolve_fn(global, caches, local_entry, None, hash, None, false) - } else { - resolved - }; + if resolved.is_none() { + resolved = self.resolve_fn(global, caches, local_entry, None, hash, None, false); + } - if let Some(FnResolutionCacheEntry { func, ref source }) = resolved.cloned() { + if let Some(FnResolutionCacheEntry { func, source }) = resolved.cloned() { // Script function call debug_assert!(func.is_script()); - let f = func.get_script_fn_def().expect("script-defined function"); let environ = func.get_encapsulated_environ(); + let func = func.get_script_fn_def().expect("script-defined function"); - if f.body.is_empty() { + if func.body.is_empty() { return Ok((Dynamic::UNIT, false)); } @@ -660,23 +649,17 @@ impl Engine { &mut empty_scope }; - let orig_source = mem::replace(&mut global.source, source.clone()); + let orig_source = mem::replace(&mut global.source, source); defer! { global => move |g| g.source = orig_source } return if _is_method_call { - // Method call of script function - map first argument to `this` - let (first_arg, rest_args) = args.split_first_mut().unwrap(); + use std::ops::DerefMut; + // Method call of script function - map first argument to `this` + let (first_arg, args) = args.split_first_mut().unwrap(); + let this_ptr = Some(first_arg.deref_mut()); self.call_script_fn( - global, - caches, - scope, - Some(first_arg), - environ, - f, - rest_args, - true, - pos, + global, caches, scope, this_ptr, environ, func, args, true, pos, ) } else { // Normal call of script function @@ -691,7 +674,7 @@ impl Engine { defer! { args = (args) if swap => move |a| backup.restore_first_arg(a) } - self.call_script_fn(global, caches, scope, None, environ, f, args, true, pos) + self.call_script_fn(global, caches, scope, None, environ, func, args, true, pos) } .map(|r| (r, false)); } @@ -748,31 +731,28 @@ impl Engine { fn_name: &str, mut hash: FnCallHashes, target: &mut crate::eval::Target, - mut call_args: &mut [Dynamic], + call_args: &mut [Dynamic], first_arg_pos: Position, pos: Position, ) -> RhaiResultOf<(Dynamic, bool)> { - let is_ref_mut = target.is_ref(); - let (result, updated) = match fn_name { // Handle fn_ptr.call(...) KEYWORD_FN_PTR_CALL if target.is_fnptr() => { let fn_ptr = target.read_lock::().expect("`FnPtr`"); // Arguments are passed as-is, adding the curried arguments - let mut curry = FnArgsVec::with_capacity(fn_ptr.curry().len()); - curry.extend(fn_ptr.curry().iter().cloned()); - let args = &mut FnArgsVec::with_capacity(curry.len() + call_args.len()); - args.extend(curry.iter_mut()); - args.extend(call_args.iter_mut()); + let mut curry = fn_ptr.curry().iter().cloned().collect::>(); + let args = &mut curry + .iter_mut() + .chain(call_args.iter_mut()) + .collect::>(); - // Linked to scripted function? + let _fn_def = (); #[cfg(not(feature = "no_function"))] - let fn_def = fn_ptr.fn_def(); - #[cfg(feature = "no_function")] - let fn_def = (); + let _fn_def = fn_ptr.fn_def(); - match fn_def { + match _fn_def { + // Linked to scripted function - short-circuit #[cfg(not(feature = "no_function"))] Some(fn_def) if fn_def.params.len() == args.len() => { let scope = &mut Scope::new(); @@ -805,23 +785,21 @@ impl Engine { } } + // Handle obj.call() + KEYWORD_FN_PTR_CALL if call_args.is_empty() => { + return Err(self + .make_type_mismatch_err::(self.map_type_name(target.type_name()), pos)) + } + // Handle obj.call(fn_ptr, ...) KEYWORD_FN_PTR_CALL => { - if call_args.is_empty() { - return Err(self.make_type_mismatch_err::( - self.map_type_name(target.type_name()), - pos, - )); - } - if !call_args[0].is_fnptr() { - return Err(self.make_type_mismatch_err::( - self.map_type_name(call_args[0].type_name()), - first_arg_pos, - )); - } + debug_assert!(!call_args.is_empty()); // FnPtr call on object - let fn_ptr = call_args[0].take().cast::(); + let typ = call_args[0].type_name(); + let fn_ptr = call_args[0].take().try_cast::().ok_or_else(|| { + self.make_type_mismatch_err::(self.map_type_name(typ), first_arg_pos) + })?; #[cfg(not(feature = "no_function"))] let (is_anon, (fn_name, fn_curry, environ, fn_def)) = @@ -829,17 +807,14 @@ impl Engine { #[cfg(feature = "no_function")] let (is_anon, (fn_name, fn_curry, _), fn_def) = (false, fn_ptr.take_data(), ()); - // Replace the first argument with the object pointer, adding the curried arguments - call_args = &mut call_args[1..]; - - let mut curry = FnArgsVec::with_capacity(fn_curry.len()); - curry.extend(fn_curry.into_iter()); - let args = &mut FnArgsVec::with_capacity(curry.len() + call_args.len() + 1); + // Adding the curried arguments and the remaining arguments + let mut curry = fn_curry.into_iter().collect::>(); + let args = &mut FnArgsVec::with_capacity(curry.len() + call_args.len()); args.extend(curry.iter_mut()); - args.extend(call_args.iter_mut()); + args.extend(call_args.iter_mut().skip(1)); - // Linked to scripted function? match fn_def { + // Linked to scripted function - short-circuit #[cfg(not(feature = "no_function"))] Some(fn_def) if fn_def.params.len() == args.len() => { // Check for data race. @@ -856,58 +831,53 @@ impl Engine { .map(|v| (v, false)) } _ => { + let name = fn_name.as_str(); + let is_ref_mut = target.is_ref(); + // Add the first argument with the object pointer args.insert(0, target.as_mut()); // Recalculate hash + let num_args = args.len(); + let new_hash = match is_anon { - false if !is_valid_function_name(&fn_name) => { - FnCallHashes::from_native_only(calc_fn_hash( - None, - &fn_name, - args.len(), - )) + false if !is_valid_function_name(name) => { + FnCallHashes::from_native_only(calc_fn_hash(None, name, num_args)) } #[cfg(not(feature = "no_function"))] _ => FnCallHashes::from_script_and_native( - calc_fn_hash(None, &fn_name, args.len() - 1), - calc_fn_hash(None, &fn_name, args.len()), + calc_fn_hash(None, name, num_args - 1), + calc_fn_hash(None, name, num_args), ), #[cfg(feature = "no_function")] - _ => FnCallHashes::from_native_only(calc_fn_hash( - None, - &fn_name, - args.len(), - )), + _ => FnCallHashes::from_native_only(calc_fn_hash(None, name, num_args)), }; // Map it to name(args) in function-call style self.exec_fn_call( - global, caches, None, &fn_name, None, new_hash, args, is_ref_mut, true, - pos, + global, caches, None, name, None, new_hash, args, is_ref_mut, true, pos, ) } } } - KEYWORD_FN_PTR_CURRY => { - if !target.is_fnptr() { - return Err(self.make_type_mismatch_err::( - self.map_type_name(target.type_name()), - pos, - )); - } - let mut fn_ptr = target.read_lock::().expect("`FnPtr`").clone(); + // Handle fn_ptr.curry(...) + KEYWORD_FN_PTR_CURRY => { + let typ = target.type_name(); + let mut fn_ptr = target + .read_lock::() + .ok_or_else(|| { + self.make_type_mismatch_err::(self.map_type_name(typ), pos) + })? + .clone(); // Append the new curried arguments to the existing list. - call_args.iter_mut().map(mem::take).for_each(|value| { - fn_ptr.add_curry(value); - }); + fn_ptr.extend(call_args.iter_mut().map(mem::take)); Ok((fn_ptr.into(), false)) } - // Handle is_shared() + // Handle var.is_shared() #[cfg(not(feature = "no_closure"))] crate::engine::KEYWORD_IS_SHARED if call_args.is_empty() => { return Ok((target.is_shared().into(), false)); @@ -917,7 +887,7 @@ impl Engine { let mut fn_name = fn_name; let _redirected; let mut _linked = None; - let mut _arg_values: FnArgsVec<_>; + let mut _arg_values; let mut call_args = call_args; // Check if it is a map method call in OOP style @@ -936,17 +906,16 @@ impl Engine { .iter() .cloned() .chain(call_args.iter_mut().map(mem::take)) - .collect(); + .collect::>(); call_args = &mut _arg_values; } - // Linked to scripted function? + let _fn_def = (); #[cfg(not(feature = "no_function"))] - let fn_def = fn_ptr.fn_def(); - #[cfg(feature = "no_function")] - let fn_def = (); + let _fn_def = fn_ptr.fn_def(); - match fn_def { + match _fn_def { + // Linked to scripted function #[cfg(not(feature = "no_function"))] Some(fn_def) if fn_def.params.len() == call_args.len() => { _linked = Some(( @@ -960,21 +929,22 @@ impl Engine { let _is_anon = fn_ptr.is_anonymous(); // Recalculate the hash based on the new function name and new arguments - let args_len = call_args.len() + 1; + let num_args = call_args.len() + 1; + hash = match _is_anon { false if !is_valid_function_name(fn_name) => { FnCallHashes::from_native_only(calc_fn_hash( - None, fn_name, args_len, + None, fn_name, num_args, )) } #[cfg(not(feature = "no_function"))] _ => FnCallHashes::from_script_and_native( - calc_fn_hash(None, fn_name, args_len - 1), - calc_fn_hash(None, fn_name, args_len), + calc_fn_hash(None, fn_name, num_args - 1), + calc_fn_hash(None, fn_name, num_args), ), #[cfg(feature = "no_function")] _ => FnCallHashes::from_native_only(calc_fn_hash( - None, fn_name, args_len, + None, fn_name, num_args, )), }; } @@ -986,7 +956,7 @@ impl Engine { match _linked { #[cfg(not(feature = "no_function"))] Some((fn_def, environ)) => { - // Linked to scripted function + // Linked to scripted function - short-circuit let scope = &mut Scope::new(); let environ = environ.as_deref(); let this_ptr = Some(target.as_mut()); @@ -1000,14 +970,15 @@ impl Engine { #[cfg(feature = "no_function")] Some(()) => unreachable!(), None => { + let is_ref_mut = target.is_ref(); + // Attached object pointer in front of the arguments - let mut args = FnArgsVec::with_capacity(call_args.len() + 1); - args.push(target.as_mut()); - args.extend(call_args.iter_mut()); + let args = &mut std::iter::once(target.as_mut()) + .chain(call_args.iter_mut()) + .collect::>(); self.exec_fn_call( - global, caches, None, fn_name, None, hash, &mut args, is_ref_mut, true, - pos, + global, caches, None, fn_name, None, hash, args, is_ref_mut, true, pos, ) } } @@ -1039,7 +1010,7 @@ impl Engine { ) -> RhaiResult { let mut first_arg = first_arg; let mut args_expr = args_expr; - let mut total_args = usize::from(first_arg.is_some()) + args_expr.len(); + let mut num_args = usize::from(first_arg.is_some()) + args_expr.len(); let mut curry = FnArgsVec::new_const(); let mut name = fn_name; let mut hashes = hashes; @@ -1049,19 +1020,15 @@ impl Engine { _ if op_token.is_some() => (), // Handle call(fn_ptr, ...) - KEYWORD_FN_PTR_CALL if total_args >= 1 => { + KEYWORD_FN_PTR_CALL if num_args >= 1 => { let arg = first_arg.unwrap(); - let (arg_value, arg_pos) = + let (first_arg_value, first_arg_pos) = self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), arg)?; - if !arg_value.is_fnptr() { - return Err(self.make_type_mismatch_err::( - self.map_type_name(arg_value.type_name()), - arg_pos, - )); - } - - let fn_ptr = arg_value.cast::(); + let typ = first_arg_value.type_name(); + let fn_ptr = first_arg_value.try_cast::().ok_or_else(|| { + self.make_type_mismatch_err::(self.map_type_name(typ), first_arg_pos) + })?; #[cfg(not(feature = "no_function"))] let (is_anon, (fn_name, fn_curry, _environ, fn_def)) = @@ -1071,20 +1038,20 @@ impl Engine { curry.extend(fn_curry.into_iter()); - // Linked to scripted function? + // Linked to scripted function - short-circuit #[cfg(not(feature = "no_function"))] if let Some(fn_def) = fn_def { if fn_def.params.len() == curry.len() + args_expr.len() { // Evaluate arguments - let mut arg_values = curry - .into_iter() - .map(Ok) - .chain(args_expr.iter().map(|expr| -> Result<_, crate::RhaiError> { - let this_ptr = this_ptr.as_deref_mut(); - self.get_arg_value(global, caches, scope, this_ptr, expr) - .map(|(v, ..)| v) - })) - .collect::>>()?; + let mut arg_values = + FnArgsVec::with_capacity(curry.len() + args_expr.len()); + arg_values.extend(curry); + for expr in args_expr { + let this_ptr = this_ptr.as_deref_mut(); + let (value, _) = + self.get_arg_value(global, caches, scope, this_ptr, expr)?; + arg_values.push(value); + } let args = &mut arg_values.iter_mut().collect::>(); let scope = &mut Scope::new(); let environ = _environ.as_deref(); @@ -1104,10 +1071,10 @@ impl Engine { if !args_expr.is_empty() { args_expr = &args_expr[1..]; } - total_args -= 1; + num_args -= 1; // Recalculate hash - let args_len = total_args + curry.len(); + let args_len = num_args + curry.len(); hashes = if !is_anon && !is_valid_function_name(name) { FnCallHashes::from_native_only(calc_fn_hash(None, name, args_len)) @@ -1115,8 +1082,9 @@ impl Engine { FnCallHashes::from_hash(calc_fn_hash(None, name, args_len)) }; } + // Handle Fn(fn_name) - KEYWORD_FN_PTR if total_args == 1 => { + KEYWORD_FN_PTR if num_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = self.get_arg_value(global, caches, scope, this_ptr, arg)?; @@ -1131,19 +1099,15 @@ impl Engine { } // Handle curry(x, ...) - KEYWORD_FN_PTR_CURRY if total_args > 1 => { + KEYWORD_FN_PTR_CURRY if num_args > 1 => { let first = first_arg.unwrap(); - let (arg_value, arg_pos) = + let (first_arg_value, first_arg_pos) = self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?; - if !arg_value.is_fnptr() { - return Err(self.make_type_mismatch_err::( - self.map_type_name(arg_value.type_name()), - arg_pos, - )); - } - - let mut fn_ptr = arg_value.cast::(); + let typ = first_arg_value.type_name(); + let mut fn_ptr = first_arg_value.try_cast::().ok_or_else(|| { + self.make_type_mismatch_err::(self.map_type_name(typ), first_arg_pos) + })?; // Append the new curried arguments to the existing list. for expr in args_expr { @@ -1157,7 +1121,7 @@ impl Engine { // Handle is_shared(var) #[cfg(not(feature = "no_closure"))] - crate::engine::KEYWORD_IS_SHARED if total_args == 1 => { + crate::engine::KEYWORD_IS_SHARED if num_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, ..) = self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), arg)?; @@ -1166,7 +1130,7 @@ impl Engine { // Handle is_def_fn(fn_name, arity) #[cfg(not(feature = "no_function"))] - crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => { + crate::engine::KEYWORD_IS_DEF_FN if num_args == 2 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?; @@ -1194,7 +1158,7 @@ impl Engine { // Handle is_def_fn(this_type, fn_name, arity) #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_object"))] - crate::engine::KEYWORD_IS_DEF_FN if total_args == 3 => { + crate::engine::KEYWORD_IS_DEF_FN if num_args == 3 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?; @@ -1235,7 +1199,7 @@ impl Engine { } // Handle is_def_var(fn_name) - KEYWORD_IS_DEF_VAR if total_args == 1 => { + KEYWORD_IS_DEF_VAR if num_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = self.get_arg_value(global, caches, scope, this_ptr, arg)?; @@ -1246,7 +1210,7 @@ impl Engine { } // Handle eval(script) - KEYWORD_EVAL if total_args == 1 => { + KEYWORD_EVAL if num_args == 1 => { // eval - only in function call style let orig_scope_len = scope.len(); #[cfg(not(feature = "no_module"))] @@ -1289,8 +1253,8 @@ impl Engine { } // Normal function call - except for Fn, curry, call and eval (handled above) - let mut arg_values = FnArgsVec::with_capacity(total_args); - let mut args = FnArgsVec::with_capacity(total_args + curry.len()); + let mut arg_values = FnArgsVec::with_capacity(num_args); + let mut args = FnArgsVec::with_capacity(num_args + curry.len()); let mut is_ref_mut = false; // Capture parent scope? @@ -1318,7 +1282,7 @@ impl Engine { } // Call with blank scope - if total_args > 0 || !curry.is_empty() { + if num_args > 0 || !curry.is_empty() { // 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 diff --git a/src/func/native.rs b/src/func/native.rs index 409e3948..300daaef 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -7,8 +7,8 @@ use crate::plugin::PluginFunction; use crate::tokenizer::{is_valid_function_name, Token, TokenizeState}; use crate::types::dynamic::Variant; use crate::{ - calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Position, RhaiResult, RhaiResultOf, - StaticVec, VarDefInfo, ERR, + calc_fn_hash, Dynamic, Engine, EvalContext, FnArgsVec, FuncArgs, Position, RhaiResult, + RhaiResultOf, StaticVec, VarDefInfo, ERR, }; use std::any::{type_name, TypeId}; #[cfg(feature = "no_std")] @@ -309,9 +309,9 @@ impl<'a> NativeCallContext<'a> { let mut arg_values = StaticVec::new_const(); args.parse(&mut arg_values); - let mut args: StaticVec<_> = arg_values.iter_mut().collect(); + let args = &mut arg_values.iter_mut().collect::>(); - self._call_fn_raw(fn_name, &mut args, false, false, false) + self._call_fn_raw(fn_name, args, false, false, false) .and_then(|result| { // Bail out early if the return type needs no cast if TypeId::of::() == TypeId::of::() { @@ -340,9 +340,9 @@ impl<'a> NativeCallContext<'a> { let mut arg_values = StaticVec::new_const(); args.parse(&mut arg_values); - let mut args: StaticVec<_> = arg_values.iter_mut().collect(); + let args = &mut arg_values.iter_mut().collect::>(); - self._call_fn_raw(fn_name, &mut args, true, false, false) + self._call_fn_raw(fn_name, args, true, false, false) .and_then(|result| { // Bail out early if the return type needs no cast if TypeId::of::() == TypeId::of::() { diff --git a/src/module/mod.rs b/src/module/mod.rs index d00cf0b2..201527cb 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -118,7 +118,7 @@ impl FuncInfo { } } } else { - let params: FnArgsVec<_> = self + let params = self .metadata .params_info .iter() @@ -134,7 +134,7 @@ impl FuncInfo { }; result }) - .collect(); + .collect::>(); signature.push_str(¶ms.join(", ")); } signature.push(')'); @@ -932,7 +932,10 @@ impl Module { hash_fn: u64, arg_names: impl IntoIterator, ) -> &mut Self { - let mut param_names: FnArgsVec<_> = arg_names.into_iter().map(Into::into).collect(); + let mut param_names = arg_names + .into_iter() + .map(Into::into) + .collect::>(); if let Some(f) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) { let (param_names, return_type_name) = if param_names.len() > f.metadata.num_params { @@ -1053,13 +1056,12 @@ impl Module { let _arg_names = arg_names; let is_method = func.is_method(); - let mut param_types: FnArgsVec<_> = arg_types + let param_types = arg_types .as_ref() .iter() .enumerate() .map(|(i, &type_id)| Self::map_type(!is_method || i > 0, type_id)) - .collect(); - param_types.shrink_to_fit(); + .collect::>(); let is_dynamic = param_types .iter() diff --git a/src/optimizer.rs b/src/optimizer.rs index 2a6e7fd0..876702c2 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1195,12 +1195,11 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { && x.constant_args() // all arguments are constants => { // First search for script-defined functions (can override built-in) + let _has_script_fn = false; #[cfg(not(feature = "no_function"))] - let has_script_fn = !x.hashes.is_native_only() && state.global.lib.iter().find_map(|m| m.get_script_fn(&x.name, x.args.len())).is_some(); - #[cfg(feature = "no_function")] - let has_script_fn = false; + let _has_script_fn = !x.hashes.is_native_only() && state.global.lib.iter().find_map(|m| m.get_script_fn(&x.name, x.args.len())).is_some(); - if !has_script_fn { + if !_has_script_fn { let arg_values = &mut x.args.iter().map(Expr::get_literal_value).collect::>>().unwrap(); let result = match x.name.as_str() { diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 664fa15b..7de4fecd 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -265,7 +265,7 @@ mod string_functions { /// /// print(text); // prints "hello, world!" /// - /// x.truncate(10); + /// text.truncate(10); /// /// print(text); // prints "hello, world!" /// ``` @@ -273,10 +273,10 @@ mod string_functions { if len > 0 { #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] let len = len.min(MAX_USIZE_INT) as usize; - let chars: StaticVec<_> = string.chars().collect(); - let copy = string.make_mut(); - copy.clear(); - copy.extend(chars.into_iter().take(len)); + if let Some((index, _)) = string.char_indices().nth(len) { + let copy = string.make_mut(); + copy.truncate(index); + } } else { clear(string); } @@ -1109,7 +1109,7 @@ mod string_functions { copy.clear(); copy.extend(chars.iter().skip(offset).take(len)); } - /// Remove all characters from the string except until the `start` position. + /// Remove all characters from the string up to the `start` position. /// /// * If `start` < 0, position counts from the end of the string (`-1` is the last character). /// * If `start` < -length of string, the string is not modified. diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index 4eec9053..ec936f67 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -156,7 +156,7 @@ impl ModuleMetadata<'_> { impl<'a> From<&'a crate::Module> for ModuleMetadata<'a> { fn from(module: &'a crate::Module) -> Self { - let mut functions: StaticVec<_> = module.iter_fn().map(Into::into).collect(); + let mut functions = module.iter_fn().map(Into::into).collect::>(); functions.sort(); Self {