From 4892875d6912aea7ebf15a897db94bb05d345790 Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Sun, 17 Nov 2024 05:08:56 +0000 Subject: [PATCH] perf(transformer/arrow-function): optimize `generate_super_binding_name` (#7312) Optimize this function in various ways: * Return a static `Atom` (rather than allocating into arena) if no `property`. * Reserve capacity for `ArenaString` at start. * Make path for unicode identifiers (very rare case) `#[cold]` and `#[inline(never)]`. * Slightly faster uppercase conversion. --- .../src/common/arrow_function_converter.rs | 50 +++++++++++++------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/crates/oxc_transformer/src/common/arrow_function_converter.rs b/crates/oxc_transformer/src/common/arrow_function_converter.rs index f87c6ca8944e10..aeb7d4736d5159 100644 --- a/crates/oxc_transformer/src/common/arrow_function_converter.rs +++ b/crates/oxc_transformer/src/common/arrow_function_converter.rs @@ -797,34 +797,52 @@ impl<'a> ArrowFunctionConverter<'a> { ) } - /// Generate a binding name for the super method, like `_superprop_getXXX`. + /// Generate a binding name for the super method, like `superprop_getXXX`. fn generate_super_binding_name( is_assignment: bool, property: &str, ctx: &TraverseCtx<'a>, ) -> Atom<'a> { - let mut name = ArenaString::new_in(ctx.ast.allocator); + let start = + if is_assignment { Atom::from("superprop_set") } else { Atom::from("superprop_get") }; - name.push_str("superprop_"); - if is_assignment { - name.push_str("set"); - } else { - name.push_str("get"); - } + let Some(&first_byte) = property.as_bytes().first() else { + return start; + }; - // Capitalize the first letter of the property name - if let Some(&first_byte) = property.as_bytes().first() { - if first_byte.is_ascii() { - name.push(first_byte.to_ascii_uppercase() as char); - if property.len() > 1 { - name.push_str(&property[1..]); - } - } else { + let mut name = + ArenaString::with_capacity_in(start.len() + property.len(), ctx.ast.allocator); + name.push_str(start.as_str()); + + // Capitalize the first letter of the property name. + // Fast path for ASCII (very common case). + // TODO(improve-on-babel): We could just use format `superprop_get_prop` and avoid capitalizing. + if first_byte.is_ascii() { + // We know `IdentifierName`s begin with `a-z`, `A-Z`, `_` or `$` if ASCII, + // so can use a slightly cheaper conversion than `u8::to_ascii_uppercase`. + // Adapted from `u8::to_ascii_uppercase`'s implementation. + // https://godbolt.org/z/5Txa6Pv9z + #[inline] + fn ascii_ident_first_char_uppercase(b: u8) -> u8 { + const ASCII_CASE_MASK: u8 = 0b0010_0000; + let is_lower_case = b >= b'a'; + b ^ (u8::from(is_lower_case) * ASCII_CASE_MASK) + } + + name.push(ascii_ident_first_char_uppercase(first_byte) as char); + if property.len() > 1 { + name.push_str(&property[1..]); + } + } else { + #[cold] + #[inline(never)] + fn push_unicode(property: &str, name: &mut ArenaString) { let mut chars = property.chars(); let first_char = chars.next().unwrap(); name.extend(first_char.to_uppercase()); name.push_str(chars.as_str()); } + push_unicode(property, &mut name); } ctx.ast.atom(name.into_bump_str())