Skip to content

Commit

Permalink
refactor(transformer/arrow-function): generate_super_binding_name t…
Browse files Browse the repository at this point in the history
…ake `&str` and `&TraverseCtx` (#7310)

Pure refactor. Take `&str` rather than `&Atom` (1 less indirection) and `&TraverseCtx<'a>` (more conventional).
  • Loading branch information
overlookmotel committed Nov 17, 2024
1 parent 7b7555a commit 7fc8905
Showing 1 changed file with 158 additions and 9 deletions.
167 changes: 158 additions & 9 deletions crates/oxc_transformer/src/common/arrow_function_converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
use rustc_hash::{FxHashMap, FxHashSet};

use oxc_allocator::{Box as ArenaBox, String as ArenaString, Vec as ArenaVec};
use oxc_ast::{ast::*, AstBuilder, NONE};
use oxc_ast::{ast::*, NONE};
use oxc_data_structures::stack::SparseStack;
use oxc_semantic::{ReferenceFlags, SymbolId};
use oxc_span::{CompactStr, SPAN};
Expand Down Expand Up @@ -568,7 +568,7 @@ impl<'a> ArrowFunctionConverter<'a> {
let super_methods = self.super_methods.as_mut()?;

let mut argument = None;
let mut property = Atom::empty();
let mut property = "";
let init = match expr.to_member_expression_mut() {
MemberExpression::ComputedMemberExpression(computed_member) => {
if !matches!(computed_member.object, Expression::Super(_)) {
Expand All @@ -585,7 +585,7 @@ impl<'a> ArrowFunctionConverter<'a> {
}

// Used to generate the name of the arrow function.
property = static_member.property.name.clone();
property = static_member.property.name.as_str();
ctx.ast.move_expression(expr)
}
MemberExpression::PrivateFieldExpression(_) => {
Expand All @@ -594,8 +594,7 @@ impl<'a> ArrowFunctionConverter<'a> {
}
};

let binding_name =
Self::generate_super_binding_name(assign_value.is_some(), &property, ctx.ast);
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 binding = ctx
.generate_uid_in_current_scope(&binding_name, SymbolFlags::FunctionScopedVariable);
Expand Down Expand Up @@ -801,10 +800,10 @@ impl<'a> ArrowFunctionConverter<'a> {
/// Generate a binding name for the super method, like `_superprop_getXXX`.
fn generate_super_binding_name(
is_assignment: bool,
property: &Atom<'a>,
ast: AstBuilder<'a>,
property: &str,
ctx: &TraverseCtx<'a>,
) -> Atom<'a> {
let mut name = ArenaString::new_in(ast.allocator);
let mut name = ArenaString::new_in(ctx.ast.allocator);

name.push_str("superprop_");
if is_assignment {
Expand All @@ -820,7 +819,157 @@ impl<'a> ArrowFunctionConverter<'a> {
if property.len() > 1 {
name.push_str(&property[1..]);
}
ast.atom(name.into_bump_str())
ctx.ast.atom(name.into_bump_str())
}

/// Whether to transform the `arguments` identifier.
fn should_transform_arguments_identifier(&self, name: &str, ctx: &mut TraverseCtx<'a>) -> bool {
self.is_async_only() && name == "arguments" && Self::is_affected_arguments_identifier(ctx)
}

/// Check if the `arguments` identifier is affected by the transformation.
fn is_affected_arguments_identifier(ctx: &mut TraverseCtx<'a>) -> bool {
let mut ancestors = ctx.ancestors().skip(1);
while let Some(ancestor) = ancestors.next() {
match ancestor {
Ancestor::ArrowFunctionExpressionParams(arrow) => {
if *arrow.r#async() {
return true;
}
}
Ancestor::ArrowFunctionExpressionBody(arrow) => {
if *arrow.r#async() {
return true;
}
}
Ancestor::FunctionBody(func) => {
return *func.r#async()
&& Self::is_class_method_like_ancestor(ancestors.next().unwrap());
}
_ => (),
}
}

false
}

/// Rename the `arguments` symbol to a new name.
fn rename_arguments_symbol(symbol_id: SymbolId, name: CompactStr, ctx: &mut TraverseCtx<'a>) {
let scope_id = ctx.symbols().get_scope_id(symbol_id);
ctx.symbols_mut().rename(symbol_id, name.clone());
ctx.scopes_mut().rename_binding(scope_id, "arguments", name);
}

/// Transform the identifier reference for `arguments` if it's affected after transformation.
///
/// See [`Self::transform_member_expression_for_super`] for the reason.
fn transform_identifier_reference_for_arguments(
&mut self,
ident: &mut IdentifierReference<'a>,
ctx: &mut TraverseCtx<'a>,
) {
if !self.should_transform_arguments_identifier(&ident.name, ctx) {
return;
}

let reference_id = ident.reference_id();
let symbol_id = ctx.symbols().get_reference(reference_id).symbol_id();

let binding = self.arguments_var_stack.last_or_init(|| {
if let Some(symbol_id) = symbol_id {
let arguments_name = ctx.generate_uid_name("arguments");
let arguments_name_atom = ctx.ast.atom(&arguments_name);
Self::rename_arguments_symbol(symbol_id, arguments_name, ctx);
// Record the symbol ID as a renamed `arguments` variable.
self.renamed_arguments_symbol_ids.insert(symbol_id);
BoundIdentifier::new(arguments_name_atom, symbol_id)
} else {
// We cannot determine the final scope ID of the `arguments` variable insertion,
// because the `arguments` variable will be inserted to a new scope which haven't been created yet,
// so we temporary use root scope id as the fake target scope ID.
let target_scope_id = ctx.scopes().root_scope_id();
ctx.generate_uid("arguments", target_scope_id, SymbolFlags::FunctionScopedVariable)
}
});

// If no symbol ID, it means there is no variable named `arguments` in the scope.
// The following code is just to sync semantics.
if symbol_id.is_none() {
let reference = ctx.symbols_mut().get_reference_mut(reference_id);
reference.set_symbol_id(binding.symbol_id);
ctx.scopes_mut().delete_root_unresolved_reference(&ident.name, reference_id);
ctx.symbols_mut().resolved_references[binding.symbol_id].push(reference_id);
}

ident.name = binding.name.clone();
}

/// Transform the binding identifier for `arguments` if it's affected after transformation.
///
/// The main work is to rename the `arguments` binding identifier to a new name.
fn transform_binding_identifier_for_arguments(
&mut self,
ident: &mut BindingIdentifier<'a>,
ctx: &mut TraverseCtx<'a>,
) {
if ctx.current_scope_flags().is_strict_mode() // `arguments` is not allowed to be defined in strict mode.
|| !self.should_transform_arguments_identifier(&ident.name, ctx)
{
return;
}

self.arguments_var_stack.last_or_init(|| {
let arguments_name = ctx.generate_uid_name("arguments");
ident.name = ctx.ast.atom(&arguments_name);
let symbol_id = ident.symbol_id();
Self::rename_arguments_symbol(symbol_id, arguments_name, ctx);
// Record the symbol ID as a renamed `arguments` variable.
self.renamed_arguments_symbol_ids.insert(symbol_id);
BoundIdentifier::new(ident.name.clone(), symbol_id)
});
}

/// Create a variable declarator looks like `_arguments = arguments;`.
fn create_arguments_var_declarator(
&self,
target_scope_id: ScopeId,
arguments_var: Option<BoundIdentifier<'a>>,
ctx: &mut TraverseCtx<'a>,
) -> Option<VariableDeclarator<'a>> {
let arguments_var = arguments_var?;

// Just a renamed `arguments` variable, we don't need to create a new variable declaration.
if self.renamed_arguments_symbol_ids.contains(&arguments_var.symbol_id) {
return None;
}

Self::adjust_binding_scope(target_scope_id, &arguments_var, ctx);
let reference =
ctx.create_unbound_ident_reference(SPAN, Atom::from("arguments"), ReferenceFlags::Read);
let mut init = Expression::Identifier(ctx.ast.alloc(reference.clone()));

// Top level may doesn't have `arguments`, so we need to check it.
// `typeof arguments === "undefined" ? void 0 : arguments;`
if ctx.scopes().root_scope_id() == target_scope_id {
let argument = Expression::Identifier(ctx.ast.alloc(reference));
let typeof_arguments = ctx.ast.expression_unary(SPAN, UnaryOperator::Typeof, argument);
let undefined_literal = ctx.ast.expression_string_literal(SPAN, "undefined");
let test = ctx.ast.expression_binary(
SPAN,
typeof_arguments,
BinaryOperator::StrictEquality,
undefined_literal,
);
init = ctx.ast.expression_conditional(SPAN, test, ctx.ast.void_0(SPAN), init);
}

Some(ctx.ast.variable_declarator(
SPAN,
VariableDeclarationKind::Var,
arguments_var.create_binding_pattern(ctx),
Some(init),
false,
))
}

/// Whether to transform the `arguments` identifier.
Expand Down

0 comments on commit 7fc8905

Please sign in to comment.