refactor(transformer/optional-chaining): avoid multiple symbol lookups (#7421)

`IdentifierReference::is_global_reference` and `MaybeBoundIdentifier::from_identifier_reference` both look up the symbol for the identifier. Do this lookup only once, rather than twice.
This commit is contained in:
overlookmotel 2024-11-22 13:36:34 +00:00
parent 971c91a2e8
commit 52784d2aea

View file

@ -51,7 +51,7 @@ use std::mem;
use oxc_allocator::CloneIn; use oxc_allocator::CloneIn;
use oxc_ast::{ast::*, NONE}; use oxc_ast::{ast::*, NONE};
use oxc_semantic::{IsGlobalReference, SymbolFlags}; use oxc_semantic::SymbolFlags;
use oxc_span::SPAN; use oxc_span::SPAN;
use oxc_traverse::{Ancestor, BoundIdentifier, MaybeBoundIdentifier, Traverse, TraverseCtx}; use oxc_traverse::{Ancestor, BoundIdentifier, MaybeBoundIdentifier, Traverse, TraverseCtx};
@ -180,17 +180,26 @@ impl<'a, 'ctx> OptionalChaining<'a, 'ctx> {
} }
} }
/// Check if we should create a temp variable for the identifier /// Check if we should create a temp variable for the identifier.
/// ///
/// Except for `eval`, we should create a temp variable for all global references /// Except for `eval`, we should create a temp variable for all global references.
fn should_create_temp_variable_for_identifier( ///
/// If no temp variable required, returns `MaybeBoundIdentifier` for existing variable/global.
/// If temp variable is required, returns `None`.
fn get_existing_binding_for_identifier(
&self, &self,
ident: &IdentifierReference<'a>, ident: &IdentifierReference<'a>,
ctx: &TraverseCtx<'a>, ctx: &TraverseCtx<'a>,
) -> bool { ) -> Option<MaybeBoundIdentifier<'a>> {
!self.ctx.assumptions.pure_getters let binding = MaybeBoundIdentifier::from_identifier_reference(ident, ctx);
&& ident.is_global_reference(ctx.symbols()) if self.ctx.assumptions.pure_getters
&& ident.name != "eval" || binding.to_bound_identifier().is_some()
|| ident.name == "eval"
{
Some(binding)
} else {
None
}
} }
/// Return `left === null` /// Return `left === null`
@ -544,8 +553,7 @@ impl<'a, 'ctx> OptionalChaining<'a, 'ctx> {
// If the expression is an identifier and it's not a global reference, we just wrap it with checks // If the expression is an identifier and it's not a global reference, we just wrap it with checks
// `foo` -> `foo === null || foo === void 0` // `foo` -> `foo === null || foo === void 0`
if let Expression::Identifier(ident) = expr { if let Expression::Identifier(ident) = expr {
if !self.should_create_temp_variable_for_identifier(ident, ctx) { if let Some(binding) = self.get_existing_binding_for_identifier(ident, ctx) {
let binding = MaybeBoundIdentifier::from_identifier_reference(ident, ctx);
let left1 = binding.create_read_expression(ctx); let left1 = binding.create_read_expression(ctx);
let left2 = binding.create_read_expression(ctx); let left2 = binding.create_read_expression(ctx);
if ident.name == "eval" { if ident.name == "eval" {
@ -568,18 +576,17 @@ impl<'a, 'ctx> OptionalChaining<'a, 'ctx> {
// If the [`MemberExpression::object`] is a global reference, we need to assign it to a temp binding. // If the [`MemberExpression::object`] is a global reference, we need to assign it to a temp binding.
// i.e `foo` -> `(_foo = foo)` // i.e `foo` -> `(_foo = foo)`
if let Expression::Identifier(ident) = object { if let Expression::Identifier(ident) = object {
let binding = if self.should_create_temp_variable_for_identifier(ident, ctx) { let binding =
let binding = self.generate_binding(object, ctx); self.get_existing_binding_for_identifier(ident, ctx).unwrap_or_else(|| {
// `(_foo = foo)` let binding = self.generate_binding(object, ctx);
*object = Self::create_assignment_expression( // `(_foo = foo)`
binding.create_write_target(ctx), *object = Self::create_assignment_expression(
ctx.ast.move_expression(object), binding.create_write_target(ctx),
ctx, ctx.ast.move_expression(object),
); ctx,
binding.to_maybe_bound_identifier() );
} else { binding.to_maybe_bound_identifier()
MaybeBoundIdentifier::from_identifier_reference(ident, ctx) });
};
self.set_binding_context(binding); self.set_binding_context(binding);
} else if matches!(object, Expression::Super(_)) { } else if matches!(object, Expression::Super(_)) {
self.set_this_context(); self.set_this_context();