From c46ae36389be7909131c0130c335f0c4971bda93 Mon Sep 17 00:00:00 2001 From: heygsc <1596920983@qq.com> Date: Mon, 13 Jan 2025 00:24:45 +0800 Subject: [PATCH 1/3] Use instead --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + clippy.toml | 3 +++ core/engine/Cargo.toml | 1 + core/engine/src/builtins/temporal/time_zone/mod.rs | 4 ++-- 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d91d84fa0a8..c5f93c9eb70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -372,6 +372,7 @@ dependencies = [ "boa_string", "bytemuck", "cfg-if", + "cow-utils", "criterion", "dashmap", "either", @@ -921,6 +922,12 @@ dependencies = [ "libm", ] +[[package]] +name = "cow-utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417bef24afe1460300965a25ff4a24b8b45ad011948302ec221e8a0a81eb2c79" + [[package]] name = "crc32fast" version = "1.4.2" diff --git a/Cargo.toml b/Cargo.toml index b411f795297..4622de3ae10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ arbitrary = "1" bitflags = "2.5.0" clap = "4.5.23" colored = "2.2.0" +cow-utils = "0.1.3" fast-float2 = "0.2.3" hashbrown = "0.15.2" indexmap = { version = "2.7.0", default-features = false } diff --git a/clippy.toml b/clippy.toml index 272425dad89..2a95f1d24b1 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,2 +1,5 @@ doc-valid-idents = ['ECMAScript', 'JavaScript', 'SpiderMonkey', 'GitHub'] allow-print-in-tests = true +disallowed-methods = [ + { path = "str::to_ascii_uppercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_ascii_uppercase` instead." }, +] \ No newline at end of file diff --git a/core/engine/Cargo.toml b/core/engine/Cargo.toml index 9a0d603e427..e6b225ebadf 100644 --- a/core/engine/Cargo.toml +++ b/core/engine/Cargo.toml @@ -75,6 +75,7 @@ boa_macros.workspace = true boa_ast.workspace = true boa_parser.workspace = true boa_string.workspace = true +cow-utils.workspace = true serde = { workspace = true, features = ["derive", "rc"] } serde_json.workspace = true rand.workspace = true diff --git a/core/engine/src/builtins/temporal/time_zone/mod.rs b/core/engine/src/builtins/temporal/time_zone/mod.rs index a1d499e5829..2e73f0a2e0c 100644 --- a/core/engine/src/builtins/temporal/time_zone/mod.rs +++ b/core/engine/src/builtins/temporal/time_zone/mod.rs @@ -2,7 +2,7 @@ #![allow(dead_code)] use crate::{builtins::temporal::to_zero_padded_decimal_string, Context}; - +use cow_utils::CowUtils; // -- TimeZone Abstract Operations -- /// Abstract operation `DefaultTimeZone ( )` @@ -96,7 +96,7 @@ fn canonicalize_time_zone_name(time_zone: &str) -> String { // do not include local political rules for any time zones performs the following steps when // called: // 1. Assert: timeZone is an ASCII-case-insensitive match for "UTC". - assert_eq!(time_zone.to_ascii_uppercase(), "UTC"); + assert_eq!(time_zone.cow_to_ascii_uppercase(), "UTC"); // 2. Return "UTC". "UTC".to_owned() } From 583676780f981cffee0d38cddab77d725ed84958 Mon Sep 17 00:00:00 2001 From: heygsc <1596920983@qq.com> Date: Tue, 14 Jan 2025 09:45:09 +0800 Subject: [PATCH 2/3] chore: clippy.toml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: José Julián Espina --- clippy.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy.toml b/clippy.toml index 2a95f1d24b1..59ef99e1fe1 100644 --- a/clippy.toml +++ b/clippy.toml @@ -2,4 +2,4 @@ doc-valid-idents = ['ECMAScript', 'JavaScript', 'SpiderMonkey', 'GitHub'] allow-print-in-tests = true disallowed-methods = [ { path = "str::to_ascii_uppercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_ascii_uppercase` instead." }, -] \ No newline at end of file +] From b73c9f3f4a1f0df777d75ed73bf66912317dd02d Mon Sep 17 00:00:00 2001 From: heygsc <1596920983@qq.com> Date: Tue, 14 Jan 2025 23:20:40 +0800 Subject: [PATCH 3/3] Use cow-utils instead --- Cargo.lock | 3 +++ cli/Cargo.toml | 1 + cli/src/debug/function.rs | 5 +++-- clippy.toml | 5 +++++ core/engine/src/builtins/number/globals.rs | 5 +++-- core/engine/src/builtins/number/mod.rs | 3 ++- core/engine/src/builtins/string/mod.rs | 5 +++-- .../engine/src/builtins/temporal/zoneddatetime/mod.rs | 3 ++- core/macros/Cargo.toml | 1 + core/macros/src/lib.rs | 11 ++++++----- tests/tester/Cargo.toml | 1 + tests/tester/src/read.rs | 3 ++- 12 files changed, 32 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e9b84e2a181..5a803c54543 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -347,6 +347,7 @@ dependencies = [ "clap", "color-eyre", "colored", + "cow-utils", "dhat", "jemallocator", "phf", @@ -508,6 +509,7 @@ dependencies = [ name = "boa_macros" version = "0.20.0" dependencies = [ + "cow-utils", "proc-macro2", "quote", "syn", @@ -587,6 +589,7 @@ dependencies = [ "color-eyre", "colored", "comfy-table", + "cow-utils", "phf", "rayon", "rustc-hash 2.1.0", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index d4d5bf6c0be..ed4987adddb 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -25,6 +25,7 @@ phf = { workspace = true, features = ["macros"] } pollster.workspace = true dhat = { workspace = true, optional = true } color-eyre.workspace = true +cow-utils.workspace = true [features] default = ["boa_engine/annex-b", "boa_engine/experimental", "boa_engine/intl_bundled"] diff --git a/cli/src/debug/function.rs b/cli/src/debug/function.rs index 8337ba708b7..8edc167d16b 100644 --- a/cli/src/debug/function.rs +++ b/cli/src/debug/function.rs @@ -5,6 +5,7 @@ use boa_engine::{ vm::flowgraph::{Direction, Graph}, Context, JsArgs, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, }; +use cow_utils::CowUtils; use crate::FlowgraphFormat; @@ -14,7 +15,7 @@ fn flowgraph_parse_format_option(value: &JsValue) -> JsResult { } if let Some(string) = value.as_string() { - return match string.to_std_string_escaped().to_lowercase().as_str() { + return match string.to_std_string_escaped().cow_to_lowercase().as_ref() { "mermaid" => Ok(FlowgraphFormat::Mermaid), "graphviz" => Ok(FlowgraphFormat::Graphviz), format => Err(JsNativeError::typ() @@ -34,7 +35,7 @@ fn flowgraph_parse_direction_option(value: &JsValue) -> JsResult { } if let Some(string) = value.as_string() { - return match string.to_std_string_escaped().to_lowercase().as_str() { + return match string.to_std_string_escaped().cow_to_lowercase().as_ref() { "leftright" | "lr" => Ok(Direction::LeftToRight), "rightleft" | "rl" => Ok(Direction::RightToLeft), "topbottom" | "tb" => Ok(Direction::TopToBottom), diff --git a/clippy.toml b/clippy.toml index 59ef99e1fe1..5b248b4ae4b 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,5 +1,10 @@ doc-valid-idents = ['ECMAScript', 'JavaScript', 'SpiderMonkey', 'GitHub'] allow-print-in-tests = true disallowed-methods = [ + { path = "str::to_ascii_lowercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_ascii_lowercase` instead." }, { path = "str::to_ascii_uppercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_ascii_uppercase` instead." }, + { path = "str::to_lowercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_lowercase` instead." }, + { path = "str::to_uppercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_uppercase` instead." }, + { path = "str::replace", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_replace` instead." }, + { path = "str::replacen", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_replacen` instead." }, ] diff --git a/core/engine/src/builtins/number/globals.rs b/core/engine/src/builtins/number/globals.rs index c9ef2a4b2b8..1418e32837f 100644 --- a/core/engine/src/builtins/number/globals.rs +++ b/core/engine/src/builtins/number/globals.rs @@ -8,6 +8,7 @@ use crate::{ }; use boa_macros::js_str; +use cow_utils::CowUtils; /// Builtin javascript 'isFinite(number)' function. /// @@ -304,8 +305,8 @@ pub(crate) fn parse_float( // TODO: parse float with optimal utf16 algorithm let input_string = val.to_string(context)?.to_std_string_escaped(); let s = input_string.trim_start_matches(is_trimmable_whitespace); - let s_prefix_lower = s.chars().take(4).collect::().to_ascii_lowercase(); - + let s_prefix = s.chars().take(4).collect::(); + let s_prefix_lower = s_prefix.cow_to_ascii_lowercase(); // TODO: write our own lexer to match syntax StrDecimalLiteral if s.starts_with("Infinity") || s.starts_with("+Infinity") { Ok(JsValue::new(f64::INFINITY)) diff --git a/core/engine/src/builtins/number/mod.rs b/core/engine/src/builtins/number/mod.rs index 2611800feba..b9b0d86e019 100644 --- a/core/engine/src/builtins/number/mod.rs +++ b/core/engine/src/builtins/number/mod.rs @@ -26,6 +26,7 @@ use crate::{ Context, JsArgs, JsResult, JsString, }; use boa_profiler::Profiler; +use cow_utils::CowUtils; use num_traits::float::FloatCore; mod globals; @@ -916,7 +917,7 @@ impl Number { /// Helper function that formats a float as a ES6-style exponential number string. fn f64_to_exponential(n: f64) -> JsString { js_string!(match n.abs() { - x if x >= 1.0 || x == 0.0 => format!("{n:e}").replace('e', "e+"), + x if x >= 1.0 || x == 0.0 => format!("{n:e}").cow_replace('e', "e+").to_string(), _ => format!("{n:e}"), }) } diff --git a/core/engine/src/builtins/string/mod.rs b/core/engine/src/builtins/string/mod.rs index 77d75771af5..96113d4580b 100644 --- a/core/engine/src/builtins/string/mod.rs +++ b/core/engine/src/builtins/string/mod.rs @@ -25,6 +25,7 @@ use crate::{ use boa_macros::utf16; use boa_profiler::Profiler; +use cow_utils::CowUtils; use icu_normalizer::{ComposingNormalizer, DecomposingNormalizer}; use std::cmp::{max, min}; @@ -1727,9 +1728,9 @@ impl String { // the Unicode Default Case Conversion algorithm. let text = string.map_valid_segments(|s| { if UPPER { - s.to_uppercase() + s.cow_to_uppercase().to_string() } else { - s.to_lowercase() + s.cow_to_lowercase().to_string() } }); diff --git a/core/engine/src/builtins/temporal/zoneddatetime/mod.rs b/core/engine/src/builtins/temporal/zoneddatetime/mod.rs index 5d989d086f6..e9979fc2d0e 100644 --- a/core/engine/src/builtins/temporal/zoneddatetime/mod.rs +++ b/core/engine/src/builtins/temporal/zoneddatetime/mod.rs @@ -17,6 +17,7 @@ use crate::{ }; use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; +use cow_utils::CowUtils; use num_traits::ToPrimitive; use temporal_rs::{ options::{ArithmeticOverflow, Disambiguation, OffsetDisambiguation}, @@ -453,7 +454,7 @@ impl ZonedDateTime { let era = zdt.inner.era_with_provider(context.tz_provider())?; Ok(era - .map(|tinystr| JsString::from(tinystr.to_lowercase())) + .map(|tinystr| JsString::from(tinystr.cow_to_lowercase().to_string())) .into_or_undefined()) } diff --git a/core/macros/Cargo.toml b/core/macros/Cargo.toml index 6f43d7dd8cb..81fc6c29872 100644 --- a/core/macros/Cargo.toml +++ b/core/macros/Cargo.toml @@ -12,6 +12,7 @@ rust-version.workspace = true proc-macro = true [dependencies] +cow-utils.workspace = true quote.workspace = true syn = { workspace = true, features = ["full"] } proc-macro2.workspace = true diff --git a/core/macros/src/lib.rs b/core/macros/src/lib.rs index b5247c4b59b..9f22e884a4f 100644 --- a/core/macros/src/lib.rs +++ b/core/macros/src/lib.rs @@ -6,6 +6,7 @@ )] #![cfg_attr(not(test), forbid(clippy::unwrap_used))] +use cow_utils::CowUtils; use proc_macro::TokenStream; use proc_macro2::Literal; use quote::{quote, ToTokens}; @@ -65,14 +66,14 @@ impl Parse for Static { let ident = if let Some(ident) = ident { syn::parse2::(ident.into_token_stream())? } else { - Ident::new(&literal.value().to_uppercase(), literal.span()) + Ident::new(&literal.value().cow_to_uppercase(), literal.span()) }; Ok(Self { literal, ident }) } Expr::Lit(expr) => match expr.lit { Lit::Str(str) => Ok(Self { - ident: Ident::new(&str.value().to_uppercase(), str.span()), + ident: Ident::new(&str.value().cow_to_uppercase(), str.span()), literal: str, }), _ => Err(syn::Error::new_spanned( @@ -108,9 +109,9 @@ pub fn static_syms(input: TokenStream) -> TokenStream { "Symbol for the \"{}\" string.", lit.literal .value() - .replace('<', r"\<") - .replace('>', r"\>") - .replace('*', r"\*") + .cow_replace('<', r"\<") + .cow_replace('>', r"\>") + .cow_replace('*', r"\*") ); let ident = &lit.ident; idx += 1; diff --git a/tests/tester/Cargo.toml b/tests/tester/Cargo.toml index f2e5f165df3..ce6f3b9023b 100644 --- a/tests/tester/Cargo.toml +++ b/tests/tester/Cargo.toml @@ -29,6 +29,7 @@ phf = { workspace = true, features = ["macros"] } comfy-table.workspace = true serde_repr.workspace = true bus.workspace = true +cow-utils.workspace = true [features] default = ["boa_engine/intl_bundled", "boa_engine/experimental", "boa_engine/annex-b"] diff --git a/tests/tester/src/read.rs b/tests/tester/src/read.rs index 1cc85a3a925..f9af9bd4ce9 100644 --- a/tests/tester/src/read.rs +++ b/tests/tester/src/read.rs @@ -11,6 +11,7 @@ use color_eyre::{ eyre::{OptionExt, WrapErr}, Result, }; +use cow_utils::CowUtils; use rustc_hash::{FxBuildHasher, FxHashMap}; use serde::Deserialize; @@ -247,7 +248,7 @@ fn read_metadata(test: &Path) -> Result { let (metadata, _) = metadata .split_once("---*/") .ok_or_eyre("invalid test metadata")?; - let metadata = metadata.replace('\r', "\n"); + let metadata = metadata.cow_replace('\r', "\n"); serde_yaml::from_str(&metadata).map_err(Into::into) }