use std::array;
use oxc_allocator::CloneIn;
use oxc_ast::ast::{AssignmentOperator, Expression};
use oxc_span::SPAN;
use oxc_syntax::reference::ReferenceFlags;
use oxc_traverse::BoundIdentifier;
use crate::context::TraverseCtx;
use super::var_declarations::VarDeclarationsStore;
pub fn duplicate_expression<'a>(
expr: Expression<'a>,
mutated_symbol_needs_temp_var: bool,
ctx: &mut TraverseCtx<'a>,
) -> (Expression<'a>, Expression<'a>) {
let (maybe_assignment, references) =
duplicate_expression_multiple::<1>(expr, mutated_symbol_needs_temp_var, ctx);
let [reference] = references;
(maybe_assignment, reference)
}
pub fn duplicate_expression_twice<'a>(
expr: Expression<'a>,
mutated_symbol_needs_temp_var: bool,
ctx: &mut TraverseCtx<'a>,
) -> (Expression<'a>, Expression<'a>, Expression<'a>) {
let (maybe_assignment, references) =
duplicate_expression_multiple::<2>(expr, mutated_symbol_needs_temp_var, ctx);
let [reference1, reference2] = references;
(maybe_assignment, reference1, reference2)
}
pub fn duplicate_expression_multiple<'a, const N: usize>(
expr: Expression<'a>,
mutated_symbol_needs_temp_var: bool,
ctx: &mut TraverseCtx<'a>,
) -> (Expression<'a>, [Expression<'a>; N]) {
let temp_var_binding = match &expr {
Expression::Identifier(ident) => {
let reference_id = ident.reference_id();
let reference = ctx.scoping().get_reference(reference_id);
if let Some(symbol_id) = reference.symbol_id()
&& (!mutated_symbol_needs_temp_var || !ctx.scoping().symbol_is_mutated(symbol_id))
{
let binding = BoundIdentifier::new(ident.name, symbol_id);
let references =
array::from_fn(|_| binding.create_spanned_read_expression(ident.span, ctx));
return (expr, references);
}
let reference = ctx.scoping_mut().get_reference_mut(reference_id);
*reference.flags_mut() = ReferenceFlags::Read;
VarDeclarationsStore::create_uid_var(&ident.name, ctx)
}
Expression::ThisExpression(_)
| Expression::Super(_)
| Expression::BooleanLiteral(_)
| Expression::NullLiteral(_)
| Expression::NumericLiteral(_)
| Expression::BigIntLiteral(_)
| Expression::RegExpLiteral(_)
| Expression::StringLiteral(_) => {
let references = array::from_fn(|_| expr.clone_in(ctx.ast.allocator));
return (expr, references);
}
Expression::TemplateLiteral(lit) if lit.expressions.is_empty() => {
let references = array::from_fn(|_| {
ctx.ast.expression_template_literal(
lit.span,
ctx.ast.vec_from_iter(lit.quasis.iter().cloned()),
ctx.ast.vec(),
)
});
return (expr, references);
}
_ => VarDeclarationsStore::create_uid_var_based_on_node(&expr, ctx),
};
let assignment = ctx.ast.expression_assignment(
SPAN,
AssignmentOperator::Assign,
temp_var_binding.create_target(ReferenceFlags::Write, ctx),
expr,
);
let references = array::from_fn(|_| temp_var_binding.create_read_expression(ctx));
(assignment, references)
}