mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
refactor(minifier): use Ctx (#8716)
This commit is contained in:
parent
0af0267077
commit
bf8be23f11
10 changed files with 134 additions and 181 deletions
|
|
@ -1,6 +1,7 @@
|
|||
use oxc_allocator::Vec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_traverse::TraverseCtx;
|
||||
|
||||
use crate::ctx::Ctx;
|
||||
|
||||
use super::PeepholeOptimizations;
|
||||
|
||||
|
|
@ -17,7 +18,7 @@ impl<'a> PeepholeOptimizations {
|
|||
pub fn collapse_variable_declarations(
|
||||
&mut self,
|
||||
stmts: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
self.join_vars(stmts, ctx);
|
||||
self.maybe_collapse_into_for_statements(stmts, ctx);
|
||||
|
|
@ -44,7 +45,7 @@ impl<'a> PeepholeOptimizations {
|
|||
None
|
||||
}
|
||||
|
||||
fn join_vars(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
fn join_vars(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: Ctx<'a, '_>) {
|
||||
if stmts.len() < 2 {
|
||||
return;
|
||||
}
|
||||
|
|
@ -102,7 +103,7 @@ impl<'a> PeepholeOptimizations {
|
|||
fn maybe_collapse_into_for_statements(
|
||||
&mut self,
|
||||
stmts: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
if stmts.len() <= 1 {
|
||||
return;
|
||||
|
|
@ -142,7 +143,7 @@ impl<'a> PeepholeOptimizations {
|
|||
&mut self,
|
||||
i: usize,
|
||||
stmts: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
if let Statement::ExpressionStatement(expr_stmt) = ctx.ast.move_statement(&mut stmts[i]) {
|
||||
if let Statement::ForStatement(for_stmt) = &mut stmts[i + 1] {
|
||||
|
|
@ -156,7 +157,7 @@ impl<'a> PeepholeOptimizations {
|
|||
&mut self,
|
||||
i: usize,
|
||||
stmts: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
if let Statement::VariableDeclaration(var) = ctx.ast.move_statement(&mut stmts[i]) {
|
||||
if let Statement::ForStatement(for_stmt) = &mut stmts[i + 1] {
|
||||
|
|
@ -181,7 +182,7 @@ impl<'a> PeepholeOptimizations {
|
|||
&mut self,
|
||||
i: usize,
|
||||
stmts: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
if let Statement::VariableDeclaration(decl) = &stmts[i] {
|
||||
if decl.kind.is_var()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use oxc_ast::ast::*;
|
||||
use oxc_syntax::identifier::is_identifier_name;
|
||||
use oxc_traverse::TraverseCtx;
|
||||
|
||||
use crate::ctx::Ctx;
|
||||
|
||||
use super::LatePeepholeOptimizations;
|
||||
use crate::ctx::Ctx;
|
||||
|
||||
impl<'a> LatePeepholeOptimizations {
|
||||
/// Converts property accesses from quoted string or bracket access syntax to dot or unquoted string
|
||||
|
|
@ -13,10 +13,7 @@ impl<'a> LatePeepholeOptimizations {
|
|||
///
|
||||
/// `foo['bar']` -> `foo.bar`
|
||||
/// `foo?.['bar']` -> `foo?.bar`
|
||||
pub fn convert_to_dotted_properties(
|
||||
expr: &mut MemberExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
pub fn convert_to_dotted_properties(expr: &mut MemberExpression<'a>, ctx: Ctx<'a, '_>) {
|
||||
let MemberExpression::ComputedMemberExpression(e) = expr else { return };
|
||||
let Expression::StringLiteral(s) = &e.expression else { return };
|
||||
if is_identifier_name(&s.value) {
|
||||
|
|
@ -32,8 +29,7 @@ impl<'a> LatePeepholeOptimizations {
|
|||
return;
|
||||
}
|
||||
if let Some(n) = Ctx::string_to_equivalent_number_value(v) {
|
||||
e.expression =
|
||||
Ctx(ctx).ast.expression_numeric_literal(s.span, n, None, NumberBase::Decimal);
|
||||
e.expression = ctx.ast.expression_numeric_literal(s.span, n, None, NumberBase::Decimal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use oxc_syntax::{
|
|||
number::NumberBase,
|
||||
operator::{BinaryOperator, LogicalOperator},
|
||||
};
|
||||
use oxc_traverse::{Ancestor, TraverseCtx};
|
||||
use oxc_traverse::Ancestor;
|
||||
|
||||
use crate::ctx::Ctx;
|
||||
|
||||
|
|
@ -18,12 +18,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
/// Constant Folding
|
||||
///
|
||||
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/PeepholeFoldConstants.java>
|
||||
pub fn fold_constants_exit_expression(
|
||||
&mut self,
|
||||
expr: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
let ctx = Ctx(ctx);
|
||||
pub fn fold_constants_exit_expression(&mut self, expr: &mut Expression<'a>, ctx: Ctx<'a, '_>) {
|
||||
if let Some(folded_expr) = match expr {
|
||||
Expression::BinaryExpression(e) => Self::try_fold_binary_expr(e, ctx)
|
||||
.or_else(|| Self::try_fold_binary_typeof_comparison(e, ctx)),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use oxc_ecmascript::{
|
|||
};
|
||||
use oxc_span::{cmp::ContentEq, GetSpan};
|
||||
use oxc_syntax::es_target::ESTarget;
|
||||
use oxc_traverse::{Ancestor, TraverseCtx};
|
||||
use oxc_traverse::Ancestor;
|
||||
|
||||
use crate::ctx::Ctx;
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ impl<'a> PeepholeOptimizations {
|
|||
pub fn minimize_conditions_exit_statements(
|
||||
&mut self,
|
||||
stmts: &mut oxc_allocator::Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
let mut changed = false;
|
||||
loop {
|
||||
|
|
@ -45,7 +45,7 @@ impl<'a> PeepholeOptimizations {
|
|||
pub fn minimize_conditions_exit_statement(
|
||||
&mut self,
|
||||
stmt: &mut Statement<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
let expr = match stmt {
|
||||
Statement::IfStatement(s) => Some(&mut s.test),
|
||||
|
|
@ -80,7 +80,7 @@ impl<'a> PeepholeOptimizations {
|
|||
pub fn minimize_conditions_exit_expression(
|
||||
&mut self,
|
||||
expr: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
let mut changed = false;
|
||||
loop {
|
||||
|
|
@ -123,7 +123,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
fn minimize_not(span: Span, expr: Expression<'a>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
|
||||
fn minimize_not(span: Span, expr: Expression<'a>, ctx: Ctx<'a, '_>) -> Expression<'a> {
|
||||
let mut unary = ctx.ast.unary_expression(span, UnaryOperator::LogicalNot, expr);
|
||||
Self::try_minimize_not(&mut unary, ctx)
|
||||
.unwrap_or_else(|| Expression::UnaryExpression(ctx.ast.alloc(unary)))
|
||||
|
|
@ -131,7 +131,7 @@ impl<'a> PeepholeOptimizations {
|
|||
|
||||
fn try_minimize_not(
|
||||
expr: &mut UnaryExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
if !expr.operator.is_not() {
|
||||
return None;
|
||||
|
|
@ -159,10 +159,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
fn try_minimize_if(
|
||||
stmt: &mut Statement<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Statement<'a>> {
|
||||
fn try_minimize_if(stmt: &mut Statement<'a>, ctx: Ctx<'a, '_>) -> Option<Statement<'a>> {
|
||||
let Statement::IfStatement(if_stmt) = stmt else { unreachable!() };
|
||||
|
||||
// `if (x) foo()` -> `x && foo()`
|
||||
|
|
@ -276,7 +273,7 @@ impl<'a> PeepholeOptimizations {
|
|||
&mut self,
|
||||
stmts: &mut Vec<'a, Statement<'a>>,
|
||||
changed: &mut bool,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
for i in 0..stmts.len() {
|
||||
let Statement::IfStatement(if_stmt) = &stmts[i] else {
|
||||
|
|
@ -328,7 +325,7 @@ impl<'a> PeepholeOptimizations {
|
|||
matches!(stmt.get_one_child(), Some(Statement::ExpressionStatement(_)))
|
||||
}
|
||||
|
||||
fn get_block_expression(stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
|
||||
fn get_block_expression(stmt: &mut Statement<'a>, ctx: Ctx<'a, '_>) -> Expression<'a> {
|
||||
let Some(Statement::ExpressionStatement(s)) = stmt.get_one_child_mut() else {
|
||||
unreachable!()
|
||||
};
|
||||
|
|
@ -353,15 +350,12 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_block_return_expression(
|
||||
stmt: &mut Statement<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Expression<'a> {
|
||||
fn get_block_return_expression(stmt: &mut Statement<'a>, ctx: Ctx<'a, '_>) -> Expression<'a> {
|
||||
let Some(stmt) = stmt.get_one_child_mut() else { unreachable!() };
|
||||
Self::take_return_argument(stmt, ctx)
|
||||
}
|
||||
|
||||
fn take_return_argument(stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
|
||||
fn take_return_argument(stmt: &mut Statement<'a>, ctx: Ctx<'a, '_>) -> Expression<'a> {
|
||||
let Statement::ReturnStatement(return_stmt) = ctx.ast.move_statement(stmt) else {
|
||||
unreachable!()
|
||||
};
|
||||
|
|
@ -377,7 +371,7 @@ impl<'a> PeepholeOptimizations {
|
|||
test: Expression<'a>,
|
||||
consequent: Expression<'a>,
|
||||
alternate: Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Expression<'a> {
|
||||
let mut cond_expr = ctx.ast.conditional_expression(span, test, consequent, alternate);
|
||||
Self::try_minimize_conditional(&mut cond_expr, ctx)
|
||||
|
|
@ -387,7 +381,7 @@ impl<'a> PeepholeOptimizations {
|
|||
// <https://github.com/evanw/esbuild/blob/v0.24.2/internal/js_ast/js_ast_helpers.go#L2745>
|
||||
fn try_minimize_conditional(
|
||||
expr: &mut ConditionalExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
match &mut expr.test {
|
||||
// "(a, b) ? c : d" => "a, b ? c : d"
|
||||
|
|
@ -484,7 +478,7 @@ impl<'a> PeepholeOptimizations {
|
|||
|
||||
// "a ? b ? c : d : d" => "a && b ? c : d"
|
||||
if let Expression::ConditionalExpression(consequent) = &mut expr.consequent {
|
||||
if Ctx(ctx).expr_eq(&consequent.alternate, &expr.alternate) {
|
||||
if ctx.expr_eq(&consequent.alternate, &expr.alternate) {
|
||||
return Some(ctx.ast.expression_conditional(
|
||||
expr.span,
|
||||
ctx.ast.expression_logical(
|
||||
|
|
@ -501,7 +495,7 @@ impl<'a> PeepholeOptimizations {
|
|||
|
||||
// "a ? b : c ? b : d" => "a || c ? b : d"
|
||||
if let Expression::ConditionalExpression(alternate) = &mut expr.alternate {
|
||||
if Ctx(ctx).expr_eq(&alternate.consequent, &expr.consequent) {
|
||||
if ctx.expr_eq(&alternate.consequent, &expr.consequent) {
|
||||
return Some(ctx.ast.expression_conditional(
|
||||
expr.span,
|
||||
ctx.ast.expression_logical(
|
||||
|
|
@ -519,7 +513,7 @@ impl<'a> PeepholeOptimizations {
|
|||
// "a ? c : (b, c)" => "(a || b), c"
|
||||
if let Expression::SequenceExpression(alternate) = &mut expr.alternate {
|
||||
if alternate.expressions.len() == 2
|
||||
&& Ctx(ctx).expr_eq(&alternate.expressions[1], &expr.consequent)
|
||||
&& ctx.expr_eq(&alternate.expressions[1], &expr.consequent)
|
||||
{
|
||||
return Some(ctx.ast.expression_sequence(
|
||||
expr.span,
|
||||
|
|
@ -539,7 +533,7 @@ impl<'a> PeepholeOptimizations {
|
|||
// "a ? (b, c) : c" => "(a && b), c"
|
||||
if let Expression::SequenceExpression(consequent) = &mut expr.consequent {
|
||||
if consequent.expressions.len() == 2
|
||||
&& Ctx(ctx).expr_eq(&consequent.expressions[1], &expr.alternate)
|
||||
&& ctx.expr_eq(&consequent.expressions[1], &expr.alternate)
|
||||
{
|
||||
return Some(ctx.ast.expression_sequence(
|
||||
expr.span,
|
||||
|
|
@ -559,7 +553,7 @@ impl<'a> PeepholeOptimizations {
|
|||
// "a ? b || c : c" => "(a && b) || c"
|
||||
if let Expression::LogicalExpression(logical_expr) = &mut expr.consequent {
|
||||
if logical_expr.operator == LogicalOperator::Or
|
||||
&& Ctx(ctx).expr_eq(&logical_expr.right, &expr.alternate)
|
||||
&& ctx.expr_eq(&logical_expr.right, &expr.alternate)
|
||||
{
|
||||
return Some(ctx.ast.expression_logical(
|
||||
expr.span,
|
||||
|
|
@ -578,7 +572,7 @@ impl<'a> PeepholeOptimizations {
|
|||
// "a ? c : b && c" => "(a || b) && c"
|
||||
if let Expression::LogicalExpression(logical_expr) = &mut expr.alternate {
|
||||
if logical_expr.operator == LogicalOperator::And
|
||||
&& Ctx(ctx).expr_eq(&logical_expr.right, &expr.consequent)
|
||||
&& ctx.expr_eq(&logical_expr.right, &expr.consequent)
|
||||
{
|
||||
return Some(ctx.ast.expression_logical(
|
||||
expr.span,
|
||||
|
|
@ -602,8 +596,8 @@ impl<'a> PeepholeOptimizations {
|
|||
) = (&expr.test, &mut expr.consequent, &mut expr.alternate)
|
||||
{
|
||||
if consequent.arguments.len() == alternate.arguments.len()
|
||||
&& !Ctx(ctx).is_global_reference(test)
|
||||
&& Ctx(ctx).expr_eq(&consequent.callee, &alternate.callee)
|
||||
&& !ctx.is_global_reference(test)
|
||||
&& ctx.expr_eq(&consequent.callee, &alternate.callee)
|
||||
&& consequent
|
||||
.arguments
|
||||
.iter()
|
||||
|
|
@ -671,7 +665,7 @@ impl<'a> PeepholeOptimizations {
|
|||
|
||||
// TODO: Try using the "??" or "?." operators
|
||||
|
||||
if Ctx(ctx).expr_eq(&expr.alternate, &expr.consequent) {
|
||||
if ctx.expr_eq(&expr.alternate, &expr.consequent) {
|
||||
// TODO:
|
||||
// "/* @__PURE__ */ a() ? b : b" => "b"
|
||||
// if ctx.ExprCanBeRemovedIfUnused(test) {
|
||||
|
|
@ -694,7 +688,7 @@ impl<'a> PeepholeOptimizations {
|
|||
/// - `x ? a = 0 : a = 1` -> `a = x ? 0 : 1`
|
||||
fn try_merge_conditional_expression_inside(
|
||||
expr: &mut ConditionalExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let (
|
||||
Expression::AssignmentExpression(consequent),
|
||||
|
|
@ -733,10 +727,7 @@ impl<'a> PeepholeOptimizations {
|
|||
/// Simplify syntax when we know it's used inside a boolean context, e.g. `if (boolean_context) {}`.
|
||||
///
|
||||
/// <https://github.com/evanw/esbuild/blob/v0.24.2/internal/js_ast/js_ast_helpers.go#L2059>
|
||||
fn try_fold_expr_in_boolean_context(
|
||||
expr: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> bool {
|
||||
fn try_fold_expr_in_boolean_context(expr: &mut Expression<'a>, ctx: Ctx<'a, '_>) -> bool {
|
||||
match expr {
|
||||
// "!!a" => "a"
|
||||
Expression::UnaryExpression(u1) if u1.operator.is_not() => {
|
||||
|
|
@ -772,7 +763,7 @@ impl<'a> PeepholeOptimizations {
|
|||
Self::try_fold_expr_in_boolean_context(&mut e.left, ctx);
|
||||
Self::try_fold_expr_in_boolean_context(&mut e.right, ctx);
|
||||
// "if (anything && truthyNoSideEffects)" => "if (anything)"
|
||||
if Ctx(ctx).get_side_free_boolean_value(&e.right) == Some(true) {
|
||||
if ctx.get_side_free_boolean_value(&e.right) == Some(true) {
|
||||
*expr = ctx.ast.move_expression(&mut e.left);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -782,7 +773,7 @@ impl<'a> PeepholeOptimizations {
|
|||
Self::try_fold_expr_in_boolean_context(&mut e.left, ctx);
|
||||
Self::try_fold_expr_in_boolean_context(&mut e.right, ctx);
|
||||
// "if (anything || falsyNoSideEffects)" => "if (anything)"
|
||||
if Ctx(ctx).get_side_free_boolean_value(&e.right) == Some(false) {
|
||||
if ctx.get_side_free_boolean_value(&e.right) == Some(false) {
|
||||
*expr = ctx.ast.move_expression(&mut e.left);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -791,7 +782,7 @@ impl<'a> PeepholeOptimizations {
|
|||
// "if (a ? !!b : !!c)" => "if (a ? b : c)"
|
||||
Self::try_fold_expr_in_boolean_context(&mut e.consequent, ctx);
|
||||
Self::try_fold_expr_in_boolean_context(&mut e.alternate, ctx);
|
||||
if let Some(boolean) = Ctx(ctx).get_side_free_boolean_value(&e.consequent) {
|
||||
if let Some(boolean) = ctx.get_side_free_boolean_value(&e.consequent) {
|
||||
let right = ctx.ast.move_expression(&mut e.alternate);
|
||||
let left = ctx.ast.move_expression(&mut e.test);
|
||||
if boolean {
|
||||
|
|
@ -806,7 +797,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
if let Some(boolean) = Ctx(ctx).get_side_free_boolean_value(&e.alternate) {
|
||||
if let Some(boolean) = ctx.get_side_free_boolean_value(&e.alternate) {
|
||||
let left = ctx.ast.move_expression(&mut e.test);
|
||||
let right = ctx.ast.move_expression(&mut e.consequent);
|
||||
if boolean {
|
||||
|
|
@ -836,7 +827,7 @@ impl<'a> PeepholeOptimizations {
|
|||
// ^^^^^^ ValueType::from(&e.left).is_number()` is `true`.
|
||||
fn try_minimize_binary(
|
||||
e: &mut BinaryExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
if !e.operator.is_equality() {
|
||||
return None;
|
||||
|
|
@ -895,7 +886,7 @@ impl<'a> PeepholeOptimizations {
|
|||
/// - `document.all == null` is `true`
|
||||
fn try_compress_is_null_or_undefined(
|
||||
expr: &mut LogicalExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let op = expr.operator;
|
||||
let target_ops = match op {
|
||||
|
|
@ -941,7 +932,7 @@ impl<'a> PeepholeOptimizations {
|
|||
right: &mut Expression<'a>,
|
||||
span: Span,
|
||||
(find_op, replace_op): (BinaryOperator, BinaryOperator),
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
enum LeftPairValueResult {
|
||||
Null(Span),
|
||||
|
|
@ -1032,7 +1023,7 @@ impl<'a> PeepholeOptimizations {
|
|||
fn try_compress_normal_assignment_to_combined_logical_assignment(
|
||||
&mut self,
|
||||
expr: &mut AssignmentExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> bool {
|
||||
if self.target < ESTarget::ES2020 {
|
||||
return false;
|
||||
|
|
@ -1053,7 +1044,7 @@ impl<'a> PeepholeOptimizations {
|
|||
};
|
||||
// It should also early return when the reference might refer to a reference value created by a with statement
|
||||
// when the minifier supports with statements
|
||||
if write_id_ref.name != read_id_ref.name || Ctx(ctx).is_global_reference(write_id_ref) {
|
||||
if write_id_ref.name != read_id_ref.name || ctx.is_global_reference(write_id_ref) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1066,7 +1057,7 @@ impl<'a> PeepholeOptimizations {
|
|||
fn try_compress_logical_expression_to_assignment_expression(
|
||||
&self,
|
||||
expr: &mut LogicalExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
if self.target < ESTarget::ES2020 {
|
||||
return None;
|
||||
|
|
@ -1093,7 +1084,7 @@ impl<'a> PeepholeOptimizations {
|
|||
/// Compress `a = a + b` to `a += b`
|
||||
fn try_compress_normal_assignment_to_combined_assignment(
|
||||
expr: &mut AssignmentExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> bool {
|
||||
if !matches!(expr.operator, AssignmentOperator::Assign) {
|
||||
return false;
|
||||
|
|
@ -1125,7 +1116,7 @@ impl<'a> PeepholeOptimizations {
|
|||
fn has_no_side_effect_for_evaluation_same_target(
|
||||
assignment_target: &AssignmentTarget,
|
||||
expr: &Expression,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> bool {
|
||||
if let (
|
||||
AssignmentTarget::AssignmentTargetIdentifier(write_id_ref),
|
||||
|
|
@ -1146,7 +1137,7 @@ impl<'a> PeepholeOptimizations {
|
|||
if let Some(read_expr) = expr.as_member_expression() {
|
||||
// It should also return false when the reference might refer to a reference value created by a with statement
|
||||
// when the minifier supports with statements
|
||||
return !Ctx(ctx).is_global_reference(write_expr_object_id)
|
||||
return !ctx.is_global_reference(write_expr_object_id)
|
||||
&& write_expr.content_eq(read_expr);
|
||||
}
|
||||
}
|
||||
|
|
@ -1156,7 +1147,7 @@ impl<'a> PeepholeOptimizations {
|
|||
/// Compress `a = a + b` to `a += b`
|
||||
fn try_compress_assignment_to_update_expression(
|
||||
expr: &mut AssignmentExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let target = expr.left.as_simple_assignment_target_mut()?;
|
||||
if !matches!(expr.operator, AssignmentOperator::Subtraction) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use oxc_allocator::Vec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_traverse::TraverseCtx;
|
||||
|
||||
use crate::ctx::Ctx;
|
||||
|
||||
use super::PeepholeOptimizations;
|
||||
|
||||
|
|
@ -9,11 +10,7 @@ impl<'a> PeepholeOptimizations {
|
|||
/// are minimized and instead flows to implicit exits conditions.
|
||||
///
|
||||
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/MinimizeExitPoints.java>
|
||||
pub fn minimize_exit_points(
|
||||
&mut self,
|
||||
body: &mut FunctionBody<'_>,
|
||||
_ctx: &mut TraverseCtx<'_>,
|
||||
) {
|
||||
pub fn minimize_exit_points(&mut self, body: &mut FunctionBody<'_>, _ctx: Ctx<'a, '_>) {
|
||||
self.remove_last_return(&mut body.statements);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,17 @@ mod replace_known_methods;
|
|||
mod statement_fusion;
|
||||
mod substitute_alternate_syntax;
|
||||
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_data_structures::stack::NonEmptyStack;
|
||||
use oxc_syntax::{es_target::ESTarget, scope::ScopeId};
|
||||
use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, TraverseCtx};
|
||||
|
||||
pub use normalize::{Normalize, NormalizeOptions};
|
||||
use rustc_hash::FxHashSet;
|
||||
use crate::ctx::Ctx;
|
||||
|
||||
pub use self::normalize::{Normalize, NormalizeOptions};
|
||||
|
||||
pub struct PeepholeOptimizations {
|
||||
target: ESTarget,
|
||||
|
|
@ -138,6 +141,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
let ctx = Ctx(ctx);
|
||||
self.statement_fusion_exit_statements(stmts, ctx);
|
||||
self.collapse_variable_declarations(stmts, ctx);
|
||||
self.minimize_conditions_exit_statements(stmts, ctx);
|
||||
|
|
@ -148,6 +152,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
let ctx = Ctx(ctx);
|
||||
self.minimize_conditions_exit_statement(stmt, ctx);
|
||||
self.remove_dead_code_exit_statement(stmt, ctx);
|
||||
}
|
||||
|
|
@ -156,6 +161,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
let ctx = Ctx(ctx);
|
||||
self.substitute_return_statement(stmt, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -163,6 +169,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
let ctx = Ctx(ctx);
|
||||
self.minimize_exit_points(body, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -174,6 +181,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
let ctx = Ctx(ctx);
|
||||
self.substitute_variable_declaration(decl, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -181,6 +189,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
let ctx = Ctx(ctx);
|
||||
self.fold_constants_exit_expression(expr, ctx);
|
||||
self.minimize_conditions_exit_expression(expr, ctx);
|
||||
self.remove_dead_code_exit_expression(expr, ctx);
|
||||
|
|
@ -192,6 +201,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
let ctx = Ctx(ctx);
|
||||
self.substitute_call_expression(expr, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -199,6 +209,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
let ctx = Ctx(ctx);
|
||||
self.substitute_object_property(prop, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -210,6 +221,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
let ctx = Ctx(ctx);
|
||||
self.substitute_assignment_target_property_property(prop, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -217,6 +229,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
let ctx = Ctx(ctx);
|
||||
self.substitute_binding_property(prop, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -228,6 +241,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
let ctx = Ctx(ctx);
|
||||
self.substitute_method_definition(prop, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -239,6 +253,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
let ctx = Ctx(ctx);
|
||||
self.substitute_property_definition(prop, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -250,6 +265,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
let ctx = Ctx(ctx);
|
||||
self.substitute_accessor_property(prop, ctx);
|
||||
}
|
||||
}
|
||||
|
|
@ -276,19 +292,19 @@ impl<'a> Traverse<'a> for LatePeepholeOptimizations {
|
|||
expr: &mut MemberExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
Self::convert_to_dotted_properties(expr, ctx);
|
||||
Self::convert_to_dotted_properties(expr, Ctx(ctx));
|
||||
}
|
||||
|
||||
fn exit_class_body(&mut self, body: &mut ClassBody<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
Self::remove_dead_code_exit_class_body(body, ctx);
|
||||
Self::remove_dead_code_exit_class_body(body, Ctx(ctx));
|
||||
}
|
||||
|
||||
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
Self::substitute_exit_expression(expr, ctx);
|
||||
Self::substitute_exit_expression(expr, Ctx(ctx));
|
||||
}
|
||||
|
||||
fn exit_catch_clause(&mut self, catch: &mut CatchClause<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.substitute_catch_clause(catch, ctx);
|
||||
self.substitute_catch_clause(catch, Ctx(ctx));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -308,15 +324,15 @@ impl<'a> DeadCodeElimination {
|
|||
|
||||
impl<'a> Traverse<'a> for DeadCodeElimination {
|
||||
fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.inner.remove_dead_code_exit_statement(stmt, ctx);
|
||||
self.inner.remove_dead_code_exit_statement(stmt, Ctx(ctx));
|
||||
}
|
||||
|
||||
fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.inner.remove_dead_code_exit_statements(stmts, ctx);
|
||||
self.inner.remove_dead_code_exit_statements(stmts, Ctx(ctx));
|
||||
}
|
||||
|
||||
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.inner.fold_constants_exit_expression(expr, ctx);
|
||||
self.inner.remove_dead_code_exit_expression(expr, ctx);
|
||||
self.inner.fold_constants_exit_expression(expr, Ctx(ctx));
|
||||
self.inner.remove_dead_code_exit_expression(expr, Ctx(ctx));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use oxc_ecmascript::{
|
|||
side_effects::MayHaveSideEffects,
|
||||
};
|
||||
use oxc_span::GetSpan;
|
||||
use oxc_traverse::{Ancestor, TraverseCtx};
|
||||
use oxc_traverse::Ancestor;
|
||||
|
||||
use crate::{ctx::Ctx, keep_var::KeepVar};
|
||||
|
||||
|
|
@ -21,20 +21,15 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
pub fn remove_dead_code_exit_statements(
|
||||
&mut self,
|
||||
stmts: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
if stmts.iter().any(|stmt| matches!(stmt, Statement::EmptyStatement(_))) {
|
||||
stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_)));
|
||||
}
|
||||
self.dead_code_elimination(stmts, Ctx(ctx));
|
||||
self.dead_code_elimination(stmts, ctx);
|
||||
}
|
||||
|
||||
pub fn remove_dead_code_exit_statement(
|
||||
&mut self,
|
||||
stmt: &mut Statement<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
let ctx = Ctx(ctx);
|
||||
pub fn remove_dead_code_exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: Ctx<'a, '_>) {
|
||||
if let Some(new_stmt) = match stmt {
|
||||
Statement::BlockStatement(s) => Self::try_optimize_block(s, ctx),
|
||||
Statement::IfStatement(s) => self.try_fold_if(s, ctx),
|
||||
|
|
@ -59,9 +54,8 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
pub fn remove_dead_code_exit_expression(
|
||||
&mut self,
|
||||
expr: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
let ctx = Ctx(ctx);
|
||||
if let Some(folded_expr) = match expr {
|
||||
Expression::ConditionalExpression(e) => Self::try_fold_conditional_expression(e, ctx),
|
||||
Expression::SequenceExpression(sequence_expression) => {
|
||||
|
|
@ -578,7 +572,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
}
|
||||
|
||||
impl<'a> LatePeepholeOptimizations {
|
||||
pub fn remove_dead_code_exit_class_body(body: &mut ClassBody<'a>, _ctx: &mut TraverseCtx<'a>) {
|
||||
pub fn remove_dead_code_exit_class_body(body: &mut ClassBody<'a>, _ctx: Ctx<'a, '_>) {
|
||||
body.body.retain(|e| !matches!(e, ClassElement::StaticBlock(s) if s.body.is_empty()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use oxc_ecmascript::{
|
|||
};
|
||||
use oxc_span::SPAN;
|
||||
use oxc_syntax::es_target::ESTarget;
|
||||
use oxc_traverse::{Ancestor, TraverseCtx};
|
||||
use oxc_traverse::Ancestor;
|
||||
|
||||
use crate::ctx::Ctx;
|
||||
|
||||
|
|
@ -21,18 +21,14 @@ impl<'a> PeepholeOptimizations {
|
|||
pub fn replace_known_methods_exit_expression(
|
||||
&mut self,
|
||||
node: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
self.try_fold_concat_chain(node, ctx);
|
||||
self.try_fold_known_string_methods(node, ctx);
|
||||
self.try_fold_known_property_access(node, ctx);
|
||||
}
|
||||
|
||||
fn try_fold_known_string_methods(
|
||||
&mut self,
|
||||
node: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
fn try_fold_known_string_methods(&mut self, node: &mut Expression<'a>, ctx: Ctx<'a, '_>) {
|
||||
let Expression::CallExpression(ce) = node else { return };
|
||||
let (name, object) = match &ce.callee {
|
||||
Expression::StaticMemberExpression(member) if !member.optional => {
|
||||
|
|
@ -70,7 +66,7 @@ impl<'a> PeepholeOptimizations {
|
|||
ce: &CallExpression<'a>,
|
||||
name: &str,
|
||||
object: &Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
if ce.arguments.len() >= 1 {
|
||||
return None;
|
||||
|
|
@ -89,7 +85,7 @@ impl<'a> PeepholeOptimizations {
|
|||
ce: &CallExpression<'a>,
|
||||
name: &str,
|
||||
object: &Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let args = &ce.arguments;
|
||||
if args.len() >= 3 {
|
||||
|
|
@ -118,7 +114,7 @@ impl<'a> PeepholeOptimizations {
|
|||
fn try_fold_string_substring_or_slice(
|
||||
ce: &CallExpression<'a>,
|
||||
object: &Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let args = &ce.arguments;
|
||||
if args.len() > 2 {
|
||||
|
|
@ -127,11 +123,11 @@ impl<'a> PeepholeOptimizations {
|
|||
let Expression::StringLiteral(s) = object else { return None };
|
||||
let start_idx = args.first().and_then(|arg| match arg {
|
||||
Argument::SpreadElement(_) => None,
|
||||
_ => Ctx(ctx).get_side_free_number_value(arg.to_expression()),
|
||||
_ => ctx.get_side_free_number_value(arg.to_expression()),
|
||||
});
|
||||
let end_idx = args.get(1).and_then(|arg| match arg {
|
||||
Argument::SpreadElement(_) => None,
|
||||
_ => Ctx(ctx).get_side_free_number_value(arg.to_expression()),
|
||||
_ => ctx.get_side_free_number_value(arg.to_expression()),
|
||||
});
|
||||
#[expect(clippy::cast_precision_loss)]
|
||||
if start_idx.is_some_and(|start| start > s.value.len() as f64 || start < 0.0)
|
||||
|
|
@ -154,7 +150,7 @@ impl<'a> PeepholeOptimizations {
|
|||
fn try_fold_string_char_at(
|
||||
ce: &CallExpression<'a>,
|
||||
object: &Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let args = &ce.arguments;
|
||||
if args.len() > 1 {
|
||||
|
|
@ -183,13 +179,13 @@ impl<'a> PeepholeOptimizations {
|
|||
fn try_fold_string_char_code_at(
|
||||
ce: &CallExpression<'a>,
|
||||
object: &Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let Expression::StringLiteral(s) = object else { return None };
|
||||
let char_at_index = match ce.arguments.first() {
|
||||
None => Some(0.0),
|
||||
Some(Argument::SpreadElement(_)) => None,
|
||||
Some(e) => Ctx(ctx).get_side_free_number_value(e.to_expression()),
|
||||
Some(e) => ctx.get_side_free_number_value(e.to_expression()),
|
||||
}?;
|
||||
let value = if (0.0..65536.0).contains(&char_at_index) {
|
||||
s.value.as_str().char_code_at(Some(char_at_index))? as f64
|
||||
|
|
@ -205,7 +201,7 @@ impl<'a> PeepholeOptimizations {
|
|||
ce: &CallExpression<'a>,
|
||||
name: &str,
|
||||
object: &Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
if ce.arguments.len() != 2 {
|
||||
return None;
|
||||
|
|
@ -216,14 +212,14 @@ impl<'a> PeepholeOptimizations {
|
|||
let search_value = match search_value {
|
||||
Argument::SpreadElement(_) => return None,
|
||||
match_expression!(Argument) => {
|
||||
Ctx(ctx).get_side_free_string_value(search_value.to_expression())?
|
||||
ctx.get_side_free_string_value(search_value.to_expression())?
|
||||
}
|
||||
};
|
||||
let replace_value = ce.arguments.get(1).unwrap();
|
||||
let replace_value = match replace_value {
|
||||
Argument::SpreadElement(_) => return None,
|
||||
match_expression!(Argument) => {
|
||||
Ctx(ctx).get_side_free_string_value(replace_value.to_expression())?
|
||||
ctx.get_side_free_string_value(replace_value.to_expression())?
|
||||
}
|
||||
};
|
||||
if replace_value.contains('$') {
|
||||
|
|
@ -241,10 +237,9 @@ impl<'a> PeepholeOptimizations {
|
|||
fn try_fold_string_from_char_code(
|
||||
ce: &CallExpression<'a>,
|
||||
object: &Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let Expression::Identifier(ident) = object else { return None };
|
||||
let ctx = Ctx(ctx);
|
||||
if ident.name != "String" || !ctx.is_global_reference(ident) {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -269,7 +264,7 @@ impl<'a> PeepholeOptimizations {
|
|||
fn try_fold_to_string(
|
||||
ce: &CallExpression<'a>,
|
||||
object: &Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let args = &ce.arguments;
|
||||
match object {
|
||||
|
|
@ -335,7 +330,7 @@ impl<'a> PeepholeOptimizations {
|
|||
|
||||
/// `[].concat(a).concat(b)` -> `[].concat(a, b)`
|
||||
/// `"".concat(a).concat(b)` -> `"".concat(a, b)`
|
||||
fn try_fold_concat_chain(&mut self, node: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
fn try_fold_concat_chain(&mut self, node: &mut Expression<'a>, ctx: Ctx<'a, '_>) {
|
||||
let original_span = if let Expression::CallExpression(root_call_expr) = node {
|
||||
root_call_expr.span
|
||||
} else {
|
||||
|
|
@ -415,10 +410,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
|
||||
/// `[].concat(1, 2)` -> `[1, 2]`
|
||||
fn try_fold_concat(
|
||||
ce: &mut CallExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Expression<'a>> {
|
||||
fn try_fold_concat(ce: &mut CallExpression<'a>, ctx: Ctx<'a, '_>) -> Option<Expression<'a>> {
|
||||
// let concat chaining reduction handle it first
|
||||
if let Ancestor::StaticMemberExpressionObject(parent_member) = ctx.parent() {
|
||||
if parent_member.property().name.as_str() == "concat" {
|
||||
|
|
@ -476,11 +468,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
fn try_fold_known_property_access(
|
||||
&mut self,
|
||||
node: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
fn try_fold_known_property_access(&mut self, node: &mut Expression<'a>, ctx: Ctx<'a, '_>) {
|
||||
let (name, object, span) = match &node {
|
||||
Expression::StaticMemberExpression(member) if !member.optional => {
|
||||
(member.property.name.as_str(), &member.object, member.span)
|
||||
|
|
@ -495,7 +483,6 @@ impl<'a> PeepholeOptimizations {
|
|||
};
|
||||
let Expression::Identifier(ident) = object else { return };
|
||||
|
||||
let ctx = &mut Ctx(ctx);
|
||||
if !ctx.is_global_reference(ident) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -515,7 +502,7 @@ impl<'a> PeepholeOptimizations {
|
|||
&self,
|
||||
name: &str,
|
||||
span: Span,
|
||||
ctx: &mut Ctx<'a, '_>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let num = |span: Span, n: f64| {
|
||||
ctx.ast.expression_numeric_literal(span, n, None, NumberBase::Decimal)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use oxc_allocator::Vec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::GetSpan;
|
||||
use oxc_traverse::TraverseCtx;
|
||||
|
||||
use crate::ctx::Ctx;
|
||||
|
||||
use super::PeepholeOptimizations;
|
||||
|
||||
|
|
@ -14,7 +15,7 @@ impl<'a> PeepholeOptimizations {
|
|||
pub fn statement_fusion_exit_statements(
|
||||
&mut self,
|
||||
stmts: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
let len = stmts.len();
|
||||
|
||||
|
|
@ -81,7 +82,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
fn fuse_into_one_statement(stmts: &mut [Statement<'a>], ctx: &mut TraverseCtx<'a>) {
|
||||
fn fuse_into_one_statement(stmts: &mut [Statement<'a>], ctx: Ctx<'a, '_>) {
|
||||
let mut exprs = ctx.ast.vec();
|
||||
|
||||
let len = stmts.len();
|
||||
|
|
@ -108,7 +109,7 @@ impl<'a> PeepholeOptimizations {
|
|||
fn fuse_expression_into_control_flow_statement(
|
||||
stmt: &mut Statement<'a>,
|
||||
exprs: Vec<'a, Expression<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
let mut exprs = exprs;
|
||||
let expr = match stmt {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use oxc_syntax::{
|
|||
number::NumberBase,
|
||||
operator::{BinaryOperator, UnaryOperator},
|
||||
};
|
||||
use oxc_traverse::{Ancestor, TraverseCtx};
|
||||
use oxc_traverse::Ancestor;
|
||||
|
||||
use crate::ctx::Ctx;
|
||||
|
||||
|
|
@ -22,18 +22,14 @@ use super::{LatePeepholeOptimizations, PeepholeOptimizations};
|
|||
/// with literals, and simplifying returns.
|
||||
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntax.java>
|
||||
impl<'a> PeepholeOptimizations {
|
||||
pub fn substitute_object_property(
|
||||
&mut self,
|
||||
prop: &mut ObjectProperty<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
pub fn substitute_object_property(&mut self, prop: &mut ObjectProperty<'a>, ctx: Ctx<'a, '_>) {
|
||||
self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx);
|
||||
}
|
||||
|
||||
pub fn substitute_assignment_target_property_property(
|
||||
&mut self,
|
||||
prop: &mut AssignmentTargetPropertyProperty<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
self.try_compress_property_key(&mut prop.name, &mut prop.computed, ctx);
|
||||
}
|
||||
|
|
@ -41,7 +37,7 @@ impl<'a> PeepholeOptimizations {
|
|||
pub fn substitute_binding_property(
|
||||
&mut self,
|
||||
prop: &mut BindingProperty<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx);
|
||||
}
|
||||
|
|
@ -49,7 +45,7 @@ impl<'a> PeepholeOptimizations {
|
|||
pub fn substitute_method_definition(
|
||||
&mut self,
|
||||
prop: &mut MethodDefinition<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx);
|
||||
}
|
||||
|
|
@ -57,7 +53,7 @@ impl<'a> PeepholeOptimizations {
|
|||
pub fn substitute_property_definition(
|
||||
&mut self,
|
||||
prop: &mut PropertyDefinition<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx);
|
||||
}
|
||||
|
|
@ -65,7 +61,7 @@ impl<'a> PeepholeOptimizations {
|
|||
pub fn substitute_accessor_property(
|
||||
&mut self,
|
||||
prop: &mut AccessorProperty<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx);
|
||||
}
|
||||
|
|
@ -73,7 +69,7 @@ impl<'a> PeepholeOptimizations {
|
|||
pub fn substitute_return_statement(
|
||||
&mut self,
|
||||
stmt: &mut ReturnStatement<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
self.compress_return_statement(stmt, ctx);
|
||||
}
|
||||
|
|
@ -81,28 +77,18 @@ impl<'a> PeepholeOptimizations {
|
|||
pub fn substitute_variable_declaration(
|
||||
&mut self,
|
||||
decl: &mut VariableDeclaration<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
for declarator in &mut decl.declarations {
|
||||
self.compress_variable_declarator(declarator, Ctx(ctx));
|
||||
self.compress_variable_declarator(declarator, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn substitute_call_expression(
|
||||
&mut self,
|
||||
expr: &mut CallExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
pub fn substitute_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: Ctx<'a, '_>) {
|
||||
self.try_compress_call_expression_arguments(expr, ctx);
|
||||
}
|
||||
|
||||
pub fn substitute_exit_expression(
|
||||
&mut self,
|
||||
expr: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
let ctx = Ctx(ctx);
|
||||
|
||||
pub fn substitute_exit_expression(&mut self, expr: &mut Expression<'a>, ctx: Ctx<'a, '_>) {
|
||||
// Change syntax
|
||||
match expr {
|
||||
Expression::ArrowFunctionExpression(e) => self.try_compress_arrow_expression(e, ctx),
|
||||
|
|
@ -445,16 +431,12 @@ impl<'a> PeepholeOptimizations {
|
|||
///
|
||||
/// `return undefined` -> `return`
|
||||
/// `return void 0` -> `return`
|
||||
fn compress_return_statement(
|
||||
&mut self,
|
||||
stmt: &mut ReturnStatement<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
fn compress_return_statement(&mut self, stmt: &mut ReturnStatement<'a>, ctx: Ctx<'a, '_>) {
|
||||
let Some(argument) = &stmt.argument else { return };
|
||||
if !match argument {
|
||||
Expression::Identifier(ident) => Ctx(ctx).is_identifier_undefined(ident),
|
||||
Expression::Identifier(ident) => ctx.is_identifier_undefined(ident),
|
||||
Expression::UnaryExpression(e) => {
|
||||
e.operator.is_void() && !Ctx(ctx).expression_may_have_side_efffects(argument)
|
||||
e.operator.is_void() && !ctx.expression_may_have_side_efffects(argument)
|
||||
}
|
||||
_ => false,
|
||||
} {
|
||||
|
|
@ -762,7 +744,7 @@ impl<'a> PeepholeOptimizations {
|
|||
&mut self,
|
||||
key: &mut PropertyKey<'a>,
|
||||
computed: &mut bool,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
if let PropertyKey::NumericLiteral(_) = key {
|
||||
if *computed {
|
||||
|
|
@ -805,7 +787,7 @@ impl<'a> PeepholeOptimizations {
|
|||
fn try_compress_call_expression_arguments(
|
||||
&mut self,
|
||||
node: &mut CallExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
ctx: Ctx<'a, '_>,
|
||||
) {
|
||||
let (new_size, should_fold) =
|
||||
node.arguments.iter().fold((0, false), |(mut new_size, mut should_fold), arg| {
|
||||
|
|
@ -865,7 +847,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
|
||||
impl<'a> LatePeepholeOptimizations {
|
||||
pub fn substitute_exit_expression(expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
pub fn substitute_exit_expression(expr: &mut Expression<'a>, ctx: Ctx<'a, '_>) {
|
||||
if let Expression::NewExpression(e) = expr {
|
||||
Self::try_compress_typed_array_constructor(e, ctx);
|
||||
}
|
||||
|
|
@ -879,10 +861,10 @@ impl<'a> LatePeepholeOptimizations {
|
|||
}
|
||||
|
||||
/// `new Int8Array(0)` -> `new Int8Array()` (also for other TypedArrays)
|
||||
fn try_compress_typed_array_constructor(e: &mut NewExpression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
fn try_compress_typed_array_constructor(e: &mut NewExpression<'a>, ctx: Ctx<'a, '_>) {
|
||||
let Expression::Identifier(ident) = &e.callee else { return };
|
||||
let name = ident.name.as_str();
|
||||
if !Self::is_typed_array_name(name) || !Ctx(ctx).is_global_reference(ident) {
|
||||
if !Self::is_typed_array_name(name) || !ctx.is_global_reference(ident) {
|
||||
return;
|
||||
}
|
||||
if e.arguments.len() == 1
|
||||
|
|
@ -893,10 +875,7 @@ impl<'a> LatePeepholeOptimizations {
|
|||
}
|
||||
|
||||
/// Transforms boolean expression `true` => `!0` `false` => `!1`.
|
||||
fn try_compress_boolean(
|
||||
expr: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Expression<'a>> {
|
||||
fn try_compress_boolean(expr: &mut Expression<'a>, ctx: Ctx<'a, '_>) -> Option<Expression<'a>> {
|
||||
let Expression::BooleanLiteral(lit) = expr else { return None };
|
||||
let num = ctx.ast.expression_numeric_literal(
|
||||
lit.span,
|
||||
|
|
@ -907,11 +886,7 @@ impl<'a> LatePeepholeOptimizations {
|
|||
Some(ctx.ast.expression_unary(lit.span, UnaryOperator::LogicalNot, num))
|
||||
}
|
||||
|
||||
pub fn substitute_catch_clause(
|
||||
&mut self,
|
||||
catch: &mut CatchClause<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
pub fn substitute_catch_clause(&mut self, catch: &mut CatchClause<'a>, ctx: Ctx<'a, '_>) {
|
||||
if self.target >= ESTarget::ES2019 {
|
||||
if let Some(param) = &catch.param {
|
||||
if let BindingPatternKind::BindingIdentifier(ident) = ¶m.pattern.kind {
|
||||
|
|
|
|||
Loading…
Reference in a new issue