From 94b20ba435d59e045fb76b6dd5e24a82b0b24870 Mon Sep 17 00:00:00 2001 From: overlookmotel Date: Sat, 16 Nov 2024 16:04:12 +0000 Subject: [PATCH] perf(transformer/arrow-function): super method binding names lazily --- Cargo.lock | 1 + crates/oxc_transformer/Cargo.toml | 1 + .../src/common/arrow_function_converter.rs | 69 ++++++++++--------- 3 files changed, 40 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2c517e01547b2..dff09b9f4c9d00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2062,6 +2062,7 @@ name = "oxc_transformer" version = "0.36.0" dependencies = [ "base64", + "compact_str", "cow-utils", "dashmap 6.1.0", "indexmap", diff --git a/crates/oxc_transformer/Cargo.toml b/crates/oxc_transformer/Cargo.toml index ce23b97be84b93..6ed2e2cfcd6878 100644 --- a/crates/oxc_transformer/Cargo.toml +++ b/crates/oxc_transformer/Cargo.toml @@ -35,6 +35,7 @@ oxc_syntax = { workspace = true, features = ["to_js_string"] } oxc_traverse = { workspace = true } base64 = { workspace = true } +compact_str = { workspace = true } cow-utils = { workspace = true } dashmap = { workspace = true } indexmap = { workspace = true } diff --git a/crates/oxc_transformer/src/common/arrow_function_converter.rs b/crates/oxc_transformer/src/common/arrow_function_converter.rs index 2f2b2d3d4e679e..50aea057d9d949 100644 --- a/crates/oxc_transformer/src/common/arrow_function_converter.rs +++ b/crates/oxc_transformer/src/common/arrow_function_converter.rs @@ -89,10 +89,11 @@ use std::hash::BuildHasherDefault; +use compact_str::CompactString; use indexmap::IndexMap; use rustc_hash::{FxHashSet, FxHasher}; -use oxc_allocator::{Box as ArenaBox, String as ArenaString, Vec as ArenaVec}; +use oxc_allocator::{Box as ArenaBox, Vec as ArenaVec}; use oxc_ast::{ast::*, NONE}; use oxc_data_structures::stack::SparseStack; use oxc_semantic::{ReferenceFlags, SymbolId}; @@ -120,13 +121,20 @@ pub enum ArrowFunctionConverterMode { AsyncOnly, } +#[derive(PartialEq, Eq, Hash)] +struct SuperMethodKey<'a> { + /// If it is true, the method should accept a value parameter. + is_assignment: bool, + /// Name of property getter/setter is for. + /// Empty string for computed properties. + property: &'a str, +} + struct SuperMethodInfo<'a> { binding: BoundIdentifier<'a>, super_expr: Expression<'a>, /// If it is true, the method should accept a prop parameter. is_computed: bool, - /// If it is true, the method should accept a value parameter. - is_assignment: bool, } pub struct ArrowFunctionConverter<'a> { @@ -136,7 +144,7 @@ pub struct ArrowFunctionConverter<'a> { renamed_arguments_symbol_ids: FxHashSet, // TODO(improve-on-babel): `FxHashMap` would suffice here. Iteration order is not important. // Only using `FxIndexMap` for predictable iteration order to match Babel's output. - super_methods: Option, SuperMethodInfo<'a>>>, + super_methods: Option, SuperMethodInfo<'a>>>, } impl<'a> ArrowFunctionConverter<'a> { @@ -601,16 +609,13 @@ impl<'a> ArrowFunctionConverter<'a> { } }; - let binding_name = Self::generate_super_binding_name(assign_value.is_some(), property, ctx); - let super_info = super_methods.entry(binding_name.clone()).or_insert_with(|| { + let is_assignment = assign_value.is_some(); + let key = SuperMethodKey { is_assignment, property }; + let super_info = super_methods.entry(key).or_insert_with(|| { + let binding_name = Self::generate_super_binding_name(is_assignment, property); let binding = ctx .generate_uid_in_current_scope(&binding_name, SymbolFlags::FunctionScopedVariable); - SuperMethodInfo { - binding, - super_expr: init, - is_computed: argument.is_some(), - is_assignment: assign_value.is_some(), - } + SuperMethodInfo { binding, super_expr: init, is_computed: argument.is_some() } }); let callee = super_info.binding.create_read_expression(ctx); @@ -729,10 +734,10 @@ impl<'a> ArrowFunctionConverter<'a> { fn generate_super_method( target_scope_id: ScopeId, super_method: SuperMethodInfo<'a>, + is_assignment: bool, ctx: &mut TraverseCtx<'a>, ) -> VariableDeclarator<'a> { - let SuperMethodInfo { binding, super_expr: mut init, is_computed, is_assignment } = - super_method; + let SuperMethodInfo { binding, super_expr: mut init, is_computed } = super_method; Self::adjust_binding_scope(target_scope_id, &binding, ctx); let scope_id = @@ -805,22 +810,17 @@ impl<'a> ArrowFunctionConverter<'a> { } /// 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 start = - if is_assignment { Atom::from("superprop_set") } else { Atom::from("superprop_get") }; + fn generate_super_binding_name(is_assignment: bool, property: &str) -> CompactString { + let mut name = if is_assignment { + CompactString::const_new("superprop_set") + } else { + CompactString::const_new("superprop_get") + }; let Some(&first_byte) = property.as_bytes().first() else { - return start; + return name; }; - 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. @@ -843,7 +843,7 @@ impl<'a> ArrowFunctionConverter<'a> { } else { #[cold] #[inline(never)] - fn push_unicode(property: &str, name: &mut ArenaString) { + fn push_unicode(property: &str, name: &mut CompactString) { let mut chars = property.chars(); let first_char = chars.next().unwrap(); name.extend(first_char.to_uppercase()); @@ -852,7 +852,7 @@ impl<'a> ArrowFunctionConverter<'a> { push_unicode(property, &mut name); } - ctx.ast.atom(name.into_bump_str()) + name } /// Whether to transform the `arguments` identifier. @@ -1037,11 +1037,18 @@ impl<'a> ArrowFunctionConverter<'a> { declarations.push(arguments); } - // `_superprop_getSomething = () => super.getSomething;` + // `_superprop_getSomething = () => super.something;` + // `_superprop_setSomething = _value => super.something = _value;` + // `_superprop_set = (_prop, _value) => super[_prop] = _value;` if is_class_method_like { if let Some(super_methods) = self.super_methods.as_mut() { - declarations.extend(super_methods.drain(..).map(|(_, super_method)| { - Self::generate_super_method(target_scope_id, super_method, ctx) + declarations.extend(super_methods.drain(..).map(|(key, super_method)| { + Self::generate_super_method( + target_scope_id, + super_method, + key.is_assignment, + ctx, + ) })); } }